Skip to content

Commit c60fdce

Browse files
authored
Merge pull request #68 from emberjs/default-utilites-from-window
default globals implementation
2 parents 8337602 + 0716d09 commit c60fdce

File tree

2 files changed

+103
-1
lines changed

2 files changed

+103
-1
lines changed

__tests__/tests.ts

+32
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import sinon from 'sinon';
1212
import { ExtendedPluginBuilder } from '../src/js-utils';
1313
import 'code-equality-assertions/jest';
1414
import { Preprocessor } from 'content-tag';
15+
import { ALLOWED_GLOBALS } from '../src/scope-locals';
1516

1617
describe('htmlbars-inline-precompile', function () {
1718
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -1901,6 +1902,37 @@ describe('htmlbars-inline-precompile', function () {
19011902
`);
19021903
});
19031904

1905+
describe('implements RFC#1070: default globals', function () {
1906+
for (let name of ALLOWED_GLOBALS) {
1907+
it(`${name}: allowed`, async function () {
1908+
plugins = [
1909+
[
1910+
HTMLBarsInlinePrecompile,
1911+
{
1912+
compiler,
1913+
targetFormat: 'hbs',
1914+
},
1915+
],
1916+
];
1917+
1918+
let transformed = await transform(
1919+
`import { template } from '@ember/template-compiler';
1920+
const data = {};
1921+
export default template('{{${name} data}}', { eval: function() { return eval(arguments[0]) } })
1922+
`
1923+
);
1924+
1925+
expect(transformed).toEqualCode(`
1926+
import { precompileTemplate } from "@ember/template-compilation";
1927+
import { setComponentTemplate } from "@ember/component";
1928+
import templateOnly from "@ember/component/template-only";
1929+
const data = {};
1930+
export default setComponentTemplate(precompileTemplate('{{${name} data}}', { strictMode: true, scope: () => ({ ${name}, data }) }), templateOnly());
1931+
`);
1932+
});
1933+
}
1934+
});
1935+
19041936
// You might think this would be confusing style, and you'd be correct. But
19051937
// that's what the lint rules are for. When it comes to correctness, we need
19061938
// our scope to behave like real Javascript, and Javascript doesn't care

src/scope-locals.ts

+71-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,76 @@ import { ASTPluginEnvironment, NodeVisitor } from '@glimmer/syntax';
1212
import { astNodeHasBinding } from './hbs-utils';
1313
import { readOnlyArray } from './read-only-array';
1414

15+
/**
16+
* RFC: https://github.com/emberjs/rfcs/pull/1070
17+
*
18+
* Criteria for inclusion in this list:
19+
*
20+
* Any of:
21+
* - begins with an uppercase letter
22+
* - guaranteed to never be added to glimmer as a keyword (e.g.: globalThis)
23+
*
24+
* And:
25+
* - must not need new to invoke
26+
* - must not require lifetime management (e.g.: setTimeout)
27+
* - must not be a single-word lower-case API, because of potential collision with future new HTML elements
28+
* - if the API is a function, the return value should not be a promise
29+
* - must be one one of these lists:
30+
* - https://tc39.es/ecma262/#sec-global-object
31+
* - https://tc39.es/ecma262/#sec-function-properties-of-the-global-object
32+
* - https://html.spec.whatwg.org/multipage/nav-history-apis.html#window
33+
* - https://html.spec.whatwg.org/multipage/indices.html#all-interfaces
34+
* - https://html.spec.whatwg.org/multipage/webappapis.html
35+
*/
36+
export const ALLOWED_GLOBALS = new Set([
37+
// ////////////////
38+
// namespaces
39+
// ////////////////
40+
// TC39
41+
'globalThis',
42+
'Atomics',
43+
'JSON',
44+
'Math',
45+
'Reflect',
46+
// WHATWG
47+
'localStorage',
48+
'sessionStorage',
49+
// ////////////////
50+
// functions / utilities
51+
// ////////////////
52+
// TC39
53+
'isNaN',
54+
'isFinite',
55+
'parseInt',
56+
'parseFloat',
57+
'decodeURI',
58+
'decodeURIComponent',
59+
'encodeURI',
60+
'encodeURIComponent',
61+
// WHATWG
62+
'postMessage',
63+
'structuredClone',
64+
// ////////////////
65+
// new-less Constructors (still functions)
66+
// ////////////////
67+
// TC39
68+
'Array', // different behavior from (array)
69+
'BigInt',
70+
'Boolean',
71+
'Date',
72+
'Number',
73+
'Object', // different behavior from (hash)
74+
'String',
75+
// ////////////////
76+
// Values
77+
// ////////////////
78+
// TC39
79+
'Infinity',
80+
'NaN',
81+
// WHATWG
82+
'isSecureContext',
83+
]);
84+
1585
/*
1686
`mode` refers to the implicit and explicit formats defined here:
1787
@@ -72,7 +142,7 @@ export class ScopeLocals {
72142

73143
#isInJsScope(hbsName: string, jsPath: NodePath) {
74144
let jsName = this.#mapping[hbsName] ?? hbsName;
75-
return ['globalThis'].includes(jsName) || jsPath.scope.getBinding(jsName);
145+
return ALLOWED_GLOBALS.has(jsName) || jsPath.scope.getBinding(jsName);
76146
}
77147

78148
// this AST transform discovers all possible upvars in HBS that refer to valid

0 commit comments

Comments
 (0)