From 8f797e637c6c79a126993d22a07a652d7a89f432 Mon Sep 17 00:00:00 2001 From: Noel Forte Date: Sat, 23 Nov 2024 17:26:25 -0500 Subject: [PATCH 01/13] declare correct type for `ESTree` nodes --- src/transformer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transformer.ts b/src/transformer.ts index 551e1ec..d855e9f 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -166,7 +166,7 @@ export function transformTemplateCode( } walker.walk(parsed, { - enter(node) { + enter(node: ESTree.Node) { switch (node.type) { // Track variable declarations case "VariableDeclaration": From 300918fba118824f49dfbe30e63070260eaf17f8 Mon Sep 17 00:00:00 2001 From: Noel Forte Date: Sat, 23 Nov 2024 17:51:12 -0500 Subject: [PATCH 02/13] temporary patch: dnt throws error for `jsr:@std/path@1.0.8`, reverting to `deno.land` URL instead --- deps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps.ts b/deps.ts index 5b86a25..8120f72 100644 --- a/deps.ts +++ b/deps.ts @@ -1,4 +1,4 @@ -export * as path from "jsr:@std/path@1.0.8"; +export * as path from "https://deno.land/std@0.224.0/path/mod.ts"; export * as html from "jsr:@std/html@1.0.3"; export * as astring from "jsr:@davidbonnet/astring@1.8.6"; From 167fd66dd458c567e730d3a426f662119cd93adf Mon Sep 17 00:00:00 2001 From: Noel Forte Date: Sat, 23 Nov 2024 21:54:41 -0500 Subject: [PATCH 03/13] move `ParseError` to transformer.ts --- src/environment.ts | 8 ++------ src/transformer.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/environment.ts b/src/environment.ts index bfa4c72..cb49ad8 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -1,7 +1,7 @@ import tokenize, { Token } from "./tokenizer.ts"; import type { Loader } from "./loader.ts"; -import { transformTemplateCode } from "./transformer.ts"; +import { transformTemplateCode, type ParseError} from "./transformer.ts"; export interface TemplateResult { content: string; @@ -387,8 +387,4 @@ function checkAsync(fn: () => unknown): boolean { return fn.constructor?.name === "AsyncFunction"; } -interface ParseError extends Error { - start: number; - end: number; - range: [number, number]; -} + diff --git a/src/transformer.ts b/src/transformer.ts index d855e9f..a1bd365 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -1,5 +1,15 @@ import { astring, ESTree, meriyah, walker } from "../deps.ts"; +// Declare types +export interface ParseError extends Error { + start: number; + end: number; + range: [number, number]; + loc: Record<'start' | 'end', Record<'line' | 'column', number>>; + description: string; + annotation?: string; +} + // List of identifiers that are in globalThis // but should be accessed as templateState.identifier const INCLUDE_GLOBAL = [ From 55c1c47f54b340508ffa813e0eafd0b39ba65a9f Mon Sep 17 00:00:00 2001 From: Noel Forte Date: Sat, 23 Nov 2024 21:56:20 -0500 Subject: [PATCH 04/13] refactor parse error handling: include substring of code that failed parsing --- src/environment.ts | 12 +++++++++--- src/transformer.ts | 20 +++++++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/environment.ts b/src/environment.ts index cb49ad8..f3c6bc8 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -139,8 +139,9 @@ export class Environment { try { code = transformTemplateCode(code, dataVarname); } catch (error) { - const end = (error as ParseError).start; - const lastPos = code.slice(0, end).lastIndexOf("__pos = "); + let { start: errorStart, message, annotation } = error as ParseError; + + const lastPos = code.slice(0, errorStart).lastIndexOf("__pos = "); const lastPosEnd = code.slice(lastPos).indexOf(";"); if (lastPos > -1 && lastPosEnd > lastPos) { @@ -148,11 +149,16 @@ export class Environment { code.slice(lastPos + 8, lastPos + lastPosEnd), 10, ); + + if (annotation) { + message += `\n\nAttempted to parse:\n\n${annotation}` + } + throw this.createError( path || "", source, pos, - new Error((error as ParseError).message), + new Error(message), ); } diff --git a/src/transformer.ts b/src/transformer.ts index a1bd365..3d8ec81 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -129,7 +129,25 @@ export function transformTemplateCode( return code; } - const parsed = meriyah.parseScript(code, { module: true }) as ESTree.Program; + let parsed; + try { + parsed = meriyah.parseScript(code, { module: true }) as ESTree.Program; + } catch (error) { + const { loc } = error as ParseError; + + const lines = code.split('\n'); + + lines.splice(loc.start.line, 0, `${' '.repeat(loc.start.column)}\x1b[31m^\x1b[0m`) + + const annotation = lines.slice(Math.min(0, loc.start.line - 2), Math.min(loc.end.line + 3, lines.length)) + .join('\n'); + + throw Object.assign(error as ParseError, { + message: 'Failed to parse template function', + annotation + }) + } + const tracker = new ScopeTracker(); const exclude = [ From 78a3b371e5fef7da225cc19dbbe78de5b1deb9a4 Mon Sep 17 00:00:00 2001 From: Noel Forte Date: Sun, 24 Nov 2024 01:04:01 -0500 Subject: [PATCH 05/13] clean up implementation of error annotation --- src/environment.ts | 12 +++--------- src/transformer.ts | 19 +++++++------------ 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/environment.ts b/src/environment.ts index f3c6bc8..2914df7 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -1,7 +1,7 @@ import tokenize, { Token } from "./tokenizer.ts"; import type { Loader } from "./loader.ts"; -import { transformTemplateCode, type ParseError} from "./transformer.ts"; +import { type ParseError, transformTemplateCode } from "./transformer.ts"; export interface TemplateResult { content: string; @@ -139,8 +139,8 @@ export class Environment { try { code = transformTemplateCode(code, dataVarname); } catch (error) { - let { start: errorStart, message, annotation } = error as ParseError; - + const { start: errorStart, message } = error as ParseError; + const lastPos = code.slice(0, errorStart).lastIndexOf("__pos = "); const lastPosEnd = code.slice(lastPos).indexOf(";"); @@ -150,10 +150,6 @@ export class Environment { 10, ); - if (annotation) { - message += `\n\nAttempted to parse:\n\n${annotation}` - } - throw this.createError( path || "", source, @@ -392,5 +388,3 @@ export function errorLine( function checkAsync(fn: () => unknown): boolean { return fn.constructor?.name === "AsyncFunction"; } - - diff --git a/src/transformer.ts b/src/transformer.ts index 3d8ec81..a770850 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -5,9 +5,8 @@ export interface ParseError extends Error { start: number; end: number; range: [number, number]; - loc: Record<'start' | 'end', Record<'line' | 'column', number>>; + loc: Record<"start" | "end", Record<"line" | "column", number>>; description: string; - annotation?: string; } // List of identifiers that are in globalThis @@ -133,19 +132,15 @@ export function transformTemplateCode( try { parsed = meriyah.parseScript(code, { module: true }) as ESTree.Program; } catch (error) { - const { loc } = error as ParseError; + const { message, loc: { start } } = error as ParseError; - const lines = code.split('\n'); + const annotation = code.split("\n")[start.line - 1] + "\n" + + " ".repeat(start.column) + "\x1b[31m^\x1b[0m"; - lines.splice(loc.start.line, 0, `${' '.repeat(loc.start.column)}\x1b[31m^\x1b[0m`) - - const annotation = lines.slice(Math.min(0, loc.start.line - 2), Math.min(loc.end.line + 3, lines.length)) - .join('\n'); - throw Object.assign(error as ParseError, { - message: 'Failed to parse template function', - annotation - }) + message: + `Failed to parse template function internally.