Skip to content

Commit 178f720

Browse files
committed
traverse ast to discover locals
1 parent 5fceb59 commit 178f720

File tree

1 file changed

+44
-16
lines changed

1 file changed

+44
-16
lines changed

src/plugin.ts

+44-16
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { JSUtils, ExtendedPluginBuilder } from './js-utils';
77
import type { EmberTemplateCompiler, PreprocessOptions } from './ember-template-compiler';
88
import { LegacyModuleName } from './public-types';
99
import { ScopeLocals } from './scope-locals';
10-
import { ASTPluginBuilder, getTemplateLocals, preprocess, print } from '@glimmer/syntax';
10+
import { ASTPluginBuilder, preprocess, print, traverse, WalkerPath } from '@glimmer/syntax';
1111

1212
export * from './public-types';
1313

@@ -373,31 +373,57 @@ function runtimeErrorIIFE(babel: typeof Babel, replacements: { ERROR_MESSAGE: st
373373
function buildScopeLocals(
374374
userTypedOptions: Record<string, unknown>,
375375
formatOptions: ModuleConfig,
376-
templateContent: string
376+
templateContent: string,
377+
path: NodePath<t.Expression>
377378
): ScopeLocals {
378379
if (formatOptions.rfc931Support && userTypedOptions.eval) {
379-
return discoverLocals(templateContent);
380+
return discoverLocals(templateContent, path);
380381
} else if (userTypedOptions.scope) {
381382
return userTypedOptions.scope as ScopeLocals;
382383
} else {
383384
return new ScopeLocals();
384385
}
385386
}
386387

387-
function discoverLocals(templateContent: string): ScopeLocals {
388-
// this is wrong, but the right thing is unreleased in
389-
// https://github.com/glimmerjs/glimmer-vm/pull/1421, so for the moment I'm
390-
// sticking with the exact behavior that ember-templates-imports has.
391-
//
392-
// (the reason it's wrong is that the correct answer depends on not just the
393-
// template, but the ambient javascript scope. Anything in locals needs to win
394-
// over ember keywords. Otherwise we can never introduce new keywords.)
388+
function discoverLocals(templateContent: string, jsPath: NodePath<t.Expression>): ScopeLocals {
395389
let scopeLocals = new ScopeLocals();
396-
for (let local of getTemplateLocals(templateContent)) {
397-
if (local.match(/^[$A-Z_][0-9A-Z_$]*$/i)) {
398-
scopeLocals.add(local);
390+
const ast = preprocess(templateContent);
391+
function isInScope(path: WalkerPath<any>, name: string) {
392+
while (path.parent) {
393+
path = path.parent;
394+
if (path.node.blockParams && path.node.blockParams.includes(name)) {
395+
return true;
396+
}
397+
}
398+
return false;
399+
}
400+
401+
function isInJsScope(name: string) {
402+
if (jsPath.scope.getBinding(name)) return true;
403+
if (['this', 'globalThis'].includes(name)) return true;
404+
if (jsPath.state.originalImportedNames.has(name)) {
405+
return true;
399406
}
407+
return false;
400408
}
409+
410+
traverse(ast, {
411+
PathExpression(node, path) {
412+
if (
413+
node.head.type === 'VarHead' &&
414+
!isInScope(path, node.head.name) &&
415+
isInJsScope(node.head.name)
416+
) {
417+
scopeLocals.add(node.head.name);
418+
}
419+
},
420+
ElementNode(node, path) {
421+
const name = node.tag.split('.')[0];
422+
if (!isInScope(path, name) && isInJsScope(name)) {
423+
scopeLocals.add(name);
424+
}
425+
},
426+
});
401427
return scopeLocals;
402428
}
403429

@@ -483,7 +509,8 @@ function insertCompiledTemplate<EnvSpecificOptions>(
483509
backingClass: NodePath<Parameters<typeof t.callExpression>[1][number]> | undefined
484510
) {
485511
let t = babel.types;
486-
let scopeLocals = buildScopeLocals(userTypedOptions, config, template);
512+
target.state = state;
513+
let scopeLocals = buildScopeLocals(userTypedOptions, config, template, target);
487514
let options = buildPrecompileOptions(
488515
babel,
489516
target,
@@ -560,7 +587,8 @@ function insertTransformedTemplate<EnvSpecificOptions>(
560587
backingClass: NodePath<Parameters<typeof t.callExpression>[1][number]> | undefined
561588
) {
562589
let t = babel.types;
563-
let scopeLocals = buildScopeLocals(userTypedOptions, formatOptions, template);
590+
target.state = state;
591+
let scopeLocals = buildScopeLocals(userTypedOptions, formatOptions, template, target);
564592
let options = buildPrecompileOptions(
565593
babel,
566594
target,

0 commit comments

Comments
 (0)