diff options
author | 2021-03-16 12:37:45 -0600 | |
---|---|---|
committer | 2021-03-16 12:37:45 -0600 | |
commit | 8c45c4a8567e31a10ae954fa11a0f3f1f94fd89c (patch) | |
tree | e526f67e93ed5a7fcf9237c2d30ec536a4a96d7b /src/transform2.ts | |
parent | 1f5c7c791fe3595f4c775192ffc6b6a15efb71a1 (diff) | |
download | astro-8c45c4a8567e31a10ae954fa11a0f3f1f94fd89c.tar.gz astro-8c45c4a8567e31a10ae954fa11a0f3f1f94fd89c.tar.zst astro-8c45c4a8567e31a10ae954fa11a0f3f1f94fd89c.zip |
Annoying Lint PR™ (#3)
* Add Prettier + ESLint
* Format files
Diffstat (limited to 'src/transform2.ts')
-rw-r--r-- | src/transform2.ts | 270 |
1 files changed, 121 insertions, 149 deletions
diff --git a/src/transform2.ts b/src/transform2.ts index 485c575c6..ab516deac 100644 --- a/src/transform2.ts +++ b/src/transform2.ts @@ -1,15 +1,15 @@ -import type { TemplateNode } from "./@types/compiler/interfaces"; - -import path from "path"; -import astring from "astring"; -import esbuild from "esbuild"; -import eslexer from "es-module-lexer"; -import micromark from "micromark"; -import gfmSyntax from "micromark-extension-gfm"; -import matter from "gray-matter"; +import type { TemplateNode } from './@types/compiler/interfaces'; + +import path from 'path'; +import astring from 'astring'; +import esbuild from 'esbuild'; +import eslexer from 'es-module-lexer'; +import micromark from 'micromark'; +import gfmSyntax from 'micromark-extension-gfm'; +import matter from 'gray-matter'; // @ts-ignore -import gfmHtml from "micromark-extension-gfm/html.js"; -import { walk, parse } from "./compiler.js"; +import gfmHtml from 'micromark-extension-gfm/html.js'; +import { walk, parse } from './compiler.js'; import markdownEncode from './markdown-encode.js'; import { preparse } from './parser.js'; @@ -18,18 +18,20 @@ const { transformSync } = esbuild; interface Attribute { start: 574; end: 595; - type: "Attribute"; - name: "class"; + type: 'Attribute'; + name: 'class'; value: any; } interface CompileOptions { - resolve: (p: string) => string + resolve: (p: string) => string; } const defaultCompileOptions: CompileOptions = { - resolve(p: string) { return p; } -} + resolve(p: string) { + return p; + }, +}; function internalImport(internalPath: string) { return `/__hmx_internal__/${internalPath}`; @@ -47,7 +49,7 @@ function getAttributes(attrs: Attribute[]): Record<string, string> { } if (attr.value.length > 1) { result[attr.name] = - "(" + + '(' + attr.value .map((v: TemplateNode) => { if (v.expression) { @@ -56,21 +58,21 @@ function getAttributes(attrs: Attribute[]): Record<string, string> { return JSON.stringify(getTextFromAttribute(v)); } }) - .join("+") + - ")"; + .join('+') + + ')'; continue; } const val: TemplateNode = attr.value[0]; switch (val.type) { - case "MustacheTag": - result[attr.name] = "(" + val.expression + ")"; + case 'MustacheTag': + result[attr.name] = '(' + val.expression + ')'; continue; - case "Text": + case 'Text': result[attr.name] = JSON.stringify(getTextFromAttribute(val)); continue; default: console.log(val); - throw new Error("UNKNOWN V"); + throw new Error('UNKNOWN V'); } } return result; @@ -84,24 +86,20 @@ function getTextFromAttribute(attr: any): string { return attr.data; } console.log(attr); - throw new Error("UNKNOWN attr"); + throw new Error('UNKNOWN attr'); } function generateAttributes(attrs: Record<string, string>): string { - let result: string = "{"; + let result: string = '{'; for (const [key, val] of Object.entries(attrs)) { - result += JSON.stringify(key) + ":" + val + ","; + result += JSON.stringify(key) + ':' + val + ','; } - return result + "}"; + return result + '}'; } -function getComponentWrapper( - _name: string, - { type, url }: { type: string; url: string }, - { resolve }: CompileOptions -) { - const [name, kind] = _name.split(":"); - switch(type) { +function getComponentWrapper(_name: string, { type, url }: { type: string; url: string }, { resolve }: CompileOptions) { + const [name, kind] = _name.split(':'); + switch (type) { case '.hmx': { if (kind) { throw new Error(`HMX does not support :${kind}`); @@ -112,11 +110,9 @@ function getComponentWrapper( }; } case '.jsx': { - if (kind === "dynamic") { + if (kind === 'dynamic') { return { - wrapper: `__preact_dynamic(${name}, new URL(${JSON.stringify( - url.replace(/\.[^.]+$/, ".js") - )}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('preact')}')`, + wrapper: `__preact_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('preact')}')`, wrapperImport: `import {__preact_dynamic} from '${internalImport('render/preact.js')}';`, }; } else { @@ -127,26 +123,22 @@ function getComponentWrapper( } } case '.svelte': { - if(kind === "dynamic") { + if (kind === 'dynamic') { return { - wrapper: `__svelte_dynamic(${name}, new URL(${JSON.stringify( - url.replace(/\.[^.]+$/, ".svelte.js") - )}, \`http://TEST\${import.meta.url}\`).pathname)`, + wrapper: `__svelte_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.svelte.js'))}, \`http://TEST\${import.meta.url}\`).pathname)`, wrapperImport: `import {__svelte_dynamic} from '${internalImport('render/svelte.js')}';`, }; } else { return { wrapper: `__svelte_static(${name})`, - wrapperImport: `import {__svelte_static} from '${internalImport('render/svelte.js')}';` + wrapperImport: `import {__svelte_static} from '${internalImport('render/svelte.js')}';`, }; } } case '.vue': { - if(kind === "dynamic") { + if (kind === 'dynamic') { return { - wrapper: `__vue_dynamic(${name}, new URL(${JSON.stringify( - url.replace(/\.[^.]+$/, ".vue.js") - )}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('vue')}')`, + wrapper: `__vue_dynamic(${name}, new URL(${JSON.stringify(url.replace(/\.[^.]+$/, '.vue.js'))}, \`http://TEST\${import.meta.url}\`).pathname, '${resolve('vue')}')`, wrapperImport: `import {__vue_dynamic} from '${internalImport('render/vue.js')}';`, }; } else { @@ -154,42 +146,44 @@ function getComponentWrapper( wrapper: `__vue_static(${name})`, wrapperImport: ` import {__vue_static} from '${internalImport('render/vue.js')}'; - ` + `, }; } } } - throw new Error("Unknown Component Type: " + name); + throw new Error('Unknown Component Type: ' + name); } function runPreparser(template: string): string { - const doc = preparse(template, tag => { - if(tag.tagName === 'script') { + const doc = preparse(template, (tag) => { + if (tag.tagName === 'script') { let isSetup = false; - for(let attr of tag.attributes) { - if(attr.name === 'hmx' && attr.value === 'setup') { + for (let attr of tag.attributes) { + if (attr.name === 'hmx' && attr.value === 'setup') { isSetup = true; break; } } - if(isSetup && typeof tag.children[0] === 'string') { + if (isSetup && typeof tag.children[0] === 'string') { debugger; const content = tag.children[0]; let { code } = transformSync(content, { - loader: "tsx", - jsxFactory: "h", - jsxFragment: "Fragment", - charset: "utf8", + loader: 'tsx', + jsxFactory: 'h', + jsxFragment: 'Fragment', + charset: 'utf8', }); return { ...tag, - children: [{ - type: 0, - data: code, - start: 0, - end: 0 - }] + children: [ + { + type: 0, + data: code, + start: 0, + end: 0, + }, + ], }; } } @@ -209,9 +203,9 @@ async function convertHmxToJsx(template: string, compileOptions: CompileOptions) const ast = parse(template, {}); // Todo: Validate that `h` and `Fragment` aren't defined in the script - const script = ast.instance ? astring.generate(ast.instance.content) : ""; + const script = ast.instance ? astring.generate(ast.instance.content) : ''; - const [scriptImports] = eslexer.parse(script, "optional-sourcename"); + const [scriptImports] = eslexer.parse(script, 'optional-sourcename'); const components = Object.fromEntries( scriptImports.map((imp) => { const componentType = path.posix.extname(imp.n!); @@ -222,7 +216,7 @@ async function convertHmxToJsx(template: string, compileOptions: CompileOptions) const additionalImports = new Set<string>(); let items: { name: string; jsx: string }[] = []; - let mode: "JSX" | "SCRIPT" | "SLOT" = "JSX"; + let mode: 'JSX' | 'SCRIPT' | 'SLOT' = 'JSX'; let collectionItem: { name: string; jsx: string } | undefined; let currentItemName: string | undefined; let currentDepth = 0; @@ -230,14 +224,14 @@ async function convertHmxToJsx(template: string, compileOptions: CompileOptions) walk(ast.html as any, { // @ts-ignore enter(node: TemplateNode, parent, prop, index) { - // console.log("enter", node.type); + // console.log("enter", node.type); switch (node.type) { - case "MustacheTag": + case 'MustacheTag': let { code } = transformSync(node.expression, { - loader: "jsx", - jsxFactory: "h", - jsxFragment: "Fragment", - charset: "utf8", + loader: 'jsx', + jsxFactory: 'h', + jsxFragment: 'Fragment', + charset: 'utf8', }); let matches: RegExpExecArray[] = []; @@ -249,142 +243,127 @@ async function convertHmxToJsx(template: string, compileOptions: CompileOptions) } for (const match of matches.reverse()) { const name = match[1]; - const [componentName, componentKind] = name.split(":"); + const [componentName, componentKind] = name.split(':'); if (!components[componentName]) { throw new Error(`Unknown Component: ${componentName}`); } - const { wrapper, wrapperImport } = getComponentWrapper( - name, - components[componentName], - compileOptions - ); + const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], compileOptions); if (wrapperImport) { additionalImports.add(wrapperImport); } if (wrapper !== name) { - code = - code.slice(0, match.index + 2) + - wrapper + - code.slice(match.index + match[0].length - 1); + code = code.slice(0, match.index + 2) + wrapper + code.slice(match.index + match[0].length - 1); } } - collectionItem!.jsx += `,(${code.trim().replace(/\;$/, "")})`; + collectionItem!.jsx += `,(${code.trim().replace(/\;$/, '')})`; return; - case "Slot": - mode = "SLOT"; + case 'Slot': + mode = 'SLOT'; collectionItem!.jsx += `,child`; return; - case "Comment": + case 'Comment': return; - case "Fragment": + case 'Fragment': // Ignore if its the top level fragment // This should be cleaned up, but right now this is how the old thing worked if (!collectionItem) { return; } - case "InlineComponent": - case "Element": + case 'InlineComponent': + case 'Element': const name: string = node.name; if (!name) { console.log(node); - throw new Error("AHHHH"); + throw new Error('AHHHH'); } const attributes = getAttributes(node.attributes); currentDepth++; currentItemName = name; if (!collectionItem) { - collectionItem = { name, jsx: "" }; + collectionItem = { name, jsx: '' }; items.push(collectionItem); } - collectionItem.jsx += collectionItem.jsx === "" ? "" : ","; + collectionItem.jsx += collectionItem.jsx === '' ? '' : ','; const COMPONENT_NAME_SCANNER = /^[A-Z]/; if (!COMPONENT_NAME_SCANNER.test(name)) { - collectionItem.jsx += `h("${name}", ${ - attributes ? generateAttributes(attributes) : "null" - }`; + collectionItem.jsx += `h("${name}", ${attributes ? generateAttributes(attributes) : 'null'}`; return; } if (name === 'Component') { collectionItem.jsx += `h(Fragment, null`; return; } - const [componentName, componentKind] = name.split(":"); + const [componentName, componentKind] = name.split(':'); const componentImportData = components[componentName]; if (!componentImportData) { throw new Error(`Unknown Component: ${componentName}`); } - const { wrapper, wrapperImport } = getComponentWrapper( - name, - components[componentName], - compileOptions - ); + const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], compileOptions); if (wrapperImport) { additionalImports.add(wrapperImport); } - collectionItem.jsx += `h(${wrapper}, ${ - attributes ? generateAttributes(attributes) : "null" - }`; + collectionItem.jsx += `h(${wrapper}, ${attributes ? generateAttributes(attributes) : 'null'}`; return; - case "Attribute": { + case 'Attribute': { this.skip(); return; } - case "Text": { + case 'Text': { const text = getTextFromAttribute(node); - if (mode === "SLOT") { + if (mode === 'SLOT') { return; } if (!text.trim()) { return; } if (!collectionItem) { - throw new Error("Not possible! TEXT:" + text); + throw new Error('Not possible! TEXT:' + text); } - if (currentItemName === "script" || currentItemName === "code") { - collectionItem.jsx += "," + JSON.stringify(text); + if (currentItemName === 'script' || currentItemName === 'code') { + collectionItem.jsx += ',' + JSON.stringify(text); return; } - collectionItem.jsx += "," + JSON.stringify(text); + collectionItem.jsx += ',' + JSON.stringify(text); return; } default: console.log(node); - throw new Error("Unexpected node type: " + node.type); + throw new Error('Unexpected node type: ' + node.type); } }, // @ts-ignore leave(node: TemplateNode, parent, prop, index) { - // console.log("leave", node.type); + // console.log("leave", node.type); switch (node.type) { - case "Text": - case "MustacheTag": - case "Attribute": - case "Comment": + case 'Text': + case 'MustacheTag': + case 'Attribute': + case 'Comment': return; - case "Slot": { + case 'Slot': { const name = node.name; - if (name === "slot") { - mode = "JSX"; + if (name === 'slot') { + mode = 'JSX'; } return; } - case "Fragment": + case 'Fragment': if (!collectionItem) { return; } - case "Element": - case "InlineComponent": + case 'Element': + case 'InlineComponent': if (!collectionItem) { - throw new Error("Not possible! CLOSE " + node.name); + throw new Error('Not possible! CLOSE ' + node.name); } - collectionItem.jsx += ")"; + collectionItem.jsx += ')'; currentDepth--; if (currentDepth === 0) { collectionItem = undefined; } return; default: - throw new Error("Unexpected node type: " + node.type); + throw new Error('Unexpected node type: ' + node.type); } }, }); @@ -398,7 +377,7 @@ async function convertHmxToJsx(template: string, compileOptions: CompileOptions) */ return { - script: script + "\n" + Array.from(additionalImports).join("\n"), + script: script + '\n' + Array.from(additionalImports).join('\n'), items, }; } @@ -427,36 +406,31 @@ async function convertMdToJsx(contents: string, compileOptions: CompileOptions) }, }; - return convertHmxToJsx(`<script hmx="setup">export function setup() { + return convertHmxToJsx( + `<script hmx="setup">export function setup() { return ${JSON.stringify(setupData)}; - }</script><head></head><body>${mdHtml}</body>`, compileOptions); + }</script><head></head><body>${mdHtml}</body>`, + compileOptions + ); } -async function transformFromSource( - contents: string, - filename: string, - compileOptions: CompileOptions -): Promise<ReturnType<typeof convertHmxToJsx>> { +async function transformFromSource(contents: string, filename: string, compileOptions: CompileOptions): Promise<ReturnType<typeof convertHmxToJsx>> { switch (path.extname(filename)) { - case ".hmx": + case '.hmx': return convertHmxToJsx(contents, compileOptions); - case ".md": + case '.md': return convertMdToJsx(contents, compileOptions); default: - throw new Error("Not Supported!"); + throw new Error('Not Supported!'); } } export async function compilePage(source: string, filename: string, opts: CompileOptions = defaultCompileOptions) { const sourceJsx = await transformFromSource(source, filename, opts); - const headItem = sourceJsx.items.find((item) => item.name === "head"); - const bodyItem = sourceJsx.items.find((item) => item.name === "body"); - const headItemJsx = !headItem - ? "null" - : headItem.jsx.replace('"head"', 'isRoot ? "head" : Fragment'); - const bodyItemJsx = !bodyItem - ? "null" - : bodyItem.jsx.replace('"head"', 'isRoot ? "body" : Fragment'); + const headItem = sourceJsx.items.find((item) => item.name === 'head'); + const bodyItem = sourceJsx.items.find((item) => item.name === 'body'); + const headItemJsx = !headItem ? 'null' : headItem.jsx.replace('"head"', 'isRoot ? "head" : Fragment'); + const bodyItemJsx = !bodyItem ? 'null' : bodyItem.jsx.replace('"head"', 'isRoot ? "body" : Fragment'); const modJsx = ` ${sourceJsx.script} @@ -473,9 +447,7 @@ export function body({title, description, props}, child, isRoot) { return (${bod export async function compileComponent(source: string, filename: string, opts: CompileOptions = defaultCompileOptions) { const sourceJsx = await transformFromSource(source, filename, opts); - const componentJsx = sourceJsx.items.find( - (item) => item.name === "Component" - ); + const componentJsx = sourceJsx.items.find((item) => item.name === 'Component'); if (!componentJsx) { throw new Error(`${filename} <Component> expected!`); } |