From 856dcd226687ace4a4d2b25977f23d59a3347afa Mon Sep 17 00:00:00 2001 From: Alex Kanunnikov Date: Fri, 16 Feb 2024 00:19:18 +0300 Subject: [PATCH] + --- package.json | 1 + .../svlelte-compiler.test.ts.snap | 404 +++++++++++++++++- plugins/svelte-compiler.ts | 52 ++- pnpm-lock.yaml | 17 +- 4 files changed, 432 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index fbb999f3..9a541c34 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "@babel/core": "^7.23.6", "@babel/preset-typescript": "^7.23.3", "@glimmer/syntax": "^0.87.1", + "@prettier/sync": "^0.5.1", "code-red": "^1.0.4", "content-tag": "^1.2.2", "decorator-transforms": "1.1.0", diff --git a/plugins/__snapshots__/svlelte-compiler.test.ts.snap b/plugins/__snapshots__/svlelte-compiler.test.ts.snap index 648870df..8a66c50c 100644 --- a/plugins/__snapshots__/svlelte-compiler.test.ts.snap +++ b/plugins/__snapshots__/svlelte-compiler.test.ts.snap @@ -1,32 +1,390 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`compiler > compile sample case #2 1`] = ` +exports[`compiler > compile sample case #1 1`] = ` +"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 $ctx = {}; + 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, $key = "@identity") => ["..."], + this, + ), + "\\n\\n", + $_tag( + "ul", + [[], [], []], + [ + "\\n\\t", + $_each( + () => items, + (item, $index, $key = "@identity") => [ + $_tag( + "li", + [[], [], []], + [() => item.name, " x ", () => item.qty], + this, + ), + ], + this, + ), + "\\n", + ], + this, + ), + "\\n\\n\\n", + $_each( + () => items, + (item, i, $key = "@identity") => [ + $_tag( + "li", + [[], [], []], + [() => i + 1, ": ", () => item.name, " x ", () => item.qty], + this, + ), + ], + this, + ), + "\\n\\n", + $_each( + () => items, + (item, $index, $key = (item) => item.id) => [ + $_tag( + "li", + [[], [], []], + [() => item.name, " x ", () => item.qty], + this, + ), + ], + this, + ), + "\\n\\n", + $_each( + () => items, + (item, i, $key = (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); +} " - - 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"; +`; + +exports[`compiler > compile sample case #2 1`] = ` +"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 $ctx = {}; - const name = 'Hello World'; -const roots = ["\\n\\n", $_tag("div", [[], [], []], [() => name], this)]; - return $_fin(roots, this); - }" +export default function unknown(args) { + const $fw = $_GET_FW(this, arguments); + const $slots = $_GET_SLOTS(this, arguments); + const $ctx = {}; + const name = "Hello World"; + const roots = ["\\n\\n", $_tag("div", [[], [], []], [() => name], this)]; + return $_fin(roots, this); +} +" `; exports[`compiler > compile sample case #3 1`] = ` -" - - 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"; +"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 $ctx = {}; - const roots = ["\\n", $_tag("input", [[], [], []], [], this)]; - return $_fin(roots, this); - }" +export default function unknown(args) { + const $fw = $_GET_FW(this, arguments); + const $slots = $_GET_SLOTS(this, arguments); + const $ctx = {}; + const roots = ["\\n", $_tag("input", [[], [], []], [], this)]; + return $_fin(roots, this); +} +" `; diff --git a/plugins/svelte-compiler.ts b/plugins/svelte-compiler.ts index 714612e9..3e8031a1 100644 --- a/plugins/svelte-compiler.ts +++ b/plugins/svelte-compiler.ts @@ -5,7 +5,8 @@ import { print } from 'code-red'; import { SYMBOLS, MAIN_IMPORT } from './symbols.js'; import MagicString from 'magic-string'; import plugin from './bebel-svelte'; -import { type PluginItem, transformSync } from '@babel/core'; +import { transformSync } from '@babel/core'; +import synchronizedPrettier from "@prettier/sync"; import type { @@ -27,16 +28,32 @@ let magic = new MagicString(''); function applyBabelTransform(code: string) { - const babelResult = transformSync(code, { - plugins: [plugin], - filename: 'item.ts', - presets: [ - [ - '@babel/preset-typescript', - { allExtensions: true, onlyRemoveTypeImports: true }, + let babelResult = null; + try { + babelResult = transformSync(code, { + plugins: [plugin], + filename: 'item.ts', + presets: [ + [ + '@babel/preset-typescript', + { allExtensions: true, onlyRemoveTypeImports: true }, + ], ], - ], - }); + }); + } catch (e) { + // loc: Position { line: 3, column: 731, index: 739 } + // let's extract error code based on loc + const loc = e.loc; + const lines = code.split('\n'); + const line = lines[loc.line - 1]; + const start = loc.column - 30; + const end = loc.column + 20; + // we need to wrap error with << and >> to make it visible + const errorPrefix = line.substring(start, loc.column); + const errorSuffix = line.substring(loc.column + 2, end); + console.log(e.reasonCode, errorPrefix + '>>' + line.substring(loc.column+1, loc.column+2) + '<<'+ errorSuffix); + } + const txt = babelResult?.code ?? ''; @@ -99,7 +116,8 @@ export function compile(code = '', fileName = 'unknown') { `)} return ${SYMBOLS.FINALIZE_COMPONENT}(roots, this); }`; - return { code: codeResult, map }; + const prettyCode = synchronizedPrettier.format(codeResult, { parser: "babel" }); + return { code: prettyCode, map }; } const p = (expression: any) => print(expression).code; @@ -205,9 +223,9 @@ function transformArgument( if (attribute.value.length === 1) { const node = attribute.value[0]; if (t.isMustacheTag(node)) { - return `${escapeText(attribute.name)}:()=>${transform(node)}]`; + return `${escapeText(attribute.name)}:()=>${transform(node)}`; } else { - return `${escapeText(attribute.name)}:${transform(node)}]`; + return `${escapeText(attribute.name)}:${transform(node)}`; } } else { return `${escapeText(attribute.name)}:()=>[${attribute.value.map((el) => { @@ -309,11 +327,9 @@ function transformSlot(node: Element): string { function transformEachBlock(node: BaseExpressionDirective): string { const item = p(node.context); - return `${SYMBOLS.EACH}(()=>${p(node.expression)},(${item},${ - node.index ? node.index : '$index' - }, ${ - node.key ? `(${item})=>${p(node.key)}` : `${escapeText('@identity')}` - })=>[${node.children?.map(transform).join(',') ?? ''}],${getContext()})`; + const index = node.index ? node.index : '$index'; + const key = node.key ? `$key = (${item})=>${p(node.key)}` : `$key = '@identity'`; + return `${SYMBOLS.EACH}(()=>${p(node.expression)},(${item},${index},${key})=>[${node.children?.map(transform).join(',') ?? ''}],${getContext()})`; } function transform( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 396277a7..794787e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@glimmer/syntax': specifier: ^0.87.1 version: 0.87.1 + '@prettier/sync': + specifier: ^0.5.1 + version: 0.5.1(prettier@3.1.1) code-red: specifier: ^1.0.4 version: 1.0.4 @@ -2099,6 +2102,15 @@ packages: playwright: 1.40.1 dev: true + /@prettier/sync@0.5.1(prettier@3.1.1): + resolution: {integrity: sha512-tpF+A1e4ynO2U4fTH21Sjgm9EYENmqg4zmJCMLrmLVfzIzuDc1cKGXyxrxbFgcH8qQRfowyDCZFAUukwhiZlsw==} + peerDependencies: + prettier: '*' + dependencies: + make-synchronized: 0.2.8 + prettier: 3.1.1 + dev: false + /@rollup/pluginutils@5.1.0: resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} @@ -5772,6 +5784,10 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true + /make-synchronized@0.2.8: + resolution: {integrity: sha512-jtXnKYCxjmGaXiZhXbDbGPbh4YyTvIIbOgcQjtAboc4RSm9k3nyhTFvFQB0cfs7QFKuZXKe2D2RvOkv1c+vpxg==} + dev: false + /map-stream@0.1.0: resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} dev: true @@ -6520,7 +6536,6 @@ packages: resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} engines: {node: '>=14'} hasBin: true - dev: true /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}