diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/@types/transformer.ts (renamed from src/@types/optimizer.ts) | 4 | ||||
-rw-r--r-- | src/build/bundle.ts | 4 | ||||
-rw-r--r-- | src/compiler/codegen.ts | 67 | ||||
-rw-r--r-- | src/compiler/index.ts | 8 | ||||
-rw-r--r-- | src/compiler/transform/doctype.ts (renamed from src/compiler/optimize/doctype.ts) | 6 | ||||
-rw-r--r-- | src/compiler/transform/index.ts (renamed from src/compiler/optimize/index.ts) | 36 | ||||
-rw-r--r-- | src/compiler/transform/module-scripts.ts (renamed from src/compiler/optimize/module-scripts.ts) | 6 | ||||
-rw-r--r-- | src/compiler/transform/postcss-scoped-styles/index.ts (renamed from src/compiler/optimize/postcss-scoped-styles/index.ts) | 0 | ||||
-rw-r--r-- | src/compiler/transform/prism.ts (renamed from src/compiler/optimize/prism.ts) | 11 | ||||
-rw-r--r-- | src/compiler/transform/styles.ts (renamed from src/compiler/optimize/styles.ts) | 10 | ||||
-rw-r--r-- | src/parser/interfaces.ts | 12 | ||||
-rw-r--r-- | src/parser/parse/acorn.ts | 42 | ||||
-rw-r--r-- | src/parser/parse/read/context.ts | 2 | ||||
-rw-r--r-- | src/parser/parse/read/expression.ts | 265 | ||||
-rw-r--r-- | src/parser/parse/read/script.ts | 4 | ||||
-rw-r--r-- | src/parser/parse/state/mustache.ts | 4 | ||||
-rw-r--r-- | src/parser/parse/state/tag.ts | 4 |
17 files changed, 333 insertions, 152 deletions
diff --git a/src/@types/optimizer.ts b/src/@types/transformer.ts index b6459ab51..7f4167558 100644 --- a/src/@types/optimizer.ts +++ b/src/@types/transformer.ts @@ -8,7 +8,7 @@ export interface NodeVisitor { leave?: VisitorFn; } -export interface Optimizer { +export interface Transformer { visitors?: { html?: Record<string, NodeVisitor>; css?: Record<string, NodeVisitor>; @@ -16,7 +16,7 @@ export interface Optimizer { finalize: () => Promise<void>; } -export interface OptimizeOptions { +export interface TransformOptions { compileOptions: CompileOptions; filename: string; fileID: string; diff --git a/src/build/bundle.ts b/src/build/bundle.ts index ba1b8f2c2..b55828c2e 100644 --- a/src/build/bundle.ts +++ b/src/build/bundle.ts @@ -7,7 +7,7 @@ import type { LogOptions } from '../logger'; import esbuild from 'esbuild'; import { promises as fsPromises } from 'fs'; import { parse } from '../parser/index.js'; -import { optimize } from '../compiler/optimize/index.js'; +import { transform } from '../compiler/transform/index.js'; import { getAttrValue } from '../ast.js'; import { walk } from 'estree-walker'; import babelParser from '@babel/parser'; @@ -86,7 +86,7 @@ export async function collectDynamicImports(filename: URL, { astroConfig, loggin return imports; } - await optimize(ast, { + await transform(ast, { filename: filename.pathname, fileID: '', compileOptions: { diff --git a/src/compiler/codegen.ts b/src/compiler/codegen.ts index 59cc2c702..8bcf3f49d 100644 --- a/src/compiler/codegen.ts +++ b/src/compiler/codegen.ts @@ -78,9 +78,10 @@ function getAttributes(attrs: Attribute[]): Record<string, string> { continue; } switch (val.type) { - case 'MustacheTag': - result[attr.name] = '(' + val.content + ')'; + case 'MustacheTag': { + result[attr.name] = '(' + val.expression.codeStart + ')'; continue; + } case 'Text': result[attr.name] = JSON.stringify(getTextFromAttribute(val)); continue; @@ -93,13 +94,21 @@ function getAttributes(attrs: Attribute[]): Record<string, string> { /** Get value from a TemplateNode Attribute (text attributes only!) */ function getTextFromAttribute(attr: any): string { - if (attr.raw !== undefined) { - return attr.raw; - } - if (attr.data !== undefined) { - return attr.data; + switch(attr.type) { + case 'Text': { + if (attr.raw !== undefined) { + return attr.raw; + } + if (attr.data !== undefined) { + return attr.data; + } + break; + } + case 'MustacheTag': { + return attr.expression.codeStart; + } } - throw new Error('UNKNOWN attr'); + throw new Error(`Unknown attribute type ${attr.type}`); } /** Convert TemplateNode attributes to string */ @@ -238,7 +247,7 @@ function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo } } -/** Evaluate mustache expression (safely) */ +/** Evaluate expression (safely) */ function compileExpressionSafe(raw: string): string { let { code } = transformSync(raw, { loader: 'tsx', @@ -468,33 +477,19 @@ function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOption walk(enterNode, { enter(node: TemplateNode) { switch (node.type) { - case 'MustacheTag': - let code = compileExpressionSafe(node.content); - - let matches: RegExpExecArray[] = []; - let match: RegExpExecArray | null | undefined; - const H_COMPONENT_SCANNER = /h\(['"]?([A-Z].*?)['"]?,/gs; - const regex = new RegExp(H_COMPONENT_SCANNER); - while ((match = regex.exec(code))) { - matches.push(match); - } - for (const astroComponent of matches.reverse()) { - const name = astroComponent[1]; - const [componentName, componentKind] = name.split(':'); - if (!components[componentName]) { - throw new Error(`Unknown Component: ${componentName}`); - } - const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], { astroConfig, dynamicImports, filename }); - if (wrapperImport) { - importExportStatements.add(wrapperImport); - } - if (wrapper !== name) { - code = code.slice(0, astroComponent.index + 2) + wrapper + code.slice(astroComponent.index + astroComponent[0].length - 1); - } + case 'Expression': { + let child = ''; + if(node.children!.length) { + child = compileHtml(node.children![0], state, compileOptions); } - outSource += `,(${code.trim().replace(/\;$/, '')})`; + let raw = node.codeStart + child + node.codeEnd; + // TODO Do we need to compile this now, or should we compile the entire module at the end? + let code = compileExpressionSafe(raw).trim().replace(/\;$/, ''); + outSource += `,(${code})`; this.skip(); - return; + break; + } + case 'MustacheTag': case 'Comment': return; case 'Fragment': @@ -557,11 +552,11 @@ function compileHtml(enterNode: TemplateNode, state: CodegenState, compileOption leave(node, parent, prop, index) { switch (node.type) { case 'Text': - case 'MustacheTag': case 'Attribute': case 'Comment': - return; case 'Fragment': + case 'Expression': + case 'MustacheTag': return; case 'Slot': case 'Head': diff --git a/src/compiler/index.ts b/src/compiler/index.ts index d33527b9b..db50abec8 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -11,7 +11,7 @@ import { parse } from '../parser/index.js'; import { createMarkdownHeadersCollector } from './markdown/micromark-collect-headers.js'; import { encodeMarkdown } from './markdown/micromark-encode.js'; import { encodeAstroMdx } from './markdown/micromark-mdx-astro.js'; -import { optimize } from './optimize/index.js'; +import { transform } from './transform/index.js'; import { codegen } from './codegen.js'; /** Return Astro internal import URL */ @@ -29,7 +29,7 @@ interface ConvertAstroOptions { * .astro -> .jsx * Core function processing .astro files. Initiates all 3 phases of compilation: * 1. Parse - * 2. Optimize + * 2. Transform * 3. Codegen */ async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): Promise<TransformResult> { @@ -40,8 +40,8 @@ async function convertAstroToJsx(template: string, opts: ConvertAstroOptions): P filename, }); - // 2. Optimize the AST - await optimize(ast, opts); + // 2. Transform the AST + await transform(ast, opts); // 3. Turn AST into JSX return await codegen(ast, opts); diff --git a/src/compiler/optimize/doctype.ts b/src/compiler/transform/doctype.ts index 176880c08..d19b01f81 100644 --- a/src/compiler/optimize/doctype.ts +++ b/src/compiler/transform/doctype.ts @@ -1,7 +1,7 @@ -import { Optimizer } from '../../@types/optimizer'; +import { Transformer } from '../../@types/transformer'; -/** Optimize <!doctype> tg */ -export default function (_opts: { filename: string; fileID: string }): Optimizer { +/** Transform <!doctype> tg */ +export default function (_opts: { filename: string; fileID: string }): Transformer { let hasDoctype = false; return { diff --git a/src/compiler/optimize/index.ts b/src/compiler/transform/index.ts index fcbd6e950..6a81b92b0 100644 --- a/src/compiler/optimize/index.ts +++ b/src/compiler/transform/index.ts @@ -1,13 +1,13 @@ import type { Ast, TemplateNode } from '../../parser/interfaces'; -import type { NodeVisitor, OptimizeOptions, Optimizer, VisitorFn } from '../../@types/optimizer'; +import type { NodeVisitor, TransformOptions, Transformer, VisitorFn } from '../../@types/transformer'; import { walk } from 'estree-walker'; -// Optimizers -import optimizeStyles from './styles.js'; -import optimizeDoctype from './doctype.js'; -import optimizeModuleScripts from './module-scripts.js'; -import optimizeCodeBlocks from './prism.js'; +// Transformers +import transformStyles from './styles.js'; +import transformDoctype from './doctype.js'; +import transformModuleScripts from './module-scripts.js'; +import transformCodeBlocks from './prism.js'; interface VisitorCollection { enter: Map<string, VisitorFn[]>; @@ -24,23 +24,23 @@ function addVisitor(visitor: NodeVisitor, collection: VisitorCollection, nodeNam collection[event].set(nodeName, visitors); } -/** Compile visitor actions from optimizer */ -function collectVisitors(optimizer: Optimizer, htmlVisitors: VisitorCollection, cssVisitors: VisitorCollection, finalizers: Array<() => Promise<void>>) { - if (optimizer.visitors) { - if (optimizer.visitors.html) { - for (const [nodeName, visitor] of Object.entries(optimizer.visitors.html)) { +/** Compile visitor actions from transformer */ +function collectVisitors(transformer: Transformer, htmlVisitors: VisitorCollection, cssVisitors: VisitorCollection, finalizers: Array<() => Promise<void>>) { + if (transformer.visitors) { + if (transformer.visitors.html) { + for (const [nodeName, visitor] of Object.entries(transformer.visitors.html)) { addVisitor(visitor, htmlVisitors, nodeName, 'enter'); addVisitor(visitor, htmlVisitors, nodeName, 'leave'); } } - if (optimizer.visitors.css) { - for (const [nodeName, visitor] of Object.entries(optimizer.visitors.css)) { + if (transformer.visitors.css) { + for (const [nodeName, visitor] of Object.entries(transformer.visitors.css)) { addVisitor(visitor, cssVisitors, nodeName, 'enter'); addVisitor(visitor, cssVisitors, nodeName, 'leave'); } } } - finalizers.push(optimizer.finalize); + finalizers.push(transformer.finalize); } /** Utility for formatting visitors */ @@ -74,17 +74,17 @@ function walkAstWithVisitors(tmpl: TemplateNode, collection: VisitorCollection) } /** - * Optimize + * Transform * Step 2/3 in Astro SSR. - * Optimize is the point at which we mutate the AST before sending off to + * Transform is the point at which we mutate the AST before sending off to * Codegen, and then to Snowpack. In some ways, it‘s a preprocessor. */ -export async function optimize(ast: Ast, opts: OptimizeOptions) { +export async function transform(ast: Ast, opts: TransformOptions) { const htmlVisitors = createVisitorCollection(); const cssVisitors = createVisitorCollection(); const finalizers: Array<() => Promise<void>> = []; - const optimizers = [optimizeStyles(opts), optimizeDoctype(opts), optimizeModuleScripts(opts), optimizeCodeBlocks(ast.module)]; + const optimizers = [transformStyles(opts), transformDoctype(opts), transformModuleScripts(opts), transformCodeBlocks(ast.module)]; for (const optimizer of optimizers) { collectVisitors(optimizer, htmlVisitors, cssVisitors, finalizers); diff --git a/src/compiler/optimize/module-scripts.ts b/src/compiler/transform/module-scripts.ts index 9d4949215..aff1ec4f6 100644 --- a/src/compiler/optimize/module-scripts.ts +++ b/src/compiler/transform/module-scripts.ts @@ -1,11 +1,11 @@ -import type { Optimizer } from '../../@types/optimizer'; +import type { Transformer } from '../../@types/transformer'; import type { CompileOptions } from '../../@types/compiler'; import path from 'path'; import { getAttrValue, setAttrValue } from '../../ast.js'; -/** Optimize <script type="module"> */ -export default function ({ compileOptions, filename }: { compileOptions: CompileOptions; filename: string; fileID: string }): Optimizer { +/** Transform <script type="module"> */ +export default function ({ compileOptions, filename }: { compileOptions: CompileOptions; filename: string; fileID: string }): Transformer { const { astroConfig } = compileOptions; const { astroRoot } = astroConfig; const fileUrl = new URL(`file://${filename}`); diff --git a/src/compiler/optimize/postcss-scoped-styles/index.ts b/src/compiler/transform/postcss-scoped-styles/index.ts index 23350869c..23350869c 100644 --- a/src/compiler/optimize/postcss-scoped-styles/index.ts +++ b/src/compiler/transform/postcss-scoped-styles/index.ts diff --git a/src/compiler/optimize/prism.ts b/src/compiler/transform/prism.ts index 5c5364796..628dcce7e 100644 --- a/src/compiler/optimize/prism.ts +++ b/src/compiler/transform/prism.ts @@ -1,4 +1,4 @@ -import type { Optimizer } from '../../@types/optimizer'; +import type { Transformer } from '../../@types/transformer'; import type { Script } from '../../parser/interfaces'; import { getAttrValue } from '../../ast.js'; @@ -11,7 +11,7 @@ function escape(code: string) { }); } -export default function (module: Script): Optimizer { +export default function (module: Script): Transformer { let usesPrism = false; return { @@ -61,7 +61,12 @@ export default function (module: Script): Optimizer { value: [ { type: 'MustacheTag', - content: '`' + escape(code) + '`', + expression: { + type: 'Expression', + codeStart: '`' + escape(code) + '`', + codeEnd: '', + children: [] + } }, ], }, diff --git a/src/compiler/optimize/styles.ts b/src/compiler/transform/styles.ts index 807d869c9..d8e3196a1 100644 --- a/src/compiler/optimize/styles.ts +++ b/src/compiler/transform/styles.ts @@ -7,7 +7,7 @@ import postcssKeyframes from 'postcss-icss-keyframes'; import findUp from 'find-up'; import sass from 'sass'; import type { RuntimeMode } from '../../@types/astro'; -import type { OptimizeOptions, Optimizer } from '../../@types/optimizer'; +import type { TransformOptions, Transformer } from '../../@types/transformer'; import type { TemplateNode } from '../../parser/interfaces'; import { debug } from '../../logger.js'; import astroScopedStyles, { NEVER_SCOPED_TAGS } from './postcss-scoped-styles/index.js'; @@ -152,8 +152,8 @@ async function transformStyle(code: string, { type, filename, scopedClass, mode return { css, type: styleType }; } -/** Optimize <style> tags */ -export default function optimizeStyles({ compileOptions, filename, fileID }: OptimizeOptions): Optimizer { +/** Transform <style> tags */ +export default function transformStyles({ compileOptions, filename, fileID }: TransformOptions): Transformer { const styleNodes: TemplateNode[] = []; // <style> tags to be updated const styleTransformPromises: Promise<StyleTransformResult>[] = []; // async style transform results to be finished in finalize(); const scopedClass = `astro-${hashFromFilename(fileID)}`; // this *should* generate same hash from fileID every time @@ -218,9 +218,9 @@ export default function optimizeStyles({ compileOptions, filename, fileID }: Opt } } else if (attr.value[k].type === 'MustacheTag' && attr.value[k]) { // don‘t add same scopedClass twice (this check is a little more basic, but should suffice) - if (!attr.value[k].content.includes(`' ${scopedClass}'`)) { + if (!attr.value[k].expression.codeStart.includes(`' ${scopedClass}'`)) { // MustacheTag - attr.value[k].content = `(${attr.value[k].content}) + ' ${scopedClass}'`; + attr.value[k].expression.codeStart = `(${attr.value[k].expression.codeStart}) + ' ${scopedClass}'`; } } } diff --git a/src/parser/interfaces.ts b/src/parser/interfaces.ts index 71b1812a3..4a4d43f71 100644 --- a/src/parser/interfaces.ts +++ b/src/parser/interfaces.ts @@ -1,7 +1,6 @@ -import type { Expression, Program } from '@babel/types'; import type { SourceMap } from 'magic-string'; -interface BaseNode { +export interface BaseNode { start: number; end: number; type: string; @@ -50,6 +49,15 @@ export type Directive = BaseDirective | Transition; export type TemplateNode = Text | MustacheTag | BaseNode | Directive | Transition; +export interface Expression { + type: 'Expression'; + start: number; + end: number; + codeStart: string; + codeEnd: string; + children: BaseNode[]; +} + export interface Parser { readonly template: string; readonly filename?: string; diff --git a/src/parser/parse/acorn.ts b/src/parser/parse/acorn.ts deleted file mode 100644 index c70756d79..000000000 --- a/src/parser/parse/acorn.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Node } from 'acorn'; -import { parseExpression } from '@babel/parser'; -// import acorn from 'acorn'; -// // @ts-ignore -// import jsx from 'acorn-jsx'; -// const acornJsx = acorn.Parser.extend(jsx()); - -export const parse = (source: string): Node => { - throw new Error('No longer used.'); - // acorn.parse(source, { - // sourceType: 'module', - // ecmaVersion: 2020, - // locations: true, - // }); -}; - -export const parse_expression_at = (source: string, index: number): number => { - // TODO: Clean up after acorn -> @babel/parser move - try { - // First, try to parse the expression. Unlike acorn, @babel/parser isn't relaxed - // enough to just stop after the first expression, so we almost always expect a - // parser error here instead. This is expected, so handle it. - parseExpression(source.slice(index), { - sourceType: 'module', - plugins: ['jsx', 'typescript'], - }); - throw new Error('Parse error.'); // Expected to fail. - } catch (err) { - if (err.message.startsWith('Unexpected token') && source[index + err.pos] === '}') { - return index + err.pos; - } - if (err.pos) { - err.pos = index + err.pos; - } - throw err; - } -}; -// acornJsx.parseExpressionAt(source, index, { -// sourceType: 'module', -// ecmaVersion: 2020, -// locations: true, -// }); diff --git a/src/parser/parse/read/context.ts b/src/parser/parse/read/context.ts index 4d8f12060..565c66d18 100644 --- a/src/parser/parse/read/context.ts +++ b/src/parser/parse/read/context.ts @@ -4,7 +4,7 @@ import { Parser } from '../index.js'; import { isIdentifierStart } from 'acorn'; import full_char_code_at from '../../utils/full_char_code_at.js'; import { is_bracket_open, is_bracket_close, is_bracket_pair, get_bracket_close } from '../utils/bracket.js'; -import { parse_expression_at } from '../acorn.js'; +import { parse_expression_at } from './expression.js'; import { Pattern } from 'estree'; export default function read_context(parser: Parser): Pattern & { start: number; end: number } { diff --git a/src/parser/parse/read/expression.ts b/src/parser/parse/read/expression.ts index 6ea0ebc56..bdd6c4b81 100644 --- a/src/parser/parse/read/expression.ts +++ b/src/parser/parse/read/expression.ts @@ -1,39 +1,254 @@ -import { parse_expression_at } from '../acorn.js'; + +import type { BaseNode, Expression } from '../../interfaces'; import { Parser } from '../index.js'; -import { whitespace } from '../../utils/patterns.js'; +import parseAstro from '../index.js'; -// @ts-ignore -export default function read_expression(parser: Parser): string { - try { - const start = parser.index; - let index = parse_expression_at(parser.template, parser.index); - let num_parens = 0; +interface ParseState { + source: string; + start: number; + index: number; + curlyCount: number; + bracketCount: number; + root: Expression; +} + +function peek_char(state: ParseState) { + return state.source[state.index]; +} + +function peek_nonwhitespace(state: ParseState) { + let index = state.index; + do { + let char = state.source[index]; + if(!/\s/.test(char)) { + return char; + } + index++; + } while(index < state.source.length); +} + +function next_char(state: ParseState) { + return state.source[state.index++]; +} + +function in_bounds(state: ParseState) { + return state.index < state.source.length; +} + +function consume_string(state: ParseState, stringChar: string) { + let inEscape; + do { + const char = next_char(state); + + if(inEscape) { + inEscape = false; + } else if(char === '\\') { + inEscape = true; + } else if(char === stringChar) { + break; + } + } while(in_bounds(state)); +} + +function consume_multiline_comment(state: ParseState) { + do { + const char = next_char(state); + + if(char === '*' && peek_char(state) === '/') { + break; + } + } while(in_bounds(state)); +} - for (let i = parser.index; i < start; i += 1) { - if (parser.template[i] === '(') num_parens += 1; +function consume_line_comment(state: ParseState) { + do { + const char = next_char(state); + if(char === '\n') { + break; } + } while(in_bounds(state)); +} + +const voidElements = new Set(['area', 'base', 'br', 'col', 'command', 'embed', + 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', + 'track', 'wbr']); - while (num_parens > 0) { - const char = parser.template[index]; +function consume_tag(state: ParseState) { + const start = state.index - 1; + let tagName = ''; + let inTag = false; + let inStart = true; + let selfClosed = false; + let inClose = false; - if (char === ')') { - num_parens -= 1; - } else if (!whitespace.test(char)) { - parser.error( - { - code: 'unexpected-token', - message: 'Expected )', - }, - index - ); + let bracketIndex = 1; + do { + const char = next_char(state); + + switch(char) { + case '\'': + case '"': { + consume_string(state, char); + break; + } + case '<': { + inTag = false; + tagName = ''; + + if(peek_nonwhitespace(state) === '/') { + inClose = true; + bracketIndex--; + } else { + inStart = true; + bracketIndex++; + } + break; } + case '>': { + // An arrow function, probably + if(!inStart && !inClose) { + break; + } + + bracketIndex--; - index += 1; + const addExpectedBrackets = + // Void elements don't need a closing + !voidElements.has(tagName.toLowerCase()) && + // Self-closing don't need a closing + !selfClosed && + // If we're in a start tag, we expect to find 2 more brackets + !inClose; + + if(addExpectedBrackets) { + bracketIndex += 2; + } + + inTag = false; + selfClosed = false; + inStart = false; + inClose = false; + break; + } + case ' ': { + inTag = true; + break; + } + case '/': { + if(inStart) { + selfClosed = true; + } + break; + } + default: { + if(!inTag) { + tagName += char; + } + break; + } + } + + // Unclosed tags + if(state.curlyCount <= 0) { + break; + } + + if(bracketIndex === 0) { + break; } + } while(in_bounds(state)); + + const source = state.source.substring(start, state.index); - parser.index = index; - return parser.template.substring(start, index); + const ast = parseAstro(source); + const fragment = ast.html; + + return fragment; +} + +function consume_expression(source: string, start: number): Expression { + const expr: Expression = { + type: 'Expression', + start, + end: Number.NaN, + codeStart: '', + codeEnd: '', + children: [] + }; + + let codeEndStart: number = 0; + const state: ParseState = { + source, start, index: start, + curlyCount: 1, + bracketCount: 0, + root: expr + }; + + do { + const char = next_char(state); + + switch(char) { + case '{': { + state.curlyCount++; + break; + } + case '}': { + state.curlyCount--; + break; + } + case '<': { + expr.codeStart = source.substring(start, state.index - 1); + const tag = consume_tag(state); + expr.children.push(tag); + codeEndStart = state.index; + break; + } + case '\'': + case '"': + case '`': { + consume_string(state, char); + break; + } + case '/': { + switch(peek_char(state)) { + case '/': { + consume_line_comment(state); + break; + } + case '*': { + consume_multiline_comment(state); + break; + } + } + } + } + } while(in_bounds(state) && state.curlyCount > 0); + + expr.end = state.index - 1; + + if(codeEndStart) { + expr.codeEnd = source.substring(codeEndStart, expr.end); + } else { + expr.codeStart = source.substring(start, expr.end); + } + + return expr; +} + +export const parse_expression_at = (source: string, index: number): Expression => { + const expression = consume_expression(source, index); + + return expression; +}; + +// @ts-ignore +export default function read_expression(parser: Parser) { + try { + debugger; + const expression = parse_expression_at(parser.template, parser.index); + parser.index = expression.end; + return expression; } catch (err) { parser.acorn_error(err); } diff --git a/src/parser/parse/read/script.ts b/src/parser/parse/read/script.ts index 7afbfb08f..4f1d31b44 100644 --- a/src/parser/parse/read/script.ts +++ b/src/parser/parse/read/script.ts @@ -1,9 +1,9 @@ // @ts-nocheck -import * as acorn from '../acorn'; +import type { Node } from 'estree'; import { Parser } from '../index.js'; import { Script } from '../../interfaces.js'; -import { Node, Program } from 'estree'; + const script_closing_tag = '</script>'; diff --git a/src/parser/parse/state/mustache.ts b/src/parser/parse/state/mustache.ts index 8ffac4f85..79372d8d9 100644 --- a/src/parser/parse/state/mustache.ts +++ b/src/parser/parse/state/mustache.ts @@ -397,7 +397,7 @@ export default function mustache(parser: Parser) { // }); throw new Error('@debug not yet supported'); } else { - const content = read_expression(parser); + const expression = read_expression(parser); parser.allow_whitespace(); parser.eat('}', true); @@ -407,7 +407,7 @@ export default function mustache(parser: Parser) { start, end: parser.index, type: 'MustacheTag', - content, + expression, }); } } diff --git a/src/parser/parse/state/tag.ts b/src/parser/parse/state/tag.ts index bacaffdef..a8b919a49 100644 --- a/src/parser/parse/state/tag.ts +++ b/src/parser/parse/state/tag.ts @@ -549,7 +549,7 @@ function read_sequence(parser: Parser, done: () => boolean): TemplateNode[] { flush(); parser.allow_whitespace(); - const content = read_expression(parser); + const expression = read_expression(parser); parser.allow_whitespace(); parser.eat('}', true); @@ -557,7 +557,7 @@ function read_sequence(parser: Parser, done: () => boolean): TemplateNode[] { start: index, end: parser.index, type: 'MustacheTag', - content, + expression, }); current_chunk = { |