From 2ad2124089e17f82ccdd9b78ace3d7f5e9b32a50 Mon Sep 17 00:00:00 2001 From: pushkine Date: Wed, 28 Apr 2021 06:19:35 +0200 Subject: [PATCH 1/5] init poc --- .../src/htmlxtojsx/nodes/action-directive.ts | 27 +++--- .../src/htmlxtojsx/utils/node-utils.ts | 84 +++++++++++++++++++ 2 files changed, 94 insertions(+), 17 deletions(-) diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts index 2081d5191..31f425492 100644 --- a/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts +++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts @@ -1,31 +1,24 @@ import MagicString from 'magic-string'; -import { isQuote } from '../utils/node-utils'; import { BaseDirective, BaseNode } from '../../interfaces'; +import { handle_subset } from '../utils/node-utils'; /** * use:xxx={params} ---> {...__sveltets_ensureAction(xxx(__sveltets_mapElementTag('ParentNodeName'),(params)))} */ export function handleActionDirective( - htmlx: string, + _htmlx: string, str: MagicString, attr: BaseDirective, parent: BaseNode ): void { - str.overwrite(attr.start, attr.start + 'use:'.length, '{...__sveltets_ensureAction('); + const subset = handle_subset(str, attr); - if (!attr.expression) { - str.appendLeft(attr.end, `(__sveltets_mapElementTag('${parent.name}')))}`); - return; - } - - str.overwrite( - attr.start + `use:${attr.name}`.length, - attr.expression.start, - `(__sveltets_mapElementTag('${parent.name}'),(` - ); - str.appendLeft(attr.expression.end, ')))'); - const lastChar = htmlx[attr.end - 1]; - if (isQuote(lastChar)) { - str.remove(attr.end - 1, attr.end); + if (attr.expression == null) { + const [action] = subset.deconstruct`use:${attr.name}`; + subset.edit`{...__sveltets_ensureAction(${action}(__sveltets_mapElementTag('${parent.name}')))}`; + } else { + // prettier-ignore + const [action, expression] = subset.deconstruct`use:${attr.name}=["']?{${attr.expression}}["']"?`; + subset.edit`{...__sveltets_ensureAction(${action}(__sveltets_mapElementTag('${parent.name}'),(${expression})))}`; } } diff --git a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts index 73c29c392..7b3e76fa5 100644 --- a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts +++ b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts @@ -1,4 +1,5 @@ import { Node, walk } from 'estree-walker'; +import MagicString from 'magic-string'; import { BaseNode } from '../../interfaces'; import { surroundWithIgnoreComments } from '../../utils/ignore'; @@ -211,3 +212,86 @@ export function getIdentifiersInIfExpression( export function usesLet(node: BaseNode): boolean { return node.attributes?.some((attr) => attr.type === 'Let'); } + +export function handle_subset(str: MagicString, fullnode: BaseNode) { + type Range = { + start: number; + end: number; + }; + + function print_string(str: string) { + return str.replace(/[\r\n]/g, '↲').replace(/\t/g, '╚'); + } + + function match_regexp(pattern: string, start: number): Range { + const re = new RegExp(pattern, 'g'); + re.lastIndex = start; + const match = re.exec(str.original); + if (!match || match.index !== start) { + throw new Error(`Expected to match /${pattern}/` + at(start)); + } + return { start, end: start + match[0].length }; + } + + function match_string(pattern: string, start: number): Range { + if (str.original.indexOf(pattern, start) !== start) { + throw new Error(`Expected "${pattern}"` + at(start)); + } + return { start, end: start + pattern.length }; + } + + function at(index: number) { + const pre = print_string(str.original.slice(Math.max(0, index - 5), index)); + const post = print_string(str.original.slice(index, index + 15)); + return `\n ${pre}${post}` + `\n ${pre.replace(/./g, ' ')}^`; + } + + function* deconstruct(patterns: TemplateStringsArray, ...nodes: (Range | string)[]) { + let start = fullnode.start; + for (let i = 0; i < patterns.length; i++) { + const pattern = patterns.raw[i]; + start = match_regexp(pattern, start).end; + if (i !== patterns.length - 1) { + const node = nodes[i]; + if (typeof node === 'string') { + yield match_string(node, start); + start += node.length; + } else { + yield node; + start = node.end; + } + } + } + } + + function edit(strings: TemplateStringsArray, ...nodes: (Range | string)[]) { + let start = fullnode.start; + strings = { ...strings }; + for (let i = 0; i < nodes.length; i++) { + const next_node = nodes[i]; + if (typeof next_node === 'string') { + // @ts-expect-error TemplateStringsArray is readonly + strings[i + 1] = strings[i] + next_node + strings[i + 1]; + } else { + if (next_node.start < start) { + const snippet = `${strings.raw[i]}\${}${strings.raw[i + 1]}`; + throw new Error(`Subset Nodes cannot be reordered! (${snippet})`); + } + if (i === 0) { + str.remove(start, next_node.start); + str.prependLeft(next_node.start, strings[i]); + } else { + str.overwrite(start, next_node.start, strings[i]); + } + start = next_node.end; + } + } + const tail = strings[nodes.length]; + if (tail) { + //if (start !== fullnode.end || tail !== str.slice(start, fullnode.end)) { + const offset = start === fullnode.end - 1 ? 2 : 1; + str.overwrite(start, fullnode.end + offset, tail + str.original.charAt(fullnode.end)); + } + } + return { deconstruct, edit }; +} From 455799e679241ee1065dd89026845f28f1c195f7 Mon Sep 17 00:00:00 2001 From: pushkine Date: Wed, 28 Apr 2021 08:53:27 +0200 Subject: [PATCH 2/5] typo --- packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts index 31f425492..ebb0991f3 100644 --- a/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts +++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/action-directive.ts @@ -18,7 +18,7 @@ export function handleActionDirective( subset.edit`{...__sveltets_ensureAction(${action}(__sveltets_mapElementTag('${parent.name}')))}`; } else { // prettier-ignore - const [action, expression] = subset.deconstruct`use:${attr.name}=["']?{${attr.expression}}["']"?`; + const [action, expression] = subset.deconstruct`use:${attr.name}=["']?{${attr.expression}}["']?`; subset.edit`{...__sveltets_ensureAction(${action}(__sveltets_mapElementTag('${parent.name}'),(${expression})))}`; } } From f94c00e516e40deddd74e16455151e3b191fc9e3 Mon Sep 17 00:00:00 2001 From: pushkine Date: Wed, 28 Apr 2021 09:17:33 +0200 Subject: [PATCH 3/5] fix offset --- packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts index 7b3e76fa5..451008342 100644 --- a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts +++ b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts @@ -288,9 +288,7 @@ export function handle_subset(str: MagicString, fullnode: BaseNode) { } const tail = strings[nodes.length]; if (tail) { - //if (start !== fullnode.end || tail !== str.slice(start, fullnode.end)) { - const offset = start === fullnode.end - 1 ? 2 : 1; - str.overwrite(start, fullnode.end + offset, tail + str.original.charAt(fullnode.end)); + str.overwrite(start, fullnode.end + 1, tail + str.original.charAt(fullnode.end)); } } return { deconstruct, edit }; From e8a651a9a392c1dbecbda110381809af326340de Mon Sep 17 00:00:00 2001 From: pushkine Date: Wed, 28 Apr 2021 09:35:51 +0200 Subject: [PATCH 4/5] lint --- .eslintrc.js | 3 ++- packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 03cd5ce38..94d48f337 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,8 +17,9 @@ module.exports = { 'import/no-cycle': 'off', 'import/no-unused-modules': 'off', 'import/no-deprecated': 'off', + // handled by prettier + 'max-len': 'off', // project-specific settings - 'max-len': ['error', { code: 100, ignoreComments: true, ignoreStrings: true }], 'no-trailing-spaces': 'error', 'one-var': ['error', 'never'], '@typescript-eslint/no-unused-vars': ['error', { args: 'none' }], diff --git a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts index 451008342..5dd4787a7 100644 --- a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts +++ b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts @@ -246,7 +246,7 @@ export function handle_subset(str: MagicString, fullnode: BaseNode) { return `\n ${pre}${post}` + `\n ${pre.replace(/./g, ' ')}^`; } - function* deconstruct(patterns: TemplateStringsArray, ...nodes: (Range | string)[]) { + function* deconstruct(patterns: TemplateStringsArray, ...nodes: Array) { let start = fullnode.start; for (let i = 0; i < patterns.length; i++) { const pattern = patterns.raw[i]; @@ -264,7 +264,7 @@ export function handle_subset(str: MagicString, fullnode: BaseNode) { } } - function edit(strings: TemplateStringsArray, ...nodes: (Range | string)[]) { + function edit(strings: TemplateStringsArray, ...nodes: Array) { let start = fullnode.start; strings = { ...strings }; for (let i = 0; i < nodes.length; i++) { From 080573469afff84509092e00852f1fee8130fa3e Mon Sep 17 00:00:00 2001 From: pushkine Date: Wed, 28 Apr 2021 09:37:31 +0200 Subject: [PATCH 5/5] format --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 94d48f337..a1cef9478 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,7 +17,7 @@ module.exports = { 'import/no-cycle': 'off', 'import/no-unused-modules': 'off', 'import/no-deprecated': 'off', - // handled by prettier + // handled by prettier 'max-len': 'off', // project-specific settings 'no-trailing-spaces': 'error',