Skip to content

Commit 7072132

Browse files
authored
Merge pull request #67 from emberjs/lexical-this
Lexical this
2 parents c60fdce + 6e7736e commit 7072132

8 files changed

+1252
-795
lines changed

__tests__/tests.ts

+201-8
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import { ALLOWED_GLOBALS } from '../src/scope-locals';
1616

1717
describe('htmlbars-inline-precompile', function () {
1818
// eslint-disable-next-line @typescript-eslint/no-var-requires
19-
let compiler: EmberTemplateCompiler = { ...require('ember-source/dist/ember-template-compiler') };
19+
let compiler: EmberTemplateCompiler = {
20+
...require('ember-source/dist/ember-template-compiler.js'),
21+
};
2022
let plugins: ([typeof HTMLBarsInlinePrecompile, Options] | [unknown])[];
2123

2224
async function transform(code: string) {
@@ -392,7 +394,7 @@ describe('htmlbars-inline-precompile', function () {
392394
*/
393395
{
394396
id: "<id>",
395-
block: "[[[8,[32,0],null,null,null]],[],false,[]]",
397+
block: "[[[8,[32,0],null,null,null]],[],[]]",
396398
moduleName: "<moduleName>",
397399
scope: () => [Setup],
398400
isStrictMode: true,
@@ -797,7 +799,7 @@ describe('htmlbars-inline-precompile', function () {
797799
*/
798800
{
799801
id: "<id>",
800-
block: '[[[8,[39,0],null,[["@text"],[[32,0]]],null]],[],false,["message"]]',
802+
block: '[[[8,[39,0],null,[["@text"],[[32,0]]],null]],[],["message"]]',
801803
moduleName: "<moduleName>",
802804
scope: () => [two],
803805
isStrictMode: false,
@@ -1694,7 +1696,7 @@ describe('htmlbars-inline-precompile', function () {
16941696
*/
16951697
{
16961698
id: "<id>",
1697-
block: "[[[8,[32,0],null,null,null]],[],false,[]]",
1699+
block: "[[[8,[32,0],null,null,null]],[],[]]",
16981700
moduleName: "<moduleName>",
16991701
scope: () => [HelloWorld],
17001702
isStrictMode: true,
@@ -1740,7 +1742,7 @@ describe('htmlbars-inline-precompile', function () {
17401742
*/
17411743
{
17421744
id: "<id>",
1743-
block: "[[[8,[32,0],null,null,null]],[],false,[]]",
1745+
block: "[[[8,[32,0],null,null,null]],[],[]]",
17441746
moduleName: "<moduleName>",
17451747
scope: () => [HelloWorld],
17461748
isStrictMode: true,
@@ -1824,7 +1826,7 @@ describe('htmlbars-inline-precompile', function () {
18241826
*/
18251827
{
18261828
id: "<id>",
1827-
block: "[[[8,[32,0],null,null,null],[8,[32,1],null,null,null]],[],false,[]]",
1829+
block: "[[[8,[32,0],null,null,null],[8,[32,1],null,null,null]],[],[]]",
18281830
moduleName: "<moduleName>",
18291831
scope: () => [bar, MyButton],
18301832
isStrictMode: false,
@@ -1872,6 +1874,34 @@ describe('htmlbars-inline-precompile', function () {
18721874
`);
18731875
expect(spy.firstCall.lastArg).toHaveProperty('locals', ['bar']);
18741876
});
1877+
1878+
it('can pass lexically scoped "this"', async function () {
1879+
let spy = sinon.spy(compiler, 'precompile');
1880+
let transformed = await transform(`
1881+
import { precompileTemplate } from '@ember/template-compilation';
1882+
export function example() {
1883+
return precompileTemplate('{{this.message}}', { scope: () => ({ "this": this }) });
1884+
}
1885+
`);
1886+
expect(spy.firstCall.lastArg).toHaveProperty('locals', ['this']);
1887+
expect(normalizeWireFormat(transformed)).toEqualCode(`
1888+
import { createTemplateFactory } from "@ember/template-factory";
1889+
export function example() {
1890+
return createTemplateFactory(
1891+
/*
1892+
{{this.message}}
1893+
*/
1894+
{
1895+
id: "<id>",
1896+
block: '[[[1,[32,0,["message"]]]],[],[]]',
1897+
moduleName: "<moduleName>",
1898+
scope: () => [this],
1899+
isStrictMode: false,
1900+
}
1901+
);
1902+
}
1903+
`);
1904+
});
18751905
});
18761906

18771907
describe('implicit-scope-form', function () {
@@ -1991,6 +2021,136 @@ describe('htmlbars-inline-precompile', function () {
19912021
`);
19922022
});
19932023

2024+
it('captures lexical "this" in mustache when template is used as an expression', async function () {
2025+
plugins = [
2026+
[
2027+
HTMLBarsInlinePrecompile,
2028+
{
2029+
compiler,
2030+
targetFormat: 'hbs',
2031+
},
2032+
],
2033+
];
2034+
2035+
let transformed = await transform(
2036+
`import { template } from '@ember/template-compiler';
2037+
function upper(s) { return s.toUpperCase() }
2038+
export function exampleTest() {
2039+
this.message = "hello";
2040+
render(template('{{upper this.message}}', { eval: function() { return eval(arguments[0]) } }))
2041+
}
2042+
`
2043+
);
2044+
2045+
expect(transformed).toEqualCode(`
2046+
import { precompileTemplate } from "@ember/template-compilation";
2047+
import { setComponentTemplate } from "@ember/component";
2048+
import templateOnly from "@ember/component/template-only";
2049+
function upper(s) {
2050+
return s.toUpperCase();
2051+
}
2052+
export function exampleTest() {
2053+
this.message = "hello";
2054+
render(
2055+
setComponentTemplate(
2056+
precompileTemplate("{{upper this.message}}", {
2057+
strictMode: true,
2058+
scope: () => ({
2059+
upper,
2060+
this: this,
2061+
}),
2062+
}),
2063+
templateOnly()
2064+
)
2065+
);
2066+
}
2067+
`);
2068+
});
2069+
2070+
it('captures lexical "this" in Element when template is used as an expression', async function () {
2071+
plugins = [
2072+
[
2073+
HTMLBarsInlinePrecompile,
2074+
{
2075+
compiler,
2076+
targetFormat: 'hbs',
2077+
},
2078+
],
2079+
];
2080+
2081+
let transformed = await transform(
2082+
`import { template } from '@ember/template-compiler';
2083+
import SomeComponent from './elsewhere.js';
2084+
export function exampleTest() {
2085+
this.message = SomeComponent;
2086+
render(template('<this.message />', { eval: function() { return eval(arguments[0]) } }))
2087+
}
2088+
`
2089+
);
2090+
2091+
expect(transformed).toEqualCode(`
2092+
import SomeComponent from './elsewhere.js';
2093+
import { precompileTemplate } from "@ember/template-compilation";
2094+
import { setComponentTemplate } from "@ember/component";
2095+
import templateOnly from "@ember/component/template-only";
2096+
export function exampleTest() {
2097+
this.message = SomeComponent;
2098+
render(
2099+
setComponentTemplate(
2100+
precompileTemplate("<this.message />", {
2101+
strictMode: true,
2102+
scope: () => ({
2103+
this: this,
2104+
}),
2105+
}),
2106+
templateOnly()
2107+
)
2108+
);
2109+
}
2110+
`);
2111+
});
2112+
2113+
it('does not captures lexical "this" when template is used in class body', async function () {
2114+
plugins = [
2115+
[
2116+
HTMLBarsInlinePrecompile,
2117+
{
2118+
compiler,
2119+
targetFormat: 'hbs',
2120+
},
2121+
],
2122+
];
2123+
2124+
let transformed = await transform(
2125+
`import { template } from '@ember/template-compiler';
2126+
import Component from '@glimmer/component';
2127+
export class Example extends Component {
2128+
upper(s) { return s.toUpperCase() }
2129+
message = "hi";
2130+
static {
2131+
template('{{this.upper this.message}}', { component: this, eval: function() { return eval(arguments[0]) } })
2132+
}
2133+
}
2134+
`
2135+
);
2136+
2137+
expect(transformed).toEqualCode(`
2138+
import Component from '@glimmer/component';
2139+
import { precompileTemplate } from "@ember/template-compilation";
2140+
import { setComponentTemplate } from "@ember/component";
2141+
export class Example extends Component {
2142+
upper(s) { return s.toUpperCase() }
2143+
message = "hi";
2144+
static {
2145+
setComponentTemplate(
2146+
precompileTemplate("{{this.upper this.message}}", {
2147+
strictMode: true,
2148+
}), this)
2149+
}
2150+
}
2151+
`);
2152+
});
2153+
19942154
it('leaves ember keywords alone when no local is defined', async function () {
19952155
plugins = [
19962156
[
@@ -2046,7 +2206,7 @@ describe('htmlbars-inline-precompile', function () {
20462206
*/
20472207
{
20482208
id: "<id>",
2049-
block: "[[[8,[32,0],null,null,null]],[],false,[]]",
2209+
block: "[[[8,[32,0],null,null,null]],[],[]]",
20502210
moduleName: "<moduleName>",
20512211
scope: () => [HelloWorld],
20522212
isStrictMode: true,
@@ -2159,7 +2319,7 @@ describe('htmlbars-inline-precompile', function () {
21592319
*/
21602320
{
21612321
id: "<id>",
2162-
block: "[[[8,[32,0],null,null,null]],[],false,[]]",
2322+
block: "[[[8,[32,0],null,null,null]],[],[]]",
21632323
moduleName: "<moduleName>",
21642324
scope: () => [HelloWorld],
21652325
isStrictMode: true,
@@ -2202,6 +2362,39 @@ describe('htmlbars-inline-precompile', function () {
22022362
`);
22032363
});
22042364

2365+
it('expression form can capture lexical "this"', async function () {
2366+
plugins = [
2367+
[
2368+
HTMLBarsInlinePrecompile,
2369+
{
2370+
compiler,
2371+
targetFormat: 'hbs',
2372+
},
2373+
],
2374+
];
2375+
2376+
let p = new Preprocessor();
2377+
2378+
let transformed = await transform(
2379+
p.process(
2380+
`
2381+
export function example() {
2382+
return <template>{{this.message}}</template>;
2383+
}
2384+
`
2385+
)
2386+
);
2387+
2388+
expect(transformed).toEqualCode(`
2389+
import { precompileTemplate } from "@ember/template-compilation";
2390+
import { setComponentTemplate } from "@ember/component";
2391+
import templateOnly from "@ember/component/template-only";
2392+
export function example() {
2393+
return setComponentTemplate(precompileTemplate('{{this.message}}', { strictMode: true, scope: () => ({ this: this }) }), templateOnly());
2394+
}
2395+
`);
2396+
});
2397+
22052398
it('works for class member form', async function () {
22062399
plugins = [
22072400
[

package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
]
3535
},
3636
"dependencies": {
37-
"@glimmer/syntax": ">= 0.84.3",
37+
"@glimmer/syntax": ">= 0.94.9",
3838
"babel-import-util": "^3.0.0"
3939
},
4040
"devDependencies": {
@@ -52,10 +52,10 @@
5252
"@types/sinon": "^10.0.13",
5353
"@typescript-eslint/eslint-plugin": "^7.14.1",
5454
"@typescript-eslint/parser": "^7.14.1",
55-
"code-equality-assertions": "^0.7.0",
55+
"code-equality-assertions": "^1.0.1",
5656
"common-tags": "^1.8.0",
5757
"content-tag": "^0.1.0",
58-
"ember-source": "^3.28.9",
58+
"ember-source": "^6.4.0-beta.1",
5959
"eslint": "^8.57.0",
6060
"eslint-config-prettier": "^6.15.0",
6161
"eslint-plugin-node": "^11.1.0",
@@ -66,7 +66,7 @@
6666
"release-it-lerna-changelog": "^3.1.0",
6767
"release-plan": "^0.9.0",
6868
"sinon": "^14.0.0",
69-
"typescript": "^5.5.2"
69+
"typescript": "^5.8.2"
7070
},
7171
"packageManager": "pnpm@9.0.6",
7272
"engines": {

0 commit comments

Comments
 (0)