summaryrefslogtreecommitdiff
path: root/src/transform2.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/transform2.ts')
-rw-r--r--src/transform2.ts270
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!`);
}