From d125d57b3a314d994149f84c18747c85bdf1f0b1 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Mon, 22 Mar 2021 00:26:59 -0700 Subject: [PATCH] add typescript support for expressions --- examples/snowpack/astro/pages/news.hmx | 2 +- package-lock.json | 6 +-- package.json | 1 + src/codegen/index.ts | 12 ++--- src/compiler/parse/acorn.ts | 65 +++++++++++++++++++------- src/compiler/parse/read/expression.ts | 1 - 6 files changed, 58 insertions(+), 29 deletions(-) diff --git a/examples/snowpack/astro/pages/news.hmx b/examples/snowpack/astro/pages/news.hmx index 51b7f616e..3cdf8b06e 100644 --- a/examples/snowpack/astro/pages/news.hmx +++ b/examples/snowpack/astro/pages/news.hmx @@ -49,7 +49,7 @@ working on! - {context.news.reverse().map((item) => + {context.news.reverse().map((item: any) => )} diff --git a/package-lock.json b/package-lock.json index cd01bf26f..cfed544f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,9 +67,9 @@ } }, "@babel/parser": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.10.tgz", - "integrity": "sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ==" + "version": "7.13.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.11.tgz", + "integrity": "sha512-PhuoqeHoO9fc4ffMEVk4qb/w/s2iOSWohvbHxLtxui0eBg3Lg5gN1U8wp1V1u61hOWkPQJJyJzGH6Y+grwkq8Q==" }, "@babel/types": { "version": "7.13.0", diff --git a/package.json b/package.json index de8682f1f..8eb21ba01 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "yargs-parser": "^20.2.7" }, "devDependencies": { + "@babel/parser": "^7.13.11", "@types/github-slugger": "^1.3.0", "@types/sass": "^1.16.0", "@types/yargs-parser": "^20.2.0", diff --git a/src/codegen/index.ts b/src/codegen/index.ts index 4cba263df..725546f51 100644 --- a/src/codegen/index.ts +++ b/src/codegen/index.ts @@ -146,8 +146,8 @@ function getComponentWrapper(_name: string, { type, url }: { type: string; url: throw new Error('Unknown Component Type: ' + name); } -function compileScriptSafe(raw: string, loader: 'jsx' | 'tsx'): string { - let compiledCode = compileExpressionSafe(raw, loader); +function compileScriptSafe(raw: string): string { + let compiledCode = compileExpressionSafe(raw); // esbuild treeshakes unused imports. In our case these are components, so let's keep them. const imports = eslexer .parse(raw)[0] @@ -161,9 +161,9 @@ function compileScriptSafe(raw: string, loader: 'jsx' | 'tsx'): string { return compiledCode; } -function compileExpressionSafe(raw: string, loader: 'jsx' | 'tsx'): string { +function compileExpressionSafe(raw: string): string { let { code } = transformSync(raw, { - loader, + loader: 'tsx', jsxFactory: 'h', jsxFragment: 'Fragment', charset: 'utf8', @@ -175,7 +175,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro await eslexer.init; // Compile scripts as TypeScript, always - const script = compileScriptSafe(ast.module ? ast.module.content : '', 'tsx'); + const script = compileScriptSafe(ast.module ? ast.module.content : ''); // Todo: Validate that `h` and `Fragment` aren't defined in the script const [scriptImports] = eslexer.parse(script, 'optional-sourcename'); @@ -198,7 +198,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro enter(node: TemplateNode) { switch (node.type) { case 'MustacheTag': - let code = compileExpressionSafe(node.expression, 'jsx'); + let code = compileExpressionSafe(node.expression); let matches: RegExpExecArray[] = []; let match: RegExpExecArray | null | undefined; diff --git a/src/compiler/parse/acorn.ts b/src/compiler/parse/acorn.ts index 966a13792..b88454151 100644 --- a/src/compiler/parse/acorn.ts +++ b/src/compiler/parse/acorn.ts @@ -1,20 +1,49 @@ -import { Node } from 'acorn'; -import acorn from 'acorn'; -// @ts-ignore -import jsx from 'acorn-jsx'; +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()); -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 = (source: string): Node => - acorn.parse(source, { - sourceType: 'module', - ecmaVersion: 2020, - locations: true, - }); - -export const parse_expression_at = (source: string, index: number): Node => - acornJsx.parseExpressionAt(source, index, { - sourceType: 'module', - ecmaVersion: 2020, - locations: true, - }); +export const parse_expression_at = (source: string, index: number) => { + // TODO: Clean up after acorn -> @babel/parser move + try { + parseExpression(source.slice(index), { + sourceType: 'module', + plugins: ['jsx', 'typescript'], + }); + throw new Error('Parse error.'); // Expected to fail. + } catch (err) { + if (!err.pos) { + throw err; + } + try { + const result = parseExpression(source.slice(index, index + err.pos), { + sourceType: 'module', + plugins: ['jsx', 'typescript'], + }); + result.start = index; + result.end = index + err.pos; + return result; + } catch (err2) { + if (err2.pos) { + err2.pos = index + err2.pos; + } + throw err2; + } + } +}; +// acornJsx.parseExpressionAt(source, index, { +// sourceType: 'module', +// ecmaVersion: 2020, +// locations: true, +// }); diff --git a/src/compiler/parse/read/expression.ts b/src/compiler/parse/read/expression.ts index 56e79265d..827a29721 100644 --- a/src/compiler/parse/read/expression.ts +++ b/src/compiler/parse/read/expression.ts @@ -8,7 +8,6 @@ import { whitespace } from '../../utils/patterns.js'; export default function read_expression(parser: Parser): string { try { const node = parse_expression_at(parser.template, parser.index); - let num_parens = 0; for (let i = parser.index; i < node.start; i += 1) {