From 8685023a9c8ea339370a210a45494786254685f0 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Thu, 1 Feb 2024 05:59:38 -0800 Subject: [PATCH 01/15] [ENHANCEMENT+CLEANUP] Better compile time errors for strict mode Removing support for implicit this fallback {{foo}} -> {{this.foo}} When a strict mode template references an undefined variable, it really should produce a compile-time error, arguably that is the point of the feature. However, it seems like previously a lot of these errors ended up propagating and is actually handled by the runtime compiler. The original/primary goal here started out as cleaning up all the code for implicit this fallback, but as I delete code it eventually revaled all the places where strict mode was inappropiately using the same infrastructure ("unresolved free variables", which isn't really a sensible thing). Also: * Removes "deprecated call" @foo={{bar}} * Removes unused opcodes --- .../test/modifiers/dynamic-modifiers-test.ts | 2 +- .../test/strict-mode-test.ts | 219 +++++----- ...ment-less-helper-paren-less-invoke-test.ts | 36 +- .../test/syntax/keyword-errors-test.ts | 8 +- .../@glimmer/compiler/lib/builder/builder.ts | 18 +- .../lib/passes/1-normalization/index.ts | 9 +- .../passes/1-normalization/keywords/impl.ts | 8 - .../passes/1-normalization/utils/is-node.ts | 2 - .../1-normalization/visitors/expressions.ts | 9 - .../1-normalization/visitors/strict-mode.ts | 390 ++++++++++++++++++ .../lib/passes/2-encoding/expressions.ts | 13 - .../compiler/lib/passes/2-encoding/mir.ts | 19 - .../compiler/lib/wire-format-debug.ts | 13 - .../@glimmer/compiler/test/compiler-test.ts | 4 +- .../interfaces/lib/compile/encoder.ts | 16 - .../lib/compile/wire-format/api.d.ts | 33 -- .../lib/compile/wire-format/opcodes.d.ts | 11 - .../lib/compile/wire-format/resolution.d.ts | 6 +- .../lib/opcode-builder/encoder.ts | 19 +- .../lib/opcode-builder/helpers/resolution.ts | 58 +-- .../lib/opcode-builder/opcodes.ts | 4 - .../opcode-compiler/lib/syntax/expressions.ts | 57 +-- .../opcode-compiler/lib/syntax/statements.ts | 3 +- packages/@glimmer/syntax/lib/keywords.ts | 23 +- packages/@glimmer/syntax/lib/v2/README.md | 101 +---- packages/@glimmer/syntax/lib/v2/builders.ts | 12 - .../syntax/lib/v2/loose-resolution.ts | 46 +-- packages/@glimmer/syntax/lib/v2/normalize.ts | 118 +++--- .../@glimmer/syntax/lib/v2/objects/expr.ts | 21 +- .../syntax/lib/v2/objects/resolution.ts | 240 ++--------- .../syntax/lib/v2/serialize/serialize.ts | 11 - packages/@glimmer/wire-format/lib/opcodes.ts | 10 - .../@glimmer/wire-format/lib/resolution.ts | 18 +- 33 files changed, 738 insertions(+), 819 deletions(-) create mode 100644 packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts diff --git a/packages/@glimmer-workspace/integration-tests/test/modifiers/dynamic-modifiers-test.ts b/packages/@glimmer-workspace/integration-tests/test/modifiers/dynamic-modifiers-test.ts index 87f6f5b9a8..4b031e8a37 100644 --- a/packages/@glimmer-workspace/integration-tests/test/modifiers/dynamic-modifiers-test.ts +++ b/packages/@glimmer-workspace/integration-tests/test/modifiers/dynamic-modifiers-test.ts @@ -156,7 +156,7 @@ class DynamicModifiersResolutionModeTest extends RenderTest { this.registerComponent('TemplateOnly', 'Bar', '
'); }, syntaxErrorFor( - 'You attempted to invoke a path (`{{#x.foo}}`) as a modifier, but x was not in scope. Try adding `this` to the beginning of the path', + 'You attempted to invoke a path (`{{x.foo}}`) as a modifier, but x was not in scope', '{{x.foo}}', 'an unknown module', 1, diff --git a/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts b/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts index 8061eabfa0..00e14ee486 100644 --- a/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts +++ b/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts @@ -73,15 +73,22 @@ class GeneralStrictModeTest extends RenderTest { @test 'Implicit this lookup does not work'() { - const Foo = defineComponent({}, '{{bar}}', { - definition: class extends GlimmerishComponent { - bar = 'Hello, world!'; + this.assert.throws( + () => { + defineComponent({}, '{{bar}}', { + definition: class extends GlimmerishComponent { + bar = 'Hello, world!'; + }, + }); }, - }); - - this.assert.throws(() => { - this.renderComponent(Foo); - }, /Attempted to resolve a value in a strict mode template, but that value was not in scope: bar/u); + syntaxErrorFor( + 'Attempted to resolve a value in a strict mode template, but that value was not in scope: bar', + '{{bar}}', + 'an unknown module', + 1, + 0 + ) + ); } @test @@ -395,132 +402,136 @@ class StaticStrictModeTest extends RenderTest { @test 'Throws an error if value in append position is not in scope'() { - const Bar = defineComponent({}, '{{foo}}'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a value in a strict mode template, but that value was not in scope: foo/u); + this.assert.throws( + () => { + defineComponent({}, '{{foo}}'); + }, + syntaxErrorFor( + 'Attempted to resolve a value in a strict mode template, but that value was not in scope: foo', + '{{foo}}', + 'an unknown module', + 1, + 0 + ) + ); } @test 'Throws an error if component or helper in append position is not in scope'() { - const Bar = defineComponent({}, '{{foo "bar"}}'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a component or helper in a strict mode template, but that value was not in scope: foo/u); + this.assert.throws( + () => { + defineComponent({}, '{{foo "bar"}}'); + }, + syntaxErrorFor( + 'Attempted to resolve a component or helper in a strict mode template, but that value was not in scope: foo', + '{{foo "bar"}}', + 'an unknown module', + 1, + 0 + ) + ); } @test 'Throws an error if a value in argument position is not in scope'() { const Foo = defineComponent({}, '{{@foo}}'); - const Bar = defineComponent({ Foo }, ''); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a value in a strict mode template, but that value was not in scope: bar/u); - } - - @test - 'Throws an error if helper in argument position (with args) is not in scope'() { - const Foo = defineComponent({}, '{{@foo}}'); - const Bar = defineComponent({ Foo }, ''); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a helper in a strict mode template, but that value was not in scope: bar/u); - } - - @test - 'Throws an error if helper in subexpression position is not in scope'() { - const foo = defineSimpleHelper((value: string) => value); - const Bar = defineComponent({ foo }, '{{foo (bar)}}'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a helper in a strict mode template, but that value was not in scope: bar/u); - } - @test - 'Throws an error if value in append position is not in scope, and component is registered'() { - this.registerComponent('TemplateOnly', 'foo', 'Hello, world!'); - const Bar = defineComponent({}, '{{foo}}'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a value in a strict mode template, but that value was not in scope: foo/u); - } - - @test - 'Throws an error if value in append position is not in scope, and helper is registered'() { - this.registerHelper('foo', () => 'Hello, world!'); - const Bar = defineComponent({}, '{{foo}}'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a value in a strict mode template, but that value was not in scope: foo/u); + this.assert.throws( + () => { + defineComponent({ Foo }, ''); + }, + syntaxErrorFor( + 'Attempted to resolve a value in a strict mode template, but that value was not in scope: bar', + '@foo={{bar}}', + 'an unknown module', + 1, + 5 + ) + ); } @test - 'Throws an error if component or helper in append position is not in scope, and helper is registered'() { - this.registerHelper('foo', () => 'Hello, world!'); - const Bar = defineComponent({}, '{{foo "bar"}}'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a component or helper in a strict mode template, but that value was not in scope: foo/u); + 'Throws an error if a value in attribute position is not in scope'() { + this.assert.throws( + () => { + defineComponent({}, '
'); + }, + syntaxErrorFor( + 'Attempted to resolve a value in a strict mode template, but that value was not in scope: foo', + 'class={{foo}}', + 'an unknown module', + 1, + 5 + ) + ); } @test - 'Throws an error if a value in argument position is not in scope, and helper is registered'() { - this.registerHelper('bar', () => 'Hello, world!'); + 'Throws an error if helper in argument position (with args) is not in scope'() { const Foo = defineComponent({}, '{{@foo}}'); - const Bar = defineComponent({ Foo }, ''); - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a value in a strict mode template, but that value was not in scope: bar/u); + this.assert.throws( + () => { + defineComponent({ Foo }, ''); + }, + syntaxErrorFor( + 'Attempted to resolve a helper in a strict mode template, but that value was not in scope: bar', + '@foo={{bar "aoeu"}}', + 'an unknown module', + 1, + 5 + ) + ); } @test - 'Throws an error if helper in argument position (with args) is not in scope, and helper is registered'() { - this.registerHelper('bar', () => 'Hello, world!'); - const Foo = defineComponent({}, '{{@foo}}'); - const Bar = defineComponent({ Foo }, ''); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a helper in a strict mode template, but that value was not in scope: bar/u); + 'Throws an error if helper in attribute position (with args) is not in scope'() { + this.assert.throws( + () => { + defineComponent({}, '
'); + }, + syntaxErrorFor( + 'Attempted to resolve a helper in a strict mode template, but that value was not in scope: foo', + 'class={{foo "bar"}}', + 'an unknown module', + 1, + 5 + ) + ); } @test - 'Throws an error if helper in subexpression position is not in scope, and helper is registered'() { - this.registerHelper('bar', () => 'Hello, world!'); + 'Throws an error if helper in subexpression position is not in scope'() { const foo = defineSimpleHelper((value: string) => value); - const Bar = defineComponent({ foo }, '{{foo (bar)}}'); - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a helper in a strict mode template, but that value was not in scope: bar/u); + this.assert.throws( + () => { + defineComponent({ foo }, '{{foo (bar)}}'); + }, + syntaxErrorFor( + 'Attempted to resolve a helper in a strict mode template, but that value was not in scope: bar', + '(bar)', + 'an unknown module', + 1, + 6 + ) + ); } @test 'Throws an error if modifier is not in scope'() { - const Bar = defineComponent({}, '
'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a modifier in a strict mode template, but it was not in scope: foo/u); - } - - @test - 'Throws an error if modifier is not in scope, and modifier is registred'() { - this.registerModifier('name', class {}); - const Bar = defineComponent({}, '
'); - - this.assert.throws(() => { - this.renderComponent(Bar); - }, /Attempted to resolve a modifier in a strict mode template, but it was not in scope: foo/u); + this.assert.throws( + () => { + defineComponent({}, '
'); + }, + syntaxErrorFor( + 'Attempted to resolve a modifier in a strict mode template, but that value was not in scope: foo', + '{{foo}}', + 'an unknown module', + 1, + 5 + ) + ); } @test diff --git a/packages/@glimmer-workspace/integration-tests/test/syntax/argument-less-helper-paren-less-invoke-test.ts b/packages/@glimmer-workspace/integration-tests/test/syntax/argument-less-helper-paren-less-invoke-test.ts index 7428ff81c2..423a50d198 100644 --- a/packages/@glimmer-workspace/integration-tests/test/syntax/argument-less-helper-paren-less-invoke-test.ts +++ b/packages/@glimmer-workspace/integration-tests/test/syntax/argument-less-helper-paren-less-invoke-test.ts @@ -1,29 +1,27 @@ -import { defineSimpleHelper, jitSuite, RenderTest, test } from '../..'; +import { defineSimpleHelper, jitSuite, preprocess, RenderTest, syntaxErrorFor, test } from '../..'; class ArgumentLessHelperParenLessInvokeTest extends RenderTest { static suiteName = 'argument-less helper paren-less invoke'; @test - 'invoking an argument-less helper without parens in named argument position is deprecated'( + 'invoking an argument-less helper without parens in named argument position is a syntax error'( assert: Assert ) { - this.registerHelper('is-string', ([value]: readonly unknown[]) => typeof value === 'string'); - - this.registerHelper('foo', () => 'Hello, world!'); - this.registerComponent('TemplateOnly', 'Bar', '[{{is-string @content}}][{{@content}}]'); - - this.render('', { foo: 'Not it!' }); - this.assertHTML('[true][Hello, world!]'); - this.assertStableRerender(); - - assert.validateDeprecations( - new RegExp( - /The `foo` helper was used in the `\(unknown template module\)` template as /.source + - /`@content=\{\{foo\}\}`\. This is ambigious between wanting the `@content` argument / - .source + - /to be the `foo` helper itself, or the result of invoking the `foo` helper /.source + - /\(current behavior\)\. This implicit invocation behavior has been deprecated\./.source, - 'u' + assert.throws( + () => { + preprocess('', { + meta: { moduleName: 'test-module' }, + }); + }, + syntaxErrorFor( + 'You attempted to pass a path as argument (`@content={{foo}}`) but foo was not in scope. Try:\n' + + '* `@content={{this.foo}}` if this is meant to be a property lookup, or\n' + + '* `@content={{(foo)}}` if this is meant to invoke the resolved helper, or\n' + + '* `@content={{helper "foo"}}` if this is meant to pass the resolved helper by value', + `@content={{foo}}`, + 'test-module', + 1, + 5 ) ); } diff --git a/packages/@glimmer-workspace/integration-tests/test/syntax/keyword-errors-test.ts b/packages/@glimmer-workspace/integration-tests/test/syntax/keyword-errors-test.ts index 9c8029a46b..740b2246a0 100644 --- a/packages/@glimmer-workspace/integration-tests/test/syntax/keyword-errors-test.ts +++ b/packages/@glimmer-workspace/integration-tests/test/syntax/keyword-errors-test.ts @@ -34,7 +34,7 @@ for (let keyword of KEYWORDS) { 'keyword can be yielded as a parameter in other keywords in non-strict mode'() { preprocess( ` - {{#let value as |${keyword}|}} + {{#let this.value as |${keyword}|}} {{some-helper ${keyword}}} {{/let}} `, @@ -46,7 +46,7 @@ for (let keyword of KEYWORDS) { 'keyword can be yielded as a parameter in other keywords in strict mode'() { preprocess( ` - {{#let value as |${keyword}|}} + {{#let this.value as |${keyword}|}} {{some-helper ${keyword}}} {{/let}} `, @@ -58,7 +58,7 @@ for (let keyword of KEYWORDS) { 'keyword can be yielded as a parameter in curly invocation in non-strict mode'() { preprocess( ` - {{#my-component value as |${keyword}|}} + {{#my-component this.value as |${keyword}|}} {{some-helper ${keyword}}} {{/my-component}} `, @@ -70,7 +70,7 @@ for (let keyword of KEYWORDS) { 'keyword can be yielded as a parameter in curly invocation in strict mode'() { preprocess( ` - {{#my-component value as |${keyword}|}} + {{#my-component this.value as |${keyword}|}} {{some-helper ${keyword}}} {{/my-component}} `, diff --git a/packages/@glimmer/compiler/lib/builder/builder.ts b/packages/@glimmer/compiler/lib/builder/builder.ts index bfb4158867..5acfd89388 100644 --- a/packages/@glimmer/compiler/lib/builder/builder.ts +++ b/packages/@glimmer/compiler/lib/builder/builder.ts @@ -247,8 +247,8 @@ export function buildStatement( let builtExpr: WireFormat.Expression = buildCallHead( path, trusted - ? VariableResolutionContext.AmbiguousInvoke - : VariableResolutionContext.AmbiguousAppendInvoke, + ? VariableResolutionContext.ResolveAsHelperHead + : VariableResolutionContext.ResolveAsComponentOrHelperHead, symbols ); @@ -639,15 +639,15 @@ export function buildVar( if (context === 'Strict') { op = Op.GetStrictKeyword; } else if (context === 'AppendBare') { - op = Op.GetFreeAsComponentOrHelperHeadOrThisFallback; + op = Op.GetFreeAsComponentOrHelperHead; } else if (context === 'AppendInvoke') { op = Op.GetFreeAsComponentOrHelperHead; } else if (context === 'TrustedAppendBare') { - op = Op.GetFreeAsHelperHeadOrThisFallback; + op = Op.GetFreeAsHelperHead; } else if (context === 'TrustedAppendInvoke') { op = Op.GetFreeAsHelperHead; } else if (context === 'AttrValueBare') { - op = Op.GetFreeAsHelperHeadOrThisFallback; + op = Op.GetFreeAsHelperHead; } else if (context === 'AttrValueInvoke') { op = Op.GetFreeAsHelperHead; } else if (context === 'SubExpression') { @@ -692,13 +692,9 @@ export function expressionContextOp(context: VariableResolutionContext): GetCont switch (context) { case VariableResolutionContext.Strict: return Op.GetStrictKeyword; - case VariableResolutionContext.AmbiguousAppend: - return Op.GetFreeAsComponentOrHelperHeadOrThisFallback; - case VariableResolutionContext.AmbiguousAppendInvoke: + case VariableResolutionContext.ResolveAsComponentOrHelperHead: return Op.GetFreeAsComponentOrHelperHead; - case VariableResolutionContext.AmbiguousInvoke: - return Op.GetFreeAsHelperHeadOrThisFallback; - case VariableResolutionContext.ResolveAsCallHead: + case VariableResolutionContext.ResolveAsHelperHead: return Op.GetFreeAsHelperHead; case VariableResolutionContext.ResolveAsModifierHead: return Op.GetFreeAsModifierHead; diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/index.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/index.ts index 6c26704a05..d6558d3b7d 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/index.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/index.ts @@ -7,6 +7,7 @@ import type { Result } from '../../shared/result'; import * as mir from '../2-encoding/mir'; import { NormalizationState } from './context'; import { VISIT_STMTS } from './visitors/statements'; +import StrictModeValidationPass from './visitors/strict-mode'; /** * Normalize the AST from @glimmer/syntax into the HIR. The HIR has special @@ -71,7 +72,13 @@ export default function normalize( } } - return body.mapOk( + let template = body.mapOk( (body) => new mir.Template({ loc: root.loc, scope: root.table, body: body.toArray() }) ); + + if (isStrict) { + template = template.andThen((template) => StrictModeValidationPass.validate(template)); + } + + return template; } diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts index 99e8e8a9bb..ead8ab356b 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts @@ -49,14 +49,6 @@ class KeywordImpl< let path = getCalleeExpression(node); if (path !== null && path.type === 'Path' && path.ref.type === 'Free') { - if (path.tail.length > 0) { - if (path.ref.resolution.serialize() === 'Loose') { - // cannot be a keyword reference, keywords do not allow paths (must be - // relying on implicit this fallback) - return false; - } - } - return path.ref.name === this.keyword; } else { return false; diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts index a889294c44..3f8963d38c 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts @@ -90,8 +90,6 @@ function printPath(path: ASTv2.ExpressionNode): string { } case 'Call': return `(${printPath(path.callee)} ...)`; - case 'DeprecatedCall': - return `${path.callee.name}`; case 'Interpolate': throw unreachable('a concat statement cannot appear as the head of an expression'); } diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts index 8df5ed41c5..d4f07b4c14 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts @@ -28,8 +28,6 @@ export class NormalizeExpressions { return this.CallExpression(node, state); } - case 'DeprecatedCall': - return this.DeprecaedCallExpression(node, state); } } @@ -112,13 +110,6 @@ export class NormalizeExpressions { } } - DeprecaedCallExpression( - { arg, callee, loc }: ASTv2.DeprecatedCallExpression, - _state: NormalizationState - ): Result { - return Ok(new mir.DeprecatedCallExpression({ loc, arg, callee })); - } - Args({ positional, named, loc }: ASTv2.Args, state: NormalizationState): Result { return Result.all(this.Positional(positional, state), this.NamedArguments(named, state)).mapOk( ([positional, named]) => diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts new file mode 100644 index 0000000000..47aef11a1f --- /dev/null +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts @@ -0,0 +1,390 @@ +import type { HasSourceSpan } from '@glimmer/syntax'; +import { generateSyntaxError, loc } from '@glimmer/syntax'; +import { CurriedTypes } from '@glimmer/vm/index'; + +import type { Result } from '../../../shared/result'; +import type * as mir from '../../2-encoding/mir'; + +import { Err, Ok } from '../../../shared/result'; + +enum ResolutionType { + Value = 'value', + Component = 'component', + Helper = 'helper', + Modifier = 'modifier', + ComponentOrHelper = 'component or helper', +} + +export default class StrictModeValidationPass { + // This is done at the end of all the keyword normalizations + // At this point any free variables that isn't a valid keyword + // in its context should be considered a syntax error. We + // probably had various opportunities to do this inline in the + // earlier passes, but this aims to produce a better syntax + // error as we don't always have the right loc-context to do + // so in the other spots. + static validate(template: mir.Template): Result { + return new this(template).validate(); + } + + private constructor(private template: mir.Template) {} + + validate(): Result { + return this.Statements(this.template.body).mapOk(() => this.template); + } + + Statements(statements: mir.Statement[]): Result { + let result = Ok(null); + + for (let statement of statements) { + result = result.andThen(() => this.Statement(statement)); + } + + return result; + } + + NamedBlocks({ blocks }: mir.NamedBlocks): Result { + let result = Ok(null); + + for (let block of blocks.toArray()) { + result = result.andThen(() => this.NamedBlock(block)); + } + + return result; + } + + NamedBlock(block: mir.NamedBlock): Result { + return this.Statements(block.body); + } + + Statement(statement: mir.Statement): Result { + switch (statement.type) { + case 'InElement': + return this.InElement(statement); + + case 'Debugger': + return Ok(null); + + case 'Yield': + return this.Yield(statement); + + case 'AppendTrustedHTML': + return this.AppendTrustedHTML(statement); + + case 'AppendTextNode': + return this.AppendTextNode(statement); + + case 'Component': + return this.Component(statement); + + case 'SimpleElement': + return this.SimpleElement(statement); + + case 'InvokeBlock': + return this.InvokeBlock(statement); + + case 'AppendComment': + return Ok(null); + + case 'If': + return this.If(statement); + + case 'Each': + return this.Each(statement); + + case 'Let': + return this.Let(statement); + + case 'WithDynamicVars': + return this.WithDynamicVars(statement); + + case 'InvokeComponent': + return this.InvokeComponent(statement); + } + } + + Expressions(expressions: mir.ExpressionNode[]): Result { + let result = Ok(null); + + for (let expression of expressions) { + result = result.andThen(() => this.Expression(expression)); + } + + return result; + } + + Expression( + expression: mir.ExpressionNode, + span: HasSourceSpan = expression, + resolution?: ResolutionType + ): Result { + switch (expression.type) { + case 'Literal': + case 'Missing': + case 'This': + case 'Arg': + case 'Local': + case 'HasBlock': + case 'HasBlockParams': + case 'GetDynamicVar': + return Ok(null); + + case 'PathExpression': + return this.Expression(expression.head, span, resolution); + + case 'Free': + return this.errorFor(expression.name, span, resolution); + + case 'InterpolateExpression': + return this.InterpolateExpression(expression, span, resolution); + + case 'CallExpression': + return this.CallExpression(expression, span, resolution ?? ResolutionType.Helper); + + case 'Not': + return this.Expression(expression.value, span, resolution); + + case 'IfInline': + return this.IfInline(expression); + + case 'Curry': + return this.Curry(expression); + + case 'Log': + return this.Log(expression); + } + } + + Args(args: mir.Args): Result { + return this.Positional(args.positional).andThen(() => this.NamedArguments(args.named)); + } + + Positional(positional: mir.Positional, span?: HasSourceSpan): Result { + let result = Ok(null); + let expressions = positional.list.toArray(); + + // For cases like {{yield foo}}, when there is only a single argument, it + // makes for a slightly better error to report that entire span. However, + // when there are more than one, we need to be specific. + if (expressions.length === 1) { + result = this.Expression(expressions[0]!, span); + } else { + result = this.Expressions(expressions); + } + + return result; + } + + NamedArguments({ entries }: mir.NamedArguments): Result { + let result = Ok(null); + + for (let arg of entries.toArray()) { + result = result.andThen(() => this.NamedArgument(arg)); + } + + return result; + } + + NamedArgument(arg: mir.NamedArgument): Result { + if (arg.value.type === 'CallExpression') { + return this.Expression(arg.value, arg, ResolutionType.Helper); + } else { + return this.Expression(arg.value, arg); + } + } + + ElementParameters({ body }: mir.ElementParameters): Result { + let result = Ok(null); + + for (let param of body.toArray()) { + result = result.andThen(() => this.ElementParameter(param)); + } + + return result; + } + + ElementParameter(param: mir.ElementParameter): Result { + switch (param.type) { + case 'StaticAttr': + return Ok(null); + case 'DynamicAttr': + return this.DynamicAttr(param); + case 'Modifier': + return this.Modifier(param); + case 'SplatAttr': + return Ok(null); + } + } + + DynamicAttr(attr: mir.DynamicAttr): Result { + if (attr.value.type === 'CallExpression') { + return this.Expression(attr.value, attr, ResolutionType.Helper); + } else { + return this.Expression(attr.value, attr); + } + } + + Modifier(modifier: mir.Modifier): Result { + return this.Expression(modifier.callee, modifier, ResolutionType.Modifier).andThen(() => + this.Args(modifier.args) + ); + } + + InElement(inElement: mir.InElement): Result { + return ( + this.Expression(inElement.destination) + // Unfortunately we lost the `insertBefore=` part of the span + .andThen(() => this.Expression(inElement.insertBefore)) + .andThen(() => this.NamedBlock(inElement.block)) + ); + } + + Yield(statement: mir.Yield): Result { + return this.Positional(statement.positional, statement); + } + + AppendTrustedHTML(statement: mir.AppendTrustedHTML): Result { + return this.Expression(statement.html, statement); + } + + AppendTextNode(statement: mir.AppendTextNode): Result { + if (statement.text.type === 'CallExpression') { + return this.Expression(statement.text, statement, ResolutionType.ComponentOrHelper); + } else { + return this.Expression(statement.text, statement); + } + } + + Component(statement: mir.Component): Result { + return this.Expression(statement.tag, statement, ResolutionType.Component) + .andThen(() => this.ElementParameters(statement.params)) + .andThen(() => this.NamedArguments(statement.args)) + .andThen(() => this.NamedBlocks(statement.blocks)); + } + + SimpleElement(statement: mir.SimpleElement): Result { + return this.ElementParameters(statement.params).andThen(() => this.Statements(statement.body)); + } + + InvokeBlock(statement: mir.InvokeBlock): Result { + return this.Expression(statement.head, statement.head, ResolutionType.Component) + .andThen(() => this.Args(statement.args)) + .andThen(() => this.NamedBlocks(statement.blocks)); + } + + If(statement: mir.If): Result { + return this.Expression(statement.condition, statement) + .andThen(() => this.NamedBlock(statement.block)) + .andThen(() => { + if (statement.inverse) { + return this.NamedBlock(statement.inverse); + } else { + return Ok(null); + } + }); + } + + Each(statement: mir.Each): Result { + return this.Expression(statement.value, statement) + .andThen(() => { + if (statement.key) { + return this.Expression(statement.key, statement); + } else { + return Ok(null); + } + }) + .andThen(() => this.NamedBlock(statement.block)) + .andThen(() => { + if (statement.inverse) { + return this.NamedBlock(statement.inverse); + } else { + return Ok(null); + } + }); + } + + Let(statement: mir.Let): Result { + return this.Positional(statement.positional).andThen(() => this.NamedBlock(statement.block)); + } + + WithDynamicVars(statement: mir.WithDynamicVars): Result { + return this.NamedArguments(statement.named).andThen(() => this.NamedBlock(statement.block)); + } + + InvokeComponent(statement: mir.InvokeComponent): Result { + return this.Expression(statement.definition, statement, ResolutionType.Component) + .andThen(() => this.Args(statement.args)) + .andThen(() => { + if (statement.blocks) { + return this.NamedBlocks(statement.blocks); + } else { + return Ok(null); + } + }); + } + + InterpolateExpression( + expression: mir.InterpolateExpression, + span: HasSourceSpan, + resolution?: ResolutionType + ): Result { + let expressions = expression.parts.toArray(); + + if (expressions.length === 1) { + return this.Expression(expressions[0], span, resolution); + } else { + return this.Expressions(expressions); + } + } + + CallExpression( + expression: mir.CallExpression, + span: HasSourceSpan, + resolution?: ResolutionType + ): Result { + return this.Expression(expression.callee, span, resolution).andThen(() => + this.Args(expression.args) + ); + } + + IfInline(expression: mir.IfInline): Result { + return this.Expression(expression.condition) + .andThen(() => this.Expression(expression.truthy)) + .andThen(() => { + if (expression.falsy) { + return this.Expression(expression.falsy); + } else { + return Ok(null); + } + }); + } + + Curry(expression: mir.Curry): Result { + let resolution: ResolutionType; + + if (expression.curriedType === CurriedTypes.Component) { + resolution = ResolutionType.Component; + } else if (expression.curriedType === CurriedTypes.Helper) { + resolution = ResolutionType.Helper; + } else { + resolution = ResolutionType.Modifier; + } + + return this.Expression(expression.definition, expression, resolution).andThen(() => + this.Args(expression.args) + ); + } + + Log(expression: mir.Log): Result { + return this.Positional(expression.positional, expression); + } + + errorFor(name: string, span: HasSourceSpan, type = ResolutionType.Value): Result { + return Err( + generateSyntaxError( + `Attempted to resolve a ${type} in a strict mode template, but that value was not in scope: ${name}`, + loc(span) + ) + ); + } +} diff --git a/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts b/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts index 2c15921d69..9c36287707 100644 --- a/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts +++ b/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts @@ -16,8 +16,6 @@ export class ExpressionEncoder { return this.Literal(expr); case 'CallExpression': return this.CallExpression(expr); - case 'DeprecatedCallExpression': - return this.DeprecatedCallExpression(expr); case 'PathExpression': return this.PathExpression(expr); case 'Arg': @@ -88,10 +86,6 @@ export class ExpressionEncoder { return [isTemplateLocal ? SexpOpcodes.GetLexicalSymbol : SexpOpcodes.GetSymbol, symbol]; } - GetWithResolver({ symbol }: mir.GetWithResolver): WireFormat.Expressions.GetContextualFree { - return [SexpOpcodes.GetFreeAsComponentOrHelperHeadOrThisFallback, symbol]; - } - PathExpression({ head, tail }: mir.PathExpression): WireFormat.Expressions.GetPath { let getOp = EXPR.expr(head) as WireFormat.Expressions.GetVar; @@ -106,13 +100,6 @@ export class ExpressionEncoder { return [SexpOpcodes.Call, EXPR.expr(callee), ...EXPR.Args(args)]; } - DeprecatedCallExpression({ - arg, - callee, - }: mir.DeprecatedCallExpression): WireFormat.Expressions.GetPathFreeAsDeprecatedHelperHeadOrThisFallback { - return [SexpOpcodes.GetFreeAsDeprecatedHelperHeadOrThisFallback, callee.symbol, [arg.chars]]; - } - Tail({ members }: mir.Tail): PresentArray { return mapPresentArray(members, (member) => member.chars); } diff --git a/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts b/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts index c743f5670f..8eb1f49187 100644 --- a/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts +++ b/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts @@ -76,7 +76,6 @@ export class NamedBlock extends node('NamedBlock').fields<{ name: SourceSlice; body: Statement[]; }>() {} -export class EndBlock extends node('EndBlock').fields() {} export class AppendTrustedHTML extends node('AppendTrustedHTML').fields<{ html: ExpressionNode; }>() {} @@ -136,10 +135,6 @@ export class CallExpression extends node('CallExpression').fields<{ callee: ExpressionNode; args: Args; }>() {} -export class DeprecatedCallExpression extends node('DeprecatedCallExpression').fields<{ - arg: SourceSlice; - callee: ASTv2.FreeVarReference; -}>() {} export class Modifier extends node('Modifier').fields<{ callee: ExpressionNode; args: Args }>() {} export class InvokeBlock extends node('InvokeBlock').fields<{ @@ -152,19 +147,6 @@ export class PathExpression extends node('PathExpression').fields<{ head: ExpressionNode; tail: Tail; }>() {} -export class GetWithResolver extends node('GetWithResolver').fields<{ - symbol: number; -}>() {} - -export class GetSymbol extends node('GetSymbol').fields<{ symbol: number }>() {} -export class GetFreeWithContext extends node('GetFreeWithContext').fields<{ - symbol: number; - context: ASTv2.FreeVarResolution; -}>() {} -/** strict mode */ -export class GetFree extends node('GetFree').fields<{ - symbol: number; -}>() {} export class Missing extends node('Missing').fields() {} export class InterpolateExpression extends node('InterpolateExpression').fields<{ @@ -203,7 +185,6 @@ export type ExpressionNode = | ASTv2.VariableReference | InterpolateExpression | CallExpression - | DeprecatedCallExpression | Not | IfInline | HasBlock diff --git a/packages/@glimmer/compiler/lib/wire-format-debug.ts b/packages/@glimmer/compiler/lib/wire-format-debug.ts index ef087107f2..d37861f9f8 100644 --- a/packages/@glimmer/compiler/lib/wire-format-debug.ts +++ b/packages/@glimmer/compiler/lib/wire-format-debug.ts @@ -172,22 +172,9 @@ export default class WireFormatDebugger { case Op.GetStrictKeyword: return ['get-strict-free', this.upvars[opcode[1]], opcode[2]]; - case Op.GetFreeAsComponentOrHelperHeadOrThisFallback: - return [ - 'GetFreeAsComponentOrHelperHeadOrThisFallback', - this.upvars[opcode[1]], - opcode[2], - ]; - case Op.GetFreeAsComponentOrHelperHead: return ['GetFreeAsComponentOrHelperHead', this.upvars[opcode[1]], opcode[2]]; - case Op.GetFreeAsHelperHeadOrThisFallback: - return ['GetFreeAsHelperHeadOrThisFallback', this.upvars[opcode[1]], opcode[2]]; - - case Op.GetFreeAsDeprecatedHelperHeadOrThisFallback: - return ['GetFreeAsDeprecatedHelperHeadOrThisFallback', this.upvars[opcode[1]]]; - case Op.GetFreeAsHelperHead: return ['GetFreeAsHelperHead', this.upvars[opcode[1]], opcode[2]]; diff --git a/packages/@glimmer/compiler/test/compiler-test.ts b/packages/@glimmer/compiler/test/compiler-test.ts index 8eee50e5bf..839fa80ce1 100644 --- a/packages/@glimmer/compiler/test/compiler-test.ts +++ b/packages/@glimmer/compiler/test/compiler-test.ts @@ -380,9 +380,9 @@ test('curlies right next to each other', `
{{a}}{{b}}{{c}}wat{{d}}
`, [ ['^a', '^b', '^c', s`wat`, '^d'], ]); -test('paths', `
{{model.foo.bar}}{{model.foo.bar}}
`, [ +test('paths', `
{{this.model.foo.bar}}{{this.model.foo.bar}}
`, [ '
', - ['^model.foo.bar', ['', ['^model.foo.bar']]], + ['this.model.foo.bar', ['', ['this.model.foo.bar']]], ]); test('whitespace', `Hello {{ foo }} `, s`Hello `, '^foo', s` `); diff --git a/packages/@glimmer/interfaces/lib/compile/encoder.ts b/packages/@glimmer/interfaces/lib/compile/encoder.ts index b4d3a06376..9e57e967dd 100644 --- a/packages/@glimmer/interfaces/lib/compile/encoder.ts +++ b/packages/@glimmer/interfaces/lib/compile/encoder.ts @@ -19,10 +19,8 @@ export type HighLevelBuilderOpcode = HighLevelLabel | HighLevelStartLabels | Hig export type HighLevelResolveModifier = 1003; export type HighLevelResolveComponent = 1004; export type HighLevelResolveHelper = 1005; -export type HighLevelResolveOptionalHelper = 1006; export type HighLevelResolveComponentOrHelper = 1007; export type HighLevelResolveOptionalComponentOrHelper = 1008; -export type HighLevelResolveFree = 1009; export type HighLevelResolveLocal = 1010; export type HighLevelResolveTemplateLocal = 1011; export type HighLevelResolveStart = HighLevelResolveModifier; @@ -32,10 +30,8 @@ export type HighLevelResolutionOpcode = | HighLevelResolveModifier | HighLevelResolveComponent | HighLevelResolveHelper - | HighLevelResolveOptionalHelper | HighLevelResolveComponentOrHelper | HighLevelResolveOptionalComponentOrHelper - | HighLevelResolveFree | HighLevelResolveLocal | HighLevelResolveTemplateLocal; @@ -85,14 +81,6 @@ export type ResolveHelperOp = [ op2: (handle: number) => void, ]; -export type ResolveOptionalHelperOp = [ - op: HighLevelResolveOptionalHelper, - op1: WireFormat.Expressions.Expression, - op2: { - ifHelper: (handle: number, name: string, moduleName: string) => void; - }, -]; - export type ResolveOptionalComponentOrHelperOp = [ op: HighLevelResolveOptionalComponentOrHelper, op1: WireFormat.Expressions.Expression, @@ -103,8 +91,6 @@ export type ResolveOptionalComponentOrHelperOp = [ }, ]; -export type ResolveFreeOp = [op: HighLevelResolveFree, op1: number, op2: (handle: number) => void]; - export type ResolveTemplateLocalOp = [ op: HighLevelResolveTemplateLocal, op1: number, @@ -122,9 +108,7 @@ export type HighLevelResolutionOp = | ResolveComponentOp | ResolveComponentOrHelperOp | ResolveHelperOp - | ResolveOptionalHelperOp | ResolveOptionalComponentOrHelperOp - | ResolveFreeOp | ResolveTemplateLocalOp | ResolveLocalOp; diff --git a/packages/@glimmer/interfaces/lib/compile/wire-format/api.d.ts b/packages/@glimmer/interfaces/lib/compile/wire-format/api.d.ts index f10ec4758f..37ad28ad85 100644 --- a/packages/@glimmer/interfaces/lib/compile/wire-format/api.d.ts +++ b/packages/@glimmer/interfaces/lib/compile/wire-format/api.d.ts @@ -21,10 +21,7 @@ import type { GetDynamicVarOpcode, GetFreeAsComponentHeadOpcode, GetFreeAsComponentOrHelperHeadOpcode, - GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode, - GetFreeAsDeprecatedHelperHeadOrThisFallbackOpcode, GetFreeAsHelperHeadOpcode, - GetFreeAsHelperHeadOrThisFallbackOpcode, GetFreeAsModifierHeadOpcode, GetLexicalSymbolOpcode, GetStrictKeywordOpcode, @@ -99,25 +96,13 @@ export namespace Expressions { export type GetSymbol = [GetSymbolOpcode, number]; export type GetLexicalSymbol = [GetLexicalSymbolOpcode, number]; export type GetStrictFree = [GetStrictKeywordOpcode, number]; - export type GetFreeAsComponentOrHelperHeadOrThisFallback = [ - GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode, - number, - ]; export type GetFreeAsComponentOrHelperHead = [GetFreeAsComponentOrHelperHeadOpcode, number]; - export type GetFreeAsHelperHeadOrThisFallback = [GetFreeAsHelperHeadOrThisFallbackOpcode, number]; - export type GetFreeAsDeprecatedHelperHeadOrThisFallback = [ - GetFreeAsDeprecatedHelperHeadOrThisFallbackOpcode, - number, - ]; export type GetFreeAsHelperHead = [GetFreeAsHelperHeadOpcode, number]; export type GetFreeAsModifierHead = [GetFreeAsModifierHeadOpcode, number]; export type GetFreeAsComponentHead = [GetFreeAsComponentHeadOpcode, number]; export type GetContextualFree = - | GetFreeAsComponentOrHelperHeadOrThisFallback | GetFreeAsComponentOrHelperHead - | GetFreeAsHelperHeadOrThisFallback - | GetFreeAsDeprecatedHelperHeadOrThisFallback | GetFreeAsHelperHead | GetFreeAsModifierHead | GetFreeAsComponentHead; @@ -127,35 +112,17 @@ export namespace Expressions { export type GetPathSymbol = [GetSymbolOpcode, number, Path]; export type GetPathTemplateSymbol = [GetLexicalSymbolOpcode, number, Path]; export type GetPathStrictFree = [GetStrictKeywordOpcode, number, Path]; - export type GetPathFreeAsComponentOrHelperHeadOrThisFallback = [ - GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode, - number, - Path, - ]; export type GetPathFreeAsComponentOrHelperHead = [ GetFreeAsComponentOrHelperHeadOpcode, number, Path, ]; - export type GetPathFreeAsHelperHeadOrThisFallback = [ - GetFreeAsHelperHeadOrThisFallbackOpcode, - number, - Path, - ]; - export type GetPathFreeAsDeprecatedHelperHeadOrThisFallback = [ - GetFreeAsDeprecatedHelperHeadOrThisFallbackOpcode, - number, - Path, - ]; export type GetPathFreeAsHelperHead = [GetFreeAsHelperHeadOpcode, number, Path]; export type GetPathFreeAsModifierHead = [GetFreeAsModifierHeadOpcode, number, Path]; export type GetPathFreeAsComponentHead = [GetFreeAsComponentHeadOpcode, number, Path]; export type GetPathContextualFree = - | GetPathFreeAsComponentOrHelperHeadOrThisFallback | GetPathFreeAsComponentOrHelperHead - | GetPathFreeAsHelperHeadOrThisFallback - | GetPathFreeAsDeprecatedHelperHeadOrThisFallback | GetPathFreeAsHelperHead | GetPathFreeAsModifierHead | GetPathFreeAsComponentHead; diff --git a/packages/@glimmer/interfaces/lib/compile/wire-format/opcodes.d.ts b/packages/@glimmer/interfaces/lib/compile/wire-format/opcodes.d.ts index 91d8270019..56781bbfff 100644 --- a/packages/@glimmer/interfaces/lib/compile/wire-format/opcodes.d.ts +++ b/packages/@glimmer/interfaces/lib/compile/wire-format/opcodes.d.ts @@ -41,14 +41,8 @@ export type GetLexicalSymbolOpcode = 32; // FIXME: Why does this make it to the wire format in the first place? export type GetStrictKeywordOpcode = 31; -// `{{x}}` in append position (might be a helper or component invocation, otherwise fall back to `this`) -export type GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode = 34; // a component or helper (`{{ x}}` in append position) export type GetFreeAsComponentOrHelperHeadOpcode = 35; -// a helper or `this` fallback `attr={{x}}` -export type GetFreeAsHelperHeadOrThisFallbackOpcode = 36; -// a helper or `this` fallback (deprecated) `@arg={{x}}` -export type GetFreeAsDeprecatedHelperHeadOrThisFallbackOpcode = 99; // a call head `(x)` export type GetFreeAsHelperHeadOpcode = 37; export type GetFreeAsModifierHeadOpcode = 38; @@ -58,7 +52,6 @@ export type GetFreeAsComponentHeadOpcode = 39; export type InElementOpcode = 40; export type IfOpcode = 41; export type EachOpcode = 42; -export type WithOpcode = 43; export type LetOpcode = 44; export type WithDynamicVarsOpcode = 45; export type InvokeComponentOpcode = 46; @@ -74,14 +67,10 @@ export type LogOpcode = 54; export type GetStartOpcode = GetSymbolOpcode; export type GetEndOpcode = GetFreeAsComponentHeadOpcode; -export type GetLooseFreeStartOpcode = GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode; export type GetLooseFreeEndOpcode = GetFreeAsComponentHeadOpcode; -export type GetContextualFreeStartOpcode = GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode; export type GetContextualFreeOpcode = - | GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode | GetFreeAsComponentOrHelperHeadOpcode - | GetFreeAsHelperHeadOrThisFallbackOpcode | GetFreeAsHelperHeadOpcode | GetFreeAsModifierHeadOpcode | GetFreeAsComponentHeadOpcode diff --git a/packages/@glimmer/interfaces/lib/compile/wire-format/resolution.d.ts b/packages/@glimmer/interfaces/lib/compile/wire-format/resolution.d.ts index 97f1b81cc2..23fce44bfc 100644 --- a/packages/@glimmer/interfaces/lib/compile/wire-format/resolution.d.ts +++ b/packages/@glimmer/interfaces/lib/compile/wire-format/resolution.d.ts @@ -2,9 +2,7 @@ * A VariableResolutionContext explains how a variable name should be resolved. */ export type StrictResolution = 0; -export type AmbiguousAppendResolution = 1; -export type AmbiguousAppendInvokeResolution = 2; -export type AmbiguousInvokeResolution = 3; -export type ResolveAsCallHeadResolution = 5; +export type ResolveAsComponentOrHelperHeadResolution = 1; +export type ResolveAsHelperHeadResolution = 5; export type ResolveAsModifierHeadResolution = 6; export type ResolveAsComponentHeadResolution = 7; diff --git a/packages/@glimmer/opcode-compiler/lib/opcode-builder/encoder.ts b/packages/@glimmer/opcode-compiler/lib/opcode-builder/encoder.ts index 0a4faf9384..0a11cefbb3 100644 --- a/packages/@glimmer/opcode-compiler/lib/opcode-builder/encoder.ts +++ b/packages/@glimmer/opcode-compiler/lib/opcode-builder/encoder.ts @@ -35,7 +35,6 @@ import { resolveHelper, resolveModifier, resolveOptionalComponentOrHelper, - resolveOptionalHelper, } from './helpers/resolution'; import { HighLevelBuilderOpcodes, HighLevelResolutionOpcodes } from './opcodes'; import { HighLevelOperands } from './operands'; @@ -83,7 +82,6 @@ export function encodeOp( return encoder.startLabels(); case HighLevelBuilderOpcodes.StopLabels: return encoder.stopLabels(); - case HighLevelResolutionOpcodes.Component: return resolveComponent(resolver, constants, meta, op); case HighLevelResolutionOpcodes.Modifier: @@ -92,8 +90,6 @@ export function encodeOp( return resolveHelper(resolver, constants, meta, op); case HighLevelResolutionOpcodes.ComponentOrHelper: return resolveComponentOrHelper(resolver, constants, meta, op); - case HighLevelResolutionOpcodes.OptionalHelper: - return resolveOptionalHelper(resolver, constants, meta, op); case HighLevelResolutionOpcodes.OptionalComponentOrHelper: return resolveOptionalComponentOrHelper(resolver, constants, meta, op); @@ -113,7 +109,7 @@ export function encodeOp( let [, valueIndex, then] = op; let value = expect( meta.scopeValues, - 'BUG: Attempted to gect a template local, but template does not have any' + 'BUG: Attempted to get a template local, but template does not have any' )[valueIndex]; then(constants.value(value)); @@ -121,19 +117,6 @@ export function encodeOp( break; } - case HighLevelResolutionOpcodes.Free: - if (import.meta.env.DEV) { - let [, upvarIndex] = op; - let freeName = expect(meta.upvars, 'BUG: attempted to resolve value but no upvars found')[ - upvarIndex - ]; - - throw new Error( - `Attempted to resolve a value in a strict mode template, but that value was not in scope: ${freeName}` - ); - } - break; - default: throw new Error(`Unexpected high level opcode ${op[0]}`); } diff --git a/packages/@glimmer/opcode-compiler/lib/opcode-builder/helpers/resolution.ts b/packages/@glimmer/opcode-compiler/lib/opcode-builder/helpers/resolution.ts index 6b8349eded..92a6580cd9 100644 --- a/packages/@glimmer/opcode-compiler/lib/opcode-builder/helpers/resolution.ts +++ b/packages/@glimmer/opcode-compiler/lib/opcode-builder/helpers/resolution.ts @@ -10,7 +10,6 @@ import type { ResolveHelperOp, ResolveModifierOp, ResolveOptionalComponentOrHelperOp, - ResolveOptionalHelperOp, SexpOpcode, } from '@glimmer/interfaces'; import { assert, debugToString, expect, unwrap } from '@glimmer/util'; @@ -46,22 +45,6 @@ export const isGetFreeComponentOrHelper = makeResolutionTypeVerifier( SexpOpcodes.GetFreeAsComponentOrHelperHead ); -export const isGetFreeOptionalHelper = makeResolutionTypeVerifier( - SexpOpcodes.GetFreeAsHelperHeadOrThisFallback -); - -export function isGetFreeDeprecatedHelper( - opcode: Expressions.Expression -): opcode is Expressions.GetPathFreeAsDeprecatedHelperHeadOrThisFallback { - return ( - Array.isArray(opcode) && opcode[0] === SexpOpcodes.GetFreeAsDeprecatedHelperHeadOrThisFallback - ); -} - -export const isGetFreeOptionalComponentOrHelper = makeResolutionTypeVerifier( - SexpOpcodes.GetFreeAsComponentOrHelperHeadOrThisFallback -); - interface ResolvedContainingMetadata extends ContainingMetadata { owner: Owner; upvars: string[]; @@ -101,6 +84,8 @@ export function resolveComponent( let type = expr[0]; if (import.meta.env.DEV && expr[0] === SexpOpcodes.GetStrictKeyword) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + throw new Error( `Attempted to resolve a component in a strict mode template, but that value was not in scope: ${ meta.upvars![expr[1]] ?? '{unknown variable}' @@ -127,6 +112,8 @@ export function resolveComponent( let definition = resolver.lookupComponent(name, owner)!; if (import.meta.env.DEV && (typeof definition !== 'object' || definition === null)) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + throw new Error( `Attempted to resolve \`${name}\`, which was expected to be a component, but nothing was found.` ); @@ -168,6 +155,8 @@ export function resolveHelper( let helper = resolver.lookupHelper(name, owner)!; if (import.meta.env.DEV && helper === null) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + throw new Error( `Attempted to resolve \`${name}\`, which was expected to be a helper, but nothing was found.` ); @@ -205,6 +194,8 @@ export function resolveModifier( let modifier = resolver.lookupBuiltInModifier(name); if (import.meta.env.DEV && modifier === null) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + throw new Error( `Attempted to resolve a modifier in a strict mode template, but it was not in scope: ${name}` ); @@ -217,6 +208,8 @@ export function resolveModifier( let modifier = resolver.lookupModifier(name, owner)!; if (import.meta.env.DEV && modifier === null) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + throw new Error( `Attempted to resolve \`${name}\`, which was expected to be a modifier, but nothing was found.` ); @@ -262,6 +255,8 @@ export function resolveComponentOrHelper( let helper = constants.helper(definition as object, null, true); if (import.meta.env.DEV && helper === null) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + throw new Error( `Attempted to use a value as either a component or helper, but it did not have a component manager or helper manager associated with it. The value was: ${debugToString!( definition @@ -292,6 +287,8 @@ export function resolveComponentOrHelper( let helper = resolver.lookupHelper(name, owner); if (import.meta.env.DEV && helper === null) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + throw new Error( `Attempted to resolve \`${name}\`, which was expected to be a component or helper, but nothing was found.` ); @@ -302,29 +299,6 @@ export function resolveComponentOrHelper( } } -/** - * - */ -export function resolveOptionalHelper( - resolver: CompileTimeResolver, - constants: CompileTimeConstants & ResolutionTimeConstants, - meta: ContainingMetadata, - [, expr, { ifHelper }]: ResolveOptionalHelperOp -): void { - assert( - isGetFreeOptionalHelper(expr) || isGetFreeDeprecatedHelper(expr), - 'Attempted to resolve a helper with incorrect opcode' - ); - let { upvars, owner } = assertResolverInvariants(meta); - - let name = unwrap(upvars[expr[1]]); - let helper = resolver.lookupHelper(name, owner); - - if (helper) { - ifHelper(constants.helper(helper, name), name, meta.moduleName); - } -} - /** * {{maybeHelperOrComponent}} */ @@ -335,7 +309,7 @@ export function resolveOptionalComponentOrHelper( [, expr, { ifComponent, ifHelper, ifValue }]: ResolveOptionalComponentOrHelperOp ): void { assert( - isGetFreeOptionalComponentOrHelper(expr), + isGetFreeComponentOrHelper(expr), 'Attempted to resolve an optional component or helper with incorrect opcode' ); @@ -411,6 +385,8 @@ function lookupBuiltInHelper( let helper = resolver.lookupBuiltInHelper(name); if (import.meta.env.DEV && helper === null) { + assert(!meta.isStrictMode, 'Strict mode errors should already be handled at compile time'); + // Keyword helper did not exist, which means that we're attempting to use a // value of some kind that is not in scope throw new Error( diff --git a/packages/@glimmer/opcode-compiler/lib/opcode-builder/opcodes.ts b/packages/@glimmer/opcode-compiler/lib/opcode-builder/opcodes.ts index 0b7fb7d2ad..0303482c90 100644 --- a/packages/@glimmer/opcode-compiler/lib/opcode-builder/opcodes.ts +++ b/packages/@glimmer/opcode-compiler/lib/opcode-builder/opcodes.ts @@ -3,12 +3,10 @@ import type { HighLevelLabel, HighLevelResolveComponent, HighLevelResolveComponentOrHelper, - HighLevelResolveFree, HighLevelResolveHelper, HighLevelResolveLocal, HighLevelResolveModifier, HighLevelResolveOptionalComponentOrHelper, - HighLevelResolveOptionalHelper, HighLevelResolveTemplateLocal, HighLevelStart, HighLevelStartLabels, @@ -19,10 +17,8 @@ export const HighLevelResolutionOpcodes = { Modifier: 1003 satisfies HighLevelResolveModifier, Component: 1004 satisfies HighLevelResolveComponent, Helper: 1005 satisfies HighLevelResolveHelper, - OptionalHelper: 1006 satisfies HighLevelResolveOptionalHelper, ComponentOrHelper: 1007 satisfies HighLevelResolveComponentOrHelper, OptionalComponentOrHelper: 1008 satisfies HighLevelResolveOptionalComponentOrHelper, - Free: 1009 satisfies HighLevelResolveFree, Local: 1010 satisfies HighLevelResolveLocal, TemplateLocal: 1011 satisfies HighLevelResolveTemplateLocal, } as const; diff --git a/packages/@glimmer/opcode-compiler/lib/syntax/expressions.ts b/packages/@glimmer/opcode-compiler/lib/syntax/expressions.ts index a39b159f8d..1cf3e675af 100644 --- a/packages/@glimmer/opcode-compiler/lib/syntax/expressions.ts +++ b/packages/@glimmer/opcode-compiler/lib/syntax/expressions.ts @@ -1,5 +1,4 @@ import type { ExpressionSexpOpcode } from '@glimmer/interfaces'; -import { assert, deprecate } from '@glimmer/global-context'; import { $v0, MachineOp, Op } from '@glimmer/vm'; import { SexpOpcodes } from '@glimmer/wire-format'; @@ -49,62 +48,18 @@ EXPRESSIONS.add(SexpOpcodes.GetLexicalSymbol, (op, [, sym, path]) => { }); }); -EXPRESSIONS.add(SexpOpcodes.GetStrictKeyword, (op, [, sym, _path]) => { - op(HighLevelResolutionOpcodes.Free, sym, (_handle: unknown) => { - // TODO: Implement in strict mode - }); -}); - -EXPRESSIONS.add(SexpOpcodes.GetFreeAsComponentOrHelperHeadOrThisFallback, () => { - // TODO: The logic for this opcode currently exists in STATEMENTS.Append, since - // we want different wrapping logic depending on if we are invoking a component, - // helper, or {{this}} fallback. Eventually we fix the opcodes so that we can - // traverse the subexpression tree like normal in this location. - throw new Error('unimplemented opcode'); -}); - -EXPRESSIONS.add(SexpOpcodes.GetFreeAsHelperHeadOrThisFallback, (op, expr) => { - //
- +EXPRESSIONS.add(SexpOpcodes.GetStrictKeyword, (op, expr) => { op(HighLevelResolutionOpcodes.Local, expr[1], (_name: string) => { - op(HighLevelResolutionOpcodes.OptionalHelper, expr, { - ifHelper: (handle: number) => { - Call(op, handle, null, null); - }, + op(HighLevelResolutionOpcodes.Helper, expr, (handle: number) => { + Call(op, handle, null, null); }); }); }); -EXPRESSIONS.add(SexpOpcodes.GetFreeAsDeprecatedHelperHeadOrThisFallback, (op, expr) => { - // - +EXPRESSIONS.add(SexpOpcodes.GetFreeAsHelperHead, (op, expr) => { op(HighLevelResolutionOpcodes.Local, expr[1], (_name: string) => { - op(HighLevelResolutionOpcodes.OptionalHelper, expr, { - ifHelper: (handle: number, name: string, moduleName: string) => { - assert(expr[2] && expr[2].length === 1, '[BUG] Missing argument name'); - - let arg = expr[2][0]; - - deprecate( - `The \`${name}\` helper was used in the \`${moduleName}\` template as \`${arg}={{${name}}}\`. ` + - `This is ambigious between wanting the \`${arg}\` argument to be the \`${name}\` helper itself, ` + - `or the result of invoking the \`${name}\` helper (current behavior). ` + - `This implicit invocation behavior has been deprecated.\n\n` + - `Instead, please explicitly invoke the helper with parenthesis, i.e. \`${arg}={{(${name})}}\`.\n\n` + - `Note: the parenthesis are only required in this exact scenario where an ambiguity is present – where ` + - `\`${name}\` referes to a global helper (as opposed to a local variable), AND ` + - `the \`${name}\` helper invocation does not take any arguments, AND ` + - `this occurs in a named argument position of a component invocation.\n\n` + - `We expect this combination to be quite rare, as most helpers require at least one argument. ` + - `There is no need to refactor helper invocations in cases where this deprecation was not triggered.`, - false, - { - id: 'argument-less-helper-paren-less-invocation', - } - ); - - Call(op, handle, null, null); - }, + op(HighLevelResolutionOpcodes.Helper, expr, (handle: number) => { + Call(op, handle, null, null); }); }); }); diff --git a/packages/@glimmer/opcode-compiler/lib/syntax/statements.ts b/packages/@glimmer/opcode-compiler/lib/syntax/statements.ts index a4a50b4207..7228aa61fb 100644 --- a/packages/@glimmer/opcode-compiler/lib/syntax/statements.ts +++ b/packages/@glimmer/opcode-compiler/lib/syntax/statements.ts @@ -26,7 +26,6 @@ import { isGetFreeComponent, isGetFreeComponentOrHelper, isGetFreeModifier, - isGetFreeOptionalComponentOrHelper, } from '../opcode-builder/helpers/resolution'; import { CompilePositional, SimpleArgs } from '../opcode-builder/helpers/shared'; import { @@ -140,7 +139,7 @@ STATEMENTS.add(SexpOpcodes.Append, (op, [, value]) => { // Special case for static values if (!Array.isArray(value)) { op(Op.Text, value === null || value === undefined ? '' : String(value)); - } else if (isGetFreeOptionalComponentOrHelper(value)) { + } else if (isGetFreeComponentOrHelper(value)) { op(HighLevelResolutionOpcodes.OptionalComponentOrHelper, value, { ifComponent(component: CompileTimeComponent) { InvokeComponent(op, component, null, null, null, null); diff --git a/packages/@glimmer/syntax/lib/keywords.ts b/packages/@glimmer/syntax/lib/keywords.ts index 56b8363593..cf62782a92 100644 --- a/packages/@glimmer/syntax/lib/keywords.ts +++ b/packages/@glimmer/syntax/lib/keywords.ts @@ -1,7 +1,20 @@ +export type Keywords = keyof typeof KEYWORDS_TYPES; export type KeywordType = 'Call' | 'Modifier' | 'Append' | 'Block'; -export function isKeyword(word: string): boolean { - return word in KEYWORDS_TYPES; +export function isKeyword(word: string): word is Keywords; +export function isKeyword(word: string, type: KeywordType): boolean; +export function isKeyword(word: string, type?: KeywordType): boolean { + if (word in KEYWORDS_TYPES) { + if (type === undefined) { + return true; + } else { + let types = KEYWORDS_TYPES[word as Keywords]; + // This seems like a TypeScript bug – it inferred types as never[]? + return types.includes(type as never); + } + } else { + return false; + } } /** @@ -9,6 +22,7 @@ export function isKeyword(word: string): boolean { * language, and where their valid usages are. */ export const KEYWORDS_TYPES = { + action: ['Call', 'Modifier'], component: ['Call', 'Append', 'Block'], debugger: ['Append'], 'each-in': ['Block'], @@ -19,16 +33,13 @@ export const KEYWORDS_TYPES = { if: ['Call', 'Append', 'Block'], 'in-element': ['Block'], let: ['Block'], - 'link-to': ['Append', 'Block'], log: ['Call', 'Append'], - modifier: ['Call'], + modifier: ['Call', 'Modifier'], mount: ['Append'], mut: ['Call', 'Append'], outlet: ['Append'], - 'query-params': ['Call'], readonly: ['Call', 'Append'], unbound: ['Call', 'Append'], unless: ['Call', 'Append', 'Block'], - with: ['Block'], yield: ['Append'], } satisfies Record; diff --git a/packages/@glimmer/syntax/lib/v2/README.md b/packages/@glimmer/syntax/lib/v2/README.md index 68f134cac3..acb6f2fc37 100644 --- a/packages/@glimmer/syntax/lib/v2/README.md +++ b/packages/@glimmer/syntax/lib/v2/README.md @@ -69,7 +69,7 @@ In `ASTv2`, every variable name is represented as a `VariableReference`. **Important Note**: The remainder of this README is a description of the loose mode rules for free variable resolution. Strict mode free variable references always refer to an in-scope JavaScript binding, regardless of their syntactic position. -RFC [#496][#496] (Handlebars Strict Mode) rationalized the rules for loose mode. This README describes the semantics of [#496][#496] in terms of namespaced free variable references and fallback semantics. +RFC [#496][#496] (Handlebars Strict Mode) rationalized the rules for loose mode. This README describes the semantics of [#496][#496] in terms of namespaced free variable references. [#496]: https://emberjs.github.io/rfcs/0496-handlebars-strict-mode.html @@ -77,10 +77,7 @@ RFC [#496][#496] (Handlebars Strict Mode) rationalized the rules for loose mode. There are two significant differences between strict and loose mode that affect the AST. -In loose mode: - -1. Certain free variable references fall back to a property lookup on `this`. -2. Free variable references in "call" positions are resolved using a contextual namespace. For example, the free variable reference `h` in `(h 123)` is resolved as `helper:h`. The free variable reference `h` in `

` is resolved as `modifier:h`. +In loose mode, free variable references in "call" positions are resolved using a contextual namespace. For example, the free variable reference `h` in `(h 123)` is resolved as `helper:h`. The free variable reference `h` in `

` is resolved as `modifier:h`. In strict mode, all free variable references refer to bindings provided by a JavaScript scope that the template will be embedded within. @@ -94,26 +91,6 @@ The `Strict` resolution applies to all free variables encountered while parsing None. Strict mode templates must be embedded in a JavaScript context where all free variable references are in scope. A compile-time error should be produced if free there are variable references that do not correspond to any in-scope variables. -### Fallback Semantics - -When a free variable resolution is said to have "fallback semantics", it means the following algorithm: - -1. Attempt to resolve the name in the namespaces for the resolution, if any. -2. If the name could not be resolved, resolve it as a property on `this`. - -> Note: A free variable resolution has fallback semantics if it's an append or attribute curly without arguments, or if it's a path in argument position. See the summary table below for full details. - -### Eval Mode Semantics - -When a free variable resolution has fallback semantics, it is also said to have "eval mode semantics", which means: - -1. If the template is evaluated in eval mode (i.e. as a partial), dynamically resolve the free variable in the context of the template that invoked the partial (the "invoker"): -1. If the variable name is in the local scope of the invoker, resolve it as a _local_ variable in the invoker's local scope -1. Otherwise: -1. if the invoker is also in eval mode, repeat the process with the invoker's invoker -1. if the invoker is not in eval mode, resolve the free variable using fallback semantics in the invoker's scope -1. Otherwise, resolve the free variable using fallback semantics in the current scope - ### Namespaced Variable Resolution | | | @@ -123,7 +100,6 @@ When a free variable resolution has fallback semantics, it is also said to have | Arguments? | Any | | | | | Namespace | see table below | -| Fallback semantics? | ⛔ | These resolutions occur in syntaxes that are definitely calls (e.g. subexpressions, blocks, modifiers, etc.). @@ -140,16 +116,15 @@ These resolutions occur in syntaxes that are definitely calls (e.g. subexpressio If the variable reference cannot be resolved in its namespace. -### Namespaced Resolution: Ambiguous Component or Helper +### Namespaced Resolution: Append | | | | ------------------- | ----------------------- | | Syntax Positions | append | | Path has dots? | ❌ | -| Arguments? | ➕ | +| Arguments? | Any | | | | | Namespace | `helper` or `component` | -| Fallback semantics? | ⛔ | This resolution occurs in append nodes with at least one argument, and when the path does not have dots (e.g. `{{hello world}}`). @@ -158,6 +133,7 @@ This resolution occurs in append nodes with at least one argument, and when the | situation | variable | namespace | | ------------------- | -------- | ------------------- | | `{{x y}}` as append | `x` | `ComponentOrHelper` | +| `{{x}}` as append | `x` | `ComponentOrHelper` | In this situation, the `x` may refer to: @@ -168,37 +144,7 @@ In this situation, the `x` may refer to: If the variable reference cannot be resolved in the `helper` or `component` namespaces. -### Ambiguous Resolution: Append Ambiguity - -| | | -| ------------------- | --------------------- | -| Syntax Positions | append | -| Path has dots? | ❌ | -| Arguments? | ❌ | -| | | -| Namespace | `helper`, `component` | -| Fallback semantics? | ✅ | - -This resolution occurs in append nodes with zero arguments, and when the path does not have dots (e.g. `{{hello}}`). - -#### Applicable Situations - -| situation | variable | ambiguity | -| ----------------- | -------- | --------- | -| `{{x}}` as append | `x` | `Append` | - -In this situation, the `x` may refer to: - -- a helper `x` -- a component `x` -- a local variable in partial scope -- `this.x`. - -#### Runtime Error Cases - -None. - -### Ambiguous Resolution: Attribute Ambiguity +### Namespaced Resolution: Attribute This resolution context occurs in attribute nodes with zero arguments, and when the path does not have dots. @@ -206,26 +152,24 @@ This resolution context occurs in attribute nodes with zero arguments, and when | ------------------- | ------------------------ | | Syntax Positions | attribute, interpolation | | Path has dots? | ❌ | -| Arguments? | ❌ | +| Arguments? | Any | | | | | Namespace | `helper` | -| Fallback semantics? | ✅ | #### Applicable Situations -| situation | variable | ambiguity | -| --------------------------------------------- | -------- | --------- | -| `

`
`` | `x` | `Attr` | +| situation | variable | namespace | +| ------------------------------------------------- | -------- | --------- | +| `

`
`
` | `x` | `Helper` | +| `

`
`
` | `x` | `Helper` | In this situation, the `x` may refer to: - a helper `x` -- a local variable in partial scope -- `this.x`. #### Runtime Error Cases -None. +If the variable reference cannot be resolved in the `helper` namespaces. ### Summary @@ -253,7 +197,6 @@ Situations that meet all three of these criteria are syntax errors: | ------------------- | --- | | Path has dots? | ❌ | | Arguments? | Any | -| Fallback semantics? | ⛔ | | Syntax Position | Example | | Namespace | | --------------- | ------------- | --- | ----------- | @@ -264,11 +207,11 @@ Situations that meet all three of these criteria are syntax errors: #### Append -| Syntax Position | Example | Dots? | Args? | | Namespace | Fallback? | -| --------------- | --------- | ----- | ----- | --- | --------------------- | --------- | -| `Append` | `{{x}}` | ❌ | ❌ | | `helper`, `component` | ✅ | -| `Append` | `{{x.y}}` | ➕ | ❌ | | None | ✅ | -| `Append` | `{{x y}}` | ❌ | ➕ | | `helper`, `component` | ⛔ | +| Syntax Position | Example | Dots? | Args? | | Namespace | +| --------------- | --------- | ----- | ----- | --- | --------------------- | +| `Append` | `{{x}}` | ❌ | ❌ | | `helper`, `component` | +| `Append` | `{{x.y}}` | ➕ | ❌ | | None | +| `Append` | `{{x y}}` | ❌ | ➕ | | `helper`, `component` | #### Attributes @@ -278,8 +221,8 @@ The `Attribute` syntax position includes: - the value of an element argument (`@title={{...}}`) - a part of an interpolation (`href="{{...}}.html"`) -| Syntax Position | Example | Dots? | Arguments? | | Namespace | Fallback? | -| --------------- | -------------- | ----- | ---------- | --- | --------- | --------- | -| `Attribute` | `href={{x}}` | ❌ | ❌ | | `helper` | ✅ | -| `Attribute` | `href={{x.y}}` | ➕ | ❌ | | None | ✅ | -| `Attribute` | `href={{x y}}` | ❌ | ➕ | | `helper` | ⛔ | +| Syntax Position | Example | Dots? | Arguments? | | Namespace | +| --------------- | -------------- | ----- | ---------- | --- | --------- | +| `Attribute` | `href={{x}}` | ❌ | ❌ | | `helper` | +| `Attribute` | `href={{x.y}}` | ➕ | ❌ | | None | +| `Attribute` | `href={{x y}}` | ❌ | ➕ | | `helper` | diff --git a/packages/@glimmer/syntax/lib/v2/builders.ts b/packages/@glimmer/syntax/lib/v2/builders.ts index 007a77ac70..3f109ac995 100644 --- a/packages/@glimmer/syntax/lib/v2/builders.ts +++ b/packages/@glimmer/syntax/lib/v2/builders.ts @@ -221,18 +221,6 @@ export class Builder { }); } - deprecatedCall( - arg: SourceSlice, - callee: ASTv2.FreeVarReference, - loc: SourceSpan - ): ASTv2.DeprecatedCallExpression { - return new ASTv2.DeprecatedCallExpression({ - loc, - arg, - callee, - }); - } - interpolate(parts: ASTv2.ExpressionNode[], loc: SourceSpan): ASTv2.InterpolateExpression { assertPresentArray(parts); diff --git a/packages/@glimmer/syntax/lib/v2/loose-resolution.ts b/packages/@glimmer/syntax/lib/v2/loose-resolution.ts index b7cb1dd888..9c4cd5e31f 100644 --- a/packages/@glimmer/syntax/lib/v2/loose-resolution.ts +++ b/packages/@glimmer/syntax/lib/v2/loose-resolution.ts @@ -34,50 +34,45 @@ export function BlockSyntaxContext(node: ASTv1.BlockStatement): ASTv2.FreeVarRes if (isSimpleCallee(node)) { return ASTv2.LooseModeResolution.namespaced(ASTv2.COMPONENT_NAMESPACE); } else { - return ASTv2.LooseModeResolution.fallback(); + return null; } } export function ComponentSyntaxContext(node: ASTv1.PathExpression): ASTv2.FreeVarResolution | null { if (isSimplePath(node)) { - return ASTv2.LooseModeResolution.namespaced(ASTv2.FreeVarNamespace.Component, true); + return ASTv2.LooseModeResolution.namespaced(ASTv2.COMPONENT_NAMESPACE, true); } else { return null; } } /** - * This corresponds to append positions (text curlies or attribute - * curlies). In strict mode, this also corresponds to arg curlies. + * This corresponds to attribute curlies (). + * In strict mode, this also corresponds to arg curlies. */ -export function AttrValueSyntaxContext(node: ASTv1.MustacheStatement): ASTv2.FreeVarResolution { - let isSimple = isSimpleCallee(node); - let isInvoke = isInvokeNode(node); - - if (isSimple) { - return isInvoke - ? ASTv2.LooseModeResolution.namespaced(ASTv2.FreeVarNamespace.Helper) - : ASTv2.LooseModeResolution.attr(); +export function AttrValueSyntaxContext( + node: ASTv1.MustacheStatement +): ASTv2.FreeVarResolution | null { + if (isSimpleCallee(node)) { + return ASTv2.LooseModeResolution.namespaced(ASTv2.HELPER_NAMESPACE); } else { - return isInvoke ? ASTv2.STRICT_RESOLUTION : ASTv2.LooseModeResolution.fallback(); + return null; } } /** - * This corresponds to append positions (text curlies or attribute - * curlies). In strict mode, this also corresponds to arg curlies. + * This corresponds to append positions text curlies. */ -export function AppendSyntaxContext(node: ASTv1.MustacheStatement): ASTv2.FreeVarResolution { +export function AppendSyntaxContext(node: ASTv1.MustacheStatement): ASTv2.FreeVarResolution | null { let isSimple = isSimpleCallee(node); - let isInvoke = isInvokeNode(node); let trusting = node.trusting; if (isSimple) { return trusting - ? ASTv2.LooseModeResolution.trustingAppend({ invoke: isInvoke }) - : ASTv2.LooseModeResolution.append({ invoke: isInvoke }); + ? ASTv2.LooseModeResolution.trustingAppend() + : ASTv2.LooseModeResolution.append(); } else { - return ASTv2.LooseModeResolution.fallback(); + return null; } } @@ -113,9 +108,7 @@ export type Resolution

= ( * ``` */ function isSimpleCallee(node: AstCallParts): boolean { - let path = node.path; - - return isSimplePath(path); + return isSimplePath(node.path); } type SimplePath = ASTv1.PathExpression & { head: ASTv1.VarHead }; @@ -127,10 +120,3 @@ function isSimplePath(node: ASTv1.Expression): node is SimplePath { return false; } } - -/** - * The call expression has at least one argument. - */ -function isInvokeNode(node: AstCallParts): boolean { - return node.params.length > 0 || node.hash.pairs.length > 0; -} diff --git a/packages/@glimmer/syntax/lib/v2/normalize.ts b/packages/@glimmer/syntax/lib/v2/normalize.ts index df1fea03cc..9d8e38c4f5 100644 --- a/packages/@glimmer/syntax/lib/v2/normalize.ts +++ b/packages/@glimmer/syntax/lib/v2/normalize.ts @@ -170,16 +170,16 @@ class ExpressionNormalizer { * * @see {SyntaxContext} */ - normalize(expr: ASTv1.Literal, resolution: ASTv2.FreeVarResolution): ASTv2.LiteralExpression; + normalize(expr: ASTv1.Literal): ASTv2.LiteralExpression; + normalize(expr: ASTv1.SubExpression): ASTv2.CallExpression; normalize( expr: ASTv1.MinimalPathExpression, resolution: ASTv2.FreeVarResolution ): ASTv2.PathExpression; - normalize(expr: ASTv1.SubExpression, resolution: ASTv2.FreeVarResolution): ASTv2.CallExpression; normalize(expr: ASTv1.Expression, resolution: ASTv2.FreeVarResolution): ASTv2.ExpressionNode; normalize( expr: ASTv1.Expression | ASTv1.MinimalPathExpression, - resolution: ASTv2.FreeVarResolution + resolution?: ASTv2.FreeVarResolution ): ASTv2.ExpressionNode { switch (expr.type) { case 'NullLiteral': @@ -189,6 +189,7 @@ class ExpressionNormalizer { case 'UndefinedLiteral': return this.block.builder.literal(expr.value, this.block.loc(expr.loc)); case 'PathExpression': + assert(resolution, '[BUG] resolution is required'); return this.path(expr, resolution); case 'SubExpression': { // expr.path used to incorrectly have the type ASTv1.Expression @@ -245,13 +246,13 @@ class ExpressionNormalizer { let { path, params, hash } = parts; let callee = this.normalize(path, context); - let paramList = params.map((p) => this.normalize(p, ASTv2.ARGUMENT_RESOLUTION)); + let paramList = params.map((p) => this.normalize(p, ASTv2.STRICT_RESOLUTION)); let paramLoc = SpanList.range(paramList, callee.loc.collapse('end')); let namedLoc = this.block.loc(hash.loc); let argsLoc = SpanList.range([paramLoc, namedLoc]); let positional = this.block.builder.positional( - params.map((p) => this.normalize(p, ASTv2.ARGUMENT_RESOLUTION)), + params.map((p) => this.normalize(p, ASTv2.STRICT_RESOLUTION)), paramLoc ); @@ -273,7 +274,7 @@ class ExpressionNormalizer { return this.block.builder.namedArgument( new SourceSlice({ chars: pair.key, loc: keyOffsets }), - this.normalize(pair.value, ASTv2.ARGUMENT_RESOLUTION) + this.normalize(pair.value, ASTv2.STRICT_RESOLUTION) ); } @@ -377,16 +378,24 @@ class StatementNormalizer { MustacheStatement(mustache: ASTv1.MustacheStatement): ASTv2.AppendContent { let { path, params, hash, trusting } = mustache; let loc = this.block.loc(mustache.loc); - let context = AppendSyntaxContext(mustache); let value: ASTv2.ExpressionNode; if (isLiteral(path)) { if (params.length === 0 && hash.pairs.length === 0) { - value = this.expr.normalize(path, context); + value = this.expr.normalize(path); } else { assertIllegalLiteral(path, loc); } } else { + let resolution = this.block.resolutionFor(mustache, AppendSyntaxContext); + + if (resolution.result === 'error') { + throw generateSyntaxError( + `You attempted to render a path (\`{{${resolution.path}}}\`), but ${resolution.head} was not in scope`, + loc + ); + } + // Normalize the call parts in AppendSyntaxContext let callParts = this.expr.callParts( { @@ -394,7 +403,7 @@ class StatementNormalizer { params, hash, }, - AppendSyntaxContext(mustache) + resolution.result ); value = callParts.args.isEmpty() ? callParts.callee : this.block.builder.sexp(callParts, loc); @@ -540,7 +549,7 @@ class ElementNormalizer { if (resolution.result === 'error') { throw generateSyntaxError( - `You attempted to invoke a path (\`{{#${resolution.path}}}\`) as a modifier, but ${resolution.head} was not in scope. Try adding \`this\` to the beginning of the path`, + `You attempted to invoke a path (\`{{${resolution.path}}}\`) as a modifier, but ${resolution.head} was not in scope`, m.loc ); } @@ -560,20 +569,28 @@ class ElementNormalizer { */ private mustacheAttr(mustache: ASTv1.MustacheStatement): ASTv2.ExpressionNode { let { path, params, hash, loc } = mustache; - let context = AttrValueSyntaxContext(mustache); if (isLiteral(path)) { if (params.length === 0 && hash.pairs.length === 0) { - return this.expr.normalize(path, context); + return this.expr.normalize(path); } else { assertIllegalLiteral(path, loc); } } // Normalize the call parts in AttrValueSyntaxContext + let resolution = this.ctx.resolutionFor(mustache, AttrValueSyntaxContext); + + if (resolution.result === 'error') { + throw generateSyntaxError( + `You attempted to render a path (\`{{${resolution.path}}}\`), but ${resolution.head} was not in scope`, + mustache.loc + ); + } + let sexp = this.ctx.builder.sexp( - this.expr.callParts(mustache as ASTv1.CallParts, AttrValueSyntaxContext(mustache)), - this.ctx.loc(loc) + this.expr.callParts(mustache as ASTv1.CallParts, resolution.result), + this.ctx.loc(mustache.loc) ); // If there are no params or hash, just return the function part as its own expression @@ -629,76 +646,63 @@ class ElementNormalizer { let offsets = this.ctx.loc(m.loc); let nameSlice = offsets.sliceStartChars({ chars: m.name.length }).toSlice(m.name); - let value = this.attrValue(m.value); + return this.ctx.builder.attr( { name: nameSlice, value: value.expr, trusting: value.trusting }, offsets ); } - private maybeDeprecatedCall( - arg: SourceSlice, - part: ASTv1.MustacheStatement | ASTv1.TextNode | ASTv1.ConcatStatement - ): { expr: ASTv2.DeprecatedCallExpression; trusting: boolean } | null { - if (this.ctx.strict) { - return null; - } - - if (part.type !== 'MustacheStatement') { - return null; - } - - let { path } = part; + // An arg curly is the same as an attribute curly for + // our purposes, except that in loose mode is an error: + private checkArgCall(arg: ASTv1.AttrNode): void { + let { value } = arg; - if (path.type !== 'PathExpression') { - return null; + if (value.type !== 'MustacheStatement') { + return; } - if (path.head.type !== 'VarHead') { - return null; + if (value.params.length !== 0 || value.hash.pairs.length !== 0) { + return; } - let { name } = path.head; + let { path } = value; - if (name === 'has-block' || name === 'has-block-params') { - return null; - } - - if (this.ctx.hasBinding(name)) { - return null; + if (path.type !== 'PathExpression') { + return; } - if (path.tail.length !== 0) { - return null; + if (path.tail.length > 0) { + return; } - if (part.params.length !== 0 || part.hash.pairs.length !== 0) { + let resolution = this.ctx.resolutionFor(path, () => { + // We deliberately don't want this to resolve anything. The purpose of + // calling `resolutionFor` here is to check for strict mode, in-scope + // local variables, etc. return null; - } - - let context = ASTv2.LooseModeResolution.attr(); - - let callee = this.ctx.builder.freeVar({ - name, - context, - symbol: this.ctx.table.allocateFree(name, context), - loc: path.loc, }); - return { - expr: this.ctx.builder.deprecatedCall(arg, callee, part.loc), - trusting: false, - }; + if (resolution.result === 'error' && resolution.path !== 'has-block') { + throw generateSyntaxError( + `You attempted to pass a path as argument (\`${arg.name}={{${resolution.path}}}\`) but ${resolution.head} was not in scope. Try:\n` + + `* \`${arg.name}={{this.${resolution.path}}}\` if this is meant to be a property lookup, or\n` + + `* \`${arg.name}={{(${resolution.path})}}\` if this is meant to invoke the resolved helper, or\n` + + `* \`${arg.name}={{helper "${resolution.path}"}}\` if this is meant to pass the resolved helper by value`, + arg.loc + ); + } } private arg(arg: ASTv1.AttrNode): ASTv2.ComponentArg { assert(arg.name[0] === '@', 'An arg name must start with `@`'); + this.checkArgCall(arg); let offsets = this.ctx.loc(arg.loc); let nameSlice = offsets.sliceStartChars({ chars: arg.name.length }).toSlice(arg.name); + let value = this.attrValue(arg.value); - let value = this.maybeDeprecatedCall(nameSlice, arg.value) || this.attrValue(arg.value); return this.ctx.builder.arg( { name: nameSlice, value: value.expr, trusting: value.trusting }, offsets @@ -899,7 +903,7 @@ class ElementChildren extends Children { assertElement(name: SourceSlice, hasBlockParams: boolean): ASTv2.SimpleElement { if (hasBlockParams) { throw generateSyntaxError( - `Unexpected block params in <${name}>: simple elements cannot have block params`, + `Unexpected block params in <${name.chars}>: simple elements cannot have block params`, this.loc ); } diff --git a/packages/@glimmer/syntax/lib/v2/objects/expr.ts b/packages/@glimmer/syntax/lib/v2/objects/expr.ts index 92b9b9d8ec..70f91d06c6 100644 --- a/packages/@glimmer/syntax/lib/v2/objects/expr.ts +++ b/packages/@glimmer/syntax/lib/v2/objects/expr.ts @@ -1,7 +1,7 @@ import type { PresentArray } from '@glimmer/interfaces'; import type { CallFields } from './base'; -import type { FreeVarReference, VariableReference } from './refs'; +import type { VariableReference } from './refs'; import { SourceSlice } from '../../source/slice'; import { node } from './node'; @@ -83,24 +83,6 @@ export class PathExpression extends node('Path').fields<{ */ export class CallExpression extends node('Call').fields() {} -/** - * Corresponds to a possible deprecated helper call. Must be: - * - * 1. A free variable (not this.foo, not @foo, not local). - * 2. Argument-less. - * 3. In a component invocation's named argument position. - * 4. Not parenthesized (not @bar={{(helper)}}). - * 5. Not interpolated (not @bar="{{helper}}"). - * - * ```hbs - * - * ``` - */ -export class DeprecatedCallExpression extends node('DeprecatedCall').fields<{ - arg: SourceSlice; - callee: FreeVarReference; -}>() {} - /** * Corresponds to an interpolation in attribute value position. * @@ -116,5 +98,4 @@ export type ExpressionNode = | LiteralExpression | PathExpression | CallExpression - | DeprecatedCallExpression | InterpolateExpression; diff --git a/packages/@glimmer/syntax/lib/v2/objects/resolution.ts b/packages/@glimmer/syntax/lib/v2/objects/resolution.ts index f297a3746e..a13317a976 100644 --- a/packages/@glimmer/syntax/lib/v2/objects/resolution.ts +++ b/packages/@glimmer/syntax/lib/v2/objects/resolution.ts @@ -3,7 +3,6 @@ * * 1. Strict resolution * 2. Namespaced resolution - * 3. Fallback resolution */ import type { GetContextualFreeOpcode } from '@glimmer/interfaces'; @@ -13,7 +12,7 @@ import { SexpOpcodes } from '@glimmer/wire-format'; * Strict resolution is used: * * 1. in a strict mode template - * 2. in an unambiguous invocation with dot paths + * 2. in an local variable invocation with dot paths */ export const STRICT_RESOLUTION = { resolution: (): GetContextualFreeOpcode => SexpOpcodes.GetStrictKeyword, @@ -35,13 +34,10 @@ export function isStrictResolution(value: unknown): value is StrictResolution { } /** - * A `LooseModeResolution` includes: - * - * - 0 or more namespaces to resolve the variable in - * - optional fallback behavior + * A `LooseModeResolution` includes one or more namespaces to resolve the variable in * * In practice, there are a limited number of possible combinations of these degrees of freedom, - * and they are captured by the `Ambiguity` union below. + * and they are captured by the `Namespaces` union below. */ export class LooseModeResolution { /** @@ -51,156 +47,76 @@ export class LooseModeResolution { * 2. `{{#block}}` (namespace: `Component`) * 3. `` (namespace: `Modifier`) * 4. `` (namespace: `Component`) - * - * @see {NamespacedAmbiguity} */ static namespaced(namespace: FreeVarNamespace, isAngleBracket = false): LooseModeResolution { - return new LooseModeResolution( - { - namespaces: [namespace], - fallback: false, - }, - isAngleBracket - ); - } - - /** - * Fallback resolution is used when no namespaced resolutions are possible, but fallback - * resolution is still allowed. - * - * ```hbs - * {{x.y}} - * ``` - * - * @see {FallbackAmbiguity} - */ - static fallback(): LooseModeResolution { - return new LooseModeResolution({ namespaces: [], fallback: true }); + return new LooseModeResolution([namespace], isAngleBracket); } /** * Append resolution is used when the variable should be resolved in both the `component` and - * `helper` namespaces. Fallback resolution is optional. + * `helper` namespaces. * * ```hbs * {{x}} * ``` * - * ^ `x` should be resolved in the `component` and `helper` namespaces with fallback resolution. - * * ```hbs * {{x y}} * ``` * - * ^ `x` should be resolved in the `component` and `helper` namespaces without fallback - * resolution. - * - * @see {ComponentOrHelperAmbiguity} + * ^ In either case, `x` should be resolved in the `component` and `helper` namespaces. */ - static append({ invoke }: { invoke: boolean }): LooseModeResolution { - return new LooseModeResolution({ - namespaces: [FreeVarNamespace.Component, FreeVarNamespace.Helper], - fallback: !invoke, - }); + static append(): LooseModeResolution { + return new LooseModeResolution([FreeVarNamespace.Component, FreeVarNamespace.Helper]); } /** - * Trusting append resolution is used when the variable should be resolved in both the `component` and - * `helper` namespaces. Fallback resolution is optional. + * Trusting append resolution is used when the variable should be resolved only in the + * `helper` namespaces. * * ```hbs * {{{x}}} * ``` * - * ^ `x` should be resolved in the `component` and `helper` namespaces with fallback resolution. - * * ```hbs * {{{x y}}} * ``` * - * ^ `x` should be resolved in the `component` and `helper` namespaces without fallback - * resolution. - * - * @see {HelperAmbiguity} - */ - static trustingAppend({ invoke }: { invoke: boolean }): LooseModeResolution { - return new LooseModeResolution({ - namespaces: [FreeVarNamespace.Helper], - fallback: !invoke, - }); - } - - /** - * Attribute resolution is used when the variable should be resolved as a `helper` with fallback - * resolution. - * - * ```hbs - * - * - * ``` - * - * ^ resolved in the `helper` namespace with fallback - * - * @see {HelperAmbiguity} + * ^ In either case, `x` should be resolved in the `helper` namespace. */ - static attr(): LooseModeResolution { - return new LooseModeResolution({ namespaces: [FreeVarNamespace.Helper], fallback: true }); + static trustingAppend(): LooseModeResolution { + return this.namespaced(FreeVarNamespace.Helper); } constructor( - readonly ambiguity: Ambiguity, + readonly namespaces: Namespaces, readonly isAngleBracket = false ) {} resolution(): GetContextualFreeOpcode { - if (this.ambiguity.namespaces.length === 0) { - return SexpOpcodes.GetStrictKeyword; - } else if (this.ambiguity.namespaces.length === 1) { - if (this.ambiguity.fallback) { - // simple namespaced resolution with fallback must be attr={{x}} - return SexpOpcodes.GetFreeAsHelperHeadOrThisFallback; - } else { - // simple namespaced resolution without fallback - switch (this.ambiguity.namespaces[0]) { - case FreeVarNamespace.Helper: - return SexpOpcodes.GetFreeAsHelperHead; - case FreeVarNamespace.Modifier: - return SexpOpcodes.GetFreeAsModifierHead; - case FreeVarNamespace.Component: - return SexpOpcodes.GetFreeAsComponentHead; - } + if (this.namespaces.length === 1) { + switch (this.namespaces[0]) { + case FreeVarNamespace.Helper: + return SexpOpcodes.GetFreeAsHelperHead; + case FreeVarNamespace.Modifier: + return SexpOpcodes.GetFreeAsModifierHead; + case FreeVarNamespace.Component: + return SexpOpcodes.GetFreeAsComponentHead; } - } else if (this.ambiguity.fallback) { - // component or helper + fallback ({{something}}) - return SexpOpcodes.GetFreeAsComponentOrHelperHeadOrThisFallback; } else { - // component or helper without fallback ({{something something}}) return SexpOpcodes.GetFreeAsComponentOrHelperHead; } } serialize(): SerializedResolution { - if (this.ambiguity.namespaces.length === 0) { - return 'Loose'; - } else if (this.ambiguity.namespaces.length === 1) { - if (this.ambiguity.fallback) { - // simple namespaced resolution with fallback must be attr={{x}} - return ['ambiguous', SerializedAmbiguity.Attr]; - } else { - return ['ns', this.ambiguity.namespaces[0]]; - } - } else if (this.ambiguity.fallback) { - // component or helper + fallback ({{something}}) - return ['ambiguous', SerializedAmbiguity.Append]; + if (this.namespaces.length === 1) { + return this.namespaces[0]; } else { - // component or helper without fallback ({{something something}}) - return ['ambiguous', SerializedAmbiguity.Invoke]; + return 'ComponentOrHelper'; } } } -export const ARGUMENT_RESOLUTION = LooseModeResolution.fallback(); - export enum FreeVarNamespace { Helper = 'Helper', Modifier = 'Modifier', @@ -212,116 +128,48 @@ export const MODIFIER_NAMESPACE = FreeVarNamespace.Modifier; export const COMPONENT_NAMESPACE = FreeVarNamespace.Component; /** - * A `ComponentOrHelperAmbiguity` might be a component or a helper, with an optional fallback - * - * ```hbs - * {{x}} - * ``` - * - * ^ `x` is resolved in the `component` and `helper` namespaces, with fallback - * - * ```hbs - * {{x y}} - * ``` - * - * ^ `x` is resolved in the `component` and `helper` namespaces, without fallback - */ -type ComponentOrHelperAmbiguity = { - namespaces: [FreeVarNamespace.Component, FreeVarNamespace.Helper]; - fallback: boolean; -}; - -/** - * A `HelperAmbiguity` must be a helper, but it has fallback. If it didn't have fallback, it would - * be a `NamespacedAmbiguity`. - * - * ```hbs - * - * - * ``` - * - * ^ `x` is resolved in the `helper` namespace with fallback - */ -type HelperAmbiguity = { namespaces: [FreeVarNamespace.Helper]; fallback: boolean }; - -/** - * A `NamespacedAmbiguity` must be resolved in a particular namespace, without fallback. + * A `Namespaced` must be resolved in one or more namespaces. * * ```hbs * * ``` * - * ^ `X` is resolved in the `component` namespace without fallback + * ^ `X` is resolved in the `component` namespace * * ```hbs * (x) * ``` * - * ^ `x` is resolved in the `helper` namespace without fallback + * ^ `x` is resolved in the `helper` namespace * * ```hbs * * ``` * - * ^ `x` is resolved in the `modifier` namespace without fallback + * ^ `x` is resolved in the `modifier` namespace */ -type NamespacedAmbiguity = { - namespaces: [FreeVarNamespace.Component | FreeVarNamespace.Helper | FreeVarNamespace.Modifier]; - fallback: false; -}; - -type FallbackAmbiguity = { - namespaces: []; - fallback: true; -}; - -type Ambiguity = - | ComponentOrHelperAmbiguity - | HelperAmbiguity - | NamespacedAmbiguity - | FallbackAmbiguity; +type Namespaces = + | [FreeVarNamespace.Helper] + | [FreeVarNamespace.Modifier] + | [FreeVarNamespace.Component] + | [FreeVarNamespace.Component, FreeVarNamespace.Helper]; export type FreeVarResolution = StrictResolution | HtmlResolution | LooseModeResolution; // Serialization - -const enum SerializedAmbiguity { - // {{x}} - Append = 'Append', - // href={{x}} - Attr = 'Attr', - // {{x y}} (not attr) - Invoke = 'Invoke', -} - export type SerializedResolution = | 'Strict' - | 'Loose' - | ['ns', FreeVarNamespace] - | ['ambiguous', SerializedAmbiguity]; + | 'Helper' + | 'Modifier' + | 'Component' + | 'ComponentOrHelper'; export function loadResolution(resolution: SerializedResolution): FreeVarResolution { - if (typeof resolution === 'string') { - switch (resolution) { - case 'Loose': - return LooseModeResolution.fallback(); - case 'Strict': - return STRICT_RESOLUTION; - } - } - - switch (resolution[0]) { - case 'ambiguous': - switch (resolution[1]) { - case SerializedAmbiguity.Append: - return LooseModeResolution.append({ invoke: false }); - case SerializedAmbiguity.Attr: - return LooseModeResolution.attr(); - case SerializedAmbiguity.Invoke: - return LooseModeResolution.append({ invoke: true }); - } - - case 'ns': - return LooseModeResolution.namespaced(resolution[1]); + if (resolution === 'Strict') { + return STRICT_RESOLUTION; + } else if (resolution === 'ComponentOrHelper') { + return LooseModeResolution.append(); + } else { + return LooseModeResolution.namespaced(resolution as FreeVarNamespace); } } diff --git a/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts b/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts index 355caef414..6c6435814a 100644 --- a/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts +++ b/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts @@ -9,7 +9,6 @@ import type { SerializedBlock, SerializedCallExpression, SerializedContentNode, - SerializedDeprecatedCallExpression, SerializedElementModifier, SerializedExpressionNode, SerializedFreeVarReference, @@ -98,14 +97,6 @@ export class ExprSerializer { }; } - deprecatedCall(call: ASTv2.DeprecatedCallExpression): SerializedDeprecatedCallExpression { - return { - type: 'DeprecatedCall', - loc: call.loc.serialize(), - callee: REF.free(call.callee), - }; - } - interpolate(interpolate: ASTv2.InterpolateExpression): SerializedInterpolateExpression { return { type: 'Interpolate', @@ -281,8 +272,6 @@ const visit = { return EXPR.path(expr); case 'Call': return EXPR.call(expr); - case 'DeprecatedCall': - return EXPR.deprecatedCall(expr); case 'Interpolate': return EXPR.interpolate(expr); } diff --git a/packages/@glimmer/wire-format/lib/opcodes.ts b/packages/@glimmer/wire-format/lib/opcodes.ts index 9214151f9d..824348c763 100644 --- a/packages/@glimmer/wire-format/lib/opcodes.ts +++ b/packages/@glimmer/wire-format/lib/opcodes.ts @@ -17,10 +17,7 @@ import type { GetDynamicVarOpcode, GetFreeAsComponentHeadOpcode, GetFreeAsComponentOrHelperHeadOpcode, - GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode, - GetFreeAsDeprecatedHelperHeadOrThisFallbackOpcode, GetFreeAsHelperHeadOpcode, - GetFreeAsHelperHeadOrThisFallbackOpcode, GetFreeAsModifierHeadOpcode, GetLexicalSymbolOpcode, GetStrictKeywordOpcode, @@ -47,7 +44,6 @@ import type { TrustingDynamicAttrOpcode, UndefinedOpcode, WithDynamicVarsOpcode, - WithOpcode, YieldOpcode, } from '@glimmer/interfaces'; @@ -81,19 +77,13 @@ export const opcodes = { GetSymbol: 30 satisfies GetSymbolOpcode, GetLexicalSymbol: 32 satisfies GetLexicalSymbolOpcode, GetStrictKeyword: 31 satisfies GetStrictKeywordOpcode, - GetFreeAsComponentOrHelperHeadOrThisFallback: - 34 satisfies GetFreeAsComponentOrHelperHeadOrThisFallbackOpcode, GetFreeAsComponentOrHelperHead: 35 satisfies GetFreeAsComponentOrHelperHeadOpcode, - GetFreeAsHelperHeadOrThisFallback: 36 satisfies GetFreeAsHelperHeadOrThisFallbackOpcode, - GetFreeAsDeprecatedHelperHeadOrThisFallback: - 99 satisfies GetFreeAsDeprecatedHelperHeadOrThisFallbackOpcode, GetFreeAsHelperHead: 37 satisfies GetFreeAsHelperHeadOpcode, GetFreeAsModifierHead: 38 satisfies GetFreeAsModifierHeadOpcode, GetFreeAsComponentHead: 39 satisfies GetFreeAsComponentHeadOpcode, InElement: 40 satisfies InElementOpcode, If: 41 satisfies IfOpcode, Each: 42 satisfies EachOpcode, - With: 43 satisfies WithOpcode, Let: 44 satisfies LetOpcode, WithDynamicVars: 45 satisfies WithDynamicVarsOpcode, InvokeComponent: 46 satisfies InvokeComponentOpcode, diff --git a/packages/@glimmer/wire-format/lib/resolution.ts b/packages/@glimmer/wire-format/lib/resolution.ts index f68a0c387c..22d560819c 100644 --- a/packages/@glimmer/wire-format/lib/resolution.ts +++ b/packages/@glimmer/wire-format/lib/resolution.ts @@ -1,29 +1,23 @@ import type { - AmbiguousAppendInvokeResolution, - AmbiguousAppendResolution, - AmbiguousInvokeResolution, - ResolveAsCallHeadResolution, ResolveAsComponentHeadResolution, + ResolveAsComponentOrHelperHeadResolution, + ResolveAsHelperHeadResolution, ResolveAsModifierHeadResolution, StrictResolution, } from '@glimmer/interfaces'; // eslint-disable-next-line @typescript-eslint/naming-convention export type resolution = - | AmbiguousAppendInvokeResolution - | AmbiguousAppendResolution - | AmbiguousInvokeResolution - | ResolveAsCallHeadResolution + | ResolveAsComponentOrHelperHeadResolution + | ResolveAsHelperHeadResolution | ResolveAsComponentHeadResolution | ResolveAsModifierHeadResolution | StrictResolution; export const resolution = { Strict: 0 satisfies StrictResolution, - AmbiguousAppend: 1 satisfies AmbiguousAppendResolution, - AmbiguousAppendInvoke: 2 satisfies AmbiguousAppendInvokeResolution, - AmbiguousInvoke: 3 satisfies AmbiguousInvokeResolution, - ResolveAsCallHead: 5 satisfies ResolveAsCallHeadResolution, + ResolveAsComponentOrHelperHead: 1 satisfies ResolveAsComponentOrHelperHeadResolution, + ResolveAsHelperHead: 5 satisfies ResolveAsHelperHeadResolution, ResolveAsModifierHead: 6 satisfies ResolveAsModifierHeadResolution, ResolveAsComponentHead: 7 satisfies ResolveAsComponentHeadResolution, } as const; From c62ba2c3d810be88df532c35db010665777397ad Mon Sep 17 00:00:00 2001 From: Andrey Fel Date: Wed, 20 Mar 2024 18:21:12 +0100 Subject: [PATCH 02/15] Add missing licenses (#1579) to all package.json files which do not have `"private": true` --- packages/@glimmer/compiler/package.json | 1 + packages/@glimmer/debug/package.json | 1 + packages/@glimmer/encoder/package.json | 1 + packages/@glimmer/global-context/package.json | 1 + packages/@glimmer/interfaces/package.json | 1 + packages/@glimmer/local-debug-flags/package.json | 1 + packages/@glimmer/manager/package.json | 1 + packages/@glimmer/node/package.json | 1 + packages/@glimmer/opcode-compiler/package.json | 1 + packages/@glimmer/program/package.json | 1 + packages/@glimmer/syntax/package.json | 1 + packages/@glimmer/vm/package.json | 1 + 12 files changed, 12 insertions(+) diff --git a/packages/@glimmer/compiler/package.json b/packages/@glimmer/compiler/package.json index e315188262..877371b379 100644 --- a/packages/@glimmer/compiler/package.json +++ b/packages/@glimmer/compiler/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/compiler", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/compiler", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/debug/package.json b/packages/@glimmer/debug/package.json index 3427be6647..4d15aa28d0 100644 --- a/packages/@glimmer/debug/package.json +++ b/packages/@glimmer/debug/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/debug", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/debug", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/encoder/package.json b/packages/@glimmer/encoder/package.json index 162198e9dc..5c1ae430a3 100644 --- a/packages/@glimmer/encoder/package.json +++ b/packages/@glimmer/encoder/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/encoder", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/encoder", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/global-context/package.json b/packages/@glimmer/global-context/package.json index 7987896bda..cfdcd7e9ea 100644 --- a/packages/@glimmer/global-context/package.json +++ b/packages/@glimmer/global-context/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/global-context", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/global-context", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/interfaces/package.json b/packages/@glimmer/interfaces/package.json index dd5114b8b4..4c10db1cd8 100644 --- a/packages/@glimmer/interfaces/package.json +++ b/packages/@glimmer/interfaces/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/interfaces", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/interfaces", "type": "module", "types": "index.d.ts", diff --git a/packages/@glimmer/local-debug-flags/package.json b/packages/@glimmer/local-debug-flags/package.json index 46042313f7..43e77c3506 100644 --- a/packages/@glimmer/local-debug-flags/package.json +++ b/packages/@glimmer/local-debug-flags/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/local-debug-flags", "version": "0.89.0", + "license": "MIT", "description": "Helpers for debugging during local development. These get stripped from published builds. This package should not be published.", "repository": { "type": "git", diff --git a/packages/@glimmer/manager/package.json b/packages/@glimmer/manager/package.json index ac0a30d14d..ce1ece9835 100644 --- a/packages/@glimmer/manager/package.json +++ b/packages/@glimmer/manager/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/manager", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/node/package.json b/packages/@glimmer/node/package.json index 335b51d8b2..38441a6aa9 100644 --- a/packages/@glimmer/node/package.json +++ b/packages/@glimmer/node/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/node", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/node", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/opcode-compiler/package.json b/packages/@glimmer/opcode-compiler/package.json index 2f28dee65b..29d029a0af 100644 --- a/packages/@glimmer/opcode-compiler/package.json +++ b/packages/@glimmer/opcode-compiler/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/opcode-compiler", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/opcode-compiler", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/program/package.json b/packages/@glimmer/program/package.json index a15ef8da97..0325048b59 100644 --- a/packages/@glimmer/program/package.json +++ b/packages/@glimmer/program/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/program", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/syntax/package.json b/packages/@glimmer/syntax/package.json index 4eeee28f02..c82923e094 100644 --- a/packages/@glimmer/syntax/package.json +++ b/packages/@glimmer/syntax/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/syntax", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/syntax", "type": "module", "main": "index.ts", diff --git a/packages/@glimmer/vm/package.json b/packages/@glimmer/vm/package.json index 0e3486b6a5..272cbf5fca 100644 --- a/packages/@glimmer/vm/package.json +++ b/packages/@glimmer/vm/package.json @@ -1,6 +1,7 @@ { "name": "@glimmer/vm", "version": "0.89.0", + "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/vm", "type": "module", "main": "index.ts", From 4913a59a74de81940f04b0b70c444a7f016e97f1 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:31:10 -0400 Subject: [PATCH 03/15] Remove deprecation for setting hash properties (from years ago) (#1580) * Remove deprecation for setting hash properties (from years ago) * Remove tests which are now irrelevant --- .../test/helpers/hash-test.ts | 87 ------------------- packages/@glimmer/runtime/lib/helpers/hash.ts | 31 +------ 2 files changed, 1 insertion(+), 117 deletions(-) diff --git a/packages/@glimmer-workspace/integration-tests/test/helpers/hash-test.ts b/packages/@glimmer-workspace/integration-tests/test/helpers/hash-test.ts index 1113ac3203..3b498af2b0 100644 --- a/packages/@glimmer-workspace/integration-tests/test/helpers/hash-test.ts +++ b/packages/@glimmer-workspace/integration-tests/test/helpers/hash-test.ts @@ -185,93 +185,6 @@ class HashTest extends RenderTest { this.assertHTML('Godfrey'); this.assertStableRerender(); } - - @test - 'defined hash keys can be updated'(assert: Assert) { - class FooBar extends GlimmerishComponent { - constructor(owner: object, args: { hash: Record }) { - super(owner, args); - args.hash['firstName'] = 'Chad'; - - assert.strictEqual(args.hash['firstName'], 'Chad', 'Name updated in JS'); - } - } - - this.registerComponent('Glimmer', 'FooBar', `{{yield @hash}}`, FooBar); - - this.render( - `{{values.firstName}} {{values.lastName}}` - ); - - // Name will not be updated in templates because templates access the child - // reference on hashes directly - this.assertHTML('Godfrey Hietala'); - this.assertStableRerender(); - - assert.validateDeprecations( - /You set the 'firstName' property on a \{\{hash\}\} object. Setting properties on objects generated by \{\{hash\}\} is deprecated. Please update to use an object created with a tracked property or getter, or with a custom helper./u - ); - } - - @test - 'defined hash keys are reset whenever the upstream changes'(assert: Assert) { - class FooBar extends GlimmerishComponent { - constructor(owner: object, args: { hash: Record }) { - super(owner, args); - args.hash['name'] = 'Chad'; - } - - get alias() { - return (this.args['hash'] as Record)['name']; - } - } - - this.registerComponent('Glimmer', 'FooBar', `{{yield @hash this.alias}}`, FooBar); - - this.render( - `{{values.name}} {{alias}}`, - { - name: 'Godfrey', - } - ); - - // JS alias will be updated, version accessed lazily in templates will not - this.assertHTML('Godfrey Chad'); - this.assertStableRerender(); - - this.rerender({ name: 'Tom' }); - - // Both will be updated to match the new value - this.assertHTML('Tom Tom'); - this.assertStableRerender(); - - assert.validateDeprecations( - /You set the 'name' property on a \{\{hash\}\} object. Setting properties on objects generated by \{\{hash\}\} is deprecated. Please update to use an object created with a tracked property or getter, or with a custom helper./u - ); - } - - @test - 'undefined hash keys can be updated'(assert: Assert) { - class FooBar extends GlimmerishComponent { - constructor(owner: object, args: { hash: Record }) { - super(owner, args); - args.hash['lastName'] = 'Chan'; - } - } - - this.registerComponent('Glimmer', 'FooBar', `{{yield @hash}}`, FooBar); - - this.render( - `{{values.firstName}} {{values.lastName}}` - ); - - this.assertHTML('Godfrey Chan'); - this.assertStableRerender(); - - assert.validateDeprecations( - /You set the 'lastName' property on a \{\{hash\}\} object. Setting properties on objects generated by \{\{hash\}\} is deprecated. Please update to use an object created with a tracked property or getter, or with a custom helper./u - ); - } } jitSuite(HashTest); diff --git a/packages/@glimmer/runtime/lib/helpers/hash.ts b/packages/@glimmer/runtime/lib/helpers/hash.ts index 3dcc77ddb1..0b19c7e87e 100644 --- a/packages/@glimmer/runtime/lib/helpers/hash.ts +++ b/packages/@glimmer/runtime/lib/helpers/hash.ts @@ -1,33 +1,10 @@ import type { CapturedArguments, Dict } from '@glimmer/interfaces'; import type { Reference } from '@glimmer/reference'; -import { deprecate } from '@glimmer/global-context'; import { createComputeRef } from '@glimmer/reference'; import { reifyNamed } from '../vm/arguments'; import { internalHelper } from './internal-helper'; -let wrapHashProxy: (hash: Record) => Record; - -if (import.meta.env.DEV) { - wrapHashProxy = (hash: Record) => { - return new Proxy(hash, { - set(target, key, value) { - deprecate( - `You set the '${String( - key - )}' property on a {{hash}} object. Setting properties on objects generated by {{hash}} is deprecated. Please update to use an object created with a tracked property or getter, or with a custom helper.`, - false, - { id: 'setting-on-hash' } - ); - - target[key as string] = value; - - return true; - }, - }); - }; -} - /** Use the `{{hash}}` helper to create a hash to pass as an option to your components. This is specially useful for contextual components where you can @@ -67,13 +44,7 @@ if (import.meta.env.DEV) { export const hash = internalHelper(({ named }: CapturedArguments): Reference> => { let ref = createComputeRef( () => { - let hash = reifyNamed(named); - - if (import.meta.env.DEV) { - hash = wrapHashProxy(hash); - } - - return hash; + return reifyNamed(named); }, null, 'hash' From 91337957c9d12fac277bb2574f24af4df22ba524 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:35:18 -0400 Subject: [PATCH 04/15] v0.90.0 --- CHANGELOG.md | 20 +++++++++++++++++++ package.json | 2 +- packages/@glimmer/compiler/package.json | 2 +- packages/@glimmer/debug/package.json | 2 +- packages/@glimmer/destroyable/package.json | 2 +- packages/@glimmer/encoder/package.json | 2 +- packages/@glimmer/global-context/package.json | 2 +- packages/@glimmer/interfaces/package.json | 2 +- .../@glimmer/local-debug-flags/package.json | 2 +- packages/@glimmer/manager/package.json | 2 +- packages/@glimmer/node/package.json | 2 +- .../@glimmer/opcode-compiler/package.json | 2 +- packages/@glimmer/owner/package.json | 2 +- packages/@glimmer/program/package.json | 2 +- packages/@glimmer/reference/package.json | 2 +- packages/@glimmer/runtime/package.json | 2 +- packages/@glimmer/syntax/package.json | 2 +- packages/@glimmer/util/package.json | 2 +- packages/@glimmer/validator/package.json | 2 +- .../@glimmer/vm-babel-plugins/package.json | 2 +- packages/@glimmer/vm/package.json | 2 +- packages/@glimmer/wire-format/package.json | 2 +- 22 files changed, 41 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f903d082d1..2cad58a3a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,26 @@ + +## v0.90.0 (2024-03-22) + +#### :boom: Breaking Change +* `@glimmer-workspace/integration-tests`, `@glimmer/runtime` + * [#1580](https://github.com/glimmerjs/glimmer-vm/pull/1580) Remove deprecation for setting hash properties (from years ago) ([@NullVoxPopuli](https://github.com/NullVoxPopuli)) + +#### :rocket: Enhancement +* `@glimmer-workspace/integration-tests`, `@glimmer/runtime` + * [#1580](https://github.com/glimmerjs/glimmer-vm/pull/1580) Remove deprecation for setting hash properties (from years ago) ([@NullVoxPopuli](https://github.com/NullVoxPopuli)) + +#### :bug: Bug Fix +* `@glimmer/compiler`, `@glimmer/debug`, `@glimmer/encoder`, `@glimmer/global-context`, `@glimmer/interfaces`, `@glimmer/local-debug-flags`, `@glimmer/manager`, `@glimmer/node`, `@glimmer/opcode-compiler`, `@glimmer/program`, `@glimmer/syntax`, `@glimmer/vm` + * [#1579](https://github.com/glimmerjs/glimmer-vm/pull/1579) Add missing licenses ([@andreyfel](https://github.com/andreyfel)) + +#### Committers: 3 +- Andrey Fel ([@andreyfel](https://github.com/andreyfel)) +- Godfrey Chan ([@chancancode](https://github.com/chancancode)) +- [@NullVoxPopuli](https://github.com/NullVoxPopuli) + ## v0.89.0 (2024-03-09) #### :rocket: Enhancement diff --git a/package.json b/package.json index 24a6709605..eefd6b22ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glimmer-engine", - "version": "0.89.0", + "version": "0.90.0", "private": true, "license": "MIT", "description": "Glimmer compiles Handlebars templates into document fragments rather than string buffers", diff --git a/packages/@glimmer/compiler/package.json b/packages/@glimmer/compiler/package.json index 877371b379..3bc25a0146 100644 --- a/packages/@glimmer/compiler/package.json +++ b/packages/@glimmer/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/compiler", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/compiler", "type": "module", diff --git a/packages/@glimmer/debug/package.json b/packages/@glimmer/debug/package.json index 4d15aa28d0..b6ce4f596f 100644 --- a/packages/@glimmer/debug/package.json +++ b/packages/@glimmer/debug/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/debug", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/debug", "type": "module", diff --git a/packages/@glimmer/destroyable/package.json b/packages/@glimmer/destroyable/package.json index 81426d293d..10e230e9b9 100644 --- a/packages/@glimmer/destroyable/package.json +++ b/packages/@glimmer/destroyable/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/destroyable", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Utilities for creating and managing a destroyable hierarchy of objects", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/destroyable", diff --git a/packages/@glimmer/encoder/package.json b/packages/@glimmer/encoder/package.json index 5c1ae430a3..da0cfa268f 100644 --- a/packages/@glimmer/encoder/package.json +++ b/packages/@glimmer/encoder/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/encoder", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/encoder", "type": "module", diff --git a/packages/@glimmer/global-context/package.json b/packages/@glimmer/global-context/package.json index cfdcd7e9ea..936d3601ae 100644 --- a/packages/@glimmer/global-context/package.json +++ b/packages/@glimmer/global-context/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/global-context", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/global-context", "type": "module", diff --git a/packages/@glimmer/interfaces/package.json b/packages/@glimmer/interfaces/package.json index 4c10db1cd8..d4932a9a72 100644 --- a/packages/@glimmer/interfaces/package.json +++ b/packages/@glimmer/interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/interfaces", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/interfaces", "type": "module", diff --git a/packages/@glimmer/local-debug-flags/package.json b/packages/@glimmer/local-debug-flags/package.json index 43e77c3506..a3b4061a20 100644 --- a/packages/@glimmer/local-debug-flags/package.json +++ b/packages/@glimmer/local-debug-flags/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/local-debug-flags", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Helpers for debugging during local development. These get stripped from published builds. This package should not be published.", "repository": { diff --git a/packages/@glimmer/manager/package.json b/packages/@glimmer/manager/package.json index ce1ece9835..7def99712e 100644 --- a/packages/@glimmer/manager/package.json +++ b/packages/@glimmer/manager/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/manager", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/node/package.json b/packages/@glimmer/node/package.json index 38441a6aa9..b08db03c0f 100644 --- a/packages/@glimmer/node/package.json +++ b/packages/@glimmer/node/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/node", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/node", "type": "module", diff --git a/packages/@glimmer/opcode-compiler/package.json b/packages/@glimmer/opcode-compiler/package.json index 29d029a0af..3f79c3e2ce 100644 --- a/packages/@glimmer/opcode-compiler/package.json +++ b/packages/@glimmer/opcode-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/opcode-compiler", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/opcode-compiler", "type": "module", diff --git a/packages/@glimmer/owner/package.json b/packages/@glimmer/owner/package.json index 93b861e72b..7901ecfe0d 100644 --- a/packages/@glimmer/owner/package.json +++ b/packages/@glimmer/owner/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/owner", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Implementation for the owner in Glimmer apps", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/owner", diff --git a/packages/@glimmer/program/package.json b/packages/@glimmer/program/package.json index 0325048b59..a6d83aa44d 100644 --- a/packages/@glimmer/program/package.json +++ b/packages/@glimmer/program/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/program", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/reference/package.json b/packages/@glimmer/reference/package.json index 945d37fc97..6cf6c98a8c 100644 --- a/packages/@glimmer/reference/package.json +++ b/packages/@glimmer/reference/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/reference", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/runtime/package.json b/packages/@glimmer/runtime/package.json index d3cb8c4991..e6f5cbf035 100644 --- a/packages/@glimmer/runtime/package.json +++ b/packages/@glimmer/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/runtime", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Minimal runtime needed to render Glimmer templates", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/runtime", diff --git a/packages/@glimmer/syntax/package.json b/packages/@glimmer/syntax/package.json index c82923e094..64e6e514f9 100644 --- a/packages/@glimmer/syntax/package.json +++ b/packages/@glimmer/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/syntax", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/syntax", "type": "module", diff --git a/packages/@glimmer/util/package.json b/packages/@glimmer/util/package.json index 42012d23a6..e5233b84d8 100644 --- a/packages/@glimmer/util/package.json +++ b/packages/@glimmer/util/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/util", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Common utilities used in Glimmer", "repository": "https://github.com/tildeio/glimmer/tree/main/packages/@glimmer/util", diff --git a/packages/@glimmer/validator/package.json b/packages/@glimmer/validator/package.json index b05e98316e..0cfa75d824 100644 --- a/packages/@glimmer/validator/package.json +++ b/packages/@glimmer/validator/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/validator", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/vm-babel-plugins/package.json b/packages/@glimmer/vm-babel-plugins/package.json index 27f1aa3fb5..05c60ef751 100644 --- a/packages/@glimmer/vm-babel-plugins/package.json +++ b/packages/@glimmer/vm-babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm-babel-plugins", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "Compiles out VM assertion and deprecation utilities and debug tooling based on environment", "repository": "https://github.com/glimmerjs/glimmer.js", diff --git a/packages/@glimmer/vm/package.json b/packages/@glimmer/vm/package.json index 272cbf5fca..2f762a18f9 100644 --- a/packages/@glimmer/vm/package.json +++ b/packages/@glimmer/vm/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/vm", "type": "module", diff --git a/packages/@glimmer/wire-format/package.json b/packages/@glimmer/wire-format/package.json index 1250450746..097b0457e9 100644 --- a/packages/@glimmer/wire-format/package.json +++ b/packages/@glimmer/wire-format/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/wire-format", - "version": "0.89.0", + "version": "0.90.0", "license": "MIT", "description": "", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/wire-format", From 84ef91ea5e0f4074c566564bd4cfbce71b286687 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:31:10 -0400 Subject: [PATCH 05/15] Remove index imports as they are not defined by package.json#exports (#1581) * Remove index imports * lint:fix --- .../compiler/lib/passes/1-normalization/visitors/strict-mode.ts | 2 +- packages/@glimmer/syntax/lib/v1/legacy-interop.ts | 2 +- packages/@glimmer/syntax/test/plugin-node-test.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts index 47aef11a1f..787139a8d5 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts @@ -1,6 +1,6 @@ import type { HasSourceSpan } from '@glimmer/syntax'; import { generateSyntaxError, loc } from '@glimmer/syntax'; -import { CurriedTypes } from '@glimmer/vm/index'; +import { CurriedTypes } from '@glimmer/vm'; import type { Result } from '../../../shared/result'; import type * as mir from '../../2-encoding/mir'; diff --git a/packages/@glimmer/syntax/lib/v1/legacy-interop.ts b/packages/@glimmer/syntax/lib/v1/legacy-interop.ts index 7687664615..404ba20d14 100644 --- a/packages/@glimmer/syntax/lib/v1/legacy-interop.ts +++ b/packages/@glimmer/syntax/lib/v1/legacy-interop.ts @@ -1,4 +1,4 @@ -import type { PresentArray } from '@glimmer/interfaces/index'; +import type { PresentArray } from '@glimmer/interfaces'; import { asPresentArray, deprecate } from '@glimmer/util'; import type * as ASTv1 from './nodes-v1'; diff --git a/packages/@glimmer/syntax/test/plugin-node-test.ts b/packages/@glimmer/syntax/test/plugin-node-test.ts index 8d725f157f..e4cf8f7fc4 100644 --- a/packages/@glimmer/syntax/test/plugin-node-test.ts +++ b/packages/@glimmer/syntax/test/plugin-node-test.ts @@ -48,7 +48,6 @@ test('deprecated program visitor', (assert) => { return { name: 'plugin', visitor: { - // eslint-disable-next-line deprecation/deprecation Program(node: AST.Program) { assert.step(node.type); }, From f3d27761545cf76867489d0024610f7dbf82b2ec Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:32:55 -0400 Subject: [PATCH 06/15] v0.90.1 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- packages/@glimmer/compiler/package.json | 2 +- packages/@glimmer/debug/package.json | 2 +- packages/@glimmer/destroyable/package.json | 2 +- packages/@glimmer/encoder/package.json | 2 +- packages/@glimmer/global-context/package.json | 2 +- packages/@glimmer/interfaces/package.json | 2 +- packages/@glimmer/local-debug-flags/package.json | 2 +- packages/@glimmer/manager/package.json | 2 +- packages/@glimmer/node/package.json | 2 +- packages/@glimmer/opcode-compiler/package.json | 2 +- packages/@glimmer/owner/package.json | 2 +- packages/@glimmer/program/package.json | 2 +- packages/@glimmer/reference/package.json | 2 +- packages/@glimmer/runtime/package.json | 2 +- packages/@glimmer/syntax/package.json | 2 +- packages/@glimmer/util/package.json | 2 +- packages/@glimmer/validator/package.json | 2 +- packages/@glimmer/vm-babel-plugins/package.json | 2 +- packages/@glimmer/vm/package.json | 2 +- packages/@glimmer/wire-format/package.json | 2 +- 22 files changed, 31 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cad58a3a1..1d88eb3fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,16 @@ + + +## v0.90.1 (2024-03-22) + +#### :bug: Bug Fix +* `@glimmer/compiler`, `@glimmer/syntax` + * [#1581](https://github.com/glimmerjs/glimmer-vm/pull/1581) Remove index imports as they are not defined by package.json#exports ([@NullVoxPopuli](https://github.com/NullVoxPopuli)) + +#### Committers: 1 +- [@NullVoxPopuli](https://github.com/NullVoxPopuli) ## v0.90.0 (2024-03-22) diff --git a/package.json b/package.json index eefd6b22ba..303a3b522f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glimmer-engine", - "version": "0.90.0", + "version": "0.90.1", "private": true, "license": "MIT", "description": "Glimmer compiles Handlebars templates into document fragments rather than string buffers", diff --git a/packages/@glimmer/compiler/package.json b/packages/@glimmer/compiler/package.json index 3bc25a0146..276ea6de34 100644 --- a/packages/@glimmer/compiler/package.json +++ b/packages/@glimmer/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/compiler", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/compiler", "type": "module", diff --git a/packages/@glimmer/debug/package.json b/packages/@glimmer/debug/package.json index b6ce4f596f..8a0eedb903 100644 --- a/packages/@glimmer/debug/package.json +++ b/packages/@glimmer/debug/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/debug", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/debug", "type": "module", diff --git a/packages/@glimmer/destroyable/package.json b/packages/@glimmer/destroyable/package.json index 10e230e9b9..f9f3a64b02 100644 --- a/packages/@glimmer/destroyable/package.json +++ b/packages/@glimmer/destroyable/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/destroyable", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Utilities for creating and managing a destroyable hierarchy of objects", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/destroyable", diff --git a/packages/@glimmer/encoder/package.json b/packages/@glimmer/encoder/package.json index da0cfa268f..e0be0356a0 100644 --- a/packages/@glimmer/encoder/package.json +++ b/packages/@glimmer/encoder/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/encoder", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/encoder", "type": "module", diff --git a/packages/@glimmer/global-context/package.json b/packages/@glimmer/global-context/package.json index 936d3601ae..4f136ded8e 100644 --- a/packages/@glimmer/global-context/package.json +++ b/packages/@glimmer/global-context/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/global-context", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/global-context", "type": "module", diff --git a/packages/@glimmer/interfaces/package.json b/packages/@glimmer/interfaces/package.json index d4932a9a72..9372cfee89 100644 --- a/packages/@glimmer/interfaces/package.json +++ b/packages/@glimmer/interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/interfaces", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/interfaces", "type": "module", diff --git a/packages/@glimmer/local-debug-flags/package.json b/packages/@glimmer/local-debug-flags/package.json index a3b4061a20..7f049e1696 100644 --- a/packages/@glimmer/local-debug-flags/package.json +++ b/packages/@glimmer/local-debug-flags/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/local-debug-flags", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Helpers for debugging during local development. These get stripped from published builds. This package should not be published.", "repository": { diff --git a/packages/@glimmer/manager/package.json b/packages/@glimmer/manager/package.json index 7def99712e..e32d9b3fdd 100644 --- a/packages/@glimmer/manager/package.json +++ b/packages/@glimmer/manager/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/manager", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/node/package.json b/packages/@glimmer/node/package.json index b08db03c0f..c0de408d31 100644 --- a/packages/@glimmer/node/package.json +++ b/packages/@glimmer/node/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/node", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/node", "type": "module", diff --git a/packages/@glimmer/opcode-compiler/package.json b/packages/@glimmer/opcode-compiler/package.json index 3f79c3e2ce..129a0ce75a 100644 --- a/packages/@glimmer/opcode-compiler/package.json +++ b/packages/@glimmer/opcode-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/opcode-compiler", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/opcode-compiler", "type": "module", diff --git a/packages/@glimmer/owner/package.json b/packages/@glimmer/owner/package.json index 7901ecfe0d..0c3dc21da5 100644 --- a/packages/@glimmer/owner/package.json +++ b/packages/@glimmer/owner/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/owner", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Implementation for the owner in Glimmer apps", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/owner", diff --git a/packages/@glimmer/program/package.json b/packages/@glimmer/program/package.json index a6d83aa44d..efe244908e 100644 --- a/packages/@glimmer/program/package.json +++ b/packages/@glimmer/program/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/program", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/reference/package.json b/packages/@glimmer/reference/package.json index 6cf6c98a8c..f7faa1d4c3 100644 --- a/packages/@glimmer/reference/package.json +++ b/packages/@glimmer/reference/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/reference", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/runtime/package.json b/packages/@glimmer/runtime/package.json index e6f5cbf035..89c34d6d0b 100644 --- a/packages/@glimmer/runtime/package.json +++ b/packages/@glimmer/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/runtime", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Minimal runtime needed to render Glimmer templates", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/runtime", diff --git a/packages/@glimmer/syntax/package.json b/packages/@glimmer/syntax/package.json index 64e6e514f9..a7efe58834 100644 --- a/packages/@glimmer/syntax/package.json +++ b/packages/@glimmer/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/syntax", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/syntax", "type": "module", diff --git a/packages/@glimmer/util/package.json b/packages/@glimmer/util/package.json index e5233b84d8..8b46709729 100644 --- a/packages/@glimmer/util/package.json +++ b/packages/@glimmer/util/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/util", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Common utilities used in Glimmer", "repository": "https://github.com/tildeio/glimmer/tree/main/packages/@glimmer/util", diff --git a/packages/@glimmer/validator/package.json b/packages/@glimmer/validator/package.json index 0cfa75d824..451803e648 100644 --- a/packages/@glimmer/validator/package.json +++ b/packages/@glimmer/validator/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/validator", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/vm-babel-plugins/package.json b/packages/@glimmer/vm-babel-plugins/package.json index 05c60ef751..972f3c1688 100644 --- a/packages/@glimmer/vm-babel-plugins/package.json +++ b/packages/@glimmer/vm-babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm-babel-plugins", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "Compiles out VM assertion and deprecation utilities and debug tooling based on environment", "repository": "https://github.com/glimmerjs/glimmer.js", diff --git a/packages/@glimmer/vm/package.json b/packages/@glimmer/vm/package.json index 2f762a18f9..ab1a9a8ed4 100644 --- a/packages/@glimmer/vm/package.json +++ b/packages/@glimmer/vm/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/vm", "type": "module", diff --git a/packages/@glimmer/wire-format/package.json b/packages/@glimmer/wire-format/package.json index 097b0457e9..00f1427a28 100644 --- a/packages/@glimmer/wire-format/package.json +++ b/packages/@glimmer/wire-format/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/wire-format", - "version": "0.90.0", + "version": "0.90.1", "license": "MIT", "description": "", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/wire-format", From be49ccd0336a82475b15246d87a8b671c7eec4a4 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:55:15 -0400 Subject: [PATCH 07/15] Prevent use of ...attributes in invalid places (#1582) --- .../lib/parser/handlebars-node-visitors.ts | 7 +++++ .../@glimmer/syntax/test/parser-node-test.ts | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts b/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts index 6f7a620bb6..aad4535a21 100644 --- a/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts +++ b/packages/@glimmer/syntax/lib/parser/handlebars-node-visitors.ts @@ -220,6 +220,13 @@ export abstract class HandlebarsNodeVisitors extends Parser { let mustache: ASTv1.MustacheStatement; const { escaped, loc, strip } = rawMustache; + if ('original' in rawMustache.path && rawMustache.path.original === '...attributes') { + throw generateSyntaxError( + 'Illegal use of ...attributes', + this.source.spanFor(rawMustache.loc) + ); + } + if (isHBSLiteral(rawMustache.path)) { mustache = b.mustache({ path: this.acceptNode<(typeof rawMustache.path)['type']>(rawMustache.path), diff --git a/packages/@glimmer/syntax/test/parser-node-test.ts b/packages/@glimmer/syntax/test/parser-node-test.ts index dc8d3e150e..5bd0ba57b8 100644 --- a/packages/@glimmer/syntax/test/parser-node-test.ts +++ b/packages/@glimmer/syntax/test/parser-node-test.ts @@ -121,6 +121,36 @@ test('a piece of Handlebars with HTML', () => { ); }); +test('attributes are not allowed as values', (assert) => { + let t = '{{...attributes}}'; + assert.throws( + () => { + parse(t, { meta: { moduleName: 'test-module' } }); + }, + syntaxErrorFor('Illegal use of ...attributes', '{{...attributes}}', 'test-module', 1, 0) + ); +}); + +test('attributes are not allowed as modifiers', (assert) => { + let t = '

'; + assert.throws( + () => { + parse(t, { meta: { moduleName: 'test-module' } }); + }, + syntaxErrorFor('Illegal use of ...attributes', '{{...attributes}}', 'test-module', 1, 5) + ); +}); + +test('attributes are not allowed as attribute values', (assert) => { + let t = '
'; + assert.throws( + () => { + parse(t, { meta: { moduleName: 'test-module' } }); + }, + syntaxErrorFor('Illegal use of ...attributes', '{{...attributes}}', 'test-module', 1, 11) + ); +}); + test('Handlebars embedded in an attribute (quoted)', () => { let t = 'some
content
done'; astEqual( From 4f73b2073011ace7a504f0e02c949afdaf172318 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:58:54 -0400 Subject: [PATCH 08/15] v0.91.0 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- packages/@glimmer/compiler/package.json | 2 +- packages/@glimmer/debug/package.json | 2 +- packages/@glimmer/destroyable/package.json | 2 +- packages/@glimmer/encoder/package.json | 2 +- packages/@glimmer/global-context/package.json | 2 +- packages/@glimmer/interfaces/package.json | 2 +- packages/@glimmer/local-debug-flags/package.json | 2 +- packages/@glimmer/manager/package.json | 2 +- packages/@glimmer/node/package.json | 2 +- packages/@glimmer/opcode-compiler/package.json | 2 +- packages/@glimmer/owner/package.json | 2 +- packages/@glimmer/program/package.json | 2 +- packages/@glimmer/reference/package.json | 2 +- packages/@glimmer/runtime/package.json | 2 +- packages/@glimmer/syntax/package.json | 2 +- packages/@glimmer/util/package.json | 2 +- packages/@glimmer/validator/package.json | 2 +- packages/@glimmer/vm-babel-plugins/package.json | 2 +- packages/@glimmer/vm/package.json | 2 +- packages/@glimmer/wire-format/package.json | 2 +- 22 files changed, 31 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d88eb3fa6..67c9153b00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,16 @@ + + +## v0.91.0 (2024-03-25) + +#### :rocket: Enhancement +* `@glimmer/syntax` + * [#1582](https://github.com/glimmerjs/glimmer-vm/pull/1582) Prevent use of ...attributes in invalid places ([@NullVoxPopuli](https://github.com/NullVoxPopuli)) + +#### Committers: 1 +- [@NullVoxPopuli](https://github.com/NullVoxPopuli) ## v0.90.1 (2024-03-22) diff --git a/package.json b/package.json index 303a3b522f..b31c404f51 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glimmer-engine", - "version": "0.90.1", + "version": "0.91.0", "private": true, "license": "MIT", "description": "Glimmer compiles Handlebars templates into document fragments rather than string buffers", diff --git a/packages/@glimmer/compiler/package.json b/packages/@glimmer/compiler/package.json index 276ea6de34..ddaa1cfbad 100644 --- a/packages/@glimmer/compiler/package.json +++ b/packages/@glimmer/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/compiler", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/compiler", "type": "module", diff --git a/packages/@glimmer/debug/package.json b/packages/@glimmer/debug/package.json index 8a0eedb903..c0030fee1a 100644 --- a/packages/@glimmer/debug/package.json +++ b/packages/@glimmer/debug/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/debug", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/debug", "type": "module", diff --git a/packages/@glimmer/destroyable/package.json b/packages/@glimmer/destroyable/package.json index f9f3a64b02..c06d743de3 100644 --- a/packages/@glimmer/destroyable/package.json +++ b/packages/@glimmer/destroyable/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/destroyable", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Utilities for creating and managing a destroyable hierarchy of objects", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/destroyable", diff --git a/packages/@glimmer/encoder/package.json b/packages/@glimmer/encoder/package.json index e0be0356a0..e399bb31ba 100644 --- a/packages/@glimmer/encoder/package.json +++ b/packages/@glimmer/encoder/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/encoder", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/encoder", "type": "module", diff --git a/packages/@glimmer/global-context/package.json b/packages/@glimmer/global-context/package.json index 4f136ded8e..23f9aad48c 100644 --- a/packages/@glimmer/global-context/package.json +++ b/packages/@glimmer/global-context/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/global-context", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/global-context", "type": "module", diff --git a/packages/@glimmer/interfaces/package.json b/packages/@glimmer/interfaces/package.json index 9372cfee89..3449bf6ec4 100644 --- a/packages/@glimmer/interfaces/package.json +++ b/packages/@glimmer/interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/interfaces", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/interfaces", "type": "module", diff --git a/packages/@glimmer/local-debug-flags/package.json b/packages/@glimmer/local-debug-flags/package.json index 7f049e1696..d54f8e1fe7 100644 --- a/packages/@glimmer/local-debug-flags/package.json +++ b/packages/@glimmer/local-debug-flags/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/local-debug-flags", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Helpers for debugging during local development. These get stripped from published builds. This package should not be published.", "repository": { diff --git a/packages/@glimmer/manager/package.json b/packages/@glimmer/manager/package.json index e32d9b3fdd..f4cc779c1f 100644 --- a/packages/@glimmer/manager/package.json +++ b/packages/@glimmer/manager/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/manager", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/node/package.json b/packages/@glimmer/node/package.json index c0de408d31..9b6319882d 100644 --- a/packages/@glimmer/node/package.json +++ b/packages/@glimmer/node/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/node", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/node", "type": "module", diff --git a/packages/@glimmer/opcode-compiler/package.json b/packages/@glimmer/opcode-compiler/package.json index 129a0ce75a..50dbe630d7 100644 --- a/packages/@glimmer/opcode-compiler/package.json +++ b/packages/@glimmer/opcode-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/opcode-compiler", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/opcode-compiler", "type": "module", diff --git a/packages/@glimmer/owner/package.json b/packages/@glimmer/owner/package.json index 0c3dc21da5..a08c1024dc 100644 --- a/packages/@glimmer/owner/package.json +++ b/packages/@glimmer/owner/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/owner", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Implementation for the owner in Glimmer apps", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/owner", diff --git a/packages/@glimmer/program/package.json b/packages/@glimmer/program/package.json index efe244908e..395fa61f68 100644 --- a/packages/@glimmer/program/package.json +++ b/packages/@glimmer/program/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/program", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/reference/package.json b/packages/@glimmer/reference/package.json index f7faa1d4c3..41aa20fdbd 100644 --- a/packages/@glimmer/reference/package.json +++ b/packages/@glimmer/reference/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/reference", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/runtime/package.json b/packages/@glimmer/runtime/package.json index 89c34d6d0b..51bf3dff6a 100644 --- a/packages/@glimmer/runtime/package.json +++ b/packages/@glimmer/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/runtime", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Minimal runtime needed to render Glimmer templates", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/runtime", diff --git a/packages/@glimmer/syntax/package.json b/packages/@glimmer/syntax/package.json index a7efe58834..e51643f6cd 100644 --- a/packages/@glimmer/syntax/package.json +++ b/packages/@glimmer/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/syntax", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/syntax", "type": "module", diff --git a/packages/@glimmer/util/package.json b/packages/@glimmer/util/package.json index 8b46709729..65e98d6831 100644 --- a/packages/@glimmer/util/package.json +++ b/packages/@glimmer/util/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/util", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Common utilities used in Glimmer", "repository": "https://github.com/tildeio/glimmer/tree/main/packages/@glimmer/util", diff --git a/packages/@glimmer/validator/package.json b/packages/@glimmer/validator/package.json index 451803e648..492f642538 100644 --- a/packages/@glimmer/validator/package.json +++ b/packages/@glimmer/validator/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/validator", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/vm-babel-plugins/package.json b/packages/@glimmer/vm-babel-plugins/package.json index 972f3c1688..2543e020ed 100644 --- a/packages/@glimmer/vm-babel-plugins/package.json +++ b/packages/@glimmer/vm-babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm-babel-plugins", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "Compiles out VM assertion and deprecation utilities and debug tooling based on environment", "repository": "https://github.com/glimmerjs/glimmer.js", diff --git a/packages/@glimmer/vm/package.json b/packages/@glimmer/vm/package.json index ab1a9a8ed4..f600957575 100644 --- a/packages/@glimmer/vm/package.json +++ b/packages/@glimmer/vm/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/vm", "type": "module", diff --git a/packages/@glimmer/wire-format/package.json b/packages/@glimmer/wire-format/package.json index 00f1427a28..f1c1a92007 100644 --- a/packages/@glimmer/wire-format/package.json +++ b/packages/@glimmer/wire-format/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/wire-format", - "version": "0.90.1", + "version": "0.91.0", "license": "MIT", "description": "", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/wire-format", From d051884981c3f45f0833a03cddef707a38094da8 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Thu, 28 Mar 2024 13:42:09 -0700 Subject: [PATCH 09/15] [BUGFIX] Ensure legacy path.parts matches existing semantics (#1583) The refactor in #1568 slightly changed the semantics of `path.parts` in that it didn't previously include `this` or the leading `@`. This commit restores the previous semantics. --- .../@glimmer/syntax/lib/v1/legacy-interop.ts | 33 ++++++++++-- .../syntax/test/legacy-interop-test.ts | 51 +++++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 packages/@glimmer/syntax/test/legacy-interop-test.ts diff --git a/packages/@glimmer/syntax/lib/v1/legacy-interop.ts b/packages/@glimmer/syntax/lib/v1/legacy-interop.ts index 404ba20d14..687a0787d3 100644 --- a/packages/@glimmer/syntax/lib/v1/legacy-interop.ts +++ b/packages/@glimmer/syntax/lib/v1/legacy-interop.ts @@ -61,12 +61,35 @@ export function buildLegacyPath({ head, tail, loc }: PathExpressionParams): ASTv Object.defineProperty(node, 'parts', { enumerable: false, get(this: { original: string }): readonly string[] { - deprecate(`The parts property on path nodes is deprecated, use trusting instead`); - return Object.freeze(this.original.split('.')); + deprecate(`The parts property on path nodes is deprecated, use head and tail instead`); + let parts = asPresentArray(this.original.split('.')); + + if (parts[0] === 'this') { + // parts does not include `this` + parts.shift(); + } else if (parts[0].startsWith('@')) { + // parts does not include leading `@` + parts[0] = parts[0].slice(1); + } + + return Object.freeze(parts); }, - set(this: { original: string }, value: PresentArray) { - deprecate(`The parts property on mustache nodes is deprecated, use trusting instead`); - this.original = value.join('.'); + set(this: { head: ASTv1.PathHead; original: string }, values: PresentArray) { + deprecate(`The parts property on mustache nodes is deprecated, use head and tail instead`); + + let parts = [...values]; + + // you are not supposed to already have `this` or `@` in the parts, but since this is + // deprecated anyway, we will infer what you meant and allow it + if (parts[0] !== 'this' && !parts[0]?.startsWith('@')) { + if (this.head.type === 'ThisHead') { + parts.unshift('this'); + } else if (this.head.type === 'AtHead') { + parts[0] = `@${parts[0]}`; + } + } + + this.original = parts.join('.'); }, }); diff --git a/packages/@glimmer/syntax/test/legacy-interop-test.ts b/packages/@glimmer/syntax/test/legacy-interop-test.ts new file mode 100644 index 0000000000..367f8a3948 --- /dev/null +++ b/packages/@glimmer/syntax/test/legacy-interop-test.ts @@ -0,0 +1,51 @@ +import { builders as b } from '@glimmer/syntax'; + +QUnit.module('[glimmer-syntax] AST nodes legacy interop'); + +QUnit.test('path.parts does not include this', (assert) => { + let path = b.path('this.foo.bar'); + + assert.deepEqual(path.original, 'this.foo.bar', 'path.original should include this'); + assert.deepEqual(path.head.type, 'ThisHead', 'path.head should be a ThisHead'); + assert.deepEqual(path.parts, ['foo', 'bar'], 'path.parts should not include this'); + + path.parts = ['bar', 'baz']; + + assert.deepEqual(path.original, 'this.bar.baz', 'path.original should include this'); + assert.deepEqual(path.head.type, 'ThisHead', 'path.head should be a ThisHead'); + assert.deepEqual(path.parts, ['bar', 'baz'], 'path.parts should not include this'); + + path.head = b.head('@foo'); + assert.deepEqual(path.head.type, 'AtHead', 'path.head should be a AtHead'); + + // Inconsistent, but we will allow it + path.parts = ['this', 'foo', 'bar', 'baz']; + + assert.deepEqual(path.original, 'this.foo.bar.baz', 'path.original should include this'); + assert.deepEqual(path.head.type, 'ThisHead', 'path.head should be a ThisHead'); + assert.deepEqual(path.parts, ['foo', 'bar', 'baz'], 'path.parts should not include this'); +}); + +QUnit.test('path.parts does not include @', (assert) => { + let path = b.path('@foo.bar'); + + assert.deepEqual(path.original, '@foo.bar', 'path.original should include @'); + assert.deepEqual(path.head.type, 'AtHead', 'path.head should be a AtHead'); + assert.deepEqual(path.parts, ['foo', 'bar'], 'path.parts should not include @'); + + path.parts = ['bar', 'baz']; + + assert.deepEqual(path.original, '@bar.baz', 'path.original should include @'); + assert.deepEqual(path.head.type, 'AtHead', 'path.head should be a AtHead'); + assert.deepEqual(path.parts, ['bar', 'baz'], 'path.parts should not include @'); + + path.head = b.head('this'); + assert.deepEqual(path.head.type, 'ThisHead', 'path.head should be a ThisHead'); + + // Inconsistent, but we will allow it + path.parts = ['@foo', 'bar', 'baz']; + + assert.deepEqual(path.original, '@foo.bar.baz', 'path.original should include @'); + assert.deepEqual(path.head.type, 'AtHead', 'path.head should be a AtHead'); + assert.deepEqual(path.parts, ['foo', 'bar', 'baz'], 'path.parts should not include this'); +}); From ba4882154b5d10df29467616f9bdb7de7e82bf1c Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:43:35 -0400 Subject: [PATCH 10/15] v0.91.1 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- packages/@glimmer/compiler/package.json | 2 +- packages/@glimmer/debug/package.json | 2 +- packages/@glimmer/destroyable/package.json | 2 +- packages/@glimmer/encoder/package.json | 2 +- packages/@glimmer/global-context/package.json | 2 +- packages/@glimmer/interfaces/package.json | 2 +- packages/@glimmer/local-debug-flags/package.json | 2 +- packages/@glimmer/manager/package.json | 2 +- packages/@glimmer/node/package.json | 2 +- packages/@glimmer/opcode-compiler/package.json | 2 +- packages/@glimmer/owner/package.json | 2 +- packages/@glimmer/program/package.json | 2 +- packages/@glimmer/reference/package.json | 2 +- packages/@glimmer/runtime/package.json | 2 +- packages/@glimmer/syntax/package.json | 2 +- packages/@glimmer/util/package.json | 2 +- packages/@glimmer/validator/package.json | 2 +- packages/@glimmer/vm-babel-plugins/package.json | 2 +- packages/@glimmer/vm/package.json | 2 +- packages/@glimmer/wire-format/package.json | 2 +- 22 files changed, 31 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67c9153b00..a2e9baf2e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,16 @@ + + +## v0.91.1 (2024-03-28) + +#### :bug: Bug Fix +* `@glimmer/syntax` + * [#1583](https://github.com/glimmerjs/glimmer-vm/pull/1583) [BUGFIX] Ensure legacy path.parts matches existing semantics ([@chancancode](https://github.com/chancancode)) + +#### Committers: 1 +- Godfrey Chan ([@chancancode](https://github.com/chancancode)) ## v0.91.0 (2024-03-25) diff --git a/package.json b/package.json index b31c404f51..16691f9448 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glimmer-engine", - "version": "0.91.0", + "version": "0.91.1", "private": true, "license": "MIT", "description": "Glimmer compiles Handlebars templates into document fragments rather than string buffers", diff --git a/packages/@glimmer/compiler/package.json b/packages/@glimmer/compiler/package.json index ddaa1cfbad..a6dba2dae6 100644 --- a/packages/@glimmer/compiler/package.json +++ b/packages/@glimmer/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/compiler", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/compiler", "type": "module", diff --git a/packages/@glimmer/debug/package.json b/packages/@glimmer/debug/package.json index c0030fee1a..7850086b00 100644 --- a/packages/@glimmer/debug/package.json +++ b/packages/@glimmer/debug/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/debug", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/debug", "type": "module", diff --git a/packages/@glimmer/destroyable/package.json b/packages/@glimmer/destroyable/package.json index c06d743de3..5e81116510 100644 --- a/packages/@glimmer/destroyable/package.json +++ b/packages/@glimmer/destroyable/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/destroyable", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Utilities for creating and managing a destroyable hierarchy of objects", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/destroyable", diff --git a/packages/@glimmer/encoder/package.json b/packages/@glimmer/encoder/package.json index e399bb31ba..2aa08dc486 100644 --- a/packages/@glimmer/encoder/package.json +++ b/packages/@glimmer/encoder/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/encoder", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/encoder", "type": "module", diff --git a/packages/@glimmer/global-context/package.json b/packages/@glimmer/global-context/package.json index 23f9aad48c..9016dcebc6 100644 --- a/packages/@glimmer/global-context/package.json +++ b/packages/@glimmer/global-context/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/global-context", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/global-context", "type": "module", diff --git a/packages/@glimmer/interfaces/package.json b/packages/@glimmer/interfaces/package.json index 3449bf6ec4..f5a1bd323a 100644 --- a/packages/@glimmer/interfaces/package.json +++ b/packages/@glimmer/interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/interfaces", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/interfaces", "type": "module", diff --git a/packages/@glimmer/local-debug-flags/package.json b/packages/@glimmer/local-debug-flags/package.json index d54f8e1fe7..12fc0b0f22 100644 --- a/packages/@glimmer/local-debug-flags/package.json +++ b/packages/@glimmer/local-debug-flags/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/local-debug-flags", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Helpers for debugging during local development. These get stripped from published builds. This package should not be published.", "repository": { diff --git a/packages/@glimmer/manager/package.json b/packages/@glimmer/manager/package.json index f4cc779c1f..1b7aaa4c2c 100644 --- a/packages/@glimmer/manager/package.json +++ b/packages/@glimmer/manager/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/manager", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/node/package.json b/packages/@glimmer/node/package.json index 9b6319882d..8bdb5f1be7 100644 --- a/packages/@glimmer/node/package.json +++ b/packages/@glimmer/node/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/node", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/node", "type": "module", diff --git a/packages/@glimmer/opcode-compiler/package.json b/packages/@glimmer/opcode-compiler/package.json index 50dbe630d7..43cb19a91d 100644 --- a/packages/@glimmer/opcode-compiler/package.json +++ b/packages/@glimmer/opcode-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/opcode-compiler", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/opcode-compiler", "type": "module", diff --git a/packages/@glimmer/owner/package.json b/packages/@glimmer/owner/package.json index a08c1024dc..41b8e6438e 100644 --- a/packages/@glimmer/owner/package.json +++ b/packages/@glimmer/owner/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/owner", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Implementation for the owner in Glimmer apps", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/owner", diff --git a/packages/@glimmer/program/package.json b/packages/@glimmer/program/package.json index 395fa61f68..f5fdc4b17b 100644 --- a/packages/@glimmer/program/package.json +++ b/packages/@glimmer/program/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/program", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/reference/package.json b/packages/@glimmer/reference/package.json index 41aa20fdbd..787ec68e60 100644 --- a/packages/@glimmer/reference/package.json +++ b/packages/@glimmer/reference/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/reference", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/runtime/package.json b/packages/@glimmer/runtime/package.json index 51bf3dff6a..236ee01511 100644 --- a/packages/@glimmer/runtime/package.json +++ b/packages/@glimmer/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/runtime", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Minimal runtime needed to render Glimmer templates", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/runtime", diff --git a/packages/@glimmer/syntax/package.json b/packages/@glimmer/syntax/package.json index e51643f6cd..7f2491c3f8 100644 --- a/packages/@glimmer/syntax/package.json +++ b/packages/@glimmer/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/syntax", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/syntax", "type": "module", diff --git a/packages/@glimmer/util/package.json b/packages/@glimmer/util/package.json index 65e98d6831..e6e206a43d 100644 --- a/packages/@glimmer/util/package.json +++ b/packages/@glimmer/util/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/util", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Common utilities used in Glimmer", "repository": "https://github.com/tildeio/glimmer/tree/main/packages/@glimmer/util", diff --git a/packages/@glimmer/validator/package.json b/packages/@glimmer/validator/package.json index 492f642538..e66db8e79a 100644 --- a/packages/@glimmer/validator/package.json +++ b/packages/@glimmer/validator/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/validator", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/vm-babel-plugins/package.json b/packages/@glimmer/vm-babel-plugins/package.json index 2543e020ed..e7c990c956 100644 --- a/packages/@glimmer/vm-babel-plugins/package.json +++ b/packages/@glimmer/vm-babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm-babel-plugins", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "Compiles out VM assertion and deprecation utilities and debug tooling based on environment", "repository": "https://github.com/glimmerjs/glimmer.js", diff --git a/packages/@glimmer/vm/package.json b/packages/@glimmer/vm/package.json index f600957575..26e3f6a66a 100644 --- a/packages/@glimmer/vm/package.json +++ b/packages/@glimmer/vm/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/vm", "type": "module", diff --git a/packages/@glimmer/wire-format/package.json b/packages/@glimmer/wire-format/package.json index f1c1a92007..2e69a3c2c5 100644 --- a/packages/@glimmer/wire-format/package.json +++ b/packages/@glimmer/wire-format/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/wire-format", - "version": "0.91.0", + "version": "0.91.1", "license": "MIT", "description": "", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/wire-format", From fabf5d4e13f6b96cbaf5ef769ad3ab0a90ca65aa Mon Sep 17 00:00:00 2001 From: Patrick Pircher Date: Fri, 5 Apr 2024 00:38:26 +0200 Subject: [PATCH 11/15] fix extra spaces resulting in invalid block params (#1577) --- .../lib/parser/tokenizer-event-handlers.ts | 1 + .../@glimmer/syntax/test/parser-node-test.ts | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts b/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts index 69bc492a16..568b616f41 100644 --- a/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts +++ b/packages/@glimmer/syntax/lib/parser/tokenizer-event-handlers.ts @@ -421,6 +421,7 @@ export class TokenizerEventHandlers extends HandlebarsNodeVisitors { ); } else { state = { state: 'AfterEndPipe' }; + this.tokenizer.consume(); } } else if (next === '>' || next === '/') { throw generateSyntaxError( diff --git a/packages/@glimmer/syntax/test/parser-node-test.ts b/packages/@glimmer/syntax/test/parser-node-test.ts index 5bd0ba57b8..db63f42616 100644 --- a/packages/@glimmer/syntax/test/parser-node-test.ts +++ b/packages/@glimmer/syntax/test/parser-node-test.ts @@ -316,6 +316,25 @@ test('block with block params', () => { ); }); +test('block with block params edge case: extra spaces', () => { + let t = `{{#foo as | bar bat baz |}}{{bar}} {{bat}} {{baz}}{{/foo}}`; + + astEqual( + t, + b.template([ + b.block( + b.path('foo'), + null, + null, + b.blockItself( + [b.mustache('bar'), b.text(' '), b.mustache('bat'), b.text(' '), b.mustache('baz')], + ['bar', 'bat', 'baz'] + ) + ), + ]) + ); +}); + test('block with block params edge case: multiline', () => { let t = `{{#foo as |bar bat @@ -389,6 +408,21 @@ test('element with block params', () => { ); }); +test('element with block params edge case: extra spaces', () => { + let t = `{{bar}} {{bat}} {{baz}}`; + + astEqual( + t, + b.template([ + element( + 'Foo', + ['as', b.var('bar'), b.var('bat'), b.var('baz')], + ['body', b.mustache('bar'), b.text(' '), b.mustache('bat'), b.text(' '), b.mustache('baz')] + ), + ]) + ); +}); + test('element with block params edge case: multiline', () => { let t = ` Date: Mon, 1 Apr 2024 22:32:47 -0700 Subject: [PATCH 12/15] Remove invalid callee nodes from AST v2 `{{("foo")}}` etc is illegal and already covered by existing tests. Currently this is checked by the parser (handlebars-node-visitors) but AST plugins can violate this constraint in theory. --- .../lib/passes/1-normalization/keywords/impl.ts | 6 ------ packages/@glimmer/syntax/lib/v1/nodes-v1.ts | 1 + packages/@glimmer/syntax/lib/v2/builders.ts | 2 +- packages/@glimmer/syntax/lib/v2/normalize.ts | 15 ++++++++++++++- packages/@glimmer/syntax/lib/v2/objects/base.ts | 6 ++++-- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts index ead8ab356b..88e4f456b9 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts @@ -78,12 +78,6 @@ class KeywordImpl< } } -export type PossibleNode = - | ASTv2.PathExpression - | ASTv2.AppendContent - | ASTv2.CallExpression - | ASTv2.InvokeBlock; - export const KEYWORD_NODES = { Call: ['Call'], Block: ['InvokeBlock'], diff --git a/packages/@glimmer/syntax/lib/v1/nodes-v1.ts b/packages/@glimmer/syntax/lib/v1/nodes-v1.ts index 9c34badb89..baf49864b9 100644 --- a/packages/@glimmer/syntax/lib/v1/nodes-v1.ts +++ b/packages/@glimmer/syntax/lib/v1/nodes-v1.ts @@ -43,6 +43,7 @@ export interface CallParts { path: CallableExpression; params: Expression[]; hash: Hash; + loc: src.SourceSpan; } export type CallNode = diff --git a/packages/@glimmer/syntax/lib/v2/builders.ts b/packages/@glimmer/syntax/lib/v2/builders.ts index 3f109ac995..84aaf215e5 100644 --- a/packages/@glimmer/syntax/lib/v2/builders.ts +++ b/packages/@glimmer/syntax/lib/v2/builders.ts @@ -9,7 +9,7 @@ import { SpanList } from '../source/span-list'; import * as ASTv2 from './api'; export interface CallParts { - callee: ASTv2.ExpressionNode; + callee: ASTv2.CalleeNode; args: ASTv2.Args; } diff --git a/packages/@glimmer/syntax/lib/v2/normalize.ts b/packages/@glimmer/syntax/lib/v2/normalize.ts index 9d8e38c4f5..f226a3d31b 100644 --- a/packages/@glimmer/syntax/lib/v2/normalize.ts +++ b/packages/@glimmer/syntax/lib/v2/normalize.ts @@ -243,7 +243,7 @@ class ExpressionNormalizer { * it to an ASTv2 CallParts. */ callParts(parts: ASTv1.CallParts, context: ASTv2.FreeVarResolution): CallParts { - let { path, params, hash } = parts; + let { path, params, hash, loc } = parts; let callee = this.normalize(path, context); let paramList = params.map((p) => this.normalize(p, ASTv2.STRICT_RESOLUTION)); @@ -261,6 +261,18 @@ class ExpressionNormalizer { this.block.loc(hash.loc) ); + switch (callee.type) { + case 'Literal': + throw generateSyntaxError( + `Invalid invocation of a literal value (\`${callee.value}\`)`, + loc + ); + + // This really shouldn't be possible, something has gone pretty wrong + case 'Interpolate': + throw generateSyntaxError(`Invalid invocation of a interpolated string`, loc); + } + return { callee, args: this.block.builder.args(positional, named, argsLoc), @@ -402,6 +414,7 @@ class StatementNormalizer { path, params, hash, + loc, }, resolution.result ); diff --git a/packages/@glimmer/syntax/lib/v2/objects/base.ts b/packages/@glimmer/syntax/lib/v2/objects/base.ts index 439a027242..59a26c8a97 100644 --- a/packages/@glimmer/syntax/lib/v2/objects/base.ts +++ b/packages/@glimmer/syntax/lib/v2/objects/base.ts @@ -2,7 +2,7 @@ import type { SerializedSourceSpan } from '../../source/span'; import type { Args } from './args'; import type { ElementModifier } from './attr-block'; import type { AppendContent, ContentNode, InvokeBlock, InvokeComponent } from './content'; -import type { CallExpression, ExpressionNode } from './expr'; +import type { CallExpression, PathExpression } from './expr'; import type { BaseNodeFields } from './node'; export interface SerializedBaseNode { @@ -14,10 +14,12 @@ export interface GlimmerParentNodeOptions extends BaseNodeFields { } export interface CallFields extends BaseNodeFields { - callee: ExpressionNode; + callee: CalleeNode; args: Args; } +export type CalleeNode = PathExpression | CallExpression; + export type CallNode = | CallExpression | InvokeBlock From 025e7429bceb7c0a97f0fc4bf0b18b5015f4e331 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Thu, 4 Apr 2024 00:23:37 -0700 Subject: [PATCH 13/15] Introduce `keywords` option for `precompile` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently in #1557 we started emitting build time error for strict mode templates containing undefined references. Previously these were not handled until runtime and so we can only emit runtime errors. However, the previous setup allowed for the host environment to implement additional keywords – these are resolved at runtime with the `lookupBuildInHelper` and `lookupBuiltInModifier` hooks, and the error is only emitted if these hooks returned `null`. To allow for this possibility, `precompile` now accept an option for additional strict mode keywords that the host environment is expected to provide. --- .../lib/modes/jit/compilation-context.ts | 4 +- .../lib/test-helpers/define.ts | 7 +- .../test/strict-mode-test.ts | 30 ++++- .../@glimmer/compiler/lib/builder/builder.ts | 3 +- .../passes/1-normalization/utils/is-node.ts | 109 +----------------- .../visitors/element/classified.ts | 5 - .../1-normalization/visitors/expressions.ts | 11 +- .../1-normalization/visitors/strict-mode.ts | 1 + .../lib/passes/2-encoding/expressions.ts | 10 +- .../compiler/lib/passes/2-encoding/mir.ts | 3 +- .../compiler/lib/wire-format-debug.ts | 2 +- .../lib/compile/wire-format/api.d.ts | 4 +- .../lib/parser/tokenizer-event-handlers.ts | 16 +++ packages/@glimmer/syntax/lib/symbol-table.ts | 35 ++++-- packages/@glimmer/syntax/lib/v2/builders.ts | 8 ++ packages/@glimmer/syntax/lib/v2/normalize.ts | 35 ++++-- .../@glimmer/syntax/lib/v2/objects/base.ts | 4 +- .../@glimmer/syntax/lib/v2/objects/expr.ts | 12 ++ .../syntax/lib/v2/serialize/serialize.ts | 20 ++++ 19 files changed, 168 insertions(+), 151 deletions(-) diff --git a/packages/@glimmer-workspace/integration-tests/lib/modes/jit/compilation-context.ts b/packages/@glimmer-workspace/integration-tests/lib/modes/jit/compilation-context.ts index c567671294..d9b4059427 100644 --- a/packages/@glimmer-workspace/integration-tests/lib/modes/jit/compilation-context.ts +++ b/packages/@glimmer-workspace/integration-tests/lib/modes/jit/compilation-context.ts @@ -23,8 +23,8 @@ export default class JitCompileTimeLookup implements CompileTimeResolver { return this.resolver.lookupComponent(name, owner); } - lookupBuiltInHelper(_name: string): Nullable { - return null; + lookupBuiltInHelper(name: string): Nullable { + return this.resolver.lookupHelper(`$keyword.${name}`); } lookupBuiltInModifier(_name: string): Nullable { diff --git a/packages/@glimmer-workspace/integration-tests/lib/test-helpers/define.ts b/packages/@glimmer-workspace/integration-tests/lib/test-helpers/define.ts index 9194c5cf4e..74e7ac7ee9 100644 --- a/packages/@glimmer-workspace/integration-tests/lib/test-helpers/define.ts +++ b/packages/@glimmer-workspace/integration-tests/lib/test-helpers/define.ts @@ -96,6 +96,9 @@ export interface DefineComponentOptions { // defaults to true when some scopeValues are passed and false otherwise strictMode?: boolean; + + // additional strict-mode keywords + keywords?: string[]; } export function defineComponent( @@ -110,8 +113,10 @@ export function defineComponent( strictMode = scopeValues !== null; } + let keywords = options.keywords ?? []; + let definition = options.definition ?? templateOnlyComponent(); - let templateFactory = createTemplate(templateSource, { strictMode }, scopeValues ?? {}); + let templateFactory = createTemplate(templateSource, { strictMode, keywords }, scopeValues ?? {}); setComponentTemplate(templateFactory, definition); return definition; } diff --git a/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts b/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts index 00e14ee486..940428bbdc 100644 --- a/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts +++ b/packages/@glimmer-workspace/integration-tests/test/strict-mode-test.ts @@ -72,12 +72,16 @@ class GeneralStrictModeTest extends RenderTest { } @test - 'Implicit this lookup does not work'() { + 'Undefined references is an error'() { + this.registerHelper('bar', () => 'should not resolve this helper'); + this.assert.throws( () => { defineComponent({}, '{{bar}}', { definition: class extends GlimmerishComponent { - bar = 'Hello, world!'; + get bar() { + throw new Error('should not fallback to this.bar'); + } }, }); }, @@ -91,6 +95,28 @@ class GeneralStrictModeTest extends RenderTest { ); } + @test + 'Non-native keyword'() { + this.registerHelper('bar', () => { + throw new Error('should not resolve this helper'); + }); + + this.registerHelper('$keyword.bar', () => 'bar keyword'); + + const Foo = defineComponent({}, '{{bar}}', { + keywords: ['bar'], + definition: class extends GlimmerishComponent { + get bar() { + throw new Error('should not fallback to this.bar'); + } + }, + }); + + this.renderComponent(Foo); + this.assertHTML('bar keyword'); + this.assertStableRerender(); + } + @test '{{component}} throws an error if a string is used in strict (append position)'() { this.assert.throws(() => { diff --git a/packages/@glimmer/compiler/lib/builder/builder.ts b/packages/@glimmer/compiler/lib/builder/builder.ts index 5acfd89388..80cdb6e2b3 100644 --- a/packages/@glimmer/compiler/lib/builder/builder.ts +++ b/packages/@glimmer/compiler/lib/builder/builder.ts @@ -632,7 +632,7 @@ export function buildVar( symbols: Symbols, path?: PresentArray ): Expressions.GetPath | Expressions.GetVar { - let op: Expressions.GetVar[0] = Op.GetSymbol; + let op: Expressions.GetPath[0] | Expressions.GetVar[0] = Op.GetSymbol; let sym: number; switch (head.kind) { case VariableKind.Free: @@ -665,6 +665,7 @@ export function buildVar( if (path === undefined || path.length === 0) { return [op, sym]; } else { + assert(op !== Op.GetStrictKeyword, '[BUG] keyword with a path'); return [op, sym, path]; } } diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts index 3f8963d38c..b0131a8efe 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/utils/is-node.ts @@ -1,111 +1,4 @@ -import type { PresentArray } from '@glimmer/interfaces'; -import type { SourceSlice } from '@glimmer/syntax'; -import { ASTv2, generateSyntaxError } from '@glimmer/syntax'; -import { unreachable } from '@glimmer/util'; - -export type HasPath = Node & { - head: ASTv2.PathExpression; -}; - -export type HasArguments = - | { - params: PresentArray; - } - | { - hash: { - pairs: PresentArray; - }; - }; - -export type HelperInvocation = HasPath & - HasArguments; - -export function hasPath(node: N): node is HasPath { - return node.callee.type === 'Path'; -} - -export function isHelperInvocation( - node: ASTv2.CallNode -): node is HelperInvocation { - if (!hasPath(node)) { - return false; - } - - return !node.args.isEmpty(); -} - -export interface SimplePath extends ASTv2.PathExpression { - tail: [SourceSlice]; - data: false; - this: false; -} - -export type SimpleHelper = N & { - path: SimplePath; -}; - -export function isSimplePath(path: ASTv2.ExpressionNode): path is SimplePath { - if (path.type === 'Path') { - let { ref: head, tail: parts } = path; - - return head.type === 'Free' && !ASTv2.isStrictResolution(head.resolution) && parts.length === 0; - } else { - return false; - } -} - -export function isStrictHelper(expr: HasPath): boolean { - if (expr.callee.type !== 'Path') { - return true; - } - - if (expr.callee.ref.type !== 'Free') { - return true; - } - - return ASTv2.isStrictResolution(expr.callee.ref.resolution); -} - -export function assertIsValidModifier( - helper: N -): asserts helper is SimpleHelper { - if (isStrictHelper(helper) || isSimplePath(helper.callee)) { - return; - } - - throw generateSyntaxError( - `\`${printPath(helper.callee)}\` is not a valid name for a modifier`, - helper.loc - ); -} - -function printPath(path: ASTv2.ExpressionNode): string { - switch (path.type) { - case 'Literal': - return JSON.stringify(path.value); - case 'Path': { - let printedPath = [printPathHead(path.ref)]; - printedPath.push(...path.tail.map((t) => t.chars)); - return printedPath.join('.'); - } - case 'Call': - return `(${printPath(path.callee)} ...)`; - case 'Interpolate': - throw unreachable('a concat statement cannot appear as the head of an expression'); - } -} - -function printPathHead(head: ASTv2.VariableReference): string { - switch (head.type) { - case 'Arg': - return head.name.chars; - case 'Free': - case 'Local': - return head.name; - case 'This': - return 'this'; - } -} +import type { ASTv2 } from '@glimmer/syntax'; /** * This function is checking whether an AST node is a triple-curly, which means that it's diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/element/classified.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/element/classified.ts index ec3317b73a..9baa4b9113 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/element/classified.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/element/classified.ts @@ -7,7 +7,6 @@ import { Ok, Result, ResultArray } from '../../../../shared/result'; import { getAttrNamespace } from '../../../../utils'; import * as mir from '../../../2-encoding/mir'; import { MODIFIER_KEYWORDS } from '../../keywords'; -import { assertIsValidModifier, isHelperInvocation } from '../../utils/is-node'; import { convertPathToCallIfKeyword, VISIT_EXPRS } from '../expressions'; export type ValidAttr = mir.StaticAttr | mir.DynamicAttr | mir.SplatAttr; @@ -75,10 +74,6 @@ export class ClassifiedElement { } private modifier(modifier: ASTv2.ElementModifier): Result { - if (isHelperInvocation(modifier)) { - assertIsValidModifier(modifier); - } - let translated = MODIFIER_KEYWORDS.translate(modifier, this.state); if (translated !== null) { diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts index d4f07b4c14..0daae08685 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/expressions.ts @@ -8,13 +8,14 @@ import type { NormalizationState } from '../context'; import { Ok, Result, ResultArray } from '../../../shared/result'; import * as mir from '../../2-encoding/mir'; import { CALL_KEYWORDS } from '../keywords'; -import { hasPath } from '../utils/is-node'; export class NormalizeExpressions { visit(node: ASTv2.ExpressionNode, state: NormalizationState): Result { switch (node.type) { case 'Literal': return Ok(this.Literal(node)); + case 'Keyword': + return Ok(this.Keyword(node)); case 'Interpolate': return this.Interpolate(node, state); case 'Path': @@ -78,6 +79,10 @@ export class NormalizeExpressions { return literal; } + Keyword(keyword: ASTv2.KeywordExpression): ASTv2.KeywordExpression { + return keyword; + } + Interpolate( expr: ASTv2.InterpolateExpression, state: NormalizationState @@ -93,8 +98,8 @@ export class NormalizeExpressions { expr: ASTv2.CallExpression, state: NormalizationState ): Result { - if (!hasPath(expr)) { - throw new Error(`unimplemented subexpression at the head of a subexpression`); + if (expr.callee.type === 'Call') { + throw new Error(`unimplemented: subexpression at the head of a subexpression`); } else { return Result.all( VISIT_EXPRS.visit(expr.callee, state), diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts index 787139a8d5..433e905711 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts @@ -120,6 +120,7 @@ export default class StrictModeValidationPass { ): Result { switch (expression.type) { case 'Literal': + case 'Keyword': case 'Missing': case 'This': case 'Arg': diff --git a/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts b/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts index 9c36287707..f9aeda9257 100644 --- a/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts +++ b/packages/@glimmer/compiler/lib/passes/2-encoding/expressions.ts @@ -1,6 +1,6 @@ import type { PresentArray, WireFormat } from '@glimmer/interfaces'; import type { ASTv2 } from '@glimmer/syntax'; -import { assertPresentArray, isPresentArray, mapPresentArray } from '@glimmer/util'; +import { assert, assertPresentArray, isPresentArray, mapPresentArray } from '@glimmer/util'; import { SexpOpcodes } from '@glimmer/wire-format'; import type * as mir from './mir'; @@ -14,6 +14,8 @@ export class ExpressionEncoder { return undefined; case 'Literal': return this.Literal(expr); + case 'Keyword': + return this.Keyword(expr); case 'CallExpression': return this.CallExpression(expr); case 'PathExpression': @@ -86,9 +88,13 @@ export class ExpressionEncoder { return [isTemplateLocal ? SexpOpcodes.GetLexicalSymbol : SexpOpcodes.GetSymbol, symbol]; } + Keyword({ symbol }: ASTv2.KeywordExpression): WireFormat.Expressions.GetStrictFree { + return [SexpOpcodes.GetStrictKeyword, symbol]; + } + PathExpression({ head, tail }: mir.PathExpression): WireFormat.Expressions.GetPath { let getOp = EXPR.expr(head) as WireFormat.Expressions.GetVar; - + assert(getOp[0] !== SexpOpcodes.GetStrictKeyword, '[BUG] keyword in a PathExpression'); return [...getOp, EXPR.Tail(tail)]; } diff --git a/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts b/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts index 8eb1f49187..cb10e013e8 100644 --- a/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts +++ b/packages/@glimmer/compiler/lib/passes/2-encoding/mir.ts @@ -180,9 +180,10 @@ export class Tail extends node('Tail').fields<{ members: PresentArray string) | undefined; } diff --git a/packages/@glimmer/syntax/lib/symbol-table.ts b/packages/@glimmer/syntax/lib/symbol-table.ts index b6ee4e0432..a9edccb8f1 100644 --- a/packages/@glimmer/syntax/lib/symbol-table.ts +++ b/packages/@glimmer/syntax/lib/symbol-table.ts @@ -15,15 +15,21 @@ interface SymbolTableOptions { } export abstract class SymbolTable { - static top(locals: string[], options: SymbolTableOptions): ProgramSymbolTable { - return new ProgramSymbolTable(locals, options); + static top( + locals: readonly string[], + keywords: readonly string[], + options: SymbolTableOptions + ): ProgramSymbolTable { + return new ProgramSymbolTable(locals, keywords, options); } abstract has(name: string): boolean; abstract get(name: string): [symbol: number, isRoot: boolean]; + abstract hasKeyword(name: string): boolean; + abstract getKeyword(name: string): number; + abstract hasLexical(name: string): boolean; - abstract getLexical(name: string): number; abstract getLocalsMap(): Dict; abstract getDebugInfo(): Core.DebugInfo; @@ -42,7 +48,8 @@ export abstract class SymbolTable { export class ProgramSymbolTable extends SymbolTable { constructor( - private templateLocals: string[], + private templateLocals: readonly string[], + private keywords: readonly string[], private options: SymbolTableOptions ) { super(); @@ -62,8 +69,12 @@ export class ProgramSymbolTable extends SymbolTable { return this.options.lexicalScope(name); } - getLexical(name: string): number { - return this.allocateFree(name, ASTv2.HTML_RESOLUTION); + hasKeyword(name: string): boolean { + return this.keywords.includes(name); + } + + getKeyword(name: string): number { + return this.allocateFree(name, ASTv2.STRICT_RESOLUTION); } getUsedTemplateLocals(): string[] { @@ -166,14 +177,18 @@ export class BlockSymbolTable extends SymbolTable { return this.symbols; } - getLexical(name: string): number { - return this.parent.getLexical(name); - } - hasLexical(name: string): boolean { return this.parent.hasLexical(name); } + getKeyword(name: string): number { + return this.parent.getKeyword(name); + } + + hasKeyword(name: string): boolean { + return this.parent.hasKeyword(name); + } + has(name: string): boolean { return this.symbols.indexOf(name) !== -1 || this.parent.has(name); } diff --git a/packages/@glimmer/syntax/lib/v2/builders.ts b/packages/@glimmer/syntax/lib/v2/builders.ts index 84aaf215e5..fd13939783 100644 --- a/packages/@glimmer/syntax/lib/v2/builders.ts +++ b/packages/@glimmer/syntax/lib/v2/builders.ts @@ -148,6 +148,14 @@ export class Builder { }); } + keyword(name: string, symbol: number, loc: SourceSpan): ASTv2.KeywordExpression { + return new ASTv2.KeywordExpression({ + loc, + name, + symbol, + }); + } + self(loc: SourceSpan): ASTv2.VariableReference { return new ASTv2.ThisReference({ loc, diff --git a/packages/@glimmer/syntax/lib/v2/normalize.ts b/packages/@glimmer/syntax/lib/v2/normalize.ts index f226a3d31b..e9b19e70a5 100644 --- a/packages/@glimmer/syntax/lib/v2/normalize.ts +++ b/packages/@glimmer/syntax/lib/v2/normalize.ts @@ -42,16 +42,13 @@ export function normalize( strictMode: false, ...options, locals: ast.blockParams, + keywords: options.keywords ?? [], }; - let top = SymbolTable.top( - normalizeOptions.locals, - - { - customizeComponentName: options.customizeComponentName ?? ((name) => name), - lexicalScope: options.lexicalScope, - } - ); + let top = SymbolTable.top(normalizeOptions.locals, normalizeOptions.keywords, { + customizeComponentName: options.customizeComponentName ?? ((name) => name), + lexicalScope: options.lexicalScope, + }); let block = new BlockContext(source, normalizeOptions, top); let normalizer = new StatementNormalizer(block); @@ -125,6 +122,10 @@ export class BlockContext { return this.table.hasLexical(variable); } + isKeyword(name: string): boolean { + return this.strict && !this.table.hasLexical(name) && this.table.hasKeyword(name); + } + private isFreeVar(callee: ASTv1.CallNode | ASTv1.PathExpression): boolean { if (callee.type === 'PathExpression') { if (callee.head.type !== 'VarHead') { @@ -217,7 +218,21 @@ class ExpressionNormalizer { private path( expr: ASTv1.MinimalPathExpression, resolution: ASTv2.FreeVarResolution - ): ASTv2.PathExpression { + ): ASTv2.KeywordExpression | ASTv2.PathExpression { + let loc = this.block.loc(expr.loc); + + if ( + expr.head.type === 'VarHead' && + expr.tail.length === 0 && + this.block.isKeyword(expr.head.name) + ) { + return this.block.builder.keyword( + expr.head.name, + this.block.table.getKeyword(expr.head.name), + loc + ); + } + let headOffsets = this.block.loc(expr.head.loc); let tail = []; @@ -235,7 +250,7 @@ class ExpressionNormalizer { ); } - return this.block.builder.path(this.ref(expr.head, resolution), tail, this.block.loc(expr.loc)); + return this.block.builder.path(this.ref(expr.head, resolution), tail, loc); } /** diff --git a/packages/@glimmer/syntax/lib/v2/objects/base.ts b/packages/@glimmer/syntax/lib/v2/objects/base.ts index 59a26c8a97..5568a077cc 100644 --- a/packages/@glimmer/syntax/lib/v2/objects/base.ts +++ b/packages/@glimmer/syntax/lib/v2/objects/base.ts @@ -2,7 +2,7 @@ import type { SerializedSourceSpan } from '../../source/span'; import type { Args } from './args'; import type { ElementModifier } from './attr-block'; import type { AppendContent, ContentNode, InvokeBlock, InvokeComponent } from './content'; -import type { CallExpression, PathExpression } from './expr'; +import type { CallExpression, KeywordExpression, PathExpression } from './expr'; import type { BaseNodeFields } from './node'; export interface SerializedBaseNode { @@ -18,7 +18,7 @@ export interface CallFields extends BaseNodeFields { args: Args; } -export type CalleeNode = PathExpression | CallExpression; +export type CalleeNode = KeywordExpression | PathExpression | CallExpression; export type CallNode = | CallExpression diff --git a/packages/@glimmer/syntax/lib/v2/objects/expr.ts b/packages/@glimmer/syntax/lib/v2/objects/expr.ts index 70f91d06c6..0da46ff03a 100644 --- a/packages/@glimmer/syntax/lib/v2/objects/expr.ts +++ b/packages/@glimmer/syntax/lib/v2/objects/expr.ts @@ -71,6 +71,17 @@ export class PathExpression extends node('Path').fields<{ tail: readonly SourceSlice[]; }>() {} +/** + * Corresponds to a known strict-mode keyword. It behaves similarly to a + * PathExpression with a FreeVarReference, but implies StrictResolution and + * is guaranteed to not have a tail, since `{{outlet.foo}}` would have been + * illegal. + */ +export class KeywordExpression extends node('Keyword').fields<{ + name: string; + symbol: number; +}>() {} + /** * Corresponds to a parenthesized call expression. * @@ -97,5 +108,6 @@ export class InterpolateExpression extends node('Interpolate').fields<{ export type ExpressionNode = | LiteralExpression | PathExpression + | KeywordExpression | CallExpression | InterpolateExpression; diff --git a/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts b/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts index 6c6435814a..068993697d 100644 --- a/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts +++ b/packages/@glimmer/syntax/lib/v2/serialize/serialize.ts @@ -35,6 +35,15 @@ import type { import { SourceSlice } from '../../source/slice'; export class RefSerializer { + keyword(keyword: ASTv2.KeywordExpression): SerializedFreeVarReference { + return { + type: 'Free', + loc: keyword.loc.serialize(), + resolution: 'Strict', + name: keyword.name, + }; + } + arg(ref: ASTv2.ArgReference): SerializedArgReference { return { type: 'Arg', @@ -79,6 +88,15 @@ export class ExprSerializer { }; } + keyword(keyword: ASTv2.KeywordExpression): SerializedPathExpression { + return { + type: 'Path', + loc: keyword.loc.serialize(), + ref: REF.keyword(keyword), + tail: [], + }; + } + path(path: ASTv2.PathExpression): SerializedPathExpression { return { type: 'Path', @@ -268,6 +286,8 @@ const visit = { switch (expr.type) { case 'Literal': return EXPR.literal(expr); + case 'Keyword': + return EXPR.keyword(expr); case 'Path': return EXPR.path(expr); case 'Call': From ec250e63510991d2209c71c5ac42dccd47726bc1 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:34:40 -0400 Subject: [PATCH 14/15] v0.91.2 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- packages/@glimmer/compiler/package.json | 2 +- packages/@glimmer/debug/package.json | 2 +- packages/@glimmer/destroyable/package.json | 2 +- packages/@glimmer/encoder/package.json | 2 +- packages/@glimmer/global-context/package.json | 2 +- packages/@glimmer/interfaces/package.json | 2 +- packages/@glimmer/local-debug-flags/package.json | 2 +- packages/@glimmer/manager/package.json | 2 +- packages/@glimmer/node/package.json | 2 +- packages/@glimmer/opcode-compiler/package.json | 2 +- packages/@glimmer/owner/package.json | 2 +- packages/@glimmer/program/package.json | 2 +- packages/@glimmer/reference/package.json | 2 +- packages/@glimmer/runtime/package.json | 2 +- packages/@glimmer/syntax/package.json | 2 +- packages/@glimmer/util/package.json | 2 +- packages/@glimmer/validator/package.json | 2 +- packages/@glimmer/vm-babel-plugins/package.json | 2 +- packages/@glimmer/vm/package.json | 2 +- packages/@glimmer/wire-format/package.json | 2 +- 22 files changed, 31 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2e9baf2e0..9b9eac1a33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,16 @@ + + +## v0.91.2 (2024-04-05) + +#### :bug: Bug Fix +* `@glimmer/syntax` + * [#1577](https://github.com/glimmerjs/glimmer-vm/pull/1577) fix extra spaces in block params ([@patricklx](https://github.com/patricklx)) + +#### Committers: 1 +- Patrick Pircher ([@patricklx](https://github.com/patricklx)) ## v0.91.1 (2024-03-28) diff --git a/package.json b/package.json index 16691f9448..44a305cba8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glimmer-engine", - "version": "0.91.1", + "version": "0.91.2", "private": true, "license": "MIT", "description": "Glimmer compiles Handlebars templates into document fragments rather than string buffers", diff --git a/packages/@glimmer/compiler/package.json b/packages/@glimmer/compiler/package.json index a6dba2dae6..6d81a115e5 100644 --- a/packages/@glimmer/compiler/package.json +++ b/packages/@glimmer/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/compiler", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/compiler", "type": "module", diff --git a/packages/@glimmer/debug/package.json b/packages/@glimmer/debug/package.json index 7850086b00..d415736a4f 100644 --- a/packages/@glimmer/debug/package.json +++ b/packages/@glimmer/debug/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/debug", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/debug", "type": "module", diff --git a/packages/@glimmer/destroyable/package.json b/packages/@glimmer/destroyable/package.json index 5e81116510..f3c5d579f6 100644 --- a/packages/@glimmer/destroyable/package.json +++ b/packages/@glimmer/destroyable/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/destroyable", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Utilities for creating and managing a destroyable hierarchy of objects", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/destroyable", diff --git a/packages/@glimmer/encoder/package.json b/packages/@glimmer/encoder/package.json index 2aa08dc486..04b8418593 100644 --- a/packages/@glimmer/encoder/package.json +++ b/packages/@glimmer/encoder/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/encoder", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/encoder", "type": "module", diff --git a/packages/@glimmer/global-context/package.json b/packages/@glimmer/global-context/package.json index 9016dcebc6..1cb63ee29c 100644 --- a/packages/@glimmer/global-context/package.json +++ b/packages/@glimmer/global-context/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/global-context", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/global-context", "type": "module", diff --git a/packages/@glimmer/interfaces/package.json b/packages/@glimmer/interfaces/package.json index f5a1bd323a..bf34a6e4f7 100644 --- a/packages/@glimmer/interfaces/package.json +++ b/packages/@glimmer/interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/interfaces", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/interfaces", "type": "module", diff --git a/packages/@glimmer/local-debug-flags/package.json b/packages/@glimmer/local-debug-flags/package.json index 12fc0b0f22..f6056f769e 100644 --- a/packages/@glimmer/local-debug-flags/package.json +++ b/packages/@glimmer/local-debug-flags/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/local-debug-flags", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Helpers for debugging during local development. These get stripped from published builds. This package should not be published.", "repository": { diff --git a/packages/@glimmer/manager/package.json b/packages/@glimmer/manager/package.json index 1b7aaa4c2c..0d2a288570 100644 --- a/packages/@glimmer/manager/package.json +++ b/packages/@glimmer/manager/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/manager", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/node/package.json b/packages/@glimmer/node/package.json index 8bdb5f1be7..72310aaa61 100644 --- a/packages/@glimmer/node/package.json +++ b/packages/@glimmer/node/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/node", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/node", "type": "module", diff --git a/packages/@glimmer/opcode-compiler/package.json b/packages/@glimmer/opcode-compiler/package.json index 43cb19a91d..77f1f50e0a 100644 --- a/packages/@glimmer/opcode-compiler/package.json +++ b/packages/@glimmer/opcode-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/opcode-compiler", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/opcode-compiler", "type": "module", diff --git a/packages/@glimmer/owner/package.json b/packages/@glimmer/owner/package.json index 41b8e6438e..46a6c208d3 100644 --- a/packages/@glimmer/owner/package.json +++ b/packages/@glimmer/owner/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/owner", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Implementation for the owner in Glimmer apps", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/owner", diff --git a/packages/@glimmer/program/package.json b/packages/@glimmer/program/package.json index f5fdc4b17b..7269d6cf80 100644 --- a/packages/@glimmer/program/package.json +++ b/packages/@glimmer/program/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/program", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/reference/package.json b/packages/@glimmer/reference/package.json index 787ec68e60..b94d929fa6 100644 --- a/packages/@glimmer/reference/package.json +++ b/packages/@glimmer/reference/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/reference", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/runtime/package.json b/packages/@glimmer/runtime/package.json index 236ee01511..a2a729455d 100644 --- a/packages/@glimmer/runtime/package.json +++ b/packages/@glimmer/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/runtime", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Minimal runtime needed to render Glimmer templates", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/runtime", diff --git a/packages/@glimmer/syntax/package.json b/packages/@glimmer/syntax/package.json index 7f2491c3f8..00672c6081 100644 --- a/packages/@glimmer/syntax/package.json +++ b/packages/@glimmer/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/syntax", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/syntax", "type": "module", diff --git a/packages/@glimmer/util/package.json b/packages/@glimmer/util/package.json index e6e206a43d..9b1b125121 100644 --- a/packages/@glimmer/util/package.json +++ b/packages/@glimmer/util/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/util", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Common utilities used in Glimmer", "repository": "https://github.com/tildeio/glimmer/tree/main/packages/@glimmer/util", diff --git a/packages/@glimmer/validator/package.json b/packages/@glimmer/validator/package.json index e66db8e79a..3612a3cf7c 100644 --- a/packages/@glimmer/validator/package.json +++ b/packages/@glimmer/validator/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/validator", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/vm-babel-plugins/package.json b/packages/@glimmer/vm-babel-plugins/package.json index e7c990c956..8c9108fbbe 100644 --- a/packages/@glimmer/vm-babel-plugins/package.json +++ b/packages/@glimmer/vm-babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm-babel-plugins", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "Compiles out VM assertion and deprecation utilities and debug tooling based on environment", "repository": "https://github.com/glimmerjs/glimmer.js", diff --git a/packages/@glimmer/vm/package.json b/packages/@glimmer/vm/package.json index 26e3f6a66a..18a59fbc29 100644 --- a/packages/@glimmer/vm/package.json +++ b/packages/@glimmer/vm/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/vm", "type": "module", diff --git a/packages/@glimmer/wire-format/package.json b/packages/@glimmer/wire-format/package.json index 2e69a3c2c5..50054c4da5 100644 --- a/packages/@glimmer/wire-format/package.json +++ b/packages/@glimmer/wire-format/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/wire-format", - "version": "0.91.1", + "version": "0.91.2", "license": "MIT", "description": "", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/wire-format", From 72bd97c92933c3779711de2adabf0cd217cae478 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:41:19 -0400 Subject: [PATCH 15/15] v0.92.0 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- packages/@glimmer/compiler/package.json | 2 +- packages/@glimmer/debug/package.json | 2 +- packages/@glimmer/destroyable/package.json | 2 +- packages/@glimmer/encoder/package.json | 2 +- packages/@glimmer/global-context/package.json | 2 +- packages/@glimmer/interfaces/package.json | 2 +- packages/@glimmer/local-debug-flags/package.json | 2 +- packages/@glimmer/manager/package.json | 2 +- packages/@glimmer/node/package.json | 2 +- packages/@glimmer/opcode-compiler/package.json | 2 +- packages/@glimmer/owner/package.json | 2 +- packages/@glimmer/program/package.json | 2 +- packages/@glimmer/reference/package.json | 2 +- packages/@glimmer/runtime/package.json | 2 +- packages/@glimmer/syntax/package.json | 2 +- packages/@glimmer/util/package.json | 2 +- packages/@glimmer/validator/package.json | 2 +- packages/@glimmer/vm-babel-plugins/package.json | 2 +- packages/@glimmer/vm/package.json | 2 +- packages/@glimmer/wire-format/package.json | 2 +- 22 files changed, 31 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9eac1a33..a154c93f0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,16 @@ + + +## v0.92.0 (2024-04-08) + +#### :rocket: Enhancement +* `@glimmer-workspace/integration-tests`, `@glimmer/compiler`, `@glimmer/interfaces`, `@glimmer/syntax` + * [#1585](https://github.com/glimmerjs/glimmer-vm/pull/1585) Introduce `keywords` option for `precompile` ([@chancancode](https://github.com/chancancode)) + +#### Committers: 1 +- Godfrey Chan ([@chancancode](https://github.com/chancancode)) ## v0.91.2 (2024-04-05) diff --git a/package.json b/package.json index 44a305cba8..3ae7238df0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glimmer-engine", - "version": "0.91.2", + "version": "0.92.0", "private": true, "license": "MIT", "description": "Glimmer compiles Handlebars templates into document fragments rather than string buffers", diff --git a/packages/@glimmer/compiler/package.json b/packages/@glimmer/compiler/package.json index 6d81a115e5..e1f486f9ff 100644 --- a/packages/@glimmer/compiler/package.json +++ b/packages/@glimmer/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/compiler", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/compiler", "type": "module", diff --git a/packages/@glimmer/debug/package.json b/packages/@glimmer/debug/package.json index d415736a4f..3d84e33e3c 100644 --- a/packages/@glimmer/debug/package.json +++ b/packages/@glimmer/debug/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/debug", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/debug", "type": "module", diff --git a/packages/@glimmer/destroyable/package.json b/packages/@glimmer/destroyable/package.json index f3c5d579f6..3275a8e927 100644 --- a/packages/@glimmer/destroyable/package.json +++ b/packages/@glimmer/destroyable/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/destroyable", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Utilities for creating and managing a destroyable hierarchy of objects", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/destroyable", diff --git a/packages/@glimmer/encoder/package.json b/packages/@glimmer/encoder/package.json index 04b8418593..f089c0fe67 100644 --- a/packages/@glimmer/encoder/package.json +++ b/packages/@glimmer/encoder/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/encoder", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/encoder", "type": "module", diff --git a/packages/@glimmer/global-context/package.json b/packages/@glimmer/global-context/package.json index 1cb63ee29c..7d225bf65b 100644 --- a/packages/@glimmer/global-context/package.json +++ b/packages/@glimmer/global-context/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/global-context", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/global-context", "type": "module", diff --git a/packages/@glimmer/interfaces/package.json b/packages/@glimmer/interfaces/package.json index bf34a6e4f7..dff50949af 100644 --- a/packages/@glimmer/interfaces/package.json +++ b/packages/@glimmer/interfaces/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/interfaces", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/interfaces", "type": "module", diff --git a/packages/@glimmer/local-debug-flags/package.json b/packages/@glimmer/local-debug-flags/package.json index f6056f769e..d7b379243c 100644 --- a/packages/@glimmer/local-debug-flags/package.json +++ b/packages/@glimmer/local-debug-flags/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/local-debug-flags", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Helpers for debugging during local development. These get stripped from published builds. This package should not be published.", "repository": { diff --git a/packages/@glimmer/manager/package.json b/packages/@glimmer/manager/package.json index 0d2a288570..0b4e7e2475 100644 --- a/packages/@glimmer/manager/package.json +++ b/packages/@glimmer/manager/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/manager", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/node/package.json b/packages/@glimmer/node/package.json index 72310aaa61..52ba1c6ef7 100644 --- a/packages/@glimmer/node/package.json +++ b/packages/@glimmer/node/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/node", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/node", "type": "module", diff --git a/packages/@glimmer/opcode-compiler/package.json b/packages/@glimmer/opcode-compiler/package.json index 77f1f50e0a..3acfeaf5fb 100644 --- a/packages/@glimmer/opcode-compiler/package.json +++ b/packages/@glimmer/opcode-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/opcode-compiler", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/opcode-compiler", "type": "module", diff --git a/packages/@glimmer/owner/package.json b/packages/@glimmer/owner/package.json index 46a6c208d3..d8bcf1f352 100644 --- a/packages/@glimmer/owner/package.json +++ b/packages/@glimmer/owner/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/owner", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Implementation for the owner in Glimmer apps", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/owner", diff --git a/packages/@glimmer/program/package.json b/packages/@glimmer/program/package.json index 7269d6cf80..1d764cd385 100644 --- a/packages/@glimmer/program/package.json +++ b/packages/@glimmer/program/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/program", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/program", "type": "module", diff --git a/packages/@glimmer/reference/package.json b/packages/@glimmer/reference/package.json index b94d929fa6..34e8b41ca4 100644 --- a/packages/@glimmer/reference/package.json +++ b/packages/@glimmer/reference/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/reference", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/runtime/package.json b/packages/@glimmer/runtime/package.json index a2a729455d..03ee457852 100644 --- a/packages/@glimmer/runtime/package.json +++ b/packages/@glimmer/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/runtime", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Minimal runtime needed to render Glimmer templates", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/runtime", diff --git a/packages/@glimmer/syntax/package.json b/packages/@glimmer/syntax/package.json index 00672c6081..7dcef56683 100644 --- a/packages/@glimmer/syntax/package.json +++ b/packages/@glimmer/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/syntax", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/syntax", "type": "module", diff --git a/packages/@glimmer/util/package.json b/packages/@glimmer/util/package.json index 9b1b125121..57e5ca363f 100644 --- a/packages/@glimmer/util/package.json +++ b/packages/@glimmer/util/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/util", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Common utilities used in Glimmer", "repository": "https://github.com/tildeio/glimmer/tree/main/packages/@glimmer/util", diff --git a/packages/@glimmer/validator/package.json b/packages/@glimmer/validator/package.json index 3612a3cf7c..2439225796 100644 --- a/packages/@glimmer/validator/package.json +++ b/packages/@glimmer/validator/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/validator", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Objects used to track values and their dirtiness in Glimmer", "repository": { diff --git a/packages/@glimmer/vm-babel-plugins/package.json b/packages/@glimmer/vm-babel-plugins/package.json index 8c9108fbbe..72fe1e7eee 100644 --- a/packages/@glimmer/vm-babel-plugins/package.json +++ b/packages/@glimmer/vm-babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm-babel-plugins", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "Compiles out VM assertion and deprecation utilities and debug tooling based on environment", "repository": "https://github.com/glimmerjs/glimmer.js", diff --git a/packages/@glimmer/vm/package.json b/packages/@glimmer/vm/package.json index 18a59fbc29..bfa8dad3e1 100644 --- a/packages/@glimmer/vm/package.json +++ b/packages/@glimmer/vm/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/vm", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/vm", "type": "module", diff --git a/packages/@glimmer/wire-format/package.json b/packages/@glimmer/wire-format/package.json index 50054c4da5..f2e541a2c3 100644 --- a/packages/@glimmer/wire-format/package.json +++ b/packages/@glimmer/wire-format/package.json @@ -1,6 +1,6 @@ { "name": "@glimmer/wire-format", - "version": "0.91.2", + "version": "0.92.0", "license": "MIT", "description": "", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/wire-format",