diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/context.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/context.ts index 4e2b541187..d379a005c1 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/context.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/context.ts @@ -9,29 +9,29 @@ import { VISIT_STMTS } from './visitors/statements'; * This is the mutable state for this compiler pass. */ export class NormalizationState { - #currentScope: SymbolTable; - #cursorCount = 0; + _currentScope: SymbolTable; + _cursorCount = 0; constructor(block: SymbolTable, readonly isStrict: boolean) { - this.#currentScope = block; + this._currentScope = block; } generateUniqueCursor(): string { - return `%cursor:${this.#cursorCount++}%`; + return `%cursor:${this._cursorCount++}%`; } get scope(): SymbolTable { - return this.#currentScope; + return this._currentScope; } visitBlock(block: ASTv2.Block): Result> { - let oldBlock = this.#currentScope; - this.#currentScope = block.scope; + let oldBlock = this._currentScope; + this._currentScope = block.scope; try { return VISIT_STMTS.visitList(block.body, this); } finally { - this.#currentScope = oldBlock; + this._currentScope = oldBlock; } } } 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 b8da4c1216..dd9019b9a2 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/keywords/impl.ts @@ -168,18 +168,18 @@ function getCalleeExpression( export class Keywords = never> implements Keyword> { - #keywords: Keyword[] = []; - #type: K; + _keywords: Keyword[] = []; + _type: K; constructor(type: K) { - this.#type = type; + this._type = type; } kw( name: S, delegate: KeywordDelegate ): Keywords> { - this.#keywords.push(keyword(name, this.#type, delegate)); + this._keywords.push(keyword(name, this._type, delegate)); return this; } @@ -188,7 +188,7 @@ export class Keywords = ne node: KeywordCandidates[K], state: NormalizationState ): Result> | null { - for (let keyword of this.#keywords) { + for (let keyword of this._keywords) { let result = keyword.translate(node, state) as Result>; if (result !== null) { return result; @@ -200,7 +200,7 @@ export class Keywords = ne if (path && path.type === 'Path' && path.ref.type === 'Free' && isKeyword(path.ref.name)) { let { name } = path.ref; - let usedType = this.#type; + let usedType = this._type; let validTypes = KEYWORDS_TYPES[name]; if (validTypes.indexOf(usedType) === -1) { diff --git a/packages/@glimmer/syntax/lib/source/loc/match.ts b/packages/@glimmer/syntax/lib/source/loc/match.ts index f441011997..41b6020f1e 100644 --- a/packages/@glimmer/syntax/lib/source/loc/match.ts +++ b/packages/@glimmer/syntax/lib/source/loc/match.ts @@ -28,14 +28,14 @@ export type IsInvisible = 'IS_INVISIBLE'; type Pattern = OffsetKind | IsInvisible | MatchAny; class WhenList { - #whens: When[]; + _whens: When[]; constructor(whens: When[]) { - this.#whens = whens; + this._whens = whens; } first(kind: OffsetKind): Out | null { - for (let when of this.#whens) { + for (let when of this._whens) { let value = when.match(kind); if (isPresent(value)) { return value[0]; @@ -47,10 +47,10 @@ class WhenList { } class When { - #map: Map = new Map(); + _map: Map = new Map(); get(pattern: Pattern, or: () => Out): Out { - let value = this.#map.get(pattern); + let value = this._map.get(pattern); if (value) { return value; @@ -58,13 +58,13 @@ class When { value = or(); - this.#map.set(pattern, value); + this._map.set(pattern, value); return value; } add(pattern: Pattern, out: Out): void { - this.#map.set(pattern, out); + this._map.set(pattern, out); } match(kind: OffsetKind): Out[] { @@ -72,8 +72,8 @@ class When { let out: Out[] = []; - let exact = this.#map.get(pattern); - let fallback = this.#map.get(MatchAny); + let exact = this._map.get(pattern); + let fallback = this._map.get(MatchAny); if (exact) { out.push(exact); @@ -105,7 +105,7 @@ export function match(callback: (m: Matcher) => ExhaustiveMatcher } class Matcher { - #whens: When Out>> = new When(); + _whens: When Out>> = new When(); /** * You didn't exhaustively match all possibilities. @@ -118,7 +118,7 @@ class Matcher { left: OffsetKind, right: OffsetKind ): (left: PositionData, right: PositionData) => Out { - let nesteds = this.#whens.match(left); + let nesteds = this._whens.match(left); assert( isPresent(nesteds), @@ -179,7 +179,7 @@ class Matcher { // eslint-disable-next-line @typescript-eslint/no-explicit-any callback: (left: any, right: any) => Out ): Matcher | ExhaustiveMatcher { - this.#whens.get(left, () => new When()).add(right, callback); + this._whens.get(left, () => new When()).add(right, callback); return this; } diff --git a/packages/@glimmer/syntax/lib/source/loc/offset.ts b/packages/@glimmer/syntax/lib/source/loc/offset.ts index 8cd9091969..76dd62ac85 100644 --- a/packages/@glimmer/syntax/lib/source/loc/offset.ts +++ b/packages/@glimmer/syntax/lib/source/loc/offset.ts @@ -162,7 +162,7 @@ export class CharPosition implements PositionData { readonly kind = OffsetKind.CharPosition; /** Computed from char offset */ - #locPos: HbsPosition | BROKEN | null = null; + _locPos: HbsPosition | BROKEN | null = null; constructor(readonly source: Source, readonly charPos: number) {} @@ -206,15 +206,15 @@ export class CharPosition implements PositionData { * computing the `HbsPosition` should be a one-time operation. */ toHbsPos(): HbsPosition | null { - let locPos = this.#locPos; + let locPos = this._locPos; if (locPos === null) { let hbsPos = this.source.hbsPosFor(this.charPos); if (hbsPos === null) { - this.#locPos = locPos = BROKEN; + this._locPos = locPos = BROKEN; } else { - this.#locPos = locPos = new HbsPosition(this.source, hbsPos, this.charPos); + this._locPos = locPos = new HbsPosition(this.source, hbsPos, this.charPos); } } @@ -225,14 +225,14 @@ export class CharPosition implements PositionData { export class HbsPosition implements PositionData { readonly kind = OffsetKind.HbsPosition; - #charPos: CharPosition | BROKEN | null; + _charPos: CharPosition | BROKEN | null; constructor( readonly source: Source, readonly hbsPos: SourcePosition, charPos: number | null = null ) { - this.#charPos = charPos === null ? null : new CharPosition(source, charPos); + this._charPos = charPos === null ? null : new CharPosition(source, charPos); } /** @@ -244,15 +244,15 @@ export class HbsPosition implements PositionData { * @implements {PositionData} */ toCharPos(): CharPosition | null { - let charPos = this.#charPos; + let charPos = this._charPos; if (charPos === null) { let charPosNumber = this.source.charPosFor(this.hbsPos); if (charPosNumber === null) { - this.#charPos = charPos = BROKEN; + this._charPos = charPos = BROKEN; } else { - this.#charPos = charPos = new CharPosition(this.source, charPosNumber); + this._charPos = charPos = new CharPosition(this.source, charPosNumber); } } diff --git a/packages/@glimmer/syntax/lib/source/loc/span.ts b/packages/@glimmer/syntax/lib/source/loc/span.ts index e230fae344..ef4a351d06 100644 --- a/packages/@glimmer/syntax/lib/source/loc/span.ts +++ b/packages/@glimmer/syntax/lib/source/loc/span.ts @@ -313,7 +313,7 @@ type AnySpan = HbsSpan | CharPositionSpan | InvisibleSpan; class CharPositionSpan implements SpanData { readonly kind = OffsetKind.CharPosition; - #locPosSpan: HbsSpan | BROKEN | null = null; + _locPosSpan: HbsSpan | BROKEN | null = null; constructor( readonly source: Source, @@ -350,16 +350,16 @@ class CharPositionSpan implements SpanData { } toHbsSpan(): HbsSpan | null { - let locPosSpan = this.#locPosSpan; + let locPosSpan = this._locPosSpan; if (locPosSpan === null) { let start = this.charPositions.start.toHbsPos(); let end = this.charPositions.end.toHbsPos(); if (start === null || end === null) { - locPosSpan = this.#locPosSpan = BROKEN; + locPosSpan = this._locPosSpan = BROKEN; } else { - locPosSpan = this.#locPosSpan = new HbsSpan(this.source, { + locPosSpan = this._locPosSpan = new HbsSpan(this.source, { start, end, }); @@ -390,17 +390,17 @@ class CharPositionSpan implements SpanData { export class HbsSpan implements SpanData { readonly kind = OffsetKind.HbsPosition; - #charPosSpan: CharPositionSpan | BROKEN | null = null; + _charPosSpan: CharPositionSpan | BROKEN | null = null; // the source location from Handlebars + AST Plugins -- could be wrong - #providedHbsLoc: SourceLocation | null; + _providedHbsLoc: SourceLocation | null; constructor( readonly source: Source, readonly hbsPositions: { start: HbsPosition; end: HbsPosition }, providedHbsLoc: SourceLocation | null = null ) { - this.#providedHbsLoc = providedHbsLoc; + this._providedHbsLoc = providedHbsLoc; } serialize(): SerializedConcreteSourceSpan { @@ -413,13 +413,13 @@ export class HbsSpan implements SpanData { } private updateProvided(pos: SourcePosition, edge: 'start' | 'end') { - if (this.#providedHbsLoc) { - this.#providedHbsLoc[edge] = pos; + if (this._providedHbsLoc) { + this._providedHbsLoc[edge] = pos; } // invalidate computed character offsets - this.#charPosSpan = null; - this.#providedHbsLoc = { + this._charPosSpan = null; + this._providedHbsLoc = { start: pos, end: pos, }; @@ -466,19 +466,19 @@ export class HbsSpan implements SpanData { } toCharPosSpan(): CharPositionSpan | null { - let charPosSpan = this.#charPosSpan; + let charPosSpan = this._charPosSpan; if (charPosSpan === null) { let start = this.hbsPositions.start.toCharPos(); let end = this.hbsPositions.end.toCharPos(); if (start && end) { - charPosSpan = this.#charPosSpan = new CharPositionSpan(this.source, { + charPosSpan = this._charPosSpan = new CharPositionSpan(this.source, { start, end, }); } else { - charPosSpan = this.#charPosSpan = BROKEN; + charPosSpan = this._charPosSpan = BROKEN; return null; } } diff --git a/packages/@glimmer/syntax/lib/source/span-list.ts b/packages/@glimmer/syntax/lib/source/span-list.ts index 31d66f9087..f65cff1d59 100644 --- a/packages/@glimmer/syntax/lib/source/span-list.ts +++ b/packages/@glimmer/syntax/lib/source/span-list.ts @@ -16,22 +16,22 @@ export class SpanList { return new SpanList(span.map(loc)).getRangeOffset(fallback); } - #span: SourceSpan[]; + _span: SourceSpan[]; constructor(span: SourceSpan[] = []) { - this.#span = span; + this._span = span; } add(offset: SourceSpan): void { - this.#span.push(offset); + this._span.push(offset); } getRangeOffset(fallback: SourceSpan): SourceSpan { - if (this.#span.length === 0) { + if (this._span.length === 0) { return fallback; } else { - let first = this.#span[0]; - let last = this.#span[this.#span.length - 1]; + let first = this._span[0]; + let last = this._span[this._span.length - 1]; return first.extend(last); } diff --git a/packages/@glimmer/syntax/lib/symbol-table.ts b/packages/@glimmer/syntax/lib/symbol-table.ts index a5fce6039f..a692d9770e 100644 --- a/packages/@glimmer/syntax/lib/symbol-table.ts +++ b/packages/@glimmer/syntax/lib/symbol-table.ts @@ -47,18 +47,18 @@ export class ProgramSymbolTable extends SymbolTable { private blocks = dict(); private usedTemplateLocals: string[] = []; - #hasEval = false; + _hasEval = false; getUsedTemplateLocals(): string[] { return this.usedTemplateLocals; } setHasEval(): void { - this.#hasEval = true; + this._hasEval = true; } get hasEval(): boolean { - return this.#hasEval; + return this._hasEval; } has(name: string): boolean { diff --git a/packages/@glimmer/syntax/lib/traversal/path.ts b/packages/@glimmer/syntax/lib/traversal/path.ts index 456a086314..2f498cef35 100644 --- a/packages/@glimmer/syntax/lib/traversal/path.ts +++ b/packages/@glimmer/syntax/lib/traversal/path.ts @@ -1,11 +1,9 @@ import * as ASTv1 from '../v1/api'; -import RootTransformScope, { TransformScope } from './scope'; export default class WalkerPath { node: N; parent: WalkerPath | null; parentKey: string | null; - scope: TransformScope; constructor( node: N, @@ -15,20 +13,6 @@ export default class WalkerPath { this.node = node; this.parent = parent; this.parentKey = parentKey; - this.scope = parent ? parent.scope.child(node) : new RootTransformScope(node); - - // Consume in scope values - if (node.type === 'PathExpression') { - this.scope.useLocal(node); - } - - if (node.type === 'ElementNode') { - this.scope.useLocal(node); - - (node as ASTv1.ElementNode).children.forEach((node: ASTv1.Statement) => - this.scope.useLocal(node) - ); - } } get parentNode(): ASTv1.Node | null { diff --git a/packages/@glimmer/syntax/lib/traversal/scope.ts b/packages/@glimmer/syntax/lib/traversal/scope.ts deleted file mode 100644 index 5c9add679b..0000000000 --- a/packages/@glimmer/syntax/lib/traversal/scope.ts +++ /dev/null @@ -1,138 +0,0 @@ -import * as ASTv1 from '../v1/api'; - -function getLocalName(node: ASTv1.Node): string | undefined { - switch (node.type) { - case 'ElementNode': - // unfortunately the ElementNode stores `tag` as a string - // if that changes in glimmer-vm this will need to be updated - return node.tag.split('.')[0]; - - case 'SubExpression': - case 'MustacheStatement': - case 'BlockStatement': - return getLocalName(node.path); - - case 'UndefinedLiteral': - case 'NullLiteral': - case 'BooleanLiteral': - case 'StringLiteral': - case 'NumberLiteral': - case 'TextNode': - case 'Template': - case 'Block': - case 'CommentStatement': - case 'MustacheCommentStatement': - case 'PartialStatement': - case 'ElementModifierStatement': - case 'AttrNode': - case 'ConcatStatement': - case 'Program': - case 'Hash': - case 'HashPair': - return undefined; - case 'PathExpression': - default: - return node.parts.length ? node.parts[0] : undefined; - } -} - -function getLocals(node: ASTv1.Node): string[] | undefined { - switch (node.type) { - case 'ElementNode': - case 'Program': - case 'Block': - case 'Template': - return node.blockParams; - - case 'BlockStatement': - return node.program.blockParams; - - default: - return undefined; - } -} - -export abstract class TransformScope { - hasPartial = false; - usedLocals: { [key: string]: boolean } = {}; - - constructor(protected locals: string[]) { - for (const local of locals) { - this.usedLocals[local] = false; - } - } - - child(node: ASTv1.Node): TransformScope { - let locals = getLocals(node); - - return locals ? new ChildTransformScope(locals, this) : this; - } - - usePartial(): void { - this.hasPartial = true; - } - - abstract isLocal(name: string): boolean; - abstract useLocal(node: ASTv1.Node): void; - abstract currentUnusedLocals(): string[] | false; -} - -export default class RootTransformScope extends TransformScope { - constructor(node: ASTv1.Node) { - let locals = getLocals(node) ?? []; - - super(locals); - } - - useLocal(node: ASTv1.Node): void { - let name = getLocalName(node); - - if (name && name in this.usedLocals) { - this.usedLocals[name] = true; - } - } - - isLocal(name: string): boolean { - return this.locals.indexOf(name) !== -1; - } - - currentUnusedLocals(): string[] | false { - if (!this.hasPartial && this.locals.length > 0) { - return this.locals.filter((local) => !this.usedLocals[local]); - } - - return false; - } -} - -class ChildTransformScope extends TransformScope { - constructor(locals: string[], private parent: TransformScope) { - super(locals); - } - - useLocal(node: ASTv1.Node): void { - let name = getLocalName(node); - - if (name && name in this.usedLocals) { - this.usedLocals[name] = true; - } else { - this.parent.useLocal(node); - } - } - - isLocal(name: string): boolean { - return this.locals.indexOf(name) !== -1 || this.parent.isLocal(name); - } - - currentUnusedLocals(): string[] | false { - if (!this.hasPartial && this.locals.length > 0) { - // We only care about the last local, because if it is used then it implies - // usage of the others (specifically when in a child block, |foo bar|) - if (!this.usedLocals[this.locals[this.locals.length - 1]]) { - return [this.locals[this.locals.length - 1]]; - } - } - - return false; - } -} diff --git a/packages/@glimmer/syntax/lib/v2-a/objects/node.ts b/packages/@glimmer/syntax/lib/v2-a/objects/node.ts index 6e54a51939..4b8524fd28 100644 --- a/packages/@glimmer/syntax/lib/v2-a/objects/node.ts +++ b/packages/@glimmer/syntax/lib/v2-a/objects/node.ts @@ -1,3 +1,5 @@ +import { assign } from '@glimmer/util'; + import { SourceSpan } from '../../source/span'; export interface BaseNodeFields { @@ -54,13 +56,13 @@ export function node( return { fields(): TypedNodeConstructor { return class { - readonly loc: SourceSpan; + // SAFETY: initialized via `assign` in the constructor. + declare readonly loc: SourceSpan; readonly type: T; constructor(fields: BaseNodeFields & Fields) { this.type = type; - this.loc = fields.loc; - copy(fields, (this as unknown) as ConstructingTypedNode); + assign(this, fields); } } as TypedNodeConstructor; }, @@ -69,12 +71,11 @@ export function node( return { fields(): NodeConstructor { return class { - readonly loc: SourceSpan; + // SAFETY: initialized via `assign` in the constructor. + declare readonly loc: SourceSpan; constructor(fields: BaseNodeFields & Fields) { - this.loc = fields.loc; - - copy(fields, (this as unknown) as ConstructingNode); + assign(this, fields); } } as NodeConstructor; }, @@ -82,10 +83,6 @@ export function node( } } -type ConstructingTypedNode = Fields & BaseNodeFields; - -type ConstructingNode = BaseNodeFields & Fields; - export interface NodeConstructor { new (fields: Fields): Readonly; } @@ -95,13 +92,3 @@ type TypedNode = { type: T } & Readonly; export interface TypedNodeConstructor { new (options: Fields): TypedNode; } - -function keys(object: O): (keyof O)[] { - return Object.keys(object) as (keyof O)[]; -} - -function copy(object1: O, object2: O) { - for (let key of keys(object1)) { - object2[key] = object1[key]; - } -} diff --git a/packages/@glimmer/syntax/test/traversal/scope-test.ts b/packages/@glimmer/syntax/test/traversal/scope-test.ts deleted file mode 100644 index 2540b15ce9..0000000000 --- a/packages/@glimmer/syntax/test/traversal/scope-test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { preprocess, traverse } from '../..'; - -const { module, test } = QUnit; - -module('[glimmer-syntax] Traversal - scope', () => { - test(`Scope is available in template root`, (assert) => { - let ast = preprocess(``, { strictMode: true, locals: ['x', 'y', 'z'] }); - - traverse(ast, { - Template: { - exit(_node, path) { - let unusedLocals = path.scope.currentUnusedLocals(); - - assert.deepEqual(unusedLocals, ['y'], 'locals marked as used/unused correctly'); - assert.ok(path.scope.isLocal('x'), 'x is a local'); - assert.ok(path.scope.isLocal('y'), 'y is a local'); - assert.ok(path.scope.isLocal('z'), 'z is a local'); - }, - }, - }); - }); - - test(`Scope is available in blocks`, (assert) => { - let ast = preprocess(``, { strictMode: true, locals: ['x', 'y'] }); - - traverse(ast, { - ElementNode: { - exit(_node, path) { - let unusedLocals = path.scope.currentUnusedLocals(); - - assert.deepEqual(unusedLocals, ['z'], 'locals marked as used/unused correctly'); - assert.ok(path.scope.isLocal('x'), 'x is a local'); - assert.ok(path.scope.isLocal('y'), 'y is a local'); - assert.ok(path.scope.isLocal('z'), 'z is a local'); - }, - }, - }); - }); - - test(`currentUnusedLocals returns false if all locals are used in the current block, string names otherwise`, (assert) => { - let ast = preprocess(`{{z}}`, { strictMode: true, locals: ['x', 'y'] }); - - traverse(ast, { - ElementNode: { - exit(_node, path) { - let unusedLocals = path.scope.currentUnusedLocals(); - - assert.deepEqual(unusedLocals, false, 'locals marked as used correctly'); - }, - }, - - Template: { - exit(_node, path) { - let unusedLocals = path.scope.currentUnusedLocals(); - - assert.deepEqual(unusedLocals, ['y'], 'locals marked as used correctly'); - }, - }, - }); - }); -});