Skip to content

Commit

Permalink
add block param nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
patricklx committed Jan 26, 2024
1 parent 2ddbbc4 commit 30c6199
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type * as HBS from '../v1/handlebars-ast';
import { Parser } from '../parser';
import { NON_EXISTENT_LOCATION } from '../source/location';
import { generateSyntaxError } from '../syntax-error';
import { appendChild, isHBSLiteral, printLiteral } from '../utils';
import { appendChild, isHBSLiteral, parseProgramBlockParamsLocs, printLiteral } from '../utils';
import { PathExpressionImplV1 } from '../v1/legacy-interop';
import b from '../v1/parser-builders';

Expand Down Expand Up @@ -109,6 +109,7 @@ export abstract class HandlebarsNodeVisitors extends Parser {
inverseStrip: block.inverseStrip,
closeStrip: block.closeStrip,
});
parseProgramBlockParamsLocs(this.source, node);

const parentProgram = this.currentElement();

Expand Down
37 changes: 33 additions & 4 deletions packages/@glimmer/syntax/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Nullable } from '@glimmer/interfaces';
import { expect, unwrap } from '@glimmer/util';

import type * as src from './source/api';
import type * as ASTv1 from './v1/api';
import type * as HBS from './v1/handlebars-ast';

Expand All @@ -17,10 +18,34 @@ let ID_INVERSE_PATTERN = /[!"#%&'()*+./;<=>@[\\\]^`{|}~]/u;

export function parseElementBlockParams(element: ASTv1.ElementNode): void {
let params = parseBlockParams(element);
if (params) element.blockParams = params;
if (params) {
element.blockParamNodes = params;
element.blockParams = params.map((p) => p.value);
}
}

export function parseProgramBlockParamsLocs(code: src.Source, block: ASTv1.BlockStatement) {
const blockRange = [block.loc.getStart().offset!, block.loc.getEnd().offset!] as [number, number];
let part = code.slice(...blockRange);
let start = blockRange[0];
let idx = part.indexOf('|') + 1;
start += idx;
part = part.slice(idx, -1);
idx = part.indexOf('|');
part = part.slice(0, idx);
for (const param of block.program.blockParamNodes) {
const regex = new RegExp(`\\b${param.value}\\b`);
const match = regex.exec(part)!;
const range = [start + match.index, 0] as [number, number];
range[1] = range[0] + param.value.length;
param.loc = code.spanFor({
start: code.hbsPosFor(range[0])!,
end: code.hbsPosFor(range[1])!,
});
}
}

function parseBlockParams(element: ASTv1.ElementNode): Nullable<string[]> {
function parseBlockParams(element: ASTv1.ElementNode): Nullable<ASTv1.BlockParam[]> {
let l = element.attributes.length;
let attrNames = [];

Expand Down Expand Up @@ -54,7 +79,7 @@ function parseBlockParams(element: ASTv1.ElementNode): Nullable<string[]> {
);
}

let params = [];
let params: ASTv1.BlockParam[] = [];
for (let i = asIndex + 1; i < l; i++) {
let param = unwrap(attrNames[i]).replace(/\|/gu, '');
if (param !== '') {
Expand All @@ -64,7 +89,11 @@ function parseBlockParams(element: ASTv1.ElementNode): Nullable<string[]> {
element.loc
);
}
params.push(param);
params.push({
type: 'BlockParam',
value: param,
loc: element.attributes[i]!.loc,
});
}
}

Expand Down
13 changes: 13 additions & 0 deletions packages/@glimmer/syntax/lib/v1/nodes-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface Program extends CommonProgram {

export interface Block extends CommonProgram {
type: 'Block';
blockParamNodes: BlockParam[];
}

export type EntityEncodingState = 'transformed' | 'raw';
Expand Down Expand Up @@ -147,6 +148,7 @@ export interface ElementNode extends BaseNode {
selfClosing: boolean;
attributes: AttrNode[];
blockParams: string[];
blockParamNodes: BlockParam[];
modifiers: ElementModifierStatement[];
comments: MustacheCommentStatement[];
children: Statement[];
Expand Down Expand Up @@ -293,6 +295,16 @@ export interface HashPair extends BaseNode {
value: Expression;
}

/**
* a param inside the pipes of elements or mustache blocks,
* <Foo as |bar|>... bar is a BlockParam.
* {{#Foo as |bar|}}... bar is a BlockParam.
*/
export interface BlockParam extends BaseNode {
type: 'BlockParam';
value: string;
}

export interface StripFlags {
open: boolean;
close: boolean;
Expand Down Expand Up @@ -324,6 +336,7 @@ export type Nodes = SharedNodes & {
PathExpression: PathExpression;
Hash: Hash;
HashPair: HashPair;
BlockParam: BlockParam;
};

export type NodeType = keyof Nodes;
Expand Down
7 changes: 6 additions & 1 deletion packages/@glimmer/syntax/lib/v1/parser-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class Builders {
type: 'Block',
body: body,
blockParams: blockParams,
blockParamNodes: blockParams?.map(
(b) => ({ type: 'BlockParam', value: b }) as ASTv1.BlockParam
),
chained,
loc,
};
Expand Down Expand Up @@ -168,7 +171,9 @@ class Builders {
tag,
selfClosing: selfClosing,
attributes: attrs || [],
blockParams: blockParams || [],
blockParams: blockParams,
blockParamNodes:
blockParams.map((x) => ({ type: 'BlockParam', value: x }) as ASTv1.BlockParam) || [],
modifiers: modifiers || [],
comments: (comments as ASTv1.MustacheCommentStatement[]) || [],
children: children || [],
Expand Down
4 changes: 4 additions & 0 deletions packages/@glimmer/syntax/lib/v1/public-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ function buildElement(tag: TagDescriptor, options: BuildElementOptions = {}): AS
selfClosing: selfClosing,
attributes: attrs || [],
blockParams: blockParams || [],
blockParamNodes:
blockParams?.map((x) => ({ type: 'BlockParam', value: x }) as ASTv1.BlockParam) || [],
modifiers: modifiers || [],
comments: (comments as ASTv1.MustacheCommentStatement[]) || [],
children: children || [],
Expand Down Expand Up @@ -469,6 +471,8 @@ function buildBlockItself(
type: 'Block',
body: body || [],
blockParams: blockParams || [],
blockParamNodes:
blockParams?.map((b) => ({ type: 'BlockParam', value: b }) as ASTv1.BlockParam) || [],
chained,
loc: buildLoc(loc || null),
};
Expand Down
1 change: 1 addition & 0 deletions packages/@glimmer/syntax/lib/v1/visitor-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const visitorKeys = {

Hash: ['pairs'],
HashPair: ['value'],
BlockParam: [],

// v2 new nodes
NamedBlock: ['attributes', 'modifiers', 'children', 'comments'],
Expand Down
24 changes: 24 additions & 0 deletions packages/@glimmer/syntax/test/loc-node-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,30 @@ data-barf="herpy"
}
});

test('element block params', () => {
let ast = parse(`<Foo as |ab cd efg|></Foo>`);

let [Foo] = ast.body;
if (assertNodeType(Foo, 'ElementNode')) {
let [ab, cd, efg] = guardArray({ blockParamNodes: Foo.blockParamNodes }, { min: 3 });
locEqual(ab, 1, 8, 1, 12);
locEqual(cd, 1, 12, 1, 15);
locEqual(efg, 1, 15, 1, 19);
}
});

test('mustache block params', () => {
let ast = parse(`{{#Foo as |ab cd efg|}}{{/Foo}}`);

let [Foo] = ast.body;
if (assertNodeType(Foo, 'BlockStatement')) {
let [ab, cd, efg] = guardArray({ blockParamNodes: Foo.program.blockParamNodes }, { min: 3 });
locEqual(ab, 1, 11, 1, 13);
locEqual(cd, 1, 14, 1, 16);
locEqual(efg, 1, 17, 1, 20);
}
});

test('element dynamic attribute', () => {
let ast = parse(`<img src={{blah}}>`);

Expand Down
2 changes: 2 additions & 0 deletions packages/@glimmer/syntax/test/parser-node-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,8 @@ export function element(tag: TagDescriptor, ...options: ElementParts[]): ASTv1.E
selfClosing: selfClosing,
attributes: attrs || [],
blockParams: blockParams || [],
blockParamNodes:
blockParams?.map((b) => ({ type: 'BlockParam', value: b }) as ASTv1.BlockParam) || [],
modifiers: modifiers || [],
comments: (comments as ASTv1.MustacheCommentStatement[]) || [],
children: children || [],
Expand Down

0 comments on commit 30c6199

Please sign in to comment.