diff --git a/package.json b/package.json
index 4e9eab44..fbb999f3 100644
--- a/package.json
+++ b/package.json
@@ -105,6 +105,7 @@
"code-red": "^1.0.4",
"content-tag": "^1.2.2",
"decorator-transforms": "1.1.0",
+ "magic-string": "^0.30.7",
"svelte": "^4.2.10"
}
}
diff --git a/plugins/__snapshots__/svlelte-compiler.test.ts.snap b/plugins/__snapshots__/svlelte-compiler.test.ts.snap
new file mode 100644
index 00000000..35e16af7
--- /dev/null
+++ b/plugins/__snapshots__/svlelte-compiler.test.ts.snap
@@ -0,0 +1,189 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`compiler > compile sample case #1 1`] = `
+{
+ "code": "
+
+ import { $_fin,$_tag,$_if,$_each,$_eachSync,$_slot,$_edp,$_args,$_text,$_c,$_dc,$SLOTS_SYMBOL,$PROPS_SYMBOL,$_GET_SLOTS,$_GET_ARGS,$_GET_FW,$_componentHelper,$_modifierHelper,$_helperHelper,$template,$_hasBlockParams,$_hasBlock,$nodes,$args,$_maybeHelper,$_maybeModifier,$_inElement,$_ucw,$__if,$__eq,$__debugger,$__log,$__array,$__hash,$__fn } from "@lifeart/gxt";
+
+
+ export default function unknown(args) {
+ const $fw = $_GET_FW(this, arguments);
+ const $slots = $_GET_SLOTS(this, arguments);
+
+ const roots = ["\\n",$_tag("div", [[], [["id","12"],["name",()=>3 + 2],["label",()=>["1",2].join('')]], []], [],this),"\\n",$_tag("button", [[], [["disabled",()=>!clickable]], []], ["..."],this),"\\n",$_tag("button", [[], [["disabled",true]], []], ["can't touch this"],this),"\\n",$_tag("input", [[], [["type","checkbox"]], []], [],this),"\\n",$_tag("input", [[], [["required",()=>false],["placeholder","This input field is not required"]], []], [],this),"\\n",$_tag("div", [[], [["title",()=>null]], []], ["This div has no title attribute"],this),"\\n",$_tag("button", [[], [["disabled",()=>number !== 42]], []], ["..."],this),"\\n",$_tag("button", [[], [["disabled",()=>disabled]], []], ["..."],this),"\\n",$_c(Widget,{"foo":()=>bar],"answer":()=>42],"text":"hello"],[$SLOTS_SYMBOL]:{default:()=>[]}},this),"\\n",$_tag("p", [[], [], []], [()=>a," + ",()=>b," = ",()=>a + b,"."],this),"\\n",$_tag("div", [[], [], []], [()=>(/^[A-Za-z ]+$/).test(value) ? x : y],this),"\\n",$_if(()=>expression,()=>["..."], ()=>["..."]),"\\n",$_if(()=>answer === 42,()=>[$_tag("p", [[], [], []], ["what was the question?"],this)], null),"\\n-----\\n",$_if(()=>porridge.temperature > 100,()=>[$_tag("p", [[], [], []], ["too hot!"],this)], ()=>[$_if(()=>80 > porridge.temperature,()=>[$_tag("p", [[], [], []], ["too cold!"],this)], ()=>[$_tag("p", [[], [], []], ["just right!"],this)])]),"\\n---\\n",$_each(()=>expression,(name,$index, "@identity")=>["..."],this),"\\n\\n",$_tag("ul", [[], [], []], ["\\n\\t",$_each(()=>items,(item,$index, "@identity")=>[$_tag("li", [[], [], []], [()=>item.name," x ",()=>item.qty],this)],this),"\\n"],this),"\\n\\n\\n",$_each(()=>items,(item,i, "@identity")=>[$_tag("li", [[], [], []], [()=>i + 1,": ",()=>item.name," x ",()=>item.qty],this)],this),"\\n\\n",$_each(()=>items,(item,$index, (item)=>item.id)=>[$_tag("li", [[], [], []], [()=>item.name," x ",()=>item.qty],this)],this),"\\n\\n",$_each(()=>items,(item,i, (item)=>item.id)=>[$_tag("li", [[], [], []], [()=>i + 1,": ",()=>item.name," x ",()=>item.qty],this)],this),"\\n\\n",$_c(MyComponent,{...rest,[$SLOTS_SYMBOL]:{default:()=>[]}},this),"\\n\\n",$_tag("button", [[], [], [["click",()=>handleClick()]]], ["\\n\\tcount: ",()=>count,"\\n"],this),"\\n\\n",$_tag("div", [[['',"name"]], [], []], [],this),"\\n",$_tag("div", [[['',()=>isActive ? 'active' : '']], [], []], ["..."],this),"\\n",$_tag("div", [[['',()=>isActive ? "active" : '']], [], []], ["..."],this),"\\n",$_tag("div", [[['',()=>active ? "active" : ''],['',()=>!active ? "inactive" : ''],['',()=>isAdmin ? "isAdmin" : '']], [], []], ["..."],this),"\\n",$_tag("div", [[], [["color",()=>myColor]], []], ["..."],this),"\\n\\n",$_tag("a", [[], [["href",()=>["page/",p].join('')]], []], ["page ",()=>p],this),"\\n\\n",$_slot("item",() => [], $slots, this),"\\n\\n",$_c(FancyList,{[$SLOTS_SYMBOL]:{footer:()=>["Copyright (c) 2019 Svelte Industries"],default:()=>["\\n ","\\n"]}},this)];
+ return $_fin(roots, this);
+ }",
+ "map": SourceMap {
+ "file": "unknown.map",
+ "mappings": ";;;;;EAAA,IACA,gGAA2C,IAC3C,sEAA0C,IAC1C,yEAA0C,IAC1C,wDAAuB,IACvB,8GAAyE,IACzE,sFAAuD,IACvD,yEAA+C,IAC/C,oEAA+B,IAC/B,mGAA6C,IAC7C,sEAA2B,IAC3B,2EAAiD,IACjD,6CAAkC,IAClC,6FAEK,WAEL,0OAMK,SAEL,+DAAoC,MAEpC,qKAIK,QAGL,iIAEO,MAEP,2HAEO,MAEP,qIAEO,MAEP,gEAAyB,MAEzB,6FAES,MAET,8CAAwB,IACxB,yEAAgD,IAChD,yEAAsC,IACtC,4IAAkE,IAClE,6DAAoC,MAEpC,+EAA+B,MAE/B,qCAA2B,MAE3B,qHAEY;",
+ "names": [],
+ "sources": [
+ "unknown",
+ ],
+ "sourcesContent": [
+ "
+
+
+
+
+
+This div has no title attribute
+
+
+
+{a} + {b} = {a + b}.
+{(/^[A-Za-z ]+$/).test(value) ? x : y}
+{#if expression}...{:else}...{/if}
+{#if answer === 42}
+ what was the question?
+{/if}
+-----
+{#if porridge.temperature > 100}
+ too hot!
+{:else if 80 > porridge.temperature}
+ too cold!
+{:else}
+ just right!
+{/if}
+---
+{#each expression as name}...{/each}
+
+
+ {#each items as item}
+ - {item.name} x {item.qty}
+ {/each}
+
+
+
+{#each items as item, i}
+ {i + 1}: {item.name} x {item.qty}
+{/each}
+
+{#each items as item (item.id)}
+ {item.name} x {item.qty}
+{/each}
+
+{#each items as item, i (item.id)}
+ {i + 1}: {item.name} x {item.qty}
+{/each}
+
+
+
+
+
+
+...
+...
+...
+...
+
+page {p}
+
+
+
+
+ Copyright (c) 2019 Svelte Industries
+
+",
+ ],
+ "version": 3,
+ },
+}
+`;
+
+exports[`compiler > compile sample case #2 1`] = `
+{
+ "code": "
+
+ import { $_fin,$_tag,$_if,$_each,$_eachSync,$_slot,$_edp,$_args,$_text,$_c,$_dc,$SLOTS_SYMBOL,$PROPS_SYMBOL,$_GET_SLOTS,$_GET_ARGS,$_GET_FW,$_componentHelper,$_modifierHelper,$_helperHelper,$template,$_hasBlockParams,$_hasBlock,$nodes,$args,$_maybeHelper,$_maybeModifier,$_inElement,$_ucw,$__if,$__eq,$__debugger,$__log,$__array,$__hash,$__fn } from "@lifeart/gxt";
+
+
+ export default function unknown(args) {
+ const $fw = $_GET_FW(this, arguments);
+ const $slots = $_GET_SLOTS(this, arguments);
+
+ const roots = ["\\n",$_tag("div", [[], [["id","12"],["name",()=>3 + 2],["label",()=>["1",2].join('')]], []], [],this),"\\n",$_tag("button", [[], [["disabled",()=>!clickable]], []], ["..."],this),"\\n",$_tag("button", [[], [["disabled",true]], []], ["can't touch this"],this),"\\n",$_tag("input", [[], [["type","checkbox"]], []], [],this),"\\n",$_tag("input", [[], [["required",()=>false],["placeholder","This input field is not required"]], []], [],this),"\\n",$_tag("div", [[], [["title",()=>null]], []], ["This div has no title attribute"],this),"\\n",$_tag("button", [[], [["disabled",()=>number !== 42]], []], ["..."],this),"\\n",$_tag("button", [[], [["disabled",()=>disabled]], []], ["..."],this),"\\n",$_c(Widget,{"foo":()=>bar],"answer":()=>42],"text":"hello"],[$SLOTS_SYMBOL]:{default:()=>[]}},this),"\\n",$_tag("p", [[], [], []], [()=>a," + ",()=>b," = ",()=>a + b,"."],this),"\\n",$_tag("div", [[], [], []], [()=>(/^[A-Za-z ]+$/).test(value) ? x : y],this),"\\n",$_if(()=>expression,()=>["..."], ()=>["..."]),"\\n",$_if(()=>answer === 42,()=>[$_tag("p", [[], [], []], ["what was the question?"],this)], null),"\\n-----\\n",$_if(()=>porridge.temperature > 100,()=>[$_tag("p", [[], [], []], ["too hot!"],this)], ()=>[$_if(()=>80 > porridge.temperature,()=>[$_tag("p", [[], [], []], ["too cold!"],this)], ()=>[$_tag("p", [[], [], []], ["just right!"],this)])]),"\\n---\\n",$_each(()=>expression,(name,$index, "@identity")=>["..."],this),"\\n\\n",$_tag("ul", [[], [], []], ["\\n\\t",$_each(()=>items,(item,$index, "@identity")=>[$_tag("li", [[], [], []], [()=>item.name," x ",()=>item.qty],this)],this),"\\n"],this),"\\n\\n\\n",$_each(()=>items,(item,i, "@identity")=>[$_tag("li", [[], [], []], [()=>i + 1,": ",()=>item.name," x ",()=>item.qty],this)],this),"\\n\\n",$_each(()=>items,(item,$index, (item)=>item.id)=>[$_tag("li", [[], [], []], [()=>item.name," x ",()=>item.qty],this)],this),"\\n\\n",$_each(()=>items,(item,i, (item)=>item.id)=>[$_tag("li", [[], [], []], [()=>i + 1,": ",()=>item.name," x ",()=>item.qty],this)],this),"\\n\\n",$_c(MyComponent,{...rest,[$SLOTS_SYMBOL]:{default:()=>[]}},this),"\\n\\n",$_tag("button", [[], [], [["click",()=>handleClick()]]], ["\\n\\tcount: ",()=>count,"\\n"],this),"\\n\\n",$_tag("div", [[['',"name"]], [], []], [],this),"\\n",$_tag("div", [[['',()=>isActive ? 'active' : '']], [], []], ["..."],this),"\\n",$_tag("div", [[['',()=>isActive ? "active" : '']], [], []], ["..."],this),"\\n",$_tag("div", [[['',()=>active ? "active" : ''],['',()=>!active ? "inactive" : ''],['',()=>isAdmin ? "isAdmin" : '']], [], []], ["..."],this),"\\n",$_tag("div", [[], [["color",()=>myColor]], []], ["..."],this),"\\n\\n",$_tag("a", [[], [["href",()=>["page/",p].join('')]], []], ["page ",()=>p],this),"\\n\\n",$_slot("item",() => [], $slots, this),"\\n\\n",$_c(FancyList,{[$SLOTS_SYMBOL]:{footer:()=>["Copyright (c) 2019 Svelte Industries"],default:()=>["\\n ","\\n"]}},this)];
+ return $_fin(roots, this);
+ }",
+ "map": SourceMap {
+ "file": "unknown.map",
+ "mappings": ";;;;;EAAA,IACA,gGAA2C,IAC3C,sEAA0C,IAC1C,yEAA0C,IAC1C,wDAAuB,IACvB,8GAAyE,IACzE,sFAAuD,IACvD,yEAA+C,IAC/C,oEAA+B,IAC/B,mGAA6C,IAC7C,sEAA2B,IAC3B,2EAAiD,IACjD,6CAAkC,IAClC,6FAEK,WAEL,0OAMK,SAEL,+DAAoC,MAEpC,qKAIK,QAGL,iIAEO,MAEP,2HAEO,MAEP,qIAEO,MAEP,gEAAyB,MAEzB,6FAES,MAET,8CAAwB,IACxB,yEAAgD,IAChD,yEAAsC,IACtC,4IAAkE,IAClE,6DAAoC,MAEpC,+EAA+B,MAE/B,qCAA2B,MAE3B,qHAEY;",
+ "names": [],
+ "sources": [
+ "unknown",
+ ],
+ "sourcesContent": [
+ "
+
+
+
+
+
+This div has no title attribute
+
+
+
+{a} + {b} = {a + b}.
+{(/^[A-Za-z ]+$/).test(value) ? x : y}
+{#if expression}...{:else}...{/if}
+{#if answer === 42}
+ what was the question?
+{/if}
+-----
+{#if porridge.temperature > 100}
+ too hot!
+{:else if 80 > porridge.temperature}
+ too cold!
+{:else}
+ just right!
+{/if}
+---
+{#each expression as name}...{/each}
+
+
+ {#each items as item}
+ - {item.name} x {item.qty}
+ {/each}
+
+
+
+{#each items as item, i}
+ {i + 1}: {item.name} x {item.qty}
+{/each}
+
+{#each items as item (item.id)}
+ {item.name} x {item.qty}
+{/each}
+
+{#each items as item, i (item.id)}
+ {i + 1}: {item.name} x {item.qty}
+{/each}
+
+
+
+
+
+
+...
+...
+...
+...
+
+page {p}
+
+
+
+
+ Copyright (c) 2019 Svelte Industries
+
+",
+ ],
+ "version": 3,
+ },
+}
+`;
diff --git a/plugins/svelte-compiler.ts b/plugins/svelte-compiler.ts
index df7d2725..b375d1f3 100644
--- a/plugins/svelte-compiler.ts
+++ b/plugins/svelte-compiler.ts
@@ -3,6 +3,8 @@
import * as parser from 'svelte/compiler';
import { print } from 'code-red';
import { SYMBOLS, MAIN_IMPORT } from './symbols.js';
+import MagicString from 'magic-string';
+
import type {
Attribute,
BaseDirective,
@@ -17,15 +19,24 @@ import type {
const importsPrefix = `
import { ${Object.values(SYMBOLS).join(',')} } from "${MAIN_IMPORT}";
`;
-
+let magic = new MagicString('');
export function compile(code = '', fileName = 'unknown') {
const componentName = fileName.split('.svelte').pop();
-
+ magic = new MagicString(code, {
+ filename: fileName,
+ });
// code-red
const result = parser.compile(code, {
dev: true,
preserveWhitespace: false,
preserveComments: false,
+ enableSourcemap: false,
+ generate: 'dom',
+ varsReport: 'strict',
+ immutable: false,
+ hydratable: false,
+ legacy: false,
+ css: 'external',
});
// console.log('ast', JSON.stringify(result.ast, null, 2));
@@ -41,7 +52,18 @@ export function compile(code = '', fileName = 'unknown') {
const imports = script.split('\n').filter((el) => el.includes('import '));
const content = script.split('\n').filter((el) => !el.includes('import '));
- return `
+ magic.prepend(`
+ ${importsPrefix}
+ ${imports.join('\n')}
+ `);
+
+ let map = magic.generateMap({
+ source: fileName,
+ file: fileName + '.map',
+ includeContent: true,
+ });
+
+ let codeResult = `
${importsPrefix}
${imports.join('\n')}
export default function ${componentName}(args) {
@@ -51,6 +73,7 @@ export function compile(code = '', fileName = 'unknown') {
const roots = [${template}];
return ${SYMBOLS.FINALIZE_COMPONENT}(roots, this);
}`;
+ return { code: codeResult, map };
}
const p = (expression: any) => print(expression).code;
@@ -199,7 +222,9 @@ function hasAttribute(element: Element, attrName: string) {
function transformInlineComponent(node: Element): string {
const argsArray = node.attributes.map((attr) => {
- return transformArgument(attr);
+ const result = transformArgument(attr);
+ magic.update(attr.start, attr.end, result);
+ return result;
});
const slotNodes =
node.children?.filter(
@@ -268,25 +293,45 @@ function transform(
} else if (typeof node !== 'object') {
return node;
} else if (t.isElement(node)) {
- return transformElement(node);
+ const result = transformElement(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isAttribute(node)) {
- return transformAttribute(node);
+ const result = transformAttribute(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isText(node)) {
- return escapeText(node.data);
+ const result = escapeText(node.data);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isMustacheTag(node)) {
- return transformMustacheTag(node);
+ const result = transformMustacheTag(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isAttributeShorthand(node)) {
- return transformAttributeShorthand(node);
+ const result = transformAttributeShorthand(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isInlineComponent(node)) {
- return transformInlineComponent(node);
+ const result = transformInlineComponent(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isIfBlock(node)) {
- return transformIfBlock(node);
+ const result = transformIfBlock(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isElseBlock(node)) {
- return transformElseBlock(node);
+ const result = transformElseBlock(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isEachBlock(node)) {
- return transformEachBlock(node);
+ const result = transformEachBlock(node);
+ magic.update(node.start, node.end, result);
+ return result;
} else if (t.isSlot(node)) {
- return transformSlot(node);
+ const result = transformSlot(node);
+ magic.update(node.start, node.end, result);
+ return result;
}
throw new Error(`Unknown node type: ${node.type}`);
diff --git a/plugins/svlelte-compiler.test.ts b/plugins/svlelte-compiler.test.ts
index 107bbdaf..8f76b5b0 100644
--- a/plugins/svlelte-compiler.test.ts
+++ b/plugins/svlelte-compiler.test.ts
@@ -1,4 +1,7 @@
-let code = `
+import { expect, test, describe } from 'vitest';
+import { compile } from './svelte-compiler';
+
+let sample1 = `
@@ -65,8 +68,19 @@ let code = `
`;
-code = `
{name}
`;
+
+describe('compiler', () => {
+ test('compile sample case #1', () => {
+ const result = compile(sample1);
+ expect(result).toMatchSnapshot();
+ });
+ test('compile sample case #2', () => {
+ const result = compile(sample1);
+ expect(result).toMatchSnapshot();
+ });
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7b2dad58..396277a7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,6 +23,9 @@ dependencies:
decorator-transforms:
specifier: 1.1.0
version: 1.1.0(@babel/core@7.23.6)
+ magic-string:
+ specifier: ^0.30.7
+ version: 0.30.7
svelte:
specifier: ^4.2.10
version: 4.2.10
@@ -2550,7 +2553,7 @@ packages:
/@vitest/snapshot@1.1.1:
resolution: {integrity: sha512-WnMHjv4VdHLbFGgCdVVvyRkRPnOKN75JJg+LLTdr6ah7YnL75W+7CTIMdzPEPzaDxA8r5yvSVlc1d8lH3yE28w==}
dependencies:
- magic-string: 0.30.5
+ magic-string: 0.30.7
pathe: 1.1.1
pretty-format: 29.7.0
dev: true
@@ -5745,8 +5748,8 @@ packages:
sourcemap-codec: 1.4.8
dev: true
- /magic-string@0.30.5:
- resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
+ /magic-string@0.30.7:
+ resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
@@ -7346,7 +7349,7 @@ packages:
estree-walker: 3.0.3
is-reference: 3.0.2
locate-character: 3.0.0
- magic-string: 0.30.5
+ magic-string: 0.30.7
periscopic: 3.1.0
dev: false
@@ -7997,7 +8000,7 @@ packages:
execa: 8.0.1
happy-dom: 13.0.6
local-pkg: 0.5.0
- magic-string: 0.30.5
+ magic-string: 0.30.7
pathe: 1.1.1
picocolors: 1.0.0
std-env: 3.7.0
diff --git a/src/components/World.svelte b/src/components/World.svelte
index ec5a46cf..9d9896e6 100644
--- a/src/components/World.svelte
+++ b/src/components/World.svelte
@@ -1,7 +1,11 @@
+