Coder Social home page Coder Social logo

ember-cli-conditional-compile's Introduction

ember-cli-conditional-compile

Build Status

The goal of ember-cli-conditional-compile is to provide easy to use feature switches to Ember applications such that code that is hidden behind a disabled feature switch is not in the compiled code.

Getting Started

This is an ember-cli addon, so all you need to do is

    ember install ember-cli-conditional-compile

Compatibility

Version 2.x targets Ember 3.x and beyond. It is tested against the latest ember release and the latest beta release (See config/ember-try.js for the testing matrix)

Upgrading to 2.x

Our workaround around a broccoli pipeline bug in newer versions of ember-cli slightly changes the semantics of includeDirByFlag. Instead of a RegExp, you now need to specify a Glob-style pattern.

Usage

To actually use the feature switches you'll need to add some configuration in your environment.js file. For example, lets pretend you want to have two feature switches; ENABLE_FOO and ENABLE_BAR:

var ENV = {
  // other settings ...

  featureFlags: {
    ENABLE_FOO: true,
    ENABLE_BAR: true,
  },
  includeDirByFlag: {
    ENABLE_FOO: ["pods/foos/**", "pods/foo/**"],
    ENABLE_BAR: [],
  },
};

// other environments ...

if (environment === "production") {
  ENV.featureFlags.ENABLE_FOO = false;
}

Alternatively, you can define your feature flags in config/feature-flags.js looking like this:

module.exports = function (environment) {
  const GLOBAL_FLAGS = {
    featureFlags: {
      ENABLE_FOO: true,
      ENABLE_BAR: true,
    },
    includeDirByFlag: {
      ENABLE_FOO: [/pods\/foos/, /pods\/foo/],
      ENABLE_BAR: [],
    },
  };

  if (environment === "production") {
    GLOBAL_FLAGS.featureFlags.ENABLE_FOO = false;
  }

  return GLOBAL_FLAGS;
};

This has two advantages: It declutters environment.js a bit, especially if you have many flags, but also prevents your flag names from leaking into the application code under certain circumstances.

We'll look at the two new options in more detail below, but for now we can see that by default both features are enabled, but in the production environment ENABLE_FOO is disabled, and related code under the pods/foos and pods/foo directories are excluded from compilation.

ENV.featureFlags

This setting sets up which flags will be available to actually switch use. A value of true means that the flag will be enabled, false means that it will not be.

ENV.includeDirByFlag

Given a key which has been defined above, the value is an array of regexes of files/paths which will only be included in the compiled product if the related feature flag is enabled. In the example above, in the development environment ENABLE_FOO is true, so the pods/foo and pods/foos paths will be included.

However, since the flag is false in production, any code in those directories will not be compiled in.

How it works

ember-cli-conditional-compile adds itself to the Broccoli compile pipeline for your Ember application. Depending on which environment you're building it acts in two different ways:

Development and test environments

Global variables are injected into the page which have the current state of the feature flags. For example:

if (ENABLE_FOO) {
  this.route("foo");
  console.log("The feature ENABLE_FOO is enabled in this environment");
}

will be represented in development and test environments as:

window.ENABLE_FOO = true;

if (ENABLE_FOO) {
  this.route("foo");
  console.log("The feature ENABLE_FOO is enabled in this environment");
}

In Handlebars/HTMLBars templates, you can also make use of the flags using the if-flag block helper:

{{#if-flag ENABLE_FOO}}
  <p>Foo is enabled! \o/</p>
{{else}}
  <p>Foo is disabled</p>
{{/if-flag}}

You can also use the unless-flag style block helper:

{{#unless-flag ENABLE_FOO}}
  <p>Foo is disabled</p>
{{else}}
  <p>Foo is enabled! \o/</p>
{{/unless-flag}}

Production environment

We use UglifyJS's global_defs feature to replace the value of feature flags with their constant values. UglifyJS's dead code implementation then cleans up unreachable code and performs inlining, such that:

if (ENABLE_FOO) {
  this.route("foo");
  console.log("The feature ENABLE_FOO is enabled in this environment");
}

will be represented in the production environment as the following if ENABLE_FOO is configured to be true:

this.route("foo");
console.log("The feature ENABLE_FOO is enabled in this environment");

or the following if ENABLE_FOO is configured to be false;

// empty since the condition can never be satisfied!

Furthermore, if you use the HTMLBars helpers the AST transformations will shake out and remove impossible-to-reach sides of the condition:

{{#if-flag ENABLE_FOO}}
  <p>Foo is enabled</p>
{{else}}
  <p>This won't be reached, because ENABLE_FOO is true</p>
{{/if-flag}}

will get transformed into:

<p>Foo is enabled</p>

This is really handy, since it vastly cuts down on the amount of precompiled template code that your users need to download even though it'll never be executed!

Defining additional environments

By defining ENV.featureFlagsEnvironment you can separate your feature flags by more than just test/development/production, for example to have a beta environment that is identical to production but has a couple more flags activated. This only works if you have your flags in config.featureFlags - The environment passed in into the wrapper function will be ENV.featureFlagsEnvironment if set.

Licence

This library is lovingly brought to you by the FreshBooks developers. We've released it under the MIT license.

ember-cli-conditional-compile's People

Contributors

alexbaizeau avatar calvin-fb avatar calvinlough avatar dependabot[bot] avatar halfbyte avatar jobackman avatar k-fish avatar minichate avatar odoe avatar oscarni avatar typeoneerror avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ember-cli-conditional-compile's Issues

Release or backport ember-cli-babel change?

The current version of this project specifies a tightly constrained version of ember-cli-babel:

"ember-cli-babel": "~7.4.0",

This results in the following warning when building an application:

WARNING: [DEPRECATION] [DEPRECATION] Usage of the Ember Global is deprecated. You should import the Ember module or the specific API instead.

See https://deprecations.emberjs.com/v3.x/#toc_ember-global for details.

Usages of the Ember Global may be caused by an outdated ember-cli-babel dependency. The following steps may help:

SNIP SNIP SNIP

* [email protected], currently used by:
  * [email protected] (Dormant)
    * Depends on ember-cli-babel@~7.4.0

As this project is marked DORMANT above it doesn't really add to the noted problem, but it does add to the background noise of day-to-day work.

Would it be possible to do a release (or backport) the changes required to loosen the version constraint of ember-cli-babel?

Ember 4.x compatibility and other fixes

This is more an inquiry than a bug report, please let me know if I should write an email to someone instead.

We've been using our own fork of this for a while at Focusrite-Novation and are somewhat dependent on it. The changes we made to our fork so far are:

  • Enable us to override the environment (As we have multiple environments and dev/test/prod is not enough for us)
  • Fix #59 by replacing files with almost empty (/**/) files instead of removing them from the tree. I haven't checked yet if the underlying bug is fixed in ember-cli so maybe this isn't needed anymore.

Over the course of this week I have started to investigate what it would take to make this work on Ember 4.x - It is mainly small things apart from the completely changed API for compiler plugins. Also, the test suite still uses the old test syntax and is currently failing anyway.

I have a branch at my fork that has a green test suite but it has a ton of changes to the dependencies and is quite safely not compatible to older ember versions - I've ran ember-cli-update against it and used a slightly modified version of the generated ember-try config that tests this against modern Ember versions and ignores the terser vs. uglify issue.

This fork does not include the additional changes mentioned above.

I am happy to open up a PR for both the modernisation and the other changes if it would make sense. Feel free to look at my fork and tell me if maybe you would want to update it in more granular steps if possible. The fork was just a way for me to force myself through the bunch of issues I encountered on my way to get green tests,

We would be really happy if we could switch back to the canonical npm module in the future and we would be willing to spend additional time on future maintenance if needed.

Getting undefined error when the value is set to false.

i'm using ember 2.5 and everything is working as expected when I define var as true, however when I define variable as false I'm having an error:

Cannot read property 'ENABLE_FOO' of undefined
TypeError: Cannot read property 'ENABLE_FOO' of undefined
  at /myproject/node_modules/ember-cli-conditional-compile/index.js:63:64
  at Array.map (native)
  at Class.postprocessTree (/Users/info/code/mesitis/canopy-fresh/node_modules/ember-cli-conditional-compile/index.js:62:38)
  at /myproject/node_modules/ember-cli/lib/broccoli/ember-app.js:508:27
  at Array.forEach (native)
  at EmberApp.addonPostprocessTree (/myproject/node_modules/ember-cli/lib/broccoli/ember-app.js:506:23)
  at EmberApp.appAndDependencies (/myproject/node_modules/ember-cli/lib/broccoli/ember-app.js:1050:31)
  at EmberApp.javascript (/myproject/node_modules/ember-cli/lib/broccoli/ember-app.js:1161:34)
  at EmberApp.toArray (/myproject/node_modules/ember-cli/lib/broccoli/ember-app.js:1566:10)
  at EmberApp.toTree (/myproject/node_modules/ember-cli/lib/broccoli/ember-app.js:1588:30)
  at module.exports (/myproject/ember-cli-build.js:85:14)
  at Class.setupBroccoliBuilder (/myproject/node_modules/ember-cli/lib/models/builder.js:54:19)
  at Class.init (/myproject/node_modules/ember-cli/lib/models/builder.js:89:10)
  at Class.superWrapper [as init] (/myproject/node_modules/ember-cli/node_modules/core-object/lib/assign-properties.js:32:18)
  at Class (/myproject/node_modules/ember-cli/node_modules/core-object/core-object.js:32:33)
  at Class.run (/myproject/node_modules/ember-cli/lib/tasks/serve.js:15:19)
  at /myproject/node_modules/ember-cli/lib/commands/serve.js:64:24
  at tryCatch (/myproject/node_modules/rsvp/dist/rsvp.js:538:12)
  at invokeCallback (/myproject/node_modules/rsvp/dist/rsvp.js:553:13)
  at /myproject/node_modules/rsvp/dist/rsvp.js:628:16
  at flush (/myproject/node_modules/rsvp/dist/rsvp.js:2373:5)
  at _combinedTickCallback (internal/process/next_tick.js:67:7)
  at process._tickDomainCallback (internal/process/next_tick.js:122:9)

Please note that in my environment.js file :

  var ENV = {
    featureFlags: {
      ENABLE_FOO: false
    },

If I turn it to true everything works as expected.

Any help? thanks

Template Lint and Flags

Template lint isn't too thrilled about flag names in templates, it'd really like us to use this.FLAG or @FLAG. Are either of these suitable, or should it be ignored in the no-implicit-this rule?

Using includeDirByFlag is causing runtime errors after Ember 3.4 upgrade

This is my config in environment/config.js

featureFlags: {
  WEBSITE_BUILD: isWebBuild,
  NATIVE_BUILD: isNativeBuild
},
includeDirByFlag: {
  WEBSITE_BUILD: [
    /components\/website-only/,
    /routes\/website-only/,
    /controllers\/website-only/,
    /templates\/website-only/
  ],
  NATIVE_BUILD: [
    /components\/native-only/,
    /routes\/native-only/,
    /controllers\/native-only/,
    /templates\/native-only/
  ]
}

On Ember 2.16 up to Ember 3.1.4, the above config worked.

However I have just tried upgrading to Ember 3.4.4 we are getting this error in the console:
Uncaught SyntaxError: Unexpected identifier

This is the output if I do a compiled build with isWebBuild true and isNativeBuild false using this command: ember build --environment=production

...
define("bookings/controllers/modal-loading",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({platform:Ember.inject.service(),isNativeBuild:Ember.computed.readOnly("platform.isNativeBuild")})
e.default=t})
import Controller from"@ember/controller"
import{inject as service}from"@ember/service"
import{readOnly}from"@ember/object/computed"
export default Controller.extend({platform:service(),hi:"appleapple",nativeAppVersion:readOnly("platform.nativeAppVersion")})
define("bookings/controllers/suburb/clinic",["exports"],function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0
var t=Ember.Controller.extend({queryParams:["top"],firstDoctorSlug:void 0})
e.default=t}),define(...

As you can see, there was a controller called controllers/native-only/whats-new.js that should have been stripped from the output. However only the define wrapping function has been stripped, leaving the "guts" of the controller still in the source code and throwing errors.

If I comment out includeDirByFlag config, the build works again.
Please note I am running this version: "ember-cli-conditional-compile": "^1.1.1"

Nice to have: Corresponding {{unless-flag to the {{if-flag

Hi!
Awesome tool! I just put it in our cordova app and it lets us carve out entire pieces of functionality in our production builds just like you say.

It would be nice to have a corresponding {{unless-flag helper we can use in our templates.

Right now, I am doing things like:

{{#if-flag-IS_CORDOVA}}
{{else}}
    // Some more HBS stuff.
{{/if-flag-IS_CORDOVA}}

Would be nice to do:

{{#unless-flag-IS_CORDOVA}}
    // Some more HBS stuff.
{{/unless-flag-IS_CORDOVA}}

Ember 3.27.0 - Receiving "[BUG] seen set should be available" Error

This usage works fine in ember 3.24:

{{#if-flag MY_CONDITIONAL}}
  <p>Hello World</p>
{{/if-flag}}

However is causing this error when after upgrading to ember 3.27:

Template Compiler Error (TemplateCompiler) in my-app/components/account-payments-page/prompt/template.hbs

Assertion Failed: [BUG] seen set should be available

Transpiler error

Hi, I just upgraded to Ember 2.13 from 2.8, updated a bunch of dependencies together with this plugin.
When I try to build for production, I get a very long error which starts with:

broccoli-babel-transpiler is opting out of caching due to a plugin that does not provide a caching strategy: (babel) => {
 const t = babel.types
 // cache for performance
 const parseMap = {}
 return {
   visitor: {
     Identifier(path, state) {
       if (path.parent.type === 'MemberExpression') {
         return;
       }
       if (path.parent.type === 'ClassMethod') {
         return;
       }
       if (path.isPure()) {
         return;
       }
       if (!state.opts.hasOwnProperty(path.node.name)) {
         return;
       }
       let replacementDescriptor = state.opts[path.node.name]
       if (replacementDescriptor === undefined || replacementDescriptor === null) {
         replacementDescriptor = t.identifier(String(replacementDescriptor))
       }
//.....

 Property key of ObjectProperty expected node to be of a type ["Identifier","StringLiteral","NumericLiteral"] but instead got "BooleanLiteral"

    at File.transform (node_modules\ember-cli-conditional-compile\node_modules\babel-core\lib
\transformation\file\index.js:548:35)
    at node_modules\ember-cli-conditional-compile\node_modules\babel-core\lib\transformation\
pipeline.js:50:19
    at File.wrap (node_modules\ember-cli-conditional-compile\node_modules\babel-core\lib\tran
sformation\file\index.js:564:16)
    at Pipeline.transform (node_modules\ember-cli-conditional-compile\node_modules\babel-core
\lib\transformation\pipeline.js:47:17)
    at Promise node_modules\ember-cli-conditional-compile\node_modules\broccoli-babel-transp
iler\lib\parallel-api.js:102:26)
    at initializePromise (node_modules\rsvp\dist\rsvp.js:567:5)
    at new Promise (node_modules\rsvp\dist\rsvp.js:1033:31)
    at Object.transformString (node_modules\ember-cli-conditional-compile\node_modules\brocco
li-babel-transpiler\lib\parallel-api.js:101:12)
    at Babel.transform (node_modules\ember-cli-conditional-compile\node_modules\broccoli-babe
l-transpiler\index.js:80:22)
    at Babel.processString (node_modules\ember-cli-conditional-compile\node_modules\broccoli-
babel-transpiler\index.js:180:15)
    at Promise.then.result.output (node_modules\broccoli-persistent-filter\lib\strategies\per
sistent.js:41:23)

I don't really know what do to. I tried uninstalling this addon, and the build was successful.

UPDATE

I have a property in one of my services, named exactly the same as the feature flag.

  export default Ember.Service.extend({
    myFeatureFlag: function() {
       return config.featureFlags && config.featureFlags.myFeatureFlag
   }.property()
})

If I remove it, or rename the property, it builds (except for the opting out of cache part). Even if I make it myFeatureFlag: true, it still doesn't work. Is this something expected?

NEW UPDATE:

I installed version 1.0.2 of this addon, and it works. 1.0.3 fails. So there seems to be an issue with this pull request: #41

HTMLbars cache isn't busted on rebuilds

I believe the cache issue (#21)
is still present.

@minichate looks like I've been celebrating a bit to early when I made this PR #25 a month ago.
Looks like the newer versions of ember were actually disabled caching when throwing the warning about missing caching strategies. So the "fix" in the PR actually did nothing except reinstating the issue.

I believe we were all testing this "fix" when other plugins were also incompatible, resulting in no cache. I just encountered that the caching issue is still an issue when I managed to remove all AST-warnings.

After taking a closer look at ember-cli-htmlbars it looks like the new AST-caching strategy (specifying a baseDir) only lets htmlbars know where to look for changes and makes sure cache is busted when file changes are made in an addon's directory. There is no support for cache busting based on options yet, which is what's needed here.

There is are discussions for making ember-cli-htmlbars the same type of cacheKeys as this module has babel/broccoli-babel-transpiler#89, but until then it looks like there are only "hacky" ways for this addon to actually bust the template cache.

  • One way would be to save the current feature-flag options and write them to a file inside this addon directory on each build. This would let the directory based caching strategy know it needs to bust cache, but it feels a bit of a hac, although would solve the issue for now.
  • Another solution would be to revert my PR, thus enabling the annoying warning about AST-caching strategy and again force cache busting on every rebuild.

Just wanted to let you know about my findings and thanks for a great plugin!

Ember 2.x Compatibility Issues

First off, thanks for this great addon, this is exactly what I've needed. ๐Ÿ‘

Right now the only breaking point is where we're dynamically creating handlebar helpers for template use. Since this way of registering helpers have been deprecated/removed since 1.13.x

Uncaught TypeError: Ember.default.Handlebars.registerHelper is not a function

Example: src file

{{#if-flag-ENABLE_FOO}}
The FOO flag is enabled \o/
{{/if-flag-ENABLE_FOO}}

@minichate I'll try and looking into this more. From what I've seen so far, nobody has dynamically defined helpers before. ๐Ÿ˜ข

Can't run production build

I'm trying to get this addon to work with v0.4.5.

I get Uncaught ReferenceError: EMBER_CLI_CONDITIONAL_COMPILE_INJECTIONS is not defined at runtime when I run in production. Everything works like expected in development.

I've read previous issues where this has been addressed, but it looks like it's here again. I'm running on ember-cli 1.13.14 and ember 2.3.0.

My ENV looks like this:

var ENV = {
  // other settings ...
  featureFlags: {
    ENABLE_FEATURE: true
  },
  includeDirByFlag: {
    ENABLE_FEATURE: [/pods\/feature/]
}

Any ideas?

Doesn't work with ember-cli 1.13.8

I get the following error at run-time: Uncaught ReferenceError: EMBER_CLI_CONDITIONAL_COMPILE_INJECTIONS is not defined.

I also receive the Could not find any feature flags.You may need to add them in your config/environment.js message on compilation.

Here is my code:

module.exports = function(environment) {
    var ENV = {
        // Testing...
        featureFlags: {
            ENABLE_FOO: true
        },

        includeDirByFlag: {
            ENABLE_FOO: [/pods\/foos/, /pods\/foo/],
        },

...

    return ENV;
};

{{#if-flags-}} dont always reflect the featureFlags value

Hi, before I begin I'd like to commend you on a great addon, it's just what I've been wanting for our project! ๐Ÿ‘๐Ÿ‘

I do believe I have found an issue in the ``{{if-flag-ENABLE_FOO}}helper though, wherefeatureFlags.keys` will not update the `if-flags` correctly. I tried reverse engineering it and I can continuously recreate the it now.

I made a fresh dummy app to make sure it wasn't our app causing the problems, and in case anyone wants to give it a try.

How to recreate:

// environment.js

// ...

featureFlags: {
  ENABLE_FOO: false,
  ENABLE_BAR: false
},
includeDirByFlag: {
  ENABLE_FOO: [/pods\/foo/],
  ENABLE_BAR: [/pods\/bar/]
}

// ...

if (process.env.baz === 'foo') {
  ENV.featureFlags.ENABLE_FOO = true;
}

if (process.env.baz === 'bar') {
  ENV.featureFlags.ENABLE_BAR = true;
}
<h2 id="title">Welcome to Ember</h2>

{{#if-flag-ENABLE_FOO}}
  <h1>FOO</h1>
{{/if-flag-ENABLE_FOO}}

{{#if-flag-ENABLE_BAR}}
  <h1>BAR</h1>
{{/if-flag-ENABLE_BAR}}

{{outlet}}
  1. Run the app; ember serve and neither <h1>foo</h1> nor <h1>bar</h1> will show in the template and their pods aren't included in the build. Nothing wrong here.
  2. Now restart the server, without changing any code, but with setting baz; baz=foo ember serve, thus enabling ENABLE_FOO. The app should now include the foo pod, which it does. But <h1>foo</h1> will still not show up in the template.
  3. If you want foo to show in the template now, edit something in your project and save, causing live-reload to recompile and create a fresh build of your app. Now only <h1>foo</h1> shows and is included in the build as it should.

I'm far from an expert on how you made you app how it should be made for that matter, but I noticed that the console.log spam "sdf" and "foo" (which btw are present in the npm package but not your repo) only occur when changes are made to a file in the app and saved, and thus recompiled in a new build.

I've tried removing dist and tmp, thinking caching of the build was the issue, but the end result has remained the same as long as I didn't edit files on the app.

I hope it makes sense!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.