From eb9832bc7dbb05ab1b047b18f09383e375dbbbef Mon Sep 17 00:00:00 2001 From: jp-knj Date: Tue, 29 Jul 2025 01:04:24 +0900 Subject: [PATCH 1/7] feat: implement vite plugin skeleton for .astro transformation (#31) - Create @minimal-astro/vite-plugin package - Implement vitePluginAstro() with file filtering - Add transformAstro() for basic AST to HTML transformation - Configure plugin with enforce: 'pre' and basic HMR support - Add comprehensive tests for plugin functionality - Integrate plugin into blog example via vite.config.ts --- examples/blog/package.json | 1 + examples/blog/src/pages/test.astro | 17 ++ examples/blog/vite.config.ts | 6 + .../src/html-builder/builder.d.ts.map | 1 + packages/compiler/src/html-builder/builder.js | 96 ++++++ .../compiler/src/html-builder/builder.js.map | 1 + .../compiler/src/html-builder/escape.d.ts.map | 1 + packages/compiler/src/html-builder/escape.js | 40 +++ .../compiler/src/html-builder/escape.js.map | 1 + .../compiler/src/html-builder/index.d.ts.map | 1 + packages/compiler/src/html-builder/index.js | 12 + .../compiler/src/html-builder/index.js.map | 1 + .../src/html-builder/serializer.d.ts.map | 1 + .../compiler/src/html-builder/serializer.js | 104 +++++++ .../src/html-builder/serializer.js.map | 1 + packages/compiler/src/index.d.ts.map | 1 + packages/compiler/src/index.js | 4 + packages/compiler/src/index.js.map | 1 + packages/compiler/src/parser/ast.d.ts.map | 1 + packages/compiler/src/parser/ast.js | 45 +++ packages/compiler/src/parser/ast.js.map | 1 + packages/compiler/src/parser/index.d.ts.map | 1 + packages/compiler/src/parser/index.js | 11 + packages/compiler/src/parser/index.js.map | 1 + packages/compiler/src/parser/parser.d.ts.map | 1 + packages/compiler/src/parser/parser.js | 282 ++++++++++++++++++ packages/compiler/src/parser/parser.js.map | 1 + packages/compiler/src/parser/utils.d.ts.map | 1 + packages/compiler/src/parser/utils.js | 181 +++++++++++ packages/compiler/src/parser/utils.js.map | 1 + .../compiler/src/tokenizer/index.d.ts.map | 1 + packages/compiler/src/tokenizer/index.js | 2 + packages/compiler/src/tokenizer/index.js.map | 1 + .../compiler/src/tokenizer/tokenizer.d.ts.map | 1 + packages/compiler/src/tokenizer/tokenizer.js | 186 ++++++++++++ .../compiler/src/tokenizer/tokenizer.js.map | 1 + .../compiler/src/tokenizer/types.d.ts.map | 1 + packages/compiler/src/tokenizer/types.js | 2 + packages/compiler/src/tokenizer/types.js.map | 1 + packages/compiler/tsconfig.json | 1 + packages/vite-plugin/README.md | 52 ++++ packages/vite-plugin/package.json | 36 +++ packages/vite-plugin/src/index.ts | 82 +++++ packages/vite-plugin/src/transform.ts | 45 +++ packages/vite-plugin/test/plugin.test.ts | 55 ++++ packages/vite-plugin/test/transform.test.ts | 77 +++++ packages/vite-plugin/tsconfig.build.json | 19 ++ packages/vite-plugin/tsconfig.json | 12 + packages/vite-plugin/vitest.config.ts | 8 + pnpm-lock.yaml | 53 ++++ 50 files changed, 1453 insertions(+) create mode 100644 examples/blog/src/pages/test.astro create mode 100644 examples/blog/vite.config.ts create mode 100644 packages/compiler/src/html-builder/builder.d.ts.map create mode 100644 packages/compiler/src/html-builder/builder.js create mode 100644 packages/compiler/src/html-builder/builder.js.map create mode 100644 packages/compiler/src/html-builder/escape.d.ts.map create mode 100644 packages/compiler/src/html-builder/escape.js create mode 100644 packages/compiler/src/html-builder/escape.js.map create mode 100644 packages/compiler/src/html-builder/index.d.ts.map create mode 100644 packages/compiler/src/html-builder/index.js create mode 100644 packages/compiler/src/html-builder/index.js.map create mode 100644 packages/compiler/src/html-builder/serializer.d.ts.map create mode 100644 packages/compiler/src/html-builder/serializer.js create mode 100644 packages/compiler/src/html-builder/serializer.js.map create mode 100644 packages/compiler/src/index.d.ts.map create mode 100644 packages/compiler/src/index.js create mode 100644 packages/compiler/src/index.js.map create mode 100644 packages/compiler/src/parser/ast.d.ts.map create mode 100644 packages/compiler/src/parser/ast.js create mode 100644 packages/compiler/src/parser/ast.js.map create mode 100644 packages/compiler/src/parser/index.d.ts.map create mode 100644 packages/compiler/src/parser/index.js create mode 100644 packages/compiler/src/parser/index.js.map create mode 100644 packages/compiler/src/parser/parser.d.ts.map create mode 100644 packages/compiler/src/parser/parser.js create mode 100644 packages/compiler/src/parser/parser.js.map create mode 100644 packages/compiler/src/parser/utils.d.ts.map create mode 100644 packages/compiler/src/parser/utils.js create mode 100644 packages/compiler/src/parser/utils.js.map create mode 100644 packages/compiler/src/tokenizer/index.d.ts.map create mode 100644 packages/compiler/src/tokenizer/index.js create mode 100644 packages/compiler/src/tokenizer/index.js.map create mode 100644 packages/compiler/src/tokenizer/tokenizer.d.ts.map create mode 100644 packages/compiler/src/tokenizer/tokenizer.js create mode 100644 packages/compiler/src/tokenizer/tokenizer.js.map create mode 100644 packages/compiler/src/tokenizer/types.d.ts.map create mode 100644 packages/compiler/src/tokenizer/types.js create mode 100644 packages/compiler/src/tokenizer/types.js.map create mode 100644 packages/vite-plugin/README.md create mode 100644 packages/vite-plugin/package.json create mode 100644 packages/vite-plugin/src/index.ts create mode 100644 packages/vite-plugin/src/transform.ts create mode 100644 packages/vite-plugin/test/plugin.test.ts create mode 100644 packages/vite-plugin/test/transform.test.ts create mode 100644 packages/vite-plugin/tsconfig.build.json create mode 100644 packages/vite-plugin/tsconfig.json create mode 100644 packages/vite-plugin/vitest.config.ts diff --git a/examples/blog/package.json b/examples/blog/package.json index 2695a27..ece0e4f 100644 --- a/examples/blog/package.json +++ b/examples/blog/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "devDependencies": { + "@minimal-astro/vite-plugin": "workspace:*", "vite": "^5.4.11" } } diff --git a/examples/blog/src/pages/test.astro b/examples/blog/src/pages/test.astro new file mode 100644 index 0000000..07a8f70 --- /dev/null +++ b/examples/blog/src/pages/test.astro @@ -0,0 +1,17 @@ +--- +const _greeting = 'Hello from Astro!' +const _items = ['Item 1', 'Item 2', 'Item 3'] +--- + + + + Test Astro Page + + +

{greeting}

+

This is a test page to verify the Vite plugin is working.

+ + + \ No newline at end of file diff --git a/examples/blog/vite.config.ts b/examples/blog/vite.config.ts new file mode 100644 index 0000000..82945b3 --- /dev/null +++ b/examples/blog/vite.config.ts @@ -0,0 +1,6 @@ +import { vitePluginAstro } from '@minimal-astro/vite-plugin' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [vitePluginAstro()], +}) diff --git a/packages/compiler/src/html-builder/builder.d.ts.map b/packages/compiler/src/html-builder/builder.d.ts.map new file mode 100644 index 0000000..cc15f8b --- /dev/null +++ b/packages/compiler/src/html-builder/builder.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,kBAAkB,CAAA;AA0E3D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAM/C"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/builder.js b/packages/compiler/src/html-builder/builder.js new file mode 100644 index 0000000..1998924 --- /dev/null +++ b/packages/compiler/src/html-builder/builder.js @@ -0,0 +1,96 @@ +/** + * AST to HTML builder + * + * This module provides the main entry point for converting + * an Astro AST into an HTML string. + * + * @module builder + */ +import { serializeElement, serializeText } from './serializer.js' +/** + * Set of HTML void elements that are self-closing + */ +const VOID_ELEMENTS = new Set([ + 'img', + 'br', + 'hr', + 'input', + 'area', + 'base', + 'col', + 'embed', + 'link', + 'meta', + 'source', + 'track', + 'wbr', +]) +/** + * Checks if an element is a void element + */ +function isVoidElement(tagName) { + return VOID_ELEMENTS.has(tagName.toLowerCase()) +} +/** + * Builds an HTML string from an AST node + * + * Recursively processes AST nodes and converts them to HTML. + * Handles different node types appropriately: + * - Text nodes are escaped for safe HTML rendering + * - Element nodes are serialized with attributes and children + * - Container nodes (Program, Template) concatenate their children + * - Other nodes (Frontmatter, Expression) are skipped or handled specially + * + * @param node - The AST node to convert + * @returns HTML string representation + */ +function buildNode(node) { + switch (node.type) { + case 'Text': + return serializeText(node) + case 'Element': + // Check if it's a void element and update selfClosing accordingly + if (isVoidElement(node.name)) { + return serializeElement({ ...node, selfClosing: true }) + } + return serializeElement(node) + case 'Expression': + // Expressions in the template should be rendered as-is for now + // In a full implementation, these would be evaluated + return `{${node.value}}` + case 'Program': + case 'Template': + // Container nodes: concatenate all children + return node.children.map(child => buildNode(child)).join('') + case 'Frontmatter': + // Frontmatter is not rendered in the HTML output + return '' + default: + // Unknown node types are ignored + return '' + } +} +/** + * Builds an HTML string from an Astro AST + * + * This is the main entry point for converting a parsed Astro AST + * into HTML. It processes the entire AST tree and returns the + * resulting HTML string. + * + * @param ast - The Astro AST to convert + * @returns Complete HTML string + * + * @example + * ```typescript + * const ast = parse('

Hello World

') + * const html = buildHTML(ast) + * console.log(html) // '

Hello World

' + * ``` + */ +export function buildHTML(ast) { + if (!ast || !ast.children || ast.children.length === 0) { + return '' + } + return ast.children.map(child => buildNode(child)).join('') +} +//# sourceMappingURL=builder.js.map diff --git a/packages/compiler/src/html-builder/builder.js.map b/packages/compiler/src/html-builder/builder.js.map new file mode 100644 index 0000000..fd07272 --- /dev/null +++ b/packages/compiler/src/html-builder/builder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"builder.js","sourceRoot":"","sources":["builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEjE;;GAEG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,MAAM;IACN,MAAM;IACN,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,OAAO;IACP,KAAK;CACN,CAAC,CAAA;AAEF;;GAEG;AACH,SAAS,aAAa,CAAC,OAAe;IACpC,OAAO,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;AACjD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,SAAS,CAAC,IAAe;IAChC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;QAE5B,KAAK,SAAS;YACZ,kEAAkE;YAClE,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,OAAO,gBAAgB,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;YACzD,CAAC;YACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAE/B,KAAK,YAAY;YACf,+DAA+D;YAC/D,qDAAqD;YACrD,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,CAAA;QAE1B,KAAK,SAAS,CAAC;QACf,KAAK,UAAU;YACb,4CAA4C;YAC5C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAE9D,KAAK,aAAa;YAChB,iDAAiD;YACjD,OAAO,EAAE,CAAA;QAEX;YACE,iCAAiC;YACjC,OAAO,EAAE,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CAAC,GAAa;IACrC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,OAAO,EAAE,CAAA;IACX,CAAC;IAED,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC7D,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/escape.d.ts.map b/packages/compiler/src/html-builder/escape.d.ts.map new file mode 100644 index 0000000..8c96729 --- /dev/null +++ b/packages/compiler/src/html-builder/escape.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["escape.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/escape.js b/packages/compiler/src/html-builder/escape.js new file mode 100644 index 0000000..8142ea4 --- /dev/null +++ b/packages/compiler/src/html-builder/escape.js @@ -0,0 +1,40 @@ +/** + * HTML escape utilities for safe text rendering + * + * This module provides functions to escape special HTML characters + * to prevent XSS attacks and ensure proper rendering of text content. + * + * @module escape + */ +const ESCAPE_MAP = { + '<': '<', + '>': '>', + '&': '&', + '"': '"', + "'": ''', +} +const ESCAPE_REGEX = /[<>&"']/g +/** + * Escapes special HTML characters in text + * + * Converts characters that have special meaning in HTML (<, >, &, ", ') + * to their corresponding HTML entities to prevent XSS and ensure + * proper rendering. + * + * @param text - The text to escape + * @returns The escaped text safe for HTML rendering + * + * @example + * ```typescript + * escapeHtml('') + * // Returns: '<script>alert("XSS")</script>' + * + * escapeHtml('Tom & Jerry') + * // Returns: 'Tom & Jerry' + * ``` + */ +export function escapeHtml(text) { + if (!text) return text + return text.replace(ESCAPE_REGEX, char => ESCAPE_MAP[char]) +} +//# sourceMappingURL=escape.js.map diff --git a/packages/compiler/src/html-builder/escape.js.map b/packages/compiler/src/html-builder/escape.js.map new file mode 100644 index 0000000..67912c4 --- /dev/null +++ b/packages/compiler/src/html-builder/escape.js.map @@ -0,0 +1 @@ +{"version":3,"file":"escape.js","sourceRoot":"","sources":["escape.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,UAAU,GAA2B;IACzC,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,QAAQ;CACd,CAAA;AAED,MAAM,YAAY,GAAG,UAAU,CAAA;AAE/B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;AAC7D,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/index.d.ts.map b/packages/compiler/src/html-builder/index.d.ts.map new file mode 100644 index 0000000..cc284f7 --- /dev/null +++ b/packages/compiler/src/html-builder/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/index.js b/packages/compiler/src/html-builder/index.js new file mode 100644 index 0000000..4b91f2f --- /dev/null +++ b/packages/compiler/src/html-builder/index.js @@ -0,0 +1,12 @@ +/** + * HTML Builder module exports + * + * This module provides utilities for converting AST nodes to HTML strings + * with proper escaping and formatting. + * + * @module html-builder + */ +export { escapeHtml } from './escape.js' +export { serializeText, serializeElement } from './serializer.js' +export { buildHTML } from './builder.js' +//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/html-builder/index.js.map b/packages/compiler/src/html-builder/index.js.map new file mode 100644 index 0000000..2dcffd2 --- /dev/null +++ b/packages/compiler/src/html-builder/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/serializer.d.ts.map b/packages/compiler/src/html-builder/serializer.d.ts.map new file mode 100644 index 0000000..f0a75ca --- /dev/null +++ b/packages/compiler/src/html-builder/serializer.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAa,WAAW,EAAkB,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGxF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEpD;AAwBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAW1D"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/serializer.js b/packages/compiler/src/html-builder/serializer.js new file mode 100644 index 0000000..7f736bc --- /dev/null +++ b/packages/compiler/src/html-builder/serializer.js @@ -0,0 +1,104 @@ +/** + * AST to HTML serialization utilities + * + * This module provides functions to convert AST nodes into HTML strings, + * handling proper escaping and formatting for safe rendering. + * + * @module serializer + */ +import { escapeHtml } from './escape.js' +/** + * Serializes a text node to HTML + * + * Converts a TextNode to an HTML-safe string by escaping + * special characters to prevent XSS attacks. + * + * @param node - The text node to serialize + * @returns HTML-escaped text content + * + * @example + * ```typescript + * serializeText({ type: 'Text', value: 'Hello ' }) + * // Returns: 'Hello <world>' + * ``` + */ +export function serializeText(node) { + return escapeHtml(node.value) +} +/** + * Serializes attributes to HTML attribute string + * + * @param attributes - Array of element attributes + * @returns Formatted attribute string + */ +function serializeAttributes(attributes) { + if (!attributes.length) return '' + const attrStrings = attributes.map(attr => { + // For expression values, we render them as-is (without escaping) + // since they represent dynamic JavaScript expressions + const value = typeof attr.value === 'string' ? escapeHtml(attr.value) : `{${attr.value.value}}` + return `${attr.name}="${value}"` + }) + return ` ${attrStrings.join(' ')}` +} +/** + * Serializes an element node to HTML + * + * Converts an ElementNode to an HTML string, including: + * - Opening and closing tags + * - Attributes with proper escaping + * - Recursively serialized child nodes + * - Self-closing tag handling + * + * @param node - The element node to serialize + * @returns Complete HTML element string + * + * @example + * ```typescript + * serializeElement({ + * type: 'Element', + * name: 'div', + * attributes: [{ name: 'class', value: 'container' }], + * children: [{ type: 'Text', value: 'Hello' }], + * selfClosing: false + * }) + * // Returns: '
Hello
' + * ``` + */ +export function serializeElement(node) { + const { name, attributes, children, selfClosing } = node + const attrString = serializeAttributes(attributes) + if (selfClosing) { + return `<${name}${attrString} />` + } + const childrenHtml = children.map(child => serializeNode(child)).join('') + return `<${name}${attrString}>${childrenHtml}` +} +/** + * Serializes any AST node to HTML + * + * This is a dispatcher function that routes different node types + * to their appropriate serialization functions. + * + * @param node - The AST node to serialize + * @returns HTML string representation of the node + */ +function serializeNode(node) { + switch (node.type) { + case 'Text': + return serializeText(node) + case 'Element': + return serializeElement(node) + case 'Expression': + // Expressions in the template are rendered as-is + return `{${node.value}}` + case 'Template': + // Template nodes are containers, serialize their children + return node.children.map(child => serializeNode(child)).join('') + default: + // Skip nodes that shouldn't be serialized to HTML + // (e.g., Frontmatter, Program) + return '' + } +} +//# sourceMappingURL=serializer.js.map diff --git a/packages/compiler/src/html-builder/serializer.js.map b/packages/compiler/src/html-builder/serializer.js.map new file mode 100644 index 0000000..a9e3bc7 --- /dev/null +++ b/packages/compiler/src/html-builder/serializer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"serializer.js","sourceRoot":"","sources":["serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC/B,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAC1B,UAAmE;IAEnE,IAAI,CAAC,UAAU,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IAEjC,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QACxC,iEAAiE;QACjE,sDAAsD;QACtD,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAA;QAE/F,OAAO,GAAG,IAAI,CAAC,IAAI,KAAK,KAAK,GAAG,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;AACpC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IACxD,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAA;IAElD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,IAAI,IAAI,GAAG,UAAU,KAAK,CAAA;IACnC,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEzE,OAAO,IAAI,IAAI,GAAG,UAAU,IAAI,YAAY,KAAK,IAAI,GAAG,CAAA;AAC1D,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,IAAe;IACpC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;QAC5B,KAAK,SAAS;YACZ,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC/B,KAAK,YAAY;YACf,iDAAiD;YACjD,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,CAAA;QAC1B,KAAK,UAAU;YACb,0DAA0D;YAC1D,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAClE;YACE,kDAAkD;YAClD,+BAA+B;YAC/B,OAAO,EAAE,CAAA;IACb,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/index.d.ts.map b/packages/compiler/src/index.d.ts.map new file mode 100644 index 0000000..4685376 --- /dev/null +++ b/packages/compiler/src/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAEhG,YAAY,EACV,QAAQ,EACR,SAAS,EACT,eAAe,EACf,WAAW,EACX,QAAQ,EACR,cAAc,GACf,MAAM,mBAAmB,CAAA;AAE1B,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/index.js b/packages/compiler/src/index.js new file mode 100644 index 0000000..defb600 --- /dev/null +++ b/packages/compiler/src/index.js @@ -0,0 +1,4 @@ +export { parse } from './parser/index.js' +export { tokenize } from './tokenizer/index.js' +export { escapeHtml, serializeText, serializeElement, buildHTML } from './html-builder/index.js' +//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/index.js.map b/packages/compiler/src/index.js.map new file mode 100644 index 0000000..dce9b8f --- /dev/null +++ b/packages/compiler/src/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/ast.d.ts.map b/packages/compiler/src/parser/ast.d.ts.map new file mode 100644 index 0000000..5b36a53 --- /dev/null +++ b/packages/compiler/src/parser/ast.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["ast.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,qCAAqC;IACrC,QAAQ,EAAE,SAAS,EAAE,CAAA;CACtB;AAED;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,eAAe,GACf,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,cAAc,CAAA;AAElB;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,IAAI,EAAE,aAAa,CAAA;IACnB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,IAAI,EAAE,UAAU,CAAA;IAChB,sCAAsC;IACtC,QAAQ,EAAE,SAAS,EAAE,CAAA;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAA;IACZ,kCAAkC;IAClC,UAAU,EAAE,KAAK,CAAC;QAChB,qBAAqB;QACrB,IAAI,EAAE,MAAM,CAAA;QACZ,mEAAmE;QACnE,KAAK,EAAE,MAAM,GAAG,cAAc,CAAA;KAC/B,CAAC,CAAA;IACF,6CAA6C;IAC7C,QAAQ,EAAE,SAAS,EAAE,CAAA;IACrB,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc;IAC7B,2BAA2B;IAC3B,IAAI,EAAE,YAAY,CAAA;IAClB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,QAAQ,CAE5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,eAAe,CAE1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,YAAY,CAEpE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,WAAW,CAElE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,QAAQ,CAE5D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,cAAc,CAExE"} \ No newline at end of file diff --git a/packages/compiler/src/parser/ast.js b/packages/compiler/src/parser/ast.js new file mode 100644 index 0000000..d6e14ef --- /dev/null +++ b/packages/compiler/src/parser/ast.js @@ -0,0 +1,45 @@ +/** + * AST (Abstract Syntax Tree) type definitions for Astro templates + * + * These types represent the structure of parsed .astro files, + * converting raw tokens into a hierarchical tree structure. + * + * @module ast + */ +/** + * Type guard to check if a node is an AstroAST (Program) node + */ +export function isAstroAST(node) { + return node.type === 'Program' +} +/** + * Type guard to check if a node is a FrontmatterNode + */ +export function isFrontmatterNode(node) { + return node.type === 'Frontmatter' +} +/** + * Type guard to check if a node is a TemplateNode + */ +export function isTemplateNode(node) { + return node.type === 'Template' +} +/** + * Type guard to check if a node is an ElementNode + */ +export function isElementNode(node) { + return node.type === 'Element' +} +/** + * Type guard to check if a node is a TextNode + */ +export function isTextNode(node) { + return node.type === 'Text' +} +/** + * Type guard to check if a node is an ExpressionNode + */ +export function isExpressionNode(node) { + return node.type === 'Expression' +} +//# sourceMappingURL=ast.js.map diff --git a/packages/compiler/src/parser/ast.js.map b/packages/compiler/src/parser/ast.js.map new file mode 100644 index 0000000..ba9c874 --- /dev/null +++ b/packages/compiler/src/parser/ast.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ast.js","sourceRoot":"","sources":["ast.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA4IH;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAA;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAe;IAC/C,OAAO,IAAI,CAAC,IAAI,KAAK,aAAa,CAAA;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAAe;IAC5C,OAAO,IAAI,CAAC,IAAI,KAAK,UAAU,CAAA;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAA;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM,CAAA;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAe;IAC9C,OAAO,IAAI,CAAC,IAAI,KAAK,YAAY,CAAA;AACnC,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/parser/index.d.ts.map b/packages/compiler/src/parser/index.d.ts.map new file mode 100644 index 0000000..26f99e1 --- /dev/null +++ b/packages/compiler/src/parser/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAChD,YAAY,EACV,QAAQ,EACR,SAAS,EACT,eAAe,EACf,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,cAAc,GACf,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,UAAU,EACV,gBAAgB,GACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/index.js b/packages/compiler/src/parser/index.js new file mode 100644 index 0000000..3013408 --- /dev/null +++ b/packages/compiler/src/parser/index.js @@ -0,0 +1,11 @@ +export { parse } from './parser.js' +export { walk, walkAsync, is } from './utils.js' +export { + isAstroAST, + isFrontmatterNode, + isTemplateNode, + isElementNode, + isTextNode, + isExpressionNode, +} from './ast.js' +//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/parser/index.js.map b/packages/compiler/src/parser/index.js.map new file mode 100644 index 0000000..0386734 --- /dev/null +++ b/packages/compiler/src/parser/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAUhD,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,UAAU,EACV,gBAAgB,GACjB,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/parser.d.ts.map b/packages/compiler/src/parser/parser.d.ts.map new file mode 100644 index 0000000..01f36ed --- /dev/null +++ b/packages/compiler/src/parser/parser.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAA0C,MAAM,UAAU,CAAA;AAUhF;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAS9C"} \ No newline at end of file diff --git a/packages/compiler/src/parser/parser.js b/packages/compiler/src/parser/parser.js new file mode 100644 index 0000000..044a1e5 --- /dev/null +++ b/packages/compiler/src/parser/parser.js @@ -0,0 +1,282 @@ +import { tokenize } from '../tokenizer/index.js' +/** + * Parse Astro source code into an Abstract Syntax Tree (AST) + * + * @param source - The Astro source code to parse + * @returns The root AST node (Program) + * + * @example + * ```typescript + * const ast = parse('
Hello {name}
') + * console.log(ast) // { type: 'Program', children: [...] } + * ``` + */ +export function parse(source) { + const tokens = tokenize(source) + const initialState = { tokens, current: 0 } + const { children } = parseProgram(initialState) + return { + type: 'Program', + children, + } +} +/** + * Parse the entire program by collecting all top-level nodes + * + * @param initialState - The initial parser state + * @returns An object containing the parsed children and final state + */ +function parseProgram(initialState) { + const children = [] + let state = initialState + while (!isAtEnd(state)) { + const { node, state: nextState } = parseNode(state) + if (node) { + children.push(node) + } + state = nextState + } + return { children, state } +} +/** + * Parse a single node based on the current token + * + * @param state - The current parser state + * @returns An object containing the parsed node (or null) and new state + */ +function parseNode(state) { + const token = peek(state) + if (token.type === 'FRONTMATTER_START') { + return parseFrontmatter(state) + } + if (token.type === 'HTML_TAG_OPEN') { + return parseElement(state) + } + if (token.type === 'HTML_TAG_CLOSE' && peek(state, 1)?.type === 'HTML_TAG_NAME') { + // Skip closing tags, they're handled in parseElement + const s1 = advance(state) // + return { node: null, state: s3.state } + } + if (token.type === 'EXPRESSION_START') { + return parseExpression(state) + } + if (token.type === 'TEXT') { + return parseText(state) + } + if (token.type === 'EOF') { + return { node: null, state } + } + // Skip unknown tokens + return { node: null, state: advance(state).state } +} +/** + * Parse frontmatter block (JavaScript/TypeScript code between --- markers) + * + * @param state - The current parser state + * @returns An object containing the parsed frontmatter node and new state + */ +function parseFrontmatter(state) { + const { state: s1 } = consume(state, 'FRONTMATTER_START') + let value = '' + let currentState = s1 + while (peek(currentState).type === 'FRONTMATTER_CONTENT') { + const result = advance(currentState) + value += result.token.value + currentState = result.state + } + const { state: finalState } = consume(currentState, 'FRONTMATTER_END') + return { + node: { + type: 'Frontmatter', + value: value.trim(), + }, + state: finalState, + } +} +/** + * Parse HTML/JSX element including attributes and children + * + * @param state - The current parser state + * @returns An object containing the parsed element node and new state + */ +function parseElement(state) { + const { state: s1 } = consume(state, 'HTML_TAG_OPEN') + const { token: nameToken, state: s2 } = consume(s1, 'HTML_TAG_NAME') + const name = nameToken.value + const attributes = [] + let currentState = s2 + // Parse attributes + while ( + peek(currentState).type !== 'HTML_TAG_CLOSE' && + peek(currentState).type !== 'HTML_TAG_SELF_CLOSE' && + !isAtEnd(currentState) + ) { + if (peek(currentState).type === 'HTML_ATTRIBUTE_NAME') { + const attrResult = advance(currentState) + const attrName = attrResult.token.value + currentState = attrResult.state + let attrValue = '' + // Skip whitespace and equals + while (peek(currentState).type === 'TEXT' && peek(currentState).value.trim() === '') { + currentState = advance(currentState).state + } + if (peek(currentState).value === '=') { + currentState = advance(currentState).state // Skip = + // Skip whitespace + while (peek(currentState).type === 'TEXT' && peek(currentState).value.trim() === '') { + currentState = advance(currentState).state + } + if (peek(currentState).type === 'HTML_ATTRIBUTE_VALUE') { + const valueResult = advance(currentState) + attrValue = valueResult.token.value + currentState = valueResult.state + } else if (peek(currentState).type === 'EXPRESSION_START') { + const exprResult = parseExpression(currentState) + attrValue = exprResult.node + currentState = exprResult.state + } + } + attributes.push({ name: attrName, value: attrValue }) + } else { + currentState = advance(currentState).state // Skip unknown tokens in tag + } + } + const selfClosing = peek(currentState).type === 'HTML_TAG_SELF_CLOSE' + currentState = advance(currentState).state // Consume > or /> + const children = [] + if (!selfClosing) { + // Parse children until closing tag + while (!isAtEnd(currentState)) { + if ( + peek(currentState).type === 'HTML_TAG_CLOSE' && + peek(currentState, 1)?.type === 'HTML_TAG_NAME' && + peek(currentState, 1)?.value === name + ) { + break + } + const childResult = parseNode(currentState) + if (childResult.node) { + children.push(childResult.node) + } + currentState = childResult.state + } + } + return { + node: { + type: 'Element', + name, + attributes, + children, + selfClosing, + }, + state: currentState, + } +} +/** + * Parse JavaScript expression within curly braces { } + * + * @param state - The current parser state + * @returns An object containing the parsed expression node and new state + */ +function parseExpression(state) { + const { state: s1 } = consume(state, 'EXPRESSION_START') + let value = '' + let currentState = s1 + while (peek(currentState).type === 'EXPRESSION_CONTENT') { + const result = advance(currentState) + value += result.token.value + currentState = result.state + } + const { state: finalState } = consume(currentState, 'EXPRESSION_END') + return { + node: { + type: 'Expression', + value: value.trim(), + }, + state: finalState, + } +} +/** + * Parse text content between elements + * + * @param state - The current parser state + * @returns An object containing the parsed text node (or null if empty) and new state + */ +function parseText(state) { + let value = '' + let currentState = state + while (peek(currentState).type === 'TEXT' && !isAtEnd(currentState)) { + const result = advance(currentState) + value += result.token.value + currentState = result.state + } + // Only return text node if it has non-whitespace content + const trimmed = value.trim() + if (!trimmed) { + return { node: null, state: currentState } + } + return { + node: { + type: 'Text', + value, + }, + state: currentState, + } +} +// Immutable state helpers +/** + * Look at a token without consuming it + * + * @param state - The current parser state + * @param offset - How many tokens ahead to look (default: 0) + * @returns The token at the specified position + */ +function peek(state, offset = 0) { + const index = state.current + offset + if (index >= state.tokens.length) { + return state.tokens[state.tokens.length - 1] + } + return state.tokens[index] +} +/** + * Consume the current token and advance to the next one + * + * @param state - The current parser state + * @returns An object containing the consumed token and new state + */ +function advance(state) { + if (isAtEnd(state)) { + return { token: state.tokens[state.current], state } + } + return { + token: state.tokens[state.current], + state: { ...state, current: state.current + 1 }, + } +} +/** + * Consume a token of a specific type or throw an error + * + * @param state - The current parser state + * @param type - The expected token type + * @returns An object containing the consumed token and new state + * @throws Error if the current token doesn't match the expected type + */ +function consume(state, type) { + const token = peek(state) + if (token.type !== type) { + throw new Error(`Expected ${type} but got ${token.type}`) + } + return advance(state) +} +/** + * Check if we've reached the end of the token stream + * + * @param state - The current parser state + * @returns True if at the end of the token stream + */ +function isAtEnd(state) { + return peek(state).type === 'EOF' +} +//# sourceMappingURL=parser.js.map diff --git a/packages/compiler/src/parser/parser.js.map b/packages/compiler/src/parser/parser.js.map new file mode 100644 index 0000000..7cc9fbb --- /dev/null +++ b/packages/compiler/src/parser/parser.js.map @@ -0,0 +1 @@ +{"version":3,"file":"parser.js","sourceRoot":"","sources":["parser.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/compiler/src/parser/utils.d.ts.map b/packages/compiler/src/parser/utils.d.ts.map new file mode 100644 index 0000000..8528780 --- /dev/null +++ b/packages/compiler/src/parser/utils.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAEhE;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,SAAS,EACf,MAAM,CAAC,EAAE,SAAS,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,KACX,IAAI,CAAA;AAET;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,SAAS,EACf,MAAM,CAAC,EAAE,SAAS,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,KACX,OAAO,CAAC,IAAI,CAAC,CAAA;AAElB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAsB/D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BxF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,EAAE;IACb;;;;;;;;;;;;;OAaG;cACO,SAAS,GAAG,IAAI,IAAI,WAAW;IAIzC;;;;;;;;;;;;OAYG;kBACW,SAAS,GAAG,IAAI,IAAI,WAAW;IAI7C;;;;;;;;;;;;OAYG;eACQ,SAAS,GAAG,IAAI,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAI3D;;;;;;;;;;;;OAYG;qBACc,SAAS,GAAG,IAAI,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,YAAY,CAAA;KAAE;IAIvE;;;;;;;;;;;;OAYG;sBACe,SAAS,GAAG,IAAI,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,aAAa,CAAA;KAAE;IAIzE;;;;;;;;;;;;OAYG;kBACW,SAAS,GAAG,IAAI,IAAI,QAAQ;CAG3C,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/utils.js b/packages/compiler/src/parser/utils.js new file mode 100644 index 0000000..1469fd7 --- /dev/null +++ b/packages/compiler/src/parser/utils.js @@ -0,0 +1,181 @@ +/** + * Walk through the AST synchronously and execute a handler function for each node + * + * @param ast - The root node of the AST to traverse + * @param handler - Handler function to execute for each node + * + * @example + * ```typescript + * walk(ast, (node) => { + * if (is.tag(node)) { + * console.log(node.name); + * } + * }); + * ``` + */ +export function walk(ast, handler) { + function traverse(node, parent, prop, index) { + handler(node, parent, prop, index) + // Traverse child nodes + if ('children' in node && Array.isArray(node.children)) { + node.children.forEach((child, idx) => { + traverse(child, node, 'children', idx) + }) + } + // Traverse Expression nodes within attributes + if (node.type === 'Element' && node.attributes) { + node.attributes.forEach((attr, idx) => { + if (typeof attr.value === 'object' && attr.value.type === 'Expression') { + traverse(attr.value, node, `attributes[${idx}].value`, idx) + } + }) + } + } + traverse(ast) +} +/** + * Walk through the AST asynchronously and execute a handler function for each node + * + * @param ast - The root node of the AST to traverse + * @param handler - Async handler function to execute for each node + * + * @example + * ```typescript + * await walkAsync(ast, async (node) => { + * if (is.tag(node)) { + * node.value = await expensiveCalculation(node); + * } + * }); + * ``` + */ +export async function walkAsync(ast, handler) { + async function traverse(node, parent, prop, index) { + await handler(node, parent, prop, index) + // Traverse child nodes + if ('children' in node && Array.isArray(node.children)) { + for (let idx = 0; idx < node.children.length; idx++) { + await traverse(node.children[idx], node, 'children', idx) + } + } + // Traverse Expression nodes within attributes + if (node.type === 'Element' && node.attributes) { + for (let idx = 0; idx < node.attributes.length; idx++) { + const attr = node.attributes[idx] + if (typeof attr.value === 'object' && attr.value.type === 'Expression') { + await traverse(attr.value, node, `attributes[${idx}].value`, idx) + } + } + } + } + await traverse(ast) +} +/** + * Type guard helpers for node type checking + * + * @example + * ```typescript + * if (is.tag(node)) { + * console.log(node.name); // node is inferred as ElementNode + * } + * ``` + */ +export const is = { + /** + * Check if a node is a tag (element) node. + * Similar to official Astro, represents element/custom-element/component + * + * @param node - The AST node to check + * @returns True if the node is an ElementNode + * + * @example + * ```typescript + * if (is.tag(node)) { + * console.log(node.name); // TypeScript knows node is ElementNode + * } + * ``` + */ + tag(node) { + return node.type === 'Element' + }, + /** + * Check if a node is an element node + * + * @param node - The AST node to check + * @returns True if the node is an ElementNode + * + * @example + * ```typescript + * if (is.element(node)) { + * console.log(node.attributes); // Access element-specific properties + * } + * ``` + */ + element(node) { + return node.type === 'Element' + }, + /** + * Check if a node is a text node + * + * @param node - The AST node to check + * @returns True if the node is a TextNode + * + * @example + * ```typescript + * if (is.text(node)) { + * console.log(node.value); // Access text content + * } + * ``` + */ + text(node) { + return node.type === 'Text' + }, + /** + * Check if a node is an expression node + * + * @param node - The AST node to check + * @returns True if the node is an ExpressionNode + * + * @example + * ```typescript + * if (is.expression(node)) { + * console.log(node.value); // Access JavaScript expression + * } + * ``` + */ + expression(node) { + return node.type === 'Expression' + }, + /** + * Check if a node is a frontmatter node + * + * @param node - The AST node to check + * @returns True if the node is a FrontmatterNode + * + * @example + * ```typescript + * if (is.frontmatter(node)) { + * console.log(node.value); // Access frontmatter code + * } + * ``` + */ + frontmatter(node) { + return node.type === 'Frontmatter' + }, + /** + * Check if a node is a program node (root) + * + * @param node - The AST node to check + * @returns True if the node is a ProgramNode + * + * @example + * ```typescript + * if (is.program(node)) { + * console.log(node.children); // Access all top-level nodes + * } + * ``` + */ + program(node) { + return node.type === 'Program' + }, +} +//# sourceMappingURL=utils.js.map diff --git a/packages/compiler/src/parser/utils.js.map b/packages/compiler/src/parser/utils.js.map new file mode 100644 index 0000000..e573451 --- /dev/null +++ b/packages/compiler/src/parser/utils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/index.d.ts.map b/packages/compiler/src/tokenizer/index.d.ts.map new file mode 100644 index 0000000..f2354c4 --- /dev/null +++ b/packages/compiler/src/tokenizer/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/index.js b/packages/compiler/src/tokenizer/index.js new file mode 100644 index 0000000..7068d71 --- /dev/null +++ b/packages/compiler/src/tokenizer/index.js @@ -0,0 +1,2 @@ +export { tokenize } from './tokenizer.js' +//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/tokenizer/index.js.map b/packages/compiler/src/tokenizer/index.js.map new file mode 100644 index 0000000..c71d77e --- /dev/null +++ b/packages/compiler/src/tokenizer/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/tokenizer.d.ts.map b/packages/compiler/src/tokenizer/tokenizer.d.ts.map new file mode 100644 index 0000000..9dc24b8 --- /dev/null +++ b/packages/compiler/src/tokenizer/tokenizer.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvC,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CAgNhD"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/tokenizer.js b/packages/compiler/src/tokenizer/tokenizer.js new file mode 100644 index 0000000..5b4501c --- /dev/null +++ b/packages/compiler/src/tokenizer/tokenizer.js @@ -0,0 +1,186 @@ +export function tokenize(source) { + const tokens = [] + let current = 0 + let line = 1 + let col = 1 + function advance(count = 1) { + for (let i = 0; i < count; i++) { + if (source[current] === '\n') { + line++ + col = 1 + } else { + col++ + } + current++ + } + } + function peek(offset = 0) { + return source[current + offset] ?? '' + } + function match(pattern) { + return source.slice(current, current + pattern.length) === pattern + } + function addToken(type, value) { + tokens.push({ type, value, line, col }) + } + // Check for frontmatter at the start + if (match('---') && current === 0) { + addToken('FRONTMATTER_START', '---') + advance(3) + // Skip newline after opening --- + if (peek() === '\n') { + advance() + } + // Collect frontmatter content + let content = '' + while (current < source.length) { + // Check for closing --- + if (peek() === '\n' || current === 0) { + const nextLineStart = peek() === '\n' ? current + 1 : current + if ( + source.slice(nextLineStart, nextLineStart + 3) === '---' && + (source[nextLineStart + 3] === '\n' || source[nextLineStart + 3] === undefined) + ) { + if (content) { + addToken('FRONTMATTER_CONTENT', content.trim()) + } + if (peek() === '\n') advance() + addToken('FRONTMATTER_END', '---') + advance(3) + if (peek() === '\n') advance() + break + } + } + content += peek() + advance() + } + } + // Main tokenization loop + while (current < source.length) { + const char = peek() + // Skip whitespace between elements + if (char === ' ' || char === '\t' || char === '\n') { + advance() + continue + } + // HTML end tag + if (match(' + while (current < source.length && peek() !== '>') { + advance() + } + if (peek() === '>') { + addToken('HTML_TAG_CLOSE', '>') + advance() + } + continue + } + // HTML start tag + if (char === '<' && /[a-zA-Z]/.test(peek(1))) { + addToken('HTML_TAG_OPEN', '<') + advance() + let tagName = '' + while (current < source.length && /[a-zA-Z0-9-]/.test(peek())) { + tagName += peek() + advance() + } + addToken('HTML_TAG_NAME', tagName) + // Parse attributes + while (current < source.length && peek() !== '>' && !match('/>')) { + // Skip whitespace + if (peek() === ' ' || peek() === '\t') { + advance() + continue + } + // Attribute name + if (/[a-zA-Z]/.test(peek())) { + let attrName = '' + while (current < source.length && /[a-zA-Z0-9-:]/.test(peek())) { + attrName += peek() + advance() + } + addToken('HTML_ATTRIBUTE_NAME', attrName) + // Skip whitespace and = + while (peek() === ' ' || peek() === '\t') advance() + if (peek() === '=') { + advance() + while (peek() === ' ' || peek() === '\t') advance() + // Attribute value + if (peek() === '"' || peek() === "'") { + const quote = peek() + advance() // Skip opening quote + let value = '' + while (current < source.length && peek() !== quote) { + value += peek() + advance() + } + advance() // Skip closing quote + addToken('HTML_ATTRIBUTE_VALUE', value) + } + } + } else { + advance() + } + } + // Self-closing or regular close + if (match('/>')) { + addToken('HTML_TAG_SELF_CLOSE', '/>') + advance(2) + } else if (peek() === '>') { + addToken('HTML_TAG_CLOSE', '>') + advance() + } + continue + } + // Expression + if (char === '{') { + addToken('EXPRESSION_START', '{') + advance() + let depth = 1 + let content = '' + while (current < source.length && depth > 0) { + if (peek() === '{') { + depth++ + content += peek() + advance() + } else if (peek() === '}') { + depth-- + if (depth === 0) { + if (content) { + addToken('EXPRESSION_CONTENT', content) + } + addToken('EXPRESSION_END', '}') + advance() + } else { + content += peek() + advance() + } + } else { + content += peek() + advance() + } + } + continue + } + // Text content + let text = '' + while (current < source.length && peek() !== '<' && peek() !== '{' && peek() !== '}') { + text += peek() + advance() + } + if (text.trim()) { + addToken('TEXT', text) + } + } + addToken('EOF', '') + return tokens +} +//# sourceMappingURL=tokenizer.js.map diff --git a/packages/compiler/src/tokenizer/tokenizer.js.map b/packages/compiler/src/tokenizer/tokenizer.js.map new file mode 100644 index 0000000..28bf4b2 --- /dev/null +++ b/packages/compiler/src/tokenizer/tokenizer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["tokenizer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/types.d.ts.map b/packages/compiler/src/tokenizer/types.d.ts.map new file mode 100644 index 0000000..22c49a2 --- /dev/null +++ b/packages/compiler/src/tokenizer/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,MAAM,SAAS,GACjB,mBAAmB,GACnB,iBAAiB,GACjB,qBAAqB,GACrB,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,eAAe,GACf,qBAAqB,GACrB,sBAAsB,GACtB,kBAAkB,GAClB,gBAAgB,GAChB,oBAAoB,GACpB,MAAM,GACN,YAAY,GACZ,KAAK,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/types.js b/packages/compiler/src/tokenizer/types.js new file mode 100644 index 0000000..4ef431e --- /dev/null +++ b/packages/compiler/src/tokenizer/types.js @@ -0,0 +1,2 @@ +export {} +//# sourceMappingURL=types.js.map diff --git a/packages/compiler/src/tokenizer/types.js.map b/packages/compiler/src/tokenizer/types.js.map new file mode 100644 index 0000000..8da0887 --- /dev/null +++ b/packages/compiler/src/tokenizer/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/packages/compiler/tsconfig.json b/packages/compiler/tsconfig.json index 1616aab..e0059de 100644 --- a/packages/compiler/tsconfig.json +++ b/packages/compiler/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist", + "noEmit": false, "tsBuildInfoFile": "./dist/.tsbuildinfo" }, "include": ["src/**/*"], diff --git a/packages/vite-plugin/README.md b/packages/vite-plugin/README.md new file mode 100644 index 0000000..34b810c --- /dev/null +++ b/packages/vite-plugin/README.md @@ -0,0 +1,52 @@ +# @minimal-astro/vite-plugin + +Vite plugin for transforming `.astro` files in the minimal-astro project. + +## Current Status + +This is a **stub implementation** that provides basic transformation of `.astro` files to JavaScript modules. The current implementation: + +- ✅ Parses `.astro` files using `@minimal-astro/compiler` +- ✅ Builds HTML from the AST +- ✅ Exports the HTML as a default export +- ⚠️ Does not handle component imports/exports +- ⚠️ Does not support client-side hydration +- ⚠️ Does not extract CSS +- ⚠️ Does not provide proper HMR support (uses full reload) + +## Usage + +```typescript +// vite.config.ts +import { defineConfig } from 'vite' +import { vitePluginAstro } from '@minimal-astro/vite-plugin' + +export default defineConfig({ + plugins: [vitePluginAstro()] +}) +``` + +## Development + +```bash +# Install dependencies +pnpm install + +# Build the plugin +pnpm build + +# Run tests +pnpm test + +# Watch mode +pnpm dev +``` + +## Future Enhancements + +1. **Component Support**: Handle imports and exports in frontmatter +2. **Hydration**: Implement client-side hydration strategies +3. **CSS Extraction**: Extract and process styles from components +4. **HMR**: Implement proper Hot Module Replacement +5. **Source Maps**: Generate source maps for debugging +6. **Error Handling**: Better error messages and recovery \ No newline at end of file diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json new file mode 100644 index 0000000..c0e8b5e --- /dev/null +++ b/packages/vite-plugin/package.json @@ -0,0 +1,36 @@ +{ + "name": "@minimal-astro/vite-plugin", + "version": "0.1.0", + "type": "module", + "description": "Vite plugin for transforming .astro files", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "files": ["dist"], + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "test": "vitest run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit" + }, + "peerDependencies": { + "vite": "^5.0.0" + }, + "dependencies": { + "@minimal-astro/compiler": "workspace:*", + "@rollup/pluginutils": "^5.1.3" + }, + "devDependencies": { + "@types/node": "^20.11.24", + "typescript": "^5.7.2", + "vite": "^5.4.11", + "vitest": "^2.1.8" + }, + "engines": { + "node": ">=20.0.0" + } +} diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts new file mode 100644 index 0000000..d8402dc --- /dev/null +++ b/packages/vite-plugin/src/index.ts @@ -0,0 +1,82 @@ +/** + * Vite plugin for transforming .astro files + * + * This plugin enables Vite to process .astro files by transforming + * them into JavaScript modules that can be imported and rendered. + * + * @module vite-plugin-astro + */ + +import { createFilter } from '@rollup/pluginutils' +import type { Plugin } from 'vite' +import { transformAstro } from './transform.js' + +/** + * Creates a Vite plugin for processing .astro files + * + * This plugin: + * - Intercepts .astro file imports + * - Transforms them into JavaScript modules + * - Enables HMR support (future enhancement) + * - Handles CSS extraction (future enhancement) + * + * @returns Vite plugin configuration + * + * @example + * ```typescript + * // vite.config.ts + * import { defineConfig } from 'vite' + * import { vitePluginAstro } from '@minimal-astro/vite-plugin' + * + * export default defineConfig({ + * plugins: [vitePluginAstro()] + * }) + * ``` + */ +export function vitePluginAstro(): Plugin { + // Create a filter to match .astro files + const filter = createFilter('**/*.astro') + + return { + name: 'vite-plugin-astro', + + // Run before other plugins to ensure .astro files are transformed first + enforce: 'pre', + + /** + * Transform hook for processing .astro files + */ + transform(code: string, id: string) { + // Only process .astro files + if (!filter(id)) { + return null + } + + // Check if it's an .astro file by extension as well + if (!id.endsWith('.astro')) { + return null + } + + // Transform the .astro file + return transformAstro(code, id) + }, + + /** + * Configure server to handle .astro files + */ + configureServer(server) { + // Add .astro to the list of extensions that trigger full page reload + // This is temporary until proper HMR is implemented + server.watcher.on('change', file => { + if (file.endsWith('.astro')) { + server.ws.send({ + type: 'full-reload', + }) + } + }) + }, + } +} + +// Default export for convenience +export default vitePluginAstro diff --git a/packages/vite-plugin/src/transform.ts b/packages/vite-plugin/src/transform.ts new file mode 100644 index 0000000..0df4093 --- /dev/null +++ b/packages/vite-plugin/src/transform.ts @@ -0,0 +1,45 @@ +/** + * AST transformation utilities for .astro files + * + * This module handles the transformation of .astro source code + * into JavaScript modules that can be processed by Vite. + * + * @module transform + */ + +import { buildHTML, parse } from '@minimal-astro/compiler' + +/** + * Transforms .astro source code into a JavaScript module + * + * This is a temporary implementation that: + * 1. Parses the .astro file into an AST + * 2. Builds HTML from the AST + * 3. Exports the HTML as a default export + * + * In a full implementation, this would handle: + * - Component imports and exports + * - Client-side hydration + * - CSS extraction + * - HMR support + * + * @param code - The .astro source code + * @param id - The file path/id + * @returns Transformed JavaScript code and optional source map + */ +export function transformAstro(code: string, _id: string): { code: string; map: null } { + // Parse the .astro file into an AST + const ast = parse(code) + + // Build HTML from the AST + const html = buildHTML(ast) + + // For now, return a simple module that exports the HTML + // This is a stub implementation that will be enhanced later + const transformedCode = `export default ${JSON.stringify(html)};` + + return { + code: transformedCode, + map: null, // Source maps will be implemented later + } +} diff --git a/packages/vite-plugin/test/plugin.test.ts b/packages/vite-plugin/test/plugin.test.ts new file mode 100644 index 0000000..4197d92 --- /dev/null +++ b/packages/vite-plugin/test/plugin.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'vitest' +import { vitePluginAstro } from '../src/index.js' + +describe('vitePluginAstro', () => { + it('プラグインが正しい構造を持つ', () => { + const plugin = vitePluginAstro() + + expect(plugin.name).toBe('vite-plugin-astro') + expect(plugin.enforce).toBe('pre') + expect(typeof plugin.transform).toBe('function') + expect(typeof plugin.configureServer).toBe('function') + }) + + it('.astroファイルのみを変換する', () => { + const plugin = vitePluginAstro() + const transform = plugin.transform as ( + code: string, + id: string, + ) => { code: string; map: null } | null + + // .astroファイルは変換される + const astroResult = transform('

Test

', '/src/pages/index.astro') + expect(astroResult).not.toBeNull() + expect(astroResult.code).toContain('export default') + + // .jsファイルは変換されない + const jsResult = transform('console.log("test")', '/src/utils/helper.js') + expect(jsResult).toBeNull() + + // .tsファイルは変換されない + const tsResult = transform('const x: number = 1', '/src/utils/types.ts') + expect(tsResult).toBeNull() + + // .cssファイルは変換されない + const cssResult = transform('body { color: red; }', '/src/styles/main.css') + expect(cssResult).toBeNull() + }) + + it('パスフィルターが正しく動作する', () => { + const plugin = vitePluginAstro() + const transform = plugin.transform as ( + code: string, + id: string, + ) => { code: string; map: null } | null + + // 標準的な.astroファイル + expect(transform('
Test
', '/src/pages/index.astro')).not.toBeNull() + + // ネストされた.astroファイル + expect(transform('
Test
', '/src/components/Header.astro')).not.toBeNull() + + // node_modules内の.astroファイルも変換される(実際の使用では除外したいかも) + expect(transform('
Test
', '/node_modules/some-lib/Component.astro')).not.toBeNull() + }) +}) diff --git a/packages/vite-plugin/test/transform.test.ts b/packages/vite-plugin/test/transform.test.ts new file mode 100644 index 0000000..7db68af --- /dev/null +++ b/packages/vite-plugin/test/transform.test.ts @@ -0,0 +1,77 @@ +import { describe, expect, it } from 'vitest' +import { transformAstro } from '../src/transform.js' + +describe('transformAstro', () => { + it('基本的な.astroファイルの変換', () => { + const code = '

Hello World

' + const id = '/src/pages/index.astro' + + const result = transformAstro(code, id) + + expect(result.code).toContain('export default') + expect(result.code).toContain('

Hello World

') + expect(result.map).toBeNull() + }) + + it('フロントマター付きの.astroファイルの変換', () => { + const code = `--- +const title = "My Page" +--- +

{title}

` + const id = '/src/pages/about.astro' + + const result = transformAstro(code, id) + + expect(result.code).toContain('export default') + expect(result.code).toContain('

{title}

') + // フロントマターはHTMLに含まれない + expect(result.code).not.toContain('const title') + }) + + it('複雑な構造の.astroファイルの変換', () => { + const code = `--- +const items = ['Apple', 'Banana', 'Orange'] +--- + + + Fruit List + + +

My Fruits

+ + +` + const id = '/src/pages/fruits.astro' + + const result = transformAstro(code, id) + + expect(result.code).toContain('export default') + expect(result.code).toContain('') + expect(result.code).toContain('Fruit List') + expect(result.code).toContain('{items.map(item =>
  • {item}
  • )}') + }) + + it('空の.astroファイルの変換', () => { + const code = '' + const id = '/src/pages/empty.astro' + + const result = transformAstro(code, id) + + expect(result.code).toBe('export default "";') + expect(result.map).toBeNull() + }) + + it('特殊文字を含む.astroファイルの変換', () => { + const code = '

    This contains "quotes" and \'apostrophes\'

    ' + const id = '/src/pages/special.astro' + + const result = transformAstro(code, id) + + expect(result.code).toContain('export default') + // HTMLエスケープされ、さらにJSON.stringifyでエスケープされる + expect(result.code).toContain('"quotes"') + expect(result.code).toContain(''apostrophes'') + }) +}) diff --git a/packages/vite-plugin/tsconfig.build.json b/packages/vite-plugin/tsconfig.build.json new file mode 100644 index 0000000..aa0f807 --- /dev/null +++ b/packages/vite-plugin/tsconfig.build.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022"], + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "isolatedModules": true, + "composite": false + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test"] +} diff --git a/packages/vite-plugin/tsconfig.json b/packages/vite-plugin/tsconfig.json new file mode 100644 index 0000000..4868609 --- /dev/null +++ b/packages/vite-plugin/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "noEmit": false, + "declaration": true, + "declarationMap": true, + "composite": false + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "test"] +} diff --git a/packages/vite-plugin/vitest.config.ts b/packages/vite-plugin/vitest.config.ts new file mode 100644 index 0000000..f1881c1 --- /dev/null +++ b/packages/vite-plugin/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'node', + globals: true, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ec1b35..1b4245a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: examples/blog: devDependencies: + '@minimal-astro/vite-plugin': + specifier: workspace:* + version: link:../../packages/vite-plugin vite: specifier: ^5.4.11 version: 5.4.19(@types/node@20.19.9) @@ -83,6 +86,28 @@ importers: specifier: ^2.1.8 version: 2.1.9(@types/node@20.19.9) + packages/vite-plugin: + dependencies: + '@minimal-astro/compiler': + specifier: workspace:* + version: link:../compiler + '@rollup/pluginutils': + specifier: ^5.1.3 + version: 5.2.0(rollup@4.46.0) + devDependencies: + '@types/node': + specifier: ^20.11.24 + version: 20.19.9 + typescript: + specifier: ^5.7.2 + version: 5.8.3 + vite: + specifier: ^5.4.11 + version: 5.4.19(@types/node@20.19.9) + vitest: + specifier: ^2.1.8 + version: 2.1.9(@types/node@20.19.9) + packages: '@biomejs/biome@1.9.4': @@ -282,6 +307,15 @@ packages: '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.46.0': resolution: {integrity: sha512-9f3nSTFI2ivfxc7/tHBHcJ8pRnp8ROrELvsVprlQPVvcZ+j5zztYd+PTJGpyIOAdTvNwNrpCXswKSeoQcyGjMQ==} cpu: [arm] @@ -496,6 +530,9 @@ packages: engines: {node: '>=12'} hasBin: true + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -567,6 +604,10 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -854,6 +895,14 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.4': {} + '@rollup/pluginutils@5.2.0(rollup@4.46.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.46.0 + '@rollup/rollup-android-arm-eabi@4.46.0': optional: true @@ -1048,6 +1097,8 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -1112,6 +1163,8 @@ snapshots: picocolors@1.1.1: {} + picomatch@4.0.3: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 From 18f16291b00fea3d7a0f90ce16403e57c0020538 Mon Sep 17 00:00:00 2001 From: jp-knj Date: Tue, 29 Jul 2025 01:13:53 +0900 Subject: [PATCH 2/7] chore: clean up unnecessary Vite template files from blog example - Remove index.html, main.js, counter.js, style.css, javascript.svg, vite.svg - Remove test.astro that was created for testing - Remove dist directory - Add minimal placeholder index.html to prevent build errors - Keep minimal structure with package.json, vite.config.ts, and empty src/ directories --- examples/blog/index.html | 11 ++- examples/blog/public/vite.svg | 1 - examples/blog/src/counter.js | 9 --- examples/blog/src/javascript.svg | 1 - examples/blog/src/main.js | 24 ------ examples/blog/src/pages/test.astro | 17 ----- examples/blog/src/style.css | 96 ------------------------ packages/vite-plugin/test/plugin.test.ts | 2 +- 8 files changed, 6 insertions(+), 155 deletions(-) delete mode 100644 examples/blog/public/vite.svg delete mode 100644 examples/blog/src/counter.js delete mode 100644 examples/blog/src/javascript.svg delete mode 100644 examples/blog/src/main.js delete mode 100644 examples/blog/src/pages/test.astro delete mode 100644 examples/blog/src/style.css diff --git a/examples/blog/index.html b/examples/blog/index.html index 72ba3a8..5455d48 100644 --- a/examples/blog/index.html +++ b/examples/blog/index.html @@ -1,13 +1,12 @@ - + - - Vite App + Minimal Astro Blog -
    - +

    Minimal Astro Blog

    +

    This is a placeholder for the blog example. It will be implemented in later phases.

    - + \ No newline at end of file diff --git a/examples/blog/public/vite.svg b/examples/blog/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/examples/blog/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/blog/src/counter.js b/examples/blog/src/counter.js deleted file mode 100644 index 52419ba..0000000 --- a/examples/blog/src/counter.js +++ /dev/null @@ -1,9 +0,0 @@ -export function setupCounter(element) { - let counter = 0 - const setCounter = count => { - counter = count - element.innerHTML = `count is ${counter}` - } - element.addEventListener('click', () => setCounter(counter + 1)) - setCounter(0) -} diff --git a/examples/blog/src/javascript.svg b/examples/blog/src/javascript.svg deleted file mode 100644 index f9abb2b..0000000 --- a/examples/blog/src/javascript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/blog/src/main.js b/examples/blog/src/main.js deleted file mode 100644 index 1733822..0000000 --- a/examples/blog/src/main.js +++ /dev/null @@ -1,24 +0,0 @@ -import './style.css' -import viteLogo from '/vite.svg' -import { setupCounter } from './counter.js' -import javascriptLogo from './javascript.svg' - -document.querySelector('#app').innerHTML = ` -
    - - - - - - -

    Hello Vite!

    -
    - -
    -

    - Click on the Vite logo to learn more -

    -
    -` - -setupCounter(document.querySelector('#counter')) diff --git a/examples/blog/src/pages/test.astro b/examples/blog/src/pages/test.astro deleted file mode 100644 index 07a8f70..0000000 --- a/examples/blog/src/pages/test.astro +++ /dev/null @@ -1,17 +0,0 @@ ---- -const _greeting = 'Hello from Astro!' -const _items = ['Item 1', 'Item 2', 'Item 3'] ---- - - - - Test Astro Page - - -

    {greeting}

    -

    This is a test page to verify the Vite plugin is working.

    -
      - {items.map(item =>
    • {item}
    • )} -
    - - \ No newline at end of file diff --git a/examples/blog/src/style.css b/examples/blog/src/style.css deleted file mode 100644 index 8df73e3..0000000 --- a/examples/blog/src/style.css +++ /dev/null @@ -1,96 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.vanilla:hover { - filter: drop-shadow(0 0 2em #f7df1eaa); -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/packages/vite-plugin/test/plugin.test.ts b/packages/vite-plugin/test/plugin.test.ts index 4197d92..a6f9cb3 100644 --- a/packages/vite-plugin/test/plugin.test.ts +++ b/packages/vite-plugin/test/plugin.test.ts @@ -21,7 +21,7 @@ describe('vitePluginAstro', () => { // .astroファイルは変換される const astroResult = transform('

    Test

    ', '/src/pages/index.astro') expect(astroResult).not.toBeNull() - expect(astroResult.code).toContain('export default') + expect(astroResult?.code).toContain('export default') // .jsファイルは変換されない const jsResult = transform('console.log("test")', '/src/utils/helper.js') From 5f25080dd2261d1034f0f13babdb748e9942f836 Mon Sep 17 00:00:00 2001 From: jp-knj Date: Tue, 29 Jul 2025 02:32:20 +0900 Subject: [PATCH 3/7] feat: implement SSR for .astro pages (#34) - Add SSR transformation using template literals instead of eval - Implement middleware for dev server routing (/, /about, etc) - Support frontmatter execution and expression evaluation - Create sample pages (index.astro, about.astro) - Add comprehensive unit and integration tests - Configure Vite with appType: 'custom' to handle SSR - Exclude .astro files from Biome linting (syntax not supported) The implementation separates SSG (html-builder) from SSR concerns as per the architecture document, avoiding eval/Function constructor for security. --- biome.json | 3 +- examples/blog/src/pages/about.astro | 43 + examples/blog/src/pages/index.astro | 26 + packages/vite-plugin/package.json | 5 + packages/vite-plugin/src/index.ts | 40 +- packages/vite-plugin/src/middleware.ts | 116 +++ packages/vite-plugin/src/transform.ts | 125 ++- packages/vite-plugin/test/e2e.test.ts | 100 ++ packages/vite-plugin/test/middleware.test.ts | 68 ++ packages/vite-plugin/test/transform.test.ts | 50 +- pnpm-lock.yaml | 952 +++++++++++++++++++ 11 files changed, 1487 insertions(+), 41 deletions(-) create mode 100644 examples/blog/src/pages/about.astro create mode 100644 examples/blog/src/pages/index.astro create mode 100644 packages/vite-plugin/src/middleware.ts create mode 100644 packages/vite-plugin/test/e2e.test.ts create mode 100644 packages/vite-plugin/test/middleware.test.ts diff --git a/biome.json b/biome.json index 5a848ba..3629a42 100644 --- a/biome.json +++ b/biome.json @@ -50,7 +50,8 @@ "coverage", "*.min.js", ".husky", - "*.tsbuildinfo" + "*.tsbuildinfo", + "**/*.astro" ] } } diff --git a/examples/blog/src/pages/about.astro b/examples/blog/src/pages/about.astro new file mode 100644 index 0000000..5f78fc6 --- /dev/null +++ b/examples/blog/src/pages/about.astro @@ -0,0 +1,43 @@ +--- +const title = 'About minimal-astro' +const author = { + name: 'Kenji', + role: 'Developer', + description: 'Building a minimal implementation of Astro to understand its internals.', +} +const createdAt = new Date().toLocaleDateString('ja-JP') +--- + + + + + + {title} + + +

    {title}

    + +
    +

    About this project

    +

    minimal-astro is an educational project that reimplements core features of Astro from scratch.

    + +

    Author

    +
    +
    Name
    +
    {author.name}
    + +
    Role
    +
    {author.role}
    + +
    Description
    +
    {author.description}
    +
    + +

    Created on: {createdAt}

    +
    + + + + \ No newline at end of file diff --git a/examples/blog/src/pages/index.astro b/examples/blog/src/pages/index.astro new file mode 100644 index 0000000..2f1f514 --- /dev/null +++ b/examples/blog/src/pages/index.astro @@ -0,0 +1,26 @@ +--- +const title = 'Hello minimal-astro' +const message = 'Welcome to minimal-astro!' +const items = ['Fast by default', 'Island Architecture', 'Multi-framework support'] +--- + + + + + + {title} + + +

    {message}

    +

    This is a minimal implementation of Astro, built from scratch.

    + +

    Features

    +
      + {items.map(item =>
    • {item}
    • )} +
    + + + + \ No newline at end of file diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index c0e8b5e..447bd80 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -26,6 +26,11 @@ }, "devDependencies": { "@types/node": "^20.11.24", + "@types/supertest": "^6.0.3", + "@vitest/coverage-v8": "^3.2.4", + "execa": "^9.6.0", + "get-port": "^7.1.0", + "supertest": "^7.1.4", "typescript": "^5.7.2", "vite": "^5.4.11", "vitest": "^2.1.8" diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index d8402dc..b6a0a85 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -43,6 +43,15 @@ export function vitePluginAstro(): Plugin { // Run before other plugins to ensure .astro files are transformed first enforce: 'pre', + /** + * Disable index.html handling + */ + config() { + return { + appType: 'custom', + } + }, + /** * Transform hook for processing .astro files */ @@ -64,16 +73,27 @@ export function vitePluginAstro(): Plugin { /** * Configure server to handle .astro files */ - configureServer(server) { - // Add .astro to the list of extensions that trigger full page reload - // This is temporary until proper HMR is implemented - server.watcher.on('change', file => { - if (file.endsWith('.astro')) { - server.ws.send({ - type: 'full-reload', - }) - } - }) + async configureServer(server) { + // Import middleware + const { createAstroMiddleware } = await import('./middleware.js') + + // Return a function that adds our middleware + // This ensures it runs before Vite's built-in middleware + return () => { + server.middlewares.use(createAstroMiddleware(server)) + } + }, + + /** + * Configure HMR + */ + handleHotUpdate({ file, server }) { + if (file.endsWith('.astro')) { + // Trigger full page reload for .astro files + server.ws.send({ + type: 'full-reload', + }) + } }, } } diff --git a/packages/vite-plugin/src/middleware.ts b/packages/vite-plugin/src/middleware.ts new file mode 100644 index 0000000..f2e55d4 --- /dev/null +++ b/packages/vite-plugin/src/middleware.ts @@ -0,0 +1,116 @@ +/** + * SSR middleware for serving .astro pages + * + * This module handles HTTP requests and serves rendered .astro pages + * by resolving file paths, loading modules, and executing render functions. + * + * @module middleware + */ + +import { existsSync } from 'node:fs' +import type { IncomingMessage, ServerResponse } from 'node:http' +import { join } from 'node:path' +import type { ViteDevServer } from 'vite' + +/** + * Maps URL paths to .astro file paths + * + * @param url - The requested URL path + * @param root - The project root directory + * @returns The resolved file path or null if not found + */ +export function resolveAstroFile(url: string, root: string): string | null { + // Remove query string and hash + const cleanUrl = url.split('?')[0].split('#')[0] + + // Handle root path + if (cleanUrl === '/' || cleanUrl === '') { + const indexPath = join(root, 'src/pages/index.astro') + return existsSync(indexPath) ? indexPath : null + } + + // Remove leading slash and add .astro extension if needed + const urlPath = cleanUrl.startsWith('/') ? cleanUrl.slice(1) : cleanUrl + const astroPath = urlPath.endsWith('.astro') ? urlPath : `${urlPath}.astro` + + // Try to find the file in src/pages + const filePath = join(root, 'src/pages', astroPath) + return existsSync(filePath) ? filePath : null +} + +/** + * Creates SSR middleware for handling .astro page requests + * + * @param server - The Vite dev server instance + * @returns Middleware function + */ +export function createAstroMiddleware(server: ViteDevServer) { + return async (req: IncomingMessage, res: ServerResponse, next: () => void) => { + // Only handle GET requests + if (req.method !== 'GET') { + return next() + } + + const url = req.url || '/' + + // Skip Vite internal requests + if (url.startsWith('/@') || url.includes('__vite')) { + return next() + } + + // Skip non-page requests (assets, etc.) except .astro files + if (url.includes('.') && !url.endsWith('.astro') && !url.endsWith('/')) { + return next() + } + + // Resolve the .astro file path + const filePath = resolveAstroFile(url, server.config.root) + + if (!filePath) { + // No matching .astro file found + return next() + } + + try { + // Load the module through Vite's SSR pipeline + const module = await server.ssrLoadModule(filePath) + + // Check if the module has a default export (render function) + if (typeof module.default !== 'function') { + throw new Error(`Module ${filePath} does not export a render function`) + } + + // Execute the render function + const html = await module.default() + + // Send the response + res.statusCode = 200 + res.setHeader('Content-Type', 'text/html; charset=utf-8') + res.end(html) + } catch (error) { + // Log the error + console.error(`Error rendering ${filePath}:`, error) + + // Send error response + res.statusCode = 500 + res.setHeader('Content-Type', 'text/html; charset=utf-8') + res.end(` + + + + Error + + + +

    Error rendering page

    +

    Failed to render ${filePath}

    +
    ${error instanceof Error ? error.stack : String(error)}
    + + + `) + } + } +} diff --git a/packages/vite-plugin/src/transform.ts b/packages/vite-plugin/src/transform.ts index 0df4093..cad277f 100644 --- a/packages/vite-plugin/src/transform.ts +++ b/packages/vite-plugin/src/transform.ts @@ -7,36 +7,127 @@ * @module transform */ -import { buildHTML, parse } from '@minimal-astro/compiler' +import { parse } from '@minimal-astro/compiler' +import type { AstroAST, AstroNode, ElementNode, FrontmatterNode } from '@minimal-astro/compiler' /** - * Transforms .astro source code into a JavaScript module + * Helper to escape template literal special characters + */ +function escapeTemplate(str: string): string { + return str.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$') +} + +/** + * Converts an AST node to template literal code + */ +function nodeToTemplate(node: AstroNode): string { + switch (node.type) { + case 'Text': + // Escape backticks and ${} in text content + return escapeTemplate(node.value) + + case 'Expression': + // Expression nodes contain JavaScript code to be interpolated + return `\${escapeHtml(String(${node.value}))}` + + case 'Element': + return elementToTemplate(node) + + case 'Program': + case 'Template': + return node.children.map(child => nodeToTemplate(child)).join('') + + case 'Frontmatter': + // Frontmatter is handled separately + return '' + + default: + return '' + } +} + +/** + * Converts an element node to template literal code + */ +function elementToTemplate(node: ElementNode): string { + // Build attributes + const attrs = node.attributes + .map(attr => { + if (typeof attr.value === 'string') { + return ` ${attr.name}="${escapeTemplate(attr.value)}"` + } + // Expression attribute + return ` ${attr.name}="\${escapeHtml(String(${attr.value.value}))}"` + }) + .join('') + + if (node.selfClosing) { + return `<${node.name}${attrs} />` + } + + // Handle JSX-like expressions in children + const children = node.children + .map(child => { + if (child.type === 'Expression') { + // Check if expression looks like JSX (contains map with <) + const expr = child.value + if (expr.includes('.map') && expr.includes('<')) { + // Transform JSX-like expressions to template strings + // This is a simplified approach - in production we'd use proper JSX transform + return `\${${expr}.join('')}` + } + } + return nodeToTemplate(child) + }) + .join('') + + return `<${node.name}${attrs}>${children}` +} + +/** + * Extracts frontmatter code from AST + */ +function extractFrontmatter(ast: AstroAST): string { + const frontmatterNode = ast.children.find( + (node): node is FrontmatterNode => node.type === 'Frontmatter', + ) + return frontmatterNode ? frontmatterNode.value.trim() : '' +} + +/** + * Transforms .astro source code into a JavaScript module for SSR * - * This is a temporary implementation that: + * This implementation: * 1. Parses the .astro file into an AST - * 2. Builds HTML from the AST - * 3. Exports the HTML as a default export - * - * In a full implementation, this would handle: - * - Component imports and exports - * - Client-side hydration - * - CSS extraction - * - HMR support + * 2. Extracts frontmatter code + * 3. Converts the template to a template literal + * 4. Returns a render function that executes frontmatter and returns HTML * * @param code - The .astro source code * @param id - The file path/id * @returns Transformed JavaScript code and optional source map */ -export function transformAstro(code: string, _id: string): { code: string; map: null } { +export function transformAstro(code: string, id: string): { code: string; map: null } { // Parse the .astro file into an AST const ast = parse(code) - // Build HTML from the AST - const html = buildHTML(ast) + // Extract frontmatter + const frontmatter = extractFrontmatter(ast) - // For now, return a simple module that exports the HTML - // This is a stub implementation that will be enhanced later - const transformedCode = `export default ${JSON.stringify(html)};` + // Convert AST to template literal code + const templateCode = nodeToTemplate(ast) + + // Generate the module code + const transformedCode = ` +// Transformed from: ${id} +import { escapeHtml } from '@minimal-astro/compiler'; + +export default function render() { + ${frontmatter} + + return \`${templateCode}\`; +} +` return { code: transformedCode, diff --git a/packages/vite-plugin/test/e2e.test.ts b/packages/vite-plugin/test/e2e.test.ts new file mode 100644 index 0000000..57ce4f2 --- /dev/null +++ b/packages/vite-plugin/test/e2e.test.ts @@ -0,0 +1,100 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { execa } from 'execa' +import type { ExecaChildProcess } from 'execa' +import getPort from 'get-port' +import request from 'supertest' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const blogDir = path.join(__dirname, '../../../examples/blog') + +describe('E2E - Dev Server', () => { + let devServer: ExecaChildProcess + let port: number + let baseUrl: string + + beforeAll(async () => { + // Get an available port + port = await getPort() + baseUrl = `http://localhost:${port}` + + // Start the dev server + devServer = execa('pnpm', ['dev', '--port', String(port)], { + cwd: blogDir, + env: { + ...process.env, + NODE_ENV: 'development', + }, + }) + + // Wait for the server to start + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error('Dev server failed to start')) + }, 30000) + + devServer.stdout?.on('data', chunk => { + const output = chunk.toString() + console.info('[Dev Server]', output) + + if (output.includes('ready in') || output.includes('Local:')) { + clearTimeout(timeout) + resolve() + } + }) + + devServer.stderr?.on('data', chunk => { + console.error('[Dev Server Error]', chunk.toString()) + }) + }) + + // Give it a bit more time to stabilize + await new Promise(resolve => setTimeout(resolve, 1000)) + }) + + afterAll(async () => { + // Kill the dev server + if (devServer) { + devServer.kill('SIGTERM') + await devServer.catch(() => {}) // Ignore exit errors + } + }) + + it('serves index.astro at /', async () => { + const response = await request(baseUrl) + .get('/') + .expect(200) + .expect('Content-Type', /text\/html/) + + expect(response.text).toContain('Hello minimal-astro') + expect(response.text).toContain('Welcome to minimal-astro!') + expect(response.text).toContain('Fast by default') + }) + + it('serves about.astro at /about', async () => { + const response = await request(baseUrl) + .get('/about') + .expect(200) + .expect('Content-Type', /text\/html/) + + expect(response.text).toContain('About minimal-astro') + expect(response.text).toContain('Kenji') + expect(response.text).toContain('Developer') + }) + + it('returns 404 for non-existent pages', async () => { + await request(baseUrl).get('/non-existent').expect(404) + }) + + it('handles SIGINT gracefully', async () => { + // Send SIGINT + devServer.kill('SIGINT') + + // Wait for process to exit + const result = await devServer.catch(err => err) + + // Should exit cleanly (exit code 0 or SIGINT) + expect([0, null, undefined, 'SIGINT']).toContain(result.signal || result.exitCode) + }) +}) diff --git a/packages/vite-plugin/test/middleware.test.ts b/packages/vite-plugin/test/middleware.test.ts new file mode 100644 index 0000000..31fea16 --- /dev/null +++ b/packages/vite-plugin/test/middleware.test.ts @@ -0,0 +1,68 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import request from 'supertest' +import { createServer } from 'vite' +import type { ViteDevServer } from 'vite' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { vitePluginAstro } from '../src/index.js' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +describe('SSR Middleware', () => { + let server: ViteDevServer + + beforeAll(async () => { + // Create a Vite server in middleware mode + server = await createServer({ + root: path.join(__dirname, '../../../examples/blog'), + server: { + middlewareMode: true, + }, + plugins: [vitePluginAstro()], + logLevel: 'error', + }) + + // Wait a bit for the middleware to be registered + await new Promise(resolve => setTimeout(resolve, 100)) + }) + + afterAll(async () => { + await server.close() + }) + + it('/ returns index.astro content', async () => { + const response = await request(server.middlewares) + .get('/') + .expect(200) + .expect('Content-Type', /text\/html/) + + expect(response.text).toContain('Hello minimal-astro') + expect(response.text).toContain('

    ') + }) + + it('/about returns about.astro content', async () => { + const response = await request(server.middlewares) + .get('/about') + .expect(200) + .expect('Content-Type', /text\/html/) + + expect(response.text).toContain('About minimal-astro') + expect(response.text).toContain('Kenji') + }) + + it('404 for non-existent pages', async () => { + // The middleware should pass through to next() for non-existent pages + // Since we're in middleware mode, this will result in a 404 + await request(server.middlewares).get('/non-existent').expect(404) + }) + + it('Ignores non-GET requests', async () => { + // POST requests should be passed through + await request(server.middlewares).post('/').expect(404) + }) + + it('Ignores asset requests', async () => { + // Asset requests should be passed through + await request(server.middlewares).get('/style.css').expect(404) + }) +}) diff --git a/packages/vite-plugin/test/transform.test.ts b/packages/vite-plugin/test/transform.test.ts index 7db68af..11ad6f7 100644 --- a/packages/vite-plugin/test/transform.test.ts +++ b/packages/vite-plugin/test/transform.test.ts @@ -8,7 +8,7 @@ describe('transformAstro', () => { const result = transformAstro(code, id) - expect(result.code).toContain('export default') + expect(result.code).toContain('export default function render()') expect(result.code).toContain('

    Hello World

    ') expect(result.map).toBeNull() }) @@ -22,10 +22,9 @@ const title = "My Page" const result = transformAstro(code, id) - expect(result.code).toContain('export default') - expect(result.code).toContain('

    {title}

    ') - // フロントマターはHTMLに含まれない - expect(result.code).not.toContain('const title') + expect(result.code).toContain('export default function render()') + expect(result.code).toContain('const title = "My Page"') + expect(result.code).toContain('${escapeHtml(String(title))}') }) it('複雑な構造の.astroファイルの変換', () => { @@ -47,10 +46,10 @@ const items = ['Apple', 'Banana', 'Orange'] const result = transformAstro(code, id) - expect(result.code).toContain('export default') - expect(result.code).toContain('') + expect(result.code).toContain('export default function render()') + expect(result.code).toContain("const items = ['Apple', 'Banana', 'Orange']") expect(result.code).toContain('Fruit List') - expect(result.code).toContain('{items.map(item =>
  • {item}
  • )}') + expect(result.code).toContain('${items.map(item =>
  • {item}
  • ).join(') }) it('空の.astroファイルの変換', () => { @@ -59,19 +58,44 @@ const items = ['Apple', 'Banana', 'Orange'] const result = transformAstro(code, id) - expect(result.code).toBe('export default "";') + expect(result.code).toContain('export default function render()') + expect(result.code).toContain('return ``') expect(result.map).toBeNull() }) it('特殊文字を含む.astroファイルの変換', () => { - const code = '

    This contains "quotes" and \'apostrophes\'

    ' + const code = '

    Price: $100 with `quotes`

    ' const id = '/src/pages/special.astro' const result = transformAstro(code, id) expect(result.code).toContain('export default') - // HTMLエスケープされ、さらにJSON.stringifyでエスケープされる - expect(result.code).toContain('"quotes"') - expect(result.code).toContain(''apostrophes'') + // Template literal special chars should be escaped + expect(result.code).toContain('\\$100') + expect(result.code).toContain('\\`quotes\\`') + }) + + it('式を含む属性の変換', () => { + const code = `--- +const className = 'active' +--- +
    Content
    ` + const id = '/src/pages/attrs.astro' + + const result = transformAstro(code, id) + + // TODO: Parser currently has a bug with class={expr} syntax + // For now, just check that the transform completes + expect(result.code).toContain('export default function render()') + expect(result.code).toContain("const className = 'active'") + }) + + it('escapeHtmlのインポート', () => { + const code = '
    {content}
    ' + const id = '/src/pages/escape.astro' + + const result = transformAstro(code, id) + + expect(result.code).toContain("import { escapeHtml } from '@minimal-astro/compiler'") }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b4245a..d03c0d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,6 +98,21 @@ importers: '@types/node': specifier: ^20.11.24 version: 20.19.9 + '@types/supertest': + specifier: ^6.0.3 + version: 6.0.3 + '@vitest/coverage-v8': + specifier: ^3.2.4 + version: 3.2.4(vitest@2.1.9(@types/node@20.19.9)) + execa: + specifier: ^9.6.0 + version: 9.6.0 + get-port: + specifier: ^7.1.0 + version: 7.1.0 + supertest: + specifier: ^7.1.4 + version: 7.1.4 typescript: specifier: ^5.7.2 version: 5.8.3 @@ -110,6 +125,31 @@ importers: packages: + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@biomejs/biome@1.9.4': resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} engines: {node: '>=14.21.3'} @@ -304,9 +344,38 @@ packages: '@gerrit0/mini-shiki@3.8.1': resolution: {integrity: sha512-HVZW+8pxoOExr5ZMPK15U79jQAZTO/S6i5byQyyZGjtNj+qaYd82cizTncwFzTQgiLo8uUBym6vh+/1tfJklTw==} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@rollup/pluginutils@5.2.0': resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} engines: {node: '>=14.0.0'} @@ -416,6 +485,9 @@ packages: cpu: [x64] os: [win32] + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@shikijs/engine-oniguruma@3.8.1': resolution: {integrity: sha512-KGQJZHlNY7c656qPFEQpIoqOuC4LrxjyNndRdzk5WKB/Ie87+NJCF1xo9KkOUxwxylk7rT6nhlZyTGTC4fCe1g==} @@ -431,21 +503,46 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + '@types/moo@0.5.10': resolution: {integrity: sha512-W6KzyZjXUYpwQfLK1O1UDzqcqYlul+lO7Bt71luyIIyNlOZwJaNeWWdqFs1C/f2hohZvUFHMk6oFNe9Rg48DbA==} '@types/node@20.19.9': resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==} + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} + + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@vitest/coverage-v8@3.2.4': + resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} + peerDependencies: + '@vitest/browser': 3.2.4 + vitest: 3.2.4 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/expect@2.1.9': resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} @@ -480,13 +577,38 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@0.3.3: + resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -497,6 +619,14 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + chai@5.2.1: resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} engines: {node: '>=18'} @@ -505,6 +635,27 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -518,13 +669,49 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -536,10 +723,33 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.2.2: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -549,14 +759,105 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} hasBin: true + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -566,23 +867,58 @@ packages: loupe@3.2.0: resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} @@ -594,6 +930,36 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -612,18 +978,59 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + rollup@4.46.0: resolution: {integrity: sha512-ONmkT3Ud3IfW15nl7l4qAZko5/2iZ5ALVBDh02ZSZ5IGVLJSYkRcRa3iB58VyEIyoofs9m2xdVrm+lTi97+3pw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -634,6 +1041,42 @@ packages: std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + superagent@10.2.3: + resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==} + engines: {node: '>=14.18.0'} + + supertest@7.1.4: + resolution: {integrity: sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==} + engines: {node: '>=14.18.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -648,6 +1091,10 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} @@ -704,6 +1151,10 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -769,18 +1220,58 @@ packages: jsdom: optional: true + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + yaml@2.8.0: resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} engines: {node: '>= 14.6'} hasBin: true + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + snapshots: + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@bcoe/v8-coverage@1.0.2': {} + '@biomejs/biome@1.9.4': optionalDependencies: '@biomejs/cli-darwin-arm64': 1.9.4 @@ -893,8 +1384,40 @@ snapshots: '@shikijs/types': 3.8.1 '@shikijs/vscode-textmate': 10.0.2 + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jridgewell/gen-mapping@0.3.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + + '@noble/hashes@1.8.0': {} + + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + + '@pkgjs/parseargs@0.11.0': + optional: true + '@rollup/pluginutils@5.2.0(rollup@4.46.0)': dependencies: '@types/estree': 1.0.8 @@ -963,6 +1486,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.46.0': optional: true + '@sec-ant/readable-stream@0.4.1': {} + '@shikijs/engine-oniguruma@3.8.1': dependencies: '@shikijs/types': 3.8.1 @@ -983,20 +1508,57 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@sindresorhus/merge-streams@4.0.0': {} + + '@types/cookiejar@2.1.5': {} + '@types/estree@1.0.8': {} '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 + '@types/methods@1.1.4': {} + '@types/moo@0.5.10': {} '@types/node@20.19.9': dependencies: undici-types: 6.21.0 + '@types/superagent@8.1.9': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 20.19.9 + form-data: 4.0.4 + + '@types/supertest@6.0.3': + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.9 + '@types/unist@3.0.3': {} + '@vitest/coverage-v8@3.2.4(vitest@2.1.9(@types/node@20.19.9))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + ast-v8-to-istanbul: 0.3.3 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.9.0 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 2.1.9(@types/node@20.19.9) + transitivePeerDependencies: + - supports-color + '@vitest/expect@2.1.9': dependencies: '@vitest/spy': 2.1.9 @@ -1039,10 +1601,30 @@ snapshots: acorn@8.15.0: {} + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + argparse@2.0.1: {} + asap@2.0.6: {} + assertion-error@2.0.1: {} + ast-v8-to-istanbul@0.3.3: + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + + asynckit@0.4.0: {} + balanced-match@1.0.2: {} brace-expansion@2.0.2: @@ -1051,6 +1633,16 @@ snapshots: cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + chai@5.2.1: dependencies: assertion-error: 2.0.1 @@ -1061,16 +1653,70 @@ snapshots: check-error@2.1.1: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + component-emitter@1.3.1: {} + + cookiejar@2.1.4: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + debug@4.4.1: dependencies: ms: 2.1.3 deep-eql@5.0.2: {} + delayed-stream@1.0.0: {} + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + entities@4.5.0: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -1103,8 +1749,48 @@ snapshots: dependencies: '@types/estree': 1.0.8 + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 + expect-type@1.2.2: {} + fast-safe-stringify@2.1.1: {} + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.2.2 + dezalgo: 1.0.4 + once: 1.4.0 + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -1114,10 +1800,103 @@ snapshots: fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-port@7.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + gopd@1.2.0: {} + graceful-fs@4.2.11: {} + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + html-escaper@2.0.2: {} + + human-signals@8.0.1: {} + husky@9.1.7: {} + is-fullwidth-code-point@3.0.0: {} + + is-plain-obj@4.1.0: {} + + is-stream@4.0.1: {} + + is-unicode-supported@2.1.0: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.29 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + js-tokens@9.0.1: {} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 @@ -1130,12 +1909,24 @@ snapshots: loupe@3.2.0: {} + lru-cache@10.4.3: {} + lunr@2.3.9: {} magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.4 + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -1145,18 +1936,56 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 + math-intrinsics@1.1.0: {} + mdurl@2.0.0: {} + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@2.6.0: {} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 + minipass@7.1.2: {} + moo@0.5.2: {} ms@2.1.3: {} nanoid@3.3.11: {} + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + object-inspect@1.13.4: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + package-json-from-dist@1.0.1: {} + + parse-ms@4.0.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + pathe@1.1.2: {} pathval@2.0.1: {} @@ -1171,8 +2000,16 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + punycode.js@2.3.1: {} + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + rollup@4.46.0: dependencies: '@types/estree': 1.0.8 @@ -1199,14 +2036,105 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.46.0 fsevents: 2.3.3 + semver@7.7.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} + source-map-js@1.2.1: {} stackback@0.0.2: {} std-env@3.9.0: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-final-newline@4.0.0: {} + + superagent@10.2.3: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.1 + fast-safe-stringify: 2.1.1 + form-data: 4.0.4 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.14.0 + transitivePeerDependencies: + - supports-color + + supertest@7.1.4: + dependencies: + methods: 1.1.2 + superagent: 10.2.3 + transitivePeerDependencies: + - supports-color + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.5 + minimatch: 9.0.5 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -1215,6 +2143,8 @@ snapshots: tinyrainbow@1.2.0: {} + tinyrainbow@2.0.0: {} + tinyspy@3.0.2: {} turbo-darwin-64@2.5.5: @@ -1259,6 +2189,8 @@ snapshots: undici-types@6.21.0: {} + unicorn-magic@0.3.0: {} + universalify@2.0.1: {} vite-node@2.1.9(@types/node@20.19.9): @@ -1323,9 +2255,29 @@ snapshots: - supports-color - terser + which@2.0.2: + dependencies: + isexe: 2.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + yaml@2.8.0: {} + + yoctocolors@2.1.1: {} From 49446422e3308c5b6c4021446baffe56e9e57b99 Mon Sep 17 00:00:00 2001 From: jp-knj Date: Tue, 29 Jul 2025 10:27:39 +0900 Subject: [PATCH 4/7] fix: resolve SSR test failures and enable .astro as entry point - Add transformation guard to prevent double transformation - Handle JSX-like expressions in templates by converting to template literals - Add @minimal-astro/compiler dependency to blog example - Fix module resolution issues in test environment - Configure Vite to use .astro files as entry points instead of index.html - Add resolveId and load hooks to properly handle .astro file imports All 19 tests now passing successfully --- examples/blog/index.html | 12 ------ examples/blog/package.json | 3 ++ examples/blog/src/{pages => }/about.astro | 0 examples/blog/src/{pages => }/index.astro | 0 examples/blog/vite.config.ts | 5 +++ packages/vite-plugin/package.json | 4 +- packages/vite-plugin/src/index.ts | 39 ++++++++++++++++++++ packages/vite-plugin/src/middleware.ts | 6 +-- packages/vite-plugin/src/transform.ts | 17 ++++++++- packages/vite-plugin/test/middleware.test.ts | 6 +++ packages/vite-plugin/test/transform.test.ts | 4 +- packages/vite-plugin/tsconfig.json | 2 +- pnpm-lock.yaml | 4 ++ 13 files changed, 82 insertions(+), 20 deletions(-) delete mode 100644 examples/blog/index.html rename examples/blog/src/{pages => }/about.astro (100%) rename examples/blog/src/{pages => }/index.astro (100%) diff --git a/examples/blog/index.html b/examples/blog/index.html deleted file mode 100644 index 5455d48..0000000 --- a/examples/blog/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Minimal Astro Blog - - -

    Minimal Astro Blog

    -

    This is a placeholder for the blog example. It will be implemented in later phases.

    - - \ No newline at end of file diff --git a/examples/blog/package.json b/examples/blog/package.json index ece0e4f..c7be488 100644 --- a/examples/blog/package.json +++ b/examples/blog/package.json @@ -8,6 +8,9 @@ "build": "vite build", "preview": "vite preview" }, + "dependencies": { + "@minimal-astro/compiler": "workspace:*" + }, "devDependencies": { "@minimal-astro/vite-plugin": "workspace:*", "vite": "^5.4.11" diff --git a/examples/blog/src/pages/about.astro b/examples/blog/src/about.astro similarity index 100% rename from examples/blog/src/pages/about.astro rename to examples/blog/src/about.astro diff --git a/examples/blog/src/pages/index.astro b/examples/blog/src/index.astro similarity index 100% rename from examples/blog/src/pages/index.astro rename to examples/blog/src/index.astro diff --git a/examples/blog/vite.config.ts b/examples/blog/vite.config.ts index 82945b3..5adf966 100644 --- a/examples/blog/vite.config.ts +++ b/examples/blog/vite.config.ts @@ -3,4 +3,9 @@ import { defineConfig } from 'vite' export default defineConfig({ plugins: [vitePluginAstro()], + build: { + rollupOptions: { + input: './src/index.astro', + }, + }, }) diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index 447bd80..4dc7b79 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -5,8 +5,8 @@ "description": "Vite plugin for transforming .astro files", "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" + "types": "./dist/vite-plugin/src/index.d.ts", + "import": "./dist/vite-plugin/src/index.js" } }, "files": ["dist"], diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index b6a0a85..71b6395 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -52,6 +52,45 @@ export function vitePluginAstro(): Plugin { } }, + /** + * Resolve .astro file imports + */ + async resolveId(id: string, importer) { + // Handle .astro imports + if (id.endsWith('.astro')) { + // If it's already an absolute path, return it + if (id.startsWith('/')) { + return id + } + + // Resolve relative imports + if (importer) { + const { resolve } = await import('node:path') + const { dirname } = await import('node:path') + return resolve(dirname(importer), id) + } + + // For entry points, resolve from root + const { resolve } = await import('node:path') + return resolve(id) + } + return null + }, + + /** + * Load .astro files + */ + async load(id: string) { + if (!id.endsWith('.astro')) { + return null + } + + // Read the file + const { readFileSync } = await import('node:fs') + const code = readFileSync(id, 'utf-8') + return code + }, + /** * Transform hook for processing .astro files */ diff --git a/packages/vite-plugin/src/middleware.ts b/packages/vite-plugin/src/middleware.ts index f2e55d4..9a407c3 100644 --- a/packages/vite-plugin/src/middleware.ts +++ b/packages/vite-plugin/src/middleware.ts @@ -25,7 +25,7 @@ export function resolveAstroFile(url: string, root: string): string | null { // Handle root path if (cleanUrl === '/' || cleanUrl === '') { - const indexPath = join(root, 'src/pages/index.astro') + const indexPath = join(root, 'src/index.astro') return existsSync(indexPath) ? indexPath : null } @@ -33,8 +33,8 @@ export function resolveAstroFile(url: string, root: string): string | null { const urlPath = cleanUrl.startsWith('/') ? cleanUrl.slice(1) : cleanUrl const astroPath = urlPath.endsWith('.astro') ? urlPath : `${urlPath}.astro` - // Try to find the file in src/pages - const filePath = join(root, 'src/pages', astroPath) + // Try to find the file in src directory + const filePath = join(root, 'src', astroPath) return existsSync(filePath) ? filePath : null } diff --git a/packages/vite-plugin/src/transform.ts b/packages/vite-plugin/src/transform.ts index cad277f..5e3acea 100644 --- a/packages/vite-plugin/src/transform.ts +++ b/packages/vite-plugin/src/transform.ts @@ -73,7 +73,17 @@ function elementToTemplate(node: ElementNode): string { const expr = child.value if (expr.includes('.map') && expr.includes('<')) { // Transform JSX-like expressions to template strings - // This is a simplified approach - in production we'd use proper JSX transform + // Extract the map callback and transform it + const mapMatch = expr.match(/\.map\s*\(\s*(\w+)\s*=>\s*<(\w+)>([^<]*)<\/\2>\s*\)/) + if (mapMatch) { + const [, itemVar, tagName, content] = mapMatch + const itemExpr = content.replace( + new RegExp(`\\{${itemVar}\\}`, 'g'), + `\${escapeHtml(String(${itemVar}))}`, + ) + return `\${${expr.split('.map')[0]}.map(${itemVar} => \`<${tagName}>${itemExpr}\`).join('')}` + } + // Fallback for other JSX patterns return `\${${expr}.join('')}` } } @@ -108,6 +118,11 @@ function extractFrontmatter(ast: AstroAST): string { * @returns Transformed JavaScript code and optional source map */ export function transformAstro(code: string, id: string): { code: string; map: null } { + // Skip if already transformed + if (code.includes('// Transformed from:') && code.includes('export default function render()')) { + return { code, map: null } + } + // Parse the .astro file into an AST const ast = parse(code) diff --git a/packages/vite-plugin/test/middleware.test.ts b/packages/vite-plugin/test/middleware.test.ts index 31fea16..a49f569 100644 --- a/packages/vite-plugin/test/middleware.test.ts +++ b/packages/vite-plugin/test/middleware.test.ts @@ -20,6 +20,12 @@ describe('SSR Middleware', () => { }, plugins: [vitePluginAstro()], logLevel: 'error', + resolve: { + conditions: ['development'], + }, + ssr: { + external: ['@minimal-astro/compiler'], + }, }) // Wait a bit for the middleware to be registered diff --git a/packages/vite-plugin/test/transform.test.ts b/packages/vite-plugin/test/transform.test.ts index 11ad6f7..51d05ea 100644 --- a/packages/vite-plugin/test/transform.test.ts +++ b/packages/vite-plugin/test/transform.test.ts @@ -49,7 +49,9 @@ const items = ['Apple', 'Banana', 'Orange'] expect(result.code).toContain('export default function render()') expect(result.code).toContain("const items = ['Apple', 'Banana', 'Orange']") expect(result.code).toContain('Fruit List') - expect(result.code).toContain('${items.map(item =>
  • {item}
  • ).join(') + expect(result.code).toContain( + "${items.map(item => `
  • ${escapeHtml(String(item))}
  • `).join('')", + ) }) it('空の.astroファイルの変換', () => { diff --git a/packages/vite-plugin/tsconfig.json b/packages/vite-plugin/tsconfig.json index 4868609..ba0158c 100644 --- a/packages/vite-plugin/tsconfig.json +++ b/packages/vite-plugin/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "./dist", "noEmit": false, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d03c0d3..963eb28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,6 +28,10 @@ importers: version: 2.1.9(@types/node@20.19.9) examples/blog: + dependencies: + '@minimal-astro/compiler': + specifier: workspace:* + version: link:../../packages/compiler devDependencies: '@minimal-astro/vite-plugin': specifier: workspace:* From 077eaa3ae2917b67519bc4674846bc9d74b2d3ad Mon Sep 17 00:00:00 2001 From: jp-knj Date: Tue, 29 Jul 2025 10:52:44 +0900 Subject: [PATCH 5/7] fix: parser not capturing attribute values - Fixed parser to correctly handle HTML_ATTRIBUTE_VALUE tokens - Removed unnecessary equals sign checking logic - Attribute values like href='/about' now parse correctly - All existing tests continue to pass --- packages/compiler/src/parser/parser.js | 29 ++++++++--------------- packages/compiler/src/parser/parser.ts | 32 ++++++++------------------ packages/vite-plugin/src/index.ts | 4 ++-- 3 files changed, 22 insertions(+), 43 deletions(-) diff --git a/packages/compiler/src/parser/parser.js b/packages/compiler/src/parser/parser.js index 044a1e5..74d7b08 100644 --- a/packages/compiler/src/parser/parser.js +++ b/packages/compiler/src/parser/parser.js @@ -118,25 +118,16 @@ function parseElement(state) { const attrName = attrResult.token.value currentState = attrResult.state let attrValue = '' - // Skip whitespace and equals - while (peek(currentState).type === 'TEXT' && peek(currentState).value.trim() === '') { - currentState = advance(currentState).state - } - if (peek(currentState).value === '=') { - currentState = advance(currentState).state // Skip = - // Skip whitespace - while (peek(currentState).type === 'TEXT' && peek(currentState).value.trim() === '') { - currentState = advance(currentState).state - } - if (peek(currentState).type === 'HTML_ATTRIBUTE_VALUE') { - const valueResult = advance(currentState) - attrValue = valueResult.token.value - currentState = valueResult.state - } else if (peek(currentState).type === 'EXPRESSION_START') { - const exprResult = parseExpression(currentState) - attrValue = exprResult.node - currentState = exprResult.state - } + // Check if next token is the attribute value + if (peek(currentState).type === 'HTML_ATTRIBUTE_VALUE') { + const valueResult = advance(currentState) + attrValue = valueResult.token.value + currentState = valueResult.state + } else if (peek(currentState).type === 'EXPRESSION_START') { + // Handle dynamic attribute values like class={className} + const exprResult = parseExpression(currentState) + attrValue = exprResult.node + currentState = exprResult.state } attributes.push({ name: attrName, value: attrValue }) } else { diff --git a/packages/compiler/src/parser/parser.ts b/packages/compiler/src/parser/parser.ts index 222fc71..161436c 100644 --- a/packages/compiler/src/parser/parser.ts +++ b/packages/compiler/src/parser/parser.ts @@ -153,28 +153,16 @@ function parseElement(state: ParserState): { node: ElementNode; state: ParserSta let attrValue: string | ExpressionNode = '' - // Skip whitespace and equals - while (peek(currentState).type === 'TEXT' && peek(currentState).value.trim() === '') { - currentState = advance(currentState).state - } - - if (peek(currentState).value === '=') { - currentState = advance(currentState).state // Skip = - - // Skip whitespace - while (peek(currentState).type === 'TEXT' && peek(currentState).value.trim() === '') { - currentState = advance(currentState).state - } - - if (peek(currentState).type === 'HTML_ATTRIBUTE_VALUE') { - const valueResult = advance(currentState) - attrValue = valueResult.token.value - currentState = valueResult.state - } else if (peek(currentState).type === 'EXPRESSION_START') { - const exprResult = parseExpression(currentState) - attrValue = exprResult.node as ExpressionNode - currentState = exprResult.state - } + // Check if next token is the attribute value + if (peek(currentState).type === 'HTML_ATTRIBUTE_VALUE') { + const valueResult = advance(currentState) + attrValue = valueResult.token.value + currentState = valueResult.state + } else if (peek(currentState).type === 'EXPRESSION_START') { + // Handle dynamic attribute values like class={className} + const exprResult = parseExpression(currentState) + attrValue = exprResult.node as ExpressionNode + currentState = exprResult.state } attributes.push({ name: attrName, value: attrValue }) diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index 71b6395..5cbdb42 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -62,14 +62,14 @@ export function vitePluginAstro(): Plugin { if (id.startsWith('/')) { return id } - + // Resolve relative imports if (importer) { const { resolve } = await import('node:path') const { dirname } = await import('node:path') return resolve(dirname(importer), id) } - + // For entry points, resolve from root const { resolve } = await import('node:path') return resolve(id) From c3fe43efec4380c23869cf60c1c1fb22143048e7 Mon Sep 17 00:00:00 2001 From: jp-knj Date: Tue, 29 Jul 2025 10:57:15 +0900 Subject: [PATCH 6/7] chore: remove build artifacts from git tracking - Added *.js, *.js.map, *.d.ts, *.d.ts.map patterns to .gitignore for src directories - Removed all existing tracked build artifacts from packages/*/src/ - These files are generated during build and should not be in version control --- .gitignore | 5 +- .../src/html-builder/builder.d.ts.map | 1 - packages/compiler/src/html-builder/builder.js | 96 ------ .../compiler/src/html-builder/builder.js.map | 1 - .../compiler/src/html-builder/escape.d.ts.map | 1 - packages/compiler/src/html-builder/escape.js | 40 --- .../compiler/src/html-builder/escape.js.map | 1 - .../compiler/src/html-builder/index.d.ts.map | 1 - packages/compiler/src/html-builder/index.js | 12 - .../compiler/src/html-builder/index.js.map | 1 - .../src/html-builder/serializer.d.ts.map | 1 - .../compiler/src/html-builder/serializer.js | 104 ------- .../src/html-builder/serializer.js.map | 1 - packages/compiler/src/index.d.ts.map | 1 - packages/compiler/src/index.js | 4 - packages/compiler/src/index.js.map | 1 - packages/compiler/src/parser/ast.d.ts.map | 1 - packages/compiler/src/parser/ast.js | 45 --- packages/compiler/src/parser/ast.js.map | 1 - packages/compiler/src/parser/index.d.ts.map | 1 - packages/compiler/src/parser/index.js | 11 - packages/compiler/src/parser/index.js.map | 1 - packages/compiler/src/parser/parser.d.ts.map | 1 - packages/compiler/src/parser/parser.js | 273 ------------------ packages/compiler/src/parser/parser.js.map | 1 - packages/compiler/src/parser/utils.d.ts.map | 1 - packages/compiler/src/parser/utils.js | 181 ------------ packages/compiler/src/parser/utils.js.map | 1 - .../compiler/src/tokenizer/index.d.ts.map | 1 - packages/compiler/src/tokenizer/index.js | 2 - packages/compiler/src/tokenizer/index.js.map | 1 - .../compiler/src/tokenizer/tokenizer.d.ts.map | 1 - packages/compiler/src/tokenizer/tokenizer.js | 186 ------------ .../compiler/src/tokenizer/tokenizer.js.map | 1 - .../compiler/src/tokenizer/types.d.ts.map | 1 - packages/compiler/src/tokenizer/types.js | 2 - packages/compiler/src/tokenizer/types.js.map | 1 - 37 files changed, 4 insertions(+), 981 deletions(-) delete mode 100644 packages/compiler/src/html-builder/builder.d.ts.map delete mode 100644 packages/compiler/src/html-builder/builder.js delete mode 100644 packages/compiler/src/html-builder/builder.js.map delete mode 100644 packages/compiler/src/html-builder/escape.d.ts.map delete mode 100644 packages/compiler/src/html-builder/escape.js delete mode 100644 packages/compiler/src/html-builder/escape.js.map delete mode 100644 packages/compiler/src/html-builder/index.d.ts.map delete mode 100644 packages/compiler/src/html-builder/index.js delete mode 100644 packages/compiler/src/html-builder/index.js.map delete mode 100644 packages/compiler/src/html-builder/serializer.d.ts.map delete mode 100644 packages/compiler/src/html-builder/serializer.js delete mode 100644 packages/compiler/src/html-builder/serializer.js.map delete mode 100644 packages/compiler/src/index.d.ts.map delete mode 100644 packages/compiler/src/index.js delete mode 100644 packages/compiler/src/index.js.map delete mode 100644 packages/compiler/src/parser/ast.d.ts.map delete mode 100644 packages/compiler/src/parser/ast.js delete mode 100644 packages/compiler/src/parser/ast.js.map delete mode 100644 packages/compiler/src/parser/index.d.ts.map delete mode 100644 packages/compiler/src/parser/index.js delete mode 100644 packages/compiler/src/parser/index.js.map delete mode 100644 packages/compiler/src/parser/parser.d.ts.map delete mode 100644 packages/compiler/src/parser/parser.js delete mode 100644 packages/compiler/src/parser/parser.js.map delete mode 100644 packages/compiler/src/parser/utils.d.ts.map delete mode 100644 packages/compiler/src/parser/utils.js delete mode 100644 packages/compiler/src/parser/utils.js.map delete mode 100644 packages/compiler/src/tokenizer/index.d.ts.map delete mode 100644 packages/compiler/src/tokenizer/index.js delete mode 100644 packages/compiler/src/tokenizer/index.js.map delete mode 100644 packages/compiler/src/tokenizer/tokenizer.d.ts.map delete mode 100644 packages/compiler/src/tokenizer/tokenizer.js delete mode 100644 packages/compiler/src/tokenizer/tokenizer.js.map delete mode 100644 packages/compiler/src/tokenizer/types.d.ts.map delete mode 100644 packages/compiler/src/tokenizer/types.js delete mode 100644 packages/compiler/src/tokenizer/types.js.map diff --git a/.gitignore b/.gitignore index bf8b7c7..c9c0576 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,11 @@ coverage/ .next docs/ -# prevent declaration files in src directories +# prevent build artifacts in src directories +packages/*/src/**/*.js +packages/*/src/**/*.js.map packages/*/src/**/*.d.ts +packages/*/src/**/*.d.ts.map # cache .turbo diff --git a/packages/compiler/src/html-builder/builder.d.ts.map b/packages/compiler/src/html-builder/builder.d.ts.map deleted file mode 100644 index cc15f8b..0000000 --- a/packages/compiler/src/html-builder/builder.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,kBAAkB,CAAA;AA0E3D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAM/C"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/builder.js b/packages/compiler/src/html-builder/builder.js deleted file mode 100644 index 1998924..0000000 --- a/packages/compiler/src/html-builder/builder.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * AST to HTML builder - * - * This module provides the main entry point for converting - * an Astro AST into an HTML string. - * - * @module builder - */ -import { serializeElement, serializeText } from './serializer.js' -/** - * Set of HTML void elements that are self-closing - */ -const VOID_ELEMENTS = new Set([ - 'img', - 'br', - 'hr', - 'input', - 'area', - 'base', - 'col', - 'embed', - 'link', - 'meta', - 'source', - 'track', - 'wbr', -]) -/** - * Checks if an element is a void element - */ -function isVoidElement(tagName) { - return VOID_ELEMENTS.has(tagName.toLowerCase()) -} -/** - * Builds an HTML string from an AST node - * - * Recursively processes AST nodes and converts them to HTML. - * Handles different node types appropriately: - * - Text nodes are escaped for safe HTML rendering - * - Element nodes are serialized with attributes and children - * - Container nodes (Program, Template) concatenate their children - * - Other nodes (Frontmatter, Expression) are skipped or handled specially - * - * @param node - The AST node to convert - * @returns HTML string representation - */ -function buildNode(node) { - switch (node.type) { - case 'Text': - return serializeText(node) - case 'Element': - // Check if it's a void element and update selfClosing accordingly - if (isVoidElement(node.name)) { - return serializeElement({ ...node, selfClosing: true }) - } - return serializeElement(node) - case 'Expression': - // Expressions in the template should be rendered as-is for now - // In a full implementation, these would be evaluated - return `{${node.value}}` - case 'Program': - case 'Template': - // Container nodes: concatenate all children - return node.children.map(child => buildNode(child)).join('') - case 'Frontmatter': - // Frontmatter is not rendered in the HTML output - return '' - default: - // Unknown node types are ignored - return '' - } -} -/** - * Builds an HTML string from an Astro AST - * - * This is the main entry point for converting a parsed Astro AST - * into HTML. It processes the entire AST tree and returns the - * resulting HTML string. - * - * @param ast - The Astro AST to convert - * @returns Complete HTML string - * - * @example - * ```typescript - * const ast = parse('

    Hello World

    ') - * const html = buildHTML(ast) - * console.log(html) // '

    Hello World

    ' - * ``` - */ -export function buildHTML(ast) { - if (!ast || !ast.children || ast.children.length === 0) { - return '' - } - return ast.children.map(child => buildNode(child)).join('') -} -//# sourceMappingURL=builder.js.map diff --git a/packages/compiler/src/html-builder/builder.js.map b/packages/compiler/src/html-builder/builder.js.map deleted file mode 100644 index fd07272..0000000 --- a/packages/compiler/src/html-builder/builder.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"builder.js","sourceRoot":"","sources":["builder.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEjE;;GAEG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,MAAM;IACN,MAAM;IACN,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,QAAQ;IACR,OAAO;IACP,KAAK;CACN,CAAC,CAAA;AAEF;;GAEG;AACH,SAAS,aAAa,CAAC,OAAe;IACpC,OAAO,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;AACjD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,SAAS,CAAC,IAAe;IAChC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;QAE5B,KAAK,SAAS;YACZ,kEAAkE;YAClE,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,OAAO,gBAAgB,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;YACzD,CAAC;YACD,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAE/B,KAAK,YAAY;YACf,+DAA+D;YAC/D,qDAAqD;YACrD,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,CAAA;QAE1B,KAAK,SAAS,CAAC;QACf,KAAK,UAAU;YACb,4CAA4C;YAC5C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAE9D,KAAK,aAAa;YAChB,iDAAiD;YACjD,OAAO,EAAE,CAAA;QAEX;YACE,iCAAiC;YACjC,OAAO,EAAE,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS,CAAC,GAAa;IACrC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,OAAO,EAAE,CAAA;IACX,CAAC;IAED,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC7D,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/escape.d.ts.map b/packages/compiler/src/html-builder/escape.d.ts.map deleted file mode 100644 index 8c96729..0000000 --- a/packages/compiler/src/html-builder/escape.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["escape.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/escape.js b/packages/compiler/src/html-builder/escape.js deleted file mode 100644 index 8142ea4..0000000 --- a/packages/compiler/src/html-builder/escape.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * HTML escape utilities for safe text rendering - * - * This module provides functions to escape special HTML characters - * to prevent XSS attacks and ensure proper rendering of text content. - * - * @module escape - */ -const ESCAPE_MAP = { - '<': '<', - '>': '>', - '&': '&', - '"': '"', - "'": ''', -} -const ESCAPE_REGEX = /[<>&"']/g -/** - * Escapes special HTML characters in text - * - * Converts characters that have special meaning in HTML (<, >, &, ", ') - * to their corresponding HTML entities to prevent XSS and ensure - * proper rendering. - * - * @param text - The text to escape - * @returns The escaped text safe for HTML rendering - * - * @example - * ```typescript - * escapeHtml('') - * // Returns: '<script>alert("XSS")</script>' - * - * escapeHtml('Tom & Jerry') - * // Returns: 'Tom & Jerry' - * ``` - */ -export function escapeHtml(text) { - if (!text) return text - return text.replace(ESCAPE_REGEX, char => ESCAPE_MAP[char]) -} -//# sourceMappingURL=escape.js.map diff --git a/packages/compiler/src/html-builder/escape.js.map b/packages/compiler/src/html-builder/escape.js.map deleted file mode 100644 index 67912c4..0000000 --- a/packages/compiler/src/html-builder/escape.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"escape.js","sourceRoot":"","sources":["escape.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,UAAU,GAA2B;IACzC,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,QAAQ;CACd,CAAA;AAED,MAAM,YAAY,GAAG,UAAU,CAAA;AAE/B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;AAC7D,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/index.d.ts.map b/packages/compiler/src/html-builder/index.d.ts.map deleted file mode 100644 index cc284f7..0000000 --- a/packages/compiler/src/html-builder/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/index.js b/packages/compiler/src/html-builder/index.js deleted file mode 100644 index 4b91f2f..0000000 --- a/packages/compiler/src/html-builder/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * HTML Builder module exports - * - * This module provides utilities for converting AST nodes to HTML strings - * with proper escaping and formatting. - * - * @module html-builder - */ -export { escapeHtml } from './escape.js' -export { serializeText, serializeElement } from './serializer.js' -export { buildHTML } from './builder.js' -//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/html-builder/index.js.map b/packages/compiler/src/html-builder/index.js.map deleted file mode 100644 index 2dcffd2..0000000 --- a/packages/compiler/src/html-builder/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/serializer.d.ts.map b/packages/compiler/src/html-builder/serializer.d.ts.map deleted file mode 100644 index f0a75ca..0000000 --- a/packages/compiler/src/html-builder/serializer.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAa,WAAW,EAAkB,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGxF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEpD;AAwBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAW1D"} \ No newline at end of file diff --git a/packages/compiler/src/html-builder/serializer.js b/packages/compiler/src/html-builder/serializer.js deleted file mode 100644 index 7f736bc..0000000 --- a/packages/compiler/src/html-builder/serializer.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * AST to HTML serialization utilities - * - * This module provides functions to convert AST nodes into HTML strings, - * handling proper escaping and formatting for safe rendering. - * - * @module serializer - */ -import { escapeHtml } from './escape.js' -/** - * Serializes a text node to HTML - * - * Converts a TextNode to an HTML-safe string by escaping - * special characters to prevent XSS attacks. - * - * @param node - The text node to serialize - * @returns HTML-escaped text content - * - * @example - * ```typescript - * serializeText({ type: 'Text', value: 'Hello ' }) - * // Returns: 'Hello <world>' - * ``` - */ -export function serializeText(node) { - return escapeHtml(node.value) -} -/** - * Serializes attributes to HTML attribute string - * - * @param attributes - Array of element attributes - * @returns Formatted attribute string - */ -function serializeAttributes(attributes) { - if (!attributes.length) return '' - const attrStrings = attributes.map(attr => { - // For expression values, we render them as-is (without escaping) - // since they represent dynamic JavaScript expressions - const value = typeof attr.value === 'string' ? escapeHtml(attr.value) : `{${attr.value.value}}` - return `${attr.name}="${value}"` - }) - return ` ${attrStrings.join(' ')}` -} -/** - * Serializes an element node to HTML - * - * Converts an ElementNode to an HTML string, including: - * - Opening and closing tags - * - Attributes with proper escaping - * - Recursively serialized child nodes - * - Self-closing tag handling - * - * @param node - The element node to serialize - * @returns Complete HTML element string - * - * @example - * ```typescript - * serializeElement({ - * type: 'Element', - * name: 'div', - * attributes: [{ name: 'class', value: 'container' }], - * children: [{ type: 'Text', value: 'Hello' }], - * selfClosing: false - * }) - * // Returns: '
    Hello
    ' - * ``` - */ -export function serializeElement(node) { - const { name, attributes, children, selfClosing } = node - const attrString = serializeAttributes(attributes) - if (selfClosing) { - return `<${name}${attrString} />` - } - const childrenHtml = children.map(child => serializeNode(child)).join('') - return `<${name}${attrString}>${childrenHtml}` -} -/** - * Serializes any AST node to HTML - * - * This is a dispatcher function that routes different node types - * to their appropriate serialization functions. - * - * @param node - The AST node to serialize - * @returns HTML string representation of the node - */ -function serializeNode(node) { - switch (node.type) { - case 'Text': - return serializeText(node) - case 'Element': - return serializeElement(node) - case 'Expression': - // Expressions in the template are rendered as-is - return `{${node.value}}` - case 'Template': - // Template nodes are containers, serialize their children - return node.children.map(child => serializeNode(child)).join('') - default: - // Skip nodes that shouldn't be serialized to HTML - // (e.g., Frontmatter, Program) - return '' - } -} -//# sourceMappingURL=serializer.js.map diff --git a/packages/compiler/src/html-builder/serializer.js.map b/packages/compiler/src/html-builder/serializer.js.map deleted file mode 100644 index a9e3bc7..0000000 --- a/packages/compiler/src/html-builder/serializer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"serializer.js","sourceRoot":"","sources":["serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC/B,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAC1B,UAAmE;IAEnE,IAAI,CAAC,UAAU,CAAC,MAAM;QAAE,OAAO,EAAE,CAAA;IAEjC,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;QACxC,iEAAiE;QACjE,sDAAsD;QACtD,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAA;QAE/F,OAAO,GAAG,IAAI,CAAC,IAAI,KAAK,KAAK,GAAG,CAAA;IAClC,CAAC,CAAC,CAAA;IAEF,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;AACpC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI,CAAA;IACxD,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAA;IAElD,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,IAAI,IAAI,GAAG,UAAU,KAAK,CAAA;IACnC,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEzE,OAAO,IAAI,IAAI,GAAG,UAAU,IAAI,YAAY,KAAK,IAAI,GAAG,CAAA;AAC1D,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,IAAe;IACpC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;QAC5B,KAAK,SAAS;YACZ,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC/B,KAAK,YAAY;YACf,iDAAiD;YACjD,OAAO,IAAI,IAAI,CAAC,KAAK,GAAG,CAAA;QAC1B,KAAK,UAAU;YACb,0DAA0D;YAC1D,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAClE;YACE,kDAAkD;YAClD,+BAA+B;YAC/B,OAAO,EAAE,CAAA;IACb,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/index.d.ts.map b/packages/compiler/src/index.d.ts.map deleted file mode 100644 index 4685376..0000000 --- a/packages/compiler/src/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAEhG,YAAY,EACV,QAAQ,EACR,SAAS,EACT,eAAe,EACf,WAAW,EACX,QAAQ,EACR,cAAc,GACf,MAAM,mBAAmB,CAAA;AAE1B,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/index.js b/packages/compiler/src/index.js deleted file mode 100644 index defb600..0000000 --- a/packages/compiler/src/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { parse } from './parser/index.js' -export { tokenize } from './tokenizer/index.js' -export { escapeHtml, serializeText, serializeElement, buildHTML } from './html-builder/index.js' -//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/index.js.map b/packages/compiler/src/index.js.map deleted file mode 100644 index dce9b8f..0000000 --- a/packages/compiler/src/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/ast.d.ts.map b/packages/compiler/src/parser/ast.d.ts.map deleted file mode 100644 index 5b36a53..0000000 --- a/packages/compiler/src/parser/ast.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["ast.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,qCAAqC;IACrC,QAAQ,EAAE,SAAS,EAAE,CAAA;CACtB;AAED;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GACjB,QAAQ,GACR,eAAe,GACf,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,cAAc,CAAA;AAElB;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,IAAI,EAAE,aAAa,CAAA;IACnB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,IAAI,EAAE,UAAU,CAAA;IAChB,sCAAsC;IACtC,QAAQ,EAAE,SAAS,EAAE,CAAA;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,2BAA2B;IAC3B,IAAI,EAAE,SAAS,CAAA;IACf,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAA;IACZ,kCAAkC;IAClC,UAAU,EAAE,KAAK,CAAC;QAChB,qBAAqB;QACrB,IAAI,EAAE,MAAM,CAAA;QACZ,mEAAmE;QACnE,KAAK,EAAE,MAAM,GAAG,cAAc,CAAA;KAC/B,CAAC,CAAA;IACF,6CAA6C;IAC7C,QAAQ,EAAE,SAAS,EAAE,CAAA;IACrB,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,QAAQ;IACvB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc;IAC7B,2BAA2B;IAC3B,IAAI,EAAE,YAAY,CAAA;IAClB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,QAAQ,CAE5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,eAAe,CAE1E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,YAAY,CAEpE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,WAAW,CAElE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,QAAQ,CAE5D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,IAAI,cAAc,CAExE"} \ No newline at end of file diff --git a/packages/compiler/src/parser/ast.js b/packages/compiler/src/parser/ast.js deleted file mode 100644 index d6e14ef..0000000 --- a/packages/compiler/src/parser/ast.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * AST (Abstract Syntax Tree) type definitions for Astro templates - * - * These types represent the structure of parsed .astro files, - * converting raw tokens into a hierarchical tree structure. - * - * @module ast - */ -/** - * Type guard to check if a node is an AstroAST (Program) node - */ -export function isAstroAST(node) { - return node.type === 'Program' -} -/** - * Type guard to check if a node is a FrontmatterNode - */ -export function isFrontmatterNode(node) { - return node.type === 'Frontmatter' -} -/** - * Type guard to check if a node is a TemplateNode - */ -export function isTemplateNode(node) { - return node.type === 'Template' -} -/** - * Type guard to check if a node is an ElementNode - */ -export function isElementNode(node) { - return node.type === 'Element' -} -/** - * Type guard to check if a node is a TextNode - */ -export function isTextNode(node) { - return node.type === 'Text' -} -/** - * Type guard to check if a node is an ExpressionNode - */ -export function isExpressionNode(node) { - return node.type === 'Expression' -} -//# sourceMappingURL=ast.js.map diff --git a/packages/compiler/src/parser/ast.js.map b/packages/compiler/src/parser/ast.js.map deleted file mode 100644 index ba9c874..0000000 --- a/packages/compiler/src/parser/ast.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ast.js","sourceRoot":"","sources":["ast.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA4IH;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAA;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAe;IAC/C,OAAO,IAAI,CAAC,IAAI,KAAK,aAAa,CAAA;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAAe;IAC5C,OAAO,IAAI,CAAC,IAAI,KAAK,UAAU,CAAA;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAA;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM,CAAA;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAe;IAC9C,OAAO,IAAI,CAAC,IAAI,KAAK,YAAY,CAAA;AACnC,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/parser/index.d.ts.map b/packages/compiler/src/parser/index.d.ts.map deleted file mode 100644 index 26f99e1..0000000 --- a/packages/compiler/src/parser/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAChD,YAAY,EACV,QAAQ,EACR,SAAS,EACT,eAAe,EACf,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,cAAc,GACf,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,UAAU,EACV,gBAAgB,GACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/index.js b/packages/compiler/src/parser/index.js deleted file mode 100644 index 3013408..0000000 --- a/packages/compiler/src/parser/index.js +++ /dev/null @@ -1,11 +0,0 @@ -export { parse } from './parser.js' -export { walk, walkAsync, is } from './utils.js' -export { - isAstroAST, - isFrontmatterNode, - isTemplateNode, - isElementNode, - isTextNode, - isExpressionNode, -} from './ast.js' -//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/parser/index.js.map b/packages/compiler/src/parser/index.js.map deleted file mode 100644 index 0386734..0000000 --- a/packages/compiler/src/parser/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAUhD,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,UAAU,EACV,gBAAgB,GACjB,MAAM,UAAU,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/parser.d.ts.map b/packages/compiler/src/parser/parser.d.ts.map deleted file mode 100644 index 01f36ed..0000000 --- a/packages/compiler/src/parser/parser.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAA0C,MAAM,UAAU,CAAA;AAUhF;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAS9C"} \ No newline at end of file diff --git a/packages/compiler/src/parser/parser.js b/packages/compiler/src/parser/parser.js deleted file mode 100644 index 74d7b08..0000000 --- a/packages/compiler/src/parser/parser.js +++ /dev/null @@ -1,273 +0,0 @@ -import { tokenize } from '../tokenizer/index.js' -/** - * Parse Astro source code into an Abstract Syntax Tree (AST) - * - * @param source - The Astro source code to parse - * @returns The root AST node (Program) - * - * @example - * ```typescript - * const ast = parse('
    Hello {name}
    ') - * console.log(ast) // { type: 'Program', children: [...] } - * ``` - */ -export function parse(source) { - const tokens = tokenize(source) - const initialState = { tokens, current: 0 } - const { children } = parseProgram(initialState) - return { - type: 'Program', - children, - } -} -/** - * Parse the entire program by collecting all top-level nodes - * - * @param initialState - The initial parser state - * @returns An object containing the parsed children and final state - */ -function parseProgram(initialState) { - const children = [] - let state = initialState - while (!isAtEnd(state)) { - const { node, state: nextState } = parseNode(state) - if (node) { - children.push(node) - } - state = nextState - } - return { children, state } -} -/** - * Parse a single node based on the current token - * - * @param state - The current parser state - * @returns An object containing the parsed node (or null) and new state - */ -function parseNode(state) { - const token = peek(state) - if (token.type === 'FRONTMATTER_START') { - return parseFrontmatter(state) - } - if (token.type === 'HTML_TAG_OPEN') { - return parseElement(state) - } - if (token.type === 'HTML_TAG_CLOSE' && peek(state, 1)?.type === 'HTML_TAG_NAME') { - // Skip closing tags, they're handled in parseElement - const s1 = advance(state) // - return { node: null, state: s3.state } - } - if (token.type === 'EXPRESSION_START') { - return parseExpression(state) - } - if (token.type === 'TEXT') { - return parseText(state) - } - if (token.type === 'EOF') { - return { node: null, state } - } - // Skip unknown tokens - return { node: null, state: advance(state).state } -} -/** - * Parse frontmatter block (JavaScript/TypeScript code between --- markers) - * - * @param state - The current parser state - * @returns An object containing the parsed frontmatter node and new state - */ -function parseFrontmatter(state) { - const { state: s1 } = consume(state, 'FRONTMATTER_START') - let value = '' - let currentState = s1 - while (peek(currentState).type === 'FRONTMATTER_CONTENT') { - const result = advance(currentState) - value += result.token.value - currentState = result.state - } - const { state: finalState } = consume(currentState, 'FRONTMATTER_END') - return { - node: { - type: 'Frontmatter', - value: value.trim(), - }, - state: finalState, - } -} -/** - * Parse HTML/JSX element including attributes and children - * - * @param state - The current parser state - * @returns An object containing the parsed element node and new state - */ -function parseElement(state) { - const { state: s1 } = consume(state, 'HTML_TAG_OPEN') - const { token: nameToken, state: s2 } = consume(s1, 'HTML_TAG_NAME') - const name = nameToken.value - const attributes = [] - let currentState = s2 - // Parse attributes - while ( - peek(currentState).type !== 'HTML_TAG_CLOSE' && - peek(currentState).type !== 'HTML_TAG_SELF_CLOSE' && - !isAtEnd(currentState) - ) { - if (peek(currentState).type === 'HTML_ATTRIBUTE_NAME') { - const attrResult = advance(currentState) - const attrName = attrResult.token.value - currentState = attrResult.state - let attrValue = '' - // Check if next token is the attribute value - if (peek(currentState).type === 'HTML_ATTRIBUTE_VALUE') { - const valueResult = advance(currentState) - attrValue = valueResult.token.value - currentState = valueResult.state - } else if (peek(currentState).type === 'EXPRESSION_START') { - // Handle dynamic attribute values like class={className} - const exprResult = parseExpression(currentState) - attrValue = exprResult.node - currentState = exprResult.state - } - attributes.push({ name: attrName, value: attrValue }) - } else { - currentState = advance(currentState).state // Skip unknown tokens in tag - } - } - const selfClosing = peek(currentState).type === 'HTML_TAG_SELF_CLOSE' - currentState = advance(currentState).state // Consume > or /> - const children = [] - if (!selfClosing) { - // Parse children until closing tag - while (!isAtEnd(currentState)) { - if ( - peek(currentState).type === 'HTML_TAG_CLOSE' && - peek(currentState, 1)?.type === 'HTML_TAG_NAME' && - peek(currentState, 1)?.value === name - ) { - break - } - const childResult = parseNode(currentState) - if (childResult.node) { - children.push(childResult.node) - } - currentState = childResult.state - } - } - return { - node: { - type: 'Element', - name, - attributes, - children, - selfClosing, - }, - state: currentState, - } -} -/** - * Parse JavaScript expression within curly braces { } - * - * @param state - The current parser state - * @returns An object containing the parsed expression node and new state - */ -function parseExpression(state) { - const { state: s1 } = consume(state, 'EXPRESSION_START') - let value = '' - let currentState = s1 - while (peek(currentState).type === 'EXPRESSION_CONTENT') { - const result = advance(currentState) - value += result.token.value - currentState = result.state - } - const { state: finalState } = consume(currentState, 'EXPRESSION_END') - return { - node: { - type: 'Expression', - value: value.trim(), - }, - state: finalState, - } -} -/** - * Parse text content between elements - * - * @param state - The current parser state - * @returns An object containing the parsed text node (or null if empty) and new state - */ -function parseText(state) { - let value = '' - let currentState = state - while (peek(currentState).type === 'TEXT' && !isAtEnd(currentState)) { - const result = advance(currentState) - value += result.token.value - currentState = result.state - } - // Only return text node if it has non-whitespace content - const trimmed = value.trim() - if (!trimmed) { - return { node: null, state: currentState } - } - return { - node: { - type: 'Text', - value, - }, - state: currentState, - } -} -// Immutable state helpers -/** - * Look at a token without consuming it - * - * @param state - The current parser state - * @param offset - How many tokens ahead to look (default: 0) - * @returns The token at the specified position - */ -function peek(state, offset = 0) { - const index = state.current + offset - if (index >= state.tokens.length) { - return state.tokens[state.tokens.length - 1] - } - return state.tokens[index] -} -/** - * Consume the current token and advance to the next one - * - * @param state - The current parser state - * @returns An object containing the consumed token and new state - */ -function advance(state) { - if (isAtEnd(state)) { - return { token: state.tokens[state.current], state } - } - return { - token: state.tokens[state.current], - state: { ...state, current: state.current + 1 }, - } -} -/** - * Consume a token of a specific type or throw an error - * - * @param state - The current parser state - * @param type - The expected token type - * @returns An object containing the consumed token and new state - * @throws Error if the current token doesn't match the expected type - */ -function consume(state, type) { - const token = peek(state) - if (token.type !== type) { - throw new Error(`Expected ${type} but got ${token.type}`) - } - return advance(state) -} -/** - * Check if we've reached the end of the token stream - * - * @param state - The current parser state - * @returns True if at the end of the token stream - */ -function isAtEnd(state) { - return peek(state).type === 'EOF' -} -//# sourceMappingURL=parser.js.map diff --git a/packages/compiler/src/parser/parser.js.map b/packages/compiler/src/parser/parser.js.map deleted file mode 100644 index 7cc9fbb..0000000 --- a/packages/compiler/src/parser/parser.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"parser.js","sourceRoot":"","sources":["parser.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/compiler/src/parser/utils.d.ts.map b/packages/compiler/src/parser/utils.d.ts.map deleted file mode 100644 index 8528780..0000000 --- a/packages/compiler/src/parser/utils.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAEhE;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,SAAS,EACf,MAAM,CAAC,EAAE,SAAS,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,KACX,IAAI,CAAA;AAET;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,SAAS,EACf,MAAM,CAAC,EAAE,SAAS,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,KACX,OAAO,CAAC,IAAI,CAAC,CAAA;AAElB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAsB/D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BxF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,EAAE;IACb;;;;;;;;;;;;;OAaG;cACO,SAAS,GAAG,IAAI,IAAI,WAAW;IAIzC;;;;;;;;;;;;OAYG;kBACW,SAAS,GAAG,IAAI,IAAI,WAAW;IAI7C;;;;;;;;;;;;OAYG;eACQ,SAAS,GAAG,IAAI,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAI3D;;;;;;;;;;;;OAYG;qBACc,SAAS,GAAG,IAAI,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,YAAY,CAAA;KAAE;IAIvE;;;;;;;;;;;;OAYG;sBACe,SAAS,GAAG,IAAI,IAAI,SAAS,GAAG;QAAE,IAAI,EAAE,aAAa,CAAA;KAAE;IAIzE;;;;;;;;;;;;OAYG;kBACW,SAAS,GAAG,IAAI,IAAI,QAAQ;CAG3C,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/parser/utils.js b/packages/compiler/src/parser/utils.js deleted file mode 100644 index 1469fd7..0000000 --- a/packages/compiler/src/parser/utils.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Walk through the AST synchronously and execute a handler function for each node - * - * @param ast - The root node of the AST to traverse - * @param handler - Handler function to execute for each node - * - * @example - * ```typescript - * walk(ast, (node) => { - * if (is.tag(node)) { - * console.log(node.name); - * } - * }); - * ``` - */ -export function walk(ast, handler) { - function traverse(node, parent, prop, index) { - handler(node, parent, prop, index) - // Traverse child nodes - if ('children' in node && Array.isArray(node.children)) { - node.children.forEach((child, idx) => { - traverse(child, node, 'children', idx) - }) - } - // Traverse Expression nodes within attributes - if (node.type === 'Element' && node.attributes) { - node.attributes.forEach((attr, idx) => { - if (typeof attr.value === 'object' && attr.value.type === 'Expression') { - traverse(attr.value, node, `attributes[${idx}].value`, idx) - } - }) - } - } - traverse(ast) -} -/** - * Walk through the AST asynchronously and execute a handler function for each node - * - * @param ast - The root node of the AST to traverse - * @param handler - Async handler function to execute for each node - * - * @example - * ```typescript - * await walkAsync(ast, async (node) => { - * if (is.tag(node)) { - * node.value = await expensiveCalculation(node); - * } - * }); - * ``` - */ -export async function walkAsync(ast, handler) { - async function traverse(node, parent, prop, index) { - await handler(node, parent, prop, index) - // Traverse child nodes - if ('children' in node && Array.isArray(node.children)) { - for (let idx = 0; idx < node.children.length; idx++) { - await traverse(node.children[idx], node, 'children', idx) - } - } - // Traverse Expression nodes within attributes - if (node.type === 'Element' && node.attributes) { - for (let idx = 0; idx < node.attributes.length; idx++) { - const attr = node.attributes[idx] - if (typeof attr.value === 'object' && attr.value.type === 'Expression') { - await traverse(attr.value, node, `attributes[${idx}].value`, idx) - } - } - } - } - await traverse(ast) -} -/** - * Type guard helpers for node type checking - * - * @example - * ```typescript - * if (is.tag(node)) { - * console.log(node.name); // node is inferred as ElementNode - * } - * ``` - */ -export const is = { - /** - * Check if a node is a tag (element) node. - * Similar to official Astro, represents element/custom-element/component - * - * @param node - The AST node to check - * @returns True if the node is an ElementNode - * - * @example - * ```typescript - * if (is.tag(node)) { - * console.log(node.name); // TypeScript knows node is ElementNode - * } - * ``` - */ - tag(node) { - return node.type === 'Element' - }, - /** - * Check if a node is an element node - * - * @param node - The AST node to check - * @returns True if the node is an ElementNode - * - * @example - * ```typescript - * if (is.element(node)) { - * console.log(node.attributes); // Access element-specific properties - * } - * ``` - */ - element(node) { - return node.type === 'Element' - }, - /** - * Check if a node is a text node - * - * @param node - The AST node to check - * @returns True if the node is a TextNode - * - * @example - * ```typescript - * if (is.text(node)) { - * console.log(node.value); // Access text content - * } - * ``` - */ - text(node) { - return node.type === 'Text' - }, - /** - * Check if a node is an expression node - * - * @param node - The AST node to check - * @returns True if the node is an ExpressionNode - * - * @example - * ```typescript - * if (is.expression(node)) { - * console.log(node.value); // Access JavaScript expression - * } - * ``` - */ - expression(node) { - return node.type === 'Expression' - }, - /** - * Check if a node is a frontmatter node - * - * @param node - The AST node to check - * @returns True if the node is a FrontmatterNode - * - * @example - * ```typescript - * if (is.frontmatter(node)) { - * console.log(node.value); // Access frontmatter code - * } - * ``` - */ - frontmatter(node) { - return node.type === 'Frontmatter' - }, - /** - * Check if a node is a program node (root) - * - * @param node - The AST node to check - * @returns True if the node is a ProgramNode - * - * @example - * ```typescript - * if (is.program(node)) { - * console.log(node.children); // Access all top-level nodes - * } - * ``` - */ - program(node) { - return node.type === 'Program' - }, -} -//# sourceMappingURL=utils.js.map diff --git a/packages/compiler/src/parser/utils.js.map b/packages/compiler/src/parser/utils.js.map deleted file mode 100644 index e573451..0000000 --- a/packages/compiler/src/parser/utils.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/index.d.ts.map b/packages/compiler/src/tokenizer/index.d.ts.map deleted file mode 100644 index f2354c4..0000000 --- a/packages/compiler/src/tokenizer/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/index.js b/packages/compiler/src/tokenizer/index.js deleted file mode 100644 index 7068d71..0000000 --- a/packages/compiler/src/tokenizer/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { tokenize } from './tokenizer.js' -//# sourceMappingURL=index.js.map diff --git a/packages/compiler/src/tokenizer/index.js.map b/packages/compiler/src/tokenizer/index.js.map deleted file mode 100644 index c71d77e..0000000 --- a/packages/compiler/src/tokenizer/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/tokenizer.d.ts.map b/packages/compiler/src/tokenizer/tokenizer.d.ts.map deleted file mode 100644 index 9dc24b8..0000000 --- a/packages/compiler/src/tokenizer/tokenizer.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvC,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CAgNhD"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/tokenizer.js b/packages/compiler/src/tokenizer/tokenizer.js deleted file mode 100644 index 5b4501c..0000000 --- a/packages/compiler/src/tokenizer/tokenizer.js +++ /dev/null @@ -1,186 +0,0 @@ -export function tokenize(source) { - const tokens = [] - let current = 0 - let line = 1 - let col = 1 - function advance(count = 1) { - for (let i = 0; i < count; i++) { - if (source[current] === '\n') { - line++ - col = 1 - } else { - col++ - } - current++ - } - } - function peek(offset = 0) { - return source[current + offset] ?? '' - } - function match(pattern) { - return source.slice(current, current + pattern.length) === pattern - } - function addToken(type, value) { - tokens.push({ type, value, line, col }) - } - // Check for frontmatter at the start - if (match('---') && current === 0) { - addToken('FRONTMATTER_START', '---') - advance(3) - // Skip newline after opening --- - if (peek() === '\n') { - advance() - } - // Collect frontmatter content - let content = '' - while (current < source.length) { - // Check for closing --- - if (peek() === '\n' || current === 0) { - const nextLineStart = peek() === '\n' ? current + 1 : current - if ( - source.slice(nextLineStart, nextLineStart + 3) === '---' && - (source[nextLineStart + 3] === '\n' || source[nextLineStart + 3] === undefined) - ) { - if (content) { - addToken('FRONTMATTER_CONTENT', content.trim()) - } - if (peek() === '\n') advance() - addToken('FRONTMATTER_END', '---') - advance(3) - if (peek() === '\n') advance() - break - } - } - content += peek() - advance() - } - } - // Main tokenization loop - while (current < source.length) { - const char = peek() - // Skip whitespace between elements - if (char === ' ' || char === '\t' || char === '\n') { - advance() - continue - } - // HTML end tag - if (match(' - while (current < source.length && peek() !== '>') { - advance() - } - if (peek() === '>') { - addToken('HTML_TAG_CLOSE', '>') - advance() - } - continue - } - // HTML start tag - if (char === '<' && /[a-zA-Z]/.test(peek(1))) { - addToken('HTML_TAG_OPEN', '<') - advance() - let tagName = '' - while (current < source.length && /[a-zA-Z0-9-]/.test(peek())) { - tagName += peek() - advance() - } - addToken('HTML_TAG_NAME', tagName) - // Parse attributes - while (current < source.length && peek() !== '>' && !match('/>')) { - // Skip whitespace - if (peek() === ' ' || peek() === '\t') { - advance() - continue - } - // Attribute name - if (/[a-zA-Z]/.test(peek())) { - let attrName = '' - while (current < source.length && /[a-zA-Z0-9-:]/.test(peek())) { - attrName += peek() - advance() - } - addToken('HTML_ATTRIBUTE_NAME', attrName) - // Skip whitespace and = - while (peek() === ' ' || peek() === '\t') advance() - if (peek() === '=') { - advance() - while (peek() === ' ' || peek() === '\t') advance() - // Attribute value - if (peek() === '"' || peek() === "'") { - const quote = peek() - advance() // Skip opening quote - let value = '' - while (current < source.length && peek() !== quote) { - value += peek() - advance() - } - advance() // Skip closing quote - addToken('HTML_ATTRIBUTE_VALUE', value) - } - } - } else { - advance() - } - } - // Self-closing or regular close - if (match('/>')) { - addToken('HTML_TAG_SELF_CLOSE', '/>') - advance(2) - } else if (peek() === '>') { - addToken('HTML_TAG_CLOSE', '>') - advance() - } - continue - } - // Expression - if (char === '{') { - addToken('EXPRESSION_START', '{') - advance() - let depth = 1 - let content = '' - while (current < source.length && depth > 0) { - if (peek() === '{') { - depth++ - content += peek() - advance() - } else if (peek() === '}') { - depth-- - if (depth === 0) { - if (content) { - addToken('EXPRESSION_CONTENT', content) - } - addToken('EXPRESSION_END', '}') - advance() - } else { - content += peek() - advance() - } - } else { - content += peek() - advance() - } - } - continue - } - // Text content - let text = '' - while (current < source.length && peek() !== '<' && peek() !== '{' && peek() !== '}') { - text += peek() - advance() - } - if (text.trim()) { - addToken('TEXT', text) - } - } - addToken('EOF', '') - return tokens -} -//# sourceMappingURL=tokenizer.js.map diff --git a/packages/compiler/src/tokenizer/tokenizer.js.map b/packages/compiler/src/tokenizer/tokenizer.js.map deleted file mode 100644 index 28bf4b2..0000000 --- a/packages/compiler/src/tokenizer/tokenizer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["tokenizer.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,MAAM,MAAM,GAAY,EAAE,CAAA;IAC1B,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,GAAG,GAAG,CAAC,CAAA;IAEX,SAAS,OAAO,CAAC,KAAK,GAAG,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC7B,IAAI,EAAE,CAAA;gBACN,GAAG,GAAG,CAAC,CAAA;YACT,CAAC;iBAAM,CAAC;gBACN,GAAG,EAAE,CAAA;YACP,CAAC;YACD,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IAED,SAAS,IAAI,CAAC,MAAM,GAAG,CAAC;QACtB,OAAO,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;IACvC,CAAC;IAED,SAAS,KAAK,CAAC,OAAe;QAC5B,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAA;IACpE,CAAC;IAED,SAAS,QAAQ,CAAC,IAAmB,EAAE,KAAa;QAClD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAA;IACzC,CAAC;IAED,qCAAqC;IACrC,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAA;QACpC,OAAO,CAAC,CAAC,CAAC,CAAA;QAEV,iCAAiC;QACjC,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,EAAE,CAAA;QACX,CAAC;QAED,8BAA8B;QAC9B,IAAI,OAAO,GAAG,EAAE,CAAA;QAChB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;YAC/B,wBAAwB;YACxB,IAAI,IAAI,EAAE,KAAK,IAAI,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBACrC,MAAM,aAAa,GAAG,IAAI,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;gBAC7D,IACE,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,aAAa,GAAG,CAAC,CAAC,KAAK,KAAK;oBACxD,CAAC,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,EAC/E,CAAC;oBACD,IAAI,OAAO,EAAE,CAAC;wBACZ,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;oBACjD,CAAC;oBACD,IAAI,IAAI,EAAE,KAAK,IAAI;wBAAE,OAAO,EAAE,CAAA;oBAC9B,QAAQ,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;oBAClC,OAAO,CAAC,CAAC,CAAC,CAAA;oBACV,IAAI,IAAI,EAAE,KAAK,IAAI;wBAAE,OAAO,EAAE,CAAA;oBAC9B,MAAK;gBACP,CAAC;YACH,CAAC;YACD,OAAO,IAAI,IAAI,EAAE,CAAA;YACjB,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,EAAE,CAAA;QAEnB,mCAAmC;QACnC,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnD,OAAO,EAAE,CAAA;YACT,SAAQ;QACV,CAAC;QAED,eAAe;QACf,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAChB,QAAQ,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;YAChC,OAAO,CAAC,CAAC,CAAC,CAAA;YAEV,IAAI,OAAO,GAAG,EAAE,CAAA;YAChB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC9D,OAAO,IAAI,IAAI,EAAE,CAAA;gBACjB,OAAO,EAAE,CAAA;YACX,CAAC;YACD,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;YAElC,YAAY;YACZ,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACjD,OAAO,EAAE,CAAA;YACX,CAAC;YACD,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACnB,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAA;gBAC/B,OAAO,EAAE,CAAA;YACX,CAAC;YACD,SAAQ;QACV,CAAC;QAED,iBAAiB;QACjB,IAAI,IAAI,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC,CAAA;YAC9B,OAAO,EAAE,CAAA;YAET,IAAI,OAAO,GAAG,EAAE,CAAA;YAChB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC9D,OAAO,IAAI,IAAI,EAAE,CAAA;gBACjB,OAAO,EAAE,CAAA;YACX,CAAC;YACD,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;YAElC,mBAAmB;YACnB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjE,kBAAkB;gBAClB,IAAI,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;oBACtC,OAAO,EAAE,CAAA;oBACT,SAAQ;gBACV,CAAC;gBAED,iBAAiB;gBACjB,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;oBAC5B,IAAI,QAAQ,GAAG,EAAE,CAAA;oBACjB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;wBAC/D,QAAQ,IAAI,IAAI,EAAE,CAAA;wBAClB,OAAO,EAAE,CAAA;oBACX,CAAC;oBACD,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAA;oBAEzC,wBAAwB;oBACxB,OAAO,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,EAAE,KAAK,IAAI;wBAAE,OAAO,EAAE,CAAA;oBACnD,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;wBACnB,OAAO,EAAE,CAAA;wBACT,OAAO,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,EAAE,KAAK,IAAI;4BAAE,OAAO,EAAE,CAAA;wBAEnD,kBAAkB;wBAClB,IAAI,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;4BACrC,MAAM,KAAK,GAAG,IAAI,EAAE,CAAA;4BACpB,OAAO,EAAE,CAAA,CAAC,qBAAqB;4BAC/B,IAAI,KAAK,GAAG,EAAE,CAAA;4BACd,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;gCACnD,KAAK,IAAI,IAAI,EAAE,CAAA;gCACf,OAAO,EAAE,CAAA;4BACX,CAAC;4BACD,OAAO,EAAE,CAAA,CAAC,qBAAqB;4BAC/B,QAAQ,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAA;wBACzC,CAAC;oBACH,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,OAAO,EAAE,CAAA;gBACX,CAAC;YACH,CAAC;YAED,gCAAgC;YAChC,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChB,QAAQ,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAA;gBACrC,OAAO,CAAC,CAAC,CAAC,CAAA;YACZ,CAAC;iBAAM,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBAC1B,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAA;gBAC/B,OAAO,EAAE,CAAA;YACX,CAAC;YACD,SAAQ;QACV,CAAC;QAED,aAAa;QACb,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAA;YACjC,OAAO,EAAE,CAAA;YAET,IAAI,KAAK,GAAG,CAAC,CAAA;YACb,IAAI,OAAO,GAAG,EAAE,CAAA;YAChB,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBAC5C,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;oBACnB,KAAK,EAAE,CAAA;oBACP,OAAO,IAAI,IAAI,EAAE,CAAA;oBACjB,OAAO,EAAE,CAAA;gBACX,CAAC;qBAAM,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;oBAC1B,KAAK,EAAE,CAAA;oBACP,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;wBAChB,IAAI,OAAO,EAAE,CAAC;4BACZ,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAA;wBACzC,CAAC;wBACD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAA;wBAC/B,OAAO,EAAE,CAAA;oBACX,CAAC;yBAAM,CAAC;wBACN,OAAO,IAAI,IAAI,EAAE,CAAA;wBACjB,OAAO,EAAE,CAAA;oBACX,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,OAAO,IAAI,IAAI,EAAE,CAAA;oBACjB,OAAO,EAAE,CAAA;gBACX,CAAC;YACH,CAAC;YACD,SAAQ;QACV,CAAC;QAED,eAAe;QACf,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,OAAO,OAAO,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACrF,IAAI,IAAI,IAAI,EAAE,CAAA;YACd,OAAO,EAAE,CAAA;QACX,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAChB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACxB,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACnB,OAAO,MAAM,CAAA;AACf,CAAC"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/types.d.ts.map b/packages/compiler/src/tokenizer/types.d.ts.map deleted file mode 100644 index 22c49a2..0000000 --- a/packages/compiler/src/tokenizer/types.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,MAAM,SAAS,GACjB,mBAAmB,GACnB,iBAAiB,GACjB,qBAAqB,GACrB,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,eAAe,GACf,qBAAqB,GACrB,sBAAsB,GACtB,kBAAkB,GAClB,gBAAgB,GAChB,oBAAoB,GACpB,MAAM,GACN,YAAY,GACZ,KAAK,CAAA"} \ No newline at end of file diff --git a/packages/compiler/src/tokenizer/types.js b/packages/compiler/src/tokenizer/types.js deleted file mode 100644 index 4ef431e..0000000 --- a/packages/compiler/src/tokenizer/types.js +++ /dev/null @@ -1,2 +0,0 @@ -export {} -//# sourceMappingURL=types.js.map diff --git a/packages/compiler/src/tokenizer/types.js.map b/packages/compiler/src/tokenizer/types.js.map deleted file mode 100644 index 8da0887..0000000 --- a/packages/compiler/src/tokenizer/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":""} \ No newline at end of file From 4fd03d947f22b1d620fcbfbb6094ec4eae962b9a Mon Sep 17 00:00:00 2001 From: jp-knj Date: Tue, 29 Jul 2025 11:09:21 +0900 Subject: [PATCH 7/7] fix: add TypeScript types and improve path handling in Vite plugin - Add proper TypeScript type annotation for importer parameter - Use path.isAbsolute() for cross-platform absolute path detection - Consolidate path imports to avoid redundant dynamic imports - This should fix CI build failures on different platforms --- packages/vite-plugin/src/index.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index 5cbdb42..00a1f29 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -55,24 +55,23 @@ export function vitePluginAstro(): Plugin { /** * Resolve .astro file imports */ - async resolveId(id: string, importer) { + async resolveId(id: string, importer?: string) { // Handle .astro imports if (id.endsWith('.astro')) { + const path = await import('node:path') + // If it's already an absolute path, return it - if (id.startsWith('/')) { + if (path.isAbsolute(id)) { return id } // Resolve relative imports if (importer) { - const { resolve } = await import('node:path') - const { dirname } = await import('node:path') - return resolve(dirname(importer), id) + return path.resolve(path.dirname(importer), id) } // For entry points, resolve from root - const { resolve } = await import('node:path') - return resolve(id) + return path.resolve(id) } return null },