From 5395b21e668b228450178561352604d820687063 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Wed, 13 Mar 2024 11:17:31 -0700
Subject: [PATCH 01/23] copy TemplateGenerator and make getTargets and bind
function
---
packages/compiler/compiler.js | 22 +-
packages/compiler/compiler.test.js | 38 +++
packages/compiler/source-maps.test.js | 46 +--
packages/compiler/transform/BlindGenerator.js | 268 ++++++++++++++++++
packages/compiler/transform/bind.test.js | 42 +++
5 files changed, 385 insertions(+), 31 deletions(-)
create mode 100644 packages/compiler/transform/BlindGenerator.js
create mode 100644 packages/compiler/transform/bind.test.js
diff --git a/packages/compiler/compiler.js b/packages/compiler/compiler.js
index 64da7ee..5ecd364 100644
--- a/packages/compiler/compiler.js
+++ b/packages/compiler/compiler.js
@@ -33,24 +33,30 @@ export function parse(code, options = {}) {
}
// generate = ast --> code + html
-export function generate(ast, config) {
- const file = config?.sourceFile || 'module.jsx';
+export function generateWith(generator, ast, config) {
+ const file = config?.sourceFile || 'script.js';
const sourceMap = new SourceMapGenerator({ file });
- const generator = new TemplateGenerator();
-
- let code = astring(ast, {
+ const code = astring(ast, {
...config,
generator,
sourceMap,
});
- const { templates } = generator;
-
return {
code,
- templates,
map: sourceMap.toJSON(),
// exposed for testing
_sourceMap: sourceMap,
};
}
+
+export function generate(ast, config) {
+ const generator = new TemplateGenerator();
+ const generated = generateWith(generator, ast, config);
+ const { templates } = generator;
+
+ return {
+ ...generated,
+ templates,
+ };
+}
diff --git a/packages/compiler/compiler.test.js b/packages/compiler/compiler.test.js
index 6b03f26..00e75f8 100644
--- a/packages/compiler/compiler.test.js
+++ b/packages/compiler/compiler.test.js
@@ -15,6 +15,44 @@ const compile = input => {
};
describe('JSX dom literals', () => {
+
+ test.only('Hello Azoth', ({ expect }) => {
+ const input = `const t =
+ Hello {"Azoth"}
+
;`;
+
+ const { code, templates } = compile(input);
+
+ expect(code).toMatchInlineSnapshot(`
+ "import { __compose } from 'azoth/runtime';
+ import { ta516887159 } from 'virtual:azoth-templates?id=a516887159';
+ const t = (() => {
+ const __root = ta516887159()[0];
+ const __child1 = __root.childNodes[1];
+ __root.className = ("className");
+ __compose(__child1, "Azoth");
+ return __root;
+ })();
+ "
+ `);
+ expect(templates).toMatchInlineSnapshot(`
+ [
+ {
+ "html": "
+ Hello
+
",
+ "id": "a516887159",
+ "imports": [
+ "compose",
+ ],
+ "isDomFragment": false,
+ "isEmpty": false,
+ "isStatic": false,
+ },
+ ]
+ `);
+ });
+
test('complex template structure with props and child nodes', ({ expect }) => {
const input = `const t =
{"felix"}
diff --git a/packages/compiler/source-maps.test.js b/packages/compiler/source-maps.test.js
index 4adf77a..59dd680 100644
--- a/packages/compiler/source-maps.test.js
+++ b/packages/compiler/source-maps.test.js
@@ -24,7 +24,7 @@ test('static one line', ({ expect }) => {
"name": "t",
"originalColumn": 6,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 10,
@@ -32,7 +32,7 @@ test('static one line', ({ expect }) => {
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 23,
@@ -40,7 +40,7 @@ test('static one line', ({ expect }) => {
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
]
`);
@@ -68,7 +68,7 @@ test('{...} one line', ({ expect }) => {
"name": undefined,
"originalColumn": 0,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 30,
@@ -76,7 +76,7 @@ test('{...} one line', ({ expect }) => {
"name": undefined,
"originalColumn": 0,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 19,
@@ -84,7 +84,7 @@ test('{...} one line', ({ expect }) => {
"name": "div",
"originalColumn": 1,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 36,
@@ -92,7 +92,7 @@ test('{...} one line', ({ expect }) => {
"name": undefined,
"originalColumn": 11,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 2,
@@ -100,7 +100,7 @@ test('{...} one line', ({ expect }) => {
"name": undefined,
"originalColumn": 11,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 12,
@@ -108,7 +108,7 @@ test('{...} one line', ({ expect }) => {
"name": undefined,
"originalColumn": 11,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 22,
@@ -116,7 +116,7 @@ test('{...} one line', ({ expect }) => {
"name": "place",
"originalColumn": 12,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 9,
@@ -124,7 +124,7 @@ test('{...} one line', ({ expect }) => {
"name": undefined,
"originalColumn": 0,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
]
`);
@@ -149,7 +149,7 @@ test('static three line', ({ expect }) => {
"name": "t",
"originalColumn": 6,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 10,
@@ -157,7 +157,7 @@ test('static three line', ({ expect }) => {
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 23,
@@ -165,7 +165,7 @@ test('static three line', ({ expect }) => {
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
]
`);
@@ -195,7 +195,7 @@ test('{...} three line', ({ expect }) => {
"name": "t",
"originalColumn": 6,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 17,
@@ -203,7 +203,7 @@ test('{...} three line', ({ expect }) => {
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 30,
@@ -211,7 +211,7 @@ test('{...} three line', ({ expect }) => {
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 19,
@@ -219,7 +219,7 @@ test('{...} three line', ({ expect }) => {
"name": "div",
"originalColumn": 11,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 36,
@@ -227,7 +227,7 @@ test('{...} three line', ({ expect }) => {
"name": undefined,
"originalColumn": 14,
"originalLine": 2,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 2,
@@ -235,7 +235,7 @@ test('{...} three line', ({ expect }) => {
"name": undefined,
"originalColumn": 14,
"originalLine": 2,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 12,
@@ -243,7 +243,7 @@ test('{...} three line', ({ expect }) => {
"name": undefined,
"originalColumn": 14,
"originalLine": 2,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 22,
@@ -251,7 +251,7 @@ test('{...} three line', ({ expect }) => {
"name": "place",
"originalColumn": 15,
"originalLine": 2,
- "source": "module.jsx",
+ "source": "script.js",
},
{
"generatedColumn": 9,
@@ -259,7 +259,7 @@ test('{...} three line', ({ expect }) => {
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
- "source": "module.jsx",
+ "source": "script.js",
},
]
`);
diff --git a/packages/compiler/transform/BlindGenerator.js b/packages/compiler/transform/BlindGenerator.js
new file mode 100644
index 0000000..20b46c5
--- /dev/null
+++ b/packages/compiler/transform/BlindGenerator.js
@@ -0,0 +1,268 @@
+import { Generator, writeNextLine } from './GeneratorBase.js';
+import { isValidESIdentifier } from 'is-valid-es-identifier';
+import { generate as astring } from 'astring';
+import { generateWith } from '../compiler.js';
+
+const OPENING_PROP = {
+ JSXElement: 'openingElement',
+ JSXFragment: 'openingFragment',
+};
+
+const IS_OPENING = {
+ JSXOpeningElement: true,
+ JSXOpeningFragment: true,
+};
+
+export class BindGenerator extends Generator {
+ static generate(template) {
+ const generator = new BindGenerator(template);
+ return generateWith(generator, template.node);
+ }
+
+ constructor(template) {
+ super();
+ this.template = template;
+ }
+
+ JSXFragment(_node, state) {
+ this.JSXTemplate(state);
+ }
+
+ JSXElement(_node, state) {
+ this.JSXTemplate(state);
+ }
+
+ JSXTemplate(state) {
+ const { template } = this;
+ // Short-circuit templates
+ const { isStatic, bindings, node: root } = template;
+ const { isComponent } = root;
+ if(isStatic || isComponent) {
+ if(isComponent) this.CreateElement(root, state);
+ else if(isStatic) this.StaticRoot(template, state);
+ return;
+ }
+
+ this.GetTargets(template, state);
+
+
+ this.Bindings(template, state);
+
+
+ }
+
+ // process javascript in {...} exprs,
+ // supports nested template: recursive processing ftw!
+ JSXExpressionContainer({ expression }, state) {
+ this[expression.type](expression, state);
+ }
+
+ JSXIdentifier(identifier, state) {
+ state.write(identifier.name, identifier);
+ }
+
+ StaticRoot(template, state) {
+ this.TemplateRenderer(template, state);
+ if(!template.isEmpty) state.write(`[0]`, template.node); // dom root
+ }
+
+ TemplateRenderer({ id, isEmpty, isDomFragment, node }, state) {
+ if(isEmpty) {
+ state.write('null', node);
+ return;
+ }
+ state.write(`t${id}`, node);
+ state.write(`(`);
+ if(isDomFragment) state.write('true');
+ state.write(`)`);
+ }
+
+ GetTargets(template, state) {
+ const { boundElements, bindings, node } = template;
+
+ const hasTargets = !!boundElements.length;
+ const params = hasTargets ? `root, targets` : `root`;
+ state.write(`function getTargets(${params}) {`);
+ state.indentLevel++;
+
+ // target variables
+ for(let i = 0; i < boundElements.length; i++) {
+ const boundElement = boundElements[i];
+ const opening = boundElement.openingElement || boundElement.openFragment;
+ writeNextLine(state);
+ state.write(`const target${i} = `);
+ state.write(`targets[${i}]`, opening?.name);
+ state.write(`;`);
+ }
+
+ const returnValues = [];
+ for(let i = 0; i < bindings.length; i++) {
+ const { element, type, index, node } = bindings[i];
+ const { queryIndex } = element;
+ const varName = queryIndex === -1 ? `root` : `target${queryIndex}`;
+ if(type !== 'child') {
+ returnValues.push(varName);
+ continue;
+ }
+
+ let opening = null;
+ if(IS_OPENING[element.type]) opening = element;
+ else {
+ const prop = OPENING_PROP[element.type];
+ if(prop) opening = element[prop];
+ else {
+ throw new TypeError(`Unexpected binding node type "${node.type}"`);
+ }
+ }
+
+ const childVar = `child${i}`;
+ returnValues.push(childVar);
+ writeNextLine(state);
+ state.write(`const ${childVar} = `);
+ state.write(`${varName}.childNodes`, opening.name);
+ state.write(`[${index}]`, node);
+ state.write(`;`);
+ }
+
+ writeNextLine(state);
+ state.write(`return [${returnValues.join(', ')}];`);
+
+ state.indentLevel--;
+ writeNextLine(state);
+ state.write(`}`);
+ state.write(state.lineEnd);
+ writeNextLine(state);
+ }
+
+ Bindings(template, state) {
+ const { boundElements, bindings, node } = template;
+
+
+ const params = [];
+ for(let i = 0; i < bindings.length; i++) {
+ params.push(`p${i}`);
+ }
+ state.write(`function apply(${params.join(', ')}) {`);
+ state.indentLevel++;
+ writeNextLine(state);
+
+ const returnStatement = template.node.returnStatement;
+
+
+ // template service renderer call
+ const hasTargets = !!boundElements.length;
+
+ const vars = ['root'];
+ for(let i = 0; i < bindings.length; i++) {
+ vars.push(`t${i}`);
+ }
+
+ state.write(`const [${vars.join(', ')}] = getTargets();`);
+
+ // bindings
+ for(let i = 0; i < bindings.length; i++) {
+ const { element, type, node, expr } = bindings[i];
+ writeNextLine(state);
+
+ if(!this[expr.type]) {
+ throw new TypeError(`Unexpected Binding expression AST type "${expr.type}"`);
+ }
+
+ if(node.isComponent) {
+ this.ComposeElement(node, expr, i, state);
+ continue;
+ }
+ if(type === 'child') {
+ this.Compose(node, expr, i, state);
+ continue;
+ }
+ if(type === 'prop') {
+ this.BindingProp(node, expr, i, element, state);
+ continue;
+ }
+
+ const message = `Unexpected binding type "${type}", expected "child" or "prop"`;
+ throw new Error(message);
+ }
+
+ state.indentLevel--;
+ writeNextLine(state);
+ state.write(`}`);
+ state.write(state.lineEnd);
+ }
+
+ Compose(node, expr, index, state) {
+ state.write(`compose(`, node);
+ state.write(`t${index}, `, node);
+ state.write(`p${index}`, expr);
+ // this[expr.type](expr, state);
+ state.write(`);`);
+ }
+
+ ComposeElement(node, expr, index, state) {
+ state.write(`composeElement(`, node);
+ state.write(`t${index}, `);
+ this.CompleteElement(node, expr, state);
+ state.write(`);`);
+ }
+
+ CreateElement(node, state) {
+ state.write(`createElement(`, node);
+ this.CompleteElement(node, node.componentExpr, state);
+ state.write(`)`);
+ }
+
+ CompleteElement({ props, slotFragment }, expr, state) {
+ this[expr.type](expr, state);
+ if(props?.length) {
+ this.ComponentProps(props, state);
+ }
+ else if(slotFragment) state.write(`, null`);
+
+ if(slotFragment) {
+ state.write(', ');
+ this.JSXTemplate(slotFragment, state);
+ }
+ }
+
+ ComponentProps(props, state) {
+ state.write(`, {`);
+ for(let i = 0; i < props.length; i++) {
+ const { node, expr } = props[i];
+ // TODO: Dom lookup, JS .prop v['prop'], etc.
+ // refactor with code below
+ state.write(` `);
+ state.write(node.name.name, node.name);
+ state.write(`: `);
+ this[expr.type](expr, state);
+ state.write(`,`);
+ }
+ state.write(` }`);
+ }
+
+ BindingProp(node, expr, index, element, state) {
+ const { queryIndex, openingElement, openingFragment } = element;
+ const varName = queryIndex === -1 ? `root` : `target${queryIndex}`;
+ const opening = openingElement ?? openingFragment;
+ state.write(`t${index}`, opening.name);
+ // TODO: more property validation
+ const identity = node.name;
+ const propName = identity.name;
+ // TODO: refactor with component props
+ if(isValidESIdentifier(propName)) {
+ state.write(`.`);
+ state.write(propName, node.name);
+ }
+ else {
+ state.write(`["`, node.name);
+ state.write(propName, node.name);
+ state.write(`"]`);
+ }
+
+ /* expression */
+ state.write(` = `);
+ // this[expr.type](expr, state);
+ state.write(`p${index}`, expr);
+ state.write(`;`);
+ }
+}
diff --git a/packages/compiler/transform/bind.test.js b/packages/compiler/transform/bind.test.js
new file mode 100644
index 0000000..18f9b15
--- /dev/null
+++ b/packages/compiler/transform/bind.test.js
@@ -0,0 +1,42 @@
+/* eslint-disable no-undef */
+import { BindGenerator } from './BlindGenerator.js';
+import { parse, generate as _generate } from '../compiler.js';
+import { describe, test } from 'vitest';
+
+function preParse(input) {
+ const ast = parse(input);
+ return _generate(ast);
+}
+describe('Bind Generator', () => {
+
+ test('Hello Bind', ({ expect }) => {
+ // const input = `const t =
yo
;`;
+ const input = `const t =
+ Hello hey {"Azoth"}!
+
;`;
+
+ const initial = preParse(input);
+ const template = initial.templates[0];
+ expect(template.node.type).toBe('JSXElement');
+
+ const { code } = BindGenerator.generate(template);
+
+ expect(code).toMatchInlineSnapshot(`
+ "function getTargets(root, targets) {
+ const target0 = targets[0];
+ const child1 = target0.childNodes[1];
+ return [root, child1];
+ }
+
+ function apply(p0, p1) {
+ const [root, t0, t1] = getTargets();
+ t0.className = p0;
+ compose(t1, p1);
+ }
+ "
+ `);
+
+
+ });
+
+});
\ No newline at end of file
From 79c4944e83d8e7b38ef2524380758918de8a2afb Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Wed, 13 Mar 2024 11:17:33 -0700
Subject: [PATCH 02/23] Copy TemplateGenerator and make getTargets and bind
functions
---
docs/get-started.md | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/docs/get-started.md b/docs/get-started.md
index fc82f20..5cc0f79 100644
--- a/docs/get-started.md
+++ b/docs/get-started.md
@@ -1,2 +1,25 @@
# Get Started
+
+
+
+## Developer Value Proposition:
+
+It is easier to reason about rendering with present data at multiple times
+
+Easier to reason about multiple data projection rendering channels
+
+
+
+
+
+
+## Data projection rendering
+
+Simple to think about:
+- all inputs present and accounted for
+- structuring and presentation components
+- html only
+- no component reentry
+
+
From fa99fa24f591651d2d926b3c0ac3db3cd4da7bc1 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Fri, 15 Mar 2024 07:08:24 -0700
Subject: [PATCH 03/23] leaner functions
---
packages/compiler/transform/Analyzer.js | 5 +-
packages/compiler/transform/BlindGenerator.js | 96 +++++++++----------
packages/compiler/transform/Template.js | 2 +-
packages/compiler/transform/bind.test.js | 13 ++-
4 files changed, 57 insertions(+), 59 deletions(-)
diff --git a/packages/compiler/transform/Analyzer.js b/packages/compiler/transform/Analyzer.js
index d9fc5ff..8792f25 100644
--- a/packages/compiler/transform/Analyzer.js
+++ b/packages/compiler/transform/Analyzer.js
@@ -92,6 +92,7 @@ export class Analyzer {
#bind(type, node, expr, index) {
const element = this.#elements.current;
+ element.isRoot = element === this.#root;
const binding = {
element,
@@ -106,6 +107,8 @@ export class Analyzer {
if(type !== 'prop') {
throw new TypeError(`Unexpected binding type "${type}", expected "prop"`);
}
+
+ // early exit! components get bindings as props
element.props.push(binding);
return;
}
@@ -116,7 +119,7 @@ export class Analyzer {
this.#imports.add('compose');
}
- if(element === this.#root) {
+ if(element.isRoot) {
// root can't be a "target", it gets a -1 queryIndex
// to signal bound template root (either el or fragment)
element.queryIndex = -1;
diff --git a/packages/compiler/transform/BlindGenerator.js b/packages/compiler/transform/BlindGenerator.js
index 20b46c5..37fa480 100644
--- a/packages/compiler/transform/BlindGenerator.js
+++ b/packages/compiler/transform/BlindGenerator.js
@@ -1,17 +1,27 @@
import { Generator, writeNextLine } from './GeneratorBase.js';
import { isValidESIdentifier } from 'is-valid-es-identifier';
-import { generate as astring } from 'astring';
import { generateWith } from '../compiler.js';
-const OPENING_PROP = {
+const OPENING_TYPES = {
+ JSXOpeningElement: true,
+ JSXOpeningFragment: true,
+};
+
+const OPENING_PROP_BY_TYPE = {
JSXElement: 'openingElement',
JSXFragment: 'openingFragment',
};
-const IS_OPENING = {
- JSXOpeningElement: true,
- JSXOpeningFragment: true,
-};
+function getOpening(element) {
+ const { type } = element;
+ if(OPENING_TYPES[type]) return element;
+
+ const prop = OPENING_PROP_BY_TYPE[type];
+ if(prop) return element[prop];
+
+ throw new TypeError(`Unexpected binding element type "${element.type}"`);
+}
+
export class BindGenerator extends Generator {
static generate(template) {
@@ -35,7 +45,7 @@ export class BindGenerator extends Generator {
JSXTemplate(state) {
const { template } = this;
// Short-circuit templates
- const { isStatic, bindings, node: root } = template;
+ const { isStatic, node: root } = template;
const { isComponent } = root;
if(isStatic || isComponent) {
if(isComponent) this.CreateElement(root, state);
@@ -43,16 +53,10 @@ export class BindGenerator extends Generator {
return;
}
- this.GetTargets(template, state);
-
-
+ this.DomTargets(template, state);
this.Bindings(template, state);
-
-
}
- // process javascript in {...} exprs,
- // supports nested template: recursive processing ftw!
JSXExpressionContainer({ expression }, state) {
this[expression.type](expression, state);
}
@@ -73,59 +77,50 @@ export class BindGenerator extends Generator {
}
state.write(`t${id}`, node);
state.write(`(`);
+ // TODO: this should go in template module
if(isDomFragment) state.write('true');
state.write(`)`);
}
- GetTargets(template, state) {
- const { boundElements, bindings, node } = template;
+ DomTargets(template, state) {
+ const { boundElements, bindings, isBoundRoot } = template;
+ const { length: elLength } = boundElements;
- const hasTargets = !!boundElements.length;
- const params = hasTargets ? `root, targets` : `root`;
- state.write(`function getTargets(${params}) {`);
- state.indentLevel++;
+ const ROOT = 'r';
+ const TARGET = 't';
- // target variables
- for(let i = 0; i < boundElements.length; i++) {
- const boundElement = boundElements[i];
- const opening = boundElement.openingElement || boundElement.openFragment;
- writeNextLine(state);
- state.write(`const target${i} = `);
- state.write(`targets[${i}]`, opening?.name);
- state.write(`;`);
+ state.write(`function getTargets(`);
+ if(isBoundRoot) state.write(ROOT);
+ if(elLength) {
+ const targets = [];
+ for(let i = 0; i < elLength; i++) {
+ targets.push(`${TARGET}${i}`);
+ }
+ state.write(`, [${targets.join(', ')}]`);
}
+ state.write(') {');
- const returnValues = [];
+ state.indentLevel++;
+ writeNextLine(state);
+
+ state.write(`return [`);
for(let i = 0; i < bindings.length; i++) {
const { element, type, index, node } = bindings[i];
- const { queryIndex } = element;
- const varName = queryIndex === -1 ? `root` : `target${queryIndex}`;
+ const { isRoot, queryIndex } = element;
+ const varName = isRoot ? ROOT : `${TARGET}${queryIndex}`;
+
+ if(i !== 0) state.write(', ');
+
if(type !== 'child') {
- returnValues.push(varName);
+ state.write(`${varName}`);
continue;
}
- let opening = null;
- if(IS_OPENING[element.type]) opening = element;
- else {
- const prop = OPENING_PROP[element.type];
- if(prop) opening = element[prop];
- else {
- throw new TypeError(`Unexpected binding node type "${node.type}"`);
- }
- }
-
- const childVar = `child${i}`;
- returnValues.push(childVar);
- writeNextLine(state);
- state.write(`const ${childVar} = `);
+ const opening = getOpening(element, node);
state.write(`${varName}.childNodes`, opening.name);
state.write(`[${index}]`, node);
- state.write(`;`);
}
-
- writeNextLine(state);
- state.write(`return [${returnValues.join(', ')}];`);
+ state.write(`];`);
state.indentLevel--;
writeNextLine(state);
@@ -266,3 +261,4 @@ export class BindGenerator extends Generator {
state.write(`;`);
}
}
+
diff --git a/packages/compiler/transform/Template.js b/packages/compiler/transform/Template.js
index a099a67..9419d6b 100644
--- a/packages/compiler/transform/Template.js
+++ b/packages/compiler/transform/Template.js
@@ -28,7 +28,7 @@ export class Template {
if(node.isComponent && bindings.length) {
throw new Error('Unexpected component binding length');
}
-
+ this.isBoundRoot = node.queryIndex === -1;
this.isDomFragment = node.isJSXFragment;
this.isEmpty = node.isComponent ||
(node.isJSXFragment && node.children.length === 0);
diff --git a/packages/compiler/transform/bind.test.js b/packages/compiler/transform/bind.test.js
index 18f9b15..10c8510 100644
--- a/packages/compiler/transform/bind.test.js
+++ b/packages/compiler/transform/bind.test.js
@@ -12,7 +12,7 @@ describe('Bind Generator', () => {
test('Hello Bind', ({ expect }) => {
// const input = `const t = yo
;`;
const input = `const t =
- Hello hey {"Azoth"}!
+ {"Greeting"} hey {"Azoth"}!
;`;
const initial = preParse(input);
@@ -22,16 +22,15 @@ describe('Bind Generator', () => {
const { code } = BindGenerator.generate(template);
expect(code).toMatchInlineSnapshot(`
- "function getTargets(root, targets) {
- const target0 = targets[0];
- const child1 = target0.childNodes[1];
- return [root, child1];
+ "function getTargets(r, [t0]) {
+ return [r, r.childNodes[1], t0.childNodes[1]];
}
- function apply(p0, p1) {
- const [root, t0, t1] = getTargets();
+ function apply(p0, p1, p2) {
+ const [root, t0, t1, t2] = getTargets();
t0.className = p0;
compose(t1, p1);
+ compose(t2, p2);
}
"
`);
From 200ea0b1da32c5f23365ef04751f31ac98c02581 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Fri, 15 Mar 2024 15:36:11 -0700
Subject: [PATCH 04/23] work out details of inject updating
---
jsconfig.json | 5 ++
packages/compiler/transform/bind.test.js | 22 ++++++
packages/runtime/renderer/magic.test.js | 98 ++++++++++++++++++++++++
packages/runtime/renderer/renderer.js | 8 +-
4 files changed, 132 insertions(+), 1 deletion(-)
create mode 100644 jsconfig.json
create mode 100644 packages/runtime/renderer/magic.test.js
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..e0fd815
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,5 @@
+{
+ "compilerOptions": {
+ "module": "ES6",
+ }
+}
\ No newline at end of file
diff --git a/packages/compiler/transform/bind.test.js b/packages/compiler/transform/bind.test.js
index 10c8510..2ecd3f8 100644
--- a/packages/compiler/transform/bind.test.js
+++ b/packages/compiler/transform/bind.test.js
@@ -34,6 +34,28 @@ describe('Bind Generator', () => {
}
"
`);
+ });
+
+ test('Hello Bind', ({ expect }) => {
+ // const input = `const t = yo
;`;
+ const input = `name => {name}
`;
+ const initial = preParse(input);
+ const template = initial.templates[0];
+ expect(template.node.type).toBe('JSXElement');
+
+ const { code } = BindGenerator.generate(template);
+
+ expect(code).toMatchInlineSnapshot(`
+ "function getTargets(r) {
+ return [r.childNodes[0]];
+ }
+
+ function apply(p0) {
+ const [root, t0] = getTargets();
+ compose(t0, p0);
+ }
+ "
+ `);
});
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/magic.test.js
new file mode 100644
index 0000000..628a1a7
--- /dev/null
+++ b/packages/runtime/renderer/magic.test.js
@@ -0,0 +1,98 @@
+import { test } from 'vitest';
+import { compose } from '../compose/compose.js';
+import { makeRenderer, getBoundElements } from './renderer.js';
+
+// template generated artifacts
+const source = makeRenderer('id', ``);
+
+let injectableRoot = null;
+function injectRender(dom, callback) {
+ injectableRoot = dom;
+ callback();
+ injectableRoot = null;
+}
+
+function getBindTargets(r, boundEls) {
+ return [r.childNodes[0]];
+}
+const makeBind = targets => {
+ const t0 = targets[0];
+ return p0 => {
+ compose(t0, p0);
+ };
+};
+
+const map = new Map();
+
+const makeTemplate = (source) => {
+ let bind = null;
+ let root = injectableRoot;
+ // TODO: test injectable is right template id
+
+ if(root) bind = map.get(root);
+ if(!bind) {
+ const result = root
+ ? [root, getBoundElements(root)]
+ : source();
+ root = result[0];
+ const nodes = getBindTargets(root, result[1]);
+ bind = makeBind(nodes);
+ map.set(root, bind);
+ }
+
+ return [root, bind];
+};
+
+function render123(p0) {
+ const [root, bind] = makeTemplate(source);
+ bind(p0);
+ return root;
+}
+
+class Controller {
+ static for(renderFn) {
+ return new this(renderFn);
+ }
+ constructor(renderFn) {
+ this.renderFn = renderFn;
+ }
+ render(props) {
+ return this.renderFn(props);
+ }
+ update(dom, props) {
+ injectRender(dom, () => this.renderFn(props));
+ }
+}
+
+class Updater extends Controller {
+ #dom = null;
+ render(props) {
+ return this.#dom = super.render(props);
+ }
+ update(props) {
+ super.update(this.#dom, props);
+ }
+}
+
+test('controller creates or injects', ({ expect }) => {
+ const controller = Controller.for(name => render123(name));
+
+ let dom1 = controller.render('felix');
+ let dom2 = controller.render('duchess');
+ expect(dom1.outerHTML).toMatchInlineSnapshot(`"felix
"`);
+ expect(dom2.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
+
+ controller.update(dom1, 'garfield');
+ controller.update(dom2, 'stimpy');
+ expect(dom1.outerHTML).toMatchInlineSnapshot(`"garfield
"`);
+ expect(dom2.outerHTML).toMatchInlineSnapshot(`"stimpy
"`);
+});
+
+test('update remembers dom', ({ expect }) => {
+ const updater = Updater.for(name => render123(name));
+ const dom = updater.render('felix');
+ expect(dom.outerHTML).toMatchInlineSnapshot(`"felix
"`);
+
+ updater.update('duchess');
+ expect(dom.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
+});
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index a2c42c7..6553e07 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -29,13 +29,19 @@ function rendererFactory(id, node, isFragment) {
return render;
}
+const QUERY_SELECTOR = '[data-bind]';
+
function renderer(fragment, isFragment) {
if(!isFragment) fragment = fragment.firstElementChild;
// TODO: malformed fragments...necessary?
return function render() {
const clone = fragment.cloneNode(true);
- const targets = clone.querySelectorAll('[data-bind]');
+ const targets = clone.querySelectorAll(QUERY_SELECTOR);
return [clone, targets];
};
+}
+
+export function getBoundElements(dom) {
+ return dom.querySelectorAll(QUERY_SELECTOR);
}
\ No newline at end of file
From bbf8ab69753c8b6098973f0a5daec13ad769f252 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Fri, 15 Mar 2024 19:43:56 -0700
Subject: [PATCH 05/23] new render
---
jsconfig.json | 1 +
.../{BlindGenerator.js => BindGenerator.js} | 83 +++++++++++--------
packages/compiler/transform/bind.test.js | 59 +++++++------
packages/runtime/renderer/magic.test.js | 29 ++-----
packages/runtime/renderer/renderer.js | 24 +++++-
5 files changed, 112 insertions(+), 84 deletions(-)
rename packages/compiler/transform/{BlindGenerator.js => BindGenerator.js} (79%)
diff --git a/jsconfig.json b/jsconfig.json
index e0fd815..98862fb 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"module": "ES6",
+ "strict": true
}
}
\ No newline at end of file
diff --git a/packages/compiler/transform/BlindGenerator.js b/packages/compiler/transform/BindGenerator.js
similarity index 79%
rename from packages/compiler/transform/BlindGenerator.js
rename to packages/compiler/transform/BindGenerator.js
index 37fa480..f2975cf 100644
--- a/packages/compiler/transform/BlindGenerator.js
+++ b/packages/compiler/transform/BindGenerator.js
@@ -22,6 +22,10 @@ function getOpening(element) {
throw new TypeError(`Unexpected binding element type "${element.type}"`);
}
+const ROOT = 'r';
+const TARGETS = 'ts';
+const TARGET = 't';
+const VALUE = 'v';
export class BindGenerator extends Generator {
static generate(template) {
@@ -53,8 +57,9 @@ export class BindGenerator extends Generator {
return;
}
- this.DomTargets(template, state);
+ this.Targets(template, state);
this.Bindings(template, state);
+ this.Render(template, state);
}
JSXExpressionContainer({ expression }, state) {
@@ -82,22 +87,13 @@ export class BindGenerator extends Generator {
state.write(`)`);
}
- DomTargets(template, state) {
+ Targets(template, state) {
const { boundElements, bindings, isBoundRoot } = template;
const { length: elLength } = boundElements;
- const ROOT = 'r';
- const TARGET = 't';
-
- state.write(`function getTargets(`);
- if(isBoundRoot) state.write(ROOT);
- if(elLength) {
- const targets = [];
- for(let i = 0; i < elLength; i++) {
- targets.push(`${TARGET}${i}`);
- }
- state.write(`, [${targets.join(', ')}]`);
- }
+ state.write(`function targets(`);
+ state.write(ROOT);
+ if(elLength) state.write(`, ${TARGETS}`);
state.write(') {');
state.indentLevel++;
@@ -107,7 +103,7 @@ export class BindGenerator extends Generator {
for(let i = 0; i < bindings.length; i++) {
const { element, type, index, node } = bindings[i];
const { isRoot, queryIndex } = element;
- const varName = isRoot ? ROOT : `${TARGET}${queryIndex}`;
+ const varName = isRoot ? ROOT : `${TARGETS}[${queryIndex}]`;
if(i !== 0) state.write(', ');
@@ -126,35 +122,51 @@ export class BindGenerator extends Generator {
writeNextLine(state);
state.write(`}`);
state.write(state.lineEnd);
- writeNextLine(state);
}
- Bindings(template, state) {
- const { boundElements, bindings, node } = template;
-
+ Render(template, state) {
+ const { bindings } = template;
const params = [];
for(let i = 0; i < bindings.length; i++) {
- params.push(`p${i}`);
+ params.push(`${VALUE}${i}`);
}
- state.write(`function apply(${params.join(', ')}) {`);
+
+ state.write(`function render(${params.join(', ')}) {`);
state.indentLevel++;
writeNextLine(state);
- const returnStatement = template.node.returnStatement;
+ state.write(`const [root, bind] = makeTemplate(source);`);
+ writeNextLine(state);
+ state.write(`bind(${params.join(', ')});`);
+ writeNextLine(state);
+ state.write(`return root;`);
+
+ state.indentLevel--;
+ writeNextLine(state);
+ state.write(`}`);
+ state.write(state.lineEnd);
+ }
+ Bindings(template, state) {
+ const { bindings } = template;
- // template service renderer call
- const hasTargets = !!boundElements.length;
+ state.write(`function bind(${TARGETS}) {`);
+ state.indentLevel++;
+ writeNextLine(state);
- const vars = ['root'];
+ const targets = [];
+ const params = [];
for(let i = 0; i < bindings.length; i++) {
- vars.push(`t${i}`);
+ targets.push(`${TARGET}${i} = ${TARGETS}[${i}]`);
+ params.push(`${VALUE}${i}`);
}
- state.write(`const [${vars.join(', ')}] = getTargets();`);
+ state.write(`const ${targets.join(', ')};`);
+ writeNextLine(state);
+ state.write(`return (${params.join(', ')}) => {`);
+ state.indentLevel++;
- // bindings
for(let i = 0; i < bindings.length; i++) {
const { element, type, node, expr } = bindings[i];
writeNextLine(state);
@@ -180,23 +192,27 @@ export class BindGenerator extends Generator {
throw new Error(message);
}
+ state.indentLevel--;
+ writeNextLine(state);
+ state.write(`};`);
state.indentLevel--;
writeNextLine(state);
state.write(`}`);
+
state.write(state.lineEnd);
}
Compose(node, expr, index, state) {
state.write(`compose(`, node);
- state.write(`t${index}, `, node);
- state.write(`p${index}`, expr);
+ state.write(`${TARGET}${index}, `, node);
+ state.write(`${VALUE}${index}`, expr);
// this[expr.type](expr, state);
state.write(`);`);
}
ComposeElement(node, expr, index, state) {
state.write(`composeElement(`, node);
- state.write(`t${index}, `);
+ state.write(`${TARGET}${index}, `);
this.CompleteElement(node, expr, state);
state.write(`);`);
}
@@ -237,9 +253,8 @@ export class BindGenerator extends Generator {
BindingProp(node, expr, index, element, state) {
const { queryIndex, openingElement, openingFragment } = element;
- const varName = queryIndex === -1 ? `root` : `target${queryIndex}`;
const opening = openingElement ?? openingFragment;
- state.write(`t${index}`, opening.name);
+ state.write(`${TARGET}${index}`, opening.name);
// TODO: more property validation
const identity = node.name;
const propName = identity.name;
@@ -257,7 +272,7 @@ export class BindGenerator extends Generator {
/* expression */
state.write(` = `);
// this[expr.type](expr, state);
- state.write(`p${index}`, expr);
+ state.write(`${VALUE}${index}`, expr);
state.write(`;`);
}
}
diff --git a/packages/compiler/transform/bind.test.js b/packages/compiler/transform/bind.test.js
index 2ecd3f8..fb925da 100644
--- a/packages/compiler/transform/bind.test.js
+++ b/packages/compiler/transform/bind.test.js
@@ -1,5 +1,5 @@
/* eslint-disable no-undef */
-import { BindGenerator } from './BlindGenerator.js';
+import { BindGenerator } from './BindGenerator.js';
import { parse, generate as _generate } from '../compiler.js';
import { describe, test } from 'vitest';
@@ -9,12 +9,9 @@ function preParse(input) {
}
describe('Bind Generator', () => {
- test('Hello Bind', ({ expect }) => {
+ test('Hello bind', ({ expect }) => {
// const input = `const t = yo
;`;
- const input = `const t =
- {"Greeting"} hey {"Azoth"}!
-
;`;
-
+ const input = `name => {name}
`;
const initial = preParse(input);
const template = initial.templates[0];
expect(template.node.type).toBe('JSXElement');
@@ -22,23 +19,30 @@ describe('Bind Generator', () => {
const { code } = BindGenerator.generate(template);
expect(code).toMatchInlineSnapshot(`
- "function getTargets(r, [t0]) {
- return [r, r.childNodes[1], t0.childNodes[1]];
+ "function targets(r) {
+ return [r.childNodes[0]];
}
-
- function apply(p0, p1, p2) {
- const [root, t0, t1, t2] = getTargets();
- t0.className = p0;
- compose(t1, p1);
- compose(t2, p2);
+ function bind(ts) {
+ const t0 = ts[0];
+ return (v0) => {
+ compose(t0, v0);
+ };
+ }
+ function render(v0) {
+ const [root, bind] = makeTemplate(source);
+ bind(v0);
+ return root;
}
"
`);
});
- test('Hello Bind', ({ expect }) => {
+ test('Bind children and props', ({ expect }) => {
// const input = `const t = yo
;`;
- const input = `name => {name}
`;
+ const input = `const t =
+ {"Greeting"} hey {"Azoth"}!
+
;`;
+
const initial = preParse(input);
const template = initial.templates[0];
expect(template.node.type).toBe('JSXElement');
@@ -46,18 +50,23 @@ describe('Bind Generator', () => {
const { code } = BindGenerator.generate(template);
expect(code).toMatchInlineSnapshot(`
- "function getTargets(r) {
- return [r.childNodes[0]];
+ "function targets(r, ts) {
+ return [r, r.childNodes[1], ts[0].childNodes[1]];
}
-
- function apply(p0) {
- const [root, t0] = getTargets();
- compose(t0, p0);
+ function bind(ts) {
+ const t0 = ts[0], t1 = ts[1], t2 = ts[2];
+ return (v0, v1, v2) => {
+ t0.className = v0;
+ compose(t1, v1);
+ compose(t2, v2);
+ };
+ }
+ function render(v0, v1, v2) {
+ const [root, bind] = makeTemplate(source);
+ bind(v0, v1, v2);
+ return root;
}
"
`);
-
-
});
-
});
\ No newline at end of file
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/magic.test.js
index 628a1a7..e85d6ee 100644
--- a/packages/runtime/renderer/magic.test.js
+++ b/packages/runtime/renderer/magic.test.js
@@ -1,6 +1,6 @@
import { test } from 'vitest';
import { compose } from '../compose/compose.js';
-import { makeRenderer, getBoundElements } from './renderer.js';
+import { makeRenderer, getBoundElements, makeTemplate } from './renderer.js';
// template generated artifacts
const source = makeRenderer('id', ``);
@@ -12,9 +12,10 @@ function injectRender(dom, callback) {
injectableRoot = null;
}
-function getBindTargets(r, boundEls) {
+function getTargets(r, boundEls) {
return [r.childNodes[0]];
}
+
const makeBind = targets => {
const t0 = targets[0];
return p0 => {
@@ -22,33 +23,13 @@ const makeBind = targets => {
};
};
-const map = new Map();
-
-const makeTemplate = (source) => {
- let bind = null;
- let root = injectableRoot;
- // TODO: test injectable is right template id
-
- if(root) bind = map.get(root);
- if(!bind) {
- const result = root
- ? [root, getBoundElements(root)]
- : source();
- root = result[0];
- const nodes = getBindTargets(root, result[1]);
- bind = makeBind(nodes);
- map.set(root, bind);
- }
-
- return [root, bind];
-};
-
function render123(p0) {
- const [root, bind] = makeTemplate(source);
+ const [root, bind] = makeTemplate(source, getTargets, makeBind);
bind(p0);
return root;
}
+
class Controller {
static for(renderFn) {
return new this(renderFn);
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 6553e07..ac7d7ea 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -44,4 +44,26 @@ function renderer(fragment, isFragment) {
export function getBoundElements(dom) {
return dom.querySelectorAll(QUERY_SELECTOR);
-}
\ No newline at end of file
+}
+
+
+const map = new Map();
+
+export function makeTemplate(source, targets, makeBind) {
+ let bind = null;
+ let root = injectableRoot;
+ // TODO: test injectable is right template id
+
+ if(root) bind = map.get(root);
+ if(!bind) {
+ const result = root
+ ? [root, getBoundElements(root)]
+ : source();
+ root = result[0];
+ const nodes = targets(root, result[1]);
+ bind = makeBind(nodes);
+ map.set(root, bind);
+ }
+
+ return [root, bind];
+};
\ No newline at end of file
From 04595e885dc6ccc0dfae0d38af0b8dd96e5628a2 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Fri, 15 Mar 2024 19:44:46 -0700
Subject: [PATCH 06/23] oops, broken test
---
packages/runtime/renderer/magic.test.js | 27 +++++++++++++++++++++----
packages/runtime/renderer/renderer.js | 4 ++--
2 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/magic.test.js
index e85d6ee..2c8c7f7 100644
--- a/packages/runtime/renderer/magic.test.js
+++ b/packages/runtime/renderer/magic.test.js
@@ -1,6 +1,6 @@
import { test } from 'vitest';
import { compose } from '../compose/compose.js';
-import { makeRenderer, getBoundElements, makeTemplate } from './renderer.js';
+import { makeRenderer, getBoundElements } from './renderer.js';
// template generated artifacts
const source = makeRenderer('id', ``);
@@ -12,10 +12,9 @@ function injectRender(dom, callback) {
injectableRoot = null;
}
-function getTargets(r, boundEls) {
+function getBindTargets(r, boundEls) {
return [r.childNodes[0]];
}
-
const makeBind = targets => {
const t0 = targets[0];
return p0 => {
@@ -24,11 +23,31 @@ const makeBind = targets => {
};
function render123(p0) {
- const [root, bind] = makeTemplate(source, getTargets, makeBind);
+ const [root, bind] = makeTemplate(source);
bind(p0);
return root;
}
+const map = new Map();
+
+const makeTemplate = (source) => {
+ let bind = null;
+ let root = injectableRoot;
+ // TODO: test injectable is right template id
+
+ if(root) bind = map.get(root);
+ if(!bind) {
+ const result = root
+ ? [root, getBoundElements(root)]
+ : source();
+ root = result[0];
+ const nodes = getBindTargets(root, result[1]);
+ bind = makeBind(nodes);
+ map.set(root, bind);
+ }
+
+ return [root, bind];
+};
class Controller {
static for(renderFn) {
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index ac7d7ea..5aa61bd 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -47,8 +47,8 @@ export function getBoundElements(dom) {
}
+/*
const map = new Map();
-
export function makeTemplate(source, targets, makeBind) {
let bind = null;
let root = injectableRoot;
@@ -66,4 +66,4 @@ export function makeTemplate(source, targets, makeBind) {
}
return [root, bind];
-};
\ No newline at end of file
+};*/
\ No newline at end of file
From a63005b780f25227cf000602fa7c0aa4897e18c2 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Fri, 15 Mar 2024 22:16:22 -0700
Subject: [PATCH 07/23] basic string child node render and update!
---
packages/runtime/renderer/magic.test.js | 164 ++++++++++++++----------
packages/runtime/renderer/renderer.js | 74 +++++++++--
2 files changed, 158 insertions(+), 80 deletions(-)
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/magic.test.js
index 2c8c7f7..3044438 100644
--- a/packages/runtime/renderer/magic.test.js
+++ b/packages/runtime/renderer/magic.test.js
@@ -1,20 +1,15 @@
-import { test } from 'vitest';
+import { describe, test } from 'vitest';
import { compose } from '../compose/compose.js';
-import { makeRenderer, getBoundElements } from './renderer.js';
+import { makeRenderer, makeTemplate, Controller, Updater, makeStringRenderer } from './renderer.js';
// template generated artifacts
const source = makeRenderer('id', ``);
+const stringSource = makeStringRenderer('id', [``, `
`]);
-let injectableRoot = null;
-function injectRender(dom, callback) {
- injectableRoot = dom;
- callback();
- injectableRoot = null;
-}
-
-function getBindTargets(r, boundEls) {
+function getTargets(r, boundEls) {
return [r.childNodes[0]];
}
+
const makeBind = targets => {
const t0 = targets[0];
return p0 => {
@@ -22,77 +17,106 @@ const makeBind = targets => {
};
};
+function getStringTargets(r, boundEls) {
+ return [boundEls[0]];
+}
+
+const makeStringBind = targets => {
+ const t0 = targets[0];
+ return p0 => {
+ t0[0] = p0;
+ };
+};
function render123(p0) {
- const [root, bind] = makeTemplate(source);
+ const [root, bind] = makeTemplate(
+ source,
+ getTargets,
+ makeBind
+ );
bind(p0);
return root;
}
+function renderString(p0) {
+ const [root, bind] = makeTemplate(
+ stringSource,
+ getStringTargets,
+ makeStringBind
+ );
+ bind(p0);
+ return root;
+}
+/*
+const NameTag = Controller.for(({ greeting, name }) => {
+ const Greeting = Controller.for(greeting => {greeting});
-const map = new Map();
-
-const makeTemplate = (source) => {
- let bind = null;
- let root = injectableRoot;
- // TODO: test injectable is right template id
-
- if(root) bind = map.get(root);
- if(!bind) {
- const result = root
- ? [root, getBoundElements(root)]
- : source();
- root = result[0];
- const nodes = getBindTargets(root, result[1]);
- bind = makeBind(nodes);
- map.set(root, bind);
- }
-
- return [root, bind];
-};
+ return
+ {name}
+
;
+});
-class Controller {
- static for(renderFn) {
- return new this(renderFn);
- }
- constructor(renderFn) {
- this.renderFn = renderFn;
- }
- render(props) {
- return this.renderFn(props);
- }
- update(dom, props) {
- injectRender(dom, () => this.renderFn(props));
- }
-}
+const Hello = Controller.for(name => {name}
);
+*/
+describe('string render', () => {
+ const flatRender = node => node.flat().join('');
+ test('Controller.for', ({ expect }) => {
+ const controller = Controller.for(name => renderString(name));
-class Updater extends Controller {
- #dom = null;
- render(props) {
- return this.#dom = super.render(props);
- }
- update(props) {
- super.update(this.#dom, props);
- }
-}
+ let node1 = controller.render('felix');
+ let node2 = controller.render('duchess');
+ expect(flatRender(node1)).toMatchInlineSnapshot(
+ `"felix
"`
+ );
+ expect(flatRender(node2)).toMatchInlineSnapshot(
+ `"duchess
"`
+ );
-test('controller creates or injects', ({ expect }) => {
- const controller = Controller.for(name => render123(name));
+ controller.update(node1, 'garfield');
+ controller.update(node2, 'stimpy');
+ expect(flatRender(node1)).toMatchInlineSnapshot(
+ `"garfield
"`
+ );
+ expect(flatRender(node2)).toMatchInlineSnapshot(
+ `"stimpy
"`
+ );
+ });
- let dom1 = controller.render('felix');
- let dom2 = controller.render('duchess');
- expect(dom1.outerHTML).toMatchInlineSnapshot(`"felix
"`);
- expect(dom2.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
+ test('Updater.for', ({ expect }) => {
+ const updater = Updater.for(name => renderString(name));
+ const node = updater.render('felix');
+ expect(flatRender(node)).toMatchInlineSnapshot(
+ `"felix
"`
+ );
+
+ updater.update('duchess');
+ expect(flatRender(node)).toMatchInlineSnapshot(
+ `"duchess
"`
+ );
+ });
- controller.update(dom1, 'garfield');
- controller.update(dom2, 'stimpy');
- expect(dom1.outerHTML).toMatchInlineSnapshot(`"garfield
"`);
- expect(dom2.outerHTML).toMatchInlineSnapshot(`"stimpy
"`);
});
+describe('dom render', () => {
+
+ test('Controller.for', ({ expect }) => {
+ const controller = Controller.for(name => render123(name));
+
+ let node1 = controller.render('felix');
+ let node2 = controller.render('duchess');
+ expect(node1.outerHTML).toMatchInlineSnapshot(`"felix
"`);
+ expect(node2.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
+
+ controller.update(node1, 'garfield');
+ controller.update(node2, 'stimpy');
+ expect(node1.outerHTML).toMatchInlineSnapshot(`"garfield
"`);
+ expect(node2.outerHTML).toMatchInlineSnapshot(`"stimpy
"`);
+ });
+
+ test('Updater.for', ({ expect }) => {
+ const updater = Updater.for(name => render123(name));
+ const node = updater.render('felix');
+ expect(node.outerHTML).toMatchInlineSnapshot(`"felix
"`);
-test('update remembers dom', ({ expect }) => {
- const updater = Updater.for(name => render123(name));
- const dom = updater.render('felix');
- expect(dom.outerHTML).toMatchInlineSnapshot(`"felix
"`);
+ updater.update('duchess');
+ expect(node.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
+ });
- updater.update('duchess');
- expect(dom.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
});
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 5aa61bd..717575a 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -4,6 +4,21 @@ export function clearTemplates() {
templates.clear();
}
+export function makeStringRenderer(id, html, isFragment = false) {
+ return () => {
+ const root = [];
+ const targets = [];
+ for(let i = 0; i < html.length; i++) {
+ root.push(html[i]);
+ if(i === html.length - 1) break;
+ const target = [];
+ targets.push(target);
+ root.push(target);
+ }
+ return [root, targets];
+ };
+}
+
export function makeRenderer(id, html, isFragment = false) {
if(templates.has(id)) return templates.get(id);
@@ -47,23 +62,62 @@ export function getBoundElements(dom) {
}
-/*
+
const map = new Map();
+
+
+export const injectable = [];
+export function inject(node, callback) {
+ injectable.push(node);
+ callback();
+ const popped = injectable.pop();
+ if(popped !== node) {
+ // TODO: display html like object for compose
+ throw new Error('Injectable stack error');
+ }
+}
+
+
export function makeTemplate(source, targets, makeBind) {
let bind = null;
- let root = injectableRoot;
+ let node = injectable.at(-1); // peek!
// TODO: test injectable is right template id
- if(root) bind = map.get(root);
+ if(node) bind = map.get(node);
if(!bind) {
- const result = root
- ? [root, getBoundElements(root)]
+ const result = node
+ ? [node, getBoundElements(node)]
: source();
- root = result[0];
- const nodes = targets(root, result[1]);
+ node = result[0];
+ const nodes = targets(node, result[1]);
bind = makeBind(nodes);
- map.set(root, bind);
+ map.set(node, bind);
}
- return [root, bind];
-};*/
\ No newline at end of file
+ return [node, bind];
+}
+
+export class Controller {
+ static for(renderFn) {
+ return new this(renderFn);
+ }
+ constructor(renderFn) {
+ this.renderFn = renderFn;
+ }
+ render(props) {
+ return this.renderFn(props);
+ }
+ update(node, props) {
+ inject(node, () => this.renderFn(props));
+ }
+}
+
+export class Updater extends Controller {
+ #dom = null;
+ render(props) {
+ return this.#dom = super.render(props);
+ }
+ update(props) {
+ super.update(this.#dom, props);
+ }
+}
\ No newline at end of file
From fb3a452730b7ba9a21bd5bb1c62738ce36254e05 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sat, 16 Mar 2024 07:44:04 -0700
Subject: [PATCH 08/23] start refactoring dom/string renderers
---
packages/compiler/transform/Analyzer.js | 7 +-
packages/compiler/transform/Stack.js | 19 -----
packages/runtime/package.json | 2 +-
packages/runtime/renderer/index.js | 1 +
packages/runtime/renderer/magic.test.js | 22 ++++--
packages/runtime/renderer/renderer.dom.js | 47 +++++++++++++
packages/runtime/renderer/renderer.js | 84 +++++------------------
7 files changed, 86 insertions(+), 96 deletions(-)
delete mode 100644 packages/compiler/transform/Stack.js
create mode 100644 packages/runtime/renderer/index.js
create mode 100644 packages/runtime/renderer/renderer.dom.js
diff --git a/packages/compiler/transform/Analyzer.js b/packages/compiler/transform/Analyzer.js
index 8792f25..0a40832 100644
--- a/packages/compiler/transform/Analyzer.js
+++ b/packages/compiler/transform/Analyzer.js
@@ -1,4 +1,3 @@
-import { Stack } from './Stack.js';
import { Template } from './Template.js';
import { voidElements } from './html.js';
@@ -14,7 +13,7 @@ const BINDING_ATTR = {
const byOrder = (a, b) => a.order - b.order;
export class Analyzer {
- #elements = new Stack();
+ #elements = []; // stack
#documentOrder = 0;
#boundElements = new Set();
#bindings = [];
@@ -91,7 +90,7 @@ export class Analyzer {
}
#bind(type, node, expr, index) {
- const element = this.#elements.current;
+ const element = this.#elements.at(-1); // peek
element.isRoot = element === this.#root;
const binding = {
@@ -261,5 +260,3 @@ function trimChildren(node) {
node.children = trimmed;
}
}
-
-
diff --git a/packages/compiler/transform/Stack.js b/packages/compiler/transform/Stack.js
deleted file mode 100644
index e4bdbba..0000000
--- a/packages/compiler/transform/Stack.js
+++ /dev/null
@@ -1,19 +0,0 @@
-export class Stack {
- #current = null;
- #stack = [];
-
- get current() {
- return this.#current;
- }
-
- push(context) {
- this.#current = context;
- this.#stack.push(context);
- }
-
- pop() {
- const context = this.#stack.pop();
- this.#current = this.#stack.at(-1);
- return context;
- }
-}
diff --git a/packages/runtime/package.json b/packages/runtime/package.json
index 8f3ecd1..a13632e 100644
--- a/packages/runtime/package.json
+++ b/packages/runtime/package.json
@@ -27,7 +27,7 @@
],
"exports": {
"./compose": "./compose/compose.js",
- "./renderer": "./renderer/renderer.js"
+ "./renderer": "./renderer/index.js"
},
"devDependencies": {
"test-utils": "workspace:^"
diff --git a/packages/runtime/renderer/index.js b/packages/runtime/renderer/index.js
new file mode 100644
index 0000000..0b844f3
--- /dev/null
+++ b/packages/runtime/renderer/index.js
@@ -0,0 +1 @@
+export { makeRenderer, rendererById } from './renderer.dom.js';
\ No newline at end of file
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/magic.test.js
index 3044438..1160ba2 100644
--- a/packages/runtime/renderer/magic.test.js
+++ b/packages/runtime/renderer/magic.test.js
@@ -1,6 +1,15 @@
import { describe, test } from 'vitest';
import { compose } from '../compose/compose.js';
-import { makeRenderer, makeTemplate, Controller, Updater, makeStringRenderer } from './renderer.js';
+import {
+ makeRenderer,
+ getBoundElements,
+} from './renderer.dom.js';
+import {
+ makeTemplate,
+ makeStringRenderer,
+ Controller,
+ Updater,
+} from './renderer.js';
// template generated artifacts
const source = makeRenderer('id', ``);
@@ -27,11 +36,12 @@ const makeStringBind = targets => {
t0[0] = p0;
};
};
-function render123(p0) {
+function renderDOM(p0) {
const [root, bind] = makeTemplate(
source,
getTargets,
- makeBind
+ makeBind,
+ getBoundElements,
);
bind(p0);
return root;
@@ -58,7 +68,7 @@ const Hello = Controller.for(name => {name}
);
*/
describe('string render', () => {
const flatRender = node => node.flat().join('');
- test('Controller.for', ({ expect }) => {
+ test.only('Controller.for', ({ expect }) => {
const controller = Controller.for(name => renderString(name));
let node1 = controller.render('felix');
@@ -97,7 +107,7 @@ describe('string render', () => {
describe('dom render', () => {
test('Controller.for', ({ expect }) => {
- const controller = Controller.for(name => render123(name));
+ const controller = Controller.for(name => renderDOM(name));
let node1 = controller.render('felix');
let node2 = controller.render('duchess');
@@ -111,7 +121,7 @@ describe('dom render', () => {
});
test('Updater.for', ({ expect }) => {
- const updater = Updater.for(name => render123(name));
+ const updater = Updater.for(name => renderDOM(name));
const node = updater.render('felix');
expect(node.outerHTML).toMatchInlineSnapshot(`"felix
"`);
diff --git a/packages/runtime/renderer/renderer.dom.js b/packages/runtime/renderer/renderer.dom.js
new file mode 100644
index 0000000..3b6fec1
--- /dev/null
+++ b/packages/runtime/renderer/renderer.dom.js
@@ -0,0 +1,47 @@
+const templates = new Map();
+
+export function clearTemplates() {
+ templates.clear();
+}
+
+export function makeRenderer(id, html, isFragment = false) {
+ if(templates.has(id)) return templates.get(id);
+
+ const template = document.createElement('template');
+ template.innerHTML = html;
+ return rendererFactory(id, template.content, isFragment);
+}
+
+export function rendererById(id, isFragment = false) {
+ if(templates.has(id)) return templates.get(id);
+
+ const templateEl = document.getElementById(id);
+ if(!templateEl) {
+ throw new Error(`No template with id "${id}"`);
+ }
+
+ return rendererFactory(id, templateEl.content, isFragment);
+}
+
+function rendererFactory(id, node, isFragment) {
+ const render = renderer(node, isFragment);
+ templates.set(id, render);
+ return render;
+}
+
+const QUERY_SELECTOR = '[data-bind]';
+
+function renderer(fragment, isFragment) {
+ if(!isFragment) fragment = fragment.firstElementChild;
+ // TODO: malformed fragments...necessary?
+
+ return function render() {
+ const clone = fragment.cloneNode(true);
+ const targets = clone.querySelectorAll(QUERY_SELECTOR);
+ return [clone, targets];
+ };
+}
+
+export function getBoundElements(dom) {
+ return dom.querySelectorAll(QUERY_SELECTOR);
+}
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 717575a..b6c8d40 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -1,8 +1,3 @@
-const templates = new Map();
-
-export function clearTemplates() {
- templates.clear();
-}
export function makeStringRenderer(id, html, isFragment = false) {
return () => {
@@ -19,54 +14,8 @@ export function makeStringRenderer(id, html, isFragment = false) {
};
}
-export function makeRenderer(id, html, isFragment = false) {
- if(templates.has(id)) return templates.get(id);
-
- const template = document.createElement('template');
- template.innerHTML = html;
- return rendererFactory(id, template.content, isFragment);
-}
-
-export function rendererById(id, isFragment = false) {
- if(templates.has(id)) return templates.get(id);
-
- const templateEl = document.getElementById(id);
- if(!templateEl) {
- throw new Error(`No template with id "${id}"`);
- }
-
- return rendererFactory(id, templateEl.content, isFragment);
-}
-
-function rendererFactory(id, node, isFragment) {
- const render = renderer(node, isFragment);
- templates.set(id, render);
- return render;
-}
-
-const QUERY_SELECTOR = '[data-bind]';
-
-function renderer(fragment, isFragment) {
- if(!isFragment) fragment = fragment.firstElementChild;
- // TODO: malformed fragments...necessary?
-
- return function render() {
- const clone = fragment.cloneNode(true);
- const targets = clone.querySelectorAll(QUERY_SELECTOR);
- return [clone, targets];
- };
-}
-
-export function getBoundElements(dom) {
- return dom.querySelectorAll(QUERY_SELECTOR);
-}
-
-
-
-const map = new Map();
-
-
-export const injectable = [];
+// stack
+const injectable = [];
export function inject(node, callback) {
injectable.push(node);
callback();
@@ -77,23 +26,28 @@ export function inject(node, callback) {
}
}
+const map = new Map();
-export function makeTemplate(source, targets, makeBind) {
+export function makeTemplate(source, targets, makeBind, getBound) {
let bind = null;
+ let boundEls = null;
let node = injectable.at(-1); // peek!
+
// TODO: test injectable is right template id
if(node) bind = map.get(node);
- if(!bind) {
- const result = node
- ? [node, getBoundElements(node)]
- : source();
- node = result[0];
- const nodes = targets(node, result[1]);
- bind = makeBind(nodes);
- map.set(node, bind);
+ if(bind) return [node, bind];
+
+ if(node) boundEls = getBound(node);
+ else {
+ // (destructured re-assignment)
+ ([node, boundEls] = source());
}
+ const nodes = targets(node, boundEls);
+ bind = makeBind(nodes);
+
+ map.set(node, bind);
return [node, bind];
}
@@ -113,11 +67,11 @@ export class Controller {
}
export class Updater extends Controller {
- #dom = null;
+ #node = null;
render(props) {
- return this.#dom = super.render(props);
+ return this.#node = super.render(props);
}
update(props) {
- super.update(this.#dom, props);
+ super.update(this.#node, props);
}
}
\ No newline at end of file
From d1e59e82a222f302511b53a865d84f6f2c4c0361 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sat, 16 Mar 2024 07:44:10 -0700
Subject: [PATCH 09/23] deps update
---
docs/pnpm-lock.yaml | 22 ++++++-------
package.json | 4 +--
pnpm-lock.yaml | 70 ++++++++++++++++++++--------------------
vite-test/package.json | 2 +-
vite-test/pnpm-lock.yaml | 60 +++++++++++++++++-----------------
5 files changed, 79 insertions(+), 79 deletions(-)
diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml
index 9b8e3cc..559ee12 100644
--- a/docs/pnpm-lock.yaml
+++ b/docs/pnpm-lock.yaml
@@ -530,14 +530,14 @@ packages:
dev: true
optional: true
- /@shikijs/core@1.1.7:
- resolution: {integrity: sha512-gTYLUIuD1UbZp/11qozD3fWpUTuMqPSf3svDMMrL0UmlGU7D9dPw/V1FonwAorCUJBltaaESxq90jrSjQyGixg==}
+ /@shikijs/core@1.2.0:
+ resolution: {integrity: sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==}
dev: true
- /@shikijs/transformers@1.1.7:
- resolution: {integrity: sha512-lXz011ao4+rvweps/9h3CchBfzb1U5OtP5D51Tqc9lQYdLblWMIxQxH6Ybe1GeGINcEVM4goMyPrI0JvlIp4UQ==}
+ /@shikijs/transformers@1.2.0:
+ resolution: {integrity: sha512-xKn7DtA65DQV4FOfYsrvqM80xOy2xuXnxWWKsZmHv1VII/IOuDUDsWDu3KnpeLH6wqNJWp1GRoNUsHR1aw/VhQ==}
dependencies:
- shiki: 1.1.7
+ shiki: 1.2.0
dev: true
/@types/estree@1.0.5:
@@ -907,10 +907,10 @@ packages:
resolution: {integrity: sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==}
dev: true
- /shiki@1.1.7:
- resolution: {integrity: sha512-9kUTMjZtcPH3i7vHunA6EraTPpPOITYTdA5uMrvsJRexktqP0s7P3s9HVK80b4pP42FRVe03D7fT3NmJv2yYhw==}
+ /shiki@1.2.0:
+ resolution: {integrity: sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==}
dependencies:
- '@shikijs/core': 1.1.7
+ '@shikijs/core': 1.2.0
dev: true
/source-map-js@1.0.2:
@@ -981,8 +981,8 @@ packages:
dependencies:
'@docsearch/css': 3.6.0
'@docsearch/js': 3.6.0(@algolia/client-search@4.22.1)(search-insights@2.13.0)
- '@shikijs/core': 1.1.7
- '@shikijs/transformers': 1.1.7
+ '@shikijs/core': 1.2.0
+ '@shikijs/transformers': 1.2.0
'@types/markdown-it': 13.0.7
'@vitejs/plugin-vue': 5.0.4(vite@5.1.6)(vue@3.4.21)
'@vue/devtools-api': 7.0.17(vue@3.4.21)
@@ -991,7 +991,7 @@ packages:
focus-trap: 7.5.4
mark.js: 8.11.1
minisearch: 6.3.0
- shiki: 1.1.7
+ shiki: 1.2.0
vite: 5.1.6
vue: 3.4.21
transitivePeerDependencies:
diff --git a/package.json b/package.json
index 7c1af8a..129f407 100644
--- a/package.json
+++ b/package.json
@@ -22,10 +22,10 @@
"azoth": "workspace:./packages/azoth",
"eslint": "^8.57.0",
"globals": "^14.0.0",
- "happy-dom": "^13.8.2",
+ "happy-dom": "^13.8.6",
"vite": "^5.1.6",
"vite-plugin-inspect": "^0.8.3",
- "vitest": "^1.3.1"
+ "vitest": "^1.4.0"
},
"packageManager": "^pnpm@8.15.1"
}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f2e6877..5c56a61 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -18,8 +18,8 @@ devDependencies:
specifier: ^14.0.0
version: 14.0.0
happy-dom:
- specifier: ^13.8.2
- version: 13.8.2
+ specifier: ^13.8.6
+ version: 13.8.6
vite:
specifier: ^5.1.6
version: 5.1.6
@@ -27,8 +27,8 @@ devDependencies:
specifier: ^0.8.3
version: 0.8.3(vite@5.1.6)
vitest:
- specifier: ^1.3.1
- version: 1.3.1(happy-dom@13.8.2)
+ specifier: ^1.4.0
+ version: 1.4.0(happy-dom@13.8.6)
packages:
@@ -518,38 +518,38 @@ packages:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
dev: true
- /@vitest/expect@1.3.1:
- resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
+ /@vitest/expect@1.4.0:
+ resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==}
dependencies:
- '@vitest/spy': 1.3.1
- '@vitest/utils': 1.3.1
+ '@vitest/spy': 1.4.0
+ '@vitest/utils': 1.4.0
chai: 4.4.1
dev: true
- /@vitest/runner@1.3.1:
- resolution: {integrity: sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==}
+ /@vitest/runner@1.4.0:
+ resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==}
dependencies:
- '@vitest/utils': 1.3.1
+ '@vitest/utils': 1.4.0
p-limit: 5.0.0
pathe: 1.1.2
dev: true
- /@vitest/snapshot@1.3.1:
- resolution: {integrity: sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==}
+ /@vitest/snapshot@1.4.0:
+ resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==}
dependencies:
magic-string: 0.30.8
pathe: 1.1.2
pretty-format: 29.7.0
dev: true
- /@vitest/spy@1.3.1:
- resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
+ /@vitest/spy@1.4.0:
+ resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==}
dependencies:
tinyspy: 2.2.1
dev: true
- /@vitest/utils@1.3.1:
- resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
+ /@vitest/utils@1.4.0:
+ resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==}
dependencies:
diff-sequences: 29.6.3
estree-walker: 3.0.3
@@ -1185,8 +1185,8 @@ packages:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
dev: true
- /happy-dom@13.8.2:
- resolution: {integrity: sha512-u9KxyeQNIzkJDR2iCitKeS5Uy0YUv5eOntpO8e7ZzbDVv4kP5Y77Zo2LnZitwMrss/1pY2Uc2e5qOVGkiKE5Gg==}
+ /happy-dom@13.8.6:
+ resolution: {integrity: sha512-Urcv2jvNel19QirWimOwYTW3slpEYGS8PLtzEwAlpTWpnKycXF8s0I7xUBK9fPvWAIc8uZf/CnV2cIwWE8jptw==}
engines: {node: '>=16.0.0'}
dependencies:
entities: 4.5.0
@@ -1544,7 +1544,7 @@ packages:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
- ufo: 1.4.0
+ ufo: 1.5.1
dev: true
/mrmime@2.0.0:
@@ -1990,8 +1990,8 @@ packages:
engines: {node: '>=10'}
dev: true
- /ufo@1.4.0:
- resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==}
+ /ufo@1.5.1:
+ resolution: {integrity: sha512-HGyF79+/qZ4soRvM+nHERR2pJ3VXDZ/8sL1uLahdgEDf580NkgiWOxLk33FetExqOWp352JZRsgXbG/4MaGOSg==}
dev: true
/universalify@2.0.1:
@@ -2005,8 +2005,8 @@ packages:
punycode: 2.3.1
dev: true
- /vite-node@1.3.1:
- resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==}
+ /vite-node@1.4.0:
+ resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
dependencies:
@@ -2086,15 +2086,15 @@ packages:
fsevents: 2.3.3
dev: true
- /vitest@1.3.1(happy-dom@13.8.2):
- resolution: {integrity: sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==}
+ /vitest@1.4.0(happy-dom@13.8.6):
+ resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@types/node': ^18.0.0 || >=20.0.0
- '@vitest/browser': 1.3.1
- '@vitest/ui': 1.3.1
+ '@vitest/browser': 1.4.0
+ '@vitest/ui': 1.4.0
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
@@ -2111,16 +2111,16 @@ packages:
jsdom:
optional: true
dependencies:
- '@vitest/expect': 1.3.1
- '@vitest/runner': 1.3.1
- '@vitest/snapshot': 1.3.1
- '@vitest/spy': 1.3.1
- '@vitest/utils': 1.3.1
+ '@vitest/expect': 1.4.0
+ '@vitest/runner': 1.4.0
+ '@vitest/snapshot': 1.4.0
+ '@vitest/spy': 1.4.0
+ '@vitest/utils': 1.4.0
acorn-walk: 8.3.2
chai: 4.4.1
debug: 4.3.4
execa: 8.0.1
- happy-dom: 13.8.2
+ happy-dom: 13.8.6
local-pkg: 0.5.0
magic-string: 0.30.8
pathe: 1.1.2
@@ -2130,7 +2130,7 @@ packages:
tinybench: 2.6.0
tinypool: 0.8.2
vite: 5.1.6
- vite-node: 1.3.1
+ vite-node: 1.4.0
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
diff --git a/vite-test/package.json b/vite-test/package.json
index 1a959b1..f3c43a4 100644
--- a/vite-test/package.json
+++ b/vite-test/package.json
@@ -37,7 +37,7 @@
},
"devDependencies": {
"vite": "^5.1.6",
- "vitest": "^1.3.1"
+ "vitest": "^1.4.0"
},
"dependencies": {
"azoth": "workspace:*"
diff --git a/vite-test/pnpm-lock.yaml b/vite-test/pnpm-lock.yaml
index a18aab6..b80dd4b 100644
--- a/vite-test/pnpm-lock.yaml
+++ b/vite-test/pnpm-lock.yaml
@@ -14,8 +14,8 @@ devDependencies:
specifier: ^5.1.6
version: 5.1.6
vitest:
- specifier: ^1.3.1
- version: 1.3.1
+ specifier: ^1.4.0
+ version: 1.4.0
packages:
@@ -349,38 +349,38 @@ packages:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: true
- /@vitest/expect@1.3.1:
- resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
+ /@vitest/expect@1.4.0:
+ resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==}
dependencies:
- '@vitest/spy': 1.3.1
- '@vitest/utils': 1.3.1
+ '@vitest/spy': 1.4.0
+ '@vitest/utils': 1.4.0
chai: 4.4.1
dev: true
- /@vitest/runner@1.3.1:
- resolution: {integrity: sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==}
+ /@vitest/runner@1.4.0:
+ resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==}
dependencies:
- '@vitest/utils': 1.3.1
+ '@vitest/utils': 1.4.0
p-limit: 5.0.0
pathe: 1.1.2
dev: true
- /@vitest/snapshot@1.3.1:
- resolution: {integrity: sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==}
+ /@vitest/snapshot@1.4.0:
+ resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==}
dependencies:
magic-string: 0.30.8
pathe: 1.1.2
pretty-format: 29.7.0
dev: true
- /@vitest/spy@1.3.1:
- resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
+ /@vitest/spy@1.4.0:
+ resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==}
dependencies:
tinyspy: 2.2.1
dev: true
- /@vitest/utils@1.3.1:
- resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
+ /@vitest/utils@1.4.0:
+ resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==}
dependencies:
diff-sequences: 29.6.3
estree-walker: 3.0.3
@@ -592,7 +592,7 @@ packages:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
- ufo: 1.4.0
+ ufo: 1.5.1
dev: true
/ms@2.1.2:
@@ -765,12 +765,12 @@ packages:
engines: {node: '>=4'}
dev: true
- /ufo@1.4.0:
- resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==}
+ /ufo@1.5.1:
+ resolution: {integrity: sha512-HGyF79+/qZ4soRvM+nHERR2pJ3VXDZ/8sL1uLahdgEDf580NkgiWOxLk33FetExqOWp352JZRsgXbG/4MaGOSg==}
dev: true
- /vite-node@1.3.1:
- resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==}
+ /vite-node@1.4.0:
+ resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
dependencies:
@@ -825,15 +825,15 @@ packages:
fsevents: 2.3.3
dev: true
- /vitest@1.3.1:
- resolution: {integrity: sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==}
+ /vitest@1.4.0:
+ resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@types/node': ^18.0.0 || >=20.0.0
- '@vitest/browser': 1.3.1
- '@vitest/ui': 1.3.1
+ '@vitest/browser': 1.4.0
+ '@vitest/ui': 1.4.0
happy-dom: '*'
jsdom: '*'
peerDependenciesMeta:
@@ -850,11 +850,11 @@ packages:
jsdom:
optional: true
dependencies:
- '@vitest/expect': 1.3.1
- '@vitest/runner': 1.3.1
- '@vitest/snapshot': 1.3.1
- '@vitest/spy': 1.3.1
- '@vitest/utils': 1.3.1
+ '@vitest/expect': 1.4.0
+ '@vitest/runner': 1.4.0
+ '@vitest/snapshot': 1.4.0
+ '@vitest/spy': 1.4.0
+ '@vitest/utils': 1.4.0
acorn-walk: 8.3.2
chai: 4.4.1
debug: 4.3.4
@@ -868,7 +868,7 @@ packages:
tinybench: 2.6.0
tinypool: 0.8.2
vite: 5.1.6
- vite-node: 1.3.1
+ vite-node: 1.4.0
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
From 5b24646d823a19b18fdf2051b59d4cef238bb0c1 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sat, 16 Mar 2024 10:20:19 -0700
Subject: [PATCH 10/23] small refactor
---
packages/runtime/renderer/magic.test.js | 31 ++++++++++++++++----
packages/runtime/renderer/renderer.dom.js | 2 +-
packages/runtime/renderer/renderer.js | 33 +++++++++++++++++-----
packages/runtime/renderer/renderer.test.js | 2 +-
4 files changed, 54 insertions(+), 14 deletions(-)
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/magic.test.js
index 1160ba2..99653b2 100644
--- a/packages/runtime/renderer/magic.test.js
+++ b/packages/runtime/renderer/magic.test.js
@@ -2,13 +2,15 @@ import { describe, test } from 'vitest';
import { compose } from '../compose/compose.js';
import {
makeRenderer,
- getBoundElements,
+ getDOMBound,
} from './renderer.dom.js';
import {
makeTemplate,
makeStringRenderer,
+ getStringBound,
Controller,
Updater,
+ clearBind,
} from './renderer.js';
// template generated artifacts
@@ -41,7 +43,7 @@ function renderDOM(p0) {
source,
getTargets,
makeBind,
- getBoundElements,
+ getDOMBound,
);
bind(p0);
return root;
@@ -50,14 +52,15 @@ function renderString(p0) {
const [root, bind] = makeTemplate(
stringSource,
getStringTargets,
- makeStringBind
+ makeStringBind,
+ getStringBound,
);
bind(p0);
return root;
}
/*
const NameTag = Controller.for(({ greeting, name }) => {
- const Greeting = Controller.for(greeting => {greeting});
+ const Greeting = Updater.for(greeting => {greeting});
return
{name}
@@ -68,7 +71,8 @@ const Hello = Controller.for(name =>
{name}
);
*/
describe('string render', () => {
const flatRender = node => node.flat().join('');
- test.only('Controller.for', ({ expect }) => {
+
+ test('Controller.for', ({ expect }) => {
const controller = Controller.for(name => renderString(name));
let node1 = controller.render('felix');
@@ -90,6 +94,14 @@ describe('string render', () => {
);
});
+ test('inject unknown node', ({ expect }) => {
+ const controller = Controller.for(name => renderString(name));
+ let node = controller.render('felix');
+ clearBind(node);
+ controller.update(node, 'garfield');
+ expect(flatRender(node)).toMatchInlineSnapshot(`"garfield
"`);
+ });
+
test('Updater.for', ({ expect }) => {
const updater = Updater.for(name => renderString(name));
const node = updater.render('felix');
@@ -104,6 +116,7 @@ describe('string render', () => {
});
});
+
describe('dom render', () => {
test('Controller.for', ({ expect }) => {
@@ -120,6 +133,14 @@ describe('dom render', () => {
expect(node2.outerHTML).toMatchInlineSnapshot(`"stimpy
"`);
});
+ test('inject unknown node creates bind', ({ expect }) => {
+ const controller = Controller.for(name => renderDOM(name));
+ let node = controller.render('felix');
+ clearBind(node);
+ controller.update(node, 'garfield');
+ expect(node.outerHTML).toMatchInlineSnapshot(`"garfield1
"`);
+ });
+
test('Updater.for', ({ expect }) => {
const updater = Updater.for(name => renderDOM(name));
const node = updater.render('felix');
diff --git a/packages/runtime/renderer/renderer.dom.js b/packages/runtime/renderer/renderer.dom.js
index 3b6fec1..525cec9 100644
--- a/packages/runtime/renderer/renderer.dom.js
+++ b/packages/runtime/renderer/renderer.dom.js
@@ -42,6 +42,6 @@ function renderer(fragment, isFragment) {
};
}
-export function getBoundElements(dom) {
+export function getDOMBound(dom) {
return dom.querySelectorAll(QUERY_SELECTOR);
}
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index b6c8d40..6db077a 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -1,19 +1,29 @@
export function makeStringRenderer(id, html, isFragment = false) {
+ const template = [];
+ for(let i = 0; i < html.length; i++) {
+ if(i !== 0) template.push(null);
+ template.push(html[i]);
+ }
+
return () => {
- const root = [];
+ const root = template.slice();
const targets = [];
- for(let i = 0; i < html.length; i++) {
- root.push(html[i]);
- if(i === html.length - 1) break;
- const target = [];
- targets.push(target);
- root.push(target);
+ for(let i = 1; i < root.length; i += 2) {
+ targets.push(root[i] = []);
}
return [root, targets];
};
}
+export function getStringBound(root) {
+ const targets = [];
+ for(let i = 1; i < root.length; i += 2) {
+ targets.push(root[i] = []);
+ }
+ return targets;
+}
+
// stack
const injectable = [];
export function inject(node, callback) {
@@ -28,6 +38,11 @@ export function inject(node, callback) {
const map = new Map();
+// TODO: will evolve once "clean-up" happens
+export function clearBind(node) {
+ if(map.has(node)) map.delete(node);
+}
+
export function makeTemplate(source, targets, makeBind, getBound) {
let bind = null;
let boundEls = null;
@@ -38,6 +53,10 @@ export function makeTemplate(source, targets, makeBind, getBound) {
if(node) bind = map.get(node);
if(bind) return [node, bind];
+ // use case would be list component optimize by
+ // not keeping bind functions,
+ // honestly not sure this really needed, the
+ // overhead is small as it is simple function
if(node) boundEls = getBound(node);
else {
// (destructured re-assignment)
diff --git a/packages/runtime/renderer/renderer.test.js b/packages/runtime/renderer/renderer.test.js
index 1e8e1aa..e435771 100644
--- a/packages/runtime/renderer/renderer.test.js
+++ b/packages/runtime/renderer/renderer.test.js
@@ -1,5 +1,5 @@
import { beforeEach, describe, test } from 'vitest';
-import { clearTemplates, makeRenderer } from './renderer.js';
+import { clearTemplates, makeRenderer } from './renderer.dom.js';
describe('isFragment', () => {
From 032e3274290592dbb88902354aeb327c8e0dbc65 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sat, 16 Mar 2024 18:41:23 -0700
Subject: [PATCH 11/23] remove .only on test, refactor some code in renderer
---
packages/compiler/compiler.test.js | 2 +-
packages/runtime/renderer/renderer.js | 15 ++++++++-------
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/packages/compiler/compiler.test.js b/packages/compiler/compiler.test.js
index 00e75f8..3256eec 100644
--- a/packages/compiler/compiler.test.js
+++ b/packages/compiler/compiler.test.js
@@ -16,7 +16,7 @@ const compile = input => {
describe('JSX dom literals', () => {
- test.only('Hello Azoth', ({ expect }) => {
+ test('Hello Azoth', ({ expect }) => {
const input = `const t =
Hello {"Azoth"}
;`;
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 6db077a..8bf88da 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -24,6 +24,14 @@ export function getStringBound(root) {
return targets;
}
+
+const map = new Map();
+
+// TODO: cleanup actions on nodes
+export function clearBind(node) {
+ if(map.has(node)) map.delete(node);
+}
+
// stack
const injectable = [];
export function inject(node, callback) {
@@ -36,13 +44,6 @@ export function inject(node, callback) {
}
}
-const map = new Map();
-
-// TODO: will evolve once "clean-up" happens
-export function clearBind(node) {
- if(map.has(node)) map.delete(node);
-}
-
export function makeTemplate(source, targets, makeBind, getBound) {
let bind = null;
let boundEls = null;
From 2dd6900532c77c7b7fecafef4f02e19c79a785fc Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sun, 17 Mar 2024 07:44:54 -0700
Subject: [PATCH 12/23] remove class constructor then calling render with props
---
.../runtime/compose/compose.element.test.js | 15 ++-----
packages/runtime/compose/compose.js | 41 +++++++++----------
packages/runtime/compose/compose.test.js | 5 +--
3 files changed, 24 insertions(+), 37 deletions(-)
diff --git a/packages/runtime/compose/compose.element.test.js b/packages/runtime/compose/compose.element.test.js
index 4bc4933..da340df 100644
--- a/packages/runtime/compose/compose.element.test.js
+++ b/packages/runtime/compose/compose.element.test.js
@@ -20,17 +20,13 @@ class ClassComp {
}
}
const ArrowComp = ({ name }) => runCompose(name, elementWithAnchor);
-class ClassCompRender {
- render({ name }) {
- return runCompose(name, elementWithAnchor);
- }
-}
+
const RenderObject = {
render({ name }) {
return runCompose(name, elementWithAnchor);
}
};
-const CONSTRUCTORS = [Component, ClassComp, RenderObject, ClassCompRender, ArrowComp];
+const CONSTRUCTORS = [Component, ClassComp, RenderObject, ArrowComp];
function ComponentP({ name }) {
return Promise.resolve(runCompose(name, elementWithAnchor));
@@ -48,13 +44,8 @@ const RenderObjectP = {
return runCompose(name, elementWithAnchor);
}
};
-class ClassCompRenderP {
- async render({ name }) {
- return () => runCompose(name, elementWithAnchor);
- }
-}
const ArrowCompP = async ({ name }) => runCompose(name, elementWithAnchor);
-const ASYNC_CONSTRUCTORS = [ComponentP, ClassCompP, RenderObjectP, ClassCompRenderP, ArrowCompP];
+const ASYNC_CONSTRUCTORS = [ComponentP, ClassCompP, RenderObjectP, ArrowCompP];
describe('create element', () => {
diff --git a/packages/runtime/compose/compose.js b/packages/runtime/compose/compose.js
index 062303d..f93453a 100644
--- a/packages/runtime/compose/compose.js
+++ b/packages/runtime/compose/compose.js
@@ -80,7 +80,7 @@ export function composeElement(anchor, Constructor, props, slottable) {
export function createElement(Constructor, props, slottable) {
const result = create(Constructor, props, slottable);
// result is returned to caller, force to be of type Node
- // and convert strings and numbers into text nodes
+ // by converting strings and numbers into text nodes
const type = typeof result;
if(type === 'string' || type === 'number') {
return document.createTextNode(result);
@@ -104,10 +104,7 @@ function create(input, props, slottable, anchor) {
return anchor ? void compose(anchor, input) : input;
case !!(input.prototype?.constructor): {
// eslint-disable-next-line new-cap
- const created = new input(props, slottable);
- return isRenderObject(created)
- ? create(created, props, slottable, anchor)
- : create(created, null, null, anchor);
+ return create(new input(props, slottable), null, null, anchor);
}
case type === 'function':
return create(input(props, slottable), null, null, anchor);
@@ -115,27 +112,29 @@ function create(input, props, slottable, anchor) {
throwTypeError(input, type);
break;
}
- case !!input[Symbol.asyncIterator]:
- if(!anchor) anchor = document.createComment('0');
- composeAsyncIterator(anchor, input, false, props, slottable);
- return anchor;
case isRenderObject(input):
return create(input.render(props, slottable), null, null, anchor);
- case input instanceof Promise: {
- if(!anchor) anchor = document.createComment('0');
- input.then(value => {
- create(value, props, slottable, anchor);
- });
- return anchor;
- }
- case Array.isArray(input): {
+ default: {
+ // these inputs require a comment anchor to which they can render
if(!anchor) anchor = document.createComment('0');
- compose(anchor, input, false);
+
+ if(input[Symbol.asyncIterator]) {
+ composeAsyncIterator(anchor, input, false, props, slottable);
+ }
+ else if(input instanceof Promise) {
+ input.then(value => {
+ create(value, props, slottable, anchor);
+ });
+ }
+ else if(Array.isArray(input)) {
+ composeArray(anchor, input, false);
+ }
+ else {
+ throwTypeErrorForObject(input, type);
+ }
+
return anchor;
}
- default: {
- throwTypeErrorForObject(input, type);
- }
}
}
diff --git a/packages/runtime/compose/compose.test.js b/packages/runtime/compose/compose.test.js
index 9b3772c..e44616b 100644
--- a/packages/runtime/compose/compose.test.js
+++ b/packages/runtime/compose/compose.test.js
@@ -1,9 +1,6 @@
import { describe, test } from 'vitest';
import { IGNORE, compose } from './compose.js';
-import {
- elements, elementWithAnchor, elementWithText,
- $anchor
-} from 'test-utils/elements';
+import { elements, elementWithAnchor, elementWithText, $anchor } from 'test-utils/elements';
export function runCompose(value, create) {
const { dom, anchor } = create();
From 0aca3c4b77e579bacb071070c58645a59265bc3a Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sun, 17 Mar 2024 09:47:08 -0700
Subject: [PATCH 13/23] refactor into single RenderService with DOMRenderEngine
and HTMLRenderEngine
---
packages/runtime/renderer/magic.test.js | 185 +++++++++++----------
packages/runtime/renderer/renderer.dom.js | 76 ++++-----
packages/runtime/renderer/renderer.html.js | 61 +++++++
packages/runtime/renderer/renderer.js | 64 +++----
4 files changed, 223 insertions(+), 163 deletions(-)
create mode 100644 packages/runtime/renderer/renderer.html.js
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/magic.test.js
index 99653b2..3d63a6b 100644
--- a/packages/runtime/renderer/magic.test.js
+++ b/packages/runtime/renderer/magic.test.js
@@ -1,63 +1,14 @@
-import { describe, test } from 'vitest';
+import { describe, test, beforeEach, beforeAll } from 'vitest';
import { compose } from '../compose/compose.js';
import {
- makeRenderer,
- getDOMBound,
-} from './renderer.dom.js';
-import {
+ get,
makeTemplate,
- makeStringRenderer,
- getStringBound,
Controller,
Updater,
clearBind,
+ RenderService,
} from './renderer.js';
-// template generated artifacts
-const source = makeRenderer('id', ``);
-const stringSource = makeStringRenderer('id', [``, `
`]);
-
-function getTargets(r, boundEls) {
- return [r.childNodes[0]];
-}
-
-const makeBind = targets => {
- const t0 = targets[0];
- return p0 => {
- compose(t0, p0);
- };
-};
-
-function getStringTargets(r, boundEls) {
- return [boundEls[0]];
-}
-
-const makeStringBind = targets => {
- const t0 = targets[0];
- return p0 => {
- t0[0] = p0;
- };
-};
-function renderDOM(p0) {
- const [root, bind] = makeTemplate(
- source,
- getTargets,
- makeBind,
- getDOMBound,
- );
- bind(p0);
- return root;
-}
-function renderString(p0) {
- const [root, bind] = makeTemplate(
- stringSource,
- getStringTargets,
- makeStringBind,
- getStringBound,
- );
- bind(p0);
- return root;
-}
/*
const NameTag = Controller.for(({ greeting, name }) => {
const Greeting = Updater.for(greeting => {greeting});
@@ -69,85 +20,141 @@ const NameTag = Controller.for(({ greeting, name }) => {
const Hello = Controller.for(name => {name}
);
*/
-describe('string render', () => {
- const flatRender = node => node.flat().join('');
+
+
+describe('dom render', () => {
+ let source = null;
+ beforeAll(() => {
+ RenderService.clear();
+ RenderService.useDOMEngine();
+ source = get('id', false, ``);
+ });
+
+ function getTargets(r, boundEls) {
+ return [r.childNodes[0]];
+ }
+
+ const makeBind = targets => {
+ const t0 = targets[0];
+ return p0 => {
+ compose(t0, p0);
+ };
+ };
+
+ function renderDOM(p0) {
+ const [root, bind] = makeTemplate(
+ source,
+ getTargets,
+ makeBind,
+ );
+ bind(p0);
+ return root;
+ }
test('Controller.for', ({ expect }) => {
- const controller = Controller.for(name => renderString(name));
+ const controller = Controller.for(name => renderDOM(name));
let node1 = controller.render('felix');
let node2 = controller.render('duchess');
- expect(flatRender(node1)).toMatchInlineSnapshot(
- `"felix
"`
- );
- expect(flatRender(node2)).toMatchInlineSnapshot(
- `"duchess
"`
- );
+ expect(node1.outerHTML).toMatchInlineSnapshot(`"felix
"`);
+ expect(node2.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
controller.update(node1, 'garfield');
controller.update(node2, 'stimpy');
- expect(flatRender(node1)).toMatchInlineSnapshot(
- `"garfield
"`
- );
- expect(flatRender(node2)).toMatchInlineSnapshot(
- `"stimpy
"`
- );
+ expect(node1.outerHTML).toMatchInlineSnapshot(`"garfield
"`);
+ expect(node2.outerHTML).toMatchInlineSnapshot(`"stimpy
"`);
});
- test('inject unknown node', ({ expect }) => {
- const controller = Controller.for(name => renderString(name));
+ test('inject unknown node creates bind', ({ expect }) => {
+ const controller = Controller.for(name => renderDOM(name));
let node = controller.render('felix');
clearBind(node);
controller.update(node, 'garfield');
- expect(flatRender(node)).toMatchInlineSnapshot(`"garfield
"`);
+ expect(node.outerHTML).toMatchInlineSnapshot(`"garfield1
"`);
});
test('Updater.for', ({ expect }) => {
- const updater = Updater.for(name => renderString(name));
+ const updater = Updater.for(name => renderDOM(name));
const node = updater.render('felix');
- expect(flatRender(node)).toMatchInlineSnapshot(
- `"felix
"`
- );
+ expect(node.outerHTML).toMatchInlineSnapshot(`"felix
"`);
updater.update('duchess');
- expect(flatRender(node)).toMatchInlineSnapshot(
- `"duchess
"`
- );
+ expect(node.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
});
});
-describe('dom render', () => {
+describe('html render', () => {
+ const flatRender = node => node.flat().join('');
+
+ let source = null;
+ beforeAll(() => {
+ RenderService.clear();
+ RenderService.useHTMLEngine();
+ source = get('id', false, [``, `
`]);
+ });
+
+ function getTargets(r, boundEls) {
+ return [boundEls[0]];
+ }
+ const makeBind = targets => {
+ const t0 = targets[0];
+ return p0 => {
+ t0[0] = p0;
+ };
+ };
+ function render(p0) {
+ const [root, bind] = makeTemplate(
+ source,
+ getTargets,
+ makeBind,
+ );
+ bind(p0);
+ return root;
+ }
test('Controller.for', ({ expect }) => {
- const controller = Controller.for(name => renderDOM(name));
+ const controller = Controller.for(name => render(name));
let node1 = controller.render('felix');
let node2 = controller.render('duchess');
- expect(node1.outerHTML).toMatchInlineSnapshot(`"felix
"`);
- expect(node2.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
+ expect(flatRender(node1)).toMatchInlineSnapshot(
+ `"felix
"`
+ );
+ expect(flatRender(node2)).toMatchInlineSnapshot(
+ `"duchess
"`
+ );
controller.update(node1, 'garfield');
controller.update(node2, 'stimpy');
- expect(node1.outerHTML).toMatchInlineSnapshot(`"garfield
"`);
- expect(node2.outerHTML).toMatchInlineSnapshot(`"stimpy
"`);
+ expect(flatRender(node1)).toMatchInlineSnapshot(
+ `"garfield
"`
+ );
+ expect(flatRender(node2)).toMatchInlineSnapshot(
+ `"stimpy
"`
+ );
});
- test('inject unknown node creates bind', ({ expect }) => {
- const controller = Controller.for(name => renderDOM(name));
+ test('inject unknown node', ({ expect }) => {
+ const controller = Controller.for(name => render(name));
let node = controller.render('felix');
clearBind(node);
controller.update(node, 'garfield');
- expect(node.outerHTML).toMatchInlineSnapshot(`"garfield1
"`);
+ expect(flatRender(node)).toMatchInlineSnapshot(`"garfield
"`);
});
test('Updater.for', ({ expect }) => {
- const updater = Updater.for(name => renderDOM(name));
+ const updater = Updater.for(name => render(name));
const node = updater.render('felix');
- expect(node.outerHTML).toMatchInlineSnapshot(`"felix
"`);
+ expect(flatRender(node)).toMatchInlineSnapshot(
+ `"felix
"`
+ );
updater.update('duchess');
- expect(node.outerHTML).toMatchInlineSnapshot(`"duchess
"`);
+ expect(flatRender(node)).toMatchInlineSnapshot(
+ `"duchess
"`
+ );
});
});
+
diff --git a/packages/runtime/renderer/renderer.dom.js b/packages/runtime/renderer/renderer.dom.js
index 525cec9..807a0a8 100644
--- a/packages/runtime/renderer/renderer.dom.js
+++ b/packages/runtime/renderer/renderer.dom.js
@@ -1,47 +1,31 @@
-const templates = new Map();
-
-export function clearTemplates() {
- templates.clear();
-}
-
-export function makeRenderer(id, html, isFragment = false) {
- if(templates.has(id)) return templates.get(id);
-
- const template = document.createElement('template');
- template.innerHTML = html;
- return rendererFactory(id, template.content, isFragment);
-}
-
-export function rendererById(id, isFragment = false) {
- if(templates.has(id)) return templates.get(id);
-
- const templateEl = document.getElementById(id);
- if(!templateEl) {
- throw new Error(`No template with id "${id}"`);
- }
-
- return rendererFactory(id, templateEl.content, isFragment);
-}
-
-function rendererFactory(id, node, isFragment) {
- const render = renderer(node, isFragment);
- templates.set(id, render);
- return render;
-}
-
const QUERY_SELECTOR = '[data-bind]';
-
-function renderer(fragment, isFragment) {
- if(!isFragment) fragment = fragment.firstElementChild;
- // TODO: malformed fragments...necessary?
-
- return function render() {
- const clone = fragment.cloneNode(true);
- const targets = clone.querySelectorAll(QUERY_SELECTOR);
- return [clone, targets];
- };
-}
-
-export function getDOMBound(dom) {
- return dom.querySelectorAll(QUERY_SELECTOR);
-}
+export const DOMRenderEngine = {
+ name: 'DOMRenderEngine',
+ make(html) {
+ const template = document.createElement('template');
+ template.innerHTML = html;
+ return template.content;
+ },
+ get(id) {
+ const template = document.getElementById(id);
+ if(!template) {
+ throw new Error(`No template with id "${id}"`);
+ }
+ return template.content;
+ },
+ renderer(fragment, isFragment) {
+ if(!isFragment) fragment = fragment.firstElementChild;
+ // TODO: malformed fragments...necessary?
+
+ return function render() {
+ const clone = fragment.cloneNode(true);
+ const targets = clone.querySelectorAll(QUERY_SELECTOR);
+ return [clone, targets];
+ };
+ },
+ bound(dom) {
+ /* bound */
+ return dom.querySelectorAll(QUERY_SELECTOR);
+ /* */
+ }
+};
diff --git a/packages/runtime/renderer/renderer.html.js b/packages/runtime/renderer/renderer.html.js
new file mode 100644
index 0000000..f1065e7
--- /dev/null
+++ b/packages/runtime/renderer/renderer.html.js
@@ -0,0 +1,61 @@
+export const HTMLRenderEngine = {
+ name: 'HTMLRenderEngine',
+ make(html) {
+ return html;
+ },
+ get(id) {
+ // TODO: what is the prod equiv? if any
+ throw new Error(`HTMLRenderEngine does not support "get(id)" of "${id}". Use "make(html)" instead`);
+ },
+ // TODO: are fragments a thing with html render?
+ renderer(html/*, isFragment*/) {
+ const template = [];
+ for(let i = 0; i < html.length; i++) {
+ if(i !== 0) template.push(null);
+ template.push(html[i]);
+ }
+
+ return () => {
+ const root = template.slice();
+ const targets = [];
+ for(let i = 1; i < root.length; i += 2) {
+ targets.push(root[i] = []);
+ }
+ return [root, targets];
+ };
+ },
+ bound(root) {
+ const targets = [];
+ for(let i = 1; i < root.length; i += 2) {
+ targets.push(root[i] = []);
+ }
+ return targets;
+ }
+};
+
+
+export function makeStringRenderer(id, html, isFragment = false) {
+ const template = [];
+ for(let i = 0; i < html.length; i++) {
+ if(i !== 0) template.push(null);
+ template.push(html[i]);
+ }
+
+ return () => {
+ const root = template.slice();
+ const targets = [];
+ for(let i = 1; i < root.length; i += 2) {
+ targets.push(root[i] = []);
+ }
+ return [root, targets];
+ };
+}
+
+export function getStringBound(root) {
+ const targets = [];
+ for(let i = 1; i < root.length; i += 2) {
+ targets.push(root[i] = []);
+ }
+ return targets;
+}
+
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 8bf88da..b017330 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -1,35 +1,43 @@
+import { DOMRenderEngine } from './renderer.dom.js';
+import { HTMLRenderEngine } from './renderer.html.js';
-export function makeStringRenderer(id, html, isFragment = false) {
- const template = [];
- for(let i = 0; i < html.length; i++) {
- if(i !== 0) template.push(null);
- template.push(html[i]);
- }
+let renderEngine = DOMRenderEngine;
+export const RenderService = {
+ useDOMEngine() {
+ renderEngine = DOMRenderEngine;
+ },
+ useHTMLEngine() {
+ renderEngine = HTMLRenderEngine;
+ },
+ clear,
+ get,
+ bound,
+};
- return () => {
- const root = template.slice();
- const targets = [];
- for(let i = 1; i < root.length; i += 2) {
- targets.push(root[i] = []);
- }
- return [root, targets];
- };
+const templates = new Map();
+function clear() {
+ templates.clear();
}
-export function getStringBound(root) {
- const targets = [];
- for(let i = 1; i < root.length; i += 2) {
- targets.push(root[i] = []);
- }
- return targets;
+export function get(id, isFragment = false, html = '') {
+ if(templates.has(id)) return templates.get(id);
+
+ let node = html ? renderEngine.make(html) : renderEngine.get(id);
+ const render = renderEngine.renderer(node, isFragment);
+
+ templates.set(id, render);
+ return render;
}
+export function bound(node) {
+ return renderEngine.bound(node);
+}
-const map = new Map();
+const bindings = new Map();
-// TODO: cleanup actions on nodes
+// TODO: impl cleanup actions on nodes
export function clearBind(node) {
- if(map.has(node)) map.delete(node);
+ if(bindings.has(node)) bindings.delete(node);
}
// stack
@@ -44,21 +52,21 @@ export function inject(node, callback) {
}
}
-export function makeTemplate(source, targets, makeBind, getBound) {
+export function makeTemplate(source, targets, makeBind) {
let bind = null;
let boundEls = null;
let node = injectable.at(-1); // peek!
- // TODO: test injectable is right template id
+ // TODO: test injectable is right template id type
- if(node) bind = map.get(node);
+ if(node) bind = bindings.get(node);
if(bind) return [node, bind];
// use case would be list component optimize by
// not keeping bind functions,
// honestly not sure this really needed, the
// overhead is small as it is simple function
- if(node) boundEls = getBound(node);
+ if(node) boundEls = renderEngine.bound(node);
else {
// (destructured re-assignment)
([node, boundEls] = source());
@@ -67,7 +75,7 @@ export function makeTemplate(source, targets, makeBind, getBound) {
const nodes = targets(node, boundEls);
bind = makeBind(nodes);
- map.set(node, bind);
+ bindings.set(node, bind);
return [node, bind];
}
From 9df889d6a0157469ec0483174ba15a93831bb99c Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sun, 17 Mar 2024 13:31:25 -0700
Subject: [PATCH 14/23] new rendering engine, WIP for integrating with new
asset generation
---
.../{magic.test.js => controller.test.js} | 42 +++++++------------
packages/runtime/renderer/index.js | 2 +-
packages/runtime/renderer/renderer.dom.js | 8 ++--
packages/runtime/renderer/renderer.html.js | 41 ++++--------------
packages/runtime/renderer/renderer.js | 34 ++++++++-------
packages/runtime/renderer/renderer.test.js | 4 +-
6 files changed, 48 insertions(+), 83 deletions(-)
rename packages/runtime/renderer/{magic.test.js => controller.test.js} (81%)
diff --git a/packages/runtime/renderer/magic.test.js b/packages/runtime/renderer/controller.test.js
similarity index 81%
rename from packages/runtime/renderer/magic.test.js
rename to packages/runtime/renderer/controller.test.js
index 3d63a6b..8c02237 100644
--- a/packages/runtime/renderer/magic.test.js
+++ b/packages/runtime/renderer/controller.test.js
@@ -2,32 +2,16 @@ import { describe, test, beforeEach, beforeAll } from 'vitest';
import { compose } from '../compose/compose.js';
import {
get,
- makeTemplate,
+ render,
Controller,
Updater,
clearBind,
RenderService,
} from './renderer.js';
-/*
-const NameTag = Controller.for(({ greeting, name }) => {
- const Greeting = Updater.for(greeting => {greeting});
-
- return
- {name}
-
;
-});
-
-const Hello = Controller.for(name => {name}
);
-*/
-
-
describe('dom render', () => {
- let source = null;
beforeAll(() => {
- RenderService.clear();
RenderService.useDOMEngine();
- source = get('id', false, ``);
});
function getTargets(r, boundEls) {
@@ -42,10 +26,12 @@ describe('dom render', () => {
};
function renderDOM(p0) {
- const [root, bind] = makeTemplate(
- source,
+ const [root, bind] = render(
+ 'id',
getTargets,
makeBind,
+ false,
+ ``,
);
bind(p0);
return root;
@@ -87,34 +73,34 @@ describe('dom render', () => {
describe('html render', () => {
const flatRender = node => node.flat().join('');
- let source = null;
beforeAll(() => {
- RenderService.clear();
RenderService.useHTMLEngine();
- source = get('id', false, [``, `
`]);
});
function getTargets(r, boundEls) {
return [boundEls[0]];
}
+
const makeBind = targets => {
const t0 = targets[0];
return p0 => {
t0[0] = p0;
};
};
- function render(p0) {
- const [root, bind] = makeTemplate(
- source,
+ function renderHTML(p0) {
+ const [root, bind] = render(
+ 'id',
getTargets,
makeBind,
+ false,
+ [``, `
`],
);
bind(p0);
return root;
}
test('Controller.for', ({ expect }) => {
- const controller = Controller.for(name => render(name));
+ const controller = Controller.for(name => renderHTML(name));
let node1 = controller.render('felix');
let node2 = controller.render('duchess');
@@ -136,7 +122,7 @@ describe('html render', () => {
});
test('inject unknown node', ({ expect }) => {
- const controller = Controller.for(name => render(name));
+ const controller = Controller.for(name => renderHTML(name));
let node = controller.render('felix');
clearBind(node);
controller.update(node, 'garfield');
@@ -144,7 +130,7 @@ describe('html render', () => {
});
test('Updater.for', ({ expect }) => {
- const updater = Updater.for(name => render(name));
+ const updater = Updater.for(name => renderHTML(name));
const node = updater.render('felix');
expect(flatRender(node)).toMatchInlineSnapshot(
`"felix
"`
diff --git a/packages/runtime/renderer/index.js b/packages/runtime/renderer/index.js
index 0b844f3..b579bc3 100644
--- a/packages/runtime/renderer/index.js
+++ b/packages/runtime/renderer/index.js
@@ -1 +1 @@
-export { makeRenderer, rendererById } from './renderer.dom.js';
\ No newline at end of file
+export { makeRenderer, rendererById } from './renderer.js';
\ No newline at end of file
diff --git a/packages/runtime/renderer/renderer.dom.js b/packages/runtime/renderer/renderer.dom.js
index 807a0a8..726c79d 100644
--- a/packages/runtime/renderer/renderer.dom.js
+++ b/packages/runtime/renderer/renderer.dom.js
@@ -1,6 +1,6 @@
const QUERY_SELECTOR = '[data-bind]';
-export const DOMRenderEngine = {
- name: 'DOMRenderEngine',
+export const DOMRenderer = {
+ name: 'DOMRenderer',
make(html) {
const template = document.createElement('template');
template.innerHTML = html;
@@ -15,7 +15,7 @@ export const DOMRenderEngine = {
},
renderer(fragment, isFragment) {
if(!isFragment) fragment = fragment.firstElementChild;
- // TODO: malformed fragments...necessary?
+ // TODO: malformed fragment check...necessary?
return function render() {
const clone = fragment.cloneNode(true);
@@ -24,8 +24,6 @@ export const DOMRenderEngine = {
};
},
bound(dom) {
- /* bound */
return dom.querySelectorAll(QUERY_SELECTOR);
- /* */
}
};
diff --git a/packages/runtime/renderer/renderer.html.js b/packages/runtime/renderer/renderer.html.js
index f1065e7..e77d01d 100644
--- a/packages/runtime/renderer/renderer.html.js
+++ b/packages/runtime/renderer/renderer.html.js
@@ -1,13 +1,17 @@
-export const HTMLRenderEngine = {
- name: 'HTMLRenderEngine',
+export const HTMLRenderer = {
+ name: 'HTMLRenderer',
make(html) {
return html;
},
get(id) {
- // TODO: what is the prod equiv? if any
- throw new Error(`HTMLRenderEngine does not support "get(id)" of "${id}". Use "make(html)" instead`);
+ // Q: what is the prod optimized equiv? if any?
+ // Array/String literal seems as good or better than JSON file?
+ // TODO: benchmark
+ throw new Error(`HTMLRenderer does not support "get(id)" of "${id}". Use "make(html)" instead`);
},
- // TODO: are fragments a thing with html render?
+ // pretty sure fragments NOT needed for html render,
+ // really a DOM optimization to avoid Fragment container
+ // on single element root
renderer(html/*, isFragment*/) {
const template = [];
for(let i = 0; i < html.length; i++) {
@@ -32,30 +36,3 @@ export const HTMLRenderEngine = {
return targets;
}
};
-
-
-export function makeStringRenderer(id, html, isFragment = false) {
- const template = [];
- for(let i = 0; i < html.length; i++) {
- if(i !== 0) template.push(null);
- template.push(html[i]);
- }
-
- return () => {
- const root = template.slice();
- const targets = [];
- for(let i = 1; i < root.length; i += 2) {
- targets.push(root[i] = []);
- }
- return [root, targets];
- };
-}
-
-export function getStringBound(root) {
- const targets = [];
- for(let i = 1; i < root.length; i += 2) {
- targets.push(root[i] = []);
- }
- return targets;
-}
-
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index b017330..b40b330 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -1,48 +1,51 @@
-import { DOMRenderEngine } from './renderer.dom.js';
-import { HTMLRenderEngine } from './renderer.html.js';
+import { DOMRenderer } from './renderer.dom.js';
+import { HTMLRenderer } from './renderer.html.js';
+
+const templates = new Map(); // cache
+let renderEngine = DOMRenderer; // DOM or HTML engine
+
+// TODO: Class !!!!!
-let renderEngine = DOMRenderEngine;
export const RenderService = {
useDOMEngine() {
- renderEngine = DOMRenderEngine;
+ renderEngine = DOMRenderer;
+ clear();
},
useHTMLEngine() {
- renderEngine = HTMLRenderEngine;
+ renderEngine = HTMLRenderer;
+ clear();
},
- clear,
get,
bound,
};
-const templates = new Map();
function clear() {
templates.clear();
}
-export function get(id, isFragment = false, html = '') {
+function get(id, isFragment = false, content) {
if(templates.has(id)) return templates.get(id);
- let node = html ? renderEngine.make(html) : renderEngine.get(id);
+ const node = content ? renderEngine.make(content) : renderEngine.get(id);
const render = renderEngine.renderer(node, isFragment);
templates.set(id, render);
return render;
}
-export function bound(node) {
+function bound(node) {
return renderEngine.bound(node);
}
-const bindings = new Map();
-
-// TODO: impl cleanup actions on nodes
+const bindings = new Map(); // cache
+// TODO: implement cleanup actions on nodes
export function clearBind(node) {
if(bindings.has(node)) bindings.delete(node);
}
// stack
const injectable = [];
-export function inject(node, callback) {
+function inject(node, callback) {
injectable.push(node);
callback();
const popped = injectable.pop();
@@ -52,7 +55,8 @@ export function inject(node, callback) {
}
}
-export function makeTemplate(source, targets, makeBind) {
+export function render(id, targets, makeBind, isFragment, content) {
+ let source = get(id, isFragment, content);
let bind = null;
let boundEls = null;
let node = injectable.at(-1); // peek!
diff --git a/packages/runtime/renderer/renderer.test.js b/packages/runtime/renderer/renderer.test.js
index e435771..c6753ac 100644
--- a/packages/runtime/renderer/renderer.test.js
+++ b/packages/runtime/renderer/renderer.test.js
@@ -1,7 +1,7 @@
import { beforeEach, describe, test } from 'vitest';
-import { clearTemplates, makeRenderer } from './renderer.dom.js';
+import { clearTemplates, makeRenderer, Renderer } from './renderer.js';
-describe('isFragment', () => {
+describe('DOM isFragment', () => {
beforeEach(clearTemplates);
From 1108506905559a588ee038b1ef587fd8b9038c09 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Sun, 17 Mar 2024 18:30:48 -0700
Subject: [PATCH 15/23] test passing, still need to connect new render assets
---
packages/channels/repeat.test.jsx | 2 +-
.../{renderer.dom.js => dom-renderer.js} | 17 +++++++++++++++--
.../{renderer.test.js => dom-renderer.test.js} | 12 +++++-------
.../{renderer.html.js => html-renderer.js} | 18 ++++++++++--------
packages/runtime/renderer/renderer.js | 15 ++++++---------
5 files changed, 37 insertions(+), 27 deletions(-)
rename packages/runtime/renderer/{renderer.dom.js => dom-renderer.js} (70%)
rename packages/runtime/renderer/{renderer.test.js => dom-renderer.test.js} (54%)
rename packages/runtime/renderer/{renderer.html.js => html-renderer.js} (69%)
diff --git a/packages/channels/repeat.test.jsx b/packages/channels/repeat.test.jsx
index 1e8530c..2ccc2fc 100644
--- a/packages/channels/repeat.test.jsx
+++ b/packages/channels/repeat.test.jsx
@@ -1,4 +1,4 @@
-import { describe, test, beforeEach } from 'vitest';
+import { test, beforeEach } from 'vitest';
import './with-resolvers-polyfill.js';
import { fixtureSetup } from 'test-utils/fixtures';
import { subject } from './generators.js';
diff --git a/packages/runtime/renderer/renderer.dom.js b/packages/runtime/renderer/dom-renderer.js
similarity index 70%
rename from packages/runtime/renderer/renderer.dom.js
rename to packages/runtime/renderer/dom-renderer.js
index 726c79d..59fe488 100644
--- a/packages/runtime/renderer/renderer.dom.js
+++ b/packages/runtime/renderer/dom-renderer.js
@@ -1,18 +1,31 @@
const QUERY_SELECTOR = '[data-bind]';
export const DOMRenderer = {
name: 'DOMRenderer',
- make(html) {
+
+ createTemplate(id, content, isFragment) {
+ const node = DOMRenderer.template(id, content);
+ const render = DOMRenderer.renderer(node, isFragment);
+ return render;
+ },
+
+ template(id, content) {
+ if(content) return DOMRenderer.create(content);
+ DOMRenderer.getById(id);
+ },
+
+ create(html) {
const template = document.createElement('template');
template.innerHTML = html;
return template.content;
},
- get(id) {
+ getById(id) {
const template = document.getElementById(id);
if(!template) {
throw new Error(`No template with id "${id}"`);
}
return template.content;
},
+
renderer(fragment, isFragment) {
if(!isFragment) fragment = fragment.firstElementChild;
// TODO: malformed fragment check...necessary?
diff --git a/packages/runtime/renderer/renderer.test.js b/packages/runtime/renderer/dom-renderer.test.js
similarity index 54%
rename from packages/runtime/renderer/renderer.test.js
rename to packages/runtime/renderer/dom-renderer.test.js
index c6753ac..5610645 100644
--- a/packages/runtime/renderer/renderer.test.js
+++ b/packages/runtime/renderer/dom-renderer.test.js
@@ -1,24 +1,22 @@
import { beforeEach, describe, test } from 'vitest';
-import { clearTemplates, makeRenderer, Renderer } from './renderer.js';
+import { DOMRenderer } from './dom-renderer.js';
describe('DOM isFragment', () => {
- beforeEach(clearTemplates);
-
test('element root w/ false and true', async ({ expect }) => {
- const [div] = makeRenderer('element-root', `text
`)();
+ const [div] = DOMRenderer.createTemplate('element-root', false, `text
`)();
expect(div).toBeInstanceOf(HTMLDivElement);
- const [fragment] = makeRenderer('fragment-root', `text
`, true)();
+ const [fragment] = DOMRenderer.createTemplate('fragment-root', true, `text
`)();
expect(fragment).toBeInstanceOf(DocumentFragment);
});
test('fragment root w/ false and true', async ({ expect }) => {
- const [fragment] = makeRenderer('fragment-root', `
`, true)();
+ const [fragment] = DOMRenderer.createTemplate('fragment-root', `
`, true)();
expect(fragment).toBeInstanceOf(DocumentFragment);
// TODO: should this be a thrown exception?
- const [hr] = makeRenderer('element-root', `
`)();
+ const [hr] = DOMRenderer.createTemplate('element-root', `
`)();
expect(hr).toBeInstanceOf(HTMLHRElement);
});
});
diff --git a/packages/runtime/renderer/renderer.html.js b/packages/runtime/renderer/html-renderer.js
similarity index 69%
rename from packages/runtime/renderer/renderer.html.js
rename to packages/runtime/renderer/html-renderer.js
index e77d01d..cc6b545 100644
--- a/packages/runtime/renderer/renderer.html.js
+++ b/packages/runtime/renderer/html-renderer.js
@@ -1,14 +1,16 @@
export const HTMLRenderer = {
name: 'HTMLRenderer',
- make(html) {
- return html;
- },
- get(id) {
- // Q: what is the prod optimized equiv? if any?
- // Array/String literal seems as good or better than JSON file?
- // TODO: benchmark
- throw new Error(`HTMLRenderer does not support "get(id)" of "${id}". Use "make(html)" instead`);
+
+ createTemplate(_id, content) {
+ if(!content) {
+ // Q: what is the prod optimized equiv? if any?
+ // Array/String literal seems as good or better than JSON file?
+ // TODO: benchmark
+ throw new TypeError(`HTMLRenderer.createTemplate requires "content" parameter`);
+ }
+ return HTMLRenderer.renderer(content);
},
+
// pretty sure fragments NOT needed for html render,
// really a DOM optimization to avoid Fragment container
// on single element root
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index b40b330..7fdfc63 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -1,11 +1,9 @@
-import { DOMRenderer } from './renderer.dom.js';
-import { HTMLRenderer } from './renderer.html.js';
+import { DOMRenderer } from './dom-renderer.js';
+import { HTMLRenderer } from './html-renderer.js';
const templates = new Map(); // cache
let renderEngine = DOMRenderer; // DOM or HTML engine
-// TODO: Class !!!!!
-
export const RenderService = {
useDOMEngine() {
renderEngine = DOMRenderer;
@@ -26,11 +24,10 @@ function clear() {
function get(id, isFragment = false, content) {
if(templates.has(id)) return templates.get(id);
- const node = content ? renderEngine.make(content) : renderEngine.get(id);
- const render = renderEngine.renderer(node, isFragment);
+ const template = renderEngine.createTemplate(id, content, isFragment);
- templates.set(id, render);
- return render;
+ templates.set(id, template);
+ return template;
}
function bound(node) {
@@ -106,4 +103,4 @@ export class Updater extends Controller {
update(props) {
super.update(this.#node, props);
}
-}
\ No newline at end of file
+}
From 30c699eff6b08c53b48e537b7df4fd64fad2df87 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Wed, 20 Mar 2024 15:54:16 -0700
Subject: [PATCH 16/23] primary functions generated
---
packages/compiler/transform/Analyzer.js | 3 +
.../compiler/transform/AssetsGenerator.js | 28 +++
packages/compiler/transform/BindGenerator.js | 16 +-
.../compiler/transform/RenderGenerator.js | 192 ++++++++++++++++++
packages/compiler/transform/bind.test.js | 72 -------
packages/compiler/transform/modules.test.js | 171 ++++++++++++++++
packages/runtime/renderer/controller.test.js | 37 ++--
.../runtime/renderer/dom-renderer.test.js | 4 +-
packages/runtime/renderer/renderer.js | 55 ++---
packages/vite-plugin/index.js | 2 +-
pnpm-workspace.yaml | 1 -
11 files changed, 455 insertions(+), 126 deletions(-)
create mode 100644 packages/compiler/transform/AssetsGenerator.js
create mode 100644 packages/compiler/transform/RenderGenerator.js
delete mode 100644 packages/compiler/transform/bind.test.js
create mode 100644 packages/compiler/transform/modules.test.js
diff --git a/packages/compiler/transform/Analyzer.js b/packages/compiler/transform/Analyzer.js
index 0a40832..7f3fc62 100644
--- a/packages/compiler/transform/Analyzer.js
+++ b/packages/compiler/transform/Analyzer.js
@@ -36,6 +36,7 @@ export class Analyzer {
});
}
+ // TODO: move generation elsewhere
generateTemplate(htmlGenerator) {
const template = this.#template;
if(!template.isEmpty) template.html = htmlGenerator(template.node);
@@ -99,6 +100,8 @@ export class Analyzer {
node,
expr,
index,
+ // placeholder value until after analysis complete
+ queryIndex: -2,
};
if(element.isComponent) {
diff --git a/packages/compiler/transform/AssetsGenerator.js b/packages/compiler/transform/AssetsGenerator.js
new file mode 100644
index 0000000..f74a9f5
--- /dev/null
+++ b/packages/compiler/transform/AssetsGenerator.js
@@ -0,0 +1,28 @@
+export function makeTargets(template) {
+ const { boundElements, bindings } = template;
+ const { length: elLength } = boundElements;
+
+ const values = bindings.map(({ element, type, index }) => {
+ const { isRoot, queryIndex } = element;
+ const target = isRoot ? 'r' : `${'t'}[${queryIndex}]`;
+ return type === 'child'
+ ? `${target}.childNodes[${index}]`
+ : target;
+ });
+
+ return `const targets = (${elLength ? 'r,t' : 'r'}) => [${values.join()}];\n`;
+}
+
+export function makeGetBound({ id, isDomFragment, html }, options) {
+ const content = options?.noContent ? '' : `, ${html}`;
+ return `const getBound = renderer('${id}', targets, bind, ${isDomFragment}${content});\n`;
+}
+
+export function makeRender({ bindings: { length } }) {
+ const params = Array.from({ length }, (_, i) => `p${i}`);
+ return `function renderDOM(${params}) {
+ const [root, bind] = getBound();
+ bind(${params});
+ return root;
+}\n`;
+}
\ No newline at end of file
diff --git a/packages/compiler/transform/BindGenerator.js b/packages/compiler/transform/BindGenerator.js
index f2975cf..acb0d77 100644
--- a/packages/compiler/transform/BindGenerator.js
+++ b/packages/compiler/transform/BindGenerator.js
@@ -88,7 +88,7 @@ export class BindGenerator extends Generator {
}
Targets(template, state) {
- const { boundElements, bindings, isBoundRoot } = template;
+ const { boundElements, bindings } = template;
const { length: elLength } = boundElements;
state.write(`function targets(`);
@@ -103,17 +103,17 @@ export class BindGenerator extends Generator {
for(let i = 0; i < bindings.length; i++) {
const { element, type, index, node } = bindings[i];
const { isRoot, queryIndex } = element;
- const varName = isRoot ? ROOT : `${TARGETS}[${queryIndex}]`;
+ const target = isRoot ? ROOT : `${TARGETS}[${queryIndex}]`;
if(i !== 0) state.write(', ');
if(type !== 'child') {
- state.write(`${varName}`);
+ state.write(`${target}`);
continue;
}
const opening = getOpening(element, node);
- state.write(`${varName}.childNodes`, opening.name);
+ state.write(`${target}.childNodes`, opening.name);
state.write(`[${index}]`, node);
}
state.write(`];`);
@@ -125,18 +125,18 @@ export class BindGenerator extends Generator {
}
Render(template, state) {
- const { bindings } = template;
+ const { bindings, isDomFragment } = template;
const params = [];
for(let i = 0; i < bindings.length; i++) {
params.push(`${VALUE}${i}`);
}
- state.write(`function render(${params.join(', ')}) {`);
+ state.write(`function renderDOM(${params.join(', ')}) {`);
state.indentLevel++;
writeNextLine(state);
- state.write(`const [root, bind] = makeTemplate(source);`);
+ state.write(`const [root, bind] = render('id', targets, bind, ${isDomFragment});`);
writeNextLine(state);
state.write(`bind(${params.join(', ')});`);
writeNextLine(state);
@@ -252,7 +252,7 @@ export class BindGenerator extends Generator {
}
BindingProp(node, expr, index, element, state) {
- const { queryIndex, openingElement, openingFragment } = element;
+ const { openingElement, openingFragment } = element;
const opening = openingElement ?? openingFragment;
state.write(`${TARGET}${index}`, opening.name);
// TODO: more property validation
diff --git a/packages/compiler/transform/RenderGenerator.js b/packages/compiler/transform/RenderGenerator.js
new file mode 100644
index 0000000..3901601
--- /dev/null
+++ b/packages/compiler/transform/RenderGenerator.js
@@ -0,0 +1,192 @@
+import { Generator, writeNextLine } from './GeneratorBase.js';
+import { isValidESIdentifier } from 'is-valid-es-identifier';
+import { generateWith } from '../compiler.js';
+
+const TARGETS = 'ts';
+const TARGET = 't';
+const VALUE = 'v';
+
+export class RenderGenerator extends Generator {
+ static generate(template) {
+ const generator = new this(template);
+ return generateWith(generator, template.node);
+ }
+ #bindings = null;
+
+ constructor(template) {
+ super();
+ this.#bindings = template.bindings;
+ }
+
+ JSXFragment(_node, state) {
+ this.JSXTemplate(state);
+ }
+
+ JSXElement(_node, state) {
+ this.JSXTemplate(state);
+ }
+
+ JSXTemplate(state) {
+ this.Bindings(state);
+ }
+
+ JSXExpressionContainer({ expression }, state) {
+ this[expression.type](expression, state);
+ }
+
+ JSXIdentifier(identifier, state) {
+ state.write(identifier.name, identifier);
+ }
+
+ // Render(template, state) {
+ // const { bindings, isDomFragment } = template;
+
+ // const params = [];
+ // for(let i = 0; i < bindings.length; i++) {
+ // params.push(`${VALUE}${i}`);
+ // }
+
+ // state.write(`function renderDOM(${params.join(', ')}) {`);
+ // state.indentLevel++;
+ // writeNextLine(state);
+
+ // state.write(`const [root, bind] = render('id', targets, bind, ${isDomFragment});`);
+ // writeNextLine(state);
+ // state.write(`bind(${params.join(', ')});`);
+ // writeNextLine(state);
+ // state.write(`return root;`);
+
+ // state.indentLevel--;
+ // writeNextLine(state);
+ // state.write(`}`);
+ // state.write(state.lineEnd);
+ // }
+
+ Bindings(state) {
+ const bindings = this.#bindings;
+
+ state.write(`function bind(${TARGETS}) {`);
+ state.indentLevel++;
+ writeNextLine(state);
+
+ const targets = [];
+ const params = [];
+ for(let i = 0; i < bindings.length; i++) {
+ targets.push(`${TARGET}${i} = ${TARGETS}[${i}]`);
+ params.push(`${VALUE}${i}`);
+ }
+
+ state.write(`const ${targets.join(', ')};`);
+ writeNextLine(state);
+ state.write(`return (${params.join(', ')}) => {`);
+ state.indentLevel++;
+
+ for(let i = 0; i < bindings.length; i++) {
+ const { element, type, node, expr } = bindings[i];
+ writeNextLine(state);
+
+ if(!this[expr.type]) {
+ throw new TypeError(`Unexpected Binding expression AST type "${expr.type}"`);
+ }
+
+ if(node.isComponent) {
+ this.ComposeElement(node, expr, i, state);
+ continue;
+ }
+ if(type === 'child') {
+ this.Compose(node, expr, i, state);
+ continue;
+ }
+ if(type === 'prop') {
+ this.BindingProp(node, expr, i, element, state);
+ continue;
+ }
+
+ const message = `Unexpected binding type "${type}", expected "child" or "prop"`;
+ throw new Error(message);
+ }
+
+ state.indentLevel--;
+ writeNextLine(state);
+ state.write(`};`);
+ state.indentLevel--;
+ writeNextLine(state);
+ state.write(`}`);
+
+ state.write(state.lineEnd);
+ }
+
+ Compose(node, expr, index, state) {
+ state.write(`compose(`, node);
+ state.write(`${TARGET}${index}, `, node);
+ state.write(`${VALUE}${index}`, expr);
+ state.write(`);`);
+ }
+
+ ComposeElement(node, expr, index, state) {
+ state.write(`composeElement(`, node);
+ state.write(`${TARGET}${index}, `);
+ this.CompleteElement(node, expr, state);
+ state.write(`);`);
+ }
+
+ CreateElement(node, state) {
+ state.write(`createElement(`, node);
+ this.CompleteElement(node, node.componentExpr, state);
+ state.write(`)`);
+ }
+
+ CompleteElement({ props, slotFragment }, expr, state) {
+ this[expr.type](expr, state);
+ if(props?.length) {
+ this.ComponentProps(props, state);
+ }
+ else if(slotFragment) state.write(`, null`);
+
+ if(slotFragment) {
+ state.write(', ');
+ this.JSXTemplate(slotFragment, state);
+ }
+ }
+
+ ComponentProps(props, state) {
+ state.write(`, {`);
+ for(let i = 0; i < props.length; i++) {
+ const { node, expr } = props[i];
+ // TODO: Dom lookup, JS .prop v['prop'], etc.
+ // refactor with code below
+ state.write(` `);
+ state.write(node.name.name, node.name);
+ state.write(`: `);
+ this[expr.type](expr, state);
+ state.write(`,`);
+ }
+ state.write(` }`);
+ }
+
+ BindingProp(node, expr, index, element, state) {
+ const { openingElement, openingFragment } = element;
+ const opening = openingElement ?? openingFragment;
+ state.write(`${TARGET}${index}`, opening.name);
+ // TODO: more property validation
+ const identity = node.name;
+ const propName = identity.name;
+ // TODO: refactor with component props
+ if(isValidESIdentifier(propName)) {
+ state.write(`.`);
+ state.write(propName, node.name);
+ }
+ else {
+ state.write(`["`, node.name);
+ state.write(propName, node.name);
+ state.write(`"]`);
+ }
+
+ /* expression */
+ state.write(` = `);
+ // this[expr.type](expr, state);
+ state.write(`${VALUE}${index}`, expr);
+ state.write(`;`);
+ }
+}
+
diff --git a/packages/compiler/transform/bind.test.js b/packages/compiler/transform/bind.test.js
deleted file mode 100644
index fb925da..0000000
--- a/packages/compiler/transform/bind.test.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/* eslint-disable no-undef */
-import { BindGenerator } from './BindGenerator.js';
-import { parse, generate as _generate } from '../compiler.js';
-import { describe, test } from 'vitest';
-
-function preParse(input) {
- const ast = parse(input);
- return _generate(ast);
-}
-describe('Bind Generator', () => {
-
- test('Hello bind', ({ expect }) => {
- // const input = `const t = yo
;`;
- const input = `name => {name}
`;
- const initial = preParse(input);
- const template = initial.templates[0];
- expect(template.node.type).toBe('JSXElement');
-
- const { code } = BindGenerator.generate(template);
-
- expect(code).toMatchInlineSnapshot(`
- "function targets(r) {
- return [r.childNodes[0]];
- }
- function bind(ts) {
- const t0 = ts[0];
- return (v0) => {
- compose(t0, v0);
- };
- }
- function render(v0) {
- const [root, bind] = makeTemplate(source);
- bind(v0);
- return root;
- }
- "
- `);
- });
-
- test('Bind children and props', ({ expect }) => {
- // const input = `const t = yo
;`;
- const input = `const t =
- {"Greeting"} hey {"Azoth"}!
-
;`;
-
- const initial = preParse(input);
- const template = initial.templates[0];
- expect(template.node.type).toBe('JSXElement');
-
- const { code } = BindGenerator.generate(template);
-
- expect(code).toMatchInlineSnapshot(`
- "function targets(r, ts) {
- return [r, r.childNodes[1], ts[0].childNodes[1]];
- }
- function bind(ts) {
- const t0 = ts[0], t1 = ts[1], t2 = ts[2];
- return (v0, v1, v2) => {
- t0.className = v0;
- compose(t1, v1);
- compose(t2, v2);
- };
- }
- function render(v0, v1, v2) {
- const [root, bind] = makeTemplate(source);
- bind(v0, v1, v2);
- return root;
- }
- "
- `);
- });
-});
\ No newline at end of file
diff --git a/packages/compiler/transform/modules.test.js b/packages/compiler/transform/modules.test.js
new file mode 100644
index 0000000..ddfdd17
--- /dev/null
+++ b/packages/compiler/transform/modules.test.js
@@ -0,0 +1,171 @@
+/* eslint-disable no-undef */
+import { makeTargets, makeGetBound, makeRender } from './AssetsGenerator.js';
+import { parse, generate as _generate } from '../compiler.js';
+import { describe, test, beforeEach } from 'vitest';
+import { RenderGenerator } from './RenderGenerator.js';
+
+function preParse(input, expect) {
+ const ast = parse(input);
+ const initial = _generate(ast);
+ const template = initial.templates[0];
+ expect(template.node.type).toBe('JSXElement');
+ return template;
+}
+
+
+describe('targets generator', () => {
+
+ beforeEach(context => {
+ context.compile = code => {
+ const template = preParse(code, context.expect);
+ return makeTargets(template);
+ };
+ });
+
+ test('simple', ({ compile, expect }) => {
+ const code = compile(`name => {name}
`);
+ expect(code).toMatchInlineSnapshot(`
+ "const targets = (r) => [r.childNodes[0]];
+ "
+ `);
+ });
+
+ test('props and elements', ({ compile, expect }) => {
+ const code = compile(`const t =
+ {"Greeting"} hey {"Azoth"}!
+
;`);
+ expect(code).toMatchInlineSnapshot(
+ `
+ "const targets = (r,t) => [r,r.childNodes[1],t[0].childNodes[1]];
+ "
+ `
+ );
+ });
+});
+
+describe('getBound generator', () => {
+
+ beforeEach(context => {
+ context.getTemplate = code => {
+ return preParse(code, context.expect);
+ };
+ });
+
+ test('simple', ({ expect }) => {
+ const template = preParse(`name => {name}
`, expect);
+ const code = makeGetBound(template);
+
+ expect(code).toMatchInlineSnapshot(`
+ "const getBound = renderer('904ca237ee', targets, bind, false, );
+ "
+ `);
+ });
+
+ test('props and elements', ({ expect }) => {
+ const template = preParse(`const t =
+ {"Greeting"} hey {"Azoth"}!
+
;`, expect);
+ const code = makeGetBound(template);
+ expect(code).toMatchInlineSnapshot(
+ `
+ "const getBound = renderer('5252cfebed', targets, bind, false,
+ hey !
+
);
+ "
+ `
+ );
+ });
+
+ test('option noContent: true', ({ getTemplate, expect }) => {
+ const template = getTemplate(`name => {name}
`);
+ const code = makeGetBound(template, { noContent: true });
+
+ expect(code).toMatchInlineSnapshot(`
+ "const getBound = renderer('904ca237ee', targets, bind, false);
+ "
+ `);
+ });
+
+
+
+});
+
+describe('bind generator', () => {
+
+ beforeEach(context => {
+ context.compile = code => {
+ const template = preParse(code, context.expect);
+ return RenderGenerator.generate(template).code;
+ };
+ });
+
+ test('simple', ({ compile, expect }) => {
+ const code = compile(`name => {name}
`);
+ expect(code).toMatchInlineSnapshot(`
+ "function bind(ts) {
+ const t0 = ts[0];
+ return (v0) => {
+ compose(t0, v0);
+ };
+ }
+ "
+ `);
+ });
+
+ test('props and elements', ({ compile, expect }) => {
+ const code = compile(`const t =
+ {"Greeting"} hey {"Azoth"}!
+
;`);
+ expect(code).toMatchInlineSnapshot(
+ `
+ "function bind(ts) {
+ const t0 = ts[0], t1 = ts[1], t2 = ts[2];
+ return (v0, v1, v2) => {
+ t0.className = v0;
+ compose(t1, v1);
+ compose(t2, v2);
+ };
+ }
+ "
+ `
+ );
+ });
+});
+
+describe('render generator', () => {
+
+ beforeEach(context => {
+ context.compile = code => {
+ const template = preParse(code, context.expect);
+ return makeRender(template);
+ };
+ });
+
+ test('simple', ({ compile, expect }) => {
+ const code = compile(`name => {name}
`);
+ expect(code).toMatchInlineSnapshot(`
+ "function renderDOM(p0) {
+ const [root, bind] = getBound();
+ bind(p0);
+ return root;
+ }
+ "
+ `);
+ });
+
+ test('props and elements', ({ compile, expect }) => {
+ const code = compile(`const t =
+ {"Greeting"} hey {"Azoth"}!
+
;`);
+ expect(code).toMatchInlineSnapshot(
+ `
+ "function renderDOM(p0,p1,p2) {
+ const [root, bind] = getBound();
+ bind(p0,p1,p2);
+ return root;
+ }
+ "
+ `
+ );
+ });
+});
\ No newline at end of file
diff --git a/packages/runtime/renderer/controller.test.js b/packages/runtime/renderer/controller.test.js
index 8c02237..d73064d 100644
--- a/packages/runtime/renderer/controller.test.js
+++ b/packages/runtime/renderer/controller.test.js
@@ -1,8 +1,7 @@
import { describe, test, beforeEach, beforeAll } from 'vitest';
import { compose } from '../compose/compose.js';
import {
- get,
- render,
+ renderer,
Controller,
Updater,
clearBind,
@@ -10,8 +9,17 @@ import {
} from './renderer.js';
describe('dom render', () => {
+
+ let getBound = null;
beforeAll(() => {
RenderService.useDOMEngine();
+ getBound = renderer(
+ 'id',
+ getTargets,
+ makeBind,
+ false,
+ ``,
+ );
});
function getTargets(r, boundEls) {
@@ -26,13 +34,7 @@ describe('dom render', () => {
};
function renderDOM(p0) {
- const [root, bind] = render(
- 'id',
- getTargets,
- makeBind,
- false,
- ``,
- );
+ const [root, bind] = getBound();
bind(p0);
return root;
}
@@ -73,8 +75,16 @@ describe('dom render', () => {
describe('html render', () => {
const flatRender = node => node.flat().join('');
+ let getBound = null;
beforeAll(() => {
RenderService.useHTMLEngine();
+ getBound = renderer(
+ 'id',
+ getTargets,
+ makeBind,
+ false,
+ [``, `
`],
+ );
});
function getTargets(r, boundEls) {
@@ -87,14 +97,9 @@ describe('html render', () => {
t0[0] = p0;
};
};
+
function renderHTML(p0) {
- const [root, bind] = render(
- 'id',
- getTargets,
- makeBind,
- false,
- [``, `
`],
- );
+ const [root, bind] = getBound();
bind(p0);
return root;
}
diff --git a/packages/runtime/renderer/dom-renderer.test.js b/packages/runtime/renderer/dom-renderer.test.js
index 5610645..216d0f2 100644
--- a/packages/runtime/renderer/dom-renderer.test.js
+++ b/packages/runtime/renderer/dom-renderer.test.js
@@ -4,10 +4,10 @@ import { DOMRenderer } from './dom-renderer.js';
describe('DOM isFragment', () => {
test('element root w/ false and true', async ({ expect }) => {
- const [div] = DOMRenderer.createTemplate('element-root', false, `text
`)();
+ const [div] = DOMRenderer.createTemplate('element-root', `text
`, false)();
expect(div).toBeInstanceOf(HTMLDivElement);
- const [fragment] = DOMRenderer.createTemplate('fragment-root', true, `text
`)();
+ const [fragment] = DOMRenderer.createTemplate('fragment-root', `text
`, true)();
expect(fragment).toBeInstanceOf(DocumentFragment);
});
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 7fdfc63..8846f2d 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -52,32 +52,35 @@ function inject(node, callback) {
}
}
-export function render(id, targets, makeBind, isFragment, content) {
- let source = get(id, isFragment, content);
- let bind = null;
- let boundEls = null;
- let node = injectable.at(-1); // peek!
-
- // TODO: test injectable is right template id type
-
- if(node) bind = bindings.get(node);
- if(bind) return [node, bind];
-
- // use case would be list component optimize by
- // not keeping bind functions,
- // honestly not sure this really needed, the
- // overhead is small as it is simple function
- if(node) boundEls = renderEngine.bound(node);
- else {
- // (destructured re-assignment)
- ([node, boundEls] = source());
- }
-
- const nodes = targets(node, boundEls);
- bind = makeBind(nodes);
-
- bindings.set(node, bind);
- return [node, bind];
+export function renderer(id, targets, makeBind, isFragment, content) {
+ const create = get(id, isFragment, content);
+
+ return function getBound() {
+ let bind = null;
+ let boundEls = null;
+ let node = injectable.at(-1); // peek!
+
+ // TODO: test injectable is right template id type
+
+ if(node) bind = bindings.get(node);
+ if(bind) return [node, bind];
+
+ // Honestly not sure this really needed,
+ // use case would be list component optimize by
+ // not keeping bind functions?
+ // overhead is small as it is simple function
+ if(node) boundEls = renderEngine.bound(node);
+ else {
+ // (destructuring re-assignment)
+ ([node, boundEls] = create());
+ }
+
+ const nodes = targets(node, boundEls);
+ bind = makeBind(nodes);
+
+ bindings.set(node, bind);
+ return [node, bind];
+ };
}
export class Controller {
diff --git a/packages/vite-plugin/index.js b/packages/vite-plugin/index.js
index 273ecf8..8ddc96c 100644
--- a/packages/vite-plugin/index.js
+++ b/packages/vite-plugin/index.js
@@ -17,7 +17,7 @@ export default function azothPlugin(options) {
name: 'azoth-jsx',
enforce: 'pre',
- config(config, { command: cmd }) {
+ config(_config, { command: cmd }) {
command = cmd;
},
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 5b9165b..d125432 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,4 +1,3 @@
-# azoth/pnpm-workspace.yaml
packages:
- "./packages/*"
- "./sandbox"
From 2eeedf951cb326f27a2263a9c4ee8502b511dabf Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Wed, 20 Mar 2024 16:25:12 -0700
Subject: [PATCH 17/23] template assets all generating
---
packages/compiler/transform/BindGenerator.js | 279 ------------------
packages/compiler/transform/GeneratorBase.js | 3 +-
.../compiler/transform/RenderGenerator.js | 24 --
...{AssetsGenerator.js => template-assets.js} | 2 +-
...odules.test.js => template-assets.test.js} | 3 +-
5 files changed, 3 insertions(+), 308 deletions(-)
delete mode 100644 packages/compiler/transform/BindGenerator.js
rename packages/compiler/transform/{AssetsGenerator.js => template-assets.js} (99%)
rename packages/compiler/transform/{modules.test.js => template-assets.test.js} (98%)
diff --git a/packages/compiler/transform/BindGenerator.js b/packages/compiler/transform/BindGenerator.js
deleted file mode 100644
index acb0d77..0000000
--- a/packages/compiler/transform/BindGenerator.js
+++ /dev/null
@@ -1,279 +0,0 @@
-import { Generator, writeNextLine } from './GeneratorBase.js';
-import { isValidESIdentifier } from 'is-valid-es-identifier';
-import { generateWith } from '../compiler.js';
-
-const OPENING_TYPES = {
- JSXOpeningElement: true,
- JSXOpeningFragment: true,
-};
-
-const OPENING_PROP_BY_TYPE = {
- JSXElement: 'openingElement',
- JSXFragment: 'openingFragment',
-};
-
-function getOpening(element) {
- const { type } = element;
- if(OPENING_TYPES[type]) return element;
-
- const prop = OPENING_PROP_BY_TYPE[type];
- if(prop) return element[prop];
-
- throw new TypeError(`Unexpected binding element type "${element.type}"`);
-}
-
-const ROOT = 'r';
-const TARGETS = 'ts';
-const TARGET = 't';
-const VALUE = 'v';
-
-export class BindGenerator extends Generator {
- static generate(template) {
- const generator = new BindGenerator(template);
- return generateWith(generator, template.node);
- }
-
- constructor(template) {
- super();
- this.template = template;
- }
-
- JSXFragment(_node, state) {
- this.JSXTemplate(state);
- }
-
- JSXElement(_node, state) {
- this.JSXTemplate(state);
- }
-
- JSXTemplate(state) {
- const { template } = this;
- // Short-circuit templates
- const { isStatic, node: root } = template;
- const { isComponent } = root;
- if(isStatic || isComponent) {
- if(isComponent) this.CreateElement(root, state);
- else if(isStatic) this.StaticRoot(template, state);
- return;
- }
-
- this.Targets(template, state);
- this.Bindings(template, state);
- this.Render(template, state);
- }
-
- JSXExpressionContainer({ expression }, state) {
- this[expression.type](expression, state);
- }
-
- JSXIdentifier(identifier, state) {
- state.write(identifier.name, identifier);
- }
-
- StaticRoot(template, state) {
- this.TemplateRenderer(template, state);
- if(!template.isEmpty) state.write(`[0]`, template.node); // dom root
- }
-
- TemplateRenderer({ id, isEmpty, isDomFragment, node }, state) {
- if(isEmpty) {
- state.write('null', node);
- return;
- }
- state.write(`t${id}`, node);
- state.write(`(`);
- // TODO: this should go in template module
- if(isDomFragment) state.write('true');
- state.write(`)`);
- }
-
- Targets(template, state) {
- const { boundElements, bindings } = template;
- const { length: elLength } = boundElements;
-
- state.write(`function targets(`);
- state.write(ROOT);
- if(elLength) state.write(`, ${TARGETS}`);
- state.write(') {');
-
- state.indentLevel++;
- writeNextLine(state);
-
- state.write(`return [`);
- for(let i = 0; i < bindings.length; i++) {
- const { element, type, index, node } = bindings[i];
- const { isRoot, queryIndex } = element;
- const target = isRoot ? ROOT : `${TARGETS}[${queryIndex}]`;
-
- if(i !== 0) state.write(', ');
-
- if(type !== 'child') {
- state.write(`${target}`);
- continue;
- }
-
- const opening = getOpening(element, node);
- state.write(`${target}.childNodes`, opening.name);
- state.write(`[${index}]`, node);
- }
- state.write(`];`);
-
- state.indentLevel--;
- writeNextLine(state);
- state.write(`}`);
- state.write(state.lineEnd);
- }
-
- Render(template, state) {
- const { bindings, isDomFragment } = template;
-
- const params = [];
- for(let i = 0; i < bindings.length; i++) {
- params.push(`${VALUE}${i}`);
- }
-
- state.write(`function renderDOM(${params.join(', ')}) {`);
- state.indentLevel++;
- writeNextLine(state);
-
- state.write(`const [root, bind] = render('id', targets, bind, ${isDomFragment});`);
- writeNextLine(state);
- state.write(`bind(${params.join(', ')});`);
- writeNextLine(state);
- state.write(`return root;`);
-
- state.indentLevel--;
- writeNextLine(state);
- state.write(`}`);
- state.write(state.lineEnd);
- }
-
- Bindings(template, state) {
- const { bindings } = template;
-
- state.write(`function bind(${TARGETS}) {`);
- state.indentLevel++;
- writeNextLine(state);
-
- const targets = [];
- const params = [];
- for(let i = 0; i < bindings.length; i++) {
- targets.push(`${TARGET}${i} = ${TARGETS}[${i}]`);
- params.push(`${VALUE}${i}`);
- }
-
- state.write(`const ${targets.join(', ')};`);
- writeNextLine(state);
- state.write(`return (${params.join(', ')}) => {`);
- state.indentLevel++;
-
- for(let i = 0; i < bindings.length; i++) {
- const { element, type, node, expr } = bindings[i];
- writeNextLine(state);
-
- if(!this[expr.type]) {
- throw new TypeError(`Unexpected Binding expression AST type "${expr.type}"`);
- }
-
- if(node.isComponent) {
- this.ComposeElement(node, expr, i, state);
- continue;
- }
- if(type === 'child') {
- this.Compose(node, expr, i, state);
- continue;
- }
- if(type === 'prop') {
- this.BindingProp(node, expr, i, element, state);
- continue;
- }
-
- const message = `Unexpected binding type "${type}", expected "child" or "prop"`;
- throw new Error(message);
- }
-
- state.indentLevel--;
- writeNextLine(state);
- state.write(`};`);
- state.indentLevel--;
- writeNextLine(state);
- state.write(`}`);
-
- state.write(state.lineEnd);
- }
-
- Compose(node, expr, index, state) {
- state.write(`compose(`, node);
- state.write(`${TARGET}${index}, `, node);
- state.write(`${VALUE}${index}`, expr);
- // this[expr.type](expr, state);
- state.write(`);`);
- }
-
- ComposeElement(node, expr, index, state) {
- state.write(`composeElement(`, node);
- state.write(`${TARGET}${index}, `);
- this.CompleteElement(node, expr, state);
- state.write(`);`);
- }
-
- CreateElement(node, state) {
- state.write(`createElement(`, node);
- this.CompleteElement(node, node.componentExpr, state);
- state.write(`)`);
- }
-
- CompleteElement({ props, slotFragment }, expr, state) {
- this[expr.type](expr, state);
- if(props?.length) {
- this.ComponentProps(props, state);
- }
- else if(slotFragment) state.write(`, null`);
-
- if(slotFragment) {
- state.write(', ');
- this.JSXTemplate(slotFragment, state);
- }
- }
-
- ComponentProps(props, state) {
- state.write(`, {`);
- for(let i = 0; i < props.length; i++) {
- const { node, expr } = props[i];
- // TODO: Dom lookup, JS .prop v['prop'], etc.
- // refactor with code below
- state.write(` `);
- state.write(node.name.name, node.name);
- state.write(`: `);
- this[expr.type](expr, state);
- state.write(`,`);
- }
- state.write(` }`);
- }
-
- BindingProp(node, expr, index, element, state) {
- const { openingElement, openingFragment } = element;
- const opening = openingElement ?? openingFragment;
- state.write(`${TARGET}${index}`, opening.name);
- // TODO: more property validation
- const identity = node.name;
- const propName = identity.name;
- // TODO: refactor with component props
- if(isValidESIdentifier(propName)) {
- state.write(`.`);
- state.write(propName, node.name);
- }
- else {
- state.write(`["`, node.name);
- state.write(propName, node.name);
- state.write(`"]`);
- }
-
- /* expression */
- state.write(` = `);
- // this[expr.type](expr, state);
- state.write(`${VALUE}${index}`, expr);
- state.write(`;`);
- }
-}
-
diff --git a/packages/compiler/transform/GeneratorBase.js b/packages/compiler/transform/GeneratorBase.js
index e756c0b..639e4b1 100644
--- a/packages/compiler/transform/GeneratorBase.js
+++ b/packages/compiler/transform/GeneratorBase.js
@@ -1,6 +1,6 @@
import { GENERATOR } from 'astring';
-// enable extending as es6 class
+// enables extending as es6 class
export function Generator() { }
Generator.prototype = GENERATOR;
@@ -9,4 +9,3 @@ export function writeNextLine(state) {
state.write(lineEnd);
state.write(indent.repeat(state.indentLevel));
}
-
diff --git a/packages/compiler/transform/RenderGenerator.js b/packages/compiler/transform/RenderGenerator.js
index 3901601..7533aee 100644
--- a/packages/compiler/transform/RenderGenerator.js
+++ b/packages/compiler/transform/RenderGenerator.js
@@ -38,30 +38,6 @@ export class RenderGenerator extends Generator {
state.write(identifier.name, identifier);
}
- // Render(template, state) {
- // const { bindings, isDomFragment } = template;
-
- // const params = [];
- // for(let i = 0; i < bindings.length; i++) {
- // params.push(`${VALUE}${i}`);
- // }
-
- // state.write(`function renderDOM(${params.join(', ')}) {`);
- // state.indentLevel++;
- // writeNextLine(state);
-
- // state.write(`const [root, bind] = render('id', targets, bind, ${isDomFragment});`);
- // writeNextLine(state);
- // state.write(`bind(${params.join(', ')});`);
- // writeNextLine(state);
- // state.write(`return root;`);
-
- // state.indentLevel--;
- // writeNextLine(state);
- // state.write(`}`);
- // state.write(state.lineEnd);
- // }
-
Bindings(state) {
const bindings = this.#bindings;
diff --git a/packages/compiler/transform/AssetsGenerator.js b/packages/compiler/transform/template-assets.js
similarity index 99%
rename from packages/compiler/transform/AssetsGenerator.js
rename to packages/compiler/transform/template-assets.js
index f74a9f5..d4798eb 100644
--- a/packages/compiler/transform/AssetsGenerator.js
+++ b/packages/compiler/transform/template-assets.js
@@ -25,4 +25,4 @@ export function makeRender({ bindings: { length } }) {
bind(${params});
return root;
}\n`;
-}
\ No newline at end of file
+}
diff --git a/packages/compiler/transform/modules.test.js b/packages/compiler/transform/template-assets.test.js
similarity index 98%
rename from packages/compiler/transform/modules.test.js
rename to packages/compiler/transform/template-assets.test.js
index ddfdd17..9977463 100644
--- a/packages/compiler/transform/modules.test.js
+++ b/packages/compiler/transform/template-assets.test.js
@@ -1,5 +1,5 @@
/* eslint-disable no-undef */
-import { makeTargets, makeGetBound, makeRender } from './AssetsGenerator.js';
+import { makeTargets, makeGetBound, makeRender } from './template-assets.js';
import { parse, generate as _generate } from '../compiler.js';
import { describe, test, beforeEach } from 'vitest';
import { RenderGenerator } from './RenderGenerator.js';
@@ -12,7 +12,6 @@ function preParse(input, expect) {
return template;
}
-
describe('targets generator', () => {
beforeEach(context => {
From c79d191a681e38dac06a1abc3ba2c3a206176dcd Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Thu, 21 Mar 2024 07:21:06 -0700
Subject: [PATCH 18/23] refactor generators
---
packages/compiler/compiler.test.js | 707 ++----------------
packages/compiler/source-maps.test.js | 150 +---
packages/compiler/transform/Analyzer.js | 6 +-
.../{RenderGenerator.js => BindGenerator.js} | 57 +-
.../compiler/transform/TemplateGenerator.js | 179 +----
...plate-assets.js => template-generators.js} | 1 +
...ts.test.js => template-generators.test.js} | 6 +-
packages/runtime/compose/compose.js | 28 +-
packages/runtime/renderer/controller.test.js | 2 +-
9 files changed, 129 insertions(+), 1007 deletions(-)
rename packages/compiler/transform/{RenderGenerator.js => BindGenerator.js} (67%)
rename packages/compiler/transform/{template-assets.js => template-generators.js} (99%)
rename packages/compiler/transform/{template-assets.test.js => template-generators.test.js} (97%)
diff --git a/packages/compiler/compiler.test.js b/packages/compiler/compiler.test.js
index 3256eec..2ab60de 100644
--- a/packages/compiler/compiler.test.js
+++ b/packages/compiler/compiler.test.js
@@ -8,8 +8,8 @@ const compile = input => {
});
return {
code, map,
- templates: templates.map(({ id, html, isDomFragment, isEmpty, isStatic, imports }) => {
- return { id, html, isDomFragment, isEmpty, isStatic, imports };
+ templates: templates.map(({ id, html, isDomFragment, isEmpty }) => {
+ return { id, html, isDomFragment, isEmpty };
})
};
};
@@ -24,15 +24,9 @@ describe('JSX dom literals', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { ta516887159 } from 'virtual:azoth-templates?id=a516887159';
- const t = (() => {
- const __root = ta516887159()[0];
- const __child1 = __root.childNodes[1];
- __root.className = ("className");
- __compose(__child1, "Azoth");
- return __root;
- })();
+ "import { ta516887159 } from 'virtual:azoth-templates?id=a516887159';
+
+ const t = ta516887159("className","Azoth");
"
`);
expect(templates).toMatchInlineSnapshot(`
@@ -42,12 +36,8 @@ describe('JSX dom literals', () => {
Hello
",
"id": "a516887159",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -73,34 +63,9 @@ describe('JSX dom literals', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { tfdd1a869cf } from 'virtual:azoth-templates?id=fdd1a869cf';
- const t = (() => {
- const [__root, __targets] = tfdd1a869cf();
- const __target0 =__targets[0];
- const __target1 =__targets[1];
- const __target2 =__targets[2];
- const __target3 =__targets[3];
- const __target4 =__targets[4];
- const __target5 =__targets[5];
- const __child1 = __target0.childNodes[0];
- const __child2 = __target1.childNodes[0];
- const __child3 = __target2.childNodes[0];
- const __child4 = __target4.childNodes[1];
- const __child5 = __target4.childNodes[3];
- const __child7 = __target3.childNodes[7];
- const __child8 = __root.childNodes[15];
- __target0.className = ("my-class");
- __compose(__child1, "felix");
- __compose(__child2, "this is");
- __compose(__child3, "azoth");
- __compose(__child4, "two");
- __compose(__child5, "and...");
- __target5.className = ("span-class");
- __compose(__child7, "ul-footer");
- __compose(__child8, "footer");
- return __root;
- })();
+ "import { tfdd1a869cf } from 'virtual:azoth-templates?id=fdd1a869cf';
+
+ const t = tfdd1a869cf("my-class","felix","this is","azoth","two","and...","span-class","ul-footer","footer");
"
`);
@@ -123,12 +88,8 @@ describe('JSX dom literals', () => {
",
"id": "fdd1a869cf",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -148,14 +109,7 @@ describe('JSX dom literals', () => {
expect(code).toMatchInlineSnapshot(`
"import { t10073da0ec } from 'virtual:azoth-templates?id=10073da0ec';
- const t = (() => {
- const __root = t10073da0ec()[0];
- __root.className = ("className");
- __root.name = ("name");
- __root["class"] = ("class");
- __root["class-name"] = ("class-name");
- return __root;
- })();
+ const t = t10073da0ec("className","name","class","class-name");
"
`);
@@ -164,10 +118,8 @@ describe('JSX dom literals', () => {
{
"html": "",
"id": "10073da0ec",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -181,14 +133,9 @@ describe('nested context', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t8dae88052a, t1a78cbe949 } from 'virtual:azoth-templates?id=8dae88052a&id=1a78cbe949';
- (() => {
- const __root = t8dae88052a()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, t1a78cbe949()[0]);
- return __root;
- })();
+ "import { t8dae88052a, t1a78cbe949 } from 'virtual:azoth-templates?id=8dae88052a&id=1a78cbe949';
+
+ t8dae88052a(t1a78cbe949());
"
`);
@@ -197,20 +144,14 @@ describe('nested context', () => {
{
"html": "",
"id": "8dae88052a",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "
",
"id": "1a78cbe949",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
},
]
`);
@@ -227,7 +168,7 @@ describe('template optimizations', () => {
expect(code).toMatchInlineSnapshot(`
"import { t5bf3d2f523 } from 'virtual:azoth-templates?id=5bf3d2f523';
- const template = t5bf3d2f523()[0];
+ const template = t5bf3d2f523();
"
`);
@@ -236,86 +177,8 @@ describe('template optimizations', () => {
{
"html": "Hello
",
"id": "5bf3d2f523",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
- },
- ]
- `);
- });
-});
-
-describe('surrounding code integration', () => {
-
- test('ArrowFunctionExpression: implicit return is block return', ({ expect }) => {
- const input = `
- const template = (text) => {text}
- `;
-
- const { code, templates } = compile(input);
-
- expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t904ca237ee } from 'virtual:azoth-templates?id=904ca237ee';
- const template = text => {
- const __root = t904ca237ee()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, text);
- return __root;
- };
- "
- `);
-
- expect(templates).toMatchInlineSnapshot(`
- [
- {
- "html": "",
- "id": "904ca237ee",
- "imports": [
- "compose",
- ],
- "isDomFragment": false,
- "isEmpty": false,
- "isStatic": false,
- },
- ]
- `);
- });
-
- test('ReturnStatement: injects statements before, returns root', ({ expect }) => {
- const input = `
- function template(text) {
- const format = 'text' + '!';
- return {text}
;
- }
- `;
-
- const { code, templates } = compile(input);
-
- expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t904ca237ee } from 'virtual:azoth-templates?id=904ca237ee';
- function template(text) {
- const format = 'text' + '!';
- const __root = t904ca237ee()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, text);
- return __root;
- }
- "
- `);
- expect(templates).toMatchInlineSnapshot(`
- [
- {
- "html": "",
- "id": "904ca237ee",
- "imports": [
- "compose",
- ],
- "isDomFragment": false,
- "isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -323,38 +186,20 @@ describe('surrounding code integration', () => {
});
describe('fragments', () => {
- test('<> ... > basic', ({ expect }) => {
+ test('empty', ({ expect }) => {
const input = `
const fragment = <>
>;
- const single = <>
>;
- const fragInFrag = <><>
>>;
- const fragInFragCompose = <><>{x}>>;
- const empty = <>>;
const compose = <>{x}>;
- const text = <>text>;
+ const empty = <>>;
`;
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { tc203fe7dcd, t1a78cbe949, tc084de4382, t1cb251ec0d } from 'virtual:azoth-templates?id=c203fe7dcd&id=1a78cbe949&id=c084de4382&id=1cb251ec0d';
- const fragment = tc203fe7dcd(true)[0];
- const single = t1a78cbe949()[0];
- const fragInFrag = t1a78cbe949()[0];
- const fragInFragCompose = (() => {
- const __root = tc084de4382(true)[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, x);
- return __root;
- })();
+ "import { tc203fe7dcd, tc084de4382 } from 'virtual:azoth-templates?id=c203fe7dcd&id=c084de4382';
+
+ const fragment = tc203fe7dcd();
+ const compose = tc084de4382(x);
const empty = null;
- const compose = (() => {
- const __root = tc084de4382(true)[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, x);
- return __root;
- })();
- const text = t1cb251ec0d(true)[0];
"
`);
@@ -363,62 +208,20 @@ describe('fragments', () => {
{
"html": "
",
"id": "c203fe7dcd",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
- },
- {
- "html": "
",
- "id": "1a78cbe949",
- "imports": [],
- "isDomFragment": false,
- "isEmpty": false,
- "isStatic": true,
- },
- {
- "html": "
",
- "id": "1a78cbe949",
- "imports": [],
- "isDomFragment": false,
- "isEmpty": false,
- "isStatic": true,
},
{
"html": "",
"id": "c084de4382",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "",
- "imports": [],
"isDomFragment": true,
"isEmpty": true,
- "isStatic": true,
- },
- {
- "html": "",
- "id": "c084de4382",
- "imports": [
- "compose",
- ],
- "isDomFragment": true,
- "isEmpty": false,
- "isStatic": false,
- },
- {
- "html": "text",
- "id": "1cb251ec0d",
- "imports": [],
- "isDomFragment": true,
- "isEmpty": false,
- "isStatic": true,
},
]
`);
@@ -455,25 +258,15 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { tc203fe7dcd, t1a78cbe949, tc084de4382, t6c72de769d } from 'virtual:azoth-templates?id=c203fe7dcd&id=1a78cbe949&id=c084de4382&id=6c72de769d';
- const fragment = tc203fe7dcd(true)[0];
- const single = t1a78cbe949()[0];
- const fragInFrag = t1a78cbe949()[0];
- const fragInFragCompose = (() => {
- const __root = tc084de4382(true)[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, x);
- return __root;
- })();
+ "import { tc203fe7dcd, t1a78cbe949, tc084de4382, t6c72de769d } from 'virtual:azoth-templates?id=c203fe7dcd&id=1a78cbe949&id=c084de4382&id=6c72de769d';
+
+ const fragment = tc203fe7dcd();
+ const single = t1a78cbe949();
+ const fragInFrag = t1a78cbe949();
+ const fragInFragCompose = tc084de4382(x);
const empty = null;
- const compose = (() => {
- const __root = tc084de4382(true)[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, x);
- return __root;
- })();
- const text = t6c72de769d(true)[0];
+ const compose = tc084de4382(x);
+ const text = t6c72de769d();
"
`);
@@ -482,64 +275,46 @@ describe('fragments', () => {
{
"html": "
",
"id": "c203fe7dcd",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "
",
"id": "1a78cbe949",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "
",
"id": "1a78cbe949",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "",
"id": "c084de4382",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "",
- "imports": [],
"isDomFragment": true,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "",
"id": "c084de4382",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "
text
",
"id": "6c72de769d",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
},
]
`);
@@ -560,22 +335,12 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t1a78cbe949, tc084de4382 } from 'virtual:azoth-templates?id=1a78cbe949&id=c084de4382';
- const start = t1a78cbe949()[0];
- const end = t1a78cbe949()[0];
- const composeStart = (() => {
- const __root = tc084de4382(true)[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, x);
- return __root;
- })();
- const composeEnd = (() => {
- const __root = tc084de4382(true)[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, x);
- return __root;
- })();
+ "import { t1a78cbe949, tc084de4382 } from 'virtual:azoth-templates?id=1a78cbe949&id=c084de4382';
+
+ const start = t1a78cbe949();
+ const end = t1a78cbe949();
+ const composeStart = tc084de4382(x);
+ const composeEnd = tc084de4382(x);
"
`);
@@ -584,38 +349,26 @@ describe('fragments', () => {
{
"html": "
",
"id": "1a78cbe949",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "
",
"id": "1a78cbe949",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "",
"id": "c084de4382",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "c084de4382",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -633,18 +386,13 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t653a3aad80, tdcaa233028, t2dc1738d5c, t0cf31b2c28, t5bc2a159b1 } from 'virtual:azoth-templates?id=653a3aad80&id=dcaa233028&id=2dc1738d5c&id=0cf31b2c28&id=5bc2a159b1';
- const fragment = t653a3aad80(true)[0];
- const single = tdcaa233028(true)[0];
- const fragInFrag = t2dc1738d5c(true)[0];
- const spaces = t0cf31b2c28(true)[0];
- const compose = (() => {
- const __root = t5bc2a159b1(true)[0];
- const __child0 = __root.childNodes[1];
- __compose(__child0, x);
- return __root;
- })();
+ "import { t653a3aad80, tdcaa233028, t2dc1738d5c, t0cf31b2c28, t5bc2a159b1 } from 'virtual:azoth-templates?id=653a3aad80&id=dcaa233028&id=2dc1738d5c&id=0cf31b2c28&id=5bc2a159b1';
+
+ const fragment = t653a3aad80();
+ const single = tdcaa233028();
+ const fragInFrag = t2dc1738d5c();
+ const spaces = t0cf31b2c28();
+ const compose = t5bc2a159b1(x);
"
`);
@@ -653,44 +401,32 @@ describe('fragments', () => {
{
"html": "
",
"id": "653a3aad80",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "
",
"id": "dcaa233028",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "
",
"id": "2dc1738d5c",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
},
{
"html": " ",
"id": "0cf31b2c28",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
},
{
"html": " ",
"id": "5bc2a159b1",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -704,14 +440,9 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { tfaf808e6cc } from 'virtual:azoth-templates?id=faf808e6cc';
- const fragment = (() => {
- const __root = tfaf808e6cc(true)[0];
- const __child0 = __root.childNodes[1];
- __compose(__child0, "two");
- return __root;
- })();
+ "import { tfaf808e6cc } from 'virtual:azoth-templates?id=faf808e6cc';
+
+ const fragment = tfaf808e6cc("two");
"
`);
@@ -720,12 +451,8 @@ describe('fragments', () => {
{
"html": "onethree",
"id": "faf808e6cc",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -745,15 +472,10 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { tccaa44c114, t681310be49 } from 'virtual:azoth-templates?id=ccaa44c114&id=681310be49';
- const extraneous = tccaa44c114()[0];
- const childNodeIndex = (() => {
- const __root = t681310be49()[0];
- const __child0 = __root.childNodes[3];
- __compose(__child0, "expect index 3");
- return __root;
- })();
+ "import { tccaa44c114, t681310be49 } from 'virtual:azoth-templates?id=ccaa44c114&id=681310be49';
+
+ const extraneous = tccaa44c114();
+ const childNodeIndex = t681310be49("expect index 3");
"
`);
@@ -762,10 +484,8 @@ describe('fragments', () => {
{
"html": "
",
"id": "ccaa44c114",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "
@@ -774,12 +494,8 @@ describe('fragments', () => {
",
"id": "681310be49",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -791,19 +507,9 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { tef691fa27a } from 'virtual:azoth-templates?id=ef691fa27a';
- const App = (() => {
- const [__root, __targets] = tef691fa27a(true);
- const __target0 =__targets[0];
- const __child0 = __root.childNodes[0];
- const __child1 = __target0.childNodes[0];
- const __child2 = __root.childNodes[2];
- __compose(__child0, 'foo');
- __compose(__child1, 'bar');
- __compose(__child2, 'qux');
- return __root;
- })();
+ "import { tef691fa27a } from 'virtual:azoth-templates?id=ef691fa27a';
+
+ const App = tef691fa27a('foo','bar','qux');
"
`);
@@ -812,49 +518,8 @@ describe('fragments', () => {
{
"html": "",
"id": "ef691fa27a",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
- },
- ]
- `);
-
- });
-});
-
-describe('template root', () => {
- test('single element is root', ({ expect }) => {
- const input = `
- const div = {hello}
;
- `;
- const { code, templates } = compile(input);
-
- expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t8dae88052a } from 'virtual:azoth-templates?id=8dae88052a';
- const div = (() => {
- const __root = t8dae88052a()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, hello);
- return __root;
- })();
- "
- `);
-
- expect(templates).toMatchInlineSnapshot(`
- [
- {
- "html": "",
- "id": "8dae88052a",
- "imports": [
- "compose",
- ],
- "isDomFragment": false,
- "isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -873,11 +538,7 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { t1cdf0d646f } from 'virtual:azoth-templates?id=1cdf0d646f';
- document.body.append((() => {
- const __root = t1cdf0d646f()[0];
- __root.prop = (prop);
- return __root;
- })());
+ document.body.append(t1cdf0d646f(prop));
"
`);
@@ -886,10 +547,8 @@ describe('element composition', () => {
{
"html": "",
"id": "1cdf0d646f",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -915,22 +574,14 @@ describe('element composition', () => {
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
]
`);
@@ -946,16 +597,9 @@ describe('element composition', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __composeElement, __compose } from 'azoth/runtime';
+ "import { __createElement } from 'azoth/runtime';
import { t2288998344 } from 'virtual:azoth-templates?id=2288998344';
- const component = (() => {
- const __root = t2288998344()[0];
- const __child0 = __root.childNodes[1];
- const __child1 = __root.childNodes[3];
- __composeElement(__child0, Component, { prop: value, prop2: "literal", });
- __composeElement(__child1, GotNoPropsAsYouCanSee);
- return __root;
- })();
+ const component = t2288998344(__createElement(Component, { prop: value, prop2: "literal", }),__createElement(GotNoPropsAsYouCanSee));
"
`);
expect(templates).toMatchInlineSnapshot(`
@@ -966,13 +610,8 @@ describe('element composition', () => {
",
"id": "2288998344",
- "imports": [
- "composeElement",
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -991,18 +630,11 @@ describe('element composition', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __createElement, __compose } from 'azoth/runtime';
+ "import { __createElement } from 'azoth/runtime';
import { t2288998344 } from 'virtual:azoth-templates?id=2288998344';
const $A = __createElement(A);
const $B = __createElement(B);
- const dom = (() => {
- const __root = t2288998344()[0];
- const __child0 = __root.childNodes[1];
- const __child1 = __root.childNodes[3];
- __compose(__child0, $A);
- __compose(__child1, $B);
- return __root;
- })();
+ const dom = t2288998344($A,$B);
"
`);
expect(templates).toMatchInlineSnapshot(`
@@ -1010,22 +642,14 @@ describe('element composition', () => {
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "
@@ -1033,50 +657,8 @@ describe('element composition', () => {
",
"id": "2288998344",
- "imports": [
- "compose",
- ],
- "isDomFragment": false,
- "isEmpty": false,
- "isStatic": false,
- },
- ]
- `);
-
- });
-
- test('return keyword in Function with static jsx', ({ expect }) => {
- const input = `
- function Surprise() {
- return
- Guess What...
- surprise!
- ;
- }
- `;
-
- const { code, templates } = compile(input);
-
- expect(code).toMatchInlineSnapshot(`
- "import { t92cc583556 } from 'virtual:azoth-templates?id=92cc583556';
-
- function Surprise() {
- return t92cc583556()[0];
- }
- "
- `);
- expect(templates).toMatchInlineSnapshot(`
- [
- {
- "html": "
- Guess What...
- surprise!
- ",
- "id": "92cc583556",
- "imports": [],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": true,
},
]
`);
@@ -1102,43 +684,14 @@ describe('element composition', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __createElement, __compose } from 'azoth/runtime';
+ "import { __createElement } from 'azoth/runtime';
import { t904ca237ee, t1cb251ec0d, t9b045328fb } from 'virtual:azoth-templates?id=904ca237ee&id=1cb251ec0d&id=9b045328fb';
- const c = __createElement(Component, null, (() => {
- const __root = t904ca237ee()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, "test");
- return __root;
- })());
- const cTrim = __createElement(Component, null, (() => {
- const __root = t904ca237ee()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, "test");
- return __root;
- })());
- const cTrimStart = __createElement(Component, null, (() => {
- const __root = t904ca237ee()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, "test");
- return __root;
- })());
- const cTrimEnd = __createElement(Component, null, (() => {
- const __root = t904ca237ee()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, "test");
- return __root;
- })());
- const cText = __createElement(Component, null, t1cb251ec0d(true)[0]);
- const cFrag = __createElement(Component, null, (() => {
- const [__root, __targets] = t9b045328fb(true);
- const __target0 =__targets[0];
- const __target1 =__targets[1];
- const __child0 = __target0.childNodes[0];
- const __child1 = __target1.childNodes[0];
- __compose(__child0, 1);
- __compose(__child1, 2);
- return __root;
- })());
+ const c = __createElement(Component, null, t904ca237ee("test"));
+ const cTrim = __createElement(Component, null, t904ca237ee("test"));
+ const cTrimStart = __createElement(Component, null, t904ca237ee("test"));
+ const cTrimEnd = __createElement(Component, null, t904ca237ee("test"));
+ const cText = __createElement(Component, null, t1cb251ec0d());
+ const cFrag = __createElement(Component, null, t9b045328fb(1,2));
"
`);
@@ -1147,121 +700,75 @@ describe('element composition', () => {
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "",
"id": "904ca237ee",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "",
"id": "904ca237ee",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "",
"id": "904ca237ee",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "",
"id": "904ca237ee",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "text",
"id": "1cb251ec0d",
- "imports": [],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": true,
},
{
"html": "",
"id": "",
- "imports": [
- "createElement",
- ],
"isDomFragment": false,
"isEmpty": true,
- "isStatic": true,
},
{
"html": "
",
"id": "9b045328fb",
- "imports": [
- "compose",
- ],
"isDomFragment": true,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -1279,22 +786,10 @@ describe('render and composition cases', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t62831a5152, t8dc93cc914 } from 'virtual:azoth-templates?id=62831a5152&id=8dc93cc914';
- const Item = name => {
- const __root = t62831a5152()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, name);
- return __root;
- };
- const Template = () => {
- const __root = t8dc93cc914()[0];
- const __child0 = __root.childNodes[0];
- const __child1 = __root.childNodes[1];
- __compose(__child0, [2, 4, 7].map(Item));
- __compose(__child1, "text");
- return __root;
- };
+ "import { t62831a5152, t8dc93cc914 } from 'virtual:azoth-templates?id=62831a5152&id=8dc93cc914';
+
+ const Item = name => t62831a5152(name);
+ const Template = () => t8dc93cc914([2, 4, 7].map(Item),"text");
"
`);
@@ -1303,58 +798,14 @@ describe('render and composition cases', () => {
{
"html": "",
"id": "62831a5152",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "8dc93cc914",
- "imports": [
- "compose",
- ],
- "isDomFragment": false,
- "isEmpty": false,
- "isStatic": false,
- },
- ]
- `);
-
- });
-
- test('edge case: previously broken esbuild jsx', ({ expect }) => {
- const input = `
- const render = () => Hello {place}
- `;
- const { code, templates } = compile(input);
-
- expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t2b440f4741 } from 'virtual:azoth-templates?id=2b440f4741';
- const render = () => {
- const __root = t2b440f4741()[0];
- const __child1 = __root.childNodes[1];
- __root.className = (category);
- __compose(__child1, place);
- return __root;
- };
- "
- `);
-
- expect(templates).toMatchInlineSnapshot(`
- [
- {
- "html": "Hello ",
- "id": "2b440f4741",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
@@ -1371,21 +822,11 @@ describe('render and composition cases', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { t62831a5152, t25ec157413 } from 'virtual:azoth-templates?id=62831a5152&id=25ec157413';
- const Emoji = ({name}) => {
- const __root = t62831a5152()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, name);
- return __root;
- };
+ "import { t62831a5152, t25ec157413 } from 'virtual:azoth-templates?id=62831a5152&id=25ec157413';
+
+ const Emoji = ({name}) => t62831a5152(name);
const promise = fetchEmojis().then(emojis => emojis.map(Emoji));
- const Emojis = (() => {
- const __root = t25ec157413()[0];
- const __child0 = __root.childNodes[0];
- __compose(__child0, promise);
- return __root;
- })();
+ const Emojis = t25ec157413(promise);
document.body.append(Emojis);
"
`);
@@ -1394,22 +835,14 @@ describe('render and composition cases', () => {
{
"html": "",
"id": "62831a5152",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
{
"html": "",
"id": "25ec157413",
- "imports": [
- "compose",
- ],
"isDomFragment": false,
"isEmpty": false,
- "isStatic": false,
},
]
`);
diff --git a/packages/compiler/source-maps.test.js b/packages/compiler/source-maps.test.js
index 59dd680..835bbc9 100644
--- a/packages/compiler/source-maps.test.js
+++ b/packages/compiler/source-maps.test.js
@@ -12,7 +12,7 @@ test('static one line', ({ expect }) => {
expect(code).toMatchInlineSnapshot(`
"import { tbc5b60ab9f } from 'virtual:azoth-templates?id=bc5b60ab9f';
- const t = tbc5b60ab9f()[0];
+ const t = tbc5b60ab9f();
"
`);
@@ -34,14 +34,6 @@ test('static one line', ({ expect }) => {
"originalLine": 1,
"source": "script.js",
},
- {
- "generatedColumn": 23,
- "generatedLine": 3,
- "name": undefined,
- "originalColumn": 10,
- "originalLine": 1,
- "source": "script.js",
- },
]
`);
});
@@ -50,82 +42,29 @@ test('{...} one line', ({ expect }) => {
const input = `Hello {place}
`;
const { _sourceMap, code } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { ta94b210052 } from 'virtual:azoth-templates?id=a94b210052';
- (() => {
- const __root = ta94b210052()[0];
- const __child0 = __root.childNodes[1];
- __compose(__child0, place);
- return __root;
- })();
+ "import { ta94b210052 } from 'virtual:azoth-templates?id=a94b210052';
+
+ ta94b210052(place);
"
`);
expect(_sourceMap._mappings._array).toMatchInlineSnapshot(`
[
{
- "generatedColumn": 17,
- "generatedLine": 4,
- "name": undefined,
- "originalColumn": 0,
- "originalLine": 1,
- "source": "script.js",
- },
- {
- "generatedColumn": 30,
- "generatedLine": 4,
+ "generatedColumn": 0,
+ "generatedLine": 3,
"name": undefined,
"originalColumn": 0,
"originalLine": 1,
"source": "script.js",
},
- {
- "generatedColumn": 19,
- "generatedLine": 5,
- "name": "div",
- "originalColumn": 1,
- "originalLine": 1,
- "source": "script.js",
- },
- {
- "generatedColumn": 36,
- "generatedLine": 5,
- "name": undefined,
- "originalColumn": 11,
- "originalLine": 1,
- "source": "script.js",
- },
- {
- "generatedColumn": 2,
- "generatedLine": 6,
- "name": undefined,
- "originalColumn": 11,
- "originalLine": 1,
- "source": "script.js",
- },
{
"generatedColumn": 12,
- "generatedLine": 6,
- "name": undefined,
- "originalColumn": 11,
- "originalLine": 1,
- "source": "script.js",
- },
- {
- "generatedColumn": 22,
- "generatedLine": 6,
+ "generatedLine": 3,
"name": "place",
"originalColumn": 12,
"originalLine": 1,
"source": "script.js",
},
- {
- "generatedColumn": 9,
- "generatedLine": 7,
- "name": undefined,
- "originalColumn": 0,
- "originalLine": 1,
- "source": "script.js",
- },
]
`);
});
@@ -138,7 +77,7 @@ test('static three line', ({ expect }) => {
expect(code).toMatchInlineSnapshot(`
"import { te36ec5cf73 } from 'virtual:azoth-templates?id=e36ec5cf73';
- const t = te36ec5cf73()[0];
+ const t = te36ec5cf73();
"
`);
expect(_sourceMap._mappings._array).toMatchInlineSnapshot(`
@@ -159,14 +98,6 @@ test('static three line', ({ expect }) => {
"originalLine": 1,
"source": "script.js",
},
- {
- "generatedColumn": 23,
- "generatedLine": 3,
- "name": undefined,
- "originalColumn": 10,
- "originalLine": 1,
- "source": "script.js",
- },
]
`);
});
@@ -177,14 +108,9 @@ test('{...} three line', ({ expect }) => {
`;
const { _sourceMap, code } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { __compose } from 'azoth/runtime';
- import { tf2d718c3f5 } from 'virtual:azoth-templates?id=f2d718c3f5';
- const t = (() => {
- const __root = tf2d718c3f5()[0];
- const __child0 = __root.childNodes[1];
- __compose(__child0, place);
- return __root;
- })();
+ "import { tf2d718c3f5 } from 'virtual:azoth-templates?id=f2d718c3f5';
+
+ const t = tf2d718c3f5(place);
"
`);
expect(_sourceMap._mappings._array).toMatchInlineSnapshot(`
@@ -198,69 +124,21 @@ test('{...} three line', ({ expect }) => {
"source": "script.js",
},
{
- "generatedColumn": 17,
- "generatedLine": 4,
- "name": undefined,
- "originalColumn": 10,
- "originalLine": 1,
- "source": "script.js",
- },
- {
- "generatedColumn": 30,
- "generatedLine": 4,
+ "generatedColumn": 10,
+ "generatedLine": 3,
"name": undefined,
"originalColumn": 10,
"originalLine": 1,
"source": "script.js",
},
- {
- "generatedColumn": 19,
- "generatedLine": 5,
- "name": "div",
- "originalColumn": 11,
- "originalLine": 1,
- "source": "script.js",
- },
- {
- "generatedColumn": 36,
- "generatedLine": 5,
- "name": undefined,
- "originalColumn": 14,
- "originalLine": 2,
- "source": "script.js",
- },
- {
- "generatedColumn": 2,
- "generatedLine": 6,
- "name": undefined,
- "originalColumn": 14,
- "originalLine": 2,
- "source": "script.js",
- },
- {
- "generatedColumn": 12,
- "generatedLine": 6,
- "name": undefined,
- "originalColumn": 14,
- "originalLine": 2,
- "source": "script.js",
- },
{
"generatedColumn": 22,
- "generatedLine": 6,
+ "generatedLine": 3,
"name": "place",
"originalColumn": 15,
"originalLine": 2,
"source": "script.js",
},
- {
- "generatedColumn": 9,
- "generatedLine": 7,
- "name": undefined,
- "originalColumn": 10,
- "originalLine": 1,
- "source": "script.js",
- },
]
`);
});
diff --git a/packages/compiler/transform/Analyzer.js b/packages/compiler/transform/Analyzer.js
index 7f3fc62..6387eb5 100644
--- a/packages/compiler/transform/Analyzer.js
+++ b/packages/compiler/transform/Analyzer.js
@@ -117,10 +117,6 @@ export class Analyzer {
this.#bindings.push(binding);
- if(binding.type === 'child') {
- this.#imports.add('compose');
- }
-
if(element.isRoot) {
// root can't be a "target", it gets a -1 queryIndex
// to signal bound template root (either el or fragment)
@@ -202,7 +198,7 @@ export class Analyzer {
else {
assessElement(child);
if(child.isComponent) {
- this.#imports.add('composeElement');
+ this.#imports.add('createElement');
this.#bind('child', child, child.componentExpr, i + adj);
}
this[type](child, i + adj);
diff --git a/packages/compiler/transform/RenderGenerator.js b/packages/compiler/transform/BindGenerator.js
similarity index 67%
rename from packages/compiler/transform/RenderGenerator.js
rename to packages/compiler/transform/BindGenerator.js
index 7533aee..091a416 100644
--- a/packages/compiler/transform/RenderGenerator.js
+++ b/packages/compiler/transform/BindGenerator.js
@@ -6,7 +6,7 @@ const TARGETS = 'ts';
const TARGET = 't';
const VALUE = 'v';
-export class RenderGenerator extends Generator {
+export class BindGenerator extends Generator {
static generate(template) {
const generator = new this(template);
return generateWith(generator, template.node);
@@ -19,25 +19,13 @@ export class RenderGenerator extends Generator {
}
JSXFragment(_node, state) {
- this.JSXTemplate(state);
+ this.Bindings(state);
}
JSXElement(_node, state) {
- this.JSXTemplate(state);
- }
-
- JSXTemplate(state) {
this.Bindings(state);
}
- JSXExpressionContainer({ expression }, state) {
- this[expression.type](expression, state);
- }
-
- JSXIdentifier(identifier, state) {
- state.write(identifier.name, identifier);
- }
-
Bindings(state) {
const bindings = this.#bindings;
@@ -99,47 +87,6 @@ export class RenderGenerator extends Generator {
state.write(`);`);
}
- ComposeElement(node, expr, index, state) {
- state.write(`composeElement(`, node);
- state.write(`${TARGET}${index}, `);
- this.CompleteElement(node, expr, state);
- state.write(`);`);
- }
-
- CreateElement(node, state) {
- state.write(`createElement(`, node);
- this.CompleteElement(node, node.componentExpr, state);
- state.write(`)`);
- }
-
- CompleteElement({ props, slotFragment }, expr, state) {
- this[expr.type](expr, state);
- if(props?.length) {
- this.ComponentProps(props, state);
- }
- else if(slotFragment) state.write(`, null`);
-
- if(slotFragment) {
- state.write(', ');
- this.JSXTemplate(slotFragment, state);
- }
- }
-
- ComponentProps(props, state) {
- state.write(`, {`);
- for(let i = 0; i < props.length; i++) {
- const { node, expr } = props[i];
- // TODO: Dom lookup, JS .prop v['prop'], etc.
- // refactor with code below
- state.write(` `);
- state.write(node.name.name, node.name);
- state.write(`: `);
- this[expr.type](expr, state);
- state.write(`,`);
- }
- state.write(` }`);
- }
-
BindingProp(node, expr, index, element, state) {
const { openingElement, openingFragment } = element;
const opening = openingElement ?? openingFragment;
diff --git a/packages/compiler/transform/TemplateGenerator.js b/packages/compiler/transform/TemplateGenerator.js
index f233e53..de5b18b 100644
--- a/packages/compiler/transform/TemplateGenerator.js
+++ b/packages/compiler/transform/TemplateGenerator.js
@@ -80,17 +80,14 @@ export class TemplateGenerator extends Generator {
if(template.id && !uniqueIds.has(template.id)) uniqueIds.add(template.id);
// Short-circuit templates
- const { isStatic, node: root } = template;
- const { isComponent, returnStatement } = root;
- if(isStatic || isComponent) {
- if(returnStatement) state.write(`return `, returnStatement);
- if(isComponent) this.CreateElement(root, state);
- else if(isStatic) this.StaticRoot(template, state);
- if(returnStatement) state.write(`;`);
+ const { node: root } = template;
+ const { isComponent } = root;
+ if(isComponent) {
+ this.CreateElement(root, state);
return;
}
- this.InjectionWrapper(template, state);
+ this.DomLiteral(template, state);
}
// process javascript in {...} exprs,
@@ -103,154 +100,33 @@ export class TemplateGenerator extends Generator {
state.write(identifier.name, identifier);
}
- /* Adopt implicit arrow as containing function */
- ArrowFunctionExpression(node, state) {
- if(node.body?.type === 'JSXElement') {
- node.body = {
- type: 'BlockStatement',
- body: [{
- type: 'ReturnStatement',
- argument: node.body,
- }]
- };
- }
- super.ArrowFunctionExpression(node, state);
- }
-
- /* Inject template statements above and return root dom */
- ReturnStatement(node, state) {
- // custom handling for direct return of template jsx
- const type = node.argument?.type;
- if(type === 'JSXElement' || type === 'JSXFragment') {
- node.argument.returnStatement = node;
- this.JSXTemplate(node.argument, state);
- return;
- }
-
- super.ReturnStatement(node, state);
- }
-
- InjectionWrapper(template, state) {
- const returnStatement = template.node.returnStatement;
- const useIIFEWrapper = !returnStatement;
-
- if(useIIFEWrapper) {
- state.write(`(() => {`);
- state.indentLevel++;
- writeNextLine(state);
- }
-
- this.DomLiteral(template, state);
- writeNextLine(state);
- state.write(`return `, returnStatement);
- state.write(`__root;`, template.node);
-
- if(useIIFEWrapper) {
- state.indentLevel--;
- writeNextLine(state);
- state.write(`})()`);
- }
- }
-
- StaticRoot(template, state) {
- this.TemplateRenderer(template, state);
- if(!template.isEmpty) state.write(`[0]`, template.node); // dom root
- }
-
- TemplateRenderer({ id, isEmpty, isDomFragment, node }, state) {
- if(isEmpty) {
- state.write('null', node);
- return;
- }
- state.write(`t${id}`, node);
- state.write(`(`);
- if(isDomFragment) state.write('true');
- state.write(`)`);
- }
-
DomLiteral(template, state) {
- const { boundElements, bindings, node } = template;
+ const { id, boundElements, bindings, node, isEmpty } = template;
// template service renderer call
const hasTargets = !!boundElements.length;
- state.write(hasTargets ? `const [__root, __targets] = ` : `const __root = `);
- this.TemplateRenderer(template, state);
- state.write(hasTargets ? ';' : '[0];', node);
-
- // target variables
- for(let i = 0; i < boundElements.length; i++) {
- const boundElement = boundElements[i];
- const opening = boundElement.openingElement || boundElement.openFragment;
- writeNextLine(state);
- state.write(`const __target${i} =`);
- state.write(`__targets[${i}]`, opening?.name);
- state.write(`;`);
- }
- // sequential tasks before bindings generation below,
- // variables prevent downstream binding mutations from
- // changing index because childNodes is live list.
- for(let i = 0; i < bindings.length; i++) {
- const { element, type, index, node } = bindings[i];
- const { queryIndex } = element;
- if(type !== 'child') continue;
- const varName = queryIndex === -1 ? `__root` : `__target${queryIndex}`;
-
- let opening = null;
- if(IS_OPENING[element.type]) opening = element;
- else {
- const prop = OPENING_PROP[element.type];
- if(prop) opening = element[prop];
- else {
- throw new TypeError(`Unexpected binding node type "${node.type}"`);
- }
- }
-
- writeNextLine(state);
- state.write(`const __child${i} = `);
- state.write(`${varName}.childNodes`, opening.name);
- state.write(`[${index}]`, node);
- state.write(`;`);
+ if(isEmpty) {
+ state.write('null', node);
+ return;
}
+ state.write(`t${id}(`, node);
- // bindings
for(let i = 0; i < bindings.length; i++) {
- const { element, type, node, expr } = bindings[i];
- writeNextLine(state);
+ const { node, expr } = bindings[i];
+ if(i !== 0) state.write(`,`);
if(!this[expr.type]) {
throw new TypeError(`Unexpected Binding expression AST type "${expr.type}"`);
}
if(node.isComponent) {
- this.ComposeElement(node, expr, i, state);
- continue;
- }
- if(type === 'child') {
- this.Compose(node, expr, i, state);
- continue;
- }
- if(type === 'prop') {
- this.BindingProp(node, expr, element, state);
+ this.CreateElement(node, state);
continue;
}
-
- const message = `Unexpected binding type "${type}", expected "child" or "prop"`;
- throw new Error(message);
+ this[expr.type](expr, state);
}
- }
- Compose(node, expr, index, state) {
- state.write(`__compose(`, node);
- state.write(`__child${index}, `, node);
- this[expr.type](expr, state);
- state.write(`);`);
- }
-
- ComposeElement(node, expr, index, state) {
- state.write(`__composeElement(`, node);
- state.write(`__child${index}, `);
- this.CompleteElement(node, expr, state);
- state.write(`);`);
+ state.write(`)`);
}
CreateElement(node, state) {
@@ -286,29 +162,4 @@ export class TemplateGenerator extends Generator {
}
state.write(` }`);
}
-
- BindingProp(node, expr, element, state) {
- const { queryIndex, openingElement, openingFragment } = element;
- const varName = queryIndex === -1 ? `__root` : `__target${queryIndex}`;
- const opening = openingElement ?? openingFragment;
- state.write(`${varName}`, opening.name);
- // TODO: more property validation
- const identity = node.name;
- const propName = identity.name;
- // TODO: refactor with component props
- if(isValidESIdentifier(propName)) {
- state.write(`.`);
- state.write(propName, node.name);
- }
- else {
- state.write(`["`, node.name);
- state.write(propName, node.name);
- state.write(`"]`);
- }
-
- /* expression */
- state.write(` = (`);
- this[expr.type](expr, state);
- state.write(`);`);
- }
}
diff --git a/packages/compiler/transform/template-assets.js b/packages/compiler/transform/template-generators.js
similarity index 99%
rename from packages/compiler/transform/template-assets.js
rename to packages/compiler/transform/template-generators.js
index d4798eb..b551f8a 100644
--- a/packages/compiler/transform/template-assets.js
+++ b/packages/compiler/transform/template-generators.js
@@ -26,3 +26,4 @@ export function makeRender({ bindings: { length } }) {
return root;
}\n`;
}
+
diff --git a/packages/compiler/transform/template-assets.test.js b/packages/compiler/transform/template-generators.test.js
similarity index 97%
rename from packages/compiler/transform/template-assets.test.js
rename to packages/compiler/transform/template-generators.test.js
index 9977463..003ea43 100644
--- a/packages/compiler/transform/template-assets.test.js
+++ b/packages/compiler/transform/template-generators.test.js
@@ -1,8 +1,8 @@
/* eslint-disable no-undef */
-import { makeTargets, makeGetBound, makeRender } from './template-assets.js';
+import { makeTargets, makeGetBound, makeRender } from './template-generators.js';
import { parse, generate as _generate } from '../compiler.js';
import { describe, test, beforeEach } from 'vitest';
-import { RenderGenerator } from './RenderGenerator.js';
+import { BindGenerator } from './BindGenerator.js';
function preParse(input, expect) {
const ast = parse(input);
@@ -94,7 +94,7 @@ describe('bind generator', () => {
beforeEach(context => {
context.compile = code => {
const template = preParse(code, context.expect);
- return RenderGenerator.generate(template).code;
+ return BindGenerator.generate(template).code;
};
});
diff --git a/packages/runtime/compose/compose.js b/packages/runtime/compose/compose.js
index f93453a..b2d0428 100644
--- a/packages/runtime/compose/compose.js
+++ b/packages/runtime/compose/compose.js
@@ -77,15 +77,30 @@ export function composeElement(anchor, Constructor, props, slottable) {
create(Constructor, props, slottable, anchor);
}
-export function createElement(Constructor, props, slottable) {
+export function createElement(Constructor, props, slottable, topLevel = false) {
const result = create(Constructor, props, slottable);
- // result is returned to caller, force to be of type Node
- // by converting strings and numbers into text nodes
+ if(!topLevel) return result;
+
+ // result is returned to caller, not composed by Azoth,
+ // force to be of type Node or null:
+ // strings and numbers into text nodes
+ // non-values to null
const type = typeof result;
- if(type === 'string' || type === 'number') {
- return document.createTextNode(result);
+ switch(true) {
+ case type === 'string':
+ case type === 'number':
+ return document.createTextNode(result);
+ case result === undefined:
+ case result === null:
+ case result === true:
+ case result === false:
+ case result === IGNORE:
+ return null;
+ default:
+ return result;
}
- return result;
+
+
}
function create(input, props, slottable, anchor) {
@@ -101,6 +116,7 @@ function create(input, props, slottable, anchor) {
case input === true:
case input === false:
case input === '':
+ case input === IGNORE:
return anchor ? void compose(anchor, input) : input;
case !!(input.prototype?.constructor): {
// eslint-disable-next-line new-cap
diff --git a/packages/runtime/renderer/controller.test.js b/packages/runtime/renderer/controller.test.js
index d73064d..169bb2d 100644
--- a/packages/runtime/renderer/controller.test.js
+++ b/packages/runtime/renderer/controller.test.js
@@ -1,4 +1,4 @@
-import { describe, test, beforeEach, beforeAll } from 'vitest';
+import { describe, test, beforeAll } from 'vitest';
import { compose } from '../compose/compose.js';
import {
renderer,
From 521e32f11e9da0d3a5a0d92ab8e80c323ed80a14 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Thu, 21 Mar 2024 08:03:25 -0700
Subject: [PATCH 19/23] convert bind generation to single function with rest of
template generators
---
packages/compiler/transform/BindGenerator.js | 115 ------------------
.../compiler/transform/template-generators.js | 44 +++++++
.../transform/template-generators.test.js | 9 +-
3 files changed, 48 insertions(+), 120 deletions(-)
delete mode 100644 packages/compiler/transform/BindGenerator.js
diff --git a/packages/compiler/transform/BindGenerator.js b/packages/compiler/transform/BindGenerator.js
deleted file mode 100644
index 091a416..0000000
--- a/packages/compiler/transform/BindGenerator.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import { Generator, writeNextLine } from './GeneratorBase.js';
-import { isValidESIdentifier } from 'is-valid-es-identifier';
-import { generateWith } from '../compiler.js';
-
-const TARGETS = 'ts';
-const TARGET = 't';
-const VALUE = 'v';
-
-export class BindGenerator extends Generator {
- static generate(template) {
- const generator = new this(template);
- return generateWith(generator, template.node);
- }
- #bindings = null;
-
- constructor(template) {
- super();
- this.#bindings = template.bindings;
- }
-
- JSXFragment(_node, state) {
- this.Bindings(state);
- }
-
- JSXElement(_node, state) {
- this.Bindings(state);
- }
-
- Bindings(state) {
- const bindings = this.#bindings;
-
- state.write(`function bind(${TARGETS}) {`);
- state.indentLevel++;
- writeNextLine(state);
-
- const targets = [];
- const params = [];
- for(let i = 0; i < bindings.length; i++) {
- targets.push(`${TARGET}${i} = ${TARGETS}[${i}]`);
- params.push(`${VALUE}${i}`);
- }
-
- state.write(`const ${targets.join(', ')};`);
- writeNextLine(state);
- state.write(`return (${params.join(', ')}) => {`);
- state.indentLevel++;
-
- for(let i = 0; i < bindings.length; i++) {
- const { element, type, node, expr } = bindings[i];
- writeNextLine(state);
-
- if(!this[expr.type]) {
- throw new TypeError(`Unexpected Binding expression AST type "${expr.type}"`);
- }
-
- if(node.isComponent) {
- this.ComposeElement(node, expr, i, state);
- continue;
- }
- if(type === 'child') {
- this.Compose(node, expr, i, state);
- continue;
- }
- if(type === 'prop') {
- this.BindingProp(node, expr, i, element, state);
- continue;
- }
-
- const message = `Unexpected binding type "${type}", expected "child" or "prop"`;
- throw new Error(message);
- }
-
- state.indentLevel--;
- writeNextLine(state);
- state.write(`};`);
- state.indentLevel--;
- writeNextLine(state);
- state.write(`}`);
-
- state.write(state.lineEnd);
- }
-
- Compose(node, expr, index, state) {
- state.write(`compose(`, node);
- state.write(`${TARGET}${index}, `, node);
- state.write(`${VALUE}${index}`, expr);
- state.write(`);`);
- }
-
- BindingProp(node, expr, index, element, state) {
- const { openingElement, openingFragment } = element;
- const opening = openingElement ?? openingFragment;
- state.write(`${TARGET}${index}`, opening.name);
- // TODO: more property validation
- const identity = node.name;
- const propName = identity.name;
- // TODO: refactor with component props
- if(isValidESIdentifier(propName)) {
- state.write(`.`);
- state.write(propName, node.name);
- }
- else {
- state.write(`["`, node.name);
- state.write(propName, node.name);
- state.write(`"]`);
- }
-
- /* expression */
- state.write(` = `);
- // this[expr.type](expr, state);
- state.write(`${VALUE}${index}`, expr);
- state.write(`;`);
- }
-}
-
diff --git a/packages/compiler/transform/template-generators.js b/packages/compiler/transform/template-generators.js
index b551f8a..c337a74 100644
--- a/packages/compiler/transform/template-generators.js
+++ b/packages/compiler/transform/template-generators.js
@@ -1,3 +1,5 @@
+import { isValidESIdentifier } from 'is-valid-es-identifier';
+
export function makeTargets(template) {
const { boundElements, bindings } = template;
const { length: elLength } = boundElements;
@@ -27,3 +29,45 @@ export function makeRender({ bindings: { length } }) {
}\n`;
}
+const TARGETS = 'ts';
+const TARGET = 't';
+const VALUE = 'v';
+
+export function makeBind({ bindings }) {
+ const targets = [], params = [];
+ for(let i = 0; i < bindings.length; i++) {
+ targets.push(`${TARGET}${i} = ${TARGETS}[${i}]`);
+ params.push(`${VALUE}${i}`);
+ }
+
+ const bound = bindings.map(({ type, node }, index) => {
+ if(node.isComponent) {
+ throw new Error('need compose element');
+ // return ComposeElement(node, expr, i, state);
+ }
+ if(type === 'child') {
+ return `compose(${TARGET}${index}, ${VALUE}${index});`;
+ }
+ if(type === 'prop') {
+ // TODO: consider source maps for prop on element
+ // TODO: refactor with component props names
+ const identity = node.name;
+ const propName = identity.name;
+ const isValidId = isValidESIdentifier(propName);
+ const refinement = isValidId ? `.${propName}` : `[${propName}]`;
+
+ return `${TARGET}${index}${refinement} = ${VALUE}${index};`;
+
+ }
+ const message = `Unexpected binding type "${type}", expected "child" or "prop"`;
+ throw new Error(message);
+ });
+
+ return `function bind(${TARGETS}) {
+ const ${targets.join(', ')};
+ return (${params.join(', ')}) => {
+ ${bound.join('\n ')}
+ };
+}\n`;
+
+}
diff --git a/packages/compiler/transform/template-generators.test.js b/packages/compiler/transform/template-generators.test.js
index 003ea43..e8c70f3 100644
--- a/packages/compiler/transform/template-generators.test.js
+++ b/packages/compiler/transform/template-generators.test.js
@@ -1,8 +1,7 @@
/* eslint-disable no-undef */
-import { makeTargets, makeGetBound, makeRender } from './template-generators.js';
+import { makeTargets, makeGetBound, makeRender, makeBind } from './template-generators.js';
import { parse, generate as _generate } from '../compiler.js';
import { describe, test, beforeEach } from 'vitest';
-import { BindGenerator } from './BindGenerator.js';
function preParse(input, expect) {
const ast = parse(input);
@@ -94,7 +93,7 @@ describe('bind generator', () => {
beforeEach(context => {
context.compile = code => {
const template = preParse(code, context.expect);
- return BindGenerator.generate(template).code;
+ return makeBind(template);
};
});
@@ -105,7 +104,7 @@ describe('bind generator', () => {
const t0 = ts[0];
return (v0) => {
compose(t0, v0);
- };
+ };
}
"
`);
@@ -123,7 +122,7 @@ describe('bind generator', () => {
t0.className = v0;
compose(t1, v1);
compose(t2, v2);
- };
+ };
}
"
`
From 0bccbf432d33ed2edcb8a04d81612dc139da14e2 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Thu, 21 Mar 2024 09:46:24 -0700
Subject: [PATCH 20/23] test refactor
---
.../transform/template-generators.test.js | 23 ++++++++++---------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/packages/compiler/transform/template-generators.test.js b/packages/compiler/transform/template-generators.test.js
index e8c70f3..843b339 100644
--- a/packages/compiler/transform/template-generators.test.js
+++ b/packages/compiler/transform/template-generators.test.js
@@ -44,14 +44,14 @@ describe('targets generator', () => {
describe('getBound generator', () => {
beforeEach(context => {
- context.getTemplate = code => {
- return preParse(code, context.expect);
+ context.compile = code => {
+ const template = preParse(code, context.expect);
+ return makeGetBound(template);
};
});
- test('simple', ({ expect }) => {
- const template = preParse(`name => {name}
`, expect);
- const code = makeGetBound(template);
+ test('simple', ({ compile, expect }) => {
+ const code = compile(`name => {name}
`, expect);
expect(code).toMatchInlineSnapshot(`
"const getBound = renderer('904ca237ee', targets, bind, false, );
@@ -59,11 +59,12 @@ describe('getBound generator', () => {
`);
});
- test('props and elements', ({ expect }) => {
- const template = preParse(`const t =
+
+ test('props and elements', ({ compile, expect }) => {
+ const code = compile(`const t =
{"Greeting"} hey {"Azoth"}!
-
;`, expect);
- const code = makeGetBound(template);
+ ;`);
+
expect(code).toMatchInlineSnapshot(
`
"const getBound = renderer('5252cfebed', targets, bind, false,
@@ -74,8 +75,8 @@ describe('getBound generator', () => {
);
});
- test('option noContent: true', ({ getTemplate, expect }) => {
- const template = getTemplate(`name =>
{name}
`);
+ test('option noContent: true', ({ expect }) => {
+ const template = preParse(`name => {name}
`, expect);
const code = makeGetBound(template, { noContent: true });
expect(code).toMatchInlineSnapshot(`
From 5cde1105d8af5127e434c1a732c08cee2ef1d652 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Thu, 21 Mar 2024 10:46:02 -0700
Subject: [PATCH 21/23] refactor renderDOM to be higherOrder dynamic function
---
packages/compiler/compiler.test.js | 20 +++----
packages/compiler/transform/Template.js | 1 +
.../compiler/transform/TemplateGenerator.js | 5 +-
.../compiler/transform/template-generators.js | 13 +----
.../transform/template-generators.test.js | 52 +++----------------
packages/runtime/renderer/controller.test.js | 20 ++-----
packages/runtime/renderer/renderer.js | 12 ++++-
7 files changed, 37 insertions(+), 86 deletions(-)
diff --git a/packages/compiler/compiler.test.js b/packages/compiler/compiler.test.js
index 2ab60de..f55d05e 100644
--- a/packages/compiler/compiler.test.js
+++ b/packages/compiler/compiler.test.js
@@ -565,8 +565,8 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { __createElement } from 'azoth/runtime';
- const c = __createElement(Component);
- const cProps = __createElement(Component, { prop: value, attr: "static", });
+ const c = __createElement(Component, true);
+ const cProps = __createElement(Component, { prop: value, attr: "static", }, true);
"
`);
expect(templates).toMatchInlineSnapshot(`
@@ -632,8 +632,8 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { __createElement } from 'azoth/runtime';
import { t2288998344 } from 'virtual:azoth-templates?id=2288998344';
- const $A = __createElement(A);
- const $B = __createElement(B);
+ const $A = __createElement(A, true);
+ const $B = __createElement(B, true);
const dom = t2288998344($A,$B);
"
`);
@@ -686,12 +686,12 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { __createElement } from 'azoth/runtime';
import { t904ca237ee, t1cb251ec0d, t9b045328fb } from 'virtual:azoth-templates?id=904ca237ee&id=1cb251ec0d&id=9b045328fb';
- const c = __createElement(Component, null, t904ca237ee("test"));
- const cTrim = __createElement(Component, null, t904ca237ee("test"));
- const cTrimStart = __createElement(Component, null, t904ca237ee("test"));
- const cTrimEnd = __createElement(Component, null, t904ca237ee("test"));
- const cText = __createElement(Component, null, t1cb251ec0d());
- const cFrag = __createElement(Component, null, t9b045328fb(1,2));
+ const c = __createElement(Component, null, t904ca237ee("test"), true);
+ const cTrim = __createElement(Component, null, t904ca237ee("test"), true);
+ const cTrimStart = __createElement(Component, null, t904ca237ee("test"), true);
+ const cTrimEnd = __createElement(Component, null, t904ca237ee("test"), true);
+ const cText = __createElement(Component, null, t1cb251ec0d(), true);
+ const cFrag = __createElement(Component, null, t9b045328fb(1,2), true);
"
`);
diff --git a/packages/compiler/transform/Template.js b/packages/compiler/transform/Template.js
index 9419d6b..2f008e9 100644
--- a/packages/compiler/transform/Template.js
+++ b/packages/compiler/transform/Template.js
@@ -28,6 +28,7 @@ export class Template {
if(node.isComponent && bindings.length) {
throw new Error('Unexpected component binding length');
}
+
this.isBoundRoot = node.queryIndex === -1;
this.isDomFragment = node.isJSXFragment;
this.isEmpty = node.isComponent ||
diff --git a/packages/compiler/transform/TemplateGenerator.js b/packages/compiler/transform/TemplateGenerator.js
index de5b18b..9d475f0 100644
--- a/packages/compiler/transform/TemplateGenerator.js
+++ b/packages/compiler/transform/TemplateGenerator.js
@@ -83,7 +83,7 @@ export class TemplateGenerator extends Generator {
const { node: root } = template;
const { isComponent } = root;
if(isComponent) {
- this.CreateElement(root, state);
+ this.CreateElement(root, state, true);
return;
}
@@ -129,9 +129,10 @@ export class TemplateGenerator extends Generator {
state.write(`)`);
}
- CreateElement(node, state) {
+ CreateElement(node, state, topLevel = false) {
state.write(`__createElement(`, node);
this.CompleteElement(node, node.componentExpr, state);
+ if(topLevel) state.write(`, true`);
state.write(`)`);
}
diff --git a/packages/compiler/transform/template-generators.js b/packages/compiler/transform/template-generators.js
index c337a74..23c5d02 100644
--- a/packages/compiler/transform/template-generators.js
+++ b/packages/compiler/transform/template-generators.js
@@ -15,18 +15,9 @@ export function makeTargets(template) {
return `const targets = (${elLength ? 'r,t' : 'r'}) => [${values.join()}];\n`;
}
-export function makeGetBound({ id, isDomFragment, html }, options) {
+export function makeRenderer({ id, isDomFragment, html }, options) {
const content = options?.noContent ? '' : `, ${html}`;
- return `const getBound = renderer('${id}', targets, bind, ${isDomFragment}${content});\n`;
-}
-
-export function makeRender({ bindings: { length } }) {
- const params = Array.from({ length }, (_, i) => `p${i}`);
- return `function renderDOM(${params}) {
- const [root, bind] = getBound();
- bind(${params});
- return root;
-}\n`;
+ return `const renderDOM = renderer('${id}', targets, bind, ${isDomFragment}${content});\n`;
}
const TARGETS = 'ts';
diff --git a/packages/compiler/transform/template-generators.test.js b/packages/compiler/transform/template-generators.test.js
index 843b339..e54424d 100644
--- a/packages/compiler/transform/template-generators.test.js
+++ b/packages/compiler/transform/template-generators.test.js
@@ -1,5 +1,5 @@
/* eslint-disable no-undef */
-import { makeTargets, makeGetBound, makeRender, makeBind } from './template-generators.js';
+import { makeTargets, makeRenderer, makeRender, makeBind } from './template-generators.js';
import { parse, generate as _generate } from '../compiler.js';
import { describe, test, beforeEach } from 'vitest';
@@ -41,12 +41,12 @@ describe('targets generator', () => {
});
});
-describe('getBound generator', () => {
+describe('renderDOM generator', () => {
beforeEach(context => {
context.compile = code => {
const template = preParse(code, context.expect);
- return makeGetBound(template);
+ return makeRenderer(template);
};
});
@@ -54,7 +54,7 @@ describe('getBound generator', () => {
const code = compile(`name => {name}
`, expect);
expect(code).toMatchInlineSnapshot(`
- "const getBound = renderer('904ca237ee', targets, bind, false, );
+ "const renderDOM = renderer('904ca237ee', targets, bind, false, );
"
`);
});
@@ -67,7 +67,7 @@ describe('getBound generator', () => {
expect(code).toMatchInlineSnapshot(
`
- "const getBound = renderer('5252cfebed', targets, bind, false,
+ "const renderDOM = renderer('5252cfebed', targets, bind, false,
hey !
);
"
@@ -77,10 +77,10 @@ describe('getBound generator', () => {
test('option noContent: true', ({ expect }) => {
const template = preParse(`name => {name}
`, expect);
- const code = makeGetBound(template, { noContent: true });
+ const code = makeRenderer(template, { noContent: true });
expect(code).toMatchInlineSnapshot(`
- "const getBound = renderer('904ca237ee', targets, bind, false);
+ "const renderDOM = renderer('904ca237ee', targets, bind, false);
"
`);
});
@@ -130,41 +130,3 @@ describe('bind generator', () => {
);
});
});
-
-describe('render generator', () => {
-
- beforeEach(context => {
- context.compile = code => {
- const template = preParse(code, context.expect);
- return makeRender(template);
- };
- });
-
- test('simple', ({ compile, expect }) => {
- const code = compile(`name => {name}
`);
- expect(code).toMatchInlineSnapshot(`
- "function renderDOM(p0) {
- const [root, bind] = getBound();
- bind(p0);
- return root;
- }
- "
- `);
- });
-
- test('props and elements', ({ compile, expect }) => {
- const code = compile(`const t =
- {"Greeting"} hey {"Azoth"}!
-
;`);
- expect(code).toMatchInlineSnapshot(
- `
- "function renderDOM(p0,p1,p2) {
- const [root, bind] = getBound();
- bind(p0,p1,p2);
- return root;
- }
- "
- `
- );
- });
-});
\ No newline at end of file
diff --git a/packages/runtime/renderer/controller.test.js b/packages/runtime/renderer/controller.test.js
index 169bb2d..cfc8a12 100644
--- a/packages/runtime/renderer/controller.test.js
+++ b/packages/runtime/renderer/controller.test.js
@@ -10,10 +10,10 @@ import {
describe('dom render', () => {
- let getBound = null;
+ let renderDOM = null;
beforeAll(() => {
RenderService.useDOMEngine();
- getBound = renderer(
+ renderDOM = renderer(
'id',
getTargets,
makeBind,
@@ -33,12 +33,6 @@ describe('dom render', () => {
};
};
- function renderDOM(p0) {
- const [root, bind] = getBound();
- bind(p0);
- return root;
- }
-
test('Controller.for', ({ expect }) => {
const controller = Controller.for(name => renderDOM(name));
@@ -75,10 +69,10 @@ describe('dom render', () => {
describe('html render', () => {
const flatRender = node => node.flat().join('');
- let getBound = null;
+ let renderHTML = null;
beforeAll(() => {
RenderService.useHTMLEngine();
- getBound = renderer(
+ renderHTML = renderer(
'id',
getTargets,
makeBind,
@@ -98,12 +92,6 @@ describe('html render', () => {
};
};
- function renderHTML(p0) {
- const [root, bind] = getBound();
- bind(p0);
- return root;
- }
-
test('Controller.for', ({ expect }) => {
const controller = Controller.for(name => renderHTML(name));
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 8846f2d..2fa9bcf 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -52,10 +52,16 @@ function inject(node, callback) {
}
}
+const templateRenderer = getBound => (...args) => {
+ const [root, bind] = getBound();
+ bind(...args);
+ return root;
+};
+
export function renderer(id, targets, makeBind, isFragment, content) {
const create = get(id, isFragment, content);
- return function getBound() {
+ function getBound() {
let bind = null;
let boundEls = null;
let node = injectable.at(-1); // peek!
@@ -80,7 +86,9 @@ export function renderer(id, targets, makeBind, isFragment, content) {
bindings.set(node, bind);
return [node, bind];
- };
+ }
+
+ return templateRenderer(getBound);
}
export class Controller {
From 6aa6ceaabb0d2dcd21feece5fff6bae1f97a1cb4 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Thu, 21 Mar 2024 14:49:32 -0700
Subject: [PATCH 22/23] new compilation integrated!
---
.vscode/settings.json | 1 +
docs/pnpm-lock.yaml | 212 +++++-----
package.json | 4 +-
packages/azoth/runtime.js | 4 +-
packages/compiler/compiler.js | 5 +-
packages/compiler/compiler.test.js | 268 +++++++++----
packages/compiler/source-maps.test.js | 8 +-
packages/compiler/transform/Analyzer.js | 10 +-
packages/compiler/transform/HtmlGenerator.js | 1 -
packages/compiler/transform/Template.js | 48 +--
.../{TemplateGenerator.js => Transpiler.js} | 17 +-
.../compiler/transform/template-generators.js | 28 +-
.../transform/template-generators.test.js | 110 +++---
packages/runtime/renderer/index.js | 2 +-
packages/runtime/renderer/renderer.js | 13 +-
packages/vite-plugin/index.js | 104 +++--
packages/vite-plugin/package.json | 1 +
packages/vite-plugin/pnpm-lock.yaml | 170 ++++----
pnpm-lock.yaml | 215 +++++-----
sandbox/package.json | 2 +-
sandbox/pnpm-lock.yaml | 164 ++++----
vite-test/expected-out/index-BDbUw1te.js | 250 ------------
vite-test/expected-out/index-CHvwx500.js | 374 ++++++++++++++++++
vite-test/expected-out/index.html | 11 +-
vite-test/out/index-BDbUw1te.js | 250 ------------
vite-test/out/index-CHvwx500.js | 374 ++++++++++++++++++
vite-test/out/index.html | 11 +-
vite-test/package.json | 4 +-
vite-test/plugin.test.js | 4 +-
vite-test/pnpm-lock.yaml | 174 ++++----
vite-test/src/fetchEmojis.js | 18 +-
vite-test/src/main.jsx | 12 +-
vite-test/vite.config.js | 1 +
33 files changed, 1625 insertions(+), 1245 deletions(-)
rename packages/compiler/transform/{TemplateGenerator.js => Transpiler.js} (91%)
delete mode 100644 vite-test/expected-out/index-BDbUw1te.js
create mode 100644 vite-test/expected-out/index-CHvwx500.js
delete mode 100644 vite-test/out/index-BDbUw1te.js
create mode 100644 vite-test/out/index-CHvwx500.js
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 50bde4f..870a1b4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -57,4 +57,5 @@
"explorer.fileNesting.patterns": {
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, pnpm-workspace.yaml"
},
+ "liveServer.settings.port": 5501,
}
\ No newline at end of file
diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml
index 559ee12..a1ef8e5 100644
--- a/docs/pnpm-lock.yaml
+++ b/docs/pnpm-lock.yaml
@@ -145,8 +145,8 @@ packages:
'@algolia/requester-common': 4.22.1
dev: true
- /@babel/helper-string-parser@7.23.4:
- resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
+ /@babel/helper-string-parser@7.24.1:
+ resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==}
engines: {node: '>=6.9.0'}
dev: true
@@ -155,8 +155,8 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
- /@babel/parser@7.24.0:
- resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
+ /@babel/parser@7.24.1:
+ resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
@@ -167,7 +167,7 @@ packages:
resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/helper-string-parser': 7.23.4
+ '@babel/helper-string-parser': 7.24.1
'@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0
dev: true
@@ -180,7 +180,7 @@ packages:
resolution: {integrity: sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==}
dependencies:
'@docsearch/react': 3.6.0(@algolia/client-search@4.22.1)(search-insights@2.13.0)
- preact: 10.19.6
+ preact: 10.20.0
transitivePeerDependencies:
- '@algolia/client-search'
- '@types/react'
@@ -215,8 +215,8 @@ packages:
- '@algolia/client-search'
dev: true
- /@esbuild/aix-ppc64@0.19.12:
- resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+ /@esbuild/aix-ppc64@0.20.2:
+ resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
@@ -224,8 +224,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm64@0.19.12:
- resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+ /@esbuild/android-arm64@0.20.2:
+ resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
@@ -233,8 +233,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm@0.19.12:
- resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+ /@esbuild/android-arm@0.20.2:
+ resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
@@ -242,8 +242,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-x64@0.19.12:
- resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+ /@esbuild/android-x64@0.20.2:
+ resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
@@ -251,8 +251,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-arm64@0.19.12:
- resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+ /@esbuild/darwin-arm64@0.20.2:
+ resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
@@ -260,8 +260,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-x64@0.19.12:
- resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+ /@esbuild/darwin-x64@0.20.2:
+ resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
@@ -269,8 +269,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-arm64@0.19.12:
- resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+ /@esbuild/freebsd-arm64@0.20.2:
+ resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
@@ -278,8 +278,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-x64@0.19.12:
- resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+ /@esbuild/freebsd-x64@0.20.2:
+ resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
@@ -287,8 +287,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm64@0.19.12:
- resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+ /@esbuild/linux-arm64@0.20.2:
+ resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
@@ -296,8 +296,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm@0.19.12:
- resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+ /@esbuild/linux-arm@0.20.2:
+ resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
@@ -305,8 +305,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ia32@0.19.12:
- resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+ /@esbuild/linux-ia32@0.20.2:
+ resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
@@ -314,8 +314,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-loong64@0.19.12:
- resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+ /@esbuild/linux-loong64@0.20.2:
+ resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
@@ -323,8 +323,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-mips64el@0.19.12:
- resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+ /@esbuild/linux-mips64el@0.20.2:
+ resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
@@ -332,8 +332,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ppc64@0.19.12:
- resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+ /@esbuild/linux-ppc64@0.20.2:
+ resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
@@ -341,8 +341,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-riscv64@0.19.12:
- resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+ /@esbuild/linux-riscv64@0.20.2:
+ resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
@@ -350,8 +350,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-s390x@0.19.12:
- resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+ /@esbuild/linux-s390x@0.20.2:
+ resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
@@ -359,8 +359,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-x64@0.19.12:
- resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+ /@esbuild/linux-x64@0.20.2:
+ resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
@@ -368,8 +368,8 @@ packages:
dev: true
optional: true
- /@esbuild/netbsd-x64@0.19.12:
- resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+ /@esbuild/netbsd-x64@0.20.2:
+ resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
@@ -377,8 +377,8 @@ packages:
dev: true
optional: true
- /@esbuild/openbsd-x64@0.19.12:
- resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+ /@esbuild/openbsd-x64@0.20.2:
+ resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
@@ -386,8 +386,8 @@ packages:
dev: true
optional: true
- /@esbuild/sunos-x64@0.19.12:
- resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+ /@esbuild/sunos-x64@0.20.2:
+ resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
@@ -395,8 +395,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-arm64@0.19.12:
- resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+ /@esbuild/win32-arm64@0.20.2:
+ resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
@@ -404,8 +404,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-ia32@0.19.12:
- resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+ /@esbuild/win32-ia32@0.20.2:
+ resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
@@ -413,8 +413,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-x64@0.19.12:
- resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+ /@esbuild/win32-x64@0.20.2:
+ resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
@@ -563,25 +563,25 @@ packages:
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
dev: true
- /@vitejs/plugin-vue@5.0.4(vite@5.1.6)(vue@3.4.21):
+ /@vitejs/plugin-vue@5.0.4(vite@5.2.2)(vue@3.4.21):
resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
vite: ^5.0.0
vue: ^3.2.25
dependencies:
- vite: 5.1.6
+ vite: 5.2.2
vue: 3.4.21
dev: true
/@vue/compiler-core@3.4.21:
resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
dependencies:
- '@babel/parser': 7.24.0
+ '@babel/parser': 7.24.1
'@vue/shared': 3.4.21
entities: 4.5.0
estree-walker: 2.0.2
- source-map-js: 1.0.2
+ source-map-js: 1.2.0
dev: true
/@vue/compiler-dom@3.4.21:
@@ -594,15 +594,15 @@ packages:
/@vue/compiler-sfc@3.4.21:
resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==}
dependencies:
- '@babel/parser': 7.24.0
+ '@babel/parser': 7.24.1
'@vue/compiler-core': 3.4.21
'@vue/compiler-dom': 3.4.21
'@vue/compiler-ssr': 3.4.21
'@vue/shared': 3.4.21
estree-walker: 2.0.2
magic-string: 0.30.8
- postcss: 8.4.35
- source-map-js: 1.0.2
+ postcss: 8.4.38
+ source-map-js: 1.2.0
dev: true
/@vue/compiler-ssr@3.4.21:
@@ -612,20 +612,20 @@ packages:
'@vue/shared': 3.4.21
dev: true
- /@vue/devtools-api@7.0.17(vue@3.4.21):
- resolution: {integrity: sha512-UWU9tqzUBv+ttUxYLaQcL5IxSSdF+i6yheFiEtz7mh88YZUYkxpEmT43iKBs3YsC54ROwPD2iZIndnju6PWfOQ==}
+ /@vue/devtools-api@7.0.20(vue@3.4.21):
+ resolution: {integrity: sha512-DGEIdotTQFll4187YGc/0awcag7UGJu9M6rE1Pxcs8AX/sGm0Ikk7UqQELmqYsyPzTT9s6OZzSPuBc4OatOXKA==}
dependencies:
- '@vue/devtools-kit': 7.0.17(vue@3.4.21)
+ '@vue/devtools-kit': 7.0.20(vue@3.4.21)
transitivePeerDependencies:
- vue
dev: true
- /@vue/devtools-kit@7.0.17(vue@3.4.21):
- resolution: {integrity: sha512-znPLSOoTP3RnR9fvkq5M+nnpEA+WocybzOo5ID73vYkE0/n0VcfU8Ld0j4AHQjV/omTdAzh6QLpPlUYdIHXg+w==}
+ /@vue/devtools-kit@7.0.20(vue@3.4.21):
+ resolution: {integrity: sha512-FgFuPuqrhQ51rR/sVi52FnGgrxJ3X1bvNra/SkBzPhxJVhfyL5w2YUJZI1FgCvtLAyPSomJNdvlG415ZbJsr6w==}
peerDependencies:
vue: ^3.0.0
dependencies:
- '@vue/devtools-shared': 7.0.17
+ '@vue/devtools-shared': 7.0.20
hookable: 5.5.3
mitt: 3.0.1
perfect-debounce: 1.0.0
@@ -633,8 +633,8 @@ packages:
vue: 3.4.21
dev: true
- /@vue/devtools-shared@7.0.17:
- resolution: {integrity: sha512-QNg2TMQBFFffRbTKE9NjytXBywGR77p2UMi/gJ0ow58S+1jkAvL8ikU/JnSs9ePvsVtspHX32m2cdfe4DJ4ygw==}
+ /@vue/devtools-shared@7.0.20:
+ resolution: {integrity: sha512-E6CiCaYr6ZWOCYJgWodXcPCXxB12vgbUA1X1sG0F1tK5Bo5I35GJuTR8LBJLFHV0VpwLWvyrIi9drT1ZbuJxlg==}
dependencies:
rfdc: 1.3.1
dev: true
@@ -777,35 +777,35 @@ packages:
engines: {node: '>=0.12'}
dev: true
- /esbuild@0.19.12:
- resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
+ /esbuild@0.20.2:
+ resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
- '@esbuild/aix-ppc64': 0.19.12
- '@esbuild/android-arm': 0.19.12
- '@esbuild/android-arm64': 0.19.12
- '@esbuild/android-x64': 0.19.12
- '@esbuild/darwin-arm64': 0.19.12
- '@esbuild/darwin-x64': 0.19.12
- '@esbuild/freebsd-arm64': 0.19.12
- '@esbuild/freebsd-x64': 0.19.12
- '@esbuild/linux-arm': 0.19.12
- '@esbuild/linux-arm64': 0.19.12
- '@esbuild/linux-ia32': 0.19.12
- '@esbuild/linux-loong64': 0.19.12
- '@esbuild/linux-mips64el': 0.19.12
- '@esbuild/linux-ppc64': 0.19.12
- '@esbuild/linux-riscv64': 0.19.12
- '@esbuild/linux-s390x': 0.19.12
- '@esbuild/linux-x64': 0.19.12
- '@esbuild/netbsd-x64': 0.19.12
- '@esbuild/openbsd-x64': 0.19.12
- '@esbuild/sunos-x64': 0.19.12
- '@esbuild/win32-arm64': 0.19.12
- '@esbuild/win32-ia32': 0.19.12
- '@esbuild/win32-x64': 0.19.12
+ '@esbuild/aix-ppc64': 0.20.2
+ '@esbuild/android-arm': 0.20.2
+ '@esbuild/android-arm64': 0.20.2
+ '@esbuild/android-x64': 0.20.2
+ '@esbuild/darwin-arm64': 0.20.2
+ '@esbuild/darwin-x64': 0.20.2
+ '@esbuild/freebsd-arm64': 0.20.2
+ '@esbuild/freebsd-x64': 0.20.2
+ '@esbuild/linux-arm': 0.20.2
+ '@esbuild/linux-arm64': 0.20.2
+ '@esbuild/linux-ia32': 0.20.2
+ '@esbuild/linux-loong64': 0.20.2
+ '@esbuild/linux-mips64el': 0.20.2
+ '@esbuild/linux-ppc64': 0.20.2
+ '@esbuild/linux-riscv64': 0.20.2
+ '@esbuild/linux-s390x': 0.20.2
+ '@esbuild/linux-x64': 0.20.2
+ '@esbuild/netbsd-x64': 0.20.2
+ '@esbuild/openbsd-x64': 0.20.2
+ '@esbuild/sunos-x64': 0.20.2
+ '@esbuild/win32-arm64': 0.20.2
+ '@esbuild/win32-ia32': 0.20.2
+ '@esbuild/win32-x64': 0.20.2
dev: true
/estree-walker@2.0.2:
@@ -863,17 +863,17 @@ packages:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
- /postcss@8.4.35:
- resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
+ /postcss@8.4.38:
+ resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
- source-map-js: 1.0.2
+ source-map-js: 1.2.0
dev: true
- /preact@10.19.6:
- resolution: {integrity: sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw==}
+ /preact@10.20.0:
+ resolution: {integrity: sha512-wU7iZw2BjsaKDal3pDRDy/HpPB6cuFOnVUCcw9aIPKG98+ZrXx3F+szkos8BVME5bquyKDKvRlOJFG8kMkcAbg==}
dev: true
/rfdc@1.3.1:
@@ -913,8 +913,8 @@ packages:
'@shikijs/core': 1.2.0
dev: true
- /source-map-js@1.0.2:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ /source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
dev: true
@@ -932,8 +932,8 @@ packages:
engines: {node: '>=4'}
dev: true
- /vite@5.1.6:
- resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
+ /vite@5.2.2:
+ resolution: {integrity: sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -960,8 +960,8 @@ packages:
terser:
optional: true
dependencies:
- esbuild: 0.19.12
- postcss: 8.4.35
+ esbuild: 0.20.2
+ postcss: 8.4.38
rollup: 4.13.0
optionalDependencies:
fsevents: 2.3.3
@@ -984,15 +984,15 @@ packages:
'@shikijs/core': 1.2.0
'@shikijs/transformers': 1.2.0
'@types/markdown-it': 13.0.7
- '@vitejs/plugin-vue': 5.0.4(vite@5.1.6)(vue@3.4.21)
- '@vue/devtools-api': 7.0.17(vue@3.4.21)
+ '@vitejs/plugin-vue': 5.0.4(vite@5.2.2)(vue@3.4.21)
+ '@vue/devtools-api': 7.0.20(vue@3.4.21)
'@vueuse/core': 10.9.0(vue@3.4.21)
'@vueuse/integrations': 10.9.0(focus-trap@7.5.4)(vue@3.4.21)
focus-trap: 7.5.4
mark.js: 8.11.1
minisearch: 6.3.0
shiki: 1.2.0
- vite: 5.1.6
+ vite: 5.2.2
vue: 3.4.21
transitivePeerDependencies:
- '@algolia/client-search'
diff --git a/package.json b/package.json
index 129f407..a62c355 100644
--- a/package.json
+++ b/package.json
@@ -22,8 +22,8 @@
"azoth": "workspace:./packages/azoth",
"eslint": "^8.57.0",
"globals": "^14.0.0",
- "happy-dom": "^13.8.6",
- "vite": "^5.1.6",
+ "happy-dom": "^13.10.1",
+ "vite": "^5.2.2",
"vite-plugin-inspect": "^0.8.3",
"vitest": "^1.4.0"
},
diff --git a/packages/azoth/runtime.js b/packages/azoth/runtime.js
index 659c312..9486dbf 100644
--- a/packages/azoth/runtime.js
+++ b/packages/azoth/runtime.js
@@ -1,10 +1,8 @@
export {
compose as __compose,
- composeElement as __composeElement,
createElement as __createElement,
} from '@azothjs/runtime/compose';
export {
- makeRenderer as __makeRenderer,
- rendererById as __rendererById,
+ renderer as __renderer,
} from '@azothjs/runtime/renderer';
\ No newline at end of file
diff --git a/packages/compiler/compiler.js b/packages/compiler/compiler.js
index 5ecd364..951894a 100644
--- a/packages/compiler/compiler.js
+++ b/packages/compiler/compiler.js
@@ -1,9 +1,10 @@
import { Parser } from 'acorn';
import acornJsx from 'acorn-jsx';
import { generate as astring } from 'astring';
-import { TemplateGenerator, templateModule } from './transform/TemplateGenerator.js';
+import { Transpiler, templateModule } from './transform/Transpiler.js';
import { SourceMapGenerator } from 'source-map';
+export * from './transform/template-generators.js';
export { templateModule };
// compile = parse + generate
@@ -51,7 +52,7 @@ export function generateWith(generator, ast, config) {
}
export function generate(ast, config) {
- const generator = new TemplateGenerator();
+ const generator = new Transpiler();
const generated = generateWith(generator, ast, config);
const { templates } = generator;
diff --git a/packages/compiler/compiler.test.js b/packages/compiler/compiler.test.js
index f55d05e..dfd643b 100644
--- a/packages/compiler/compiler.test.js
+++ b/packages/compiler/compiler.test.js
@@ -8,8 +8,8 @@ const compile = input => {
});
return {
code, map,
- templates: templates.map(({ id, html, isDomFragment, isEmpty }) => {
- return { id, html, isDomFragment, isEmpty };
+ templates: templates.map(({ id, childBindKey, propBindKey, html, isDomFragment, isEmpty }) => {
+ return { id, childBindKey, propBindKey, html, isDomFragment, isEmpty };
})
};
};
@@ -24,20 +24,22 @@ describe('JSX dom literals', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { ta516887159 } from 'virtual:azoth-templates?id=a516887159';
+ "import { tf312946f36 } from 'virtual:azoth-templates?id=f312946f36';
- const t = ta516887159("className","Azoth");
+ const t = tf312946f36("className","Azoth");
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
Hello
",
- "id": "a516887159",
+ "id": "f312946f36",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -63,15 +65,16 @@ describe('JSX dom literals', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { tfdd1a869cf } from 'virtual:azoth-templates?id=fdd1a869cf';
+ "import { t9fcf48061b } from 'virtual:azoth-templates?id=9fcf48061b';
- const t = tfdd1a869cf("my-class","felix","this is","azoth","two","and...","span-class","ul-footer","footer");
+ const t = t9fcf48061b("my-class","felix","this is","azoth","two","and...","span-class","ul-footer","footer");
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
static
@@ -87,9 +90,10 @@ describe('JSX dom literals', () => {
",
- "id": "fdd1a869cf",
+ "id": "9fcf48061b",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -107,19 +111,21 @@ describe('JSX dom literals', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { t10073da0ec } from 'virtual:azoth-templates?id=10073da0ec';
+ "import { tfef7238342 } from 'virtual:azoth-templates?id=fef7238342';
- const t = t10073da0ec("className","name","class","class-name");
+ const t = tfef7238342("className","name","class","class-name");
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "10073da0ec",
+ "id": "fef7238342",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -133,25 +139,29 @@ describe('nested context', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { t8dae88052a, t1a78cbe949 } from 'virtual:azoth-templates?id=8dae88052a&id=1a78cbe949';
+ "import { t728beb50f0, t1a78cbe949 } from 'virtual:azoth-templates?id=728beb50f0&id=1a78cbe949';
- t8dae88052a(t1a78cbe949());
+ t728beb50f0(t1a78cbe949());
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "8dae88052a",
+ "id": "728beb50f0",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
"id": "1a78cbe949",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -175,10 +185,12 @@ describe('template optimizations', () => {
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "Hello
",
"id": "5bf3d2f523",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -195,10 +207,10 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { tc203fe7dcd, tc084de4382 } from 'virtual:azoth-templates?id=c203fe7dcd&id=c084de4382';
+ "import { tc203fe7dcd, t84c9741b4d, td41d8cd98f } from 'virtual:azoth-templates?id=c203fe7dcd&id=84c9741b4d&id=d41d8cd98f';
const fragment = tc203fe7dcd();
- const compose = tc084de4382(x);
+ const compose = t84c9741b4d(x);
const empty = null;
"
`);
@@ -206,22 +218,28 @@ describe('fragments', () => {
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
",
"id": "c203fe7dcd",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "c084de4382",
+ "id": "84c9741b4d",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": true,
"isEmpty": true,
+ "propBindKey": undefined,
},
]
`);
@@ -258,14 +276,14 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { tc203fe7dcd, t1a78cbe949, tc084de4382, t6c72de769d } from 'virtual:azoth-templates?id=c203fe7dcd&id=1a78cbe949&id=c084de4382&id=6c72de769d';
+ "import { tc203fe7dcd, t1a78cbe949, t84c9741b4d, td41d8cd98f, t6c72de769d } from 'virtual:azoth-templates?id=c203fe7dcd&id=1a78cbe949&id=84c9741b4d&id=d41d8cd98f&id=6c72de769d';
const fragment = tc203fe7dcd();
const single = t1a78cbe949();
const fragInFrag = t1a78cbe949();
- const fragInFragCompose = tc084de4382(x);
+ const fragInFragCompose = t84c9741b4d(x);
const empty = null;
- const compose = tc084de4382(x);
+ const compose = t84c9741b4d(x);
const text = t6c72de769d();
"
`);
@@ -273,48 +291,62 @@ describe('fragments', () => {
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
",
"id": "c203fe7dcd",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
"id": "1a78cbe949",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
"id": "1a78cbe949",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "c084de4382",
+ "id": "84c9741b4d",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": true,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "c084de4382",
+ "id": "84c9741b4d",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
text
",
"id": "6c72de769d",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -335,40 +367,48 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { t1a78cbe949, tc084de4382 } from 'virtual:azoth-templates?id=1a78cbe949&id=c084de4382';
+ "import { t1a78cbe949, t84c9741b4d } from 'virtual:azoth-templates?id=1a78cbe949&id=84c9741b4d';
const start = t1a78cbe949();
const end = t1a78cbe949();
- const composeStart = tc084de4382(x);
- const composeEnd = tc084de4382(x);
+ const composeStart = t84c9741b4d(x);
+ const composeEnd = t84c9741b4d(x);
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
",
"id": "1a78cbe949",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
"id": "1a78cbe949",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "c084de4382",
+ "id": "84c9741b4d",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "c084de4382",
+ "id": "84c9741b4d",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -386,47 +426,57 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { t653a3aad80, tdcaa233028, t2dc1738d5c, t0cf31b2c28, t5bc2a159b1 } from 'virtual:azoth-templates?id=653a3aad80&id=dcaa233028&id=2dc1738d5c&id=0cf31b2c28&id=5bc2a159b1';
+ "import { t653a3aad80, tdcaa233028, t2dc1738d5c, t0cf31b2c28, t3c41ad0e2b } from 'virtual:azoth-templates?id=653a3aad80&id=dcaa233028&id=2dc1738d5c&id=0cf31b2c28&id=3c41ad0e2b';
const fragment = t653a3aad80();
const single = tdcaa233028();
const fragInFrag = t2dc1738d5c();
const spaces = t0cf31b2c28();
- const compose = t5bc2a159b1(x);
+ const compose = t3c41ad0e2b(x);
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
",
"id": "653a3aad80",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
"id": "dcaa233028",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
"id": "2dc1738d5c",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": " ",
"id": "0cf31b2c28",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": " ",
- "id": "5bc2a159b1",
+ "id": "3c41ad0e2b",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -440,19 +490,21 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { tfaf808e6cc } from 'virtual:azoth-templates?id=faf808e6cc';
+ "import { t253e2ffa84 } from 'virtual:azoth-templates?id=253e2ffa84';
- const fragment = tfaf808e6cc("two");
+ const fragment = t253e2ffa84("two");
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "onethree",
- "id": "faf808e6cc",
+ "id": "253e2ffa84",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -472,30 +524,34 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { tccaa44c114, t681310be49 } from 'virtual:azoth-templates?id=ccaa44c114&id=681310be49';
+ "import { tccaa44c114, taccbe53128 } from 'virtual:azoth-templates?id=ccaa44c114&id=accbe53128';
const extraneous = tccaa44c114();
- const childNodeIndex = t681310be49("expect index 3");
+ const childNodeIndex = taccbe53128("expect index 3");
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
",
"id": "ccaa44c114",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "681310be49",
+ "id": "accbe53128",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -507,19 +563,21 @@ describe('fragments', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { tef691fa27a } from 'virtual:azoth-templates?id=ef691fa27a';
+ "import { t97874fe2c4 } from 'virtual:azoth-templates?id=97874fe2c4';
- const App = tef691fa27a('foo','bar','qux');
+ const App = t97874fe2c4('foo','bar','qux');
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "ef691fa27a",
+ "id": "97874fe2c4",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -536,19 +594,21 @@ describe('element composition', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { t1cdf0d646f } from 'virtual:azoth-templates?id=1cdf0d646f';
+ "import { tb3611c2834 } from 'virtual:azoth-templates?id=b3611c2834';
- document.body.append(t1cdf0d646f(prop));
+ document.body.append(tb3611c2834(prop));
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "1cdf0d646f",
+ "id": "b3611c2834",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -564,7 +624,7 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { __createElement } from 'azoth/runtime';
-
+ import { td41d8cd98f } from 'virtual:azoth-templates?id=d41d8cd98f';
const c = __createElement(Component, true);
const cProps = __createElement(Component, { prop: value, attr: "static", }, true);
"
@@ -572,16 +632,20 @@ describe('element composition', () => {
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
]
`);
@@ -598,20 +662,22 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { __createElement } from 'azoth/runtime';
- import { t2288998344 } from 'virtual:azoth-templates?id=2288998344';
- const component = t2288998344(__createElement(Component, { prop: value, prop2: "literal", }),__createElement(GotNoPropsAsYouCanSee));
+ import { t06bef50336 } from 'virtual:azoth-templates?id=06bef50336';
+ const component = t06bef50336(__createElement(Component, { prop: value, prop2: "literal", }),__createElement(GotNoPropsAsYouCanSee));
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "
",
- "id": "2288998344",
+ "id": "06bef50336",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -631,34 +697,40 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { __createElement } from 'azoth/runtime';
- import { t2288998344 } from 'virtual:azoth-templates?id=2288998344';
+ import { td41d8cd98f, t06bef50336 } from 'virtual:azoth-templates?id=d41d8cd98f&id=06bef50336';
const $A = __createElement(A, true);
const $B = __createElement(B, true);
- const dom = t2288998344($A,$B);
+ const dom = t06bef50336($A,$B);
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
- "id": "2288998344",
+ "id": "06bef50336",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -685,90 +757,114 @@ describe('element composition', () => {
expect(code).toMatchInlineSnapshot(`
"import { __createElement } from 'azoth/runtime';
- import { t904ca237ee, t1cb251ec0d, t9b045328fb } from 'virtual:azoth-templates?id=904ca237ee&id=1cb251ec0d&id=9b045328fb';
- const c = __createElement(Component, null, t904ca237ee("test"), true);
- const cTrim = __createElement(Component, null, t904ca237ee("test"), true);
- const cTrimStart = __createElement(Component, null, t904ca237ee("test"), true);
- const cTrimEnd = __createElement(Component, null, t904ca237ee("test"), true);
+ import { td41d8cd98f, tc193fcb516, t1cb251ec0d, t047dd60a46 } from 'virtual:azoth-templates?id=d41d8cd98f&id=c193fcb516&id=1cb251ec0d&id=047dd60a46';
+ const c = __createElement(Component, null, tc193fcb516("test"), true);
+ const cTrim = __createElement(Component, null, tc193fcb516("test"), true);
+ const cTrimStart = __createElement(Component, null, tc193fcb516("test"), true);
+ const cTrimEnd = __createElement(Component, null, tc193fcb516("test"), true);
const cText = __createElement(Component, null, t1cb251ec0d(), true);
- const cFrag = __createElement(Component, null, t9b045328fb(1,2), true);
+ const cFrag = __createElement(Component, null, t047dd60a46(1,2), true);
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "904ca237ee",
+ "id": "c193fcb516",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "904ca237ee",
+ "id": "c193fcb516",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "904ca237ee",
+ "id": "c193fcb516",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "904ca237ee",
+ "id": "c193fcb516",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "text",
"id": "1cb251ec0d",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "",
+ "id": "d41d8cd98f",
"isDomFragment": false,
"isEmpty": true,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "
",
- "id": "9b045328fb",
+ "id": "047dd60a46",
"isDomFragment": true,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -786,26 +882,30 @@ describe('render and composition cases', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { t62831a5152, t8dc93cc914 } from 'virtual:azoth-templates?id=62831a5152&id=8dc93cc914';
+ "import { tcb5355f810, t9d84b2deff } from 'virtual:azoth-templates?id=cb5355f810&id=9d84b2deff';
- const Item = name => t62831a5152(name);
- const Template = () => t8dc93cc914([2, 4, 7].map(Item),"text");
+ const Item = name => tcb5355f810(name);
+ const Template = () => t9d84b2deff([2, 4, 7].map(Item),"text");
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "62831a5152",
+ "id": "cb5355f810",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "8dc93cc914",
+ "id": "9d84b2deff",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
@@ -822,27 +922,31 @@ describe('render and composition cases', () => {
const { code, templates } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { t62831a5152, t25ec157413 } from 'virtual:azoth-templates?id=62831a5152&id=25ec157413';
+ "import { tcb5355f810, t65cf075bba } from 'virtual:azoth-templates?id=cb5355f810&id=65cf075bba';
- const Emoji = ({name}) => t62831a5152(name);
+ const Emoji = ({name}) => tcb5355f810(name);
const promise = fetchEmojis().then(emojis => emojis.map(Emoji));
- const Emojis = t25ec157413(promise);
+ const Emojis = t65cf075bba(promise);
document.body.append(Emojis);
"
`);
expect(templates).toMatchInlineSnapshot(`
[
{
+ "childBindKey": undefined,
"html": "",
- "id": "62831a5152",
+ "id": "cb5355f810",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
{
+ "childBindKey": undefined,
"html": "",
- "id": "25ec157413",
+ "id": "65cf075bba",
"isDomFragment": false,
"isEmpty": false,
+ "propBindKey": undefined,
},
]
`);
diff --git a/packages/compiler/source-maps.test.js b/packages/compiler/source-maps.test.js
index 835bbc9..372961a 100644
--- a/packages/compiler/source-maps.test.js
+++ b/packages/compiler/source-maps.test.js
@@ -42,9 +42,9 @@ test('{...} one line', ({ expect }) => {
const input = `Hello {place}
`;
const { _sourceMap, code } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { ta94b210052 } from 'virtual:azoth-templates?id=a94b210052';
+ "import { t9be5e1247d } from 'virtual:azoth-templates?id=9be5e1247d';
- ta94b210052(place);
+ t9be5e1247d(place);
"
`);
expect(_sourceMap._mappings._array).toMatchInlineSnapshot(`
@@ -108,9 +108,9 @@ test('{...} three line', ({ expect }) => {
`;
const { _sourceMap, code } = compile(input);
expect(code).toMatchInlineSnapshot(`
- "import { tf2d718c3f5 } from 'virtual:azoth-templates?id=f2d718c3f5';
+ "import { t1f0853a65c } from 'virtual:azoth-templates?id=1f0853a65c';
- const t = tf2d718c3f5(place);
+ const t = t1f0853a65c(place);
"
`);
expect(_sourceMap._mappings._array).toMatchInlineSnapshot(`
diff --git a/packages/compiler/transform/Analyzer.js b/packages/compiler/transform/Analyzer.js
index 6387eb5..6b48bf3 100644
--- a/packages/compiler/transform/Analyzer.js
+++ b/packages/compiler/transform/Analyzer.js
@@ -17,7 +17,6 @@ export class Analyzer {
#documentOrder = 0;
#boundElements = new Set();
#bindings = [];
- #template = null;
#root = null;
#imports = new Set();
@@ -29,20 +28,13 @@ export class Analyzer {
boundElements[i].queryIndex = i;
}
- this.#template = new Template(this.#root, {
+ this.template = new Template(this.#root, {
bindings: this.#bindings,
boundElements,
imports: [...(this.#imports.values())],
});
}
- // TODO: move generation elsewhere
- generateTemplate(htmlGenerator) {
- const template = this.#template;
- if(!template.isEmpty) template.html = htmlGenerator(template.node);
- return template;
- }
-
#analyze(node) {
const isJSXFragment = node.isJSXFragment = node.type === 'JSXFragment';
diff --git a/packages/compiler/transform/HtmlGenerator.js b/packages/compiler/transform/HtmlGenerator.js
index c927ef2..46b12ec 100644
--- a/packages/compiler/transform/HtmlGenerator.js
+++ b/packages/compiler/transform/HtmlGenerator.js
@@ -94,7 +94,6 @@ export class HtmlGenerator extends Generator {
// ...text...
JSXText({ value }, state) {
- // const normalized = value.replace(NORMALIZE_PATTERN, ' ');
state.write(value);
}
}
diff --git a/packages/compiler/transform/Template.js b/packages/compiler/transform/Template.js
index 2f008e9..e5f2b15 100644
--- a/packages/compiler/transform/Template.js
+++ b/packages/compiler/transform/Template.js
@@ -1,38 +1,42 @@
+import { generate } from 'astring';
import revHash from 'rev-hash';
+import { HtmlGenerator } from './HtmlGenerator.js';
-export class Template {
- isDomFragment = false;
- isEmpty = false;
- isStatic = false;
- #html = '';
- #id = '';
-
- get id() {
- return this.#id;
- }
- get html() {
- return this.#html;
- }
+const generator = new HtmlGenerator();
+const htmlGenerator = node => generate(node, { generator });
- set html(html) {
- this.#html = html;
- this.#id = revHash(html);
- }
+export class Template {
constructor(node, { bindings, boundElements, imports }) {
- this.node = node;
- this.bindings = bindings;
- this.boundElements = boundElements;
- this.imports = imports;
-
if(node.isComponent && bindings.length) {
throw new Error('Unexpected component binding length');
}
+ this.node = node;
+ this.bindings = bindings;
+ this.boundElements = boundElements;
+ this.imports = imports;
this.isBoundRoot = node.queryIndex === -1;
this.isDomFragment = node.isJSXFragment;
this.isEmpty = node.isComponent ||
(node.isJSXFragment && node.children.length === 0);
this.isStatic = this.isEmpty || (!boundElements.length) && node.queryIndex !== -1;
+
+ this.html = this.isEmpty ? '' : htmlGenerator(node);
+
+ let tKey = '', bKey = '';
+ if(bindings.length) {
+ tKey = revHash(bindings.map(({ type, index, element: { isRoot, queryIndex } }) => {
+ return (isRoot ? '' : `${queryIndex}`) + (type === 'child' ? `:${index}` : '');
+ }).join());
+ bKey = revHash(bindings.map(({ type, node }) => {
+ return type === 'prop' ? node.name.name : '';
+ }).join());
+ }
+
+ this.targetKey = tKey;
+ this.bindKey = bKey;
+ this.id = revHash(this.html + this.bindKey + this.targetKey);
+
}
}
diff --git a/packages/compiler/transform/TemplateGenerator.js b/packages/compiler/transform/Transpiler.js
similarity index 91%
rename from packages/compiler/transform/TemplateGenerator.js
rename to packages/compiler/transform/Transpiler.js
index 9d475f0..5195c26 100644
--- a/packages/compiler/transform/TemplateGenerator.js
+++ b/packages/compiler/transform/Transpiler.js
@@ -1,22 +1,11 @@
import { generate } from 'astring';
import { HtmlGenerator } from './HtmlGenerator.js';
-import { Generator, writeNextLine } from './GeneratorBase.js';
-import { isValidESIdentifier } from 'is-valid-es-identifier';
+import { Generator } from './GeneratorBase.js';
import { Analyzer } from './Analyzer.js';
export const templateModule = `virtual:azoth-templates`;
-const OPENING_PROP = {
- JSXElement: 'openingElement',
- JSXFragment: 'openingFragment',
-};
-
-const IS_OPENING = {
- JSXOpeningElement: true,
- JSXOpeningFragment: true,
-};
-
-export class TemplateGenerator extends Generator {
+export class Transpiler extends Generator {
templates = [];
uniqueIds = new Set();
@@ -73,7 +62,7 @@ export class TemplateGenerator extends Generator {
JSXTemplate(node, state) {
const analyzer = new Analyzer(node);
- const template = analyzer.generateTemplate(this.htmlGenerator);
+ const template = analyzer.template;
const { templates, uniqueIds } = this;
templates.push(template);
diff --git a/packages/compiler/transform/template-generators.js b/packages/compiler/transform/template-generators.js
index 23c5d02..24287a2 100644
--- a/packages/compiler/transform/template-generators.js
+++ b/packages/compiler/transform/template-generators.js
@@ -12,12 +12,20 @@ export function makeTargets(template) {
: target;
});
- return `const targets = (${elLength ? 'r,t' : 'r'}) => [${values.join()}];\n`;
+ return `(${elLength ? 'r,t' : 'r'}) => [${values.join()}]`;
}
-export function makeRenderer({ id, isDomFragment, html }, options) {
- const content = options?.noContent ? '' : `, ${html}`;
- return `const renderDOM = renderer('${id}', targets, bind, ${isDomFragment}${content});\n`;
+export function makeRenderer({ id, targetKey, bindKey, isDomFragment, html }, options) {
+ const content = !!options?.includeContent;
+ const target = targetKey ? `g${targetKey}` : `null`;
+ const bind = bindKey ? `b${bindKey}` : `null`;
+
+ let renderer = `__renderer(`;
+ renderer += `"${id}", ${target}, ${bind}, ${isDomFragment}`;
+ if(content) renderer += `, \`${html}\``;
+ renderer += `)`;
+
+ return renderer;
}
const TARGETS = 'ts';
@@ -32,16 +40,12 @@ export function makeBind({ bindings }) {
}
const bound = bindings.map(({ type, node }, index) => {
- if(node.isComponent) {
- throw new Error('need compose element');
- // return ComposeElement(node, expr, i, state);
- }
if(type === 'child') {
- return `compose(${TARGET}${index}, ${VALUE}${index});`;
+ return `__compose(${TARGET}${index}, ${VALUE}${index});`;
}
if(type === 'prop') {
// TODO: consider source maps for prop on element
- // TODO: refactor with component props names
+ // TODO: refactor with component props names when DOMProp/attr lookup exists
const identity = node.name;
const propName = identity.name;
const isValidId = isValidESIdentifier(propName);
@@ -54,11 +58,11 @@ export function makeBind({ bindings }) {
throw new Error(message);
});
- return `function bind(${TARGETS}) {
+ return `(${TARGETS}) => {
const ${targets.join(', ')};
return (${params.join(', ')}) => {
${bound.join('\n ')}
};
-}\n`;
+}`;
}
diff --git a/packages/compiler/transform/template-generators.test.js b/packages/compiler/transform/template-generators.test.js
index e54424d..b27127a 100644
--- a/packages/compiler/transform/template-generators.test.js
+++ b/packages/compiler/transform/template-generators.test.js
@@ -1,5 +1,5 @@
/* eslint-disable no-undef */
-import { makeTargets, makeRenderer, makeRender, makeBind } from './template-generators.js';
+import { makeTargets, makeRenderer, makeBind } from './template-generators.js';
import { parse, generate as _generate } from '../compiler.js';
import { describe, test, beforeEach } from 'vitest';
@@ -22,10 +22,7 @@ describe('targets generator', () => {
test('simple', ({ compile, expect }) => {
const code = compile(`name => {name}
`);
- expect(code).toMatchInlineSnapshot(`
- "const targets = (r) => [r.childNodes[0]];
- "
- `);
+ expect(code).toMatchInlineSnapshot(`"(r) => [r.childNodes[0]]"`);
});
test('props and elements', ({ compile, expect }) => {
@@ -33,100 +30,105 @@ describe('targets generator', () => {
{"Greeting"} hey {"Azoth"}!
;`);
expect(code).toMatchInlineSnapshot(
- `
- "const targets = (r,t) => [r,r.childNodes[1],t[0].childNodes[1]];
- "
- `
+ `"(r,t) => [r,r.childNodes[1],t[0].childNodes[1]]"`
);
});
});
-describe('renderDOM generator', () => {
+describe('bind generator', () => {
beforeEach(context => {
context.compile = code => {
const template = preParse(code, context.expect);
- return makeRenderer(template);
+ return makeBind(template);
};
});
test('simple', ({ compile, expect }) => {
- const code = compile(`name => {name}
`, expect);
-
+ const code = compile(`name => {name}
`);
expect(code).toMatchInlineSnapshot(`
- "const renderDOM = renderer('904ca237ee', targets, bind, false, );
- "
+ "(ts) => {
+ const t0 = ts[0];
+ return (v0) => {
+ compose(t0, v0);
+ };
+ }"
`);
});
-
test('props and elements', ({ compile, expect }) => {
const code = compile(`const t =
{"Greeting"} hey {"Azoth"}!
;`);
-
expect(code).toMatchInlineSnapshot(
`
- "const renderDOM = renderer('5252cfebed', targets, bind, false,
- hey !
-
);
- "
+ "(ts) => {
+ const t0 = ts[0], t1 = ts[1], t2 = ts[2];
+ return (v0, v1, v2) => {
+ t0.className = v0;
+ compose(t1, v1);
+ compose(t2, v2);
+ };
+ }"
`
);
});
-
- test('option noContent: true', ({ expect }) => {
- const template = preParse(`name => {name}
`, expect);
- const code = makeRenderer(template, { noContent: true });
-
- expect(code).toMatchInlineSnapshot(`
- "const renderDOM = renderer('904ca237ee', targets, bind, false);
- "
- `);
- });
-
-
-
});
-describe('bind generator', () => {
+describe('renderDOM generator', () => {
beforeEach(context => {
context.compile = code => {
const template = preParse(code, context.expect);
- return makeBind(template);
+ return makeRenderer(template, { includeContent: true });
};
});
test('simple', ({ compile, expect }) => {
const code = compile(`name => {name}
`);
- expect(code).toMatchInlineSnapshot(`
- "function bind(ts) {
- const t0 = ts[0];
- return (v0) => {
- compose(t0, v0);
- };
- }
- "
- `);
+
+ expect(code).toMatchInlineSnapshot(`"renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false, "")"`);
});
+ test('static', ({ compile, expect }) => {
+ const code = compile(`() => static
`);
+
+ expect(code).toMatchInlineSnapshot(`"renderer("e8a7ca1ef0", null, null, false, "static
")"`);
+ });
+
+
test('props and elements', ({ compile, expect }) => {
const code = compile(`const t =
{"Greeting"} hey {"Azoth"}!
;`);
+
expect(code).toMatchInlineSnapshot(
`
- "function bind(ts) {
- const t0 = ts[0], t1 = ts[1], t2 = ts[2];
- return (v0, v1, v2) => {
- t0.className = v0;
- compose(t1, v1);
- compose(t2, v2);
- };
- }
- "
+ "renderer("b32dab1494", g98cb41d3ff, bb90a39b45c, false, "
+ hey !
+
")"
`
);
});
+
+ test('option { noContent: true }', ({ expect }) => {
+ const template = preParse(`name => {name}
`, expect);
+ const code = makeRenderer(template, { noContent: true });
+
+ expect(code).toMatchInlineSnapshot(`"renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false)"`);
+ });
+
+ test('option inject { targets: code, bind: code }', ({ expect }) => {
+ const template = preParse(`name => {name}
`, expect);
+ const code = makeRenderer(template, {
+ targets: `"targets!"`,
+ bind: `"bind!"`,
+ });
+
+ expect(code).toMatchInlineSnapshot(`"renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false)"`);
+ });
+
+
+
});
+
diff --git a/packages/runtime/renderer/index.js b/packages/runtime/renderer/index.js
index b579bc3..9687fff 100644
--- a/packages/runtime/renderer/index.js
+++ b/packages/runtime/renderer/index.js
@@ -1 +1 @@
-export { makeRenderer, rendererById } from './renderer.js';
\ No newline at end of file
+export { renderer } from './renderer.js';
\ No newline at end of file
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index 2fa9bcf..b9739bb 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -54,7 +54,7 @@ function inject(node, callback) {
const templateRenderer = getBound => (...args) => {
const [root, bind] = getBound();
- bind(...args);
+ if(bind) bind(...args);
return root;
};
@@ -68,8 +68,11 @@ export function renderer(id, targets, makeBind, isFragment, content) {
// TODO: test injectable is right template id type
- if(node) bind = bindings.get(node);
- if(bind) return [node, bind];
+ if(node) {
+ const hasBind = bindings.has(node);
+ bind = bindings.get(node);
+ if(hasBind) return [node, bind];
+ }
// Honestly not sure this really needed,
// use case would be list component optimize by
@@ -81,8 +84,8 @@ export function renderer(id, targets, makeBind, isFragment, content) {
([node, boundEls] = create());
}
- const nodes = targets(node, boundEls);
- bind = makeBind(nodes);
+ const nodes = targets ? targets(node, boundEls) : null;
+ bind = makeBind ? makeBind(nodes) : null;
bindings.set(node, bind);
return [node, bind];
diff --git a/packages/vite-plugin/index.js b/packages/vite-plugin/index.js
index 8ddc96c..e3f4821 100644
--- a/packages/vite-plugin/index.js
+++ b/packages/vite-plugin/index.js
@@ -1,9 +1,20 @@
-import { compile, templateModule } from '@azothjs/compiler';
+import revHash from 'rev-hash';
+import { compile, templateModule, makeTargets, makeRenderer, makeBind } from '@azothjs/compiler';
import { createFilter } from '@rollup/pluginutils';
import path from 'node:path';
+export const targetModule = `virtual:azoth-target`;
+export const bindModule = `virtual:azoth-bind`;
+const resolvedTargetModule = '\0' + targetModule;
+const resolvedBindModule = '\0' + bindModule;
const resolvedTemplateModule = '\0' + templateModule;
+const RESOLVED = {
+ [templateModule]: resolvedTemplateModule,
+ [targetModule]: resolvedTargetModule,
+ [bindModule]: resolvedBindModule,
+};
+
export default function azothPlugin(options) {
options = options ?? {};
@@ -11,6 +22,9 @@ export default function azothPlugin(options) {
const filter = createFilter(include, exclude);
const programTemplates = new Map();
+ const byTarget = new Map();
+ const byBind = new Map();
+
let command = '';
const transformJSX = {
@@ -23,37 +37,26 @@ export default function azothPlugin(options) {
resolveId(id) {
const [name, ids] = id.split('?', 2);
- if(name !== templateModule) return;
- return ids ? `${resolvedTemplateModule}?${ids}` : resolvedTemplateModule;
+ const resolved = RESOLVED[name];
+ if(!resolved) return;
+ return `${resolved}?${ids}`;
},
load(id) {
- const [name, ids] = id.split('?', 2);
- if(name !== resolvedTemplateModule) return;
-
+ const [name, params] = id.split('?', 2);
const isBuild = command === 'build';
- const renderer = isBuild ? '__rendererById' : '__makeRenderer';
- const importRenderer = `import { ${renderer} } from 'azoth/runtime';\n`;
-
- const exports = new URLSearchParams(ids)
- .getAll('id')
- .map(id => {
- const { html, isDomFragment } = programTemplates.get(id);
- let exportRender = `\nexport const t${id} = ${renderer}('${id}'`;
-
- // html gets added to index.html in build,
- // but dev mode html is string in virtual module
- if(!isBuild) exportRender += `, \`${html}\``;
-
- // default is false, so only add if true (which is less common)
- if(isDomFragment) exportRender += ', true';
-
- exportRender += `);\n`;
- return exportRender;
- })
- .join('');
-
- return importRenderer + exports;
+ const ids = new URLSearchParams(params).getAll('id');
+
+ switch(name) {
+ case resolvedTemplateModule:
+ return loadTemplateModule(ids, isBuild);
+ case resolvedTargetModule:
+ return loadTargetModule(ids);
+ case resolvedBindModule:
+ return loadBindModule(ids);
+ default:
+ return;
+ }
},
async transform(source, id) {
@@ -76,6 +79,50 @@ export default function azothPlugin(options) {
}
};
+ function loadTargetModule([id]) {
+ return `export const g${id} = ${makeTargets(byTarget.get(id))};`;
+ }
+
+ function loadBindModule([id]) {
+ return `import { __compose } from 'azoth/runtime';\nexport const b${id} = ${makeBind(byBind.get(id))};`;
+ }
+
+ function loadTemplateModule(ids, isBuild) {
+ const moduleImports = [`import { __renderer } from 'azoth/runtime';\n`];
+
+ const templates = ids.map(id => programTemplates.get(id));
+
+ const targetGenerators = new Set();
+ const bindGenerators = new Set();
+
+ const templateExports = templates.map(template => {
+ const { id, targetKey, bindKey } = template;
+
+ // TODO: refactor cleanup on this apparent duplication
+
+ if(targetKey) {
+ if(!byTarget.has(targetKey)) byTarget.set(targetKey, template);
+ if(!targetGenerators.has(targetKey)) {
+ moduleImports.push(`import { g${targetKey} } from '${targetModule}?id=${targetKey}';\n`);
+ targetGenerators.add(targetKey);
+ }
+ }
+
+ if(bindKey) {
+ if(!byBind.has(bindKey)) byBind.set(bindKey, template);
+ if(!bindGenerators.has(bindKey)) {
+ moduleImports.push(`import { b${bindKey} } from '${bindModule}?id=${bindKey}';\n`);
+ bindGenerators.add(bindKey);
+ }
+ }
+
+ return `export const t${id} = ${makeRenderer(template, { includeContent: !isBuild })};\n`;
+
+ });
+
+ return moduleImports.join('') + templateExports.join('');
+ }
+
const TEMPLATE_COMMENT = '';
const BODY_START = '';
const makeReplacement = (html, prefix) => `${prefix}\n ${html}\n `;
@@ -100,3 +147,4 @@ export default function azothPlugin(options) {
return [transformJSX, injectHTML];
}
+
diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json
index 41eba31..532363e 100644
--- a/packages/vite-plugin/package.json
+++ b/packages/vite-plugin/package.json
@@ -37,6 +37,7 @@
"dependencies": {
"@azothjs/compiler": "workspace:*",
"@rollup/pluginutils": "^5.1.0",
+ "rev-hash": "^4.1.0",
"source-map": "^0.7.4"
}
}
\ No newline at end of file
diff --git a/packages/vite-plugin/pnpm-lock.yaml b/packages/vite-plugin/pnpm-lock.yaml
index 3090e96..09d2dde 100644
--- a/packages/vite-plugin/pnpm-lock.yaml
+++ b/packages/vite-plugin/pnpm-lock.yaml
@@ -11,17 +11,20 @@ dependencies:
'@rollup/pluginutils':
specifier: ^5.1.0
version: 5.1.0
+ rev-hash:
+ specifier: ^4.1.0
+ version: 4.1.0
source-map:
specifier: ^0.7.4
version: 0.7.4
vite:
specifier: ^5.0.12
- version: 5.1.6
+ version: 5.2.2
packages:
- /@esbuild/aix-ppc64@0.19.12:
- resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+ /@esbuild/aix-ppc64@0.20.2:
+ resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
@@ -29,8 +32,8 @@ packages:
dev: false
optional: true
- /@esbuild/android-arm64@0.19.12:
- resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+ /@esbuild/android-arm64@0.20.2:
+ resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
@@ -38,8 +41,8 @@ packages:
dev: false
optional: true
- /@esbuild/android-arm@0.19.12:
- resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+ /@esbuild/android-arm@0.20.2:
+ resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
@@ -47,8 +50,8 @@ packages:
dev: false
optional: true
- /@esbuild/android-x64@0.19.12:
- resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+ /@esbuild/android-x64@0.20.2:
+ resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
@@ -56,8 +59,8 @@ packages:
dev: false
optional: true
- /@esbuild/darwin-arm64@0.19.12:
- resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+ /@esbuild/darwin-arm64@0.20.2:
+ resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
@@ -65,8 +68,8 @@ packages:
dev: false
optional: true
- /@esbuild/darwin-x64@0.19.12:
- resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+ /@esbuild/darwin-x64@0.20.2:
+ resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
@@ -74,8 +77,8 @@ packages:
dev: false
optional: true
- /@esbuild/freebsd-arm64@0.19.12:
- resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+ /@esbuild/freebsd-arm64@0.20.2:
+ resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
@@ -83,8 +86,8 @@ packages:
dev: false
optional: true
- /@esbuild/freebsd-x64@0.19.12:
- resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+ /@esbuild/freebsd-x64@0.20.2:
+ resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
@@ -92,8 +95,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-arm64@0.19.12:
- resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+ /@esbuild/linux-arm64@0.20.2:
+ resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
@@ -101,8 +104,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-arm@0.19.12:
- resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+ /@esbuild/linux-arm@0.20.2:
+ resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
@@ -110,8 +113,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-ia32@0.19.12:
- resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+ /@esbuild/linux-ia32@0.20.2:
+ resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
@@ -119,8 +122,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-loong64@0.19.12:
- resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+ /@esbuild/linux-loong64@0.20.2:
+ resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
@@ -128,8 +131,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-mips64el@0.19.12:
- resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+ /@esbuild/linux-mips64el@0.20.2:
+ resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
@@ -137,8 +140,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-ppc64@0.19.12:
- resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+ /@esbuild/linux-ppc64@0.20.2:
+ resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
@@ -146,8 +149,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-riscv64@0.19.12:
- resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+ /@esbuild/linux-riscv64@0.20.2:
+ resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
@@ -155,8 +158,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-s390x@0.19.12:
- resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+ /@esbuild/linux-s390x@0.20.2:
+ resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
@@ -164,8 +167,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-x64@0.19.12:
- resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+ /@esbuild/linux-x64@0.20.2:
+ resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
@@ -173,8 +176,8 @@ packages:
dev: false
optional: true
- /@esbuild/netbsd-x64@0.19.12:
- resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+ /@esbuild/netbsd-x64@0.20.2:
+ resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
@@ -182,8 +185,8 @@ packages:
dev: false
optional: true
- /@esbuild/openbsd-x64@0.19.12:
- resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+ /@esbuild/openbsd-x64@0.20.2:
+ resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
@@ -191,8 +194,8 @@ packages:
dev: false
optional: true
- /@esbuild/sunos-x64@0.19.12:
- resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+ /@esbuild/sunos-x64@0.20.2:
+ resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
@@ -200,8 +203,8 @@ packages:
dev: false
optional: true
- /@esbuild/win32-arm64@0.19.12:
- resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+ /@esbuild/win32-arm64@0.20.2:
+ resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
@@ -209,8 +212,8 @@ packages:
dev: false
optional: true
- /@esbuild/win32-ia32@0.19.12:
- resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+ /@esbuild/win32-ia32@0.20.2:
+ resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
@@ -218,8 +221,8 @@ packages:
dev: false
optional: true
- /@esbuild/win32-x64@0.19.12:
- resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+ /@esbuild/win32-x64@0.20.2:
+ resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
@@ -349,35 +352,35 @@ packages:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: false
- /esbuild@0.19.12:
- resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
+ /esbuild@0.20.2:
+ resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
- '@esbuild/aix-ppc64': 0.19.12
- '@esbuild/android-arm': 0.19.12
- '@esbuild/android-arm64': 0.19.12
- '@esbuild/android-x64': 0.19.12
- '@esbuild/darwin-arm64': 0.19.12
- '@esbuild/darwin-x64': 0.19.12
- '@esbuild/freebsd-arm64': 0.19.12
- '@esbuild/freebsd-x64': 0.19.12
- '@esbuild/linux-arm': 0.19.12
- '@esbuild/linux-arm64': 0.19.12
- '@esbuild/linux-ia32': 0.19.12
- '@esbuild/linux-loong64': 0.19.12
- '@esbuild/linux-mips64el': 0.19.12
- '@esbuild/linux-ppc64': 0.19.12
- '@esbuild/linux-riscv64': 0.19.12
- '@esbuild/linux-s390x': 0.19.12
- '@esbuild/linux-x64': 0.19.12
- '@esbuild/netbsd-x64': 0.19.12
- '@esbuild/openbsd-x64': 0.19.12
- '@esbuild/sunos-x64': 0.19.12
- '@esbuild/win32-arm64': 0.19.12
- '@esbuild/win32-ia32': 0.19.12
- '@esbuild/win32-x64': 0.19.12
+ '@esbuild/aix-ppc64': 0.20.2
+ '@esbuild/android-arm': 0.20.2
+ '@esbuild/android-arm64': 0.20.2
+ '@esbuild/android-x64': 0.20.2
+ '@esbuild/darwin-arm64': 0.20.2
+ '@esbuild/darwin-x64': 0.20.2
+ '@esbuild/freebsd-arm64': 0.20.2
+ '@esbuild/freebsd-x64': 0.20.2
+ '@esbuild/linux-arm': 0.20.2
+ '@esbuild/linux-arm64': 0.20.2
+ '@esbuild/linux-ia32': 0.20.2
+ '@esbuild/linux-loong64': 0.20.2
+ '@esbuild/linux-mips64el': 0.20.2
+ '@esbuild/linux-ppc64': 0.20.2
+ '@esbuild/linux-riscv64': 0.20.2
+ '@esbuild/linux-s390x': 0.20.2
+ '@esbuild/linux-x64': 0.20.2
+ '@esbuild/netbsd-x64': 0.20.2
+ '@esbuild/openbsd-x64': 0.20.2
+ '@esbuild/sunos-x64': 0.20.2
+ '@esbuild/win32-arm64': 0.20.2
+ '@esbuild/win32-ia32': 0.20.2
+ '@esbuild/win32-x64': 0.20.2
dev: false
/estree-walker@2.0.2:
@@ -407,13 +410,18 @@ packages:
engines: {node: '>=8.6'}
dev: false
- /postcss@8.4.35:
- resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
+ /postcss@8.4.38:
+ resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
- source-map-js: 1.0.2
+ source-map-js: 1.2.0
+ dev: false
+
+ /rev-hash@4.1.0:
+ resolution: {integrity: sha512-e0EGnaveLY2IYpYwHNdh43WZ2M84KgW3Z/T4F6+Z/BlZI/T1ZbxTWj36xgYgUPOieGXYo2q225jTeUXn+LWYjw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: false
/rollup@4.13.0:
@@ -439,8 +447,8 @@ packages:
fsevents: 2.3.3
dev: false
- /source-map-js@1.0.2:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ /source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
dev: false
@@ -449,8 +457,8 @@ packages:
engines: {node: '>= 8'}
dev: false
- /vite@5.1.6:
- resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
+ /vite@5.2.2:
+ resolution: {integrity: sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -477,8 +485,8 @@ packages:
terser:
optional: true
dependencies:
- esbuild: 0.19.12
- postcss: 8.4.35
+ esbuild: 0.20.2
+ postcss: 8.4.38
rollup: 4.13.0
optionalDependencies:
fsevents: 2.3.3
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5c56a61..f79d407 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -18,17 +18,17 @@ devDependencies:
specifier: ^14.0.0
version: 14.0.0
happy-dom:
- specifier: ^13.8.6
- version: 13.8.6
+ specifier: ^13.10.1
+ version: 13.10.1
vite:
- specifier: ^5.1.6
- version: 5.1.6
+ specifier: ^5.2.2
+ version: 5.2.2
vite-plugin-inspect:
specifier: ^0.8.3
- version: 0.8.3(vite@5.1.6)
+ version: 0.8.3(vite@5.2.2)
vitest:
specifier: ^1.4.0
- version: 1.4.0(happy-dom@13.8.6)
+ version: 1.4.0(happy-dom@13.10.1)
packages:
@@ -41,12 +41,12 @@ packages:
resolution: {integrity: sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg==}
dev: true
- /@babel/code-frame@7.23.5:
- resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
+ /@babel/code-frame@7.24.2:
+ resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/highlight': 7.23.4
- chalk: 2.4.2
+ '@babel/highlight': 7.24.2
+ picocolors: 1.0.0
dev: true
/@babel/helper-validator-identifier@7.22.20:
@@ -54,24 +54,25 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
- /@babel/highlight@7.23.4:
- resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
+ /@babel/highlight@7.24.2:
+ resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.22.20
chalk: 2.4.2
js-tokens: 4.0.0
+ picocolors: 1.0.0
dev: true
- /@babel/runtime@7.24.0:
- resolution: {integrity: sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==}
+ /@babel/runtime@7.24.1:
+ resolution: {integrity: sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.1
dev: true
- /@esbuild/aix-ppc64@0.19.12:
- resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+ /@esbuild/aix-ppc64@0.20.2:
+ resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
@@ -79,8 +80,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm64@0.19.12:
- resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+ /@esbuild/android-arm64@0.20.2:
+ resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
@@ -88,8 +89,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm@0.19.12:
- resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+ /@esbuild/android-arm@0.20.2:
+ resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
@@ -97,8 +98,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-x64@0.19.12:
- resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+ /@esbuild/android-x64@0.20.2:
+ resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
@@ -106,8 +107,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-arm64@0.19.12:
- resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+ /@esbuild/darwin-arm64@0.20.2:
+ resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
@@ -115,8 +116,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-x64@0.19.12:
- resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+ /@esbuild/darwin-x64@0.20.2:
+ resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
@@ -124,8 +125,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-arm64@0.19.12:
- resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+ /@esbuild/freebsd-arm64@0.20.2:
+ resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
@@ -133,8 +134,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-x64@0.19.12:
- resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+ /@esbuild/freebsd-x64@0.20.2:
+ resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
@@ -142,8 +143,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm64@0.19.12:
- resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+ /@esbuild/linux-arm64@0.20.2:
+ resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
@@ -151,8 +152,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm@0.19.12:
- resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+ /@esbuild/linux-arm@0.20.2:
+ resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
@@ -160,8 +161,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ia32@0.19.12:
- resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+ /@esbuild/linux-ia32@0.20.2:
+ resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
@@ -169,8 +170,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-loong64@0.19.12:
- resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+ /@esbuild/linux-loong64@0.20.2:
+ resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
@@ -178,8 +179,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-mips64el@0.19.12:
- resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+ /@esbuild/linux-mips64el@0.20.2:
+ resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
@@ -187,8 +188,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ppc64@0.19.12:
- resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+ /@esbuild/linux-ppc64@0.20.2:
+ resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
@@ -196,8 +197,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-riscv64@0.19.12:
- resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+ /@esbuild/linux-riscv64@0.20.2:
+ resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
@@ -205,8 +206,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-s390x@0.19.12:
- resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+ /@esbuild/linux-s390x@0.20.2:
+ resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
@@ -214,8 +215,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-x64@0.19.12:
- resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+ /@esbuild/linux-x64@0.20.2:
+ resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
@@ -223,8 +224,8 @@ packages:
dev: true
optional: true
- /@esbuild/netbsd-x64@0.19.12:
- resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+ /@esbuild/netbsd-x64@0.20.2:
+ resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
@@ -232,8 +233,8 @@ packages:
dev: true
optional: true
- /@esbuild/openbsd-x64@0.19.12:
- resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+ /@esbuild/openbsd-x64@0.20.2:
+ resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
@@ -241,8 +242,8 @@ packages:
dev: true
optional: true
- /@esbuild/sunos-x64@0.19.12:
- resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+ /@esbuild/sunos-x64@0.20.2:
+ resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
@@ -250,8 +251,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-arm64@0.19.12:
- resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+ /@esbuild/win32-arm64@0.20.2:
+ resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
@@ -259,8 +260,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-ia32@0.19.12:
- resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+ /@esbuild/win32-ia32@0.20.2:
+ resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
@@ -268,8 +269,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-x64@0.19.12:
- resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+ /@esbuild/win32-x64@0.20.2:
+ resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
@@ -496,8 +497,8 @@ packages:
resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==}
engines: {node: '>=14'}
dependencies:
- '@babel/code-frame': 7.23.5
- '@babel/runtime': 7.24.0
+ '@babel/code-frame': 7.24.2
+ '@babel/runtime': 7.24.1
'@types/aria-query': 5.0.4
aria-query: 5.1.3
chalk: 4.1.2
@@ -881,35 +882,35 @@ packages:
stop-iteration-iterator: 1.0.0
dev: true
- /esbuild@0.19.12:
- resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
+ /esbuild@0.20.2:
+ resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
- '@esbuild/aix-ppc64': 0.19.12
- '@esbuild/android-arm': 0.19.12
- '@esbuild/android-arm64': 0.19.12
- '@esbuild/android-x64': 0.19.12
- '@esbuild/darwin-arm64': 0.19.12
- '@esbuild/darwin-x64': 0.19.12
- '@esbuild/freebsd-arm64': 0.19.12
- '@esbuild/freebsd-x64': 0.19.12
- '@esbuild/linux-arm': 0.19.12
- '@esbuild/linux-arm64': 0.19.12
- '@esbuild/linux-ia32': 0.19.12
- '@esbuild/linux-loong64': 0.19.12
- '@esbuild/linux-mips64el': 0.19.12
- '@esbuild/linux-ppc64': 0.19.12
- '@esbuild/linux-riscv64': 0.19.12
- '@esbuild/linux-s390x': 0.19.12
- '@esbuild/linux-x64': 0.19.12
- '@esbuild/netbsd-x64': 0.19.12
- '@esbuild/openbsd-x64': 0.19.12
- '@esbuild/sunos-x64': 0.19.12
- '@esbuild/win32-arm64': 0.19.12
- '@esbuild/win32-ia32': 0.19.12
- '@esbuild/win32-x64': 0.19.12
+ '@esbuild/aix-ppc64': 0.20.2
+ '@esbuild/android-arm': 0.20.2
+ '@esbuild/android-arm64': 0.20.2
+ '@esbuild/android-x64': 0.20.2
+ '@esbuild/darwin-arm64': 0.20.2
+ '@esbuild/darwin-x64': 0.20.2
+ '@esbuild/freebsd-arm64': 0.20.2
+ '@esbuild/freebsd-x64': 0.20.2
+ '@esbuild/linux-arm': 0.20.2
+ '@esbuild/linux-arm64': 0.20.2
+ '@esbuild/linux-ia32': 0.20.2
+ '@esbuild/linux-loong64': 0.20.2
+ '@esbuild/linux-mips64el': 0.20.2
+ '@esbuild/linux-ppc64': 0.20.2
+ '@esbuild/linux-riscv64': 0.20.2
+ '@esbuild/linux-s390x': 0.20.2
+ '@esbuild/linux-x64': 0.20.2
+ '@esbuild/netbsd-x64': 0.20.2
+ '@esbuild/openbsd-x64': 0.20.2
+ '@esbuild/sunos-x64': 0.20.2
+ '@esbuild/win32-arm64': 0.20.2
+ '@esbuild/win32-ia32': 0.20.2
+ '@esbuild/win32-x64': 0.20.2
dev: true
/escape-string-regexp@1.0.5:
@@ -1185,8 +1186,8 @@ packages:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
dev: true
- /happy-dom@13.8.6:
- resolution: {integrity: sha512-Urcv2jvNel19QirWimOwYTW3slpEYGS8PLtzEwAlpTWpnKycXF8s0I7xUBK9fPvWAIc8uZf/CnV2cIwWE8jptw==}
+ /happy-dom@13.10.1:
+ resolution: {integrity: sha512-9GZLEFvQL5EgfJX2zcBgu1nsPUn98JF/EiJnSfQbdxI6YEQGqpd09lXXxOmYonRBIEFz9JlGCOiPflDzgS1p8w==}
engines: {node: '>=16.0.0'}
dependencies:
entities: 4.5.0
@@ -1544,7 +1545,7 @@ packages:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
- ufo: 1.5.1
+ ufo: 1.5.3
dev: true
/mrmime@2.0.0:
@@ -1717,13 +1718,13 @@ packages:
engines: {node: '>= 0.4'}
dev: true
- /postcss@8.4.35:
- resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
+ /postcss@8.4.38:
+ resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
- source-map-js: 1.0.2
+ source-map-js: 1.2.0
dev: true
/prelude-ls@1.2.1:
@@ -1893,8 +1894,8 @@ packages:
totalist: 3.0.1
dev: true
- /source-map-js@1.0.2:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ /source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
dev: true
@@ -1990,8 +1991,8 @@ packages:
engines: {node: '>=10'}
dev: true
- /ufo@1.5.1:
- resolution: {integrity: sha512-HGyF79+/qZ4soRvM+nHERR2pJ3VXDZ/8sL1uLahdgEDf580NkgiWOxLk33FetExqOWp352JZRsgXbG/4MaGOSg==}
+ /ufo@1.5.3:
+ resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==}
dev: true
/universalify@2.0.1:
@@ -2014,7 +2015,7 @@ packages:
debug: 4.3.4
pathe: 1.1.2
picocolors: 1.0.0
- vite: 5.1.6
+ vite: 5.2.2
transitivePeerDependencies:
- '@types/node'
- less
@@ -2026,7 +2027,7 @@ packages:
- terser
dev: true
- /vite-plugin-inspect@0.8.3(vite@5.1.6):
+ /vite-plugin-inspect@0.8.3(vite@5.2.2):
resolution: {integrity: sha512-SBVzOIdP/kwe6hjkt7LSW4D0+REqqe58AumcnCfRNw4Kt3mbS9pEBkch+nupu2PBxv2tQi69EQHQ1ZA1vgB/Og==}
engines: {node: '>=14'}
peerDependencies:
@@ -2045,14 +2046,14 @@ packages:
perfect-debounce: 1.0.0
picocolors: 1.0.0
sirv: 2.0.4
- vite: 5.1.6
+ vite: 5.2.2
transitivePeerDependencies:
- rollup
- supports-color
dev: true
- /vite@5.1.6:
- resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
+ /vite@5.2.2:
+ resolution: {integrity: sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -2079,14 +2080,14 @@ packages:
terser:
optional: true
dependencies:
- esbuild: 0.19.12
- postcss: 8.4.35
+ esbuild: 0.20.2
+ postcss: 8.4.38
rollup: 4.13.0
optionalDependencies:
fsevents: 2.3.3
dev: true
- /vitest@1.4.0(happy-dom@13.8.6):
+ /vitest@1.4.0(happy-dom@13.10.1):
resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
@@ -2120,7 +2121,7 @@ packages:
chai: 4.4.1
debug: 4.3.4
execa: 8.0.1
- happy-dom: 13.8.6
+ happy-dom: 13.10.1
local-pkg: 0.5.0
magic-string: 0.30.8
pathe: 1.1.2
@@ -2129,7 +2130,7 @@ packages:
strip-literal: 2.0.0
tinybench: 2.6.0
tinypool: 0.8.2
- vite: 5.1.6
+ vite: 5.2.2
vite-node: 1.4.0
why-is-node-running: 2.2.2
transitivePeerDependencies:
diff --git a/sandbox/package.json b/sandbox/package.json
index 1bf6e19..23e1f3e 100644
--- a/sandbox/package.json
+++ b/sandbox/package.json
@@ -16,6 +16,6 @@
"jsonic": "workspace:*"
},
"devDependencies": {
- "vite": "^5.1.6"
+ "vite": "^5.2.2"
}
}
\ No newline at end of file
diff --git a/sandbox/pnpm-lock.yaml b/sandbox/pnpm-lock.yaml
index 5846e85..1d5cb17 100644
--- a/sandbox/pnpm-lock.yaml
+++ b/sandbox/pnpm-lock.yaml
@@ -14,13 +14,13 @@ dependencies:
devDependencies:
vite:
- specifier: ^5.1.6
- version: 5.1.6
+ specifier: ^5.2.2
+ version: 5.2.2
packages:
- /@esbuild/aix-ppc64@0.19.12:
- resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+ /@esbuild/aix-ppc64@0.20.2:
+ resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
@@ -28,8 +28,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm64@0.19.12:
- resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+ /@esbuild/android-arm64@0.20.2:
+ resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
@@ -37,8 +37,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm@0.19.12:
- resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+ /@esbuild/android-arm@0.20.2:
+ resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
@@ -46,8 +46,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-x64@0.19.12:
- resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+ /@esbuild/android-x64@0.20.2:
+ resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
@@ -55,8 +55,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-arm64@0.19.12:
- resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+ /@esbuild/darwin-arm64@0.20.2:
+ resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
@@ -64,8 +64,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-x64@0.19.12:
- resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+ /@esbuild/darwin-x64@0.20.2:
+ resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
@@ -73,8 +73,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-arm64@0.19.12:
- resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+ /@esbuild/freebsd-arm64@0.20.2:
+ resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
@@ -82,8 +82,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-x64@0.19.12:
- resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+ /@esbuild/freebsd-x64@0.20.2:
+ resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
@@ -91,8 +91,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm64@0.19.12:
- resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+ /@esbuild/linux-arm64@0.20.2:
+ resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
@@ -100,8 +100,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm@0.19.12:
- resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+ /@esbuild/linux-arm@0.20.2:
+ resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
@@ -109,8 +109,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ia32@0.19.12:
- resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+ /@esbuild/linux-ia32@0.20.2:
+ resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
@@ -118,8 +118,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-loong64@0.19.12:
- resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+ /@esbuild/linux-loong64@0.20.2:
+ resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
@@ -127,8 +127,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-mips64el@0.19.12:
- resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+ /@esbuild/linux-mips64el@0.20.2:
+ resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
@@ -136,8 +136,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ppc64@0.19.12:
- resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+ /@esbuild/linux-ppc64@0.20.2:
+ resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
@@ -145,8 +145,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-riscv64@0.19.12:
- resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+ /@esbuild/linux-riscv64@0.20.2:
+ resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
@@ -154,8 +154,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-s390x@0.19.12:
- resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+ /@esbuild/linux-s390x@0.20.2:
+ resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
@@ -163,8 +163,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-x64@0.19.12:
- resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+ /@esbuild/linux-x64@0.20.2:
+ resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
@@ -172,8 +172,8 @@ packages:
dev: true
optional: true
- /@esbuild/netbsd-x64@0.19.12:
- resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+ /@esbuild/netbsd-x64@0.20.2:
+ resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
@@ -181,8 +181,8 @@ packages:
dev: true
optional: true
- /@esbuild/openbsd-x64@0.19.12:
- resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+ /@esbuild/openbsd-x64@0.20.2:
+ resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
@@ -190,8 +190,8 @@ packages:
dev: true
optional: true
- /@esbuild/sunos-x64@0.19.12:
- resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+ /@esbuild/sunos-x64@0.20.2:
+ resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
@@ -199,8 +199,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-arm64@0.19.12:
- resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+ /@esbuild/win32-arm64@0.20.2:
+ resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
@@ -208,8 +208,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-ia32@0.19.12:
- resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+ /@esbuild/win32-ia32@0.20.2:
+ resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
@@ -217,8 +217,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-x64@0.19.12:
- resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+ /@esbuild/win32-x64@0.20.2:
+ resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
@@ -334,35 +334,35 @@ packages:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: true
- /esbuild@0.19.12:
- resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
+ /esbuild@0.20.2:
+ resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
- '@esbuild/aix-ppc64': 0.19.12
- '@esbuild/android-arm': 0.19.12
- '@esbuild/android-arm64': 0.19.12
- '@esbuild/android-x64': 0.19.12
- '@esbuild/darwin-arm64': 0.19.12
- '@esbuild/darwin-x64': 0.19.12
- '@esbuild/freebsd-arm64': 0.19.12
- '@esbuild/freebsd-x64': 0.19.12
- '@esbuild/linux-arm': 0.19.12
- '@esbuild/linux-arm64': 0.19.12
- '@esbuild/linux-ia32': 0.19.12
- '@esbuild/linux-loong64': 0.19.12
- '@esbuild/linux-mips64el': 0.19.12
- '@esbuild/linux-ppc64': 0.19.12
- '@esbuild/linux-riscv64': 0.19.12
- '@esbuild/linux-s390x': 0.19.12
- '@esbuild/linux-x64': 0.19.12
- '@esbuild/netbsd-x64': 0.19.12
- '@esbuild/openbsd-x64': 0.19.12
- '@esbuild/sunos-x64': 0.19.12
- '@esbuild/win32-arm64': 0.19.12
- '@esbuild/win32-ia32': 0.19.12
- '@esbuild/win32-x64': 0.19.12
+ '@esbuild/aix-ppc64': 0.20.2
+ '@esbuild/android-arm': 0.20.2
+ '@esbuild/android-arm64': 0.20.2
+ '@esbuild/android-x64': 0.20.2
+ '@esbuild/darwin-arm64': 0.20.2
+ '@esbuild/darwin-x64': 0.20.2
+ '@esbuild/freebsd-arm64': 0.20.2
+ '@esbuild/freebsd-x64': 0.20.2
+ '@esbuild/linux-arm': 0.20.2
+ '@esbuild/linux-arm64': 0.20.2
+ '@esbuild/linux-ia32': 0.20.2
+ '@esbuild/linux-loong64': 0.20.2
+ '@esbuild/linux-mips64el': 0.20.2
+ '@esbuild/linux-ppc64': 0.20.2
+ '@esbuild/linux-riscv64': 0.20.2
+ '@esbuild/linux-s390x': 0.20.2
+ '@esbuild/linux-x64': 0.20.2
+ '@esbuild/netbsd-x64': 0.20.2
+ '@esbuild/openbsd-x64': 0.20.2
+ '@esbuild/sunos-x64': 0.20.2
+ '@esbuild/win32-arm64': 0.20.2
+ '@esbuild/win32-ia32': 0.20.2
+ '@esbuild/win32-x64': 0.20.2
dev: true
/fsevents@2.3.3:
@@ -383,13 +383,13 @@ packages:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
- /postcss@8.4.35:
- resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
+ /postcss@8.4.38:
+ resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
- source-map-js: 1.0.2
+ source-map-js: 1.2.0
dev: true
/rollup@4.13.0:
@@ -415,13 +415,13 @@ packages:
fsevents: 2.3.3
dev: true
- /source-map-js@1.0.2:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ /source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
dev: true
- /vite@5.1.6:
- resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
+ /vite@5.2.2:
+ resolution: {integrity: sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -448,8 +448,8 @@ packages:
terser:
optional: true
dependencies:
- esbuild: 0.19.12
- postcss: 8.4.35
+ esbuild: 0.20.2
+ postcss: 8.4.38
rollup: 4.13.0
optionalDependencies:
fsevents: 2.3.3
diff --git a/vite-test/expected-out/index-BDbUw1te.js b/vite-test/expected-out/index-BDbUw1te.js
deleted file mode 100644
index b61aa69..0000000
--- a/vite-test/expected-out/index-BDbUw1te.js
+++ /dev/null
@@ -1,250 +0,0 @@
-/* compose, composeElement, create, createElement */
-const IGNORE = Symbol.for('azoth.compose.IGNORE');
-
-function compose(anchor, input, keepLast, props, slottable) {
- if(keepLast !== true) keepLast = false;
- const type = typeof input;
-
- switch(true) {
- case input === IGNORE:
- break;
- case input === undefined:
- case input === null:
- case input === true:
- case input === false:
- case input === '':
- if(!keepLast) clear(anchor);
- break;
- case type === 'number':
- case type === 'bigint':
- input = `${input}`;
- // eslint-disable-next-line no-fallthrough
- case type === 'string':
- replace(anchor, input, keepLast);
- break;
- case input instanceof Node:
- if(props) Object.assign(input, props);
- if(slottable) input.slottable = slottable;
- replace(anchor, input, keepLast);
- break;
- case type === 'function': {
- // will throw if function is class,
- // unlike create or compose element
- let out = slottable
- ? input(props, slottable)
- : props ? input(props) : input();
- compose(anchor, out, keepLast);
- break;
- }
- case type !== 'object': {
- // ES2023: Symbol should be only type
- throwTypeError(input, type);
- break;
- }
- case input instanceof Promise:
- input.then(value => compose(anchor, value, keepLast, props, slottable));
- break;
- case Array.isArray(input):
- composeArray(anchor, input, keepLast);
- break;
- // w/o the !! this causes intermittent failures :p maybe vitest/node thing?
- case !!input[Symbol.asyncIterator]:
- composeAsyncIterator(anchor, input, keepLast, props, slottable);
- break;
- case input instanceof ReadableStream:
- // no props and slottable propagation on streams
- composeStream(anchor, input, true);
- break;
- case isRenderObject(input): {
- let out = slottable
- ? input.render(props, slottable)
- : props ? input.render(props) : input.render();
- compose(anchor, out, keepLast);
- break;
- }
- // TODO:
- case !!input.subscribe:
- case !!input.on:
- default: {
- throwTypeErrorForObject(input);
- }
- }
-}
-
-const isRenderObject = obj => obj && typeof obj === 'object' && obj.render && typeof obj.render === 'function';
-
-
-/* replace and clear */
-
-function replace(anchor, input, keepLast) {
- if(!keepLast) clear(anchor);
- anchor.before(input);
- anchor.data = ++anchor.data;
-}
-
-function clear(anchor) {
- let node = anchor;
- let count = +anchor.data;
-
- while(count--) {
- const { previousSibling } = node;
- if(!previousSibling) break;
-
- if(previousSibling.nodeType === Node.COMMENT_NODE) {
- // TODO: how to guard for azoth comments only?
- clear(previousSibling);
- }
-
- clear(previousSibling);
- previousSibling.remove();
- }
-
- anchor.data = 0;
-}
-
-
-/* complex types */
-
-function composeArray(anchor, array, keepLast) {
- if(!keepLast) clear(anchor);
- // TODO: optimize arrays here if Node[]
- for(let i = 0; i < array.length; i++) {
- compose(anchor, array[i], true);
- }
-}
-
-async function composeStream(anchor, stream, keepLast) {
- stream.pipeTo(new WritableStream({
- write(chunk) {
- compose(anchor, chunk, keepLast);
- }
- }));
-}
-
-async function composeAsyncIterator(anchor, iterator, keepLast, props, slottable) {
- // TODO: use iterator and intercept system messages
- for await(const value of iterator) {
- compose(anchor, value, keepLast, props, slottable);
- }
-}
-
-/* thrown errors */
-
-function throwTypeError(input, type, footer = '') {
- // Passing Symbol to `{...}` throws!
- if(type === 'symbol') input = 'Symbol';
- throw new TypeError(`\
-Invalid compose {...} input type "${type}", value ${input}.\
-${footer}`
- );
-}
-
-function throwTypeErrorForObject(obj) {
- let message = '';
- try {
- const json = JSON.stringify(obj, null, 2);
- message = `\n\nReceived as:\n\n${json}\n\n`;
- }
- catch(ex) {
- /* no-op */
- }
- throwTypeError(obj, 'object', message);
-}
-
-const templates = new Map();
-
-function rendererById(id, isFragment = false) {
- if(templates.has(id)) return templates.get(id);
-
- const templateEl = document.getElementById(id);
- if(!templateEl) {
- throw new Error(`No template with id "${id}"`);
- }
-
- return rendererFactory(id, templateEl.content, isFragment);
-}
-
-function rendererFactory(id, node, isFragment) {
- const render = renderer(node, isFragment);
- templates.set(id, render);
- return render;
-}
-
-function renderer(fragment, isFragment) {
- if(!isFragment) fragment = fragment.firstElementChild;
- // TODO: malformed fragments...necessary?
-
- return function render() {
- const clone = fragment.cloneNode(true);
- const targets = clone.querySelectorAll('[data-bind]');
- return [clone, targets];
- };
-}
-
-const t14720b3874 = rendererById('14720b3874', true);
-
-const ta51edaabfe = rendererById('a51edaabfe');
-
-const t880311674b = rendererById('880311674b');
-
-const tdfc9870d38 = rendererById('dfc9870d38');
-
-const EMOJIS = 'EMOJIS';
-async function fetchEmojis() {
- const json = localStorage.getItem(EMOJIS);
- if(json) {
- try {
- return JSON.parse(json);
- }
- catch(ex) {
- // failed parse
- localStorage.removeItem(EMOJIS);
- }
- }
- // await sleep(3000);
- const res = await fetch('https://emojihub.yurace.pro/api/all');
- const emojis = await res.json();
-
- localStorage.setItem(EMOJIS, JSON.stringify(emojis, true, 4));
-
- return emojis;
-}
-
-const List = fetchEmojis().then(emojis => EmojiList({
- emojis
-}));
-const App = (() => {
- const [__root, __targets] = t14720b3874(true);
- const __target0 =__targets[0];
- const __child0 = __target0.childNodes[3];
- compose(__child0, List);
- return __root;
-})();
-document.body.append(App);
-function EmojiList({emojis}) {
- const __root = ta51edaabfe()[0];
- const __child0 = __root.childNodes[1];
- compose(__child0, emojis.map(Emoji));
- return __root;
-}
-function Emoji({name, unicode, htmlCode}) {
- const __root = t880311674b()[0];
- const __child0 = __root.childNodes[1];
- const __child1 = __root.childNodes[3];
- const __child2 = __root.childNodes[5];
- compose(__child0, InnerHtml({
- html: htmlCode.join('')
- }));
- compose(__child1, name);
- compose(__child2, unicode);
- return __root;
-}
-function InnerHtml({html, className = ''}) {
- const rawEmoji = (() => {
- const __root = tdfc9870d38()[0];
- __root.className = (className ?? '');
- return __root;
- })();
- rawEmoji.firstChild.innerHTML = html;
- return rawEmoji;
-}
diff --git a/vite-test/expected-out/index-CHvwx500.js b/vite-test/expected-out/index-CHvwx500.js
new file mode 100644
index 0000000..bb22775
--- /dev/null
+++ b/vite-test/expected-out/index-CHvwx500.js
@@ -0,0 +1,374 @@
+/* compose, composeElement, create, createElement */
+const IGNORE = Symbol.for('azoth.compose.IGNORE');
+
+function compose(anchor, input, keepLast, props, slottable) {
+ if(keepLast !== true) keepLast = false;
+ const type = typeof input;
+
+ switch(true) {
+ case input === IGNORE:
+ break;
+ case input === undefined:
+ case input === null:
+ case input === true:
+ case input === false:
+ case input === '':
+ if(!keepLast) clear(anchor);
+ break;
+ case type === 'number':
+ case type === 'bigint':
+ input = `${input}`;
+ // eslint-disable-next-line no-fallthrough
+ case type === 'string':
+ replace(anchor, input, keepLast);
+ break;
+ case input instanceof Node:
+ if(props) Object.assign(input, props);
+ if(slottable) input.slottable = slottable;
+ replace(anchor, input, keepLast);
+ break;
+ case type === 'function': {
+ // will throw if function is class,
+ // unlike create or compose element
+ let out = slottable
+ ? input(props, slottable)
+ : props ? input(props) : input();
+ compose(anchor, out, keepLast);
+ break;
+ }
+ case type !== 'object': {
+ // ES2023: Symbol should be only type
+ throwTypeError(input, type);
+ break;
+ }
+ case input instanceof Promise:
+ input.then(value => compose(anchor, value, keepLast, props, slottable));
+ break;
+ case Array.isArray(input):
+ composeArray(anchor, input, keepLast);
+ break;
+ // w/o the !! this causes intermittent failures :p maybe vitest/node thing?
+ case !!input[Symbol.asyncIterator]:
+ composeAsyncIterator(anchor, input, keepLast, props, slottable);
+ break;
+ case input instanceof ReadableStream:
+ // no props and slottable propagation on streams
+ composeStream(anchor, input, true);
+ break;
+ case isRenderObject(input): {
+ let out = slottable
+ ? input.render(props, slottable)
+ : props ? input.render(props) : input.render();
+ compose(anchor, out, keepLast);
+ break;
+ }
+ // TODO:
+ case !!input.subscribe:
+ case !!input.on:
+ default: {
+ throwTypeErrorForObject(input);
+ }
+ }
+}
+
+const isRenderObject = obj => obj && typeof obj === 'object' && obj.render && typeof obj.render === 'function';
+
+function createElement(Constructor, props, slottable, topLevel = false) {
+ const result = create(Constructor, props, slottable);
+ if(!topLevel) return result;
+
+ // result is returned to caller, not composed by Azoth,
+ // force to be of type Node or null:
+ // strings and numbers into text nodes
+ // non-values to null
+ const type = typeof result;
+ switch(true) {
+ case type === 'string':
+ case type === 'number':
+ return document.createTextNode(result);
+ case result === undefined:
+ case result === null:
+ case result === true:
+ case result === false:
+ case result === IGNORE:
+ return null;
+ default:
+ return result;
+ }
+
+
+}
+
+function create(input, props, slottable, anchor) {
+ const type = typeof input;
+ switch(true) {
+ case input instanceof Node:
+ if(props) Object.assign(input, props);
+ // eslint-disable-next-line no-fallthrough
+ case type === 'string':
+ case type === 'number':
+ case input === undefined:
+ case input === null:
+ case input === true:
+ case input === false:
+ case input === '':
+ case input === IGNORE:
+ return anchor ? void compose(anchor, input) : input;
+ case !!(input.prototype?.constructor): {
+ // eslint-disable-next-line new-cap
+ return create(new input(props, slottable), null, null, anchor);
+ }
+ case type === 'function':
+ return create(input(props, slottable), null, null, anchor);
+ case type !== 'object': {
+ throwTypeError(input, type);
+ break;
+ }
+ case isRenderObject(input):
+ return create(input.render(props, slottable), null, null, anchor);
+ default: {
+ // these inputs require a comment anchor to which they can render
+ if(!anchor) anchor = document.createComment('0');
+
+ if(input[Symbol.asyncIterator]) {
+ composeAsyncIterator(anchor, input, false, props, slottable);
+ }
+ else if(input instanceof Promise) {
+ input.then(value => {
+ create(value, props, slottable, anchor);
+ });
+ }
+ else if(Array.isArray(input)) {
+ composeArray(anchor, input, false);
+ }
+ else {
+ throwTypeErrorForObject(input);
+ }
+
+ return anchor;
+ }
+ }
+}
+
+
+/* replace and clear */
+
+function replace(anchor, input, keepLast) {
+ if(!keepLast) clear(anchor);
+ anchor.before(input);
+ anchor.data = ++anchor.data;
+}
+
+function clear(anchor) {
+ let node = anchor;
+ let count = +anchor.data;
+
+ while(count--) {
+ const { previousSibling } = node;
+ if(!previousSibling) break;
+
+ if(previousSibling.nodeType === Node.COMMENT_NODE) {
+ // TODO: how to guard for azoth comments only?
+ clear(previousSibling);
+ }
+
+ clear(previousSibling);
+ previousSibling.remove();
+ }
+
+ anchor.data = 0;
+}
+
+
+/* complex types */
+
+function composeArray(anchor, array, keepLast) {
+ if(!keepLast) clear(anchor);
+ // TODO: optimize arrays here if Node[]
+ for(let i = 0; i < array.length; i++) {
+ compose(anchor, array[i], true);
+ }
+}
+
+async function composeStream(anchor, stream, keepLast) {
+ stream.pipeTo(new WritableStream({
+ write(chunk) {
+ compose(anchor, chunk, keepLast);
+ }
+ }));
+}
+
+async function composeAsyncIterator(anchor, iterator, keepLast, props, slottable) {
+ // TODO: use iterator and intercept system messages
+ for await(const value of iterator) {
+ compose(anchor, value, keepLast, props, slottable);
+ }
+}
+
+/* thrown errors */
+
+function throwTypeError(input, type, footer = '') {
+ // Passing Symbol to `{...}` throws!
+ if(type === 'symbol') input = 'Symbol';
+ throw new TypeError(`\
+Invalid compose {...} input type "${type}", value ${input}.\
+${footer}`
+ );
+}
+
+function throwTypeErrorForObject(obj) {
+ let message = '';
+ try {
+ const json = JSON.stringify(obj, null, 2);
+ message = `\n\nReceived as:\n\n${json}\n\n`;
+ }
+ catch(ex) {
+ /* no-op */
+ }
+ throwTypeError(obj, 'object', message);
+}
+
+const QUERY_SELECTOR = '[data-bind]';
+const DOMRenderer = {
+ name: 'DOMRenderer',
+
+ createTemplate(id, content, isFragment) {
+ const node = DOMRenderer.template(id, content);
+ const render = DOMRenderer.renderer(node, isFragment);
+ return render;
+ },
+
+ template(id, content) {
+ if(content) return DOMRenderer.create(content);
+ DOMRenderer.getById(id);
+ },
+
+ create(html) {
+ const template = document.createElement('template');
+ template.innerHTML = html;
+ return template.content;
+ },
+ getById(id) {
+ const template = document.getElementById(id);
+ if(!template) {
+ throw new Error(`No template with id "${id}"`);
+ }
+ return template.content;
+ },
+
+ renderer(fragment, isFragment) {
+ if(!isFragment) fragment = fragment.firstElementChild;
+ // TODO: malformed fragment check...necessary?
+
+ return function render() {
+ const clone = fragment.cloneNode(true);
+ const targets = clone.querySelectorAll(QUERY_SELECTOR);
+ return [clone, targets];
+ };
+ },
+ bound(dom) {
+ return dom.querySelectorAll(QUERY_SELECTOR);
+ }
+};
+
+const templates = new Map(); // cache
+let renderEngine = DOMRenderer; // DOM or HTML engine
+
+function get(id, isFragment = false, content) {
+ if(templates.has(id)) return templates.get(id);
+
+ const template = renderEngine.createTemplate(id, content, isFragment);
+
+ templates.set(id, template);
+ return template;
+}
+
+const bindings = new Map(); // cache
+
+// stack
+const injectable = [];
+
+const templateRenderer = getBound => (...args) => {
+ const [root, bind] = getBound();
+ if(bind) bind(...args);
+ return root;
+};
+
+function renderer(id, targets, makeBind, isFragment, content) {
+ const create = get(id, isFragment, content);
+
+ function getBound() {
+ let bind = null;
+ let boundEls = null;
+ let node = injectable.at(-1); // peek!
+
+ // TODO: test injectable is right template id type
+
+ if(node) {
+ const hasBind = bindings.has(node);
+ bind = bindings.get(node);
+ if(hasBind) return [node, bind];
+ }
+
+ // Honestly not sure this really needed,
+ // use case would be list component optimize by
+ // not keeping bind functions?
+ // overhead is small as it is simple function
+ if(node) boundEls = renderEngine.bound(node);
+ else {
+ // (destructuring re-assignment)
+ ([node, boundEls] = create());
+ }
+
+ const nodes = targets ? targets(node, boundEls) : null;
+ bind = makeBind ? makeBind(nodes) : null;
+
+ bindings.set(node, bind);
+ return [node, bind];
+ }
+
+ return templateRenderer(getBound);
+}
+
+const gac282a7be0 = (r,t) => [t[0].childNodes[3]];
+
+const bd41d8cd98f = (ts) => {
+ const t0 = ts[0];
+ return (v0) => {
+ compose(t0, v0);
+ };
+};
+
+const g3558193cd9 = (r) => [r.childNodes[1]];
+
+const g2cc7b6176d = (r,t) => [t[0],r.childNodes[3],r.childNodes[5]];
+
+const bb3ae510d64 = (ts) => {
+ const t0 = ts[0], t1 = ts[1], t2 = ts[2];
+ return (v0, v1, v2) => {
+ t0.innerHTML = v0;
+ compose(t1, v1);
+ compose(t2, v2);
+ };
+};
+
+const tf30ef00ee2 = renderer("f30ef00ee2", gac282a7be0, bd41d8cd98f, true);
+const te23131e855 = renderer("e23131e855", g3558193cd9, bd41d8cd98f, false);
+const t0f61ee8206 = renderer("0f61ee8206", g2cc7b6176d, bb3ae510d64, false);
+
+async function fetchEmojis() {
+ const res = await fetch('https://emojihub.yurace.pro/api/all');
+ return await res.json();
+}
+
+const List = fetchEmojis().then(emojis => EmojiList({
+ emojis
+}));
+const App = tf30ef00ee2(createElement(List));
+document.body.append(App);
+function EmojiList({emojis}) {
+ return te23131e855(emojis.map(Emoji));
+}
+function Emoji({name, unicode, htmlCode}) {
+ return t0f61ee8206(htmlCode.join(''),name,unicode);
+}
diff --git a/vite-test/expected-out/index.html b/vite-test/expected-out/index.html
index b9605c8..e3ea516 100644
--- a/vite-test/expected-out/index.html
+++ b/vite-test/expected-out/index.html
@@ -6,13 +6,13 @@
vite-plugin-azoth
-
+
-
+
Emojis for all my friends
@@ -20,15 +20,14 @@ Emojis for all my friends
Amazing Emoji List
-
+
- -
-
+
-
+
-
diff --git a/vite-test/out/index-BDbUw1te.js b/vite-test/out/index-BDbUw1te.js
deleted file mode 100644
index b61aa69..0000000
--- a/vite-test/out/index-BDbUw1te.js
+++ /dev/null
@@ -1,250 +0,0 @@
-/* compose, composeElement, create, createElement */
-const IGNORE = Symbol.for('azoth.compose.IGNORE');
-
-function compose(anchor, input, keepLast, props, slottable) {
- if(keepLast !== true) keepLast = false;
- const type = typeof input;
-
- switch(true) {
- case input === IGNORE:
- break;
- case input === undefined:
- case input === null:
- case input === true:
- case input === false:
- case input === '':
- if(!keepLast) clear(anchor);
- break;
- case type === 'number':
- case type === 'bigint':
- input = `${input}`;
- // eslint-disable-next-line no-fallthrough
- case type === 'string':
- replace(anchor, input, keepLast);
- break;
- case input instanceof Node:
- if(props) Object.assign(input, props);
- if(slottable) input.slottable = slottable;
- replace(anchor, input, keepLast);
- break;
- case type === 'function': {
- // will throw if function is class,
- // unlike create or compose element
- let out = slottable
- ? input(props, slottable)
- : props ? input(props) : input();
- compose(anchor, out, keepLast);
- break;
- }
- case type !== 'object': {
- // ES2023: Symbol should be only type
- throwTypeError(input, type);
- break;
- }
- case input instanceof Promise:
- input.then(value => compose(anchor, value, keepLast, props, slottable));
- break;
- case Array.isArray(input):
- composeArray(anchor, input, keepLast);
- break;
- // w/o the !! this causes intermittent failures :p maybe vitest/node thing?
- case !!input[Symbol.asyncIterator]:
- composeAsyncIterator(anchor, input, keepLast, props, slottable);
- break;
- case input instanceof ReadableStream:
- // no props and slottable propagation on streams
- composeStream(anchor, input, true);
- break;
- case isRenderObject(input): {
- let out = slottable
- ? input.render(props, slottable)
- : props ? input.render(props) : input.render();
- compose(anchor, out, keepLast);
- break;
- }
- // TODO:
- case !!input.subscribe:
- case !!input.on:
- default: {
- throwTypeErrorForObject(input);
- }
- }
-}
-
-const isRenderObject = obj => obj && typeof obj === 'object' && obj.render && typeof obj.render === 'function';
-
-
-/* replace and clear */
-
-function replace(anchor, input, keepLast) {
- if(!keepLast) clear(anchor);
- anchor.before(input);
- anchor.data = ++anchor.data;
-}
-
-function clear(anchor) {
- let node = anchor;
- let count = +anchor.data;
-
- while(count--) {
- const { previousSibling } = node;
- if(!previousSibling) break;
-
- if(previousSibling.nodeType === Node.COMMENT_NODE) {
- // TODO: how to guard for azoth comments only?
- clear(previousSibling);
- }
-
- clear(previousSibling);
- previousSibling.remove();
- }
-
- anchor.data = 0;
-}
-
-
-/* complex types */
-
-function composeArray(anchor, array, keepLast) {
- if(!keepLast) clear(anchor);
- // TODO: optimize arrays here if Node[]
- for(let i = 0; i < array.length; i++) {
- compose(anchor, array[i], true);
- }
-}
-
-async function composeStream(anchor, stream, keepLast) {
- stream.pipeTo(new WritableStream({
- write(chunk) {
- compose(anchor, chunk, keepLast);
- }
- }));
-}
-
-async function composeAsyncIterator(anchor, iterator, keepLast, props, slottable) {
- // TODO: use iterator and intercept system messages
- for await(const value of iterator) {
- compose(anchor, value, keepLast, props, slottable);
- }
-}
-
-/* thrown errors */
-
-function throwTypeError(input, type, footer = '') {
- // Passing Symbol to `{...}` throws!
- if(type === 'symbol') input = 'Symbol';
- throw new TypeError(`\
-Invalid compose {...} input type "${type}", value ${input}.\
-${footer}`
- );
-}
-
-function throwTypeErrorForObject(obj) {
- let message = '';
- try {
- const json = JSON.stringify(obj, null, 2);
- message = `\n\nReceived as:\n\n${json}\n\n`;
- }
- catch(ex) {
- /* no-op */
- }
- throwTypeError(obj, 'object', message);
-}
-
-const templates = new Map();
-
-function rendererById(id, isFragment = false) {
- if(templates.has(id)) return templates.get(id);
-
- const templateEl = document.getElementById(id);
- if(!templateEl) {
- throw new Error(`No template with id "${id}"`);
- }
-
- return rendererFactory(id, templateEl.content, isFragment);
-}
-
-function rendererFactory(id, node, isFragment) {
- const render = renderer(node, isFragment);
- templates.set(id, render);
- return render;
-}
-
-function renderer(fragment, isFragment) {
- if(!isFragment) fragment = fragment.firstElementChild;
- // TODO: malformed fragments...necessary?
-
- return function render() {
- const clone = fragment.cloneNode(true);
- const targets = clone.querySelectorAll('[data-bind]');
- return [clone, targets];
- };
-}
-
-const t14720b3874 = rendererById('14720b3874', true);
-
-const ta51edaabfe = rendererById('a51edaabfe');
-
-const t880311674b = rendererById('880311674b');
-
-const tdfc9870d38 = rendererById('dfc9870d38');
-
-const EMOJIS = 'EMOJIS';
-async function fetchEmojis() {
- const json = localStorage.getItem(EMOJIS);
- if(json) {
- try {
- return JSON.parse(json);
- }
- catch(ex) {
- // failed parse
- localStorage.removeItem(EMOJIS);
- }
- }
- // await sleep(3000);
- const res = await fetch('https://emojihub.yurace.pro/api/all');
- const emojis = await res.json();
-
- localStorage.setItem(EMOJIS, JSON.stringify(emojis, true, 4));
-
- return emojis;
-}
-
-const List = fetchEmojis().then(emojis => EmojiList({
- emojis
-}));
-const App = (() => {
- const [__root, __targets] = t14720b3874(true);
- const __target0 =__targets[0];
- const __child0 = __target0.childNodes[3];
- compose(__child0, List);
- return __root;
-})();
-document.body.append(App);
-function EmojiList({emojis}) {
- const __root = ta51edaabfe()[0];
- const __child0 = __root.childNodes[1];
- compose(__child0, emojis.map(Emoji));
- return __root;
-}
-function Emoji({name, unicode, htmlCode}) {
- const __root = t880311674b()[0];
- const __child0 = __root.childNodes[1];
- const __child1 = __root.childNodes[3];
- const __child2 = __root.childNodes[5];
- compose(__child0, InnerHtml({
- html: htmlCode.join('')
- }));
- compose(__child1, name);
- compose(__child2, unicode);
- return __root;
-}
-function InnerHtml({html, className = ''}) {
- const rawEmoji = (() => {
- const __root = tdfc9870d38()[0];
- __root.className = (className ?? '');
- return __root;
- })();
- rawEmoji.firstChild.innerHTML = html;
- return rawEmoji;
-}
diff --git a/vite-test/out/index-CHvwx500.js b/vite-test/out/index-CHvwx500.js
new file mode 100644
index 0000000..bb22775
--- /dev/null
+++ b/vite-test/out/index-CHvwx500.js
@@ -0,0 +1,374 @@
+/* compose, composeElement, create, createElement */
+const IGNORE = Symbol.for('azoth.compose.IGNORE');
+
+function compose(anchor, input, keepLast, props, slottable) {
+ if(keepLast !== true) keepLast = false;
+ const type = typeof input;
+
+ switch(true) {
+ case input === IGNORE:
+ break;
+ case input === undefined:
+ case input === null:
+ case input === true:
+ case input === false:
+ case input === '':
+ if(!keepLast) clear(anchor);
+ break;
+ case type === 'number':
+ case type === 'bigint':
+ input = `${input}`;
+ // eslint-disable-next-line no-fallthrough
+ case type === 'string':
+ replace(anchor, input, keepLast);
+ break;
+ case input instanceof Node:
+ if(props) Object.assign(input, props);
+ if(slottable) input.slottable = slottable;
+ replace(anchor, input, keepLast);
+ break;
+ case type === 'function': {
+ // will throw if function is class,
+ // unlike create or compose element
+ let out = slottable
+ ? input(props, slottable)
+ : props ? input(props) : input();
+ compose(anchor, out, keepLast);
+ break;
+ }
+ case type !== 'object': {
+ // ES2023: Symbol should be only type
+ throwTypeError(input, type);
+ break;
+ }
+ case input instanceof Promise:
+ input.then(value => compose(anchor, value, keepLast, props, slottable));
+ break;
+ case Array.isArray(input):
+ composeArray(anchor, input, keepLast);
+ break;
+ // w/o the !! this causes intermittent failures :p maybe vitest/node thing?
+ case !!input[Symbol.asyncIterator]:
+ composeAsyncIterator(anchor, input, keepLast, props, slottable);
+ break;
+ case input instanceof ReadableStream:
+ // no props and slottable propagation on streams
+ composeStream(anchor, input, true);
+ break;
+ case isRenderObject(input): {
+ let out = slottable
+ ? input.render(props, slottable)
+ : props ? input.render(props) : input.render();
+ compose(anchor, out, keepLast);
+ break;
+ }
+ // TODO:
+ case !!input.subscribe:
+ case !!input.on:
+ default: {
+ throwTypeErrorForObject(input);
+ }
+ }
+}
+
+const isRenderObject = obj => obj && typeof obj === 'object' && obj.render && typeof obj.render === 'function';
+
+function createElement(Constructor, props, slottable, topLevel = false) {
+ const result = create(Constructor, props, slottable);
+ if(!topLevel) return result;
+
+ // result is returned to caller, not composed by Azoth,
+ // force to be of type Node or null:
+ // strings and numbers into text nodes
+ // non-values to null
+ const type = typeof result;
+ switch(true) {
+ case type === 'string':
+ case type === 'number':
+ return document.createTextNode(result);
+ case result === undefined:
+ case result === null:
+ case result === true:
+ case result === false:
+ case result === IGNORE:
+ return null;
+ default:
+ return result;
+ }
+
+
+}
+
+function create(input, props, slottable, anchor) {
+ const type = typeof input;
+ switch(true) {
+ case input instanceof Node:
+ if(props) Object.assign(input, props);
+ // eslint-disable-next-line no-fallthrough
+ case type === 'string':
+ case type === 'number':
+ case input === undefined:
+ case input === null:
+ case input === true:
+ case input === false:
+ case input === '':
+ case input === IGNORE:
+ return anchor ? void compose(anchor, input) : input;
+ case !!(input.prototype?.constructor): {
+ // eslint-disable-next-line new-cap
+ return create(new input(props, slottable), null, null, anchor);
+ }
+ case type === 'function':
+ return create(input(props, slottable), null, null, anchor);
+ case type !== 'object': {
+ throwTypeError(input, type);
+ break;
+ }
+ case isRenderObject(input):
+ return create(input.render(props, slottable), null, null, anchor);
+ default: {
+ // these inputs require a comment anchor to which they can render
+ if(!anchor) anchor = document.createComment('0');
+
+ if(input[Symbol.asyncIterator]) {
+ composeAsyncIterator(anchor, input, false, props, slottable);
+ }
+ else if(input instanceof Promise) {
+ input.then(value => {
+ create(value, props, slottable, anchor);
+ });
+ }
+ else if(Array.isArray(input)) {
+ composeArray(anchor, input, false);
+ }
+ else {
+ throwTypeErrorForObject(input);
+ }
+
+ return anchor;
+ }
+ }
+}
+
+
+/* replace and clear */
+
+function replace(anchor, input, keepLast) {
+ if(!keepLast) clear(anchor);
+ anchor.before(input);
+ anchor.data = ++anchor.data;
+}
+
+function clear(anchor) {
+ let node = anchor;
+ let count = +anchor.data;
+
+ while(count--) {
+ const { previousSibling } = node;
+ if(!previousSibling) break;
+
+ if(previousSibling.nodeType === Node.COMMENT_NODE) {
+ // TODO: how to guard for azoth comments only?
+ clear(previousSibling);
+ }
+
+ clear(previousSibling);
+ previousSibling.remove();
+ }
+
+ anchor.data = 0;
+}
+
+
+/* complex types */
+
+function composeArray(anchor, array, keepLast) {
+ if(!keepLast) clear(anchor);
+ // TODO: optimize arrays here if Node[]
+ for(let i = 0; i < array.length; i++) {
+ compose(anchor, array[i], true);
+ }
+}
+
+async function composeStream(anchor, stream, keepLast) {
+ stream.pipeTo(new WritableStream({
+ write(chunk) {
+ compose(anchor, chunk, keepLast);
+ }
+ }));
+}
+
+async function composeAsyncIterator(anchor, iterator, keepLast, props, slottable) {
+ // TODO: use iterator and intercept system messages
+ for await(const value of iterator) {
+ compose(anchor, value, keepLast, props, slottable);
+ }
+}
+
+/* thrown errors */
+
+function throwTypeError(input, type, footer = '') {
+ // Passing Symbol to `{...}` throws!
+ if(type === 'symbol') input = 'Symbol';
+ throw new TypeError(`\
+Invalid compose {...} input type "${type}", value ${input}.\
+${footer}`
+ );
+}
+
+function throwTypeErrorForObject(obj) {
+ let message = '';
+ try {
+ const json = JSON.stringify(obj, null, 2);
+ message = `\n\nReceived as:\n\n${json}\n\n`;
+ }
+ catch(ex) {
+ /* no-op */
+ }
+ throwTypeError(obj, 'object', message);
+}
+
+const QUERY_SELECTOR = '[data-bind]';
+const DOMRenderer = {
+ name: 'DOMRenderer',
+
+ createTemplate(id, content, isFragment) {
+ const node = DOMRenderer.template(id, content);
+ const render = DOMRenderer.renderer(node, isFragment);
+ return render;
+ },
+
+ template(id, content) {
+ if(content) return DOMRenderer.create(content);
+ DOMRenderer.getById(id);
+ },
+
+ create(html) {
+ const template = document.createElement('template');
+ template.innerHTML = html;
+ return template.content;
+ },
+ getById(id) {
+ const template = document.getElementById(id);
+ if(!template) {
+ throw new Error(`No template with id "${id}"`);
+ }
+ return template.content;
+ },
+
+ renderer(fragment, isFragment) {
+ if(!isFragment) fragment = fragment.firstElementChild;
+ // TODO: malformed fragment check...necessary?
+
+ return function render() {
+ const clone = fragment.cloneNode(true);
+ const targets = clone.querySelectorAll(QUERY_SELECTOR);
+ return [clone, targets];
+ };
+ },
+ bound(dom) {
+ return dom.querySelectorAll(QUERY_SELECTOR);
+ }
+};
+
+const templates = new Map(); // cache
+let renderEngine = DOMRenderer; // DOM or HTML engine
+
+function get(id, isFragment = false, content) {
+ if(templates.has(id)) return templates.get(id);
+
+ const template = renderEngine.createTemplate(id, content, isFragment);
+
+ templates.set(id, template);
+ return template;
+}
+
+const bindings = new Map(); // cache
+
+// stack
+const injectable = [];
+
+const templateRenderer = getBound => (...args) => {
+ const [root, bind] = getBound();
+ if(bind) bind(...args);
+ return root;
+};
+
+function renderer(id, targets, makeBind, isFragment, content) {
+ const create = get(id, isFragment, content);
+
+ function getBound() {
+ let bind = null;
+ let boundEls = null;
+ let node = injectable.at(-1); // peek!
+
+ // TODO: test injectable is right template id type
+
+ if(node) {
+ const hasBind = bindings.has(node);
+ bind = bindings.get(node);
+ if(hasBind) return [node, bind];
+ }
+
+ // Honestly not sure this really needed,
+ // use case would be list component optimize by
+ // not keeping bind functions?
+ // overhead is small as it is simple function
+ if(node) boundEls = renderEngine.bound(node);
+ else {
+ // (destructuring re-assignment)
+ ([node, boundEls] = create());
+ }
+
+ const nodes = targets ? targets(node, boundEls) : null;
+ bind = makeBind ? makeBind(nodes) : null;
+
+ bindings.set(node, bind);
+ return [node, bind];
+ }
+
+ return templateRenderer(getBound);
+}
+
+const gac282a7be0 = (r,t) => [t[0].childNodes[3]];
+
+const bd41d8cd98f = (ts) => {
+ const t0 = ts[0];
+ return (v0) => {
+ compose(t0, v0);
+ };
+};
+
+const g3558193cd9 = (r) => [r.childNodes[1]];
+
+const g2cc7b6176d = (r,t) => [t[0],r.childNodes[3],r.childNodes[5]];
+
+const bb3ae510d64 = (ts) => {
+ const t0 = ts[0], t1 = ts[1], t2 = ts[2];
+ return (v0, v1, v2) => {
+ t0.innerHTML = v0;
+ compose(t1, v1);
+ compose(t2, v2);
+ };
+};
+
+const tf30ef00ee2 = renderer("f30ef00ee2", gac282a7be0, bd41d8cd98f, true);
+const te23131e855 = renderer("e23131e855", g3558193cd9, bd41d8cd98f, false);
+const t0f61ee8206 = renderer("0f61ee8206", g2cc7b6176d, bb3ae510d64, false);
+
+async function fetchEmojis() {
+ const res = await fetch('https://emojihub.yurace.pro/api/all');
+ return await res.json();
+}
+
+const List = fetchEmojis().then(emojis => EmojiList({
+ emojis
+}));
+const App = tf30ef00ee2(createElement(List));
+document.body.append(App);
+function EmojiList({emojis}) {
+ return te23131e855(emojis.map(Emoji));
+}
+function Emoji({name, unicode, htmlCode}) {
+ return t0f61ee8206(htmlCode.join(''),name,unicode);
+}
diff --git a/vite-test/out/index.html b/vite-test/out/index.html
index b9605c8..e3ea516 100644
--- a/vite-test/out/index.html
+++ b/vite-test/out/index.html
@@ -6,13 +6,13 @@
vite-plugin-azoth
-
+
-
+
Emojis for all my friends
@@ -20,15 +20,14 @@ Emojis for all my friends
Amazing Emoji List
-
+
- -
-
+
-
+
-
diff --git a/vite-test/package.json b/vite-test/package.json
index f3c43a4..dd59467 100644
--- a/vite-test/package.json
+++ b/vite-test/package.json
@@ -21,7 +21,9 @@
},
"type": "module",
"scripts": {
+ "build": "vite build",
"test": "vite build && vitest -w false ",
+ "start": "vite",
"test:update": "rm -rf ./expected-out && cp -R ./out ./expected-out"
},
"files": [
@@ -36,7 +38,7 @@
}
},
"devDependencies": {
- "vite": "^5.1.6",
+ "vite": "^5.2.2",
"vitest": "^1.4.0"
},
"dependencies": {
diff --git a/vite-test/plugin.test.js b/vite-test/plugin.test.js
index 4ac7f50..6ee6912 100644
--- a/vite-test/plugin.test.js
+++ b/vite-test/plugin.test.js
@@ -12,8 +12,8 @@ test('plugin output produces same snapshot', async ({ expect }) => {
// it programmatically. Likely need to work out directory permission.
// When that's done, switch to dir-compare
- const expectedJS = resolve(__dirname, './expected-out/index-BDbUw1te.js');
- const actualJS = await readFile(resolve(__dirname, './out/index-BDbUw1te.js'), 'utf8');
+ const expectedJS = resolve(__dirname, './expected-out/index-CHvwx500.js');
+ const actualJS = await readFile(resolve(__dirname, './out/index-CHvwx500.js'), 'utf8');
expect(actualJS).toMatchFileSnapshot(expectedJS);
const expectedCSS = resolve(__dirname, './expected-out/index-DDapMaSx.css');
diff --git a/vite-test/pnpm-lock.yaml b/vite-test/pnpm-lock.yaml
index b80dd4b..359d2d9 100644
--- a/vite-test/pnpm-lock.yaml
+++ b/vite-test/pnpm-lock.yaml
@@ -11,16 +11,16 @@ dependencies:
devDependencies:
vite:
- specifier: ^5.1.6
- version: 5.1.6
+ specifier: ^5.2.2
+ version: 5.2.2
vitest:
specifier: ^1.4.0
version: 1.4.0
packages:
- /@esbuild/aix-ppc64@0.19.12:
- resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+ /@esbuild/aix-ppc64@0.20.2:
+ resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
@@ -28,8 +28,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm64@0.19.12:
- resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+ /@esbuild/android-arm64@0.20.2:
+ resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
@@ -37,8 +37,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm@0.19.12:
- resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+ /@esbuild/android-arm@0.20.2:
+ resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
@@ -46,8 +46,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-x64@0.19.12:
- resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+ /@esbuild/android-x64@0.20.2:
+ resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
@@ -55,8 +55,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-arm64@0.19.12:
- resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+ /@esbuild/darwin-arm64@0.20.2:
+ resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
@@ -64,8 +64,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-x64@0.19.12:
- resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+ /@esbuild/darwin-x64@0.20.2:
+ resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
@@ -73,8 +73,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-arm64@0.19.12:
- resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+ /@esbuild/freebsd-arm64@0.20.2:
+ resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
@@ -82,8 +82,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-x64@0.19.12:
- resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+ /@esbuild/freebsd-x64@0.20.2:
+ resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
@@ -91,8 +91,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm64@0.19.12:
- resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+ /@esbuild/linux-arm64@0.20.2:
+ resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
@@ -100,8 +100,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm@0.19.12:
- resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+ /@esbuild/linux-arm@0.20.2:
+ resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
@@ -109,8 +109,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ia32@0.19.12:
- resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+ /@esbuild/linux-ia32@0.20.2:
+ resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
@@ -118,8 +118,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-loong64@0.19.12:
- resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+ /@esbuild/linux-loong64@0.20.2:
+ resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
@@ -127,8 +127,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-mips64el@0.19.12:
- resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+ /@esbuild/linux-mips64el@0.20.2:
+ resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
@@ -136,8 +136,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ppc64@0.19.12:
- resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+ /@esbuild/linux-ppc64@0.20.2:
+ resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
@@ -145,8 +145,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-riscv64@0.19.12:
- resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+ /@esbuild/linux-riscv64@0.20.2:
+ resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
@@ -154,8 +154,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-s390x@0.19.12:
- resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+ /@esbuild/linux-s390x@0.20.2:
+ resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
@@ -163,8 +163,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-x64@0.19.12:
- resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+ /@esbuild/linux-x64@0.20.2:
+ resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
@@ -172,8 +172,8 @@ packages:
dev: true
optional: true
- /@esbuild/netbsd-x64@0.19.12:
- resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+ /@esbuild/netbsd-x64@0.20.2:
+ resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
@@ -181,8 +181,8 @@ packages:
dev: true
optional: true
- /@esbuild/openbsd-x64@0.19.12:
- resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+ /@esbuild/openbsd-x64@0.20.2:
+ resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
@@ -190,8 +190,8 @@ packages:
dev: true
optional: true
- /@esbuild/sunos-x64@0.19.12:
- resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+ /@esbuild/sunos-x64@0.20.2:
+ resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
@@ -199,8 +199,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-arm64@0.19.12:
- resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+ /@esbuild/win32-arm64@0.20.2:
+ resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
@@ -208,8 +208,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-ia32@0.19.12:
- resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+ /@esbuild/win32-ia32@0.20.2:
+ resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
@@ -217,8 +217,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-x64@0.19.12:
- resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+ /@esbuild/win32-x64@0.20.2:
+ resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
@@ -465,35 +465,35 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dev: true
- /esbuild@0.19.12:
- resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
+ /esbuild@0.20.2:
+ resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
- '@esbuild/aix-ppc64': 0.19.12
- '@esbuild/android-arm': 0.19.12
- '@esbuild/android-arm64': 0.19.12
- '@esbuild/android-x64': 0.19.12
- '@esbuild/darwin-arm64': 0.19.12
- '@esbuild/darwin-x64': 0.19.12
- '@esbuild/freebsd-arm64': 0.19.12
- '@esbuild/freebsd-x64': 0.19.12
- '@esbuild/linux-arm': 0.19.12
- '@esbuild/linux-arm64': 0.19.12
- '@esbuild/linux-ia32': 0.19.12
- '@esbuild/linux-loong64': 0.19.12
- '@esbuild/linux-mips64el': 0.19.12
- '@esbuild/linux-ppc64': 0.19.12
- '@esbuild/linux-riscv64': 0.19.12
- '@esbuild/linux-s390x': 0.19.12
- '@esbuild/linux-x64': 0.19.12
- '@esbuild/netbsd-x64': 0.19.12
- '@esbuild/openbsd-x64': 0.19.12
- '@esbuild/sunos-x64': 0.19.12
- '@esbuild/win32-arm64': 0.19.12
- '@esbuild/win32-ia32': 0.19.12
- '@esbuild/win32-x64': 0.19.12
+ '@esbuild/aix-ppc64': 0.20.2
+ '@esbuild/android-arm': 0.20.2
+ '@esbuild/android-arm64': 0.20.2
+ '@esbuild/android-x64': 0.20.2
+ '@esbuild/darwin-arm64': 0.20.2
+ '@esbuild/darwin-x64': 0.20.2
+ '@esbuild/freebsd-arm64': 0.20.2
+ '@esbuild/freebsd-x64': 0.20.2
+ '@esbuild/linux-arm': 0.20.2
+ '@esbuild/linux-arm64': 0.20.2
+ '@esbuild/linux-ia32': 0.20.2
+ '@esbuild/linux-loong64': 0.20.2
+ '@esbuild/linux-mips64el': 0.20.2
+ '@esbuild/linux-ppc64': 0.20.2
+ '@esbuild/linux-riscv64': 0.20.2
+ '@esbuild/linux-s390x': 0.20.2
+ '@esbuild/linux-x64': 0.20.2
+ '@esbuild/netbsd-x64': 0.20.2
+ '@esbuild/openbsd-x64': 0.20.2
+ '@esbuild/sunos-x64': 0.20.2
+ '@esbuild/win32-arm64': 0.20.2
+ '@esbuild/win32-ia32': 0.20.2
+ '@esbuild/win32-x64': 0.20.2
dev: true
/estree-walker@3.0.3:
@@ -592,7 +592,7 @@ packages:
acorn: 8.11.3
pathe: 1.1.2
pkg-types: 1.0.3
- ufo: 1.5.1
+ ufo: 1.5.3
dev: true
/ms@2.1.2:
@@ -656,13 +656,13 @@ packages:
pathe: 1.1.2
dev: true
- /postcss@8.4.35:
- resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
+ /postcss@8.4.38:
+ resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
- source-map-js: 1.0.2
+ source-map-js: 1.2.0
dev: true
/pretty-format@29.7.0:
@@ -722,8 +722,8 @@ packages:
engines: {node: '>=14'}
dev: true
- /source-map-js@1.0.2:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ /source-map-js@1.2.0:
+ resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
dev: true
@@ -765,8 +765,8 @@ packages:
engines: {node: '>=4'}
dev: true
- /ufo@1.5.1:
- resolution: {integrity: sha512-HGyF79+/qZ4soRvM+nHERR2pJ3VXDZ/8sL1uLahdgEDf580NkgiWOxLk33FetExqOWp352JZRsgXbG/4MaGOSg==}
+ /ufo@1.5.3:
+ resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==}
dev: true
/vite-node@1.4.0:
@@ -778,7 +778,7 @@ packages:
debug: 4.3.4
pathe: 1.1.2
picocolors: 1.0.0
- vite: 5.1.6
+ vite: 5.2.2
transitivePeerDependencies:
- '@types/node'
- less
@@ -790,8 +790,8 @@ packages:
- terser
dev: true
- /vite@5.1.6:
- resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
+ /vite@5.2.2:
+ resolution: {integrity: sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -818,8 +818,8 @@ packages:
terser:
optional: true
dependencies:
- esbuild: 0.19.12
- postcss: 8.4.35
+ esbuild: 0.20.2
+ postcss: 8.4.38
rollup: 4.13.0
optionalDependencies:
fsevents: 2.3.3
@@ -867,7 +867,7 @@ packages:
strip-literal: 2.0.0
tinybench: 2.6.0
tinypool: 0.8.2
- vite: 5.1.6
+ vite: 5.2.2
vite-node: 1.4.0
why-is-node-running: 2.2.2
transitivePeerDependencies:
diff --git a/vite-test/src/fetchEmojis.js b/vite-test/src/fetchEmojis.js
index 021a53b..751c665 100644
--- a/vite-test/src/fetchEmojis.js
+++ b/vite-test/src/fetchEmojis.js
@@ -1,20 +1,4 @@
-const EMOJIS = 'EMOJIS';
export async function fetchEmojis() {
- const json = localStorage.getItem(EMOJIS);
- if(json) {
- try {
- return JSON.parse(json);
- }
- catch(ex) {
- // failed parse
- localStorage.removeItem(EMOJIS);
- }
- }
- // await sleep(3000);
const res = await fetch('https://emojihub.yurace.pro/api/all');
- const emojis = await res.json();
-
- localStorage.setItem(EMOJIS, JSON.stringify(emojis, true, 4));
-
- return emojis;
+ return await res.json();
}
diff --git a/vite-test/src/main.jsx b/vite-test/src/main.jsx
index 10a4b15..a8b1985 100644
--- a/vite-test/src/main.jsx
+++ b/vite-test/src/main.jsx
@@ -10,7 +10,7 @@ const App = <>
Amazing Emoji List
- {List}
+
>;
@@ -24,18 +24,10 @@ function EmojiList({ emojis }) {
function Emoji({ name, unicode, htmlCode }) {
return -
- {InnerHtml({ html: htmlCode.join('') })}
+
{name}
{unicode}
;
}
-function InnerHtml({ html, className = '' }) {
- const rawEmoji = ;
- rawEmoji.firstChild.innerHTML = html;
- return rawEmoji;
-}
-
-
-
diff --git a/vite-test/vite.config.js b/vite-test/vite.config.js
index fc44053..0028c64 100644
--- a/vite-test/vite.config.js
+++ b/vite-test/vite.config.js
@@ -15,6 +15,7 @@ export default defineConfig({
assetsDir: './',
modulePreload: false,
rollupOptions: {
+ logLevel: 'debug',
output: [{
format: 'es'
}]
From ef64cbb23621552be8bd909723096e3c43b1cd59 Mon Sep 17 00:00:00 2001
From: Marty Nelson
Date: Thu, 21 Mar 2024 15:33:57 -0700
Subject: [PATCH 23/23] all tests passing with new compile/runtime
---
.../compiler/transform/template-generators.js | 3 +-
.../transform/template-generators.test.js | 30 ++++++-------------
packages/runtime/renderer/dom-renderer.js | 2 ++
packages/runtime/renderer/renderer.js | 11 +++----
packages/vite-plugin/index.js | 15 +++++-----
vite-test/vite.config.js | 2 +-
6 files changed, 25 insertions(+), 38 deletions(-)
diff --git a/packages/compiler/transform/template-generators.js b/packages/compiler/transform/template-generators.js
index 24287a2..39b940c 100644
--- a/packages/compiler/transform/template-generators.js
+++ b/packages/compiler/transform/template-generators.js
@@ -16,10 +16,9 @@ export function makeTargets(template) {
}
export function makeRenderer({ id, targetKey, bindKey, isDomFragment, html }, options) {
- const content = !!options?.includeContent;
+ const content = !options?.noContent;
const target = targetKey ? `g${targetKey}` : `null`;
const bind = bindKey ? `b${bindKey}` : `null`;
-
let renderer = `__renderer(`;
renderer += `"${id}", ${target}, ${bind}, ${isDomFragment}`;
if(content) renderer += `, \`${html}\``;
diff --git a/packages/compiler/transform/template-generators.test.js b/packages/compiler/transform/template-generators.test.js
index b27127a..9e570e4 100644
--- a/packages/compiler/transform/template-generators.test.js
+++ b/packages/compiler/transform/template-generators.test.js
@@ -50,7 +50,7 @@ describe('bind generator', () => {
"(ts) => {
const t0 = ts[0];
return (v0) => {
- compose(t0, v0);
+ __compose(t0, v0);
};
}"
`);
@@ -66,8 +66,8 @@ describe('bind generator', () => {
const t0 = ts[0], t1 = ts[1], t2 = ts[2];
return (v0, v1, v2) => {
t0.className = v0;
- compose(t1, v1);
- compose(t2, v2);
+ __compose(t1, v1);
+ __compose(t2, v2);
};
}"
`
@@ -75,7 +75,7 @@ describe('bind generator', () => {
});
});
-describe('renderDOM generator', () => {
+describe('render generator', () => {
beforeEach(context => {
context.compile = code => {
@@ -87,13 +87,13 @@ describe('renderDOM generator', () => {
test('simple', ({ compile, expect }) => {
const code = compile(`name => {name}
`);
- expect(code).toMatchInlineSnapshot(`"renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false, "")"`);
+ expect(code).toMatchInlineSnapshot(`"__renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false, \`\`)"`);
});
test('static', ({ compile, expect }) => {
const code = compile(`() => static
`);
- expect(code).toMatchInlineSnapshot(`"renderer("e8a7ca1ef0", null, null, false, "static
")"`);
+ expect(code).toMatchInlineSnapshot(`"__renderer("e8a7ca1ef0", null, null, false, \`static
\`)"`);
});
@@ -104,9 +104,9 @@ describe('renderDOM generator', () => {
expect(code).toMatchInlineSnapshot(
`
- "renderer("b32dab1494", g98cb41d3ff, bb90a39b45c, false, "
+ "__renderer("b32dab1494", g98cb41d3ff, bb90a39b45c, false, \`
hey !
-
")"
+ \`)"
`
);
});
@@ -115,20 +115,8 @@ describe('renderDOM generator', () => {
const template = preParse(`name => {name}
`, expect);
const code = makeRenderer(template, { noContent: true });
- expect(code).toMatchInlineSnapshot(`"renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false)"`);
+ expect(code).toMatchInlineSnapshot(`"__renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false)"`);
});
- test('option inject { targets: code, bind: code }', ({ expect }) => {
- const template = preParse(`name => {name}
`, expect);
- const code = makeRenderer(template, {
- targets: `"targets!"`,
- bind: `"bind!"`,
- });
-
- expect(code).toMatchInlineSnapshot(`"renderer("c193fcb516", g1a9d5db22c, bd41d8cd98f, false)"`);
- });
-
-
-
});
diff --git a/packages/runtime/renderer/dom-renderer.js b/packages/runtime/renderer/dom-renderer.js
index 59fe488..f6a623f 100644
--- a/packages/runtime/renderer/dom-renderer.js
+++ b/packages/runtime/renderer/dom-renderer.js
@@ -4,12 +4,14 @@ export const DOMRenderer = {
createTemplate(id, content, isFragment) {
const node = DOMRenderer.template(id, content);
+ if(!node) return null;
const render = DOMRenderer.renderer(node, isFragment);
return render;
},
template(id, content) {
if(content) return DOMRenderer.create(content);
+ if(content === '') return null;
DOMRenderer.getById(id);
},
diff --git a/packages/runtime/renderer/renderer.js b/packages/runtime/renderer/renderer.js
index b9739bb..eb99dd9 100644
--- a/packages/runtime/renderer/renderer.js
+++ b/packages/runtime/renderer/renderer.js
@@ -7,25 +7,20 @@ let renderEngine = DOMRenderer; // DOM or HTML engine
export const RenderService = {
useDOMEngine() {
renderEngine = DOMRenderer;
- clear();
+ templates.clear();
},
useHTMLEngine() {
renderEngine = HTMLRenderer;
- clear();
+ templates.clear();
},
get,
bound,
};
-function clear() {
- templates.clear();
-}
function get(id, isFragment = false, content) {
if(templates.has(id)) return templates.get(id);
-
const template = renderEngine.createTemplate(id, content, isFragment);
-
templates.set(id, template);
return template;
}
@@ -74,6 +69,8 @@ export function renderer(id, targets, makeBind, isFragment, content) {
if(hasBind) return [node, bind];
}
+ // if(!create) return [null, null];
+
// Honestly not sure this really needed,
// use case would be list component optimize by
// not keeping bind functions?
diff --git a/packages/vite-plugin/index.js b/packages/vite-plugin/index.js
index e3f4821..d502eca 100644
--- a/packages/vite-plugin/index.js
+++ b/packages/vite-plugin/index.js
@@ -25,16 +25,18 @@ export default function azothPlugin(options) {
const byTarget = new Map();
const byBind = new Map();
- let command = '';
+ let config = null;
const transformJSX = {
name: 'azoth-jsx',
enforce: 'pre',
- config(_config, { command: cmd }) {
- command = cmd;
+ configResolved(resolvedConfig) {
+ // store the resolved config
+ config = resolvedConfig;
},
+
resolveId(id) {
const [name, ids] = id.split('?', 2);
const resolved = RESOLVED[name];
@@ -44,12 +46,12 @@ export default function azothPlugin(options) {
load(id) {
const [name, params] = id.split('?', 2);
- const isBuild = command === 'build';
+ const isProdBuild = config.mode !== 'test' && config.command === 'build';
const ids = new URLSearchParams(params).getAll('id');
switch(name) {
case resolvedTemplateModule:
- return loadTemplateModule(ids, isBuild);
+ return loadTemplateModule(ids, isProdBuild);
case resolvedTargetModule:
return loadTargetModule(ids);
case resolvedBindModule:
@@ -97,7 +99,6 @@ export default function azothPlugin(options) {
const templateExports = templates.map(template => {
const { id, targetKey, bindKey } = template;
-
// TODO: refactor cleanup on this apparent duplication
if(targetKey) {
@@ -116,7 +117,7 @@ export default function azothPlugin(options) {
}
}
- return `export const t${id} = ${makeRenderer(template, { includeContent: !isBuild })};\n`;
+ return `export const t${id} = ${makeRenderer(template, { noContent: isBuild })};\n`;
});
diff --git a/vite-test/vite.config.js b/vite-test/vite.config.js
index 0028c64..bc9cfe0 100644
--- a/vite-test/vite.config.js
+++ b/vite-test/vite.config.js
@@ -8,6 +8,7 @@ export default defineConfig({
esbuild: {
exclude: '**/*.jsx',
},
+ logLevel: 'debug',
build: {
target: 'esnext',
minify: false,
@@ -15,7 +16,6 @@ export default defineConfig({
assetsDir: './',
modulePreload: false,
rollupOptions: {
- logLevel: 'debug',
output: [{
format: 'es'
}]