1.16.0 • Published 2 days ago

@embroider/macros v1.16.0

Weekly downloads
222,346
License
MIT
Repository
github
Last release
2 days ago

@embroider/macros

A standardized solution for modifying your package's Javascript and Glimmer templates at app-compilation-time.

Motivation

Traditionally, Ember addons have a lot of power to run arbitrary code during the build process. This lets them do whatever they need to do, but it also makes them hard to statically analyze and makes them play badly with some tooling (like IDEs).

The Embroider package spec proposes fixing this by making Ember addons much more static. But they will still need the ability to change themselves in certain ways at app compilation time. Hence this package.

This package works in both Embroider and Classical builds, so that addon authors can switch to this newer pattern without disruption.

Setting Configuration: from an Ember app

  1. Add @embroider/macros as devDependency.
  2. In ember-cli-build.js, do:
let app = new EmberApp(defaults, {
  '@embroider/macros': {
    // this is how you configure your own package
    setOwnConfig: {
      // your config goes here
    },
    // this is how you can optionally send configuration into your
    // dependencies, if those dependencies choose to use
    // @embroider/macros configs.
    setConfig: {
      'some-dependency': {
        // config for some-dependency
      },
    },
  },
});

Setting Configuration: from an Ember Addon

  1. Add @embroider/macros as dependency.
  2. In index.js, do:
module.exports = {
  name: require('./package').name,
  options: {
    '@embroider/macros': {
      setOwnConfig: {
        // your config goes here
      },
      setConfig: {
        'some-dependency': {
          // config for some-dependency
        },
      },
    },
  },
};

The Macros

macroCondition

The macroCondition macro allows branch level code isolation (and deletion in the case of production builds). Generally macroConditions are viewed as a foundation macro and are combined with others marcos (detailed below) to create more complex scenarios. macroCondition takes a single argument which must be statically known or another macro which will compile down to a static value.

import { macroCondition } from '@embroider/macros';

if (macroCondition(true)) {
  // this branch will remain in both dev and production builds
} else if (macroCondition(false)) {
  // this branch will never be hit and furthermore in production
  // builds it will be fully removed
}

// they can also be used as ternary expressions:
let specialVariable = 'Hello ' + (macroCondition(true) ? 'Bob' : 'Jane');

console.log(specialVariable); // will print "Hello Bob"

Macros can also be used inside of templates:

{{#if (macroCondition true)}}
  red
{{else}}
  blue
{{/if}}

Starting with Ember 3.25 you can also use it to conditionally apply modifiers:

<button {{(if (macroCondition true) on) "click" this.something}}>Submit</button>

However, in all cases the argument to macroCondition must be statically analyzable:

import { macroCondition } from '@embroider/macros';

let foo = true;
if (macroCondition(foo)) {
  // this is not allowed as the first argument must be statically known
}

importSync

The primary reason for Embroider's existence is to create statically analyzable builds. An under pinning of this is the ability to walk and understand the dependency graph of every module. Embroider can natively understand imports such as import foo as 'foo' but cannot handle require's (imagine: require(bar ? 'bar' : 'baz'). The importSync macro is way to "tell" Embroider about the existence of a module and to bring it into a package's scope such that it can be discovered and included into the final build. importSync takes a single static string as its only required argument.

import { importSync } from '@embroider/macros';

let foo = importSync('foo');

// will compile to:

let foo = require('foo');

dependencySatisfies

Tests whether a given dependency is present and satisfies the given semver range. Both arguments must be strings and the second argument will be passed into semver's satisfies method.

import { dependencySatisfies } from '@embroider/macros';

let doesFooExist = dependencySatisfies('foo', '1.0.0');

// will compile to:

let doesFooExist = true; // or false if the dependency was not satisfied

We can use this macro along with the macroCondition and importSync macro's from above to do something more complex:

import { macroCondition, dependencySatisfies, importSync } from '@embroider/macros';

if (macroCondition(dependencySatisfies('ember-qunit', '*'))) {
  return importSync('ember-qunit');
} else if (macroCondition(dependencySatisfies('ember-mocha', '*'))) {
  return importSync('ember-mocha');
}
{{macroDependencySatisfies 'qunit' '^2.8.0'}}

getOwnConfig, getConfig, and getGlobalConfig

A common pattern is to have a set of configuration properties that you define (or a consumer defines for you) which you base certain build time conditions around. This is achieved via the getOwnConfig, getConfig, and getGlobalConfig macros (depending on which config you want to read).

module.exports = {
  name: require('./package').name,
  options: {
    '@embroider/macros': {
      setOwnConfig: {
        themeColor: 'red',
      },
    },
  },
  included() {
    this._super.included.apply(this, arguments);
    this.options['@embroider/macros'].setOwnConfig.shouldIncludeMinifiedLibrary = false;
  },
};
import { getOwnConfig, importSync, macroCondition } from '@embroider/macros';

if (macroCondition(getOwnConfig().shouldIncludeMinifiedLibrary)) {
  importSync('minified-library');
} else {
  importSync('unminified-library');
}
<button class="{{macroGetOwnConfig "themeColor"}}">My Themed Button</button>

Real world examples

Below are a list of addons that have started using @embroider/macros so that you can get a feel for common use cases that can be solved via the macro system.

@sentry-murz/ember@everything-registry/sub-chunk-288ember-cli-imgixember-aria-utilitiesember-apollo-clientember-alexandriaember-animatedember-arg-typesember-cli-clipboardember-addon-configember-bootstrapember-bootstrap-modals-managerember-cached-decorator-polyfillember-cli-mirageember-cli-raygunember-cognito-identityember-collapsible-panelember-commandember-cp-validationsember-data-relationship-dirty-trackingember-data-factory-guyember-data-mirrorember-dataember-d3-modifiersember-dayjsember-deep-trackedember-citeember-changesetember-classic-decoratorember-basic-dropdownember-behaveember-auto-importember-headless-tableember-swappable-serviceember-sweet-ownerember-stereoember-svg-jarember-tarteaucitronember-tippyember-froala-editorember-formidableember-legacy-built-in-componentsember-linkember-localized-modelember-file-uploadember-resourcesember-responsive-imageember-replember-router-service-refresh-polyfillember-tooltipsember-validatorsember-validated-formember-in-viewportember-indexeddbember-query-params-serviceember-qunitember-get-configember-enginesember-engines-router-serviceember-emeisember-power-calendarember-power-calendar-luxonember-power-calendar-momentember-primitivesember-modal-dialogember-models-tableember-models-table-floating-filterember-modify-based-class-resourceember-short-numberember-simple-authember-simple-auth-oidcilios-commonlimber-uiliquid-firereactivewebcsii-sentry-ember@runspired-forks/ember-auto-import@pgengler/ember-cli-miragetracked-queue@embroider/compat@embroider/core@embroider/util@embroider/vite@ember-responsive-image/cloudinary@ember-responsive-image/imgix@ember/legacy-built-in-components@ember/render-modifiers@ember/test-helpers@lblod/ember-rdfa-editor@lblod/ember-environment-banner@lblod/ember-submission-form-fields@fleetbase/ember-ui@html-next/flexi-layouts@html-next/flexi-sustain@eflexsystems/ember-exam@eflexsystems/ember-simple-auth-oidc@ember-data/adapter@ember-data/debug@ember-data/graph@ember-data/json-api
1.16.0

8 days ago

1.15.1

15 days ago

1.15.0

2 months ago

1.14.0

2 months ago

1.13.5

3 months ago

1.13.4

4 months ago

1.12.3

10 months ago

1.12.2

10 months ago

1.12.1

10 months ago

1.12.0

10 months ago

1.13.2

7 months ago

1.13.1

9 months ago

1.13.0

9 months ago

1.13.3

6 months ago

1.11.1

11 months ago

1.11.0

11 months ago

1.9.0

2 years ago

1.10.0

1 year ago

1.8.2

2 years ago

1.8.1

2 years ago

1.8.3

2 years ago

1.8.0

2 years ago

1.7.1

2 years ago

1.7.0

2 years ago

1.6.0

2 years ago

1.2.0

2 years ago

1.1.0

2 years ago

1.5.0

2 years ago

0.49.0

2 years ago

1.0.0

2 years ago

1.4.0

2 years ago

1.3.0

2 years ago

0.50.1

2 years ago

0.50.2

2 years ago

0.50.0

2 years ago

0.48.0

2 years ago

0.48.1

2 years ago

0.47.2

2 years ago

0.47.1

3 years ago

0.46.2

3 years ago

0.46.0

3 years ago

0.46.1

3 years ago

0.47.0

3 years ago

0.45.0

3 years ago

0.44.2

3 years ago

0.44.1

3 years ago

0.44.0

3 years ago

0.44.0-alpha.2

3 years ago

0.43.5

3 years ago

0.43.4

3 years ago

0.44.0-alpha.1

3 years ago

0.44.0-alpha.0

3 years ago

0.43.3

3 years ago

0.43.2

3 years ago

0.43.1

3 years ago

0.43.0

3 years ago

0.42.3

3 years ago

0.42.2

3 years ago

0.42.1

3 years ago

0.42.0

3 years ago

0.41.0

3 years ago

0.40.0

3 years ago

0.38.0

3 years ago

0.39.1

3 years ago

0.39.0

3 years ago

0.38.0-alpha.3

3 years ago

0.38.0-alpha.2

3 years ago

0.38.0-alpha.1

3 years ago

0.38.0-alpha.0

3 years ago

0.37.0

3 years ago

0.36.0

3 years ago

0.35.1

3 years ago

0.35.0

3 years ago

0.34.0

3 years ago

0.33.0

3 years ago

0.32.0

3 years ago

0.31.0

3 years ago

0.30.0

3 years ago

0.29.0

3 years ago

0.28.0

4 years ago

0.27.0

4 years ago

0.26.0

4 years ago

0.25.0

4 years ago

0.24.1

4 years ago

0.24.0

4 years ago

0.23.0

4 years ago

0.22.0

4 years ago

0.21.0

4 years ago

0.20.0

4 years ago

0.19.0

4 years ago

0.19.0-alpha.0

4 years ago

0.18.0

4 years ago

0.17.0

4 years ago

0.16.0

4 years ago

0.16.1

4 years ago

0.15.0

4 years ago

0.14.0

4 years ago

0.13.0

4 years ago

0.12.0

4 years ago

0.11.1

4 years ago

0.11.0

4 years ago

0.10.0

4 years ago

0.9.0

4 years ago

0.8.0

4 years ago

0.7.1

5 years ago

0.7.0

5 years ago

0.6.0

5 years ago

0.5.1

5 years ago

0.5.0

5 years ago

0.4.3

5 years ago

0.4.2

5 years ago

0.4.1

5 years ago

0.4.0

5 years ago

0.3.5

5 years ago

0.3.4

5 years ago

0.3.3

5 years ago

0.3.2

5 years ago

0.3.1

5 years ago

0.3.0

5 years ago

0.2.0

5 years ago

0.1.0

5 years ago

0.0.12

5 years ago

0.0.11

5 years ago

0.0.10

5 years ago

0.0.9

5 years ago

0.0.8

5 years ago

0.0.7

5 years ago