Skip to content

Commit 3dbde3f

Browse files
Merge pull request #1668 from NullVoxPopuli/fix-TopLevelScope-ember-repl
[ember-repl] Fix an issue where hbs live blocks did not have scope properly forwarded to them and make errors about missing scope clearer, rather than 'tried to render undefined, how dare you'
2 parents bf4118d + 61eb6be commit 3dbde3f

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

packages/ember-repl/addon/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ember-repl",
3-
"version": "3.0.0-beta.7",
3+
"version": "3.0.0-beta.8",
44
"description": "Addon for enabling REPL and Playground creation with Ember/Glimmer",
55
"keywords": [
66
"ember-addon"

packages/ember-repl/addon/src/browser/compile/formats.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { ExtractedCode } from './markdown-to-ember.ts';
55
import type { UnifiedPlugin } from './types.ts';
66
import type { EvalImportMap, ScopeMap } from './types.ts';
77

8-
async function compileAll(js: { code: string }[], importMap?: EvalImportMap) {
8+
async function compileGJSArray(js: { code: string }[], importMap?: EvalImportMap) {
99
let modules = await Promise.all(
1010
js.map(async ({ code }) => {
1111
return await compileGJS(code, importMap);
@@ -46,15 +46,18 @@ export async function compileHBS(
4646

4747
async function extractScope(
4848
liveCode: ExtractedCode[],
49-
importMap?: EvalImportMap
49+
options?: {
50+
importMap?: EvalImportMap;
51+
topLevelScope?: ScopeMap;
52+
}
5053
): Promise<CompileResult[]> {
5154
let scope: CompileResult[] = [];
5255

5356
let hbs = liveCode.filter((code) => code.lang === 'hbs');
5457
let js = liveCode.filter((code) => ['js', 'gjs'].includes(code.lang));
5558

5659
if (js.length > 0) {
57-
let compiled = await compileAll(js, importMap);
60+
let compiled = await compileGJSArray(js, options?.importMap);
5861

5962
await Promise.all(
6063
compiled.map(async (info) => {
@@ -74,7 +77,7 @@ async function extractScope(
7477
}
7578

7679
for (let { code } of hbs) {
77-
let compiled = await compileHBS(code);
80+
let compiled = await compileHBS(code, { scope: options?.topLevelScope });
7881

7982
scope.push(compiled);
8083
}
@@ -92,7 +95,6 @@ export async function compileMD(
9295
ShadowComponent?: string;
9396
}
9497
): Promise<CompileResult & { rootTemplate?: string }> {
95-
let importMap = options?.importMap;
9698
let topLevelScope = options?.topLevelScope ?? {};
9799
let rootTemplate: string;
98100
let liveCode: ExtractedCode[];
@@ -126,7 +128,7 @@ export async function compileMD(
126128
*/
127129
if (liveCode.length > 0) {
128130
try {
129-
scope = await extractScope(liveCode, importMap);
131+
scope = await extractScope(liveCode, options);
130132
} catch (error) {
131133
console.info({ scope });
132134
console.error(error);

packages/ember-repl/addon/src/browser/hbs.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,15 @@ function compileTemplate(source: string, { moduleName, scope = {} }: CompileTemp
7575
// Copied from @glimmer/compiler/lib/compiler#precompile
7676
let [block, usedLocals] = precompileJSON(source, options);
7777

78-
let usedScope = usedLocals.map((key: string) => localScope[key]);
78+
let usedScope = usedLocals.map((key: string) => {
79+
let value = localScope[key];
80+
81+
if (!value) {
82+
throw new Error(`Attempt to use ${key} in compiled hbs, but it was not available in scope.`);
83+
}
84+
85+
return value;
86+
});
7987

8088
let blockJSON = JSON.stringify(block);
8189
let templateJSONObject = {

packages/ember-repl/test-app/tests/rendering/markdown-test.gts

+76
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,80 @@ module('Rendering | compile()', function (hooks) {
164164
});
165165
});
166166
});
167+
168+
module('passing options', function () {
169+
test('adding to top-level scope', async function (assert) {
170+
const text = `This is a local component added to topLevelScope`;
171+
const LocalComponent = <template>{{text}}</template>;
172+
173+
setupOnerror(() => {
174+
assert.notOk('This should not error');
175+
});
176+
177+
let snippet = stripIndent`
178+
<LocalComponent />
179+
`;
180+
181+
let component: ComponentLike | undefined;
182+
183+
await compile(snippet, {
184+
format: 'glimdown',
185+
onSuccess: (comp) => (component = comp),
186+
onError: () => assert.notOk('did not expect error'),
187+
onCompileStart: () => {
188+
/* not used */
189+
},
190+
topLevelScope: {
191+
LocalComponent,
192+
},
193+
});
194+
195+
debugAssert(`[BUG]`, component);
196+
197+
await render(component);
198+
199+
assert.dom().hasText(text);
200+
});
201+
202+
test('adding to top-level scope applies to rendered "hbs" codefences', async function (assert) {
203+
const text = `This is a local component added to topLevelScope`;
204+
const LocalComponent = <template>{{text}}</template>;
205+
206+
setupOnerror((e) => {
207+
console.error(e);
208+
assert.notOk('This should not error');
209+
});
210+
211+
let snippet = stripIndent`
212+
Using a live hbs tag is how we can syntax highlighting
213+
214+
\`\`\`hbs live
215+
<LocalComponent />
216+
\`\`\`
217+
`;
218+
219+
let component: ComponentLike | undefined;
220+
221+
await compile(snippet, {
222+
format: 'glimdown',
223+
onSuccess: (comp) => (component = comp),
224+
onError: (e) => {
225+
console.error(e);
226+
assert.notOk('did not expect error');
227+
},
228+
onCompileStart: () => {
229+
/* not used */
230+
},
231+
topLevelScope: {
232+
LocalComponent,
233+
},
234+
});
235+
236+
debugAssert(`[BUG]`, component);
237+
238+
await render(component);
239+
240+
assert.dom().containsText(text);
241+
});
242+
});
167243
});

0 commit comments

Comments
 (0)