diff --git a/biome.json b/biome.json index ca0a95ef4..4f9b31f5b 100644 --- a/biome.json +++ b/biome.json @@ -1,14 +1,14 @@ { "$schema": "https://biomejs.dev/schemas/1.8.1/schema.json", "files": { - "ignore": ["**/dist", "**/pnpm-lock.yaml"], + "ignore": ["**/dist", "**/pnpm-lock.yaml", "wasm_exec.ts"], "include": ["packages/**"] }, "formatter": { "enabled": true, - "indentStyle": "space", + "indentStyle": "tab", "indentWidth": 2, - "lineWidth": 180 + "lineWidth": 100 }, "organizeImports": { "enabled": true @@ -36,12 +36,24 @@ }, "javascript": { "formatter": { - "quoteStyle": "single" + "trailingCommas": "es5", + "quoteStyle": "single", + "semicolons": "always" + } + }, + "json": { + "parser": { + "allowComments": true, + "allowTrailingCommas": true + }, + "formatter": { + "indentStyle": "space", + "trailingCommas": "none" } }, "overrides": [ { - "include": ["package.json", "biome.jsonc"], + "include": ["package.json"], "json": { "formatter": { "lineWidth": 1 diff --git a/packages/compiler/src/browser/index.ts b/packages/compiler/src/browser/index.ts index 372d770a7..04110bebb 100644 --- a/packages/compiler/src/browser/index.ts +++ b/packages/compiler/src/browser/index.ts @@ -2,83 +2,95 @@ import type * as types from '../shared/types'; import Go from './wasm_exec.js'; export const transform: typeof types.transform = (input, options) => { - return ensureServiceIsRunning().transform(input, options); + return ensureServiceIsRunning().transform(input, options); }; export const parse: typeof types.parse = (input, options) => { - return ensureServiceIsRunning().parse(input, options); + return ensureServiceIsRunning().parse(input, options); }; export const convertToTSX: typeof types.convertToTSX = (input, options) => { - return ensureServiceIsRunning().convertToTSX(input, options); + return ensureServiceIsRunning().convertToTSX(input, options); }; interface Service { - transform: typeof types.transform; - parse: typeof types.parse; - convertToTSX: typeof types.convertToTSX; + transform: typeof types.transform; + parse: typeof types.parse; + convertToTSX: typeof types.convertToTSX; } let initializePromise: Promise | undefined; let longLivedService: Service | undefined; export const teardown: typeof types.teardown = () => { - initializePromise = undefined; - longLivedService = undefined; - (globalThis as any)['@astrojs/compiler'] = undefined; + initializePromise = undefined; + longLivedService = undefined; + (globalThis as any)['@astrojs/compiler'] = undefined; }; export const initialize: typeof types.initialize = async (options) => { - let wasmURL = options.wasmURL; - if (!wasmURL) throw new Error('Must provide the "wasmURL" option'); - wasmURL += ''; - if (!initializePromise) { - initializePromise = startRunningService(wasmURL).catch((err) => { - // Let the caller try again if this fails. - initializePromise = void 0; - // But still, throw the error back up the caller. - throw err; - }); - } - longLivedService = longLivedService || (await initializePromise); + let wasmURL = options.wasmURL; + if (!wasmURL) throw new Error('Must provide the "wasmURL" option'); + wasmURL += ''; + if (!initializePromise) { + initializePromise = startRunningService(wasmURL).catch((err) => { + // Let the caller try again if this fails. + initializePromise = void 0; + // But still, throw the error back up the caller. + throw err; + }); + } + longLivedService = longLivedService || (await initializePromise); }; const ensureServiceIsRunning = (): Service => { - if (!initializePromise) throw new Error('You need to call "initialize" before calling this'); - if (!longLivedService) throw new Error('You need to wait for the promise returned from "initialize" to be resolved before calling this'); - return longLivedService; + if (!initializePromise) throw new Error('You need to call "initialize" before calling this'); + if (!longLivedService) + throw new Error( + 'You need to wait for the promise returned from "initialize" to be resolved before calling this' + ); + return longLivedService; }; -const instantiateWASM = async (wasmURL: string, importObject: Record): Promise => { - let response = undefined; +const instantiateWASM = async ( + wasmURL: string, + importObject: Record +): Promise => { + let response = undefined; - if (WebAssembly.instantiateStreaming) { - response = await WebAssembly.instantiateStreaming(fetch(wasmURL), importObject); - } else { - const fetchAndInstantiateTask = async () => { - const wasmArrayBuffer = await fetch(wasmURL).then((res) => res.arrayBuffer()); - return WebAssembly.instantiate(wasmArrayBuffer, importObject); - }; - response = await fetchAndInstantiateTask(); - } + if (WebAssembly.instantiateStreaming) { + response = await WebAssembly.instantiateStreaming(fetch(wasmURL), importObject); + } else { + const fetchAndInstantiateTask = async () => { + const wasmArrayBuffer = await fetch(wasmURL).then((res) => res.arrayBuffer()); + return WebAssembly.instantiate(wasmArrayBuffer, importObject); + }; + response = await fetchAndInstantiateTask(); + } - return response; + return response; }; const startRunningService = async (wasmURL: string): Promise => { - const go = new Go(); - const wasm = await instantiateWASM(wasmURL, go.importObject); - go.run(wasm.instance); + const go = new Go(); + const wasm = await instantiateWASM(wasmURL, go.importObject); + go.run(wasm.instance); - const service: any = (globalThis as any)['@astrojs/compiler']; + const service: any = (globalThis as any)['@astrojs/compiler']; - return { - transform: (input, options) => new Promise((resolve) => resolve(service.transform(input, options || {}))), - convertToTSX: (input, options) => - new Promise((resolve) => resolve(service.convertToTSX(input, options || {}))).then((result: any) => ({ - ...result, - map: JSON.parse(result.map), - })), - parse: (input, options) => new Promise((resolve) => resolve(service.parse(input, options || {}))).then((result: any) => ({ ...result, ast: JSON.parse(result.ast) })), - }; + return { + transform: (input, options) => + new Promise((resolve) => resolve(service.transform(input, options || {}))), + convertToTSX: (input, options) => + new Promise((resolve) => resolve(service.convertToTSX(input, options || {}))).then( + (result: any) => ({ + ...result, + map: JSON.parse(result.map), + }) + ), + parse: (input, options) => + new Promise((resolve) => resolve(service.parse(input, options || {}))).then( + (result: any) => ({ ...result, ast: JSON.parse(result.ast) }) + ), + }; }; diff --git a/packages/compiler/src/browser/utils.ts b/packages/compiler/src/browser/utils.ts index ef0feee61..92bcdfb1f 100644 --- a/packages/compiler/src/browser/utils.ts +++ b/packages/compiler/src/browser/utils.ts @@ -1,144 +1,149 @@ import type { - CommentNode, - ComponentNode, - CustomElementNode, - DoctypeNode, - ElementNode, - ExpressionNode, - FragmentNode, - FrontmatterNode, - LiteralNode, - Node, - ParentNode, - RootNode, - TagLikeNode, - TextNode, + CommentNode, + ComponentNode, + CustomElementNode, + DoctypeNode, + ElementNode, + ExpressionNode, + FragmentNode, + FrontmatterNode, + LiteralNode, + Node, + ParentNode, + RootNode, + TagLikeNode, + TextNode, } from '../shared/ast'; export type Visitor = (node: Node, parent?: ParentNode, index?: number) => void | Promise; function guard(type: string) { - return (node: Node): node is Type => node.type === type; + return (node: Node): node is Type => node.type === type; } export const is = { - parent(node: Node): node is ParentNode { - return Array.isArray((node as any).children); - }, - literal(node: Node): node is LiteralNode { - return typeof (node as any).value === 'string'; - }, - tag(node: Node): node is TagLikeNode { - return node.type === 'element' || node.type === 'custom-element' || node.type === 'component' || node.type === 'fragment'; - }, - whitespace(node: Node): node is TextNode { - return node.type === 'text' && node.value.trim().length === 0; - }, - root: guard('root'), - element: guard('element'), - customElement: guard('custom-element'), - component: guard('component'), - fragment: guard('fragment'), - expression: guard('expression'), - text: guard('text'), - doctype: guard('doctype'), - comment: guard('comment'), - frontmatter: guard('frontmatter'), + parent(node: Node): node is ParentNode { + return Array.isArray((node as any).children); + }, + literal(node: Node): node is LiteralNode { + return typeof (node as any).value === 'string'; + }, + tag(node: Node): node is TagLikeNode { + return ( + node.type === 'element' || + node.type === 'custom-element' || + node.type === 'component' || + node.type === 'fragment' + ); + }, + whitespace(node: Node): node is TextNode { + return node.type === 'text' && node.value.trim().length === 0; + }, + root: guard('root'), + element: guard('element'), + customElement: guard('custom-element'), + component: guard('component'), + fragment: guard('fragment'), + expression: guard('expression'), + text: guard('text'), + doctype: guard('doctype'), + comment: guard('comment'), + frontmatter: guard('frontmatter'), }; class Walker { - constructor(private callback: Visitor) {} - async visit(node: Node, parent?: ParentNode, index?: number): Promise { - await this.callback(node, parent, index); - if (is.parent(node)) { - const promises = []; - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - promises.push(this.callback(child, node as ParentNode, i)); - } - await Promise.all(promises); - } - } + constructor(private callback: Visitor) {} + async visit(node: Node, parent?: ParentNode, index?: number): Promise { + await this.callback(node, parent, index); + if (is.parent(node)) { + const promises = []; + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + promises.push(this.callback(child, node as ParentNode, i)); + } + await Promise.all(promises); + } + } } export function walk(node: ParentNode, callback: Visitor): void { - const walker = new Walker(callback); - walker.visit(node); + const walker = new Walker(callback); + walker.visit(node); } function serializeAttributes(node: TagLikeNode): string { - let output = ''; - for (const attr of node.attributes) { - output += ' '; - switch (attr.kind) { - case 'empty': { - output += `${attr.name}`; - break; - } - case 'expression': { - output += `${attr.name}={${attr.value}}`; - break; - } - case 'quoted': { - output += `${attr.name}=${attr.raw}`; - break; - } - case 'template-literal': { - output += `${attr.name}=\`${attr.value}\``; - break; - } - case 'shorthand': { - output += `{${attr.name}}`; - break; - } - case 'spread': { - output += `{...${attr.value}}`; - break; - } - } - } - return output; + let output = ''; + for (const attr of node.attributes) { + output += ' '; + switch (attr.kind) { + case 'empty': { + output += `${attr.name}`; + break; + } + case 'expression': { + output += `${attr.name}={${attr.value}}`; + break; + } + case 'quoted': { + output += `${attr.name}=${attr.raw}`; + break; + } + case 'template-literal': { + output += `${attr.name}=\`${attr.value}\``; + break; + } + case 'shorthand': { + output += `{${attr.name}}`; + break; + } + case 'spread': { + output += `{...${attr.value}}`; + break; + } + } + } + return output; } export interface SerializeOptions { - selfClose: boolean; + selfClose: boolean; } /** @deprecated Please use `SerializeOptions` */ export type SerializeOtions = SerializeOptions; export function serialize(root: Node, opts: SerializeOptions = { selfClose: true }): string { - let output = ''; - function visitor(node: Node) { - if (is.root(node)) { - for (const child of node.children) { - visitor(child); - } - } else if (is.frontmatter(node)) { - output += `---${node.value}---\n\n`; - } else if (is.comment(node)) { - output += ``; - } else if (is.expression(node)) { - output += '{'; - for (const child of node.children) { - visitor(child); - } - output += '}'; - } else if (is.literal(node)) { - output += node.value; - } else if (is.tag(node)) { - output += `<${node.name}`; - output += serializeAttributes(node); - if (node.children.length === 0 && opts.selfClose) { - output += ' />'; - } else { - output += '>'; - for (const child of node.children) { - visitor(child); - } - output += ``; - } - } - } - visitor(root); - return output; + let output = ''; + function visitor(node: Node) { + if (is.root(node)) { + for (const child of node.children) { + visitor(child); + } + } else if (is.frontmatter(node)) { + output += `---${node.value}---\n\n`; + } else if (is.comment(node)) { + output += ``; + } else if (is.expression(node)) { + output += '{'; + for (const child of node.children) { + visitor(child); + } + output += '}'; + } else if (is.literal(node)) { + output += node.value; + } else if (is.tag(node)) { + output += `<${node.name}`; + output += serializeAttributes(node); + if (node.children.length === 0 && opts.selfClose) { + output += ' />'; + } else { + output += '>'; + for (const child of node.children) { + visitor(child); + } + output += ``; + } + } + } + visitor(root); + return output; } diff --git a/packages/compiler/src/node/index.ts b/packages/compiler/src/node/index.ts index 44b63dcb2..9071558be 100644 --- a/packages/compiler/src/node/index.ts +++ b/packages/compiler/src/node/index.ts @@ -1,95 +1,110 @@ -export type { HoistedScript, ParseOptions, ParseResult, PreprocessorResult, TransformOptions, TransformResult } from '../shared/types'; +export type { + HoistedScript, + ParseOptions, + ParseResult, + PreprocessorResult, + TransformOptions, + TransformResult, +} from '../shared/types'; import { promises as fs } from 'node:fs'; import { fileURLToPath } from 'node:url'; import type * as types from '../shared/types'; import Go from './wasm_exec.js'; export const transform: typeof types.transform = async (input, options) => { - return getService().then((service) => service.transform(input, options)); + return getService().then((service) => service.transform(input, options)); }; export const parse: typeof types.parse = async (input, options) => { - return getService().then((service) => service.parse(input, options)); + return getService().then((service) => service.parse(input, options)); }; export const convertToTSX: typeof types.convertToTSX = async (input, options) => { - return getService().then((service) => service.convertToTSX(input, options)); + return getService().then((service) => service.convertToTSX(input, options)); }; export const compile = async (template: string): Promise => { - const { default: mod } = await import(`data:text/javascript;charset=utf-8;base64,${Buffer.from(template).toString('base64')}`); - return mod; + const { default: mod } = await import( + `data:text/javascript;charset=utf-8;base64,${Buffer.from(template).toString('base64')}` + ); + return mod; }; interface Service { - transform: typeof types.transform; - parse: typeof types.parse; - convertToTSX: typeof types.convertToTSX; + transform: typeof types.transform; + parse: typeof types.parse; + convertToTSX: typeof types.convertToTSX; } let longLivedService: Promise | undefined; export const teardown: typeof types.teardown = () => { - longLivedService = undefined; - (globalThis as any)['@astrojs/compiler'] = undefined; + longLivedService = undefined; + (globalThis as any)['@astrojs/compiler'] = undefined; }; const getService = (): Promise => { - if (!longLivedService) { - longLivedService = startRunningService().catch((err) => { - // Let the caller try again if this fails. - longLivedService = void 0; - // But still, throw the error back up the caller. - throw err; - }); - } - return longLivedService; + if (!longLivedService) { + longLivedService = startRunningService().catch((err) => { + // Let the caller try again if this fails. + longLivedService = void 0; + // But still, throw the error back up the caller. + throw err; + }); + } + return longLivedService; }; -const instantiateWASM = async (wasmURL: string, importObject: Record): Promise => { - let response = undefined; +const instantiateWASM = async ( + wasmURL: string, + importObject: Record +): Promise => { + let response = undefined; - const fetchAndInstantiateTask = async () => { - const wasmArrayBuffer = await fs.readFile(wasmURL).then((res) => res.buffer); - return WebAssembly.instantiate(new Uint8Array(wasmArrayBuffer), importObject); - }; - response = await fetchAndInstantiateTask(); + const fetchAndInstantiateTask = async () => { + const wasmArrayBuffer = await fs.readFile(wasmURL).then((res) => res.buffer); + return WebAssembly.instantiate(new Uint8Array(wasmArrayBuffer), importObject); + }; + response = await fetchAndInstantiateTask(); - return response; + return response; }; const startRunningService = async (): Promise => { - const go = new Go(); - const wasm = await instantiateWASM(fileURLToPath(new URL('../astro.wasm', import.meta.url)), go.importObject); - go.run(wasm.instance); - const _service: any = (globalThis as any)['@astrojs/compiler']; - return { - transform: (input, options) => - new Promise((resolve) => { - try { - resolve(_service.transform(input, options || {})); - } catch (err) { - // Recreate the service next time on panic - longLivedService = void 0; - throw err; - } - }), - parse: (input, options) => - new Promise((resolve) => resolve(_service.parse(input, options || {}))) - .catch((error) => { - longLivedService = void 0; - throw error; - }) - .then((result: any) => ({ ...result, ast: JSON.parse(result.ast) })), - convertToTSX: (input, options) => { - return new Promise((resolve) => resolve(_service.convertToTSX(input, options || {}))) - .catch((error) => { - longLivedService = void 0; - throw error; - }) - .then((result: any) => { - return { ...result, map: JSON.parse(result.map) }; - }); - }, - }; + const go = new Go(); + const wasm = await instantiateWASM( + fileURLToPath(new URL('../astro.wasm', import.meta.url)), + go.importObject + ); + go.run(wasm.instance); + const _service: any = (globalThis as any)['@astrojs/compiler']; + return { + transform: (input, options) => + new Promise((resolve) => { + try { + resolve(_service.transform(input, options || {})); + } catch (err) { + // Recreate the service next time on panic + longLivedService = void 0; + throw err; + } + }), + parse: (input, options) => + new Promise((resolve) => resolve(_service.parse(input, options || {}))) + .catch((error) => { + longLivedService = void 0; + throw error; + }) + .then((result: any) => ({ ...result, ast: JSON.parse(result.ast) })), + convertToTSX: (input, options) => { + return new Promise((resolve) => resolve(_service.convertToTSX(input, options || {}))) + .catch((error) => { + longLivedService = void 0; + throw error; + }) + .then((result: any) => { + return { ...result, map: JSON.parse(result.map) }; + }); + }, + }; }; diff --git a/packages/compiler/src/node/sync.ts b/packages/compiler/src/node/sync.ts index 686be4e83..8a905b43c 100644 --- a/packages/compiler/src/node/sync.ts +++ b/packages/compiler/src/node/sync.ts @@ -3,70 +3,76 @@ import { fileURLToPath } from 'node:url'; import type * as types from '../shared/types'; import Go from './wasm_exec.js'; -type UnwrappedPromise = T extends (...params: any) => Promise ? (...params: Parameters) => Return : T; +type UnwrappedPromise = T extends (...params: any) => Promise + ? (...params: Parameters) => Return + : T; interface Service { - transform: UnwrappedPromise; - parse: UnwrappedPromise; - convertToTSX: UnwrappedPromise; + transform: UnwrappedPromise; + parse: UnwrappedPromise; + convertToTSX: UnwrappedPromise; } function getService(): Service { - if (!longLivedService) { - longLivedService = startRunningService(); - } - return longLivedService; + if (!longLivedService) { + longLivedService = startRunningService(); + } + return longLivedService; } let longLivedService: Service | undefined; -export const transform = ((input, options) => getService().transform(input, options)) satisfies Service['transform']; +export const transform = ((input, options) => + getService().transform(input, options)) satisfies Service['transform']; export const parse = ((input, options) => { - return getService().parse(input, options); + return getService().parse(input, options); }) satisfies Service['parse']; export const convertToTSX = ((input, options) => { - return getService().convertToTSX(input, options); + return getService().convertToTSX(input, options); }) satisfies Service['convertToTSX']; export function startRunningService(): Service { - const go = new Go(); - const wasm = instantiateWASM(fileURLToPath(new URL('../astro.wasm', import.meta.url)), go.importObject); - go.run(wasm); - const _service: any = (globalThis as any)['@astrojs/compiler']; - return { - transform: (input, options) => { - try { - return _service.transform(input, options || {}); - } catch (err) { - // Recreate the service next time on panic - longLivedService = void 0; - throw err; - } - }, - parse: (input, options) => { - try { - const result = _service.parse(input, options || {}); - return { ...result, ast: JSON.parse(result.ast) }; - } catch (err) { - longLivedService = void 0; - throw err; - } - }, - convertToTSX: (input, options) => { - try { - const result = _service.convertToTSX(input, options || {}); - return { ...result, map: JSON.parse(result.map) }; - } catch (err) { - longLivedService = void 0; - throw err; - } - }, - }; + const go = new Go(); + const wasm = instantiateWASM( + fileURLToPath(new URL('../astro.wasm', import.meta.url)), + go.importObject + ); + go.run(wasm); + const _service: any = (globalThis as any)['@astrojs/compiler']; + return { + transform: (input, options) => { + try { + return _service.transform(input, options || {}); + } catch (err) { + // Recreate the service next time on panic + longLivedService = void 0; + throw err; + } + }, + parse: (input, options) => { + try { + const result = _service.parse(input, options || {}); + return { ...result, ast: JSON.parse(result.ast) }; + } catch (err) { + longLivedService = void 0; + throw err; + } + }, + convertToTSX: (input, options) => { + try { + const result = _service.convertToTSX(input, options || {}); + return { ...result, map: JSON.parse(result.map) }; + } catch (err) { + longLivedService = void 0; + throw err; + } + }, + }; } function instantiateWASM(wasmURL: string, importObject: Record): WebAssembly.Instance { - const wasmArrayBuffer = readFileSync(wasmURL); - return new WebAssembly.Instance(new WebAssembly.Module(wasmArrayBuffer), importObject); + const wasmArrayBuffer = readFileSync(wasmURL); + return new WebAssembly.Instance(new WebAssembly.Module(wasmArrayBuffer), importObject); } diff --git a/packages/compiler/src/node/utils.ts b/packages/compiler/src/node/utils.ts index f39e6fc31..fd7646cb7 100644 --- a/packages/compiler/src/node/utils.ts +++ b/packages/compiler/src/node/utils.ts @@ -1,144 +1,149 @@ import type { - CommentNode, - ComponentNode, - CustomElementNode, - DoctypeNode, - ElementNode, - ExpressionNode, - FragmentNode, - FrontmatterNode, - LiteralNode, - Node, - ParentNode, - RootNode, - TagLikeNode, - TextNode, + CommentNode, + ComponentNode, + CustomElementNode, + DoctypeNode, + ElementNode, + ExpressionNode, + FragmentNode, + FrontmatterNode, + LiteralNode, + Node, + ParentNode, + RootNode, + TagLikeNode, + TextNode, } from '../shared/ast'; export type Visitor = (node: Node, parent?: ParentNode, index?: number) => void | Promise; function guard(type: string) { - return (node: Node): node is Type => node.type === type; + return (node: Node): node is Type => node.type === type; } export const is = { - parent(node: Node): node is ParentNode { - return Array.isArray((node as any).children); - }, - literal(node: Node): node is LiteralNode { - return typeof (node as any).value === 'string'; - }, - tag(node: Node): node is TagLikeNode { - return node.type === 'element' || node.type === 'custom-element' || node.type === 'component' || node.type === 'fragment'; - }, - whitespace(node: Node): node is TextNode { - return node.type === 'text' && node.value.trim().length === 0; - }, - root: guard('root'), - element: guard('element'), - customElement: guard('custom-element'), - component: guard('component'), - fragment: guard('fragment'), - expression: guard('expression'), - text: guard('text'), - doctype: guard('doctype'), - comment: guard('comment'), - frontmatter: guard('frontmatter'), + parent(node: Node): node is ParentNode { + return Array.isArray((node as any).children); + }, + literal(node: Node): node is LiteralNode { + return typeof (node as any).value === 'string'; + }, + tag(node: Node): node is TagLikeNode { + return ( + node.type === 'element' || + node.type === 'custom-element' || + node.type === 'component' || + node.type === 'fragment' + ); + }, + whitespace(node: Node): node is TextNode { + return node.type === 'text' && node.value.trim().length === 0; + }, + root: guard('root'), + element: guard('element'), + customElement: guard('custom-element'), + component: guard('component'), + fragment: guard('fragment'), + expression: guard('expression'), + text: guard('text'), + doctype: guard('doctype'), + comment: guard('comment'), + frontmatter: guard('frontmatter'), }; class Walker { - constructor(private callback: Visitor) {} - async visit(node: Node, parent?: ParentNode, index?: number): Promise { - await this.callback(node, parent, index); - if (is.parent(node)) { - const promises = []; - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; - promises.push(this.callback(child, node as ParentNode, i)); - } - await Promise.all(promises); - } - } + constructor(private callback: Visitor) {} + async visit(node: Node, parent?: ParentNode, index?: number): Promise { + await this.callback(node, parent, index); + if (is.parent(node)) { + const promises = []; + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + promises.push(this.callback(child, node as ParentNode, i)); + } + await Promise.all(promises); + } + } } export function walk(node: ParentNode, callback: Visitor): void { - const walker = new Walker(callback); - walker.visit(node); + const walker = new Walker(callback); + walker.visit(node); } function serializeAttributes(node: TagLikeNode): string { - let output = ''; - for (const attr of node.attributes) { - output += ' '; - switch (attr.kind) { - case 'empty': { - output += `${attr.name}`; - break; - } - case 'expression': { - output += `${attr.name}={${attr.value}}`; - break; - } - case 'quoted': { - output += `${attr.name}=${attr.raw}`; - break; - } - case 'template-literal': { - output += `${attr.name}=\`${attr.value}\``; - break; - } - case 'shorthand': { - output += `{${attr.name}}`; - break; - } - case 'spread': { - output += `{...${attr.name}}`; - break; - } - } - } - return output; + let output = ''; + for (const attr of node.attributes) { + output += ' '; + switch (attr.kind) { + case 'empty': { + output += `${attr.name}`; + break; + } + case 'expression': { + output += `${attr.name}={${attr.value}}`; + break; + } + case 'quoted': { + output += `${attr.name}=${attr.raw}`; + break; + } + case 'template-literal': { + output += `${attr.name}=\`${attr.value}\``; + break; + } + case 'shorthand': { + output += `{${attr.name}}`; + break; + } + case 'spread': { + output += `{...${attr.name}}`; + break; + } + } + } + return output; } export interface SerializeOptions { - selfClose: boolean; + selfClose: boolean; } /** @deprecated Please use `SerializeOptions` */ export type SerializeOtions = SerializeOptions; export function serialize(root: Node, opts: SerializeOptions = { selfClose: true }): string { - let output = ''; - function visitor(node: Node) { - if (is.root(node)) { - for (const child of node.children) { - visitor(child); - } - } else if (is.frontmatter(node)) { - output += `---${node.value}---\n\n`; - } else if (is.comment(node)) { - output += ``; - } else if (is.expression(node)) { - output += '{'; - for (const child of node.children) { - visitor(child); - } - output += '}'; - } else if (is.literal(node)) { - output += node.value; - } else if (is.tag(node)) { - output += `<${node.name}`; - output += serializeAttributes(node); - if (node.children.length === 0 && opts.selfClose) { - output += ' />'; - } else { - output += '>'; - for (const child of node.children) { - visitor(child); - } - output += ``; - } - } - } - visitor(root); - return output; + let output = ''; + function visitor(node: Node) { + if (is.root(node)) { + for (const child of node.children) { + visitor(child); + } + } else if (is.frontmatter(node)) { + output += `---${node.value}---\n\n`; + } else if (is.comment(node)) { + output += ``; + } else if (is.expression(node)) { + output += '{'; + for (const child of node.children) { + visitor(child); + } + output += '}'; + } else if (is.literal(node)) { + output += node.value; + } else if (is.tag(node)) { + output += `<${node.name}`; + output += serializeAttributes(node); + if (node.children.length === 0 && opts.selfClose) { + output += ' />'; + } else { + output += '>'; + for (const child of node.children) { + visitor(child); + } + output += ``; + } + } + } + visitor(root); + return output; } diff --git a/packages/compiler/src/node/wasm_exec.ts b/packages/compiler/src/node/wasm_exec.ts index 979ed779b..9be9dd4a3 100644 --- a/packages/compiler/src/node/wasm_exec.ts +++ b/packages/compiler/src/node/wasm_exec.ts @@ -10,38 +10,38 @@ import fs from 'node:fs'; import { TextDecoder, TextEncoder } from 'node:util'; if (!globalThis.fs) { - Object.defineProperty(globalThis, 'fs', { - value: fs, - }); + Object.defineProperty(globalThis, 'fs', { + value: fs, + }); } if (!globalThis.process) { - Object.defineProperties(globalThis, 'process', { - value: process, - }); + Object.defineProperties(globalThis, 'process', { + value: process, + }); } if (!globalThis.crypto) { - Object.defineProperty(globalThis, 'crypto', { - value: crypto.webcrypto - ? crypto.webcrypto - : { - getRandomValues(b) { - return crypto.randomFillSync(b); - }, - }, - }); + Object.defineProperty(globalThis, 'crypto', { + value: crypto.webcrypto + ? crypto.webcrypto + : { + getRandomValues(b) { + return crypto.randomFillSync(b); + }, + }, + }); } if (!globalThis.performance) { - Object.defineProperty(globalThis, 'performance', { - value: { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }, - }); + Object.defineProperty(globalThis, 'performance', { + value: { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, + }, + }); } // End of polyfills for common API. @@ -50,460 +50,460 @@ const decoder = new TextDecoder('utf-8'); var logLine = []; export default class Go { - public importObject; - constructor() { - this.argv = ['js']; - this.env = {}; - this.exit = (code) => { - if (code !== 0) { - console.warn('exit code:', code); - } - }; - this._exitPromise = new Promise((resolve) => { - this._resolveExitPromise = resolve; - }); - this._pendingEvent = null; - this._scheduledTimeouts = new Map(); - this._nextCallbackTimeoutID = 1; - - const setInt64 = (addr, v) => { - this.mem.setUint32(addr + 0, v, true); - this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); - }; - - const getInt64 = (addr) => { - const low = this.mem.getUint32(addr + 0, true); - const high = this.mem.getInt32(addr + 4, true); - return low + high * 4294967296; - }; - - const loadValue = (addr) => { - const f = this.mem.getFloat64(addr, true); - if (f === 0) { - return undefined; - } - if (!isNaN(f)) { - return f; - } - - const id = this.mem.getUint32(addr, true); - return this._values[id]; - }; - - const storeValue = (addr, v) => { - const nanHead = 0x7ff80000; - - if (typeof v === 'number' && v !== 0) { - if (isNaN(v)) { - this.mem.setUint32(addr + 4, nanHead, true); - this.mem.setUint32(addr, 0, true); - return; - } - this.mem.setFloat64(addr, v, true); - return; - } - - if (v === undefined) { - this.mem.setFloat64(addr, 0, true); - return; - } - - let id = this._ids.get(v); - if (id === undefined) { - id = this._idPool.pop(); - if (id === undefined) { - id = this._values.length; - } - this._values[id] = v; - this._goRefCounts[id] = 0; - this._ids.set(v, id); - } - this._goRefCounts[id]++; - let typeFlag = 0; - switch (typeof v) { - case 'object': - if (v !== null) { - typeFlag = 1; - } - break; - case 'string': - typeFlag = 2; - break; - case 'symbol': - typeFlag = 3; - break; - case 'function': - typeFlag = 4; - break; - } - this.mem.setUint32(addr + 4, nanHead | typeFlag, true); - this.mem.setUint32(addr, id, true); - }; - - const loadSlice = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - return new Uint8Array(this._inst.exports.mem.buffer, array, len); - }; - - const loadSliceOfValues = (addr) => { - const array = getInt64(addr + 0); - const len = getInt64(addr + 8); - const a = new Array(len); - for (let i = 0; i < len; i++) { - a[i] = loadValue(array + i * 8); - } - return a; - }; - - const loadString = (addr) => { - const saddr = getInt64(addr + 0); - const len = getInt64(addr + 8); - return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); - }; - - const timeOrigin = Date.now() - performance.now(); - this.importObject = { - gojs: { - // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) - // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported - // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). - // This changes the SP, thus we have to update the SP used by the imported function. - - // func wasmExit(code int32) - 'runtime.wasmExit': (sp) => { - sp >>>= 0; - const code = this.mem.getInt32(sp + 8, true); - this.exited = true; - delete this._inst; - delete this._values; - delete this._goRefCounts; - delete this._ids; - delete this._idPool; - this.exit(code); - }, - - // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) - 'runtime.wasmWrite': (sp) => { - sp >>>= 0; - const fd = getInt64(sp + 8); - const p = getInt64(sp + 16); - const n = this.mem.getInt32(sp + 24, true); - fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); - }, - - // func resetMemoryDataView() - 'runtime.resetMemoryDataView': (sp) => { - sp >>>= 0; - this.mem = new DataView(this._inst.exports.mem.buffer); - }, - - // func nanotime1() int64 - 'runtime.nanotime1': (sp) => { - sp >>>= 0; - setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); - }, - - // func walltime() (sec int64, nsec int32) - 'runtime.walltime': (sp) => { - sp >>>= 0; - const msec = new Date().getTime(); - setInt64(sp + 8, msec / 1000); - this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); - }, - - // func scheduleTimeoutEvent(delay int64) int32 - 'runtime.scheduleTimeoutEvent': (sp) => { - sp >>>= 0; - const id = this._nextCallbackTimeoutID; - this._nextCallbackTimeoutID++; - this._scheduledTimeouts.set( - id, - setTimeout( - () => { - this._resume(); - while (this._scheduledTimeouts.has(id)) { - // for some reason Go failed to register the timeout event, log and try again - // (temporary workaround for https://github.com/golang/go/issues/28975) - console.warn('scheduleTimeoutEvent: missed timeout event'); - this._resume(); - } - }, - getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early - ), - ); - this.mem.setInt32(sp + 16, id, true); - }, - - // func clearTimeoutEvent(id int32) - 'runtime.clearTimeoutEvent': (sp) => { - sp >>>= 0; - const id = this.mem.getInt32(sp + 8, true); - clearTimeout(this._scheduledTimeouts.get(id)); - this._scheduledTimeouts.delete(id); - }, - - // func getRandomData(r []byte) - 'runtime.getRandomData': (sp) => { - sp >>>= 0; - globalThis.crypto.getRandomValues(loadSlice(sp + 8)); - }, - - // func finalizeRef(v ref) - 'syscall/js.finalizeRef': (sp) => { - sp >>>= 0; - const id = this.mem.getUint32(sp + 8, true); - this._goRefCounts[id]--; - if (this._goRefCounts[id] === 0) { - const v = this._values[id]; - this._values[id] = null; - this._ids.delete(v); - this._idPool.push(id); - } - }, - - // func stringVal(value string) ref - 'syscall/js.stringVal': (sp) => { - sp >>>= 0; - storeValue(sp + 24, loadString(sp + 8)); - }, - - // func valueGet(v ref, p string) ref - 'syscall/js.valueGet': (sp) => { - sp >>>= 0; - const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 32, result); - }, - - // func valueSet(v ref, p string, x ref) - 'syscall/js.valueSet': (sp) => { - sp >>>= 0; - Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); - }, - - // func valueDelete(v ref, p string) - 'syscall/js.valueDelete': (sp) => { - sp >>>= 0; - Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); - }, - - // func valueIndex(v ref, i int) ref - 'syscall/js.valueIndex': (sp) => { - sp >>>= 0; - storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); - }, - - // valueSetIndex(v ref, i int, x ref) - 'syscall/js.valueSetIndex': (sp) => { - sp >>>= 0; - Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); - }, - - // func valueCall(v ref, m string, args []ref) (ref, bool) - 'syscall/js.valueCall': (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const m = Reflect.get(v, loadString(sp + 16)); - const args = loadSliceOfValues(sp + 32); - const result = Reflect.apply(m, v, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 56, result); - this.mem.setUint8(sp + 64, 1); - } catch (err) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 56, err); - this.mem.setUint8(sp + 64, 0); - } - }, - - // func valueInvoke(v ref, args []ref) (ref, bool) - 'syscall/js.valueInvoke': (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - const result = Reflect.apply(v, undefined, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, result); - this.mem.setUint8(sp + 48, 1); - } catch (err) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, err); - this.mem.setUint8(sp + 48, 0); - } - }, - - // func valueNew(v ref, args []ref) (ref, bool) - 'syscall/js.valueNew': (sp) => { - sp >>>= 0; - try { - const v = loadValue(sp + 8); - const args = loadSliceOfValues(sp + 16); - const result = Reflect.construct(v, args); - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, result); - this.mem.setUint8(sp + 48, 1); - } catch (err) { - sp = this._inst.exports.getsp() >>> 0; // see comment above - storeValue(sp + 40, err); - this.mem.setUint8(sp + 48, 0); - } - }, - - // func valueLength(v ref) int - 'syscall/js.valueLength': (sp) => { - sp >>>= 0; - setInt64(sp + 16, Number.parseInt(loadValue(sp + 8).length)); - }, - - // valuePrepareString(v ref) (ref, int) - 'syscall/js.valuePrepareString': (sp) => { - sp >>>= 0; - const str = encoder.encode(String(loadValue(sp + 8))); - storeValue(sp + 16, str); - setInt64(sp + 24, str.length); - }, - - // valueLoadString(v ref, b []byte) - 'syscall/js.valueLoadString': (sp) => { - sp >>>= 0; - const str = loadValue(sp + 8); - loadSlice(sp + 16).set(str); - }, - - // func valueInstanceOf(v ref, t ref) bool - 'syscall/js.valueInstanceOf': (sp) => { - sp >>>= 0; - this.mem.setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16) ? 1 : 0); - }, - - // func copyBytesToGo(dst []byte, src ref) (int, bool) - 'syscall/js.copyBytesToGo': (sp) => { - sp >>>= 0; - const dst = loadSlice(sp + 8); - const src = loadValue(sp + 32); - if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { - this.mem.setUint8(sp + 48, 0); - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(sp + 40, toCopy.length); - this.mem.setUint8(sp + 48, 1); - }, - - // func copyBytesToJS(dst ref, src []byte) (int, bool) - 'syscall/js.copyBytesToJS': (sp) => { - sp >>>= 0; - const dst = loadValue(sp + 8); - const src = loadSlice(sp + 16); - if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { - this.mem.setUint8(sp + 48, 0); - return; - } - const toCopy = src.subarray(0, dst.length); - dst.set(toCopy); - setInt64(sp + 40, toCopy.length); - this.mem.setUint8(sp + 48, 1); - }, - - debug: (value) => { - console.log(value); - }, - }, - }; - } - - async run(instance) { - if (!(instance instanceof WebAssembly.Instance)) { - throw new Error('Go.run: WebAssembly.Instance expected'); - } - this._inst = instance; - this.mem = new DataView(this._inst.exports.mem.buffer); - this._values = [ - // JS values that Go currently has references to, indexed by reference id - Number.NaN, - 0, - null, - true, - false, - globalThis, - this, - ]; - this._goRefCounts = new Array(this._values.length).fill(Number.POSITIVE_INFINITY); // number of references that Go has to a JS value, indexed by reference id - this._ids = new Map([ - // mapping from JS values to reference ids - [0, 1], - [null, 2], - [true, 3], - [false, 4], - [globalThis, 5], - [this, 6], - ]); - this._idPool = []; // unused ids that have been garbage collected - this.exited = false; // whether the Go program has exited - - // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. - let offset = 4096; - - const strPtr = (str) => { - const ptr = offset; - const bytes = encoder.encode(`${str}\0`); - new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); - offset += bytes.length; - if (offset % 8 !== 0) { - offset += 8 - (offset % 8); - } - return ptr; - }; - - const argc = this.argv.length; - - const argvPtrs = []; - this.argv.forEach((arg) => { - argvPtrs.push(strPtr(arg)); - }); - argvPtrs.push(0); - - const keys = Object.keys(this.env).sort(); - keys.forEach((key) => { - argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); - }); - argvPtrs.push(0); - - const argv = offset; - argvPtrs.forEach((ptr) => { - this.mem.setUint32(offset, ptr, true); - this.mem.setUint32(offset + 4, 0, true); - offset += 8; - }); - - this._inst.exports.run(argc, argv); - if (this.exited) { - this._resolveExitPromise(); - } - await this._exitPromise; - } - - private _resume() { - if (this.exited) { - throw new Error('Go program has already exited'); - } - this._inst.exports.resume(); - if (this.exited) { - this._resolveExitPromise(); - } - } - - private _makeFuncWrapper(id) { - const go = this; - return function () { - const event = { id: id, this: this, args: arguments }; - go._pendingEvent = event; - go._resume(); - return event.result; - }; - } + public importObject; + constructor() { + this.argv = ['js']; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn('exit code:', code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + }; + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + }; + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + }; + + const storeValue = (addr, v) => { + const nanHead = 0x7ff80000; + + if (typeof v === 'number' && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case 'object': + if (v !== null) { + typeFlag = 1; + } + break; + case 'string': + typeFlag = 2; + break; + case 'symbol': + typeFlag = 3; + break; + case 'function': + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + }; + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + }; + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + }; + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + }; + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + gojs: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + 'runtime.wasmExit': (sp) => { + sp >>>= 0; + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + 'runtime.wasmWrite': (sp) => { + sp >>>= 0; + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + 'runtime.resetMemoryDataView': (sp) => { + sp >>>= 0; + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + 'runtime.nanotime1': (sp) => { + sp >>>= 0; + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + 'runtime.walltime': (sp) => { + sp >>>= 0; + const msec = new Date().getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + 'runtime.scheduleTimeoutEvent': (sp) => { + sp >>>= 0; + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set( + id, + setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn('scheduleTimeoutEvent: missed timeout event'); + this._resume(); + } + }, + getInt64(sp + 8) + 1 // setTimeout has been seen to fire up to 1 millisecond early + ) + ); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + 'runtime.clearTimeoutEvent': (sp) => { + sp >>>= 0; + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + 'runtime.getRandomData': (sp) => { + sp >>>= 0; + globalThis.crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + 'syscall/js.finalizeRef': (sp) => { + sp >>>= 0; + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + 'syscall/js.stringVal': (sp) => { + sp >>>= 0; + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + 'syscall/js.valueGet': (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + 'syscall/js.valueSet': (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + 'syscall/js.valueDelete': (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + 'syscall/js.valueIndex': (sp) => { + sp >>>= 0; + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + 'syscall/js.valueSetIndex': (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + 'syscall/js.valueCall': (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + 'syscall/js.valueInvoke': (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + 'syscall/js.valueNew': (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + 'syscall/js.valueLength': (sp) => { + sp >>>= 0; + setInt64(sp + 16, Number.parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + 'syscall/js.valuePrepareString': (sp) => { + sp >>>= 0; + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + 'syscall/js.valueLoadString': (sp) => { + sp >>>= 0; + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + 'syscall/js.valueInstanceOf': (sp) => { + sp >>>= 0; + this.mem.setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + 'syscall/js.copyBytesToGo': (sp) => { + sp >>>= 0; + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + 'syscall/js.copyBytesToJS': (sp) => { + sp >>>= 0; + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + debug: (value) => { + console.log(value); + }, + }, + }; + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error('Go.run: WebAssembly.Instance expected'); + } + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ + // JS values that Go currently has references to, indexed by reference id + Number.NaN, + 0, + null, + true, + false, + globalThis, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Number.POSITIVE_INFINITY); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ + // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [globalThis, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(`${str}\0`); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + private _resume() { + if (this.exited) { + throw new Error('Go program has already exited'); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + private _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } } diff --git a/packages/compiler/src/shared/ast.ts b/packages/compiler/src/shared/ast.ts index 1f7e6d19a..6030d735e 100644 --- a/packages/compiler/src/shared/ast.ts +++ b/packages/compiler/src/shared/ast.ts @@ -1,88 +1,104 @@ -export type ParentNode = RootNode | ElementNode | ComponentNode | CustomElementNode | FragmentNode | ExpressionNode; +export type ParentNode = + | RootNode + | ElementNode + | ComponentNode + | CustomElementNode + | FragmentNode + | ExpressionNode; export type LiteralNode = TextNode | DoctypeNode | CommentNode | FrontmatterNode; -export type Node = RootNode | ElementNode | ComponentNode | CustomElementNode | FragmentNode | ExpressionNode | TextNode | FrontmatterNode | DoctypeNode | CommentNode; +export type Node = + | RootNode + | ElementNode + | ComponentNode + | CustomElementNode + | FragmentNode + | ExpressionNode + | TextNode + | FrontmatterNode + | DoctypeNode + | CommentNode; export interface Position { - start: Point; - end?: Point; + start: Point; + end?: Point; } export interface Point { - /** 1-based line number */ - line: number; - /** 1-based column number, per-line */ - column: number; - /** 0-based byte offset */ - offset: number; + /** 1-based line number */ + line: number; + /** 1-based column number, per-line */ + column: number; + /** 0-based byte offset */ + offset: number; } export interface BaseNode { - type: string; - position?: Position; + type: string; + position?: Position; } export interface ParentLikeNode extends BaseNode { - type: 'element' | 'component' | 'custom-element' | 'fragment' | 'expression' | 'root'; - children: Node[]; + type: 'element' | 'component' | 'custom-element' | 'fragment' | 'expression' | 'root'; + children: Node[]; } export interface ValueNode extends BaseNode { - value: string; + value: string; } export interface RootNode extends ParentLikeNode { - type: 'root'; + type: 'root'; } export interface AttributeNode extends BaseNode { - type: 'attribute'; - kind: 'quoted' | 'empty' | 'expression' | 'spread' | 'shorthand' | 'template-literal'; - name: string; - value: string; - raw?: string; + type: 'attribute'; + kind: 'quoted' | 'empty' | 'expression' | 'spread' | 'shorthand' | 'template-literal'; + name: string; + value: string; + raw?: string; } export interface TextNode extends ValueNode { - type: 'text'; + type: 'text'; } export interface ElementNode extends ParentLikeNode { - type: 'element'; - name: string; - attributes: AttributeNode[]; + type: 'element'; + name: string; + attributes: AttributeNode[]; } export interface FragmentNode extends ParentLikeNode { - type: 'fragment'; - name: string; - attributes: AttributeNode[]; + type: 'fragment'; + name: string; + attributes: AttributeNode[]; } export interface ComponentNode extends ParentLikeNode { - type: 'component'; - name: string; - attributes: AttributeNode[]; + type: 'component'; + name: string; + attributes: AttributeNode[]; } export interface CustomElementNode extends ParentLikeNode { - type: 'custom-element'; - name: string; - attributes: AttributeNode[]; + type: 'custom-element'; + name: string; + attributes: AttributeNode[]; } export type TagLikeNode = ElementNode | FragmentNode | ComponentNode | CustomElementNode; export interface DoctypeNode extends ValueNode { - type: 'doctype'; + type: 'doctype'; } export interface CommentNode extends ValueNode { - type: 'comment'; + type: 'comment'; } export interface FrontmatterNode extends ValueNode { - type: 'frontmatter'; + type: 'frontmatter'; } export interface ExpressionNode extends ParentLikeNode { - type: 'expression'; + type: 'expression'; } diff --git a/packages/compiler/src/shared/diagnostics.ts b/packages/compiler/src/shared/diagnostics.ts index 502184945..dce9273ef 100644 --- a/packages/compiler/src/shared/diagnostics.ts +++ b/packages/compiler/src/shared/diagnostics.ts @@ -1,16 +1,16 @@ export enum DiagnosticCode { - ERROR = 1000, - ERROR_UNTERMINATED_JS_COMMENT = 1001, - ERROR_FRAGMENT_SHORTHAND_ATTRS = 1002, - ERROR_UNMATCHED_IMPORT = 1003, - ERROR_UNSUPPORTED_SLOT_ATTRIBUTE = 1004, - WARNING = 2000, - WARNING_UNTERMINATED_HTML_COMMENT = 2001, - WARNING_UNCLOSED_HTML_TAG = 2002, - WARNING_DEPRECATED_DIRECTIVE = 2003, - WARNING_IGNORED_DIRECTIVE = 2004, - WARNING_UNSUPPORTED_EXPRESSION = 2005, - WARNING_SET_WITH_CHILDREN = 2006, - INFO = 3000, - HINT = 4000, + ERROR = 1000, + ERROR_UNTERMINATED_JS_COMMENT = 1001, + ERROR_FRAGMENT_SHORTHAND_ATTRS = 1002, + ERROR_UNMATCHED_IMPORT = 1003, + ERROR_UNSUPPORTED_SLOT_ATTRIBUTE = 1004, + WARNING = 2000, + WARNING_UNTERMINATED_HTML_COMMENT = 2001, + WARNING_UNCLOSED_HTML_TAG = 2002, + WARNING_DEPRECATED_DIRECTIVE = 2003, + WARNING_IGNORED_DIRECTIVE = 2004, + WARNING_UNSUPPORTED_EXPRESSION = 2005, + WARNING_SET_WITH_CHILDREN = 2006, + INFO = 3000, + HINT = 4000, } diff --git a/packages/compiler/src/shared/types.ts b/packages/compiler/src/shared/types.ts index 9c70f8a08..8748307e1 100644 --- a/packages/compiler/src/shared/types.ts +++ b/packages/compiler/src/shared/types.ts @@ -3,129 +3,132 @@ import type { DiagnosticCode } from './diagnostics'; export type * from './ast'; export interface PreprocessorResult { - code: string; - map?: string; + code: string; + map?: string; } export interface PreprocessorError { - error: string; + error: string; } export interface ParseOptions { - position?: boolean; + position?: boolean; } export enum DiagnosticSeverity { - Error = 1, - Warning = 2, - Information = 3, - Hint = 4, + Error = 1, + Warning = 2, + Information = 3, + Hint = 4, } export interface DiagnosticMessage { - severity: DiagnosticSeverity; - code: DiagnosticCode; - location: DiagnosticLocation; - hint?: string; - text: string; + severity: DiagnosticSeverity; + code: DiagnosticCode; + location: DiagnosticLocation; + hint?: string; + text: string; } export interface DiagnosticLocation { - file: string; - // 1-based - line: number; - // 1-based - column: number; - length: number; + file: string; + // 1-based + line: number; + // 1-based + column: number; + length: number; } export interface TransformOptions { - internalURL?: string; - filename?: string; - normalizedFilename?: string; - sourcemap?: boolean | 'inline' | 'external' | 'both'; - astroGlobalArgs?: string; - compact?: boolean; - resultScopedSlot?: boolean; - scopedStyleStrategy?: 'where' | 'class' | 'attribute'; - /** - * @deprecated "as" has been removed and no longer has any effect! - */ - as?: 'document' | 'fragment'; - transitionsAnimationURL?: string; - resolvePath?: (specifier: string) => Promise; - preprocessStyle?: (content: string, attrs: Record) => null | Promise; - annotateSourceFile?: boolean; - /** - * Render script tags to be processed (e.g. script tags that have no attributes or only a `src` attribute) - * using a `renderScript` function from `internalURL`, instead of stripping the script entirely. - * @experimental - */ - renderScript?: boolean; + internalURL?: string; + filename?: string; + normalizedFilename?: string; + sourcemap?: boolean | 'inline' | 'external' | 'both'; + astroGlobalArgs?: string; + compact?: boolean; + resultScopedSlot?: boolean; + scopedStyleStrategy?: 'where' | 'class' | 'attribute'; + /** + * @deprecated "as" has been removed and no longer has any effect! + */ + as?: 'document' | 'fragment'; + transitionsAnimationURL?: string; + resolvePath?: (specifier: string) => Promise; + preprocessStyle?: ( + content: string, + attrs: Record + ) => null | Promise; + annotateSourceFile?: boolean; + /** + * Render script tags to be processed (e.g. script tags that have no attributes or only a `src` attribute) + * using a `renderScript` function from `internalURL`, instead of stripping the script entirely. + * @experimental + */ + renderScript?: boolean; } export type ConvertToTSXOptions = Pick; export type HoistedScript = { type: string } & ( - | { - type: 'external'; - src: string; - } - | { - type: 'inline'; - code: string; - map: string; - } + | { + type: 'external'; + src: string; + } + | { + type: 'inline'; + code: string; + map: string; + } ); export interface HydratedComponent { - exportName: string; - specifier: string; - resolvedPath: string; + exportName: string; + specifier: string; + resolvedPath: string; } export interface TransformResult { - code: string; - map: string; - scope: string; - styleError: string[]; - diagnostics: DiagnosticMessage[]; - css: string[]; - scripts: HoistedScript[]; - hydratedComponents: HydratedComponent[]; - clientOnlyComponents: HydratedComponent[]; - containsHead: boolean; - propagation: boolean; + code: string; + map: string; + scope: string; + styleError: string[]; + diagnostics: DiagnosticMessage[]; + css: string[]; + scripts: HoistedScript[]; + hydratedComponents: HydratedComponent[]; + clientOnlyComponents: HydratedComponent[]; + containsHead: boolean; + propagation: boolean; } export interface SourceMap { - file: string; - mappings: string; - names: string[]; - sources: string[]; - sourcesContent: string[]; - version: number; + file: string; + mappings: string; + names: string[]; + sources: string[]; + sourcesContent: string[]; + version: number; } export interface TSXResult { - code: string; - map: SourceMap; - diagnostics: DiagnosticMessage[]; - metaRanges: { - frontmatter: { - start: number; - end: number; - }; - body: { - start: number; - end: number; - }; - }; + code: string; + map: SourceMap; + diagnostics: DiagnosticMessage[]; + metaRanges: { + frontmatter: { + start: number; + end: number; + }; + body: { + start: number; + end: number; + }; + }; } export interface ParseResult { - ast: RootNode; - diagnostics: DiagnosticMessage[]; + ast: RootNode; + diagnostics: DiagnosticMessage[]; } // This function transforms a single JavaScript file. It can be used to minify @@ -135,11 +138,17 @@ export interface ParseResult { // // Works in node: yes // Works in browser: yes -export declare function transform(input: string, options?: TransformOptions): Promise; +export declare function transform( + input: string, + options?: TransformOptions +): Promise; export declare function parse(input: string, options?: ParseOptions): Promise; -export declare function convertToTSX(input: string, options?: ConvertToTSXOptions): Promise; +export declare function convertToTSX( + input: string, + options?: ConvertToTSXOptions +): Promise; // This configures the browser-based version of astro. It is necessary to // call this first and wait for the returned promise to be resolved before @@ -162,7 +171,7 @@ export declare function initialize(options: InitializeOptions): Promise; export declare function teardown(): void; export interface InitializeOptions { - // The URL of the "astro.wasm" file. This must be provided when running - // astro in the browser. - wasmURL?: string; + // The URL of the "astro.wasm" file. This must be provided when running + // astro in the browser. + wasmURL?: string; } diff --git a/packages/compiler/test/bad-styles/sass.ts b/packages/compiler/test/bad-styles/sass.ts index a4a2f8551..bafdcb1b6 100644 --- a/packages/compiler/test/bad-styles/sass.ts +++ b/packages/compiler/test/bad-styles/sass.ts @@ -16,16 +16,16 @@ const FIXTURE = ` `; test('it works', async () => { - const result = await transform(FIXTURE, { - filename: '/users/astro/apps/pacman/src/pages/index.astro', - async preprocessStyle() { - return { - error: new Error('Unable to convert').message, - }; - }, - }); - assert.equal(result.styleError.length, 2); - assert.equal(result.styleError[0], 'Unable to convert'); + const result = await transform(FIXTURE, { + filename: '/users/astro/apps/pacman/src/pages/index.astro', + async preprocessStyle() { + return { + error: new Error('Unable to convert').message, + }; + }, + }); + assert.equal(result.styleError.length, 2); + assert.equal(result.styleError[0], 'Unable to convert'); }); test.run(); diff --git a/packages/compiler/test/bad-styles/unclosed-style.ts b/packages/compiler/test/bad-styles/unclosed-style.ts index 9a0517b89..ef46411e4 100644 --- a/packages/compiler/test/bad-styles/unclosed-style.ts +++ b/packages/compiler/test/bad-styles/unclosed-style.ts @@ -3,18 +3,18 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('can compile unfinished style', async () => { - let error = 0; - let result: unknown; - try { - result = await parse('`, - { filename: '/src/components/Foo.astro' }, - ); - assert.ok(Array.isArray(result.diagnostics)); - assert.is(result.diagnostics.length, 1); - assert.is(result.diagnostics[0].code, 2007); + { filename: '/src/components/Foo.astro' } + ); + assert.ok(Array.isArray(result.diagnostics)); + assert.is(result.diagnostics.length, 1); + assert.is(result.diagnostics[0].code, 2007); }); test('define:vars no warning', async () => { - const result = await transform( - `
+ const result = await transform( + `
`, - { filename: '/src/components/Foo.astro' }, - ); - assert.ok(Array.isArray(result.diagnostics)); - assert.is(result.diagnostics.length, 0); + { filename: '/src/components/Foo.astro' } + ); + assert.ok(Array.isArray(result.diagnostics)); + assert.is(result.diagnostics.length, 0); }); test.run(); diff --git a/packages/compiler/test/errors/fragment-shorthand.ts b/packages/compiler/test/errors/fragment-shorthand.ts index 78bbc15fc..b83eacea5 100644 --- a/packages/compiler/test/errors/fragment-shorthand.ts +++ b/packages/compiler/test/errors/fragment-shorthand.ts @@ -13,18 +13,24 @@ const FIXTURE = ` let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - filename: '/src/components/fragment.astro', - }); + result = await transform(FIXTURE, { + filename: '/src/components/fragment.astro', + }); }); test('got a tokenizer error', () => { - assert.ok(Array.isArray(result.diagnostics)); - assert.is(result.diagnostics.length, 1); - assert.is(result.diagnostics[0].text, 'Unable to assign attributes when using <> Fragment shorthand syntax!'); - const loc = result.diagnostics[0].location; - assert.is(FIXTURE.split('\n')[loc.line - 1], ` < data-test="hello">
`); - assert.is(FIXTURE.split('\n')[loc.line - 1].slice(loc.column - 1, loc.column - 1 + loc.length), `< data-test="hello">`); + assert.ok(Array.isArray(result.diagnostics)); + assert.is(result.diagnostics.length, 1); + assert.is( + result.diagnostics[0].text, + 'Unable to assign attributes when using <> Fragment shorthand syntax!' + ); + const loc = result.diagnostics[0].location; + assert.is(FIXTURE.split('\n')[loc.line - 1], ` < data-test="hello">
`); + assert.is( + FIXTURE.split('\n')[loc.line - 1].slice(loc.column - 1, loc.column - 1 + loc.length), + `< data-test="hello">` + ); }); test.run(); diff --git a/packages/compiler/test/errors/html-comment.ts b/packages/compiler/test/errors/html-comment.ts index 01f77357e..01c35dbe0 100644 --- a/packages/compiler/test/errors/html-comment.ts +++ b/packages/compiler/test/errors/html-comment.ts @@ -15,16 +15,16 @@ const FIXTURE = ` let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - filename: '/src/components/EOF.astro', - }); + result = await transform(FIXTURE, { + filename: '/src/components/EOF.astro', + }); }); test('html comment error', () => { - assert.ok(Array.isArray(result.diagnostics)); - assert.is(result.diagnostics.length, 1); - assert.is(result.diagnostics[0].text, 'Unterminated comment'); - assert.is(FIXTURE.split('\n')[result.diagnostics[0].location.line - 1], '
`; - const { ast } = await parse(input); + const { ast } = await parse(input); - const comment = ast.children[1]; - assert.is(comment.type, 'comment'); - assert.ok(comment.position.start, 'Expected serialized output to contain a start position'); - assert.ok(comment.position.end, 'Expected serialized output to contain an end position'); + const comment = ast.children[1]; + assert.is(comment.type, 'comment'); + assert.ok(comment.position.start, 'Expected serialized output to contain a start position'); + assert.ok(comment.position.end, 'Expected serialized output to contain an end position'); }); test('include start and end positions for text', async () => { - const input = `--- + const input = `--- // Hello world! --- Hello world!`; - const { ast } = await parse(input); + const { ast } = await parse(input); - const text = ast.children[1]; - assert.is(text.type, 'text'); - assert.ok(text.position.start, 'Expected serialized output to contain a start position'); - assert.ok(text.position.end, 'Expected serialized output to contain an end position'); + const text = ast.children[1]; + assert.is(text.type, 'text'); + assert.ok(text.position.start, 'Expected serialized output to contain a start position'); + assert.ok(text.position.end, 'Expected serialized output to contain an end position'); }); test('include start and end positions for self-closing tags', async () => { - const input = ''; - const { ast } = await parse(input); - - const element = ast.children[0]; - assert.is(element.type, 'element'); - assert.is(element.name, 'input'); - assert.ok(element.position.start, 'Expected serialized output to contain a start position'); - assert.ok(element.position.end, 'Expected serialized output to contain an end position'); + const input = ''; + const { ast } = await parse(input); + + const element = ast.children[0]; + assert.is(element.type, 'element'); + assert.is(element.name, 'input'); + assert.ok(element.position.start, 'Expected serialized output to contain a start position'); + assert.ok(element.position.end, 'Expected serialized output to contain an end position'); }); test('include correct start and end position for self-closing tag', async () => { - const input = ` + const input = `
  • `; - const { ast } = await parse(input); - - const li = ast.children[1]; - assert.is(li.name, 'li'); - assert.ok(li.position.start, 'Expected serialized output to contain a start position'); - assert.ok(li.position.end, 'Expected serialized output to contain an end position'); - - assert.equal(li.position.start, { line: 3, column: 1, offset: 26 }, 'Expected serialized output to contain a start position'); - assert.equal(li.position.end, { line: 3, column: 6, offset: 31 }, 'Expected serialized output to contain an end position'); + const { ast } = await parse(input); + + const li = ast.children[1]; + assert.is(li.name, 'li'); + assert.ok(li.position.start, 'Expected serialized output to contain a start position'); + assert.ok(li.position.end, 'Expected serialized output to contain an end position'); + + assert.equal( + li.position.start, + { line: 3, column: 1, offset: 26 }, + 'Expected serialized output to contain a start position' + ); + assert.equal( + li.position.end, + { line: 3, column: 6, offset: 31 }, + 'Expected serialized output to contain an end position' + ); }); test('include correct start and end position for normal closing tag', async () => { - const input = ` + const input = `
  • `; - const { ast } = await parse(input); - - const li = ast.children[1]; - assert.is(li.name, 'li'); - assert.ok(li.position.start, 'Expected serialized output to contain a start position'); - assert.ok(li.position.end, 'Expected serialized output to contain an end position'); - - assert.equal(li.position.start, { line: 3, column: 1, offset: 26 }, 'Expected serialized output to contain a start position'); - assert.equal(li.position.end, { line: 3, column: 10, offset: 35 }, 'Expected serialized output to contain an end position'); + const { ast } = await parse(input); + + const li = ast.children[1]; + assert.is(li.name, 'li'); + assert.ok(li.position.start, 'Expected serialized output to contain a start position'); + assert.ok(li.position.end, 'Expected serialized output to contain an end position'); + + assert.equal( + li.position.start, + { line: 3, column: 1, offset: 26 }, + 'Expected serialized output to contain a start position' + ); + assert.equal( + li.position.end, + { line: 3, column: 10, offset: 35 }, + 'Expected serialized output to contain an end position' + ); }); test('include start and end position if frontmatter is only thing in file (#802)', async () => { - const input = `--- + const input = `--- ---`; - const { ast } = await parse(input); - - const frontmatter = ast.children[0]; - assert.is(frontmatter.type, 'frontmatter'); - assert.ok(frontmatter.position.start, 'Expected serialized output to contain a start position'); - assert.ok(frontmatter.position.end, 'Expected serialized output to contain an end position'); - - assert.equal(frontmatter.position.start, { line: 1, column: 1, offset: 0 }, 'Expected serialized output to contain a start position'); - assert.equal(frontmatter.position.end, { line: 2, column: 4, offset: 7 }, 'Expected serialized output to contain an end position'); + const { ast } = await parse(input); + + const frontmatter = ast.children[0]; + assert.is(frontmatter.type, 'frontmatter'); + assert.ok(frontmatter.position.start, 'Expected serialized output to contain a start position'); + assert.ok(frontmatter.position.end, 'Expected serialized output to contain an end position'); + + assert.equal( + frontmatter.position.start, + { line: 1, column: 1, offset: 0 }, + 'Expected serialized output to contain a start position' + ); + assert.equal( + frontmatter.position.end, + { line: 2, column: 4, offset: 7 }, + 'Expected serialized output to contain an end position' + ); }); test.run(); diff --git a/packages/compiler/test/parse/serialize.ts b/packages/compiler/test/parse/serialize.ts index 3bdef67a3..4eadd1760 100644 --- a/packages/compiler/test/parse/serialize.ts +++ b/packages/compiler/test/parse/serialize.ts @@ -27,34 +27,34 @@ let content = "Testing 123"; let result: unknown; test.before(async () => { - const { ast } = await parse(FIXTURE); - try { - result = serialize(ast); - } catch (e) { - // eslint-disable-next-line no-console - console.log(e); - } + const { ast } = await parse(FIXTURE); + try { + result = serialize(ast); + } catch (e) { + // eslint-disable-next-line no-console + console.log(e); + } }); test('serialize', () => { - assert.type(result, 'string', `Expected "serialize" to return an object!`); - assert.equal(result, FIXTURE, 'Expected serialized output to equal input'); + assert.type(result, 'string', `Expected "serialize" to return an object!`); + assert.equal(result, FIXTURE, 'Expected serialized output to equal input'); }); test('self-close elements', async () => { - const input = '
    '; - const { ast } = await parse(input); - const output = serialize(ast, { selfClose: false }); - const selfClosedOutput = serialize(ast); - assert.equal(output, '
    ', 'Expected serialized output to equal
    '); - assert.equal(selfClosedOutput, input, `Expected serialized output to equal ${input}`); + const input = '
    '; + const { ast } = await parse(input); + const output = serialize(ast, { selfClose: false }); + const selfClosedOutput = serialize(ast); + assert.equal(output, '
    ', 'Expected serialized output to equal
    '); + assert.equal(selfClosedOutput, input, `Expected serialized output to equal ${input}`); }); test('raw attributes', async () => { - const input = `
    `; - const { ast } = await parse(input); - const output = serialize(ast); - assert.equal(output, input, `Expected serialized output to equal ${input}`); + const input = `
    `; + const { ast } = await parse(input); + const output = serialize(ast); + assert.equal(output, input, `Expected serialized output to equal ${input}`); }); test.run(); diff --git a/packages/compiler/test/resolve-path/preserve.ts b/packages/compiler/test/resolve-path/preserve.ts index 7c372d3ab..3141f46d7 100644 --- a/packages/compiler/test/resolve-path/preserve.ts +++ b/packages/compiler/test/resolve-path/preserve.ts @@ -15,21 +15,21 @@ import { name } './foo.module.css' let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - resolvePath: async (s) => s, - }); + result = await transform(FIXTURE, { + resolvePath: async (s) => s, + }); }); test('preserve path', () => { - assert.match(result.code, /"client:load":true.*"client:component-path":\("\.\/Foo\.jsx"\)/); - assert.match(result.code, /"client:only":"react".*"client:component-path":\("\.\/Foo\.jsx"\)/); + assert.match(result.code, /"client:load":true.*"client:component-path":\("\.\/Foo\.jsx"\)/); + assert.match(result.code, /"client:only":"react".*"client:component-path":\("\.\/Foo\.jsx"\)/); }); test('no metadata', () => { - assert.not.match(result.code, /\$\$metadata/); - assert.not.match(result.code, /\$\$createMetadata/); - assert.not.match(result.code, /createMetadata as \$\$createMetadata/); - assert.not.match(result.code, /import \* as \$\$module\d/); + assert.not.match(result.code, /\$\$metadata/); + assert.not.match(result.code, /\$\$createMetadata/); + assert.not.match(result.code, /createMetadata as \$\$createMetadata/); + assert.not.match(result.code, /import \* as \$\$module\d/); }); test.run(); diff --git a/packages/compiler/test/scope/same-source.ts b/packages/compiler/test/scope/same-source.ts index 8b1870458..8e10579de 100644 --- a/packages/compiler/test/scope/same-source.ts +++ b/packages/compiler/test/scope/same-source.ts @@ -18,28 +18,28 @@ div { `.trim(); function grabAstroScope(code: string) { - const match = /astro-[0-9A-Za-z]+/.exec(code); - if (match) { - return match[0]; - } - return null; + const match = /astro-[0-9A-Za-z]+/.exec(code); + if (match) { + return match[0]; + } + return null; } test('Similar components have different scoped class names', async () => { - let result = await transform(FIXTURE, { - normalizedFilename: '/src/pages/index.astro', - }); - const scopeA = grabAstroScope(result.code); - assert.ok(scopeA); + let result = await transform(FIXTURE, { + normalizedFilename: '/src/pages/index.astro', + }); + const scopeA = grabAstroScope(result.code); + assert.ok(scopeA); - result = await transform(FIXTURE, { - normalizedFilename: '/src/pages/two.astro', - }); + result = await transform(FIXTURE, { + normalizedFilename: '/src/pages/two.astro', + }); - const scopeB = grabAstroScope(result.code); - assert.ok(scopeB); + const scopeB = grabAstroScope(result.code); + assert.ok(scopeB); - assert.ok(scopeA !== scopeB, 'The scopes should not match for different files'); + assert.ok(scopeA !== scopeB, 'The scopes should not match for different files'); }); test.run(); diff --git a/packages/compiler/test/scripts/isinline-hint.ts b/packages/compiler/test/scripts/isinline-hint.ts index c8313efbb..6381d1fd4 100644 --- a/packages/compiler/test/scripts/isinline-hint.ts +++ b/packages/compiler/test/scripts/isinline-hint.ts @@ -3,15 +3,15 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('reports a hint for adding attributes to a script tag without is:inline', async () => { - const result = await transform(``); - assert.equal(result.diagnostics[0].severity, 4); - assert.match(result.diagnostics[0].text, /\#script-processing/); + const result = await transform(``); + assert.equal(result.diagnostics[0].severity, 4); + assert.match(result.diagnostics[0].text, /\#script-processing/); }); test('does not report a diagnostic for the src attribute', async () => { - const result = await transform(``); - console.log(result.diagnostics); - assert.equal(result.diagnostics.length, 0); + const result = await transform(``); + console.log(result.diagnostics); + assert.equal(result.diagnostics.length, 0); }); test.run(); diff --git a/packages/compiler/test/slot-result/result.ts b/packages/compiler/test/slot-result/result.ts index 2ca0f9542..4a9d54580 100644 --- a/packages/compiler/test/slot-result/result.ts +++ b/packages/compiler/test/slot-result/result.ts @@ -13,14 +13,14 @@ import Parent from './Parent.astro'; let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - resolvePath: async (s) => s, - resultScopedSlot: true, - }); + result = await transform(FIXTURE, { + resolvePath: async (s) => s, + resultScopedSlot: true, + }); }); test('resultScopedSlot: includes the result object in the call to the slot', () => { - assert.match(result.code, /\(\$\$result\) =>/); + assert.match(result.code, /\(\$\$result\) =>/); }); test.run(); diff --git a/packages/compiler/test/static-extraction/css.ts b/packages/compiler/test/static-extraction/css.ts index 4635545e8..3ef654a11 100644 --- a/packages/compiler/test/static-extraction/css.ts +++ b/packages/compiler/test/static-extraction/css.ts @@ -14,19 +14,23 @@ const FIXTURE = ` let result: unknown; test.before(async () => { - result = await transform(FIXTURE); + result = await transform(FIXTURE); }); test('extracts styles', () => { - assert.equal(result.css.length, 1, `Incorrect CSS returned. Expected a length of 1 and got ${result.css.length}`); + assert.equal( + result.css.length, + 1, + `Incorrect CSS returned. Expected a length of 1 and got ${result.css.length}` + ); }); test('escape url with space', () => { - assert.match(result.css[0], 'background:url(/white\\ space.png)'); + assert.match(result.css[0], 'background:url(/white\\ space.png)'); }); test('escape css syntax', () => { - assert.match(result.css[0], ':not(#\\#)'); + assert.match(result.css[0], ':not(#\\#)'); }); test.run(); diff --git a/packages/compiler/test/static-extraction/hoist-expression.ts b/packages/compiler/test/static-extraction/hoist-expression.ts index 06fbf4cf3..e29c5066a 100644 --- a/packages/compiler/test/static-extraction/hoist-expression.ts +++ b/packages/compiler/test/static-extraction/hoist-expression.ts @@ -11,11 +11,11 @@ const url = 'foo'; let result: unknown; test.before(async () => { - result = await transform(FIXTURE); + result = await transform(FIXTURE); }); test('logs warning with hoisted expression', () => { - assert.ok(result.code); + assert.ok(result.code); }); test.run(); diff --git a/packages/compiler/test/stress/index.ts b/packages/compiler/test/stress/index.ts index 2ccba3512..783633a9e 100644 --- a/packages/compiler/test/stress/index.ts +++ b/packages/compiler/test/stress/index.ts @@ -1,8 +1,8 @@ import { transform } from '@astrojs/compiler'; async function run() { - await transform( - `--- + await transform( + `--- import CartItems from './CartItems.astro'; --- @@ -188,62 +188,62 @@ import CartItems from './CartItems.astro';
    `, - { - sourcemap: true, - }, - ); + { + sourcemap: true, + } + ); } const MAX_CONCURRENT_RENDERS = 25; const MAX_RENDERS = 1e4; async function test() { - await run(); - const promises = []; - const tests = []; + await run(); + const promises = []; + const tests = []; - for (let i = 0; i < MAX_RENDERS; i++) { - tests.push(() => { - if (i % 1000 === 0) { - console.log(`Test ${i}`); - } - return run(); - }); - } + for (let i = 0; i < MAX_RENDERS; i++) { + tests.push(() => { + if (i % 1000 === 0) { + console.log(`Test ${i}`); + } + return run(); + }); + } - // Throttle the paths to avoid overloading the CPU with too many tasks. - for (const ts of throttle(MAX_CONCURRENT_RENDERS, tests)) { - for (const t of ts) { - promises.push(t()); - } - // This blocks generating more paths until these 10 complete. - await Promise.all(promises); - // This empties the array without allocating a new one. - promises.length = 0; - } + // Throttle the paths to avoid overloading the CPU with too many tasks. + for (const ts of throttle(MAX_CONCURRENT_RENDERS, tests)) { + for (const t of ts) { + promises.push(t()); + } + // This blocks generating more paths until these 10 complete. + await Promise.all(promises); + // This empties the array without allocating a new one. + promises.length = 0; + } } // Throttle the rendering a paths to prevents creating too many Promises on the microtask queue. function* throttle(max, tests) { - const tmp = []; - let i = 0; - for (const t of tests) { - tmp.push(t); - if (i === max) { - yield tmp; - // Empties the array, to avoid allocating a new one. - tmp.length = 0; - i = 0; - } else { - i++; - } - } + const tmp = []; + let i = 0; + for (const t of tests) { + tmp.push(t); + if (i === max) { + yield tmp; + // Empties the array, to avoid allocating a new one. + tmp.length = 0; + i = 0; + } else { + i++; + } + } - // If tmp has items in it, that means there were less than {max} paths remaining - // at the end, so we need to yield these too. - if (tmp.length) { - yield tmp; - } + // If tmp has items in it, that means there were less than {max} paths remaining + // at the end, so we need to yield these too. + if (tmp.length) { + yield tmp; + } } test(); diff --git a/packages/compiler/test/styles/define-vars.ts b/packages/compiler/test/styles/define-vars.ts index 671134932..0ef587557 100644 --- a/packages/compiler/test/styles/define-vars.ts +++ b/packages/compiler/test/styles/define-vars.ts @@ -4,7 +4,7 @@ import * as assert from 'uvu/assert'; import { preprocessStyle } from '../utils'; test('does not include define:vars in generated markup', async () => { - const input = ` + const input = ` --- let color = 'red'; --- @@ -19,15 +19,15 @@ let color = 'red';
    Ahhh
    `; - const result = await transform(input, { - preprocessStyle, - }); - assert.ok(!result.code.includes('STYLES')); - assert.equal(result.css.length, 1); + const result = await transform(input, { + preprocessStyle, + }); + assert.ok(!result.code.includes('STYLES')); + assert.equal(result.css.length, 1); }); test('handles style object and define:vars', async () => { - const input = ` + const input = ` --- let color = 'red'; --- @@ -36,8 +36,8 @@ let color = 'red'; `; - const result = await transform(input); - assert.match(result.code, `$$addAttribute([{ color: 'var(--color)' },$$definedVars], "style")`); + const result = await transform(input); + assert.match(result.code, `$$addAttribute([{ color: 'var(--color)' },$$definedVars], "style")`); }); test.run(); diff --git a/packages/compiler/test/styles/emit-scope.ts b/packages/compiler/test/styles/emit-scope.ts index 6cd06d75f..717ae6bdf 100644 --- a/packages/compiler/test/styles/emit-scope.ts +++ b/packages/compiler/test/styles/emit-scope.ts @@ -14,13 +14,13 @@ let value = 'world'; let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - sourcemap: true, - }); + result = await transform(FIXTURE, { + sourcemap: true, + }); }); test('emits a scope', () => { - assert.ok(result.scope, 'Expected to return a scope'); + assert.ok(result.scope, 'Expected to return a scope'); }); test.run(); diff --git a/packages/compiler/test/styles/empty-style.ts b/packages/compiler/test/styles/empty-style.ts index 141f91c34..9b63f4f2d 100644 --- a/packages/compiler/test/styles/empty-style.ts +++ b/packages/compiler/test/styles/empty-style.ts @@ -17,14 +17,14 @@ let value = 'world'; let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - sourcemap: true, - preprocessStyle, - }); + result = await transform(FIXTURE, { + sourcemap: true, + preprocessStyle, + }); }); test('can compile empty style', () => { - assert.ok(result.code, 'Expected to compile with empty style.'); + assert.ok(result.code, 'Expected to compile with empty style.'); }); test.run(); diff --git a/packages/compiler/test/styles/hash.ts b/packages/compiler/test/styles/hash.ts index a9927406a..505fd31d2 100644 --- a/packages/compiler/test/styles/hash.ts +++ b/packages/compiler/test/styles/hash.ts @@ -33,18 +33,20 @@ const FIXTURE_D = ` const scopes: string[] = []; test.before(async () => { - const [{ scope: a }, { scope: b }, { scope: c }, { scope: d }] = await Promise.all([FIXTURE_A, FIXTURE_B, FIXTURE_C, FIXTURE_D].map((source) => transform(source))); - scopes.push(a, b, c, d); + const [{ scope: a }, { scope: b }, { scope: c }, { scope: d }] = await Promise.all( + [FIXTURE_A, FIXTURE_B, FIXTURE_C, FIXTURE_D].map((source) => transform(source)) + ); + scopes.push(a, b, c, d); }); test('hash changes when content outside of style change', () => { - const [, b, c] = scopes; - assert.not.equal(b, c, 'Expected scopes to not be equal'); + const [, b, c] = scopes; + assert.not.equal(b, c, 'Expected scopes to not be equal'); }); test('hash changes when scripts change', () => { - const [, , c, d] = scopes; - assert.not.equal(c, d, 'Expected scopes to not be equal'); + const [, , c, d] = scopes; + assert.not.equal(c, d, 'Expected scopes to not be equal'); }); test.run(); diff --git a/packages/compiler/test/styles/sass.ts b/packages/compiler/test/styles/sass.ts index 141f1f3db..b6d510056 100644 --- a/packages/compiler/test/styles/sass.ts +++ b/packages/compiler/test/styles/sass.ts @@ -30,18 +30,22 @@ div { let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - sourcemap: true, - preprocessStyle, - }); + result = await transform(FIXTURE, { + sourcemap: true, + preprocessStyle, + }); }); test('transforms scss one', () => { - assert.match(result.css[result.css.length - 1], 'color:red', 'Expected "color:red" to be present.'); + assert.match( + result.css[result.css.length - 1], + 'color:red', + 'Expected "color:red" to be present.' + ); }); test('transforms scss two', () => { - assert.match(result.css[0], 'color:green', 'Expected "color:green" to be present.'); + assert.match(result.css[0], 'color:green', 'Expected "color:green" to be present.'); }); test.run(); diff --git a/packages/compiler/test/table/components.ts b/packages/compiler/test/table/components.ts index 5b936447e..8c6525b24 100644 --- a/packages/compiler/test/table/components.ts +++ b/packages/compiler/test/table/components.ts @@ -4,7 +4,7 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('allows components in table', async () => { - const input = ` + const input = ` --- const MyTableRow = "tr"; --- @@ -19,12 +19,12 @@ const MyTableRow = "tr"; `; - let error = 0; - try { - const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); - parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); - } catch (e) { - error = 1; - } - assert.equal(error, 0, 'compiler should generate valid code'); + let error = 0; + try { + const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); + parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); + } catch (e) { + error = 1; + } + assert.equal(error, 0, 'compiler should generate valid code'); }); diff --git a/packages/compiler/test/table/expressions.ts b/packages/compiler/test/table/expressions.ts index 5f6922f2d..5c199b607 100644 --- a/packages/compiler/test/table/expressions.ts +++ b/packages/compiler/test/table/expressions.ts @@ -4,7 +4,7 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('allows expressions in table', async () => { - const input = ` + const input = ` --- --- @@ -28,19 +28,19 @@ test('allows expressions in table', async () => { `; - let error = 0; - try { - const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); - parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); - assert.match(code, '${num}'); - } catch (e) { - error = 1; - } - assert.equal(error, 0, 'compiler should generate valid code'); + let error = 0; + try { + const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); + parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); + assert.match(code, '${num}'); + } catch (e) { + error = 1; + } + assert.equal(error, 0, 'compiler should generate valid code'); }); test('allows many expressions in table', async () => { - const input = ` + const input = ` --- --- @@ -79,13 +79,13 @@ test('allows many expressions in table', async () => { `; - let error = 0; - try { - const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); - parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); - assert.match(code, '${num}'); - } catch (e) { - error = 1; - } - assert.equal(error, 0, 'compiler should generate valid code'); + let error = 0; + try { + const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); + parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); + assert.match(code, '${num}'); + } catch (e) { + error = 1; + } + assert.equal(error, 0, 'compiler should generate valid code'); }); diff --git a/packages/compiler/test/table/in-expression.ts b/packages/compiler/test/table/in-expression.ts index 9641c8d15..22bc970ad 100644 --- a/packages/compiler/test/table/in-expression.ts +++ b/packages/compiler/test/table/in-expression.ts @@ -4,7 +4,7 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('does not panic on table in expression', async () => { - const input = ` + const input = `
    {course.reviews && course.reviews.length && <> @@ -31,18 +31,18 @@ test('does not panic on table in expression', async () => {
    `; - let error = 0; - try { - const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); - parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); - } catch (e) { - error = 1; - } - assert.equal(error, 0, 'compiler should generate valid code'); + let error = 0; + try { + const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); + parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); + } catch (e) { + error = 1; + } + assert.equal(error, 0, 'compiler should generate valid code'); }); test('does not generate invalid markup on table in expression', async () => { - const input = ` + const input = `
      {Astro.props.page.data.map(page =>
    • @@ -57,18 +57,18 @@ test('does not generate invalid markup on table in expression', async () => {
    `; - let error = 0; - try { - const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); - parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); - } catch (e) { - error = 1; - } - assert.equal(error, 0, 'compiler should generate valid code'); + let error = 0; + try { + const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); + parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); + } catch (e) { + error = 1; + } + assert.equal(error, 0, 'compiler should generate valid code'); }); test('does not generate invalid markup on multiple tables', async () => { - const input = ` + const input = `
    {["a", "b", "c"].map(char=> { @@ -83,12 +83,12 @@ test('does not generate invalid markup on multiple tables', async () => {
    `; - let error = 0; - try { - const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); - parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); - } catch (e) { - error = 1; - } - assert.equal(error, 0, 'compiler should generate valid code'); + let error = 0; + try { + const { code } = await transform(input, { filename: 'index.astro', sourcemap: 'inline' }); + parse(code, { ecmaVersion: 'latest', sourceType: 'module' }); + } catch (e) { + error = 1; + } + assert.equal(error, 0, 'compiler should generate valid code'); }); diff --git a/packages/compiler/test/teardown/parse.ts b/packages/compiler/test/teardown/parse.ts index d1e01f294..9d6650dcf 100644 --- a/packages/compiler/test/teardown/parse.ts +++ b/packages/compiler/test/teardown/parse.ts @@ -5,12 +5,12 @@ import * as assert from 'uvu/assert'; const FIXTURE = '
    hello
    '; test('parse still works after teardown', async () => { - const ast1 = await parse(FIXTURE); - assert.ok(ast1); - teardown(); - // Make sure `parse` creates a new WASM instance after teardown removed the previous one - const ast2 = await parse(FIXTURE); - assert.ok(ast2); + const ast1 = await parse(FIXTURE); + assert.ok(ast1); + teardown(); + // Make sure `parse` creates a new WASM instance after teardown removed the previous one + const ast2 = await parse(FIXTURE); + assert.ok(ast2); }); test.run(); diff --git a/packages/compiler/test/transition/data-astro.ts b/packages/compiler/test/transition/data-astro.ts index 1b1384044..13ca1c901 100644 --- a/packages/compiler/test/transition/data-astro.ts +++ b/packages/compiler/test/transition/data-astro.ts @@ -13,10 +13,10 @@ const FIXTURE = ` `; test('Issues warnings for data-astro-* attributes', async () => { - const result = await transform(FIXTURE); - assert.equal(result.diagnostics.length, 2); - assert.equal(result.diagnostics[0].code, 2000); - assert.equal(result.diagnostics[1].code, 2010); + const result = await transform(FIXTURE); + assert.equal(result.diagnostics.length, 2); + assert.equal(result.diagnostics[0].code, 2000); + assert.equal(result.diagnostics[1].code, 2010); }); test.run(); diff --git a/packages/compiler/test/transition/meta.ts b/packages/compiler/test/transition/meta.ts index 5999f2abd..50c281dec 100644 --- a/packages/compiler/test/transition/meta.ts +++ b/packages/compiler/test/transition/meta.ts @@ -8,13 +8,13 @@ const FIXTURE = ` let result: unknown; test.before(async () => { - result = await transform(FIXTURE, { - resolvePath: async (s) => s, - }); + result = await transform(FIXTURE, { + resolvePath: async (s) => s, + }); }); test('tagged with propagation metadata', () => { - assert.equal(result.propagation, true); + assert.equal(result.propagation, true); }); test.run(); diff --git a/packages/compiler/test/tsx-errors/eof.ts b/packages/compiler/test/tsx-errors/eof.ts index 8f38708e2..e3c43be5a 100644 --- a/packages/compiler/test/tsx-errors/eof.ts +++ b/packages/compiler/test/tsx-errors/eof.ts @@ -15,16 +15,16 @@ const FIXTURE = ` let result: unknown; test.before(async () => { - result = await convertToTSX(FIXTURE, { - filename: '/src/components/EOF.astro', - }); + result = await convertToTSX(FIXTURE, { + filename: '/src/components/EOF.astro', + }); }); test('got a tokenizer error', () => { - assert.ok(Array.isArray(result.diagnostics)); - assert.is(result.diagnostics.length, 1); - assert.is(result.diagnostics[0].text, 'Unterminated comment'); - assert.is(FIXTURE.split('\n')[result.diagnostics[0].location.line - 1], ' {/*'); + assert.ok(Array.isArray(result.diagnostics)); + assert.is(result.diagnostics.length, 1); + assert.is(result.diagnostics[0].text, 'Unterminated comment'); + assert.is(FIXTURE.split('\n')[result.diagnostics[0].location.line - 1], ' {/*'); }); test.run(); diff --git a/packages/compiler/test/tsx-errors/fragment-shorthand.ts b/packages/compiler/test/tsx-errors/fragment-shorthand.ts index c36b7570e..f809f98fe 100644 --- a/packages/compiler/test/tsx-errors/fragment-shorthand.ts +++ b/packages/compiler/test/tsx-errors/fragment-shorthand.ts @@ -13,18 +13,24 @@ const FIXTURE = ` let result: unknown; test.before(async () => { - result = await convertToTSX(FIXTURE, { - filename: '/src/components/fragment.astro', - }); + result = await convertToTSX(FIXTURE, { + filename: '/src/components/fragment.astro', + }); }); test('got a tokenizer error', () => { - assert.ok(Array.isArray(result.diagnostics)); - assert.is(result.diagnostics.length, 1); - assert.is(result.diagnostics[0].text, 'Unable to assign attributes when using <> Fragment shorthand syntax!'); - const loc = result.diagnostics[0].location; - assert.is(FIXTURE.split('\n')[loc.line - 1], ` < data-test="hello">
    `); - assert.is(FIXTURE.split('\n')[loc.line - 1].slice(loc.column - 1, loc.column - 1 + loc.length), `< data-test="hello">`); + assert.ok(Array.isArray(result.diagnostics)); + assert.is(result.diagnostics.length, 1); + assert.is( + result.diagnostics[0].text, + 'Unable to assign attributes when using <> Fragment shorthand syntax!' + ); + const loc = result.diagnostics[0].location; + assert.is(FIXTURE.split('\n')[loc.line - 1], ` < data-test="hello">
    `); + assert.is( + FIXTURE.split('\n')[loc.line - 1].slice(loc.column - 1, loc.column - 1 + loc.length), + `< data-test="hello">` + ); }); test.run(); diff --git a/packages/compiler/test/tsx-errors/unfinished-component.ts b/packages/compiler/test/tsx-errors/unfinished-component.ts index b4536d52f..e392269ca 100644 --- a/packages/compiler/test/tsx-errors/unfinished-component.ts +++ b/packages/compiler/test/tsx-errors/unfinished-component.ts @@ -6,15 +6,15 @@ const FIXTURE = '
    { - result = await convertToTSX(FIXTURE, { - filename: '/src/components/unfinished.astro', - }); + result = await convertToTSX(FIXTURE, { + filename: '/src/components/unfinished.astro', + }); }); test('did not crash on unfinished component', () => { - assert.ok(result); - assert.ok(Array.isArray(result.diagnostics)); - assert.is(result.diagnostics.length, 0); + assert.ok(result); + assert.ok(Array.isArray(result.diagnostics)); + assert.is(result.diagnostics.length, 0); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/404.ts b/packages/compiler/test/tsx-sourcemaps/404.ts index d1f4a633e..bf0abcc10 100644 --- a/packages/compiler/test/tsx-sourcemaps/404.ts +++ b/packages/compiler/test/tsx-sourcemaps/404.ts @@ -3,8 +3,8 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('404 generates a valid identifier', async () => { - const input = '
    '; + const input = '
    '; - const output = await convertToTSX(input, { filename: '404.astro', sourcemap: 'inline' }); - assert.match(output.code, 'export default function __AstroComponent_'); + const output = await convertToTSX(input, { filename: '404.astro', sourcemap: 'inline' }); + assert.match(output.code, 'export default function __AstroComponent_'); }); diff --git a/packages/compiler/test/tsx-sourcemaps/attributes.ts b/packages/compiler/test/tsx-sourcemaps/attributes.ts index 77e4e4683..e57f721aa 100644 --- a/packages/compiler/test/tsx-sourcemaps/attributes.ts +++ b/packages/compiler/test/tsx-sourcemaps/attributes.ts @@ -3,55 +3,55 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('shorthand attribute', async () => { - const input = '
    '; - - const output = await testTSXSourcemap(input, 'name'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 6, - name: null, - }); + const input = '
    '; + + const output = await testTSXSourcemap(input, 'name'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 6, + name: null, + }); }); test('empty quoted attribute', async () => { - const input = `
    `; - - const open = await testTSXSourcemap(input, '"'); - assert.equal(open, { - source: 'index.astro', - line: 1, - column: 9, - name: null, - }); + const input = `
    `; + + const open = await testTSXSourcemap(input, '"'); + assert.equal(open, { + source: 'index.astro', + line: 1, + column: 9, + name: null, + }); }); test('template literal attribute', async () => { - const input = `--- + const input = `--- --- `; - const open = await testTSXSourcemap(input, 'foo'); - assert.equal(open, { - source: 'index.astro', - line: 3, - column: 16, - name: null, - }); + const open = await testTSXSourcemap(input, 'foo'); + assert.equal(open, { + source: 'index.astro', + line: 3, + column: 16, + name: null, + }); }); test('multiline quoted attribute', async () => { - const input = ``; - const output = await testTSXSourcemap(input, 'Z'); - assert.equal(output, { - source: 'index.astro', - line: 3, - column: 1, - name: null, - }); + const output = await testTSXSourcemap(input, 'Z'); + assert.equal(output, { + source: 'index.astro', + line: 3, + column: 1, + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/deprecated.ts b/packages/compiler/test/tsx-sourcemaps/deprecated.ts index 453cdf5c2..a522f282f 100644 --- a/packages/compiler/test/tsx-sourcemaps/deprecated.ts +++ b/packages/compiler/test/tsx-sourcemaps/deprecated.ts @@ -3,21 +3,21 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('script is:inline', async () => { - const input = `--- + const input = `--- /** @deprecated */ const deprecated = "Astro" deprecated; const hello = "Astro" --- `; - const output = await testTSXSourcemap(input, 'deprecated;'); + const output = await testTSXSourcemap(input, 'deprecated;'); - assert.equal(output, { - line: 4, - column: 1, - source: 'index.astro', - name: null, - }); + assert.equal(output, { + line: 4, + column: 1, + source: 'index.astro', + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/error.ts b/packages/compiler/test/tsx-sourcemaps/error.ts index 6dd0d1b45..a4afac495 100644 --- a/packages/compiler/test/tsx-sourcemaps/error.ts +++ b/packages/compiler/test/tsx-sourcemaps/error.ts @@ -3,46 +3,46 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('svelte error', async () => { - const input = `--- + const input = `--- import SvelteOptionalProps from "./SvelteOptionalProps.svelte" --- `; - const output = await testTSXSourcemap(input, ''); - - assert.equal(output, { - line: 5, - column: 1, - source: 'index.astro', - name: null, - }); + const output = await testTSXSourcemap(input, ''); + + assert.equal(output, { + line: 5, + column: 1, + source: 'index.astro', + name: null, + }); }); test('vue error', async () => { - const input = `--- + const input = `--- import SvelteError from "./SvelteError.svelte" import VueError from "./VueError.vue" --- `; - const svelte = await testTSXSourcemap(input, ''); - - assert.equal(svelte, { - line: 6, - column: 1, - source: 'index.astro', - name: null, - }); - - const vue = await testTSXSourcemap(input, ''); - - assert.equal(vue, { - line: 7, - column: 1, - source: 'index.astro', - name: null, - }); + const svelte = await testTSXSourcemap(input, ''); + + assert.equal(svelte, { + line: 6, + column: 1, + source: 'index.astro', + name: null, + }); + + const vue = await testTSXSourcemap(input, ''); + + assert.equal(vue, { + line: 7, + column: 1, + source: 'index.astro', + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/frontmatter.ts b/packages/compiler/test/tsx-sourcemaps/frontmatter.ts index 6064f8b20..a84d7190c 100644 --- a/packages/compiler/test/tsx-sourcemaps/frontmatter.ts +++ b/packages/compiler/test/tsx-sourcemaps/frontmatter.ts @@ -3,18 +3,18 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('frontmatter', async () => { - const input = `--- + const input = `--- nonexistent --- `; - const output = await testTSXSourcemap(input, 'nonexistent'); + const output = await testTSXSourcemap(input, 'nonexistent'); - assert.equal(output, { - line: 2, - column: 1, - source: 'index.astro', - name: null, - }); + assert.equal(output, { + line: 2, + column: 1, + source: 'index.astro', + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/hover.ts b/packages/compiler/test/tsx-sourcemaps/hover.ts index 2e43a7c96..44fc23991 100644 --- a/packages/compiler/test/tsx-sourcemaps/hover.ts +++ b/packages/compiler/test/tsx-sourcemaps/hover.ts @@ -14,39 +14,39 @@ const fixture = `--- `; test('hover I', async () => { - const input = fixture; - const output = await testTSXSourcemap(input, 'MyVariable'); - - assert.equal(output, { - line: 2, - column: 11, - source: 'index.astro', - name: null, - }); + const input = fixture; + const output = await testTSXSourcemap(input, 'MyVariable'); + + assert.equal(output, { + line: 2, + column: 11, + source: 'index.astro', + name: null, + }); }); test('hover II', async () => { - const input = fixture; - const output = await testTSXSourcemap(input, 'MyDocumentedVariable'); - - assert.equal(output, { - line: 5, - column: 11, - source: 'index.astro', - name: null, - }); + const input = fixture; + const output = await testTSXSourcemap(input, 'MyDocumentedVariable'); + + assert.equal(output, { + line: 5, + column: 11, + source: 'index.astro', + name: null, + }); }); test('hover III', async () => { - const input = fixture; - const output = await testTSXSourcemap(input, 'MyJSDocVariable'); - - assert.equal(output, { - line: 8, - column: 11, - source: 'index.astro', - name: null, - }); + const input = fixture; + const output = await testTSXSourcemap(input, 'MyJSDocVariable'); + + assert.equal(output, { + line: 8, + column: 11, + source: 'index.astro', + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/module.ts b/packages/compiler/test/tsx-sourcemaps/module.ts index 1428fc852..e3fb6a51a 100644 --- a/packages/compiler/test/tsx-sourcemaps/module.ts +++ b/packages/compiler/test/tsx-sourcemaps/module.ts @@ -3,7 +3,7 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('script is:inline', async () => { - const input = `--- + const input = `--- // valid import { foo } from './script.js'; import ComponentAstro from './astro.astro'; @@ -14,14 +14,14 @@ test('script is:inline', async () => { foo;baz;ComponentAstro;ComponentSvelte;ComponentVue; --- `; - const output = await testTSXSourcemap(input, `'./script'`); + const output = await testTSXSourcemap(input, `'./script'`); - assert.equal(output, { - line: 8, - column: 23, - source: 'index.astro', - name: null, - }); + assert.equal(output, { + line: 8, + column: 23, + source: 'index.astro', + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/multibyte.ts b/packages/compiler/test/tsx-sourcemaps/multibyte.ts index 904c384bb..366a7051c 100644 --- a/packages/compiler/test/tsx-sourcemaps/multibyte.ts +++ b/packages/compiler/test/tsx-sourcemaps/multibyte.ts @@ -3,51 +3,51 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('multibyte content', async () => { - const input = '

    '; - - const output = await testTSXSourcemap(input, 'ツ'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 4, - name: null, - }); + const input = '

    '; + + const output = await testTSXSourcemap(input, 'ツ'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 4, + name: null, + }); }); test('content after multibyte character', async () => { - const input = '

    foobar

    '; - - const output = await testTSXSourcemap(input, 'foobar'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 13, - name: null, - }); + const input = '

    foobar

    '; + + const output = await testTSXSourcemap(input, 'foobar'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 13, + name: null, + }); }); test('many characters', async () => { - const input = '

    こんにちは

    '; - - const output = await testTSXSourcemap(input, 'ん'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 5, - name: null, - }); + const input = '

    こんにちは

    '; + + const output = await testTSXSourcemap(input, 'ん'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 5, + name: null, + }); }); test('many characters', async () => { - const input = '

    こんにちは

    '; - - const output = await testTSXSourcemap(input, 'に'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 6, - name: null, - }); + const input = '

    こんにちは

    '; + + const output = await testTSXSourcemap(input, 'に'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 6, + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/script.ts b/packages/compiler/test/tsx-sourcemaps/script.ts index 9b5e7da82..bc74b26ef 100644 --- a/packages/compiler/test/tsx-sourcemaps/script.ts +++ b/packages/compiler/test/tsx-sourcemaps/script.ts @@ -3,19 +3,19 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('script is:inline', async () => { - const input = ` `; - const output = await testTSXSourcemap(input, '\n'); + const output = await testTSXSourcemap(input, '\n'); - assert.equal(output, { - line: 1, - column: 18, - source: 'index.astro', - name: null, - }); + assert.equal(output, { + line: 1, + column: 18, + source: 'index.astro', + name: null, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/tags.ts b/packages/compiler/test/tsx-sourcemaps/tags.ts index 1094e5fce..74a389166 100644 --- a/packages/compiler/test/tsx-sourcemaps/tags.ts +++ b/packages/compiler/test/tsx-sourcemaps/tags.ts @@ -5,28 +5,28 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('tag close', async () => { - const input = ''; - const output = await testTSXSourcemap(input, '>'); + const input = ''; + const output = await testTSXSourcemap(input, '>'); - assert.equal(output, { - line: 1, - column: 6, - source: 'index.astro', - name: null, - }); + assert.equal(output, { + line: 1, + column: 6, + source: 'index.astro', + name: null, + }); }); test('tag with spaces', async () => { - const input = ''; - const { map } = await convertToTSX(input, { sourcemap: 'both', filename: 'index.astro' }); - const tracer = new TraceMap(map); + const input = ''; + const { map } = await convertToTSX(input, { sourcemap: 'both', filename: 'index.astro' }); + const tracer = new TraceMap(map); - const generated = generatedPositionFor(tracer, { source: 'index.astro', line: 1, column: 14 }); + const generated = generatedPositionFor(tracer, { source: 'index.astro', line: 1, column: 14 }); - assert.equal(generated, { - line: 4, - column: 9, - }); + assert.equal(generated, { + line: 4, + column: 9, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/template-windows.ts b/packages/compiler/test/tsx-sourcemaps/template-windows.ts index 9805ad1d1..3753e8da8 100644 --- a/packages/compiler/test/tsx-sourcemaps/template-windows.ts +++ b/packages/compiler/test/tsx-sourcemaps/template-windows.ts @@ -4,135 +4,135 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('template expression basic', async () => { - const input = '
    {\r\nnonexistent\r\n}
    '; + const input = '
    {\r\nnonexistent\r\n}
    '; - const output = await testTSXSourcemap(input, 'nonexistent'); - assert.equal(output, { - source: 'index.astro', - line: 2, - column: 1, - name: null, - }); + const output = await testTSXSourcemap(input, 'nonexistent'); + assert.equal(output, { + source: 'index.astro', + line: 2, + column: 1, + name: null, + }); }); test('template expression has dot', async () => { - const input = '
    {\nconsole.log(hey)\n}
    '; - const output = await testTSXSourcemap(input, 'log'); - assert.equal(output, { - source: 'index.astro', - line: 2, - column: 9, - name: null, - }); + const input = '
    {\nconsole.log(hey)\n}
    '; + const output = await testTSXSourcemap(input, 'log'); + assert.equal(output, { + source: 'index.astro', + line: 2, + column: 9, + name: null, + }); }); test('template expression has dot', async () => { - const input = '
    {\r\nconsole.log(hey)\r\n}
    '; - const output = await testTSXSourcemap(input, 'log'); - assert.equal(output, { - source: 'index.astro', - line: 2, - column: 9, - name: null, - }); + const input = '
    {\r\nconsole.log(hey)\r\n}
    '; + const output = await testTSXSourcemap(input, 'log'); + assert.equal(output, { + source: 'index.astro', + line: 2, + column: 9, + name: null, + }); }); test('template expression with addition', async () => { - const input = `{"hello" + \nhey}`; - const output = await testTSXSourcemap(input, 'hey'); - assert.equal(output, { - source: 'index.astro', - line: 2, - column: 1, - name: null, - }); + const input = `{"hello" + \nhey}`; + const output = await testTSXSourcemap(input, 'hey'); + assert.equal(output, { + source: 'index.astro', + line: 2, + column: 1, + name: null, + }); }); test('template expression with addition', async () => { - const input = `{"hello" + \r\nhey}`; - const output = await testTSXSourcemap(input, 'hey'); - assert.equal(output, { - source: 'index.astro', - line: 2, - column: 1, - name: null, - }); + const input = `{"hello" + \r\nhey}`; + const output = await testTSXSourcemap(input, 'hey'); + assert.equal(output, { + source: 'index.astro', + line: 2, + column: 1, + name: null, + }); }); test('html attribute', async () => { - const input = ``; - const output = await testTSXSourcemap(input, 'color'); - assert.equal(output, { - source: 'index.astro', - name: null, - line: 2, - column: 12, - }); + const input = ``; + const output = await testTSXSourcemap(input, 'color'); + assert.equal(output, { + source: 'index.astro', + name: null, + line: 2, + column: 12, + }); }); test('html attribute', async () => { - const input = ``; - const output = await testTSXSourcemap(input, 'color'); - assert.equal(output, { - source: 'index.astro', - name: null, - line: 2, - column: 12, - }); + const input = ``; + const output = await testTSXSourcemap(input, 'color'); + assert.equal(output, { + source: 'index.astro', + name: null, + line: 2, + column: 12, + }); }); test('complex template expression', async () => { - const input = `{[].map(ITEM => {\r\nv = "what";\r\nreturn
    {ITEMS}
    \r\n})}`; - const item = await testTSXSourcemap(input, 'ITEM'); - const items = await testTSXSourcemap(input, 'ITEMS'); - assert.equal(item, { - source: 'index.astro', - name: null, - line: 1, - column: 8, - }); - assert.equal(items, { - source: 'index.astro', - name: null, - line: 3, - column: 14, - }); + const input = `{[].map(ITEM => {\r\nv = "what";\r\nreturn
    {ITEMS}
    \r\n})}`; + const item = await testTSXSourcemap(input, 'ITEM'); + const items = await testTSXSourcemap(input, 'ITEMS'); + assert.equal(item, { + source: 'index.astro', + name: null, + line: 1, + column: 8, + }); + assert.equal(items, { + source: 'index.astro', + name: null, + line: 3, + column: 14, + }); }); test('attributes', async () => { - const input = ``; - const className = await testTSXSourcemap(input, 'className'); - assert.equal(className, { - source: 'index.astro', - name: null, - line: 2, - column: 6, - }); + const input = ``; + const className = await testTSXSourcemap(input, 'className'); + assert.equal(className, { + source: 'index.astro', + name: null, + line: 2, + column: 6, + }); }); test('special attributes', async () => { - const input = ``; - const onClick = await testTSXSourcemap(input, '@on.click'); - assert.equal(onClick, { - source: 'index.astro', - name: null, - line: 2, - column: 6, - }); + const input = ``; + const onClick = await testTSXSourcemap(input, '@on.click'); + assert.equal(onClick, { + source: 'index.astro', + name: null, + line: 2, + column: 6, + }); }); test('whitespace', async () => { - const input = `---\r\nimport A from "a";\r\n\timport B from "b";\r\n---\r\n`; - const { code } = await convertToTSX(input, { sourcemap: 'both', filename: 'index.astro' }); - assert.match(code, '\t', 'output includes \\t'); + const input = `---\r\nimport A from "a";\r\n\timport B from "b";\r\n---\r\n`; + const { code } = await convertToTSX(input, { sourcemap: 'both', filename: 'index.astro' }); + assert.match(code, '\t', 'output includes \\t'); - const B = await testTSXSourcemap(input, 'B'); - assert.equal(B, { - source: 'index.astro', - name: null, - line: 3, - column: 9, - }); + const B = await testTSXSourcemap(input, 'B'); + assert.equal(B, { + source: 'index.astro', + name: null, + line: 3, + column: 9, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/template.ts b/packages/compiler/test/tsx-sourcemaps/template.ts index baacb8404..101fa8e44 100644 --- a/packages/compiler/test/tsx-sourcemaps/template.ts +++ b/packages/compiler/test/tsx-sourcemaps/template.ts @@ -3,91 +3,91 @@ import * as assert from 'uvu/assert'; import { testTSXSourcemap } from '../utils'; test('template expression basic', async () => { - const input = '
    {nonexistent}
    '; + const input = '
    {nonexistent}
    '; - const output = await testTSXSourcemap(input, 'nonexistent'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 6, - name: null, - }); + const output = await testTSXSourcemap(input, 'nonexistent'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 6, + name: null, + }); }); test('template expression has dot', async () => { - const input = '
    {console.log(hey)}
    '; - const output = await testTSXSourcemap(input, 'log'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 14, - name: null, - }); + const input = '
    {console.log(hey)}
    '; + const output = await testTSXSourcemap(input, 'log'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 14, + name: null, + }); }); test('template expression with addition', async () => { - const input = `{"hello" + hey}`; - const output = await testTSXSourcemap(input, 'hey'); - assert.equal(output, { - source: 'index.astro', - line: 1, - column: 11, - name: null, - }); + const input = `{"hello" + hey}`; + const output = await testTSXSourcemap(input, 'hey'); + assert.equal(output, { + source: 'index.astro', + line: 1, + column: 11, + name: null, + }); }); test('html attribute', async () => { - const input = ``; - const output = await testTSXSourcemap(input, 'color'); - assert.equal(output, { - source: 'index.astro', - name: null, - line: 1, - column: 5, - }); + const input = ``; + const output = await testTSXSourcemap(input, 'color'); + assert.equal(output, { + source: 'index.astro', + name: null, + line: 1, + column: 5, + }); }); test('complex template expression', async () => { - const input = `{[].map(ITEM => { + const input = `{[].map(ITEM => { v = "what"; return
    {ITEMS}
    })}`; - const item = await testTSXSourcemap(input, 'ITEM'); - const items = await testTSXSourcemap(input, 'ITEMS'); - assert.equal(item, { - source: 'index.astro', - name: null, - line: 1, - column: 8, - }); - assert.equal(items, { - source: 'index.astro', - name: null, - line: 3, - column: 14, - }); + const item = await testTSXSourcemap(input, 'ITEM'); + const items = await testTSXSourcemap(input, 'ITEMS'); + assert.equal(item, { + source: 'index.astro', + name: null, + line: 1, + column: 8, + }); + assert.equal(items, { + source: 'index.astro', + name: null, + line: 3, + column: 14, + }); }); test('attributes', async () => { - const input = `
    `; - const className = await testTSXSourcemap(input, 'className'); - assert.equal(className, { - source: 'index.astro', - name: null, - line: 1, - column: 5, - }); + const input = `
    `; + const className = await testTSXSourcemap(input, 'className'); + assert.equal(className, { + source: 'index.astro', + name: null, + line: 1, + column: 5, + }); }); test('special attributes', async () => { - const input = `
    `; - const onClick = await testTSXSourcemap(input, '@on.click'); - assert.equal(onClick, { - source: 'index.astro', - name: null, - line: 1, - column: 5, - }); + const input = `
    `; + const onClick = await testTSXSourcemap(input, '@on.click'); + assert.equal(onClick, { + source: 'index.astro', + name: null, + line: 1, + column: 5, + }); }); test.run(); diff --git a/packages/compiler/test/tsx-sourcemaps/unfinished-literal.ts b/packages/compiler/test/tsx-sourcemaps/unfinished-literal.ts index dba1213d9..2e54ec649 100644 --- a/packages/compiler/test/tsx-sourcemaps/unfinished-literal.ts +++ b/packages/compiler/test/tsx-sourcemaps/unfinished-literal.ts @@ -3,41 +3,41 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('does not panic on unfinished template literal attribute', async () => { - const input = `
    + const input = `
    `; - let error = 0; - try { - const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); - assert.match(output.code, 'class={``}'); - } catch (e) { - error = 1; - } + let error = 0; + try { + const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); + assert.match(output.code, 'class={``}'); + } catch (e) { + error = 1; + } - assert.equal(error, 0, 'compiler should not have panicked'); + assert.equal(error, 0, 'compiler should not have panicked'); }); test('does not panic on unfinished double quoted attribute', async () => { - const input = `
    { - const input = `
    { - const input = ` + const input = ` --- let value = 'world'; --- @@ -12,7 +12,7 @@ let value = 'world';

    Hello {value}

    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} let value = 'world'; @@ -21,12 +21,12 @@ let value = 'world'; export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('named export', async () => { - const input = ` + const input = ` --- let value = 'world'; --- @@ -34,7 +34,7 @@ let value = 'world';

    Hello {value}

    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} let value = 'world'; @@ -43,29 +43,32 @@ let value = 'world'; export default function Test__AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { filename: '/Users/nmoo/test.astro', sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { + filename: '/Users/nmoo/test.astro', + sourcemap: 'external', + }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('moves @attributes to spread', async () => { - const input = `
    {}} name="value">
    `; - const output = `${TSXPrefix} + const input = `
    {}} name="value">
    `; + const output = `${TSXPrefix}
    {})}}>
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('add trailing semicolon to frontmatter', async () => { - const input = ` + const input = ` --- console.log("hello") --- {hello} `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} console.log("hello") {}; @@ -73,19 +76,19 @@ console.log("hello") export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('add trailing semicolon to frontmatter II', async () => { - const input = ` + const input = ` --- const { hello } = Astro.props ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} const { hello } = Astro.props {}; @@ -93,85 +96,85 @@ const { hello } = Astro.props export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('moves attributes with dots in them to spread', async () => { - const input = `
    `; - const output = `${TSXPrefix} + const input = `
    `; + const output = `${TSXPrefix}
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('moves attributes that starts with : to spread', async () => { - const input = `
    `; - const output = `${TSXPrefix} + const input = `
    `; + const output = `${TSXPrefix}
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test("Don't move attributes to spread unnecessarily", async () => { - const input = `
    `; - const output = `${TSXPrefix} + const input = `
    `; + const output = `${TSXPrefix}
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserves unclosed tags', async () => { - const input = ' + const input = ' export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('template literal attribute', async () => { - const input = '
    '; - const output = `${TSXPrefix} + const input = '
    '; + const output = `${TSXPrefix}
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('unclosed tags', async () => { - const input = `--- + const input = `--- const myMarkdown = await import('../content/post.md'); --- export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('unclosed tags II', async () => { - const input = `--- + const input = `--- const myMarkdown = await import('../content/post.md'); --- @@ -179,113 +182,113 @@ const myMarkdown = await import('../content/post.md');
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('spread object', async () => { - const input = ``; - const output = `${TSXPrefix} + const input = ``; + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('spread object II', async () => { - const input = ` + const input = ` `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('fragment with no name', async () => { - const input = '<>+0123456789'; - const output = `${TSXPrefix} + const input = '<>+0123456789'; + const output = `${TSXPrefix} <>+0123456789 export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserves spaces in tag', async () => { - const input = ''; - const output = `${TSXPrefix} + const input = ''; + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserves spaces after attributes in tag', async () => { - const input = ''; - const output = `${TSXPrefix} + const input = ''; + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserves spaces in tag', async () => { - const input = ' export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('preserves line returns in tag by transforming to space', async () => { - const input = ` export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('return ranges', async () => { - const input = `---\nconsole.log("Hello!")\n---\n\n
    `; - const { metaRanges } = await convertToTSX(input, { sourcemap: 'external' }); - - assert.equal(metaRanges, { - frontmatter: { - start: 30, - end: 54, - }, - body: { - start: 68, - end: 80, - }, - }); + const input = `---\nconsole.log("Hello!")\n---\n\n
    `; + const { metaRanges } = await convertToTSX(input, { sourcemap: 'external' }); + + assert.equal(metaRanges, { + frontmatter: { + start: 30, + end: 54, + }, + body: { + start: 68, + end: 80, + }, + }); }); test('return ranges - no frontmatter', async () => { - const input = '
    '; - const { metaRanges } = await convertToTSX(input, { sourcemap: 'external' }); - - assert.equal(metaRanges, { - frontmatter: { - start: 30, - end: 30, - }, - body: { - start: 41, - end: 53, - }, - }); + const input = '
    '; + const { metaRanges } = await convertToTSX(input, { sourcemap: 'external' }); + + assert.equal(metaRanges, { + frontmatter: { + start: 30, + end: 30, + }, + body: { + start: 41, + end: 53, + }, + }); }); test.run(); diff --git a/packages/compiler/test/tsx/comment-whitespace.ts b/packages/compiler/test/tsx/comment-whitespace.ts index 197a9769b..fa447b130 100644 --- a/packages/compiler/test/tsx/comment-whitespace.ts +++ b/packages/compiler/test/tsx/comment-whitespace.ts @@ -4,7 +4,7 @@ import * as assert from 'uvu/assert'; import { TSXPrefix } from '../utils'; test('preverve whitespace around jsx comments', async () => { - const input = `{/* @ts-expect-error */} + const input = `{/* @ts-expect-error */} { @@ -21,7 +21,7 @@ test('preverve whitespace around jsx comments', async () => { // @ts-expect-error }`; - const output = `${TSXPrefix} + const output = `${TSXPrefix} {/* @ts-expect-error */} @@ -41,8 +41,8 @@ test('preverve whitespace around jsx comments', async () => { } export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); diff --git a/packages/compiler/test/tsx/complex-generics.ts b/packages/compiler/test/tsx/complex-generics.ts index ff578db77..949b0e250 100644 --- a/packages/compiler/test/tsx/complex-generics.ts +++ b/packages/compiler/test/tsx/complex-generics.ts @@ -33,13 +33,13 @@ const { article } = Astro.props; `; test('does not panic on complex generics', async () => { - let error = 0; - try { - await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); - } catch (e) { - error = 1; - } - assert.equal(error, 0, 'compiler should not have panicked'); + let error = 0; + try { + await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); + } catch (e) { + error = 1; + } + assert.equal(error, 0, 'compiler should not have panicked'); }); test.run(); diff --git a/packages/compiler/test/tsx/escape.ts b/packages/compiler/test/tsx/escape.ts index 2070ab590..68c2d54fc 100644 --- a/packages/compiler/test/tsx/escape.ts +++ b/packages/compiler/test/tsx/escape.ts @@ -4,43 +4,43 @@ import * as assert from 'uvu/assert'; import { TSXPrefix } from '../utils'; test('escapes braces in comment', async () => { - const input = ''; - const output = `${TSXPrefix} + const input = ''; + const output = `${TSXPrefix} {/** \\\\{
    Not JSX!
    \\\\}*/} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('always inserts space before comment', async () => { - const input = ''; - const output = `${TSXPrefix} + const input = ''; + const output = `${TSXPrefix} {/** /
    Error?
    */} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('simple escapes star slashes (*/)', async () => { - const input = ''; - const output = `${TSXPrefix} + const input = ''; + const output = `${TSXPrefix} {/** *\\/
    Evil comment
    */} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('multiple escapes star slashes (*/)', async () => { - const input = ''; - const output = `${TSXPrefix} + const input = ''; + const output = `${TSXPrefix} {/** ***\\/*\\/**\\/*\\/*\\/*\\/
    Even more evil comment
    */} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); diff --git a/packages/compiler/test/tsx/line-terminator.ts b/packages/compiler/test/tsx/line-terminator.ts index bc49427d5..4b60d08a9 100644 --- a/packages/compiler/test/tsx/line-terminator.ts +++ b/packages/compiler/test/tsx/line-terminator.ts @@ -3,16 +3,16 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('handles non-standard line terminators', async () => { - const inputs = ['
', 'something
something', 'something

', '


']; - let err = 0; - for (const input of inputs) { - try { - await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); - } catch (e) { - err = 1; - } - } - assert.equal(err, 0, 'did not error'); + const inputs = ['
', 'something
something', 'something

', '


']; + let err = 0; + for (const input of inputs) { + try { + await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); + } catch (e) { + err = 1; + } + } + assert.equal(err, 0, 'did not error'); }); test.run(); diff --git a/packages/compiler/test/tsx/nested-generics.ts b/packages/compiler/test/tsx/nested-generics.ts index 880b93db2..c98900c35 100644 --- a/packages/compiler/test/tsx/nested-generics.ts +++ b/packages/compiler/test/tsx/nested-generics.ts @@ -3,28 +3,28 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; test('handles plain aliases', async () => { - const input = `--- + const input = `--- interface LocalImageProps {} type Props = LocalImageProps; ---`; - const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); - assert.ok(output.code.includes('(_props: Props)'), 'Includes aliased Props as correct props'); + const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); + assert.ok(output.code.includes('(_props: Props)'), 'Includes aliased Props as correct props'); }); test('handles aliases with nested generics', async () => { - const input = `--- + const input = `--- interface LocalImageProps { src: Promise<{ default: string }>; } type Props = LocalImageProps; ---`; - const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); - assert.ok(output.code.includes('(_props: Props)'), 'Includes aliased Props as correct props'); + const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); + assert.ok(output.code.includes('(_props: Props)'), 'Includes aliased Props as correct props'); }); test('gracefully handles Image props', async () => { - const input = `--- + const input = `--- interface LocalImageProps extends Omit, Omit, @@ -51,8 +51,8 @@ interface RemoteImageProps } export type Props = LocalImageProps | RemoteImageProps; ---`; - const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); - assert.ok(output.code.includes('(_props: Props)'), 'Includes aliased Props as correct props'); + const output = await convertToTSX(input, { filename: 'index.astro', sourcemap: 'inline' }); + assert.ok(output.code.includes('(_props: Props)'), 'Includes aliased Props as correct props'); }); test.run(); diff --git a/packages/compiler/test/tsx/non-latin.ts b/packages/compiler/test/tsx/non-latin.ts index 6a73f39a4..7a657fe6e 100644 --- a/packages/compiler/test/tsx/non-latin.ts +++ b/packages/compiler/test/tsx/non-latin.ts @@ -49,14 +49,14 @@ var Ⅴ = 5; var Hͫ̆̒̐ͣ̊̄ͯ͗͏̵̗̻̰̠̬͝ͅE̴̷̬͎̱̘͇͍̾ͦ͊͒͊̓̓̐_̫̠̱̩̭̤͈̑̎̋ͮͩ̒͑̾͋͘Ç̳͕̯̭̱̲̣̠̜͋̍O̴̦̗̯̹̼ͭ̐ͨ̊̈͘͠M̶̝̠̭̭̤̻͓͑̓̊ͣͤ̎͟͠E̢̞̮̹͍̞̳̣ͣͪ͐̈T̡̯̳̭̜̠͕͌̈́̽̿ͤ̿̅̑Ḧ̱̱̺̰̳̹̘̰́̏ͪ̂̽͂̀͠ = 'Zalgo';`; test('non-latin characters', async () => { - const input = ` + const input = ` --- ${value} ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} ${value} @@ -64,8 +64,8 @@ ${value} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); diff --git a/packages/compiler/test/tsx/props-and-getStaticPaths.ts b/packages/compiler/test/tsx/props-and-getStaticPaths.ts index c1947c647..82787746c 100644 --- a/packages/compiler/test/tsx/props-and-getStaticPaths.ts +++ b/packages/compiler/test/tsx/props-and-getStaticPaths.ts @@ -4,15 +4,15 @@ import * as assert from 'uvu/assert'; import { TSXPrefix } from '../utils'; function getPrefix({ - props = `ASTRO__MergeUnion>`, - component = '__AstroComponent_', - params = `ASTRO__Get`, + props = `ASTRO__MergeUnion>`, + component = '__AstroComponent_', + params = `ASTRO__Get`, }: { - props?: string; - component?: string; - params?: string; + props?: string; + component?: string; + params?: string; } = {}) { - return `/** + return `/** * Astro global available in all contexts in .astro files * * [Astro documentation](https://docs.astro.build/reference/api-reference/#astro-global) @@ -21,7 +21,7 @@ declare const Astro: Readonly = ArrayType extends readonly (infer ElementType)[] ? ElementType : never; + return `type ASTRO__ArrayElement = ArrayType extends readonly (infer ElementType)[] ? ElementType : never; type ASTRO__Flattened = T extends Array ? ASTRO__Flattened : T; type ASTRO__InferredGetStaticPath = ASTRO__Flattened>>>; type ASTRO__MergeUnion = T extends unknown ? T & { [P in Exclude]?: never } extends infer O ? { [P in keyof O]: O[P] } : never : never; @@ -29,7 +29,7 @@ type ASTRO__Get = T extends undefined ? undefined : K extends keyof T ? T[ } test('explicit props definition', async () => { - const input = `--- + const input = `--- interface Props {}; export function getStaticPaths() { return {}; @@ -37,7 +37,7 @@ export function getStaticPaths() { ---
    `; - const output = `${TSXPrefix}\ninterface Props {}; + const output = `${TSXPrefix}\ninterface Props {}; export function getStaticPaths() { return {}; } @@ -48,19 +48,19 @@ export function getStaticPaths() { export default function __AstroComponent_(_props: Props): any {} ${getSuffix()} ${getPrefix({ props: 'Props' })}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('inferred props', async () => { - const input = `--- + const input = `--- export function getStaticPaths() { return {}; } ---
    `; - const output = `${TSXPrefix}\nexport function getStaticPaths() { + const output = `${TSXPrefix}\nexport function getStaticPaths() { return {}; } @@ -70,8 +70,8 @@ export function getStaticPaths() { export default function __AstroComponent_(_props: ASTRO__MergeUnion>): any {} ${getSuffix()} ${getPrefix()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); diff --git a/packages/compiler/test/tsx/props.ts b/packages/compiler/test/tsx/props.ts index 5214d1d33..ffa2c5d5f 100644 --- a/packages/compiler/test/tsx/props.ts +++ b/packages/compiler/test/tsx/props.ts @@ -11,37 +11,37 @@ const PREFIX = (component = '__AstroComponent_') => `/** declare const Astro: Readonly>`; test('no props', async () => { - const input = '
    '; - const output = `${TSXPrefix} + const input = '
    '; + const output = `${TSXPrefix}
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('nested Props', async () => { - const input = `--- + const input = `--- function DoTheThing(Props) {} ---`; - const output = `${TSXPrefix} + const output = `${TSXPrefix} function DoTheThing(Props) {} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props interface', async () => { - const input = ` + const input = ` --- interface Props {} ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} interface Props {} {}; @@ -50,19 +50,19 @@ interface Props {} export default function __AstroComponent_(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props import', async () => { - const input = ` + const input = ` --- import { Props } from './somewhere'; ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} import { Props } from './somewhere'; @@ -71,19 +71,19 @@ import { Props } from './somewhere'; export default function __AstroComponent_(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props alias', async () => { - const input = ` + const input = ` --- import { MyComponent as Props } from './somewhere'; ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} import { MyComponent as Props } from './somewhere'; @@ -92,19 +92,19 @@ import { MyComponent as Props } from './somewhere'; export default function __AstroComponent_(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props type import', async () => { - const input = ` + const input = ` --- import type { Props } from './somewhere'; ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} import type { Props } from './somewhere'; @@ -113,19 +113,19 @@ import type { Props } from './somewhere'; export default function __AstroComponent_(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props type', async () => { - const input = ` + const input = ` --- type Props = {} ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} type Props = {} {}; @@ -134,19 +134,22 @@ type Props = {} export default function Test__AstroComponent_(_props: Props): any {} ${PREFIX('Test__AstroComponent_')}`; - const { code } = await convertToTSX(input, { filename: '/Users/nmoo/test.astro', sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { + filename: '/Users/nmoo/test.astro', + sourcemap: 'external', + }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props generic (simple)', async () => { - const input = ` + const input = ` --- interface Props {} ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} interface Props {} {}; @@ -155,19 +158,19 @@ interface Props {} export default function __AstroComponent_(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props generic (complex)', async () => { - const input = ` + const input = ` --- interface Props> {} ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} interface Props> {} {}; @@ -176,19 +179,19 @@ interface Props> {} export default function __AstroComponent_>(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props generic (very complex)', async () => { - const input = ` + const input = ` --- interface Props {} ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} interface Props {} {}; @@ -197,19 +200,19 @@ interface Props export default function __AstroComponent_(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('props generic (very complex II)', async () => { - const input = ` + const input = ` --- interface Props ? A : B, P extends string ? { [key: string]: any }: never> {} ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} interface Props ? A : B, P extends string ? { [key: string]: any }: never> {} {}; @@ -218,12 +221,12 @@ interface Props ? A : B, P extends string ? { [key: s export default function __AstroComponent_ ? A : B, P extends string ? { [key: string]: any }: never>(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('polymorphic props', async () => { - const input = ` + const input = ` --- interface Props extends HTMLAttributes { as?: Tag; @@ -232,7 +235,7 @@ interface Props extends HTMLAttributes<
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} interface Props extends HTMLAttributes { as?: Tag; } @@ -243,19 +246,19 @@ interface Props extends HTMLAttributes<
    export default function __AstroComponent_(_props: Props): any {} ${PREFIX()}`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('unrelated prop import', async () => { - const input = ` + const input = ` --- import SvelteOptionalProps from './SvelteOptionalProps.svelte'; --- `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} import SvelteOptionalProps from './SvelteOptionalProps.svelte'; @@ -263,18 +266,18 @@ import SvelteOptionalProps from './SvelteOptionalProps.svelte'; export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('unrelated sibling prop', async () => { - const input = `--- + const input = `--- import type { Props as ComponentBProps } from './ComponentB.astro' ---
    `; - const output = `${TSXPrefix} + const output = `${TSXPrefix} import type { Props as ComponentBProps } from './ComponentB.astro' {}; @@ -282,8 +285,8 @@ import type { Props as ComponentBProps } from './ComponentB.astro' export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); diff --git a/packages/compiler/test/tsx/raw.ts b/packages/compiler/test/tsx/raw.ts index 6c8fbedfe..7be5faf48 100644 --- a/packages/compiler/test/tsx/raw.ts +++ b/packages/compiler/test/tsx/raw.ts @@ -4,23 +4,23 @@ import * as assert from 'uvu/assert'; import { TSXPrefix } from '../utils'; test('style is raw', async () => { - const input = ''; - const output = `${TSXPrefix} + const input = ''; + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('is:raw is raw', async () => { - const input = '
    A{B}C
    '; - const output = `${TSXPrefix} + const input = '
    A{B}C
    '; + const output = `${TSXPrefix}
    {\`A{B}C\`}
    export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); diff --git a/packages/compiler/test/tsx/script.ts b/packages/compiler/test/tsx/script.ts index ab828f79b..13a486c95 100644 --- a/packages/compiler/test/tsx/script.ts +++ b/packages/compiler/test/tsx/script.ts @@ -4,37 +4,37 @@ import * as assert from 'uvu/assert'; import { TSXPrefix } from '../utils'; test('script function', async () => { - const input = ``; - const output = `${TSXPrefix} + const input = ``; + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('partytown function', async () => { - const input = ``; - const output = `${TSXPrefix} + const input = ``; + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test('ld+json wrapping', async () => { - const input = ``; - const output = `${TSXPrefix} + const input = ``; + const output = `${TSXPrefix} export default function __AstroComponent_(_props: Record): any {}\n`; - const { code } = await convertToTSX(input, { sourcemap: 'external' }); - assert.snapshot(code, output, 'expected code to match snapshot'); + const { code } = await convertToTSX(input, { sourcemap: 'external' }); + assert.snapshot(code, output, 'expected code to match snapshot'); }); test.run(); diff --git a/packages/compiler/test/utils.ts b/packages/compiler/test/utils.ts index b2f42b3d6..68f9bf768 100644 --- a/packages/compiler/test/utils.ts +++ b/packages/compiler/test/utils.ts @@ -3,77 +3,95 @@ import { TraceMap, generatedPositionFor, originalPositionFor } from '@jridgewell import sass from 'sass'; export async function preprocessStyle(value, attrs): Promise { - if (!attrs.lang) { - return null; - } - if (attrs.lang === 'scss') { - return transformSass(value); - } - return null; + if (!attrs.lang) { + return null; + } + if (attrs.lang === 'scss') { + return transformSass(value); + } + return null; } export function transformSass(value: string) { - return new Promise((resolve, reject) => { - sass.render({ data: value }, (err, result) => { - if (err) { - reject(err); - return; - } - resolve({ code: result.css.toString('utf8'), map: result.map }); - return; - }); - }); + return new Promise((resolve, reject) => { + sass.render({ data: value }, (err, result) => { + if (err) { + reject(err); + return; + } + resolve({ code: result.css.toString('utf8'), map: result.map }); + return; + }); + }); } export function getPositionFor(input: string, snippet: string) { - let index = 0; - let line = 0; - let column = 0; - for (const c of input) { - if (c === snippet[0] && input.slice(index).startsWith(snippet)) { - return { line: line + 1, column }; - } - if (c === '\n') { - line++; - column = 0; - } - column++; - index++; - } - return null; + let index = 0; + let line = 0; + let column = 0; + for (const c of input) { + if (c === snippet[0] && input.slice(index).startsWith(snippet)) { + return { line: line + 1, column }; + } + if (c === '\n') { + line++; + column = 0; + } + column++; + index++; + } + return null; } export async function testTSXSourcemap(input: string, snippet: string) { - const snippetLoc = getPositionFor(input, snippet); - if (!snippetLoc) throw new Error(`Unable to find "${snippet}"`); + const snippetLoc = getPositionFor(input, snippet); + if (!snippetLoc) throw new Error(`Unable to find "${snippet}"`); - const { code, map } = await convertToTSX(input, { sourcemap: 'both', filename: 'index.astro' }); - const tracer = new TraceMap(map); + const { code, map } = await convertToTSX(input, { sourcemap: 'both', filename: 'index.astro' }); + const tracer = new TraceMap(map); - const generated = generatedPositionFor(tracer, { source: 'index.astro', line: snippetLoc.line, column: snippetLoc.column }); - if (!generated || generated.line === null) { - console.log(code); - throw new Error(`"${snippet}" position incorrectly mapped in generated output.`); - } - const originalPosition = originalPositionFor(tracer, { line: generated.line, column: generated.column }); + const generated = generatedPositionFor(tracer, { + source: 'index.astro', + line: snippetLoc.line, + column: snippetLoc.column, + }); + if (!generated || generated.line === null) { + console.log(code); + throw new Error(`"${snippet}" position incorrectly mapped in generated output.`); + } + const originalPosition = originalPositionFor(tracer, { + line: generated.line, + column: generated.column, + }); - return originalPosition; + return originalPosition; } export async function testJSSourcemap(input: string, snippet: string) { - const snippetLoc = getPositionFor(input, snippet); - if (!snippetLoc) throw new Error(`Unable to find "${snippet}"`); + const snippetLoc = getPositionFor(input, snippet); + if (!snippetLoc) throw new Error(`Unable to find "${snippet}"`); - const { code, map } = await transform(input, { sourcemap: 'both', filename: 'index.astro', resolvePath: (i: string) => i }); - const tracer = new TraceMap(map); + const { code, map } = await transform(input, { + sourcemap: 'both', + filename: 'index.astro', + resolvePath: (i: string) => i, + }); + const tracer = new TraceMap(map); - const generated = generatedPositionFor(tracer, { source: 'index.astro', line: snippetLoc.line, column: snippetLoc.column }); - if (!generated || generated.line === null) { - console.log(code); - throw new Error(`"${snippet}" position incorrectly mapped in generated output.`); - } - const originalPosition = originalPositionFor(tracer, { line: generated.line, column: generated.column }); + const generated = generatedPositionFor(tracer, { + source: 'index.astro', + line: snippetLoc.line, + column: snippetLoc.column, + }); + if (!generated || generated.line === null) { + console.log(code); + throw new Error(`"${snippet}" position incorrectly mapped in generated output.`); + } + const originalPosition = originalPositionFor(tracer, { + line: generated.line, + column: generated.column, + }); - return originalPosition; + return originalPosition; } export const TSXPrefix = '/* @jsxImportSource astro */\n\n'; diff --git a/packages/compiler/tsup.config.ts b/packages/compiler/tsup.config.ts index 6b7211a9e..336994165 100644 --- a/packages/compiler/tsup.config.ts +++ b/packages/compiler/tsup.config.ts @@ -1,14 +1,14 @@ import { defineConfig } from 'tsup'; export default defineConfig((options) => ({ - entry: ['src/node/**', 'src/browser/**', 'src/shared/**'], - outDir: 'dist', - format: ['cjs', 'esm'], - dts: true, - clean: true, - minify: !options.watch, - sourcemap: Boolean(options.watch), - watch: options.watch, - publicDir: 'wasm', - shims: true, + entry: ['src/node/**', 'src/browser/**', 'src/shared/**'], + outDir: 'dist', + format: ['cjs', 'esm'], + dts: true, + clean: true, + minify: !options.watch, + sourcemap: Boolean(options.watch), + watch: options.watch, + publicDir: 'wasm', + shims: true, }));