diff --git a/__tests__/all.test.ts b/__tests__/all.test.ts
index d180146..49ea93f 100644
--- a/__tests__/all.test.ts
+++ b/__tests__/all.test.ts
@@ -417,6 +417,24 @@ describe('htmlbars-inline-precompile', function () {
`);
});
+ it('puts a scope local into the compiled scope', async function () {
+ let transformed = await transform(`
+ import { Setup } from './foo.js';
+ import { precompileTemplate } from '@ember/template-compilation';
+ import { setComponentTemplate } from '@ember/component';
+ import templateOnly from '@ember/component/template-only';
+
+ export default setComponentTemplate(precompileTemplate("", {
+ strictMode: true,
+ scope: () => ({
+ Setup
+ })
+ }), templateOnly());
+ `);
+
+ expect(wireScope(transformed)).toEqual({ Setup: 'Setup' });
+ });
+
it('does not fully remove imports that have other imports', async function () {
let transformed = await transform(`
import { precompileTemplate, compileTemplate } from '@ember/template-compilation';
@@ -2537,6 +2555,60 @@ describe('htmlbars-inline-precompile', function () {
});
});
+// Reads the compiled scope back out of the wire format as a mapping from the
+// name the template uses to the JS it resolves to.
+//
+// Older compilers emit the scope as an array (`scope: () => [Setup]`) and newer
+// ones as an object (`scope: () => ({ Foo: Setup })`). Both carry the same
+// information, but the array form leaves the names implicit: they're the
+// `locals` we handed the compiler, in order. Reading those back off the
+// precompile call puts both shapes into the same form, so a test can assert on
+// the scope without caring which ember-source is installed.
+function wireScope(src: string): Record {
+ let t = babel.types;
+ let found: Record | undefined;
+
+ babel.traverse(babel.parse(src, { configFile: false, babelrc: false })!, {
+ ObjectProperty(path) {
+ let { key, value } = path.node;
+ let isScopeKey =
+ (t.isIdentifier(key) && key.name === 'scope') ||
+ (t.isStringLiteral(key) && key.value === 'scope');
+ if (!isScopeKey || !t.isArrowFunctionExpression(value)) {
+ return;
+ }
+
+ let source = (node: babel.types.Node) => src.slice(node.start!, node.end!);
+
+ if (t.isObjectExpression(value.body)) {
+ found = Object.fromEntries(
+ value.body.properties.map((prop) => {
+ if (!t.isObjectProperty(prop)) {
+ throw new Error(`unexpected scope entry: ${prop.type}`);
+ }
+ let name = t.isIdentifier(prop.key) ? prop.key.name : source(prop.key);
+ return [name, source(prop.value)];
+ })
+ );
+ } else if (t.isArrayExpression(value.body)) {
+ let locals = (precompileSpy.mock.lastCall?.at(-1) as { locals?: string[] })?.locals;
+ if (!locals) {
+ throw new Error('cannot name an array-form scope without the compiler options');
+ }
+ found = Object.fromEntries(
+ value.body.elements.map((element, index) => [locals[index], source(element!)])
+ );
+ }
+ path.stop();
+ },
+ });
+
+ if (!found) {
+ throw new Error(`no wire-format scope found in:\n${src}`);
+ }
+ return found;
+}
+
// This takes out parts of ember's wire format that aren't our job and shouldn't
// break our tests if they change.
function normalizeWireFormat(src: string): string {