@embroider/macros
Advanced tools
Comparing version 0.3.5 to 0.4.0
{ | ||
"name": "@embroider/macros", | ||
"version": "0.3.5", | ||
"version": "0.4.0", | ||
"description": "Standardized build-time macros for ember apps.", | ||
@@ -16,5 +16,8 @@ "keywords": [ | ||
}, | ||
"files": [ | ||
"src" | ||
], | ||
"devDependencies": { | ||
"@babel/plugin-transform-modules-commonjs": "^7.2.0", | ||
"@embroider/test-support": "0.3.5", | ||
"@embroider/test-support": "0.4.0", | ||
"@types/babel__core": "^7.0.4", | ||
@@ -34,3 +37,3 @@ "@types/babel__generator": "^7.0.1", | ||
"@babel/types": "^7.3.2", | ||
"@embroider/core": "0.3.5", | ||
"@embroider/core": "0.4.0", | ||
"resolve": "^1.8.1", | ||
@@ -37,0 +40,0 @@ "semver": "^5.6.0" |
265
README.md
@@ -15,66 +15,61 @@ # @embroider/macros | ||
- `getOwnConfig()`: a macro that returns arbitrary JSON-serializable configuration that was sent to your package. See "Setting Configuration" for how to get configuration in. | ||
- `getOwnConfig()`: a macro that returns arbitrary JSON-serializable configuration that was sent to your package. See "Setting Configuration" for how to get configuration in. | ||
Assuming a config of `{ flavor: 'chocolate' }`, this code: | ||
Assuming a config of `{ flavor: 'chocolate' }`, this code: | ||
```js | ||
import { getOwnConfig } from '@embroider/macros'; | ||
console.log(getOwnConfig().flavor); | ||
``` | ||
```js | ||
import { getOwnConfig } from '@embroider/macros'; | ||
console.log(getOwnConfig().flavor); | ||
``` | ||
Compiles to: | ||
Compiles to: | ||
```js | ||
console.log({ "flavor": "chocolate" }.flavor); | ||
``` | ||
```js | ||
console.log({ flavor: 'chocolate' }.flavor); | ||
``` | ||
- `getConfig(packageName)`: like `getOwnConfig`, but will retrieve the configuration that was sent to another package. We will resolve which one based on node_modules resolution rules from your package. | ||
- `getConfig(packageName)`: like `getOwnConfig`, but will retrieve the configuration that was sent to another package. We will resolve which one based on node_modules resolution rules from your package. | ||
- `dependencySatisfies(packagename, semverRange)`: a macro that compiles to a boolean literal. It will be true if the given package can be resolved (via normal node_modules resolution rules) and meets the stated semver requirement. The package version will be `semver.coerce()`'d first, such that nonstandard versions like "3.9.0-beta.0" will appropriately satisfy constraints like "> 3.8". | ||
- `dependencySatisfies(packagename, semverRange)`: a macro that compiles to a boolean literal. It will be true if the given package can be resolved (via normal node_modules resolution rules) and meets the stated semver requirement. The package version will be `semver.coerce()`'d first, such that nonstandard versions like "3.9.0-beta.0" will appropriately satisfy constraints like "> 3.8". | ||
Assuming you have `ember-source` 3.9.0 available, this code: | ||
Assuming you have `ember-source` 3.9.0 available, this code: | ||
```js | ||
import { dependencySatisfies } from '@embroider/macros'; | ||
let hasNativeArrayHelper = dependencySatisfies('ember-source', '>=3.8.0'); | ||
``` | ||
```js | ||
import { dependencySatisfies } from '@embroider/macros'; | ||
let hasNativeArrayHelper = dependencySatisfies('ember-source', '>=3.8.0'); | ||
``` | ||
Compiles to: | ||
Compiles to: | ||
```js | ||
let hasNativeArrayHelper = true; | ||
``` | ||
```js | ||
let hasNativeArrayHelper = true; | ||
``` | ||
* `macroIf(predicate, consequent, alternate)`: a compile time conditional. Lets you choose between two blocks of code and only include one of them. Critically, it will also strip import statements that are used only inside the dead block. The predicate is usually one of the other macros. | ||
- `macroIf(predicate, consequent, alternate)`: a compile time conditional. Lets you choose between two blocks of code and only include one of them. Critically, it will also strip import statements that are used only inside the dead block. The predicate is usually one of the other macros. | ||
This code: | ||
This code: | ||
```js | ||
import { dependencySatisfies, macroIf } from '@embroider/macros'; | ||
import OldComponent from './old-component'; | ||
import NewComponent from './new-component'; | ||
export default macroIf(dependencySatisfies('ember-source', '>=3.8.0'), () => NewComponent, () => OldComponent); | ||
``` | ||
```js | ||
import { dependencySatisfies, macroIf } from '@embroider/macros'; | ||
import OldComponent from './old-component'; | ||
import NewComponent from './new-component'; | ||
export default macroIf( | ||
dependencySatisfies('ember-source', '>=3.8.0'), | ||
() => NewComponent, | ||
() => OldComponent, | ||
); | ||
``` | ||
Will compile to either this: | ||
Will compile to either this: | ||
```js | ||
import NewComponent from './new-component'; | ||
export default NewComponent; | ||
``` | ||
```js | ||
import NewComponent from './new-component'; | ||
export default NewComponent; | ||
``` | ||
Or this: | ||
Or this: | ||
```js | ||
import OldComponent from './old-component'; | ||
export default OldComponent; | ||
``` | ||
```js | ||
import OldComponent from './old-component'; | ||
export default OldComponent; | ||
``` | ||
* `failBuild(message, ...params)`: cause a compile-time build failure. Generally only useful if you put it inside a `macroIf`. All the arguments must be statically analyzable, and they get passed to Node's standard `utils.format()`. | ||
- `failBuild(message, ...params)`: cause a compile-time build failure. Generally only useful if you put it inside a `macroIf`. All the arguments must be statically analyzable, and they get passed to Node's standard `utils.format()`. | ||
```js | ||
@@ -85,6 +80,7 @@ import { macroIf, failBuild, dependencySatisfies } from '@embroider/macros'; | ||
() => true, | ||
() => failBuild("You need to have ember-source >= 3.8.0") | ||
() => failBuild('You need to have ember-source >= 3.8.0') | ||
); | ||
``` | ||
* `importSync(moduleSpecifier)`: exactly like standard ECMA `import()` except instead of returning `Promise<Module>` it returns `Module`. Under Emroider this is interpreted at build-time. Under classic ember-cli it is interpreted at runtime. This exists to provide synchronous & dynamic import. That's not a think ECMA supports, but it's a thing Ember historically has done, so we sometimes need this macro to bridge the worlds. | ||
@@ -95,86 +91,81 @@ ## Template macros | ||
- `macroGetOwnConfig`: works like a helper that pulls values out of your config. For example, assuming you have the config: | ||
- `macroGetOwnConfig`: works like a helper that pulls values out of your config. For example, assuming you have the config: | ||
```json | ||
{ | ||
"items": [ | ||
{ "score": 42 } | ||
] | ||
} | ||
``` | ||
```json | ||
{ | ||
"items": [{ "score": 42 }] | ||
} | ||
``` | ||
Then: | ||
Then: | ||
```hbs | ||
<SomeComponent @score={{macroGetOwnConfig "items" "0" "score" }} /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<SomeComponent @score={{42}} /> | ||
``` | ||
```hbs | ||
<SomeComponent @score={{macroGetOwnConfig "items" "0" "score" }} /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<SomeComponent @score={{42}} /> | ||
``` | ||
If you don't pass any keys, you can get the whole thing (although this makes your template bigger, so use keys when you can): | ||
If you don't pass any keys, you can get the whole thing (although this makes your template bigger, so use keys when you can): | ||
```hbs | ||
<SomeComponent @config={{macroGetOwnConfig}} /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<SomeComponent @config={{hash items=(array (hash score=42))}} /> | ||
``` | ||
```hbs | ||
<SomeComponent @config={{macroGetOwnConfig}} /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<SomeComponent @config={{hash items=(array (hash score=42))}} /> | ||
``` | ||
* `macroGetConfig`: similar to `macroGetOwnConfig`, but takes the name of another package and gets that package's config. We will locate the other package following node_modules rules from your package. Additional extra arguments are treated as property keys just like in the previous examples. | ||
- `macroGetConfig`: similar to `macroGetOwnConfig`, but takes the name of another package and gets that package's config. We will locate the other package following node_modules rules from your package. Additional extra arguments are treated as property keys just like in the previous examples. | ||
```hbs | ||
<SomeComponent @config={{macroGetConfig "liquid-fire"}} /> | ||
``` | ||
```hbs | ||
<SomeComponent @config={{macroGetConfig "liquid-fire"}} /> | ||
``` | ||
* `macroDependencySatisfies` | ||
- `macroDependencySatisfies` | ||
```hbs | ||
<SomeComponent @canAnimate={{macroDependencySatisfies "liquid-fire" "*"}} /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<SomeComponent @canAnimate={{true}} /> | ||
``` | ||
```hbs | ||
<SomeComponent @canAnimate={{macroDependencySatisfies "liquid-fire" "*"}} /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<SomeComponent @canAnimate={{true}} /> | ||
``` | ||
* `macroIf`: Like Ember's own `if`, this can be used in both block form and expresion form. The block form looks like: | ||
- `macroIf`: Like Ember's own `if`, this can be used in both block form and expresion form. The block form looks like: | ||
```hbs | ||
{{#macroIf (macroGetOwnConfig "shouldUseThing") }} | ||
<Thing /> | ||
{{else}} | ||
<OtherThing /> | ||
{{/macroIf}} | ||
```hbs | ||
{{#macroIf (macroGetOwnConfig "shouldUseThing") }} | ||
<Thing /> | ||
{{else}} | ||
<OtherThing /> | ||
{{/macroIf}} | ||
{{! ⬆️compiles to ⬇️ }} | ||
<Thing /> | ||
``` | ||
{{! ⬆️compiles to ⬇️ }} | ||
<Thing /> | ||
``` | ||
The expression form looks like: | ||
The expression form looks like: | ||
```hbs | ||
<div class="box {{macroIf (macroGetOwnConfig "extraModeEnabled") extraClass regularClass}}" /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<div class="box {{extraClass}}"/> | ||
``` | ||
```hbs | ||
<div class="box {{macroIf (macroGetOwnConfig "extraModeEnabled") extraClass regularClass}}" /> | ||
{{! ⬆️compiles to ⬇️ }} | ||
<div class="box {{extraClass}}"/> | ||
``` | ||
- `macroMaybeAttrs`: This macro allows you to include or strip HTML attributes themselves (not just change their values). It works like an element modifier: | ||
```hbs | ||
<div {{macroMaybeAttr (macroGetConfig "ember-test-selectors" "enabled") data-test-here data-test-there=42}} > | ||
{{! ⬆️compiles to either this ⬇️ }} | ||
<div data-test-here data-test-there=42 > | ||
{{! or this ⬇️ }} | ||
<div> | ||
``` | ||
- `macroMaybeAttrs`: This macro allows you to include or strip HTML attributes themselves (not just change their values). It works like an element modifier: | ||
- `macroFailBuild`: cause a compile-time build failure. Generally only useful if you put it inside a `macroIf`. All the arguments must be statically analyzable, and they get passed to Node's standard `utils.format()`. | ||
```hbs | ||
<div {{macroMaybeAttr (macroGetConfig "ember-test-selectors" "enabled") data-test-here data-test-there=42}} > | ||
{{! ⬆️compiles to either this ⬇️ }} | ||
<div data-test-here data-test-there=42 > | ||
{{! or this ⬇️ }} | ||
<div> | ||
``` | ||
```hbs | ||
{{#macroIf (dependencySatisfies "important-thing" ">= 1.0")}} | ||
<UseThing /> | ||
{{else}} | ||
{{macroFailBuild "You need to have import-thing >= 1.0"}} | ||
{{/macroIf}} | ||
``` | ||
- `macroFailBuild`: cause a compile-time build failure. Generally only useful if you put it inside a `macroIf`. All the arguments must be statically analyzable, and they get passed to Node's standard `utils.format()`. | ||
```hbs | ||
{{#macroIf (dependencySatisfies "important-thing" ">= 1.0")}} | ||
<UseThing /> | ||
{{else}} | ||
{{macroFailBuild "You need to have import-thing >= 1.0"}} | ||
{{/macroIf}} | ||
``` | ||
## Setting Configuration: from an Ember app | ||
@@ -185,19 +176,19 @@ | ||
```js | ||
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 | ||
} | ||
} | ||
} | ||
``` | ||
```js | ||
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 | ||
} | ||
} | ||
} | ||
``` | ||
@@ -209,3 +200,3 @@ ## Setting Configuration: from an Ember Addon | ||
```js | ||
```js | ||
module.exports = { | ||
@@ -221,11 +212,9 @@ name: require('./package').name, | ||
// config for some-dependency | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
``` | ||
``` | ||
## Setting Configuration: Low Level API | ||
@@ -239,4 +228,6 @@ | ||
- `import { MacrosConfig } from '@embroider/macros'`: constructs the shared global object that stores config. It has methods for setting configuration and for retrieving the necessary Babel and HTMLBars plugins that will implment the config. See `macros-config.ts` for details. | ||
- `import { MacrosConfig } from '@embroider/macros'`: constructs the shared global object that stores config. It has methods for setting configuration and for retrieving the necessary Babel and HTMLBars plugins that will implment the config. See `macros-config.ts` for details. | ||
``` | ||
``` |
import { NodePath } from '@babel/traverse'; | ||
import { CallExpression } from '@babel/types'; | ||
import { CallExpression, Identifier } from '@babel/types'; | ||
import State from './state'; | ||
@@ -11,4 +11,4 @@ export default function main(): { | ||
CallExpression(path: NodePath<CallExpression>, state: State): void; | ||
ReferencedIdentifier(path: NodePath<import("@babel/types").Node>): void; | ||
ReferencedIdentifier(path: NodePath<Identifier>, state: State): void; | ||
}; | ||
}; |
@@ -6,2 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const types_1 = require("@babel/types"); | ||
const core_1 = require("@embroider/core"); | ||
@@ -21,2 +22,3 @@ const dependency_satisfies_1 = __importDefault(require("./dependency-satisfies")); | ||
state.pendingTasks = []; | ||
state.generatedRequires = new Set(); | ||
}, | ||
@@ -46,4 +48,9 @@ exit(path, state) { | ||
} | ||
if (callee.referencesImport('@embroider/macros', 'importSync')) { | ||
let r = types_1.identifier('require'); | ||
state.generatedRequires.add(r); | ||
callee.replaceWith(r); | ||
} | ||
}, | ||
ReferencedIdentifier(path) { | ||
ReferencedIdentifier(path, state) { | ||
if (path.referencesImport('@embroider/macros', 'dependencySatisfies')) { | ||
@@ -64,2 +71,25 @@ throw error_1.default(path, `You can only use dependencySatisfies as a function call`); | ||
} | ||
if (path.referencesImport('@embroider/macros', 'importSync')) { | ||
throw error_1.default(path, `You can only use importSync as a function call`); | ||
} | ||
if (state.opts.owningPackageRoot) { | ||
// there is only an owningPackageRoot when we are running inside a | ||
// classic ember-cli build. In the embroider stage3 build, there is no | ||
// owning package root because we're compiling *all* packages | ||
// simultaneously. | ||
// | ||
// given that we're inside classic ember-cli, stop here without trying | ||
// to require bare `require`. It's not needed, because both our | ||
// `importSync` and any user-written bare `require` can both mean the | ||
// same thing: runtime AMD `require`. | ||
return; | ||
} | ||
if (path.node.name === 'require' && | ||
!state.generatedRequires.has(path.node) && | ||
!path.scope.hasBinding('require')) { | ||
// Our importSync macro has been compiled to `require`. But we want to | ||
// distinguish that from any pre-existing, user-written `require`, which | ||
// should retain its *runtime* meaning. | ||
path.replaceWith(types_1.memberExpression(types_1.identifier('window'), path.node)); | ||
} | ||
}, | ||
@@ -66,0 +96,0 @@ }; |
import { NodePath } from '@babel/traverse'; | ||
import { ImportDeclaration, CallExpression } from '@babel/types'; | ||
import { ImportDeclaration, CallExpression, Identifier, memberExpression, identifier } from '@babel/types'; | ||
import { PackageCache } from '@embroider/core'; | ||
@@ -20,2 +20,3 @@ import State from './state'; | ||
state.pendingTasks = []; | ||
state.generatedRequires = new Set(); | ||
}, | ||
@@ -45,4 +46,9 @@ exit(path: NodePath, state: State) { | ||
} | ||
if (callee.referencesImport('@embroider/macros', 'importSync')) { | ||
let r = identifier('require'); | ||
state.generatedRequires.add(r); | ||
callee.replaceWith(r); | ||
} | ||
}, | ||
ReferencedIdentifier(path: NodePath) { | ||
ReferencedIdentifier(path: NodePath<Identifier>, state: State) { | ||
if (path.referencesImport('@embroider/macros', 'dependencySatisfies')) { | ||
@@ -63,2 +69,29 @@ throw error(path, `You can only use dependencySatisfies as a function call`); | ||
} | ||
if (path.referencesImport('@embroider/macros', 'importSync')) { | ||
throw error(path, `You can only use importSync as a function call`); | ||
} | ||
if (state.opts.owningPackageRoot) { | ||
// there is only an owningPackageRoot when we are running inside a | ||
// classic ember-cli build. In the embroider stage3 build, there is no | ||
// owning package root because we're compiling *all* packages | ||
// simultaneously. | ||
// | ||
// given that we're inside classic ember-cli, stop here without trying | ||
// to require bare `require`. It's not needed, because both our | ||
// `importSync` and any user-written bare `require` can both mean the | ||
// same thing: runtime AMD `require`. | ||
return; | ||
} | ||
if ( | ||
path.node.name === 'require' && | ||
!state.generatedRequires.has(path.node) && | ||
!path.scope.hasBinding('require') | ||
) { | ||
// Our importSync macro has been compiled to `require`. But we want to | ||
// distinguish that from any pre-existing, user-written `require`, which | ||
// should retain its *runtime* meaning. | ||
path.replaceWith(memberExpression(identifier('window'), path.node)); | ||
} | ||
}, | ||
@@ -65,0 +98,0 @@ }; |
@@ -1,5 +0,6 @@ | ||
import { NodePath } from '@babel/traverse'; | ||
import { NodePath, Node } from '@babel/traverse'; | ||
export default interface State { | ||
removed: NodePath[]; | ||
pendingTasks: (() => void)[]; | ||
generatedRequires: Set<Node>; | ||
opts: { | ||
@@ -6,0 +7,0 @@ userConfigs: { |
@@ -1,2 +0,2 @@ | ||
import { NodePath } from '@babel/traverse'; | ||
import { NodePath, Node } from '@babel/traverse'; | ||
@@ -6,2 +6,3 @@ export default interface State { | ||
pendingTasks: (() => void)[]; | ||
generatedRequires: Set<Node>; | ||
opts: { | ||
@@ -8,0 +9,0 @@ userConfigs: { |
@@ -5,2 +5,3 @@ import { PluginItem } from '@babel/core'; | ||
static shared(): MacrosConfig; | ||
static reset(): void; | ||
private configs; | ||
@@ -7,0 +8,0 @@ private mergers; |
@@ -39,2 +39,7 @@ "use strict"; | ||
} | ||
static reset() { | ||
this.shared().configs.clear(); | ||
this.shared().mergers.clear(); | ||
localSharedState = undefined; | ||
} | ||
// Registers a new source of configuration to be given to the named package. | ||
@@ -41,0 +46,0 @@ // Your config type must be json-serializable. You must always set fromPath to |
@@ -38,2 +38,8 @@ import { join } from 'path'; | ||
static reset() { | ||
this.shared().configs.clear(); | ||
this.shared().mergers.clear(); | ||
localSharedState = undefined; | ||
} | ||
private configs: Map<string, unknown[]> = new Map(); | ||
@@ -40,0 +46,0 @@ private mergers: Map<string, { merger: Merger; fromPath: string }> = new Map(); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
131727
2272
74
227
+ Added@embroider/core@0.4.0(transitive)
- Removed@embroider/core@0.3.5(transitive)
Updated@embroider/core@0.4.0