@embroider/compat 2.1.1 → 2.1.2-unstable.1ecd57e
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/package.json +9 -5
- package/src/addon-dependency-rules/ember-data.js +1 -0
- package/src/addon-dependency-rules/ember-data.js.map +1 -1
- package/src/audit/babel-visitor.d.ts +1 -0
- package/src/audit/babel-visitor.js +2 -2
- package/src/audit/babel-visitor.js.map +1 -1
- package/src/audit/build.js +48 -46
- package/src/audit/build.js.map +1 -1
- package/src/audit/options.d.ts +3 -2
- package/src/audit/options.js.map +1 -1
- package/src/audit-cli.js +0 -0
- package/src/audit.d.ts +25 -1
- package/src/audit.js +55 -33
- package/src/audit.js.map +1 -1
- package/src/babel-plugin-adjust-imports.d.ts +16 -0
- package/src/babel-plugin-adjust-imports.js +198 -0
- package/src/babel-plugin-adjust-imports.js.map +1 -0
- package/src/compat-adapters/active-model-adapter.d.ts +1 -1
- package/src/compat-adapters/ember-asset-loader.d.ts +1 -1
- package/src/compat-adapters/ember-cli-addon-docs.d.ts +1 -1
- package/src/compat-adapters/ember-data.d.ts +1 -0
- package/src/compat-adapters/ember-data.js +4 -0
- package/src/compat-adapters/ember-data.js.map +1 -1
- package/src/compat-adapters/ember-decorators.d.ts +1 -1
- package/src/compat-adapters/ember-get-config.d.ts +1 -1
- package/src/compat-adapters/ember-macro-helpers.d.ts +1 -1
- package/src/compat-adapters/ember-percy.d.ts +1 -1
- package/src/compat-adapters/ember-source.d.ts +1 -1
- package/src/compat-adapters/ember-svg-jar.d.ts +1 -1
- package/src/compat-adapters/ember-test-selectors.d.ts +1 -1
- package/src/compat-app.js +41 -77
- package/src/compat-app.js.map +1 -1
- package/src/dependency-rules.d.ts +12 -9
- package/src/dependency-rules.js +21 -37
- package/src/dependency-rules.js.map +1 -1
- package/src/modules-compat.js.map +1 -1
- package/src/resolver-transform.d.ts +12 -5
- package/src/resolver-transform.js +717 -320
- package/src/resolver-transform.js.map +1 -1
- package/src/v1-addon.js +1 -0
- package/src/v1-addon.js.map +1 -1
- package/src/v1-app.js +2 -2
- package/src/v1-app.js.map +1 -1
- package/src/audit/capture.d.ts +0 -8
- package/src/audit/capture.js +0 -45
- package/src/audit/capture.js.map +0 -1
- package/src/resolver.d.ts +0 -128
- package/src/resolver.js +0 -677
- package/src/resolver.js.map +0 -1
- package/src/template-compiler-broccoli-plugin.d.ts +0 -10
- package/src/template-compiler-broccoli-plugin.js +0 -28
- package/src/template-compiler-broccoli-plugin.js.map +0 -1
|
@@ -1,152 +1,170 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
2
8
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
10
|
};
|
|
5
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
|
|
12
|
+
exports.builtInKeywords = void 0;
|
|
13
|
+
const dependency_rules_1 = require("./dependency-rules");
|
|
14
|
+
const typescript_memoize_1 = require("typescript-memoize");
|
|
7
15
|
const assert_never_1 = __importDefault(require("assert-never"));
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const fs_extra_1 = require("fs-extra");
|
|
18
|
+
const dasherize_component_name_1 = require("./dasherize-component-name");
|
|
19
|
+
const core_1 = require("@embroider/core");
|
|
20
|
+
const lodash_1 = require("lodash");
|
|
21
|
+
exports.builtInKeywords = [
|
|
22
|
+
'-get-dynamic-var',
|
|
23
|
+
'-in-element',
|
|
24
|
+
'-with-dynamic-vars',
|
|
25
|
+
'action',
|
|
26
|
+
'array',
|
|
27
|
+
'component',
|
|
28
|
+
'concat',
|
|
29
|
+
'debugger',
|
|
30
|
+
'each-in',
|
|
31
|
+
'each',
|
|
32
|
+
'fn',
|
|
33
|
+
'get',
|
|
34
|
+
'has-block-params',
|
|
35
|
+
'has-block',
|
|
36
|
+
'hasBlock',
|
|
37
|
+
'hasBlockParams',
|
|
38
|
+
'hash',
|
|
39
|
+
'helper',
|
|
40
|
+
'if',
|
|
41
|
+
'in-element',
|
|
42
|
+
'input',
|
|
43
|
+
'let',
|
|
44
|
+
'link-to',
|
|
45
|
+
'loc',
|
|
46
|
+
'log',
|
|
47
|
+
'modifier',
|
|
48
|
+
'mount',
|
|
49
|
+
'mut',
|
|
50
|
+
'on',
|
|
51
|
+
'outlet',
|
|
52
|
+
'partial',
|
|
53
|
+
'query-params',
|
|
54
|
+
'readonly',
|
|
55
|
+
'textarea',
|
|
56
|
+
'unbound',
|
|
57
|
+
'unique-id',
|
|
58
|
+
'unless',
|
|
59
|
+
'with',
|
|
60
|
+
'yield',
|
|
61
|
+
];
|
|
62
|
+
class TemplateResolver {
|
|
63
|
+
constructor(env, config) {
|
|
64
|
+
this.env = env;
|
|
65
|
+
this.config = config;
|
|
66
|
+
this.name = 'embroider-build-time-resolver';
|
|
67
|
+
this.scopeStack = new ScopeStack();
|
|
68
|
+
this.visitor = {
|
|
69
|
+
Template: {
|
|
70
|
+
enter: () => {
|
|
71
|
+
if (this.env.locals) {
|
|
72
|
+
this.scopeStack.pushMustacheBlock(this.env.locals);
|
|
45
73
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
emittedLexicalBindings.set(name, resolution);
|
|
51
|
-
setter(parentPath.node, builders.path(name));
|
|
74
|
+
},
|
|
75
|
+
exit: () => {
|
|
76
|
+
if (this.env.locals) {
|
|
77
|
+
this.scopeStack.pop();
|
|
52
78
|
}
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
Block: {
|
|
82
|
+
enter: node => {
|
|
83
|
+
this.scopeStack.pushMustacheBlock(node.blockParams);
|
|
84
|
+
},
|
|
85
|
+
exit: () => {
|
|
86
|
+
this.scopeStack.pop();
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
BlockStatement: (node, path) => {
|
|
90
|
+
if (node.path.type !== 'PathExpression') {
|
|
53
91
|
return;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
58
|
-
emittedLexicalBindings.set(name, resolution);
|
|
59
|
-
setter(parentPath.node, builders.path(name));
|
|
92
|
+
}
|
|
93
|
+
let rootName = node.path.parts[0];
|
|
94
|
+
if (this.scopeStack.inScope(rootName, path)) {
|
|
60
95
|
return;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// polaris-style first-class templates, we see only JS files for their
|
|
64
|
-
// components, because the template association is handled before
|
|
65
|
-
// we're doing any resolving here. In that case, we can safely do
|
|
66
|
-
// component invocation via lexical scope.
|
|
67
|
-
//
|
|
68
|
-
// But when people are using the older non-co-located template style,
|
|
69
|
-
// we can't safely do that -- ember needs to discover both the
|
|
70
|
-
// component and the template in the AMD loader to associate them. In
|
|
71
|
-
// that case, we emit just-in-time AMD definitions for them.
|
|
72
|
-
if (resolution.jsModule && !resolution.hbsModule) {
|
|
73
|
-
let name = jsutils.bindImport(resolution.jsModule.path, 'default', parentPath, {
|
|
74
|
-
nameHint: resolution.nameHint,
|
|
75
|
-
});
|
|
76
|
-
emittedLexicalBindings.set(name, resolution);
|
|
77
|
-
setter(parentPath.node, builders.path(name));
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
emitAMD(resolution.hbsModule);
|
|
81
|
-
emitAMD(resolution.jsModule);
|
|
82
|
-
}
|
|
83
|
-
case undefined:
|
|
96
|
+
}
|
|
97
|
+
if (node.path.this === true) {
|
|
84
98
|
return;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
99
|
+
}
|
|
100
|
+
if (node.path.parts.length > 1) {
|
|
101
|
+
// paths with a dot in them (which therefore split into more than
|
|
102
|
+
// one "part") are classically understood by ember to be contextual
|
|
103
|
+
// components, which means there's nothing to resolve at this
|
|
104
|
+
// location.
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (node.path.original === 'component' && node.params.length > 0) {
|
|
108
|
+
let resolution = this.handleComponentHelper(node.params[0]);
|
|
109
|
+
this.emit(path, resolution, (node, newIdentifier) => {
|
|
110
|
+
node.params[0] = newIdentifier;
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
let resolution = this.targetComponent(node.path.original);
|
|
115
|
+
this.emit(path, resolution, (node, newId) => {
|
|
116
|
+
node.path = newId;
|
|
98
117
|
});
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
argumentName: name,
|
|
118
|
+
if ((resolution === null || resolution === void 0 ? void 0 : resolution.type) === 'component') {
|
|
119
|
+
this.scopeStack.enteringComponentBlock(resolution, ({ argumentsAreComponents }) => {
|
|
120
|
+
this.handleDynamicComponentArguments(rootName, argumentsAreComponents, extendPath(extendPath(path, 'hash'), 'pairs'));
|
|
103
121
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
SubExpression: (node, path) => {
|
|
125
|
+
if (node.path.type !== 'PathExpression') {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (node.path.this === true) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (this.scopeStack.inScope(node.path.parts[0], path)) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (node.path.original === 'component' && node.params.length > 0) {
|
|
135
|
+
let resolution = this.handleComponentHelper(node.params[0]);
|
|
136
|
+
this.emit(path, resolution, (node, newId) => {
|
|
137
|
+
node.params[0] = newId;
|
|
111
138
|
});
|
|
139
|
+
return;
|
|
112
140
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
BlockStatement(node, path) {
|
|
141
|
+
if (node.path.original === 'helper' && node.params.length > 0) {
|
|
142
|
+
let resolution = this.handleDynamicHelper(node.params[0]);
|
|
143
|
+
this.emit(path, resolution, (node, newId) => {
|
|
144
|
+
node.params[0] = newId;
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (node.path.original === 'modifier' && node.params.length > 0) {
|
|
149
|
+
let resolution = this.handleDynamicModifier(node.params[0]);
|
|
150
|
+
this.emit(path, resolution, (node, newId) => {
|
|
151
|
+
node.params[0] = newId;
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
let resolution = this.targetHelper(node.path.original);
|
|
156
|
+
this.emit(path, resolution, (node, newId) => {
|
|
157
|
+
node.path = newId;
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
MustacheStatement: {
|
|
161
|
+
enter: (node, path) => {
|
|
162
|
+
var _a;
|
|
139
163
|
if (node.path.type !== 'PathExpression') {
|
|
140
164
|
return;
|
|
141
165
|
}
|
|
142
166
|
let rootName = node.path.parts[0];
|
|
143
|
-
if (scopeStack.inScope(rootName)) {
|
|
144
|
-
let resolution = emittedLexicalBindings.get(rootName);
|
|
145
|
-
if ((resolution === null || resolution === void 0 ? void 0 : resolution.type) === 'component') {
|
|
146
|
-
scopeStack.enteringComponentBlock(resolution, ({ argumentsAreComponents }) => {
|
|
147
|
-
handleDynamicComponentArguments(rootName, argumentsAreComponents, extendPath(extendPath(path, 'hash'), 'pairs'));
|
|
148
|
-
});
|
|
149
|
-
}
|
|
167
|
+
if (this.scopeStack.inScope(rootName, path)) {
|
|
150
168
|
return;
|
|
151
169
|
}
|
|
152
170
|
if (node.path.this === true) {
|
|
@@ -159,161 +177,556 @@ function makeResolverTransform({ resolver, patchHelpersBug }) {
|
|
|
159
177
|
// location.
|
|
160
178
|
return;
|
|
161
179
|
}
|
|
162
|
-
if (node.path.original
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
// a block counts as args from our perpsective (it's enough to prove
|
|
170
|
-
// this thing must be a component, not content)
|
|
171
|
-
let hasArgs = true;
|
|
172
|
-
let resolution = resolver.resolveMustache(node.path.original, hasArgs, filename, node.path.loc);
|
|
173
|
-
emit(path, resolution, (node, newId) => {
|
|
174
|
-
node.path = newId;
|
|
175
|
-
});
|
|
176
|
-
if ((resolution === null || resolution === void 0 ? void 0 : resolution.type) === 'component') {
|
|
177
|
-
scopeStack.enteringComponentBlock(resolution, ({ argumentsAreComponents }) => {
|
|
178
|
-
handleDynamicComponentArguments(rootName, argumentsAreComponents, extendPath(extendPath(path, 'hash'), 'pairs'));
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
SubExpression(node, path) {
|
|
183
|
-
if (node.path.type !== 'PathExpression') {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
if (node.path.this === true) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (scopeStack.inScope(node.path.parts[0])) {
|
|
180
|
+
if (node.path.original.startsWith('@')) {
|
|
181
|
+
// similarly, global resolution of helpers and components never
|
|
182
|
+
// happens with argument paths (it could still be an invocation, but
|
|
183
|
+
// it would be a lexically-scoped invocation, not one we need to
|
|
184
|
+
// adjust)
|
|
190
185
|
return;
|
|
191
186
|
}
|
|
192
187
|
if (node.path.original === 'component' && node.params.length > 0) {
|
|
193
|
-
let resolution = handleComponentHelper(node.params[0]
|
|
194
|
-
emit(path, resolution, (node, newId) => {
|
|
188
|
+
let resolution = this.handleComponentHelper(node.params[0]);
|
|
189
|
+
this.emit(path, resolution, (node, newId) => {
|
|
195
190
|
node.params[0] = newId;
|
|
196
191
|
});
|
|
197
192
|
return;
|
|
198
193
|
}
|
|
199
194
|
if (node.path.original === 'helper' && node.params.length > 0) {
|
|
200
|
-
handleDynamicHelper(node.params[0]
|
|
195
|
+
let resolution = this.handleDynamicHelper(node.params[0]);
|
|
196
|
+
this.emit(path, resolution, (node, newIdentifier) => {
|
|
197
|
+
node.params[0] = newIdentifier;
|
|
198
|
+
});
|
|
201
199
|
return;
|
|
202
200
|
}
|
|
203
|
-
if (
|
|
204
|
-
|
|
201
|
+
if (((_a = path.parent) === null || _a === void 0 ? void 0 : _a.node.type) === 'AttrNode') {
|
|
202
|
+
// this mustache is the value of an attribute. Components aren't
|
|
203
|
+
// allowed here, so we're not ambiguous, so resolve a helper.
|
|
204
|
+
let resolution = this.targetHelper(node.path.original);
|
|
205
|
+
this.emit(path, resolution, (node, newIdentifier) => {
|
|
206
|
+
node.path = newIdentifier;
|
|
207
|
+
});
|
|
205
208
|
return;
|
|
206
209
|
}
|
|
207
|
-
let
|
|
208
|
-
|
|
209
|
-
|
|
210
|
+
let hasArgs = node.params.length > 0 || node.hash.pairs.length > 0;
|
|
211
|
+
let resolution = this.targetHelperOrComponent(node.path.original, node.path.loc, hasArgs);
|
|
212
|
+
this.emit(path, resolution, (node, newIdentifier) => {
|
|
213
|
+
node.path = newIdentifier;
|
|
210
214
|
});
|
|
215
|
+
if ((resolution === null || resolution === void 0 ? void 0 : resolution.type) === 'component') {
|
|
216
|
+
this.handleDynamicComponentArguments(node.path.original, resolution.argumentsAreComponents, extendPath(extendPath(path, 'hash'), 'pairs'));
|
|
217
|
+
}
|
|
211
218
|
},
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
219
|
+
},
|
|
220
|
+
ElementModifierStatement: (node, path) => {
|
|
221
|
+
if (node.path.type !== 'PathExpression') {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (this.scopeStack.inScope(node.path.parts[0], path)) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (node.path.this === true) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (node.path.data === true) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (node.path.parts.length > 1) {
|
|
234
|
+
// paths with a dot in them (which therefore split into more than
|
|
235
|
+
// one "part") are classically understood by ember to be contextual
|
|
236
|
+
// components. With the introduction of `Template strict mode` in Ember 3.25
|
|
237
|
+
// it is also possible to pass modifiers this way which means there's nothing
|
|
238
|
+
// to resolve at this location.
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
let resolution = this.targetElementModifier(node.path.original);
|
|
242
|
+
this.emit(path, resolution, (node, newId) => {
|
|
243
|
+
node.path = newId;
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
ElementNode: {
|
|
247
|
+
enter: (node, path) => {
|
|
248
|
+
let rootName = node.tag.split('.')[0];
|
|
249
|
+
if (!this.scopeStack.inScope(rootName, path)) {
|
|
250
|
+
let resolution = null;
|
|
251
|
+
// if it starts with lower case, it can't be a component we need to
|
|
252
|
+
// globally resolve
|
|
253
|
+
if (node.tag[0] !== node.tag[0].toLowerCase()) {
|
|
254
|
+
resolution = this.targetComponent((0, dasherize_component_name_1.dasherize)(node.tag));
|
|
245
255
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
emit(path, resolution, (node, newIdentifier) => {
|
|
249
|
-
node.path = newIdentifier;
|
|
256
|
+
this.emit(path, resolution, (node, newId) => {
|
|
257
|
+
node.tag = newId.original;
|
|
250
258
|
});
|
|
251
259
|
if ((resolution === null || resolution === void 0 ? void 0 : resolution.type) === 'component') {
|
|
252
|
-
|
|
260
|
+
this.scopeStack.enteringComponentBlock(resolution, ({ argumentsAreComponents }) => {
|
|
261
|
+
this.handleDynamicComponentArguments(node.tag, argumentsAreComponents, extendPath(path, 'attributes'));
|
|
262
|
+
});
|
|
253
263
|
}
|
|
254
|
-
},
|
|
255
|
-
},
|
|
256
|
-
ElementModifierStatement(node, path) {
|
|
257
|
-
if (node.path.type !== 'PathExpression') {
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (scopeStack.inScope(node.path.parts[0])) {
|
|
261
|
-
return;
|
|
262
264
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
this.scopeStack.pushElementBlock(node.blockParams, node);
|
|
266
|
+
},
|
|
267
|
+
exit: () => {
|
|
268
|
+
this.scopeStack.pop();
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
this.moduleResolver = new core_1.Resolver(config);
|
|
273
|
+
if (globalThis.embroider_audit) {
|
|
274
|
+
this.auditHandler = globalThis.embroider_audit;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
emit(parentPath, resolution, setter) {
|
|
278
|
+
switch (resolution === null || resolution === void 0 ? void 0 : resolution.type) {
|
|
279
|
+
case 'error':
|
|
280
|
+
this.reportError(resolution);
|
|
281
|
+
return;
|
|
282
|
+
case 'component':
|
|
283
|
+
case 'modifier':
|
|
284
|
+
case 'helper': {
|
|
285
|
+
let name = this.env.meta.jsutils.bindImport(resolution.specifier, 'default', parentPath, {
|
|
286
|
+
nameHint: resolution.nameHint,
|
|
287
|
+
});
|
|
288
|
+
setter(parentPath.node, this.env.syntax.builders.path(name));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
case undefined:
|
|
292
|
+
return;
|
|
293
|
+
default:
|
|
294
|
+
(0, assert_never_1.default)(resolution);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
reportError(dep) {
|
|
298
|
+
if (!this.auditHandler && !this.config.options.allowUnsafeDynamicComponents) {
|
|
299
|
+
let e = new Error(`${dep.message}: ${dep.detail} in ${this.humanReadableFile(this.env.filename)}`);
|
|
300
|
+
e.isTemplateResolverError = true;
|
|
301
|
+
e.loc = dep.loc;
|
|
302
|
+
e.moduleName = this.env.filename;
|
|
303
|
+
throw e;
|
|
304
|
+
}
|
|
305
|
+
if (this.auditHandler) {
|
|
306
|
+
this.auditHandler({
|
|
307
|
+
message: dep.message,
|
|
308
|
+
filename: this.env.filename,
|
|
309
|
+
detail: dep.detail,
|
|
310
|
+
loc: dep.loc,
|
|
311
|
+
source: this.env.contents,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
humanReadableFile(file) {
|
|
316
|
+
let { appRoot } = this.config;
|
|
317
|
+
if (!appRoot.endsWith(path_1.sep)) {
|
|
318
|
+
appRoot += path_1.sep;
|
|
319
|
+
}
|
|
320
|
+
if (file.startsWith(appRoot)) {
|
|
321
|
+
return file.slice(appRoot.length);
|
|
322
|
+
}
|
|
323
|
+
return file;
|
|
324
|
+
}
|
|
325
|
+
handleComponentHelper(param, impliedBecause) {
|
|
326
|
+
let locator;
|
|
327
|
+
switch (param.type) {
|
|
328
|
+
case 'StringLiteral':
|
|
329
|
+
locator = { type: 'literal', path: param.value };
|
|
330
|
+
break;
|
|
331
|
+
case 'PathExpression':
|
|
332
|
+
locator = { type: 'path', path: param.original };
|
|
333
|
+
break;
|
|
334
|
+
case 'MustacheStatement':
|
|
335
|
+
if (param.hash.pairs.length === 0 && param.params.length === 0) {
|
|
336
|
+
return this.handleComponentHelper(param.path, impliedBecause);
|
|
337
|
+
}
|
|
338
|
+
else if (param.path.type === 'PathExpression' && param.path.original === 'component') {
|
|
339
|
+
// safe because we will handle this inner `{{component ...}}` mustache on its own
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
locator = { type: 'other' };
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
case 'TextNode':
|
|
347
|
+
locator = { type: 'literal', path: param.chars };
|
|
348
|
+
break;
|
|
349
|
+
case 'SubExpression':
|
|
350
|
+
if (param.path.type === 'PathExpression' && param.path.original === 'component') {
|
|
351
|
+
// safe because we will handle this inner `(component ...)` subexpression on its own
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
if (param.path.type === 'PathExpression' && param.path.original === 'ensure-safe-component') {
|
|
355
|
+
// safe because we trust ensure-safe-component
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
locator = { type: 'other' };
|
|
359
|
+
break;
|
|
360
|
+
default:
|
|
361
|
+
locator = { type: 'other' };
|
|
362
|
+
}
|
|
363
|
+
if (locator.type === 'path' && this.scopeStack.safeComponentInScope(locator.path)) {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
return this.targetComponentHelper(locator, param.loc, impliedBecause);
|
|
367
|
+
}
|
|
368
|
+
handleDynamicComponentArguments(componentName, argumentsAreComponents, attributes) {
|
|
369
|
+
for (let name of argumentsAreComponents) {
|
|
370
|
+
let attr = attributes.find(attr => {
|
|
371
|
+
if (attr.node.type === 'AttrNode') {
|
|
372
|
+
return attr.node.name === '@' + name;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
return attr.node.key === name;
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
if (attr) {
|
|
379
|
+
let resolution = this.handleComponentHelper(attr.node.value, {
|
|
380
|
+
componentName,
|
|
381
|
+
argumentName: name,
|
|
382
|
+
});
|
|
383
|
+
this.emit(attr, resolution, (node, newId) => {
|
|
384
|
+
if (node.type === 'AttrNode') {
|
|
385
|
+
node.value = this.env.syntax.builders.mustache(newId);
|
|
268
386
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// one "part") are classically understood by ember to be contextual
|
|
272
|
-
// components. With the introduction of `Template strict mode` in Ember 3.25
|
|
273
|
-
// it is also possible to pass modifiers this way which means there's nothing
|
|
274
|
-
// to resolve at this location.
|
|
275
|
-
return;
|
|
387
|
+
else {
|
|
388
|
+
node.value = newId;
|
|
276
389
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
get staticComponentsEnabled() {
|
|
395
|
+
return this.config.options.staticComponents || Boolean(this.auditHandler);
|
|
396
|
+
}
|
|
397
|
+
get staticHelpersEnabled() {
|
|
398
|
+
return this.config.options.staticHelpers || Boolean(this.auditHandler);
|
|
399
|
+
}
|
|
400
|
+
get staticModifiersEnabled() {
|
|
401
|
+
return this.config.options.staticModifiers || Boolean(this.auditHandler);
|
|
402
|
+
}
|
|
403
|
+
isIgnoredComponent(dasherizedName) {
|
|
404
|
+
var _a;
|
|
405
|
+
return (_a = this.rules.components.get(dasherizedName)) === null || _a === void 0 ? void 0 : _a.safeToIgnore;
|
|
406
|
+
}
|
|
407
|
+
get rules() {
|
|
408
|
+
// rules that are keyed by the filename they're talking about
|
|
409
|
+
let files = new Map();
|
|
410
|
+
// rules that are keyed by our dasherized interpretation of the component's name
|
|
411
|
+
let components = new Map();
|
|
412
|
+
// we're not responsible for filtering out rules for inactive packages here,
|
|
413
|
+
// that is done before getting to us. So we should assume these are all in
|
|
414
|
+
// force.
|
|
415
|
+
for (let rule of this.config.activePackageRules) {
|
|
416
|
+
if (rule.components) {
|
|
417
|
+
for (let [snippet, rules] of Object.entries(rule.components)) {
|
|
418
|
+
let processedRules = (0, dependency_rules_1.preprocessComponentRule)(rules);
|
|
419
|
+
let dasherizedName = this.standardDasherize(snippet, rule);
|
|
420
|
+
components.set(dasherizedName, processedRules);
|
|
421
|
+
if (rules.layout) {
|
|
422
|
+
for (let root of rule.roots) {
|
|
423
|
+
if (rules.layout.addonPath) {
|
|
424
|
+
files.set((0, path_1.join)(root, rules.layout.addonPath), processedRules);
|
|
291
425
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const resolution = resolver.resolveElement(node.tag, filename, node.loc);
|
|
295
|
-
emit(path, resolution, (node, newId) => {
|
|
296
|
-
node.tag = newId.original;
|
|
297
|
-
});
|
|
298
|
-
if ((resolution === null || resolution === void 0 ? void 0 : resolution.type) === 'component') {
|
|
299
|
-
scopeStack.enteringComponentBlock(resolution, ({ argumentsAreComponents }) => {
|
|
300
|
-
handleDynamicComponentArguments(node.tag, argumentsAreComponents, extendPath(path, 'attributes'));
|
|
301
|
-
});
|
|
426
|
+
if (rules.layout.appPath) {
|
|
427
|
+
files.set((0, path_1.join)(root, rules.layout.appPath), processedRules);
|
|
302
428
|
}
|
|
303
429
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (rule.appTemplates) {
|
|
434
|
+
for (let [path, templateRules] of Object.entries(rule.appTemplates)) {
|
|
435
|
+
let processedRules = (0, dependency_rules_1.preprocessComponentRule)(templateRules);
|
|
436
|
+
for (let root of rule.roots) {
|
|
437
|
+
files.set((0, path_1.join)((0, dependency_rules_1.appTreeRulesDir)(root, this.moduleResolver), path), processedRules);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (rule.addonTemplates) {
|
|
442
|
+
for (let [path, templateRules] of Object.entries(rule.addonTemplates)) {
|
|
443
|
+
let processedRules = (0, dependency_rules_1.preprocessComponentRule)(templateRules);
|
|
444
|
+
for (let root of rule.roots) {
|
|
445
|
+
files.set((0, path_1.join)(root, path), processedRules);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return { files, components };
|
|
451
|
+
}
|
|
452
|
+
findRules(absPath) {
|
|
453
|
+
let fileRules = this.rules.files.get(absPath);
|
|
454
|
+
let componentRules;
|
|
455
|
+
let componentName = this.moduleResolver.reverseComponentLookup(absPath);
|
|
456
|
+
if (componentName) {
|
|
457
|
+
componentRules = this.rules.components.get(componentName);
|
|
458
|
+
}
|
|
459
|
+
if (fileRules && componentRules) {
|
|
460
|
+
return (0, lodash_1.mergeWith)(fileRules, componentRules, appendArrays);
|
|
461
|
+
}
|
|
462
|
+
return fileRules !== null && fileRules !== void 0 ? fileRules : componentRules;
|
|
463
|
+
}
|
|
464
|
+
standardDasherize(snippet, rule) {
|
|
465
|
+
let name = (0, dasherize_component_name_1.snippetToDasherizedName)(snippet);
|
|
466
|
+
if (name == null) {
|
|
467
|
+
throw new Error(`unable to parse component snippet "${snippet}" from rule ${JSON.stringify(rule, null, 2)}`);
|
|
468
|
+
}
|
|
469
|
+
return name;
|
|
470
|
+
}
|
|
471
|
+
targetComponent(name) {
|
|
472
|
+
if (!this.staticComponentsEnabled) {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
if (exports.builtInKeywords.includes(name)) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
if (this.isIgnoredComponent(name)) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
let componentRules = this.rules.components.get(name);
|
|
482
|
+
return {
|
|
483
|
+
type: 'component',
|
|
484
|
+
specifier: `#embroider_compat/components/${name}`,
|
|
485
|
+
yieldsComponents: componentRules ? componentRules.yieldsSafeComponents : [],
|
|
486
|
+
yieldsArguments: componentRules ? componentRules.yieldsArguments : [],
|
|
487
|
+
argumentsAreComponents: componentRules ? componentRules.argumentsAreComponents : [],
|
|
488
|
+
nameHint: this.nameHint(name),
|
|
311
489
|
};
|
|
490
|
+
}
|
|
491
|
+
targetComponentHelper(component, loc, impliedBecause) {
|
|
492
|
+
if (!this.staticComponentsEnabled) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
let message;
|
|
496
|
+
if (impliedBecause) {
|
|
497
|
+
message = `argument "${impliedBecause.argumentName}" to component "${impliedBecause.componentName}" is treated as a component, but the value you're passing is dynamic`;
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
message = `Unsafe dynamic component`;
|
|
501
|
+
}
|
|
502
|
+
if (component.type === 'other') {
|
|
503
|
+
return {
|
|
504
|
+
type: 'error',
|
|
505
|
+
message,
|
|
506
|
+
detail: `cannot statically analyze this expression`,
|
|
507
|
+
loc,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
if (component.type === 'path') {
|
|
511
|
+
let ownComponentRules = this.findRules(this.env.filename);
|
|
512
|
+
if (ownComponentRules && ownComponentRules.safeInteriorPaths.includes(component.path)) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
type: 'error',
|
|
517
|
+
message,
|
|
518
|
+
detail: component.path,
|
|
519
|
+
loc,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
return this.targetComponent(component.path);
|
|
523
|
+
}
|
|
524
|
+
targetHelper(path) {
|
|
525
|
+
if (!this.staticHelpersEnabled) {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
// people are not allowed to override the built-in helpers with their own
|
|
529
|
+
// globally-named helpers. It throws an error. So it's fine for us to
|
|
530
|
+
// prioritize the builtIns here without bothering to resolve a user helper
|
|
531
|
+
// of the same name.
|
|
532
|
+
if (exports.builtInKeywords.includes(path)) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
type: 'helper',
|
|
537
|
+
specifier: `#embroider_compat/helpers/${path}`,
|
|
538
|
+
nameHint: this.nameHint(path),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
targetHelperOrComponent(path, loc, hasArgs) {
|
|
542
|
+
/*
|
|
543
|
+
|
|
544
|
+
In earlier embroider versions we would do a bunch of module resolution right
|
|
545
|
+
here inside the ast transform to try to resolve the ambiguity of this case
|
|
546
|
+
and if we didn't find anything, leave the template unchanged. But that leads
|
|
547
|
+
to both a lot of extra build-time expense (since we are attempting
|
|
548
|
+
resolution for lots of things that may in fact be just some data and not a
|
|
549
|
+
component invocation at all, and also since we are pre-resolving modules
|
|
550
|
+
that will get resolved a second time by the final stage packager).
|
|
551
|
+
|
|
552
|
+
Now, we're going to be less forgiving, because it streamlines the build for
|
|
553
|
+
everyone who's not still using these *extremely* old patterns.
|
|
554
|
+
|
|
555
|
+
The problematic case is:
|
|
556
|
+
|
|
557
|
+
1. In a non-strict template (because this whole resolver-transform.ts is a
|
|
558
|
+
no-op on strict handlebars).
|
|
559
|
+
|
|
560
|
+
2. Have a mustache statement like: `{{something}}`, where `something` is:
|
|
561
|
+
|
|
562
|
+
a. Not a variable in scope (for example, there's no preceeding line
|
|
563
|
+
like `<Parent as |something|>`)
|
|
564
|
+
b. Does not start with `@` because that must be an argument from outside this template.
|
|
565
|
+
c. Does not contain a dot, like `some.thing` (because that case is classically
|
|
566
|
+
never a global component resolution that we would need to handle)
|
|
567
|
+
d. Does not start with `this` (this rule is mostly redundant with the previous rule,
|
|
568
|
+
but even a standalone `this` is never a component invocation).
|
|
569
|
+
e. Does not have any arguments. If there are argument like `{{something a=b}}`,
|
|
570
|
+
there is still ambiguity between helper vs component, but there is no longer
|
|
571
|
+
the possibility that this was just rendering some data.
|
|
572
|
+
f. Does not take a block, like `{{#something}}{{/something}}` (because that is
|
|
573
|
+
always a component, no ambiguity.)
|
|
574
|
+
|
|
575
|
+
We can't tell if this problematic case is really:
|
|
576
|
+
|
|
577
|
+
1. A helper invocation with no arguments that is being directly rendered.
|
|
578
|
+
Out-of-the-box, ember already generates [a lint
|
|
579
|
+
error](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-curly-component-invocation.md)
|
|
580
|
+
for this, although it tells you to whitelist your helper when IMO it
|
|
581
|
+
should tell you to use an unambiguous syntax like `{{ (something) }}`
|
|
582
|
+
instead.
|
|
583
|
+
|
|
584
|
+
2. A component invocation, which you could have written `<Something />`
|
|
585
|
+
instead. Angle-bracket invocation has been available and easy-to-adopt
|
|
586
|
+
for a very long time.
|
|
587
|
+
|
|
588
|
+
3. Property-this-fallback for `{{this.something}}`. Property-this-fallback
|
|
589
|
+
is eliminated at Ember 4.0, so people have been heavily pushed to get
|
|
590
|
+
it out of their addons.
|
|
591
|
+
*/
|
|
592
|
+
// first, bail out on all the stuff we can obviously ignore
|
|
593
|
+
if ((!this.staticHelpersEnabled && !this.staticComponentsEnabled) ||
|
|
594
|
+
exports.builtInKeywords.includes(path) ||
|
|
595
|
+
this.isIgnoredComponent(path)) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
let ownComponentRules = this.findRules(this.env.filename);
|
|
599
|
+
if (ownComponentRules === null || ownComponentRules === void 0 ? void 0 : ownComponentRules.disambiguate[path]) {
|
|
600
|
+
switch (ownComponentRules.disambiguate[path]) {
|
|
601
|
+
case 'component':
|
|
602
|
+
return this.targetComponent(path);
|
|
603
|
+
case 'helper':
|
|
604
|
+
return this.targetHelper(path);
|
|
605
|
+
case 'data':
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (!hasArgs && !path.includes('/') && !path.includes('@')) {
|
|
610
|
+
// this is the case that could also be property-this-fallback. We're going
|
|
611
|
+
// to force people to disambiguate, because letting a potential component
|
|
612
|
+
// or helper invocation lurk inside every bit of data you render is not
|
|
613
|
+
// ok.
|
|
614
|
+
this.reportError({
|
|
615
|
+
type: 'error',
|
|
616
|
+
message: 'unsupported ambiguous syntax',
|
|
617
|
+
detail: `"{{${path}}}" is ambiguous and could mean "{{this.${path}}}" or component "<${capitalize((0, lodash_1.camelCase)(path))} />" or helper "{{ (${path}) }}". Change it to one of those unambigous forms, or use a "disambiguate" packageRule to work around the problem if its in third-party code you cannot easily fix.`,
|
|
618
|
+
loc,
|
|
619
|
+
});
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
// Above we already bailed out if both of these were disabled, so we know at
|
|
623
|
+
// least one is turned on. If both aren't turned on, we're stuck, because we
|
|
624
|
+
// can't even tell if this *is* a component vs a helper.
|
|
625
|
+
if (!this.staticHelpersEnabled || !this.staticComponentsEnabled) {
|
|
626
|
+
this.reportError({
|
|
627
|
+
type: 'error',
|
|
628
|
+
message: 'unsupported ambiguity between helper and component',
|
|
629
|
+
detail: `this use of "{{${path}}}" could be helper "{{ (${path}) }}" or component "<${capitalize((0, lodash_1.camelCase)(path))} />", and your settings for staticHelpers and staticComponents do not agree. Either switch to one of the unambiguous forms, or make staticHelpers and staticComponents agree, or use a "disambiguate" packageRule to work around the problem if its in third-party code you cannot easily fix.`,
|
|
630
|
+
loc,
|
|
631
|
+
});
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
let componentRules = this.rules.components.get(path);
|
|
635
|
+
return {
|
|
636
|
+
type: 'component',
|
|
637
|
+
specifier: `#embroider_compat/ambiguous/${path}`,
|
|
638
|
+
yieldsComponents: componentRules ? componentRules.yieldsSafeComponents : [],
|
|
639
|
+
yieldsArguments: componentRules ? componentRules.yieldsArguments : [],
|
|
640
|
+
argumentsAreComponents: componentRules ? componentRules.argumentsAreComponents : [],
|
|
641
|
+
nameHint: this.nameHint(path),
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
targetElementModifier(path) {
|
|
645
|
+
if (!this.staticModifiersEnabled) {
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
if (exports.builtInKeywords.includes(path)) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
type: 'modifier',
|
|
653
|
+
specifier: `#embroider_compat/modifiers/${path}`,
|
|
654
|
+
nameHint: this.nameHint(path),
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
targetDynamicModifier(modifier, loc) {
|
|
658
|
+
if (!this.staticModifiersEnabled) {
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
if (modifier.type === 'literal') {
|
|
662
|
+
return this.targetElementModifier(modifier.path);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
return {
|
|
666
|
+
type: 'error',
|
|
667
|
+
message: 'Unsafe dynamic modifier',
|
|
668
|
+
detail: `cannot statically analyze this expression`,
|
|
669
|
+
loc,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
targetDynamicHelper(helper) {
|
|
674
|
+
if (!this.staticHelpersEnabled) {
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
677
|
+
if (helper.type === 'literal') {
|
|
678
|
+
return this.targetHelper(helper.path);
|
|
679
|
+
}
|
|
680
|
+
// we don't have to manage any errors in this case because ember itself
|
|
681
|
+
// considers it an error to pass anything but a string literal to the
|
|
682
|
+
// `helper` helper.
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
nameHint(path) {
|
|
686
|
+
let parts = path.split('@');
|
|
687
|
+
// the extra underscore here guarantees that we will never collide with an
|
|
688
|
+
// HTML element.
|
|
689
|
+
return parts[parts.length - 1] + '_';
|
|
690
|
+
}
|
|
691
|
+
handleDynamicModifier(param) {
|
|
692
|
+
if (param.type === 'StringLiteral') {
|
|
693
|
+
return this.targetDynamicModifier({ type: 'literal', path: param.value }, param.loc);
|
|
694
|
+
}
|
|
695
|
+
// we don't have to manage any errors in this case because ember itself
|
|
696
|
+
// considers it an error to pass anything but a string literal to the
|
|
697
|
+
// modifier helper.
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
handleDynamicHelper(param) {
|
|
701
|
+
// We only need to handle StringLiterals since Ember already throws an error if unsupported values
|
|
702
|
+
// are passed to the helper keyword.
|
|
703
|
+
// If a helper reference is passed in we don't need to do anything since it's either the result of a previous
|
|
704
|
+
// helper keyword invocation, or a helper reference that was imported somewhere.
|
|
705
|
+
if (param.type === 'StringLiteral') {
|
|
706
|
+
return this.targetDynamicHelper({ type: 'literal', path: param.value });
|
|
707
|
+
}
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
__decorate([
|
|
712
|
+
(0, typescript_memoize_1.Memoize)()
|
|
713
|
+
], TemplateResolver.prototype, "rules", null);
|
|
714
|
+
// This is the AST transform that resolves components, helpers and modifiers at build time
|
|
715
|
+
function makeResolverTransform({ appRoot }) {
|
|
716
|
+
let config = (0, fs_extra_1.readJSONSync)((0, path_1.join)(appRoot, '.embroider', 'resolver.json'));
|
|
717
|
+
const resolverTransform = env => {
|
|
718
|
+
if (env.strictMode) {
|
|
719
|
+
return {
|
|
720
|
+
name: 'embroider-build-time-resolver-strict-noop',
|
|
721
|
+
visitor: {},
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
return new TemplateResolver(env, config);
|
|
312
725
|
};
|
|
313
726
|
resolverTransform.parallelBabel = {
|
|
314
727
|
requireFile: __filename,
|
|
315
728
|
buildUsing: 'makeResolverTransform',
|
|
316
|
-
params:
|
|
729
|
+
params: { appRoot: appRoot },
|
|
317
730
|
};
|
|
318
731
|
return resolverTransform;
|
|
319
732
|
}
|
|
@@ -322,10 +735,25 @@ class ScopeStack {
|
|
|
322
735
|
constructor() {
|
|
323
736
|
this.stack = [];
|
|
324
737
|
}
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
738
|
+
// mustache blocks like:
|
|
739
|
+
//
|
|
740
|
+
// {{#stuff as |some block vars|}}
|
|
741
|
+
//
|
|
742
|
+
// are relatively simple for us because there's a dedicated `Block` AST node
|
|
743
|
+
// that exactly covers the range in which the variables are in scope.
|
|
744
|
+
pushMustacheBlock(blockParams) {
|
|
745
|
+
this.stack.unshift({ type: 'mustache', blockParams });
|
|
746
|
+
}
|
|
747
|
+
// element blocks like:
|
|
748
|
+
//
|
|
749
|
+
// <Stuff as |some block vars|>
|
|
750
|
+
//
|
|
751
|
+
// are *not* so simple for us because there's no single AST node that exactly
|
|
752
|
+
// covers the range in which the variables are in scope. For example, the
|
|
753
|
+
// *attributes* of the element do not see the variables, but the children of
|
|
754
|
+
// the element do.
|
|
755
|
+
pushElementBlock(blockParams, childrenOf) {
|
|
756
|
+
this.stack.unshift({ type: 'element', blockParams, childrenOf });
|
|
329
757
|
}
|
|
330
758
|
// and when we leave the block they go out of scope. If this block was tagged
|
|
331
759
|
// by a safe component marker, we also clear that.
|
|
@@ -348,9 +776,14 @@ class ScopeStack {
|
|
|
348
776
|
exit,
|
|
349
777
|
});
|
|
350
778
|
}
|
|
351
|
-
inScope(name) {
|
|
779
|
+
inScope(name, fromPath) {
|
|
352
780
|
for (let scope of this.stack) {
|
|
353
|
-
if (scope.type === '
|
|
781
|
+
if (scope.type === 'mustache' && scope.blockParams.includes(name)) {
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
if (scope.type === 'element' &&
|
|
785
|
+
scope.blockParams.includes(name) &&
|
|
786
|
+
withinElementBlock(fromPath, scope.childrenOf)) {
|
|
354
787
|
return true;
|
|
355
788
|
}
|
|
356
789
|
}
|
|
@@ -367,7 +800,7 @@ class ScopeStack {
|
|
|
367
800
|
for (let i = 0; i < this.stack.length - 1; i++) {
|
|
368
801
|
let here = this.stack[i];
|
|
369
802
|
let next = this.stack[i + 1];
|
|
370
|
-
if (here.type === '
|
|
803
|
+
if ((here.type === 'mustache' || here.type === 'element') && next.type === 'componentBlockMarker') {
|
|
371
804
|
let positionalIndex = here.blockParams.indexOf(parts[0]);
|
|
372
805
|
if (positionalIndex === -1) {
|
|
373
806
|
continue;
|
|
@@ -404,63 +837,6 @@ class ScopeStack {
|
|
|
404
837
|
return false;
|
|
405
838
|
}
|
|
406
839
|
}
|
|
407
|
-
function handleComponentHelper(param, resolver, moduleName, scopeStack, impliedBecause) {
|
|
408
|
-
let locator;
|
|
409
|
-
switch (param.type) {
|
|
410
|
-
case 'StringLiteral':
|
|
411
|
-
locator = { type: 'literal', path: param.value };
|
|
412
|
-
break;
|
|
413
|
-
case 'PathExpression':
|
|
414
|
-
locator = { type: 'path', path: param.original };
|
|
415
|
-
break;
|
|
416
|
-
case 'MustacheStatement':
|
|
417
|
-
if (param.hash.pairs.length === 0 && param.params.length === 0) {
|
|
418
|
-
return handleComponentHelper(param.path, resolver, moduleName, scopeStack, impliedBecause);
|
|
419
|
-
}
|
|
420
|
-
else if (param.path.type === 'PathExpression' && param.path.original === 'component') {
|
|
421
|
-
// safe because we will handle this inner `{{component ...}}` mustache on its own
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
locator = { type: 'other' };
|
|
426
|
-
}
|
|
427
|
-
break;
|
|
428
|
-
case 'TextNode':
|
|
429
|
-
locator = { type: 'literal', path: param.chars };
|
|
430
|
-
break;
|
|
431
|
-
case 'SubExpression':
|
|
432
|
-
if (param.path.type === 'PathExpression' && param.path.original === 'component') {
|
|
433
|
-
// safe because we will handle this inner `(component ...)` subexpression on its own
|
|
434
|
-
return null;
|
|
435
|
-
}
|
|
436
|
-
if (param.path.type === 'PathExpression' && param.path.original === 'ensure-safe-component') {
|
|
437
|
-
// safe because we trust ensure-safe-component
|
|
438
|
-
return null;
|
|
439
|
-
}
|
|
440
|
-
locator = { type: 'other' };
|
|
441
|
-
break;
|
|
442
|
-
default:
|
|
443
|
-
locator = { type: 'other' };
|
|
444
|
-
}
|
|
445
|
-
if (locator.type === 'path' && scopeStack.safeComponentInScope(locator.path)) {
|
|
446
|
-
return null;
|
|
447
|
-
}
|
|
448
|
-
return resolver.resolveComponentHelper(locator, moduleName, param.loc, impliedBecause);
|
|
449
|
-
}
|
|
450
|
-
function handleDynamicHelper(param, resolver, moduleName) {
|
|
451
|
-
// We only need to handle StringLiterals since Ember already throws an error if unsupported values
|
|
452
|
-
// are passed to the helper keyword.
|
|
453
|
-
// If a helper reference is passed in we don't need to do anything since it's either the result of a previous
|
|
454
|
-
// helper keyword invocation, or a helper reference that was imported somewhere.
|
|
455
|
-
if (param.type === 'StringLiteral') {
|
|
456
|
-
resolver.resolveDynamicHelper({ type: 'literal', path: param.value }, moduleName, param.loc);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
function handleDynamicModifier(param, resolver, moduleName) {
|
|
460
|
-
if (param.type === 'StringLiteral') {
|
|
461
|
-
resolver.resolveDynamicModifier({ type: 'literal', path: param.value }, moduleName, param.loc);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
840
|
function extendPath(path, key) {
|
|
465
841
|
const _WalkerPath = path.constructor;
|
|
466
842
|
let child = path.node[key];
|
|
@@ -471,4 +847,25 @@ function extendPath(path, key) {
|
|
|
471
847
|
return new _WalkerPath(child, path, key);
|
|
472
848
|
}
|
|
473
849
|
}
|
|
850
|
+
function capitalize(word) {
|
|
851
|
+
return word[0].toUpperCase() + word.slice(1);
|
|
852
|
+
}
|
|
853
|
+
// ElementNodes have both children and attributes and both of those are
|
|
854
|
+
// "children" in the abstract syntax tree sense, but here we want to distinguish
|
|
855
|
+
// between them.
|
|
856
|
+
function withinElementBlock(childPath, ancestorNode) {
|
|
857
|
+
let cursor = childPath;
|
|
858
|
+
while (cursor && cursor.node !== ancestorNode) {
|
|
859
|
+
if (ancestorNode.children.includes(cursor.node)) {
|
|
860
|
+
return true;
|
|
861
|
+
}
|
|
862
|
+
cursor = cursor.parent;
|
|
863
|
+
}
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
function appendArrays(objValue, srcValue) {
|
|
867
|
+
if (Array.isArray(objValue)) {
|
|
868
|
+
return objValue.concat(srcValue);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
474
871
|
//# sourceMappingURL=resolver-transform.js.map
|