@norith/glimmerx-babel-preset 1.0.8 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js
CHANGED
|
@@ -2,6 +2,20 @@ function defaultTo(value, defaultVal) {
|
|
|
2
2
|
return value === undefined ? defaultVal : value;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
// The preserve-scope plugin MUST be added as a top-level plugin in the
|
|
6
|
+
// consumer's babel config, not inside this preset. See
|
|
7
|
+
// ./preserve-scope-plugin.js for the reason. We cannot wire it in here
|
|
8
|
+
// because presets expand in reverse array order — any plugin inside this
|
|
9
|
+
// preset would run AFTER @babel/preset-typescript's import stripper, which
|
|
10
|
+
// is too late to prevent template-only imports from being removed.
|
|
11
|
+
//
|
|
12
|
+
// Consumers must add it like so:
|
|
13
|
+
//
|
|
14
|
+
// plugins: [
|
|
15
|
+
// require('@glimmerx/babel-preset/preserve-scope-plugin'),
|
|
16
|
+
// // ...their other plugins
|
|
17
|
+
// ],
|
|
18
|
+
|
|
5
19
|
module.exports = function (api, options) {
|
|
6
20
|
let __loadPlugins = defaultTo(options.__loadPlugins, false);
|
|
7
21
|
|
|
@@ -44,3 +58,9 @@ module.exports = function (api, options) {
|
|
|
44
58
|
],
|
|
45
59
|
};
|
|
46
60
|
};
|
|
61
|
+
|
|
62
|
+
// Named re-export so consumers can do:
|
|
63
|
+
// const { preserveScopePlugin } = require('@glimmerx/babel-preset');
|
|
64
|
+
// as an alternative to:
|
|
65
|
+
// require('@glimmerx/babel-preset/preserve-scope-plugin')
|
|
66
|
+
module.exports.preserveScopePlugin = require('./preserve-scope-plugin');
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@norith/glimmerx-babel-preset",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "GlimmerX Babel Preset",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/snorith/glimmer-experimental"
|
|
8
|
+
},
|
|
6
9
|
"author": "Tom Dale <tom@tomdale.net>",
|
|
7
10
|
"license": "MIT",
|
|
8
11
|
"private": false,
|
|
@@ -10,7 +13,7 @@
|
|
|
10
13
|
"test": "mocha -r esm"
|
|
11
14
|
},
|
|
12
15
|
"dependencies": {
|
|
13
|
-
"@norith/glimmer-babel-preset": "^1.0.
|
|
16
|
+
"@norith/glimmer-babel-preset": "^1.0.2"
|
|
14
17
|
},
|
|
15
18
|
"devDependencies": {
|
|
16
19
|
"babel-plugin-tester": "^6.4.0",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Preserves top-level bindings through the babel pass so that other transforms
|
|
2
|
+
// (notably @babel/preset-typescript's unused-import stripper) do not remove
|
|
3
|
+
// imports that are referenced only from inside an `hbs` tagged template string.
|
|
4
|
+
//
|
|
5
|
+
// Babel's scope tracker cannot see identifiers inside template literal strings,
|
|
6
|
+
// so a bare `import X from './X'` whose only use is `hbs\`<X />\`` looks
|
|
7
|
+
// unused to preset-typescript and gets stripped. By the time
|
|
8
|
+
// babel-plugin-htmlbars-inline-precompile walks the template and calls
|
|
9
|
+
// getScope(path.scope), the binding is already gone — the template compiles
|
|
10
|
+
// with no locals and fails with `not in scope in a strict mode template`.
|
|
11
|
+
//
|
|
12
|
+
// This plugin runs first in the pass. On Program.enter it calls
|
|
13
|
+
// binding.reference(noop) for every top-level binding, which bumps each
|
|
14
|
+
// binding's reference count to >= 1 from preset-typescript's perspective, so
|
|
15
|
+
// preset-typescript leaves the imports alone. By the time
|
|
16
|
+
// babel-plugin-htmlbars-inline-precompile visits the `hbs` tag and emits the
|
|
17
|
+
// scope closure (`scope: () => [X, Y]`), it sees the bindings intact and
|
|
18
|
+
// references them for real inside the closure. On Program.exit we remove the
|
|
19
|
+
// phantom references and the noop placeholder — the real references inside
|
|
20
|
+
// the scope closure remain and carry the imports through tree-shaking.
|
|
21
|
+
//
|
|
22
|
+
// Ported from the removed @glimmerx/babel-plugin-component-templates (which
|
|
23
|
+
// used the same mechanism until the upstream consolidation around
|
|
24
|
+
// babel-plugin-htmlbars-inline-precompile dropped it).
|
|
25
|
+
|
|
26
|
+
module.exports = function preserveScopePlugin({ types: t }) {
|
|
27
|
+
return {
|
|
28
|
+
name: '@norith/glimmerx-babel-preset/preserve-scope',
|
|
29
|
+
visitor: {
|
|
30
|
+
Program: {
|
|
31
|
+
enter(path, state) {
|
|
32
|
+
const parentScope = path.scope.getProgramParent();
|
|
33
|
+
|
|
34
|
+
// Skip TS type-only bindings — we don't need to keep those alive and
|
|
35
|
+
// marking them would interfere with preset-typescript's type-only
|
|
36
|
+
// import handling.
|
|
37
|
+
const bindings = Object.values(parentScope.bindings).filter(
|
|
38
|
+
(b) => !b.referencePaths.some((p) => p.parent && p.parent.type === 'TSTypeReference')
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (bindings.length === 0) return;
|
|
42
|
+
|
|
43
|
+
// Insert a noop at the top of the program to anchor the phantom refs.
|
|
44
|
+
let firstNode = path.get('body.0');
|
|
45
|
+
firstNode.insertBefore(t.noop());
|
|
46
|
+
firstNode = path.get('body.0');
|
|
47
|
+
|
|
48
|
+
bindings.forEach((b) => b.reference(firstNode));
|
|
49
|
+
|
|
50
|
+
state.originalBindings = bindings;
|
|
51
|
+
state.emptyPath = firstNode;
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
exit(path, state) {
|
|
55
|
+
if (state.originalBindings) {
|
|
56
|
+
state.originalBindings.forEach((b) => b.dereference(state.emptyPath));
|
|
57
|
+
state.emptyPath.remove();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Reproduces the consumer pipeline that triggered the bug:
|
|
2
|
+
// @babel/preset-typescript strips PlainAwait/on (referenced only inside an
|
|
3
|
+
// `hbs` tagged template literal) before babel-plugin-htmlbars-inline-precompile
|
|
4
|
+
// can extract the template scope. The preserveScopePlugin top-level plugin
|
|
5
|
+
// fixes this by phantom-referencing every top-level binding through the pass.
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
plugins: [
|
|
10
|
+
require(path.resolve(__dirname, '../../../preserve-scope-plugin')),
|
|
11
|
+
],
|
|
12
|
+
presets: [
|
|
13
|
+
['@glimmerx/babel-preset', { isDebug: false }],
|
|
14
|
+
['@babel/preset-typescript', { allowDeclareFields: true }],
|
|
15
|
+
],
|
|
16
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Component, { hbs } from '@norith/glimmerx-component';
|
|
2
|
+
import { on } from '@norith/glimmerx-modifier';
|
|
3
|
+
import PlainAwait from 'components/PlainAwait';
|
|
4
|
+
|
|
5
|
+
export default class Foo extends Component {
|
|
6
|
+
go() {}
|
|
7
|
+
static template = hbs`<PlainAwait /><button {{on 'click' this.go}}>Go</button>`;
|
|
8
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { setComponentTemplate as _setComponentTemplate } from "@norith/glimmer-core";
|
|
2
|
+
import { createTemplateFactory as _createTemplateFactory } from "@norith/glimmer-core";
|
|
3
|
+
import Component from '@norith/glimmerx-component';
|
|
4
|
+
import { on } from '@norith/glimmerx-modifier';
|
|
5
|
+
import PlainAwait from 'components/PlainAwait';
|
|
6
|
+
export default class Foo extends Component {
|
|
7
|
+
go() {}
|
|
8
|
+
}
|
|
9
|
+
_setComponentTemplate(_createTemplateFactory(
|
|
10
|
+
/*
|
|
11
|
+
<PlainAwait /><button {{on 'click' this.go}}>Go</button>
|
|
12
|
+
*/
|
|
13
|
+
{
|
|
14
|
+
"id": "3B6tIOMC",
|
|
15
|
+
"block": "[[[8,[32,0],null,null,null],[11,\"button\"],[4,[32,1],[\"click\",[30,0,[\"go\"]]],null],[12],[1,\"Go\"],[13]],[],false,[]]",
|
|
16
|
+
"moduleName": "(unknown template module)",
|
|
17
|
+
"scope": () => [PlainAwait, on],
|
|
18
|
+
"isStrictMode": true
|
|
19
|
+
}), Foo);
|