From f56c12e7accc6fa229c11ddca2d8192dddd21bfc Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sat, 15 Aug 2020 15:13:34 +0800 Subject: [PATCH 01/21] basic await and each --- .../svelte2tsx/src/nodes/TemplateScope.ts | 42 +++++++ packages/svelte2tsx/src/nodes/slot.ts | 118 ++++++++++++++++++ packages/svelte2tsx/src/svelte2tsx.ts | 86 +++++++------ packages/svelte2tsx/src/utils/svelteAst.ts | 17 +++ packages/svelte2tsx/svelte-shims.d.ts | 3 + .../component-slot-inside-await/expected.tsx | 14 +++ .../component-slot-inside-await/input.svelte | 7 ++ .../component-slot-inside-each/expected.tsx | 14 +++ .../component-slot-inside-each/input.svelte | 7 ++ 9 files changed, 264 insertions(+), 44 deletions(-) create mode 100644 packages/svelte2tsx/src/nodes/TemplateScope.ts create mode 100644 packages/svelte2tsx/src/nodes/slot.ts create mode 100644 packages/svelte2tsx/src/utils/svelteAst.ts create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte diff --git a/packages/svelte2tsx/src/nodes/TemplateScope.ts b/packages/svelte2tsx/src/nodes/TemplateScope.ts new file mode 100644 index 000000000..c4884253f --- /dev/null +++ b/packages/svelte2tsx/src/nodes/TemplateScope.ts @@ -0,0 +1,42 @@ +import { Node } from 'estree-walker'; + +/** + * adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/Slot.ts + */ +export default class TemplateScope { + names: Set; + owners: Map = new Map(); + inits: Map = new Map(); + parent?: TemplateScope; + + constructor(parent?: TemplateScope) { + this.parent = parent; + this.names = new Set(parent ? parent.names : []); + } + + add(init: Node, owner: Node) { + const { name } = init; + this.names.add(name); + this.inits.set(name, init); + this.owners.set(name, owner); + return this; + } + + child() { + const child = new TemplateScope(this); + return child; + } + + getOwner(name: string): Node { + return this.owners.get(name) || (this.parent && this.parent.getOwner(name)); + } + + getInit(name: string): Node { + return this.inits.get(name) || this.parent?.getInit(name); + } + + isLet(name: string) { + const owner = this.getOwner(name); + return owner && (owner.type === 'Element' || owner.type === 'InlineComponent'); + } +} diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts new file mode 100644 index 000000000..6b2a94796 --- /dev/null +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -0,0 +1,118 @@ +import { Node, walk } from 'estree-walker'; +import MagicString from 'magic-string'; +import { attributeValueIsString, isMember } from '../utils/svelteAst'; +import TemplateScope from './TemplateScope'; + +function AttributeStrValueAsJsExpression(htmlx: string, attr: Node): string { + if (attr.value.length == 0) return "''"; //wut? + + //handle single value + if (attr.value.length == 1) { + const attrVal = attr.value[0]; + + if (attrVal.type == 'Text') { + return '"' + attrVal.raw + '"'; + } + } + + // we have multiple attribute values, so we know we are building a string out of them. + // so return a dummy string, it will typecheck the same :) + return '"__svelte_ts_string"'; +} + +export class SlotHandler { + + constructor(private readonly str: MagicString, private readonly htmlx: string) { } + + slots = new Map>(); + resloved = new Map(); + + reslove(identifierDef: Node, initExpression: Node, scope: TemplateScope) { + let resloved = this.resloved.get(identifierDef); + if (resloved) { + return resloved; + } + const { name } = identifierDef; + + const owner = scope.getOwner(name); + + if (owner.type === 'CatchBlock') { + resloved = '__sveltets_any({})'; + } else if (owner.type === 'ThenBlock') { + const reslovedExpression = this.resolveExpression(initExpression, scope); + + resloved = `__sveltets_unwrapPromiseLike(${reslovedExpression})`; + } else if (owner.type === 'EachBlock') { + const reslovedExpression = this.resolveExpression(initExpression, scope); + + resloved = `_sveltets_unwrapArr(${reslovedExpression})`; + } + this.resloved.set(identifierDef, resloved); + return resloved; + } + + private resolveExpression(expression: Node, scope: TemplateScope) { + const strForExpression = new MagicString(this.htmlx); + + const identifiers: Node[] = []; + walk(expression, { + enter(node, parent, prop) { + if (node.type === 'Identifier' && (!parent || !isMember(parent, prop))) { + this.skip(); + identifiers.push(node); + } + } + }); + + for (const identifier of identifiers) { + const { start, end, name } = identifier; + const init = scope.getInit(name); + const value = init ? this.resloved.get(init) : name; + strForExpression.overwrite(start, end, value); + } + + return strForExpression.slice(expression.start, expression.end); + } + + handleSlot(node: Node, scope: TemplateScope) { + const nameAttr = node.attributes.find((a: Node) => a.name == 'name'); + const slotName = nameAttr ? nameAttr.value[0].raw : 'default'; + //collect attributes + const attributes = new Map(); + for (const attr of node.attributes) { + if (attr.name == 'name') continue; + if (!attr.value.length) continue; + + if (attributeValueIsString(attr)) { + attributes.set(attr.name, AttributeStrValueAsJsExpression(this.str.original, attr)); + continue; + } + attributes.set(attr.name, this.resloveAttr(attr, scope)); + } + this.slots.set(slotName, attributes); + } + + getSlotDef() { + return this.slots; + } + + resloveAttr(attr: Node, scope: TemplateScope): string { + const attrVal = attr.value[0]; + if (!attrVal) { + return null; + } + + if (attrVal.type == 'AttributeShorthand') { + const { name } = attrVal.expression; + const init = scope.getInit(name); + const resolved = this.resloved.get(init); + + return resolved ?? name; + } + + if (attrVal.type == 'MustacheTag') { + return this.resolveExpression(attrVal.expression, scope); + } + throw Error('Unknown attribute value type:' + attrVal.type); + } +} diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index d4928d649..3a5d48260 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -11,32 +11,9 @@ import { findExortKeyword } from './utils/tsAst'; import { InstanceScriptProcessResult, CreateRenderFunctionPara } from './interfaces'; import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters'; import { ExportedNames } from './nodes/ExportedNames'; - -function AttributeValueAsJsExpression(htmlx: string, attr: Node): string { - if (attr.value.length == 0) return "''"; //wut? - - //handle single value - if (attr.value.length == 1) { - const attrVal = attr.value[0]; - - if (attrVal.type == 'AttributeShorthand') { - return attrVal.expression.name; - } - - if (attrVal.type == 'Text') { - return '"' + attrVal.raw + '"'; - } - - if (attrVal.type == 'MustacheTag') { - return htmlx.substring(attrVal.expression.start, attrVal.expression.end); - } - throw Error('Unknown attribute value type:' + attrVal.type); - } - - // we have multiple attribute values, so we know we are building a string out of them. - // so return a dummy string, it will typecheck the same :) - return '"__svelte_ts_string"'; -} +import * as astUtil from './utils/svelteAst'; +import { SlotHandler } from './nodes/slot'; +import TemplateScope from './nodes/TemplateScope'; type TemplateProcessResult = { uses$$props: boolean; @@ -217,12 +194,12 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { //handle potential store if (node.name[0] == '$') { if (isDeclaration) { - if (parent.type == 'Property' && prop == 'key') return; + if (astUtil.isObjectKey(parent, prop)) return; scope.declared.add(node.name); } else { - if (parent.type == 'MemberExpression' && prop == 'property' && !parent.computed) + if (astUtil.isMember(parent, prop) && !parent.computed) return; - if (parent.type == 'Property' && prop == 'key') return; + if (astUtil.isObjectKey(parent, prop)) return; pendingStoreResolutions.push({ node, parent, scope }); } return; @@ -267,25 +244,32 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { }); }; - const slots = new Map>(); - const handleSlot = (node: Node) => { - const nameAttr = node.attributes.find((a) => a.name == 'name'); - const slotName = nameAttr ? nameAttr.value[0].raw : 'default'; - //collect attributes - const attributes = new Map(); - for (const attr of node.attributes) { - if (attr.name == 'name') continue; - if (!attr.value.length) continue; - attributes.set(attr.name, AttributeValueAsJsExpression(str.original, attr)); - } - slots.set(slotName, attributes); - }; const handleStyleTag = (node: Node) => { str.remove(node.start, node.end); }; + const slotHandler = new SlotHandler(str, str.original); + let templateScope = new TemplateScope(); + const { handleEventHandler, getEvents } = createEventHandlerTransformer(); + const handleEachScope = (node: Node) => { + templateScope = templateScope.child(); + + if (node.context) { + templateScope.add(node.context, node); + } + }; + + const handleAwaitScope = (node: Node) => { + templateScope = templateScope.child(); + if (node.value) { + templateScope.add(node.value, node.then); + } + if (node.error) { + templateScope.add(node.error, node.catch); + } + }; const onHtmlxWalk = (node: Node, parent: Node, prop: string) => { if ( @@ -306,7 +290,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { handleIdentifier(node, parent, prop); break; case 'Slot': - handleSlot(node); + slotHandler.handleSlot(node, templateScope); break; case 'Style': handleStyleTag(node); @@ -329,6 +313,14 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { case 'VariableDeclarator': isDeclaration = true; break; + case 'EachBlock': + handleEachScope(node); + slotHandler.reslove(node.context, node.expression, templateScope); + break; + case 'AwaitBlock': + handleAwaitScope(node); + slotHandler.reslove(node.value, node.expression, templateScope); + break; } }; @@ -354,6 +346,12 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { case 'ArrowFunctionExpression': leaveArrowFunctionExpression(); break; + case 'EachBlock': + templateScope = templateScope.parent; + break; + case 'AwaitBlock': + templateScope = templateScope.parent; + break; } }; @@ -369,7 +367,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { return { moduleScriptTag, scriptTag, - slots, + slots: slotHandler.getSlotDef(), events: getEvents(), uses$$props, uses$$restProps, diff --git a/packages/svelte2tsx/src/utils/svelteAst.ts b/packages/svelte2tsx/src/utils/svelteAst.ts new file mode 100644 index 000000000..36acc9192 --- /dev/null +++ b/packages/svelte2tsx/src/utils/svelteAst.ts @@ -0,0 +1,17 @@ +import { Node } from 'estree-walker'; + +export function isMember(parent: Node, prop: string) { + return parent.type == 'MemberExpression' && prop == 'property'; +} + +export function isObjectKey(parent: Node, prop: string) { + return parent.type == 'Property' && prop == 'key'; +} + +export function isText(node: Node) { + return node.type === 'Text'; +} + +export function attributeValueIsString(attr: Node) { + return attr.value.length !== 1 || attr.value[0]?.type === 'Text'; +} diff --git a/packages/svelte2tsx/svelte-shims.d.ts b/packages/svelte2tsx/svelte-shims.d.ts index 36a3618e7..705d3c5fa 100644 --- a/packages/svelte2tsx/svelte-shims.d.ts +++ b/packages/svelte2tsx/svelte-shims.d.ts @@ -163,3 +163,6 @@ declare function __sveltets_each( declare function createSvelte2TsxComponent( render: () => {props?: Props, events?: Events, slots?: Slots } ): AConstructorTypeOf;}, Slots>>; + +declare function __sveltets_unwrapArr(arr: ArrayLike): T +declare function __sveltets_unwrapPromiseLike(promise: PromiseLike | T): T diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx new file mode 100644 index 000000000..3fdf47248 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx @@ -0,0 +1,14 @@ +/// +<>;function render() { + + const promise = Promise.resolve(); +; +() => (<> + +{() => {let _$$p = (promise); __sveltets_awaitThen(_$$p, (value) => {<> + Hello +})}}); +return { props: {}, slots: {default: {a:__sveltets_unwrapPromiseLike(promise)}}, getters: {}, events: {} }} + +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte new file mode 100644 index 000000000..ded6eb639 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte @@ -0,0 +1,7 @@ + + +{#await promise then value} + Hello +{/await} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx new file mode 100644 index 000000000..2eceadc7e --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx @@ -0,0 +1,14 @@ +/// +<>;function render() { + + const items = []; +; +() => (<> + +{__sveltets_each(items, (item) => <> + Hello +)}); +return { props: {}, slots: {default: {a:_sveltets_unwrapArr(items)}}, getters: {}, events: {} }} + +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte new file mode 100644 index 000000000..578432f10 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte @@ -0,0 +1,7 @@ + + +{#each items as item} + Hello +{/each} From 9e277468592cfb9b2e7dc18d70fe384f8d041fc7 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 16 Aug 2020 22:02:57 +0800 Subject: [PATCH 02/21] basic support for destructuring --- packages/svelte2tsx/package.json | 1 + packages/svelte2tsx/src/interfaces.ts | 21 ++++++ .../svelte2tsx/src/nodes/TemplateScope.ts | 14 ++-- packages/svelte2tsx/src/nodes/slot.ts | 64 +++++++++++++++---- packages/svelte2tsx/src/svelte2tsx.ts | 40 +++++++++--- packages/svelte2tsx/src/utils/svelteAst.ts | 9 +++ .../component-slot-inside-await/expected.tsx | 14 ++-- .../component-slot-inside-await/input.svelte | 7 +- .../component-slot-inside-each/expected.tsx | 14 ++-- .../component-slot-inside-each/input.svelte | 7 +- yarn.lock | 20 ++++++ 11 files changed, 162 insertions(+), 49 deletions(-) diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json index b7be35dff..b83774a21 100644 --- a/packages/svelte2tsx/package.json +++ b/packages/svelte2tsx/package.json @@ -23,6 +23,7 @@ "@types/vfile": "^3.0.2", "magic-string": "^0.25.4", "mocha": "^6.2.2", + "periscopic": "^2.0.2", "rollup": "^1.12.0", "rollup-plugin-commonjs": "^10.0.0", "rollup-plugin-delete": "^1.1.0", diff --git a/packages/svelte2tsx/src/interfaces.ts b/packages/svelte2tsx/src/interfaces.ts index 2613bb5c6..46708f852 100644 --- a/packages/svelte2tsx/src/interfaces.ts +++ b/packages/svelte2tsx/src/interfaces.ts @@ -17,3 +17,24 @@ export interface CreateRenderFunctionPara extends InstanceScriptProcessResult { events: Map; isTsFile: boolean; } + +export interface Identifier { + name: string; + type: 'Identifier'; +} + +export interface ArrayPattern { + type: 'ArrayPattern'; + start: number; + end: number; +} + +export interface ObjectPattern { + type: 'ArrayPattern'; + start: number; + end: number; +} + +export interface BaseNode { + type: string; +} diff --git a/packages/svelte2tsx/src/nodes/TemplateScope.ts b/packages/svelte2tsx/src/nodes/TemplateScope.ts index c4884253f..3b1cf69ce 100644 --- a/packages/svelte2tsx/src/nodes/TemplateScope.ts +++ b/packages/svelte2tsx/src/nodes/TemplateScope.ts @@ -1,4 +1,5 @@ import { Node } from 'estree-walker'; +import { Identifier } from '../interfaces'; /** * adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/Slot.ts @@ -6,15 +7,20 @@ import { Node } from 'estree-walker'; export default class TemplateScope { names: Set; owners: Map = new Map(); - inits: Map = new Map(); + inits: Map = new Map(); parent?: TemplateScope; constructor(parent?: TemplateScope) { this.parent = parent; this.names = new Set(parent ? parent.names : []); - } + } + + addMany(inits: Identifier[], owner: Node) { + inits.forEach((item) => this.add(item, owner)); + return this; + } - add(init: Node, owner: Node) { + add(init: Identifier, owner: Node) { const { name } = init; this.names.add(name); this.inits.set(name, init); @@ -31,7 +37,7 @@ export default class TemplateScope { return this.owners.get(name) || (this.parent && this.parent.getOwner(name)); } - getInit(name: string): Node { + getInit(name: string): Identifier { return this.inits.get(name) || this.parent?.getInit(name); } diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 6b2a94796..e954cdd1b 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -2,8 +2,9 @@ import { Node, walk } from 'estree-walker'; import MagicString from 'magic-string'; import { attributeValueIsString, isMember } from '../utils/svelteAst'; import TemplateScope from './TemplateScope'; +import { Identifier } from '../interfaces'; -function AttributeStrValueAsJsExpression(htmlx: string, attr: Node): string { +function AttributeStrValueAsJsExpression(attr: Node): string { if (attr.value.length == 0) return "''"; //wut? //handle single value @@ -25,33 +26,69 @@ export class SlotHandler { constructor(private readonly str: MagicString, private readonly htmlx: string) { } slots = new Map>(); - resloved = new Map(); + resloved = new Map(); + reslovedExpression = new Map(); - reslove(identifierDef: Node, initExpression: Node, scope: TemplateScope) { + reslove(identifierDef: Identifier, initExpression: Node, scope: TemplateScope) { let resloved = this.resloved.get(identifierDef); if (resloved) { return resloved; } + + resloved = this.getResloveExpressionStr(identifierDef, scope, initExpression); + + this.resloved.set(identifierDef, resloved); + + return resloved; + } + + private getResloveExpressionStr( + identifierDef: Identifier, + scope: TemplateScope, + initExpression: Node + ) { const { name } = identifierDef; const owner = scope.getOwner(name); if (owner.type === 'CatchBlock') { - resloved = '__sveltets_any({})'; - } else if (owner.type === 'ThenBlock') { + return '__sveltets_any({})'; + } + else if (owner.type === 'ThenBlock') { const reslovedExpression = this.resolveExpression(initExpression, scope); - resloved = `__sveltets_unwrapPromiseLike(${reslovedExpression})`; - } else if (owner.type === 'EachBlock') { + return `__sveltets_unwrapPromiseLike(${reslovedExpression})`; + } + else if (owner.type === 'EachBlock') { const reslovedExpression = this.resolveExpression(initExpression, scope); - resloved = `_sveltets_unwrapArr(${reslovedExpression})`; + return `__sveltets_unwrapArr(${reslovedExpression})`; } - this.resloved.set(identifierDef, resloved); - return resloved; + return null; + } + + resloveDestructuringAssigment( + destructuringNode: Node, + identifiers: Identifier[], + initExpression: Node, + scope: TemplateScope, + ) { + const destructuring = this.htmlx.slice(destructuringNode.start, destructuringNode.end); + identifiers.forEach((identifier) => { + const resloved = this.getResloveExpressionStr(identifier, scope, initExpression); + this.resloved.set( + identifier, + `((${destructuring}) => ${identifier.name})(${resloved})` + ); + }); } private resolveExpression(expression: Node, scope: TemplateScope) { + let resolved = this.reslovedExpression.get(expression); + if (resolved) { + return resolved; + } + const strForExpression = new MagicString(this.htmlx); const identifiers: Node[] = []; @@ -71,7 +108,10 @@ export class SlotHandler { strForExpression.overwrite(start, end, value); } - return strForExpression.slice(expression.start, expression.end); + resolved = strForExpression.slice(expression.start, expression.end); + this.reslovedExpression.set(expression, resolved); + + return resolved; } handleSlot(node: Node, scope: TemplateScope) { @@ -84,7 +124,7 @@ export class SlotHandler { if (!attr.value.length) continue; if (attributeValueIsString(attr)) { - attributes.set(attr.name, AttributeStrValueAsJsExpression(this.str.original, attr)); + attributes.set(attr.name, AttributeStrValueAsJsExpression(attr)); continue; } attributes.set(attr.name, this.resloveAttr(attr, scope)); diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index 3a5d48260..9cbf5252f 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -8,12 +8,13 @@ import { Node } from 'estree-walker'; import * as ts from 'typescript'; import { createEventHandlerTransformer, eventMapToString } from './nodes/event-handler'; import { findExortKeyword } from './utils/tsAst'; -import { InstanceScriptProcessResult, CreateRenderFunctionPara } from './interfaces'; +import { InstanceScriptProcessResult, CreateRenderFunctionPara, Identifier, BaseNode } from './interfaces'; import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters'; import { ExportedNames } from './nodes/ExportedNames'; import * as astUtil from './utils/svelteAst'; import { SlotHandler } from './nodes/slot'; import TemplateScope from './nodes/TemplateScope'; +import { extract_identifiers as extractIdentifiers } from 'periscopic'; type TemplateProcessResult = { uses$$props: boolean; @@ -253,24 +254,47 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { let templateScope = new TemplateScope(); const { handleEventHandler, getEvents } = createEventHandlerTransformer(); - const handleEachScope = (node: Node) => { + const handleEach = (node: Node) => { templateScope = templateScope.child(); if (node.context) { - templateScope.add(node.context, node); + handleScopeAndResolveForSlot(node.context, node.expression, node); } }; - const handleAwaitScope = (node: Node) => { + const handleAwait = (node: Node) => { templateScope = templateScope.child(); if (node.value) { - templateScope.add(node.value, node.then); + handleScopeAndResolveForSlot(node.value, node.expression, node.then); } if (node.error) { templateScope.add(node.error, node.catch); } }; + const handleScopeAndResolveForSlot = ( + identifierDef: BaseNode, + initExpression: Node, + owner: Node + ) => { + if (astUtil.isIdentifier(identifierDef)) { + templateScope.add(identifierDef, owner); + + slotHandler.reslove(identifierDef, initExpression, templateScope); + } + if (astUtil.isDestructuringPatterns(identifierDef)) { + const identifiers = extractIdentifiers(identifierDef as any); + templateScope.addMany(identifiers, owner); + + slotHandler.resloveDestructuringAssigment( + identifierDef, + identifiers, + initExpression, + templateScope + ); + } + }; + const onHtmlxWalk = (node: Node, parent: Node, prop: string) => { if ( prop == 'params' && @@ -314,12 +338,10 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { isDeclaration = true; break; case 'EachBlock': - handleEachScope(node); - slotHandler.reslove(node.context, node.expression, templateScope); + handleEach(node); break; case 'AwaitBlock': - handleAwaitScope(node); - slotHandler.reslove(node.value, node.expression, templateScope); + handleAwait(node); break; } }; diff --git a/packages/svelte2tsx/src/utils/svelteAst.ts b/packages/svelte2tsx/src/utils/svelteAst.ts index 36acc9192..3dcd52869 100644 --- a/packages/svelte2tsx/src/utils/svelteAst.ts +++ b/packages/svelte2tsx/src/utils/svelteAst.ts @@ -1,4 +1,5 @@ import { Node } from 'estree-walker'; +import { Identifier, BaseNode, ArrayPattern, ObjectPattern } from '../interfaces'; export function isMember(parent: Node, prop: string) { return parent.type == 'MemberExpression' && prop == 'property'; @@ -15,3 +16,11 @@ export function isText(node: Node) { export function attributeValueIsString(attr: Node) { return attr.value.length !== 1 || attr.value[0]?.type === 'Text'; } + +export function isDestructuringPatterns(node: BaseNode): node is ArrayPattern | ObjectPattern { + return node.type === 'ArrayPattern' || node.type === 'ObjectPattern'; +} + +export function isIdentifier(node: any): node is Identifier { + return node.type === 'Identifier'; +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx index 3fdf47248..ed0e722ee 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx @@ -1,14 +1,12 @@ /// <>;function render() { - - const promise = Promise.resolve(); -; -() => (<> - -{() => {let _$$p = (promise); __sveltets_awaitThen(_$$p, (value) => {<> +<>{() => {let _$$p = (promise); __sveltets_awaitThen(_$$p, (value) => {<> Hello -})}}); -return { props: {}, slots: {default: {a:__sveltets_unwrapPromiseLike(promise)}}, getters: {}, events: {} }} +})}} +{() => {let _$$p = (promise2); __sveltets_awaitThen(_$$p, ({ b }) => {<> + Hello +})}} +return { props: {}, slots: {default: {a:__sveltets_unwrapPromiseLike(promise)}, second: {a:(({ b }) => b)(__sveltets_unwrapPromiseLike(promise2))}}, getters: {}, events: {} }} export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte index ded6eb639..3be7f4446 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte @@ -1,7 +1,6 @@ - - {#await promise then value} Hello {/await} +{#await promise2 then { b }} + Hello +{/await} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx index 2eceadc7e..796203c7c 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx @@ -1,14 +1,12 @@ /// <>;function render() { - - const items = []; -; -() => (<> - -{__sveltets_each(items, (item) => <> +<>{__sveltets_each(items, (item) => <> Hello -)}); -return { props: {}, slots: {default: {a:_sveltets_unwrapArr(items)}}, getters: {}, events: {} }} +)} +{__sveltets_each(items2, ({ a }) => <> + Hello +)} +return { props: {}, slots: {default: {a:__sveltets_unwrapArr(items)}, second: {a:(({ a }) => a)(__sveltets_unwrapArr(items2))}}, getters: {}, events: {} }} export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte index 578432f10..008f1e50b 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte @@ -1,7 +1,6 @@ - - {#each items as item} Hello {/each} +{#each items2 as { a }} + Hello +{/each} diff --git a/yarn.lock b/yarn.lock index 81a84a62d..b0604d447 100644 --- a/yarn.lock +++ b/yarn.lock @@ -834,6 +834,11 @@ estree-walker@^0.6.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== +estree-walker@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + estree-walker@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.1.tgz#f8e030fb21cefa183b44b7ad516b747434e7a3e0" @@ -1194,6 +1199,13 @@ is-reference@^1.1.2: dependencies: "@types/estree" "0.0.39" +is-reference@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + is-regex@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" @@ -1652,6 +1664,14 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +periscopic@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-2.0.2.tgz#17cb0fd368b6e7774cbaa62fa74897c01c7a1bf3" + integrity sha512-Ngkg+fjibPB9V2ss67QY3EmNd+NBoXoxUkNEsDdvo4wk4yPW0LHyRh37/L61bkifUQsbtJxrbt8DE1oLjdV9Nw== + dependencies: + estree-walker "^1.0.0" + is-reference "^1.1.4" + picomatch@^2.0.4: version "2.2.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" From 113c5e18ddabbedf2123a783d7868afadb109d61 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 16 Aug 2020 22:10:21 +0800 Subject: [PATCH 03/21] fix TemplateScope reference code url --- packages/svelte2tsx/src/nodes/TemplateScope.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte2tsx/src/nodes/TemplateScope.ts b/packages/svelte2tsx/src/nodes/TemplateScope.ts index 3b1cf69ce..0b93baa5e 100644 --- a/packages/svelte2tsx/src/nodes/TemplateScope.ts +++ b/packages/svelte2tsx/src/nodes/TemplateScope.ts @@ -2,7 +2,7 @@ import { Node } from 'estree-walker'; import { Identifier } from '../interfaces'; /** - * adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/Slot.ts + * adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/shared/TemplateScope.ts */ export default class TemplateScope { names: Set; From f930623ce1d7eecbd4ae279abfde817a94074107 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 16 Aug 2020 22:59:46 +0800 Subject: [PATCH 04/21] indent --- .../svelte2tsx/src/nodes/TemplateScope.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/svelte2tsx/src/nodes/TemplateScope.ts b/packages/svelte2tsx/src/nodes/TemplateScope.ts index 0b93baa5e..e9fc043bf 100644 --- a/packages/svelte2tsx/src/nodes/TemplateScope.ts +++ b/packages/svelte2tsx/src/nodes/TemplateScope.ts @@ -5,14 +5,14 @@ import { Identifier } from '../interfaces'; * adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/shared/TemplateScope.ts */ export default class TemplateScope { - names: Set; + names: Set; owners: Map = new Map(); inits: Map = new Map(); - parent?: TemplateScope; + parent?: TemplateScope; - constructor(parent?: TemplateScope) { - this.parent = parent; - this.names = new Set(parent ? parent.names : []); + constructor(parent?: TemplateScope) { + this.parent = parent; + this.names = new Set(parent ? parent.names : []); } addMany(inits: Identifier[], owner: Node) { @@ -20,29 +20,29 @@ export default class TemplateScope { return this; } - add(init: Identifier, owner: Node) { + add(init: Identifier, owner: Node) { const { name } = init; - this.names.add(name); + this.names.add(name); this.inits.set(name, init); - this.owners.set(name, owner); - return this; - } + this.owners.set(name, owner); + return this; + } - child() { - const child = new TemplateScope(this); - return child; - } + child() { + const child = new TemplateScope(this); + return child; + } - getOwner(name: string): Node { - return this.owners.get(name) || (this.parent && this.parent.getOwner(name)); + getOwner(name: string): Node { + return this.owners.get(name) || (this.parent && this.parent.getOwner(name)); } getInit(name: string): Identifier { return this.inits.get(name) || this.parent?.getInit(name); } - isLet(name: string) { - const owner = this.getOwner(name); - return owner && (owner.type === 'Element' || owner.type === 'InlineComponent'); - } + isLet(name: string) { + const owner = this.getOwner(name); + return owner && (owner.type === 'Element' || owner.type === 'InlineComponent'); + } } From 55527503ec0dbfcf8c3bc5d373948eec17672313 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 16 Aug 2020 23:10:26 +0800 Subject: [PATCH 05/21] fix typos --- packages/svelte2tsx/src/nodes/slot.ts | 48 +++++++++++++------------- packages/svelte2tsx/src/svelte2tsx.ts | 12 +++---- packages/svelte2tsx/src/utils/tsAst.ts | 2 +- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index e954cdd1b..8be7fc0c3 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -26,23 +26,23 @@ export class SlotHandler { constructor(private readonly str: MagicString, private readonly htmlx: string) { } slots = new Map>(); - resloved = new Map(); - reslovedExpression = new Map(); + resolved = new Map(); + resolvedExpression = new Map(); - reslove(identifierDef: Identifier, initExpression: Node, scope: TemplateScope) { - let resloved = this.resloved.get(identifierDef); - if (resloved) { - return resloved; + resolve(identifierDef: Identifier, initExpression: Node, scope: TemplateScope) { + let resolved = this.resolved.get(identifierDef); + if (resolved) { + return resolved; } - resloved = this.getResloveExpressionStr(identifierDef, scope, initExpression); + resolved = this.getResolveExpressionStr(identifierDef, scope, initExpression); - this.resloved.set(identifierDef, resloved); + this.resolved.set(identifierDef, resolved); - return resloved; + return resolved; } - private getResloveExpressionStr( + private getResolveExpressionStr( identifierDef: Identifier, scope: TemplateScope, initExpression: Node @@ -55,19 +55,19 @@ export class SlotHandler { return '__sveltets_any({})'; } else if (owner.type === 'ThenBlock') { - const reslovedExpression = this.resolveExpression(initExpression, scope); + const resolvedExpression = this.resolveExpression(initExpression, scope); - return `__sveltets_unwrapPromiseLike(${reslovedExpression})`; + return `__sveltets_unwrapPromiseLike(${resolvedExpression})`; } else if (owner.type === 'EachBlock') { - const reslovedExpression = this.resolveExpression(initExpression, scope); + const resolvedExpression = this.resolveExpression(initExpression, scope); - return `__sveltets_unwrapArr(${reslovedExpression})`; + return `__sveltets_unwrapArr(${resolvedExpression})`; } return null; } - resloveDestructuringAssigment( + resolveDestructuringAssignment( destructuringNode: Node, identifiers: Identifier[], initExpression: Node, @@ -75,16 +75,16 @@ export class SlotHandler { ) { const destructuring = this.htmlx.slice(destructuringNode.start, destructuringNode.end); identifiers.forEach((identifier) => { - const resloved = this.getResloveExpressionStr(identifier, scope, initExpression); - this.resloved.set( + const resolved = this.getResolveExpressionStr(identifier, scope, initExpression); + this.resolved.set( identifier, - `((${destructuring}) => ${identifier.name})(${resloved})` + `((${destructuring}) => ${identifier.name})(${resolved})` ); }); } private resolveExpression(expression: Node, scope: TemplateScope) { - let resolved = this.reslovedExpression.get(expression); + let resolved = this.resolvedExpression.get(expression); if (resolved) { return resolved; } @@ -104,12 +104,12 @@ export class SlotHandler { for (const identifier of identifiers) { const { start, end, name } = identifier; const init = scope.getInit(name); - const value = init ? this.resloved.get(init) : name; + const value = init ? this.resolved.get(init) : name; strForExpression.overwrite(start, end, value); } resolved = strForExpression.slice(expression.start, expression.end); - this.reslovedExpression.set(expression, resolved); + this.resolvedExpression.set(expression, resolved); return resolved; } @@ -127,7 +127,7 @@ export class SlotHandler { attributes.set(attr.name, AttributeStrValueAsJsExpression(attr)); continue; } - attributes.set(attr.name, this.resloveAttr(attr, scope)); + attributes.set(attr.name, this.resolveAttr(attr, scope)); } this.slots.set(slotName, attributes); } @@ -136,7 +136,7 @@ export class SlotHandler { return this.slots; } - resloveAttr(attr: Node, scope: TemplateScope): string { + resolveAttr(attr: Node, scope: TemplateScope): string { const attrVal = attr.value[0]; if (!attrVal) { return null; @@ -145,7 +145,7 @@ export class SlotHandler { if (attrVal.type == 'AttributeShorthand') { const { name } = attrVal.expression; const init = scope.getInit(name); - const resolved = this.resloved.get(init); + const resolved = this.resolved.get(init); return resolved ?? name; } diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index 9cbf5252f..685a66283 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -7,7 +7,7 @@ import { convertHtmlxToJsx } from './htmlxtojsx'; import { Node } from 'estree-walker'; import * as ts from 'typescript'; import { createEventHandlerTransformer, eventMapToString } from './nodes/event-handler'; -import { findExortKeyword } from './utils/tsAst'; +import { findExportKeyword } from './utils/tsAst'; import { InstanceScriptProcessResult, CreateRenderFunctionPara, Identifier, BaseNode } from './interfaces'; import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters'; import { ExportedNames } from './nodes/ExportedNames'; @@ -280,13 +280,13 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { if (astUtil.isIdentifier(identifierDef)) { templateScope.add(identifierDef, owner); - slotHandler.reslove(identifierDef, initExpression, templateScope); + slotHandler.resolve(identifierDef, initExpression, templateScope); } if (astUtil.isDestructuringPatterns(identifierDef)) { const identifiers = extractIdentifiers(identifierDef as any); templateScope.addMany(identifiers, owner); - slotHandler.resloveDestructuringAssigment( + slotHandler.resolveDestructuringAssignment( identifierDef, identifiers, initExpression, @@ -686,7 +686,7 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS const onLeaveCallbacks: onLeaveCallback[] = []; if (ts.isVariableStatement(node)) { - const exportModifier = findExortKeyword(node); + const exportModifier = findExportKeyword(node); if (exportModifier) { const isLet = node.declarationList.flags === ts.NodeFlags.Let; const isConst = node.declarationList.flags === ts.NodeFlags.Const; @@ -707,7 +707,7 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS if (ts.isFunctionDeclaration(node)) { if (node.modifiers) { - const exportModifier = findExortKeyword(node); + const exportModifier = findExportKeyword(node); if (exportModifier) { removeExport(exportModifier.getStart(), exportModifier.end); addGetter(node.name); @@ -719,7 +719,7 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS } if (ts.isClassDeclaration(node)) { - const exportModifier = findExortKeyword(node); + const exportModifier = findExportKeyword(node); if (exportModifier) { removeExport(exportModifier.getStart(), exportModifier.end); addGetter(node.name); diff --git a/packages/svelte2tsx/src/utils/tsAst.ts b/packages/svelte2tsx/src/utils/tsAst.ts index 852250c79..d7a68d5aa 100644 --- a/packages/svelte2tsx/src/utils/tsAst.ts +++ b/packages/svelte2tsx/src/utils/tsAst.ts @@ -1,5 +1,5 @@ import ts from 'typescript'; -export function findExortKeyword(node: ts.Node) { +export function findExportKeyword(node: ts.Node) { return node.modifiers?.find((x) => x.kind == ts.SyntaxKind.ExportKeyword); } From 819f99a00defaaad4fed26cb4e9a5ef93b20f5b6 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 16 Aug 2020 23:11:12 +0800 Subject: [PATCH 06/21] camelCase --- packages/svelte2tsx/src/nodes/slot.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 8be7fc0c3..0d6d8b188 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -4,7 +4,7 @@ import { attributeValueIsString, isMember } from '../utils/svelteAst'; import TemplateScope from './TemplateScope'; import { Identifier } from '../interfaces'; -function AttributeStrValueAsJsExpression(attr: Node): string { +function attributeStrValueAsJsExpression(attr: Node): string { if (attr.value.length == 0) return "''"; //wut? //handle single value @@ -124,7 +124,7 @@ export class SlotHandler { if (!attr.value.length) continue; if (attributeValueIsString(attr)) { - attributes.set(attr.name, AttributeStrValueAsJsExpression(attr)); + attributes.set(attr.name, attributeStrValueAsJsExpression(attr)); continue; } attributes.set(attr.name, this.resolveAttr(attr, scope)); From 50d83c776a6989323c2932c16440bed14352d4d4 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Mon, 17 Aug 2020 18:57:33 +0800 Subject: [PATCH 07/21] test for nest --- .../samples/component-slot-nest-scope/expected.tsx | 11 +++++++++++ .../samples/component-slot-nest-scope/input.svelte | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx new file mode 100644 index 000000000..9978bc9b7 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx @@ -0,0 +1,11 @@ +/// +<>;function render() { +<>{__sveltets_each(items, (item) => <> + {__sveltets_each(item, ({ a }) => <> + Hello + )} +)} +return { props: {}, slots: {default: {a:(({ a }) => a)(__sveltets_unwrapArr(__sveltets_unwrapArr(items)))}}, getters: {}, events: {} }} + +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte new file mode 100644 index 000000000..a46a99e0f --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte @@ -0,0 +1,5 @@ +{#each items as item} + {#each item as { a }} + Hello + {/each} +{/each} From b826904e904c4e80b1b0508f5d365dfb942c0831 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Mon, 17 Aug 2020 19:08:34 +0800 Subject: [PATCH 08/21] var shadowing --- packages/svelte2tsx/src/nodes/slot.ts | 7 +++++-- .../samples/component-slot-var-shadowing/expected.tsx | 9 +++++++++ .../samples/component-slot-var-shadowing/input.svelte | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/input.svelte diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 0d6d8b188..207349941 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -54,13 +54,16 @@ export class SlotHandler { if (owner.type === 'CatchBlock') { return '__sveltets_any({})'; } + + // list.map(list => list.someProperty) + // initExpression's scope should the parent scope of identifier scope else if (owner.type === 'ThenBlock') { - const resolvedExpression = this.resolveExpression(initExpression, scope); + const resolvedExpression = this.resolveExpression(initExpression, scope.parent); return `__sveltets_unwrapPromiseLike(${resolvedExpression})`; } else if (owner.type === 'EachBlock') { - const resolvedExpression = this.resolveExpression(initExpression, scope); + const resolvedExpression = this.resolveExpression(initExpression, scope.parent); return `__sveltets_unwrapArr(${resolvedExpression})`; } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx new file mode 100644 index 000000000..3394a239a --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx @@ -0,0 +1,9 @@ +/// +<>;function render() { +<>{__sveltets_each(items, (items) => <> + Hello +)} +return { props: {}, slots: {default: {a:__sveltets_unwrapArr(items)}}, getters: {}, events: {} }} + +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/input.svelte new file mode 100644 index 000000000..941b323e9 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/input.svelte @@ -0,0 +1,3 @@ +{#each items as items} + Hello +{/each} From 2e950d1e69c2b9ee8e0fe4c9dc9ac8e11e8013b6 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Tue, 18 Aug 2020 08:36:15 +0800 Subject: [PATCH 09/21] catch block --- packages/svelte2tsx/src/svelte2tsx.ts | 2 +- .../samples/component-slot-inside-await/expected.tsx | 4 +++- .../samples/component-slot-inside-await/input.svelte | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index 9bbc1303b..77c5b0fa4 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -269,7 +269,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { handleScopeAndResolveForSlot(node.value, node.expression, node.then); } if (node.error) { - templateScope.add(node.error, node.catch); + handleScopeAndResolveForSlot(node.error, node.expression, node.catch); } }; diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx index ed0e722ee..52d2a131e 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx @@ -2,11 +2,13 @@ <>;function render() { <>{() => {let _$$p = (promise); __sveltets_awaitThen(_$$p, (value) => {<> Hello +}, (err) => {<> + Hello })}} {() => {let _$$p = (promise2); __sveltets_awaitThen(_$$p, ({ b }) => {<> Hello })}} -return { props: {}, slots: {default: {a:__sveltets_unwrapPromiseLike(promise)}, second: {a:(({ b }) => b)(__sveltets_unwrapPromiseLike(promise2))}}, getters: {}, events: {} }} +return { props: {}, slots: {default: {a:__sveltets_unwrapPromiseLike(promise)}, err: {err:__sveltets_any({})}, second: {a:(({ b }) => b)(__sveltets_unwrapPromiseLike(promise2))}}, getters: {}, events: {} }} export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte index 3be7f4446..b052aafee 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte @@ -1,5 +1,7 @@ {#await promise then value} Hello +{:catch err} + Hello {/await} {#await promise2 then { b }} Hello From 2824b1290daaf323d6498a336f87be9e25ac9528 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sat, 22 Aug 2020 08:38:39 +0800 Subject: [PATCH 10/21] fix test --- .../svelte2tsx/samples/component-slot-inside-await/expected.tsx | 2 +- .../svelte2tsx/samples/component-slot-inside-each/expected.tsx | 2 +- .../svelte2tsx/samples/component-slot-nest-scope/expected.tsx | 2 +- .../samples/component-slot-var-shadowing/expected.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx index 52d2a131e..0a939fe2d 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx @@ -10,5 +10,5 @@ })}} return { props: {}, slots: {default: {a:__sveltets_unwrapPromiseLike(promise)}, err: {err:__sveltets_any({})}, second: {a:(({ b }) => b)(__sveltets_unwrapPromiseLike(promise2))}}, getters: {}, events: {} }} -export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx index 796203c7c..9d2be705f 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx @@ -8,5 +8,5 @@ )} return { props: {}, slots: {default: {a:__sveltets_unwrapArr(items)}, second: {a:(({ a }) => a)(__sveltets_unwrapArr(items2))}}, getters: {}, events: {} }} -export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx index 9978bc9b7..45e9cb088 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx @@ -7,5 +7,5 @@ )} return { props: {}, slots: {default: {a:(({ a }) => a)(__sveltets_unwrapArr(__sveltets_unwrapArr(items)))}}, getters: {}, events: {} }} -export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx index 3394a239a..6cf4d4ed5 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-var-shadowing/expected.tsx @@ -5,5 +5,5 @@ )} return { props: {}, slots: {default: {a:__sveltets_unwrapArr(items)}}, getters: {}, events: {} }} -export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(render)) { +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { } From 3e69807b976a0e49dd1947138e3708e85626ebe5 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sat, 22 Aug 2020 09:38:38 +0800 Subject: [PATCH 11/21] test for member aceess, object key and object shorthand --- packages/svelte2tsx/src/interfaces.ts | 4 +++ packages/svelte2tsx/src/nodes/slot.ts | 33 ++++++++++++++++--- packages/svelte2tsx/src/utils/svelteAst.ts | 16 ++++++++- .../component-slot-object-key/expected.tsx | 9 +++++ .../component-slot-object-key/input.svelte | 3 ++ 5 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte diff --git a/packages/svelte2tsx/src/interfaces.ts b/packages/svelte2tsx/src/interfaces.ts index c1b9199b0..a8be54ed6 100644 --- a/packages/svelte2tsx/src/interfaces.ts +++ b/packages/svelte2tsx/src/interfaces.ts @@ -26,6 +26,10 @@ export interface Identifier { type: 'Identifier'; } +export interface IdentifierWithRange extends Identifier, Node { + type: 'Identifier'; +} + export interface ArrayPattern { type: 'ArrayPattern'; start: number; diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 207349941..0d17b9809 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -1,6 +1,6 @@ import { Node, walk } from 'estree-walker'; import MagicString from 'magic-string'; -import { attributeValueIsString, isMember } from '../utils/svelteAst'; +import { attributeValueIsString, isMember, isObjectKey, isObjectValueShortHand, isObjectValue } from '../utils/svelteAst'; import TemplateScope from './TemplateScope'; import { Identifier } from '../interfaces'; @@ -95,19 +95,44 @@ export class SlotHandler { const strForExpression = new MagicString(this.htmlx); const identifiers: Node[] = []; + const objectShortHands: Node[] = []; walk(expression, { enter(node, parent, prop) { - if (node.type === 'Identifier' && (!parent || !isMember(parent, prop))) { + if (node.type === 'Identifier') { + if (parent) { + if (isMember(parent, prop)) + return; + if (isObjectKey(parent, prop)) { + return; + } + if (isObjectValue(parent, prop)) { + // { value } + if (isObjectValueShortHand(parent, node)) { + this.skip(); + objectShortHands.push(node); + } + return; + } + } + this.skip(); identifiers.push(node); } } }); + const getOverwrite = (name: string) => { + const init = scope.getInit(name); + return init ? this.resolved.get(init) : name; + }; + for (const identifier of objectShortHands) { + const { end, name } = identifier; + const value = getOverwrite(name); + strForExpression.appendLeft(end, `:${value}`); + } for (const identifier of identifiers) { const { start, end, name } = identifier; - const init = scope.getInit(name); - const value = init ? this.resolved.get(init) : name; + const value = getOverwrite(name); strForExpression.overwrite(start, end, value); } diff --git a/packages/svelte2tsx/src/utils/svelteAst.ts b/packages/svelte2tsx/src/utils/svelteAst.ts index 3dcd52869..d91ecfefd 100644 --- a/packages/svelte2tsx/src/utils/svelteAst.ts +++ b/packages/svelte2tsx/src/utils/svelteAst.ts @@ -1,5 +1,5 @@ import { Node } from 'estree-walker'; -import { Identifier, BaseNode, ArrayPattern, ObjectPattern } from '../interfaces'; +import { Identifier, BaseNode, ArrayPattern, ObjectPattern, IdentifierWithRange } from '../interfaces'; export function isMember(parent: Node, prop: string) { return parent.type == 'MemberExpression' && prop == 'property'; @@ -9,6 +9,16 @@ export function isObjectKey(parent: Node, prop: string) { return parent.type == 'Property' && prop == 'key'; } +export function isObjectValue(parent: Node, prop: string) { + return parent.type == 'Property' && prop == 'value'; +} + +export function isObjectValueShortHand(parent: Node, node: Node) { + const { value } = parent; + return value && isIdentifierWithRange(value) + && node.start === value.start && node.end == value.end; +} + export function isText(node: Node) { return node.type === 'Text'; } @@ -24,3 +34,7 @@ export function isDestructuringPatterns(node: BaseNode): node is ArrayPattern | export function isIdentifier(node: any): node is Identifier { return node.type === 'Identifier'; } + +export function isIdentifierWithRange(node: any): node is IdentifierWithRange { + return node.type === 'Identifier' && 'start' in node && 'end' in node; +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx new file mode 100644 index 000000000..35e7e1fdc --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx @@ -0,0 +1,9 @@ +/// +<>;function render() { +<>{__sveltets_each(items, (item) => <> + Hello +)} +return { props: {}, slots: {default: {a:__sveltets_unwrapArr(items), b:{ item:__sveltets_unwrapArr(items) }, c:{ item: 'abc' }.item}}, getters: {}, events: {} }} + +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte new file mode 100644 index 000000000..c8bc47f3d --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte @@ -0,0 +1,3 @@ +{#each items as item} + Hello +{/each} From 32928acf5e26bd899ab206fa8c58b59b400a4282 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sat, 22 Aug 2020 13:23:38 +0800 Subject: [PATCH 12/21] fix non object shorthand property --- packages/svelte2tsx/src/nodes/slot.ts | 2 +- packages/svelte2tsx/src/utils/svelteAst.ts | 6 +++--- .../samples/component-slot-object-key/expected.tsx | 4 ++-- .../samples/component-slot-object-key/input.svelte | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 0d17b9809..9937ca1c6 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -110,8 +110,8 @@ export class SlotHandler { if (isObjectValueShortHand(parent, node)) { this.skip(); objectShortHands.push(node); + return; } - return; } } diff --git a/packages/svelte2tsx/src/utils/svelteAst.ts b/packages/svelte2tsx/src/utils/svelteAst.ts index d91ecfefd..0046ba3ff 100644 --- a/packages/svelte2tsx/src/utils/svelteAst.ts +++ b/packages/svelte2tsx/src/utils/svelteAst.ts @@ -13,10 +13,10 @@ export function isObjectValue(parent: Node, prop: string) { return parent.type == 'Property' && prop == 'value'; } -export function isObjectValueShortHand(parent: Node, node: Node) { - const { value } = parent; +export function isObjectValueShortHand(property: Node) { + const { value, key } = property; return value && isIdentifierWithRange(value) - && node.start === value.start && node.end == value.end; + && key.start === value.start && key.end == value.end; } export function isText(node: Node) { diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx index 35e7e1fdc..2c468ef81 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/expected.tsx @@ -1,9 +1,9 @@ /// <>;function render() { <>{__sveltets_each(items, (item) => <> - Hello + Hello )} -return { props: {}, slots: {default: {a:__sveltets_unwrapArr(items), b:{ item:__sveltets_unwrapArr(items) }, c:{ item: 'abc' }.item}}, getters: {}, events: {} }} +return { props: {}, slots: {default: {a:__sveltets_unwrapArr(items), b:{ item:__sveltets_unwrapArr(items) }, c:{ item: 'abc' }.item, d:{ item: __sveltets_unwrapArr(items) }}}, getters: {}, events: {} }} export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte index c8bc47f3d..61411a164 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte @@ -1,3 +1,3 @@ {#each items as item} - Hello + Hello {/each} From 95f3bcd54f36fb761281e93986e0d7d61277a5e5 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 23 Aug 2020 14:46:09 +0800 Subject: [PATCH 13/21] support for component let:prop --- packages/svelte2tsx/src/htmlxtojsx/index.ts | 10 +-- packages/svelte2tsx/src/interfaces.ts | 12 +++ .../svelte2tsx/src/nodes/TemplateScope.ts | 10 +-- packages/svelte2tsx/src/nodes/slot.ts | 79 ++++++++++++++++++- packages/svelte2tsx/src/svelte2tsx.ts | 49 +++++++++++- packages/svelte2tsx/src/utils/svelteAst.ts | 6 ++ .../expected.tsx | 11 +++ .../input.svelte | 5 ++ .../component-slot-let-forward/expected.tsx | 9 +++ .../component-slot-let-forward/input.svelte | 3 + 10 files changed, 177 insertions(+), 17 deletions(-) create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/expected.tsx create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/input.svelte create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/expected.tsx create mode 100644 packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/input.svelte diff --git a/packages/svelte2tsx/src/htmlxtojsx/index.ts b/packages/svelte2tsx/src/htmlxtojsx/index.ts index 7e0c3dd84..ded350a33 100644 --- a/packages/svelte2tsx/src/htmlxtojsx/index.ts +++ b/packages/svelte2tsx/src/htmlxtojsx/index.ts @@ -5,6 +5,7 @@ import { parseHtmlx } from '../htmlxparser'; import svgAttributes from './svgattributes'; import { getTypeForComponent } from './nodes/component-type'; import { handleAwait } from './nodes/await-block'; +import { getSlotName } from '../utils/svelteAst'; type ElementType = string; const oneWayBindingAttributes: Map = new Map( @@ -344,12 +345,9 @@ export function convertHtmlxToJsx( //we could lean on leave/enter, but I am lazy if (!el.children) return; for (const child of el.children) { - if (!child.attributes) continue; - const slot = child.attributes.find((a) => a.name == 'slot'); - if (slot) { - if (slot.value && slot.value.length) { - handleSlot(child, el.name, slot.value[0].raw); - } + const slotName = getSlotName(child); + if (slotName) { + handleSlot(child, el.name, slotName); } } }; diff --git a/packages/svelte2tsx/src/interfaces.ts b/packages/svelte2tsx/src/interfaces.ts index a8be54ed6..cdda12863 100644 --- a/packages/svelte2tsx/src/interfaces.ts +++ b/packages/svelte2tsx/src/interfaces.ts @@ -45,6 +45,18 @@ export interface ObjectPattern { export interface BaseNode { type: string; } + +export interface WithName { + type: string; + name: string; +} + +export interface Let { + type: 'Let'; + name: string; + expression: Node | null; +} + export interface AddComponentExportPara { str: MagicString; uses$$propsOr$$restProps: boolean; diff --git a/packages/svelte2tsx/src/nodes/TemplateScope.ts b/packages/svelte2tsx/src/nodes/TemplateScope.ts index e9fc043bf..4730bf8de 100644 --- a/packages/svelte2tsx/src/nodes/TemplateScope.ts +++ b/packages/svelte2tsx/src/nodes/TemplateScope.ts @@ -1,5 +1,5 @@ import { Node } from 'estree-walker'; -import { Identifier } from '../interfaces'; +import { WithName } from '../interfaces'; /** * adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/shared/TemplateScope.ts @@ -7,7 +7,7 @@ import { Identifier } from '../interfaces'; export default class TemplateScope { names: Set; owners: Map = new Map(); - inits: Map = new Map(); + inits: Map = new Map(); parent?: TemplateScope; constructor(parent?: TemplateScope) { @@ -15,12 +15,12 @@ export default class TemplateScope { this.names = new Set(parent ? parent.names : []); } - addMany(inits: Identifier[], owner: Node) { + addMany(inits: WithName[], owner: Node) { inits.forEach((item) => this.add(item, owner)); return this; } - add(init: Identifier, owner: Node) { + add(init: WithName, owner: Node) { const { name } = init; this.names.add(name); this.inits.set(name, init); @@ -37,7 +37,7 @@ export default class TemplateScope { return this.owners.get(name) || (this.parent && this.parent.getOwner(name)); } - getInit(name: string): Identifier { + getInit(name: string): WithName { return this.inits.get(name) || this.parent?.getInit(name); } diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 9937ca1c6..8f9eb4849 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -1,8 +1,15 @@ import { Node, walk } from 'estree-walker'; import MagicString from 'magic-string'; -import { attributeValueIsString, isMember, isObjectKey, isObjectValueShortHand, isObjectValue } from '../utils/svelteAst'; +import { + attributeValueIsString, + isMember, + isObjectKey, + isObjectValueShortHand, + isObjectValue, + getSlotName, +} from '../utils/svelteAst'; import TemplateScope from './TemplateScope'; -import { Identifier } from '../interfaces'; +import { Identifier, WithName, Let } from '../interfaces'; function attributeStrValueAsJsExpression(attr: Node): string { if (attr.value.length == 0) return "''"; //wut? @@ -26,7 +33,7 @@ export class SlotHandler { constructor(private readonly str: MagicString, private readonly htmlx: string) { } slots = new Map>(); - resolved = new Map(); + resolved = new Map(); resolvedExpression = new Map(); resolve(identifierDef: Identifier, initExpression: Node, scope: TemplateScope) { @@ -86,6 +93,70 @@ export class SlotHandler { }); } + resolveDestructuringAssignmentForLet( + destructuringNode: Node, + identifiers: Identifier[], + letNode: Let, + component: Node, + slotName: string, + ) { + const destructuring = this.htmlx.slice(destructuringNode.start, destructuringNode.end); + identifiers.forEach((identifier) => { + const resolved = this.getResolveExpressionStrForLet(letNode, component, slotName); + this.resolved.set( + identifier, + `((${destructuring}) => ${identifier.name})(${resolved})` + ); + }); + } + + private getResolveExpressionStrForLet(letNode: Let, component: Node, slotName: string) { + const componentTypeStr = `__sveltets_instanceOf(${component.name})`; + + return `${componentTypeStr}.$$slot_def.${slotName}.${letNode.name}`; + } + + resolveLet(letNode: Let, identifierDef: WithName, component: Node, slotName: string) { + let resolved = this.resolved.get(identifierDef); + if (resolved) { + return resolved; + } + + resolved = this.getResolveExpressionStrForLet(letNode, component, slotName); + + this.resolved.set(identifierDef, resolved); + + return resolved; + } + + getSlotConsumerOfComponent(component: Node) { + let result = this.getLetNodes(component, 'default') ?? []; + for (const child of component.children) { + const slotName = getSlotName(child); + + if (slotName) { + const letNodes = this.getLetNodes(child, slotName); + + if (letNodes?.length) { + result = result.concat(letNodes); + } + } + } + + return result; + } + + private getLetNodes(child: Node, slotName: string) { + const letNodes = (child?.attributes as Node[] ?? []).filter( + (attr) => attr.type === 'Let' + ) as unknown as Let[]; + + return letNodes?.map((letNode) => ({ + letNode, + slotName + })); + } + private resolveExpression(expression: Node, scope: TemplateScope) { let resolved = this.resolvedExpression.get(expression); if (resolved) { @@ -107,7 +178,7 @@ export class SlotHandler { } if (isObjectValue(parent, prop)) { // { value } - if (isObjectValueShortHand(parent, node)) { + if (isObjectValueShortHand(parent)) { this.skip(); objectShortHands.push(node); return; diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index bd59ecbb1..4bdb5bbf0 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -4,7 +4,7 @@ import MagicString from 'magic-string'; import path from 'path'; import { parseHtmlx } from './htmlxparser'; import { convertHtmlxToJsx } from './htmlxtojsx'; -import { Node } from 'estree-walker'; +import { Node, walk } from 'estree-walker'; import * as ts from 'typescript'; import { extract_identifiers as extractIdentifiers } from 'periscopic'; import { findExportKeyword, getBinaryAssignmentExpr } from './utils/tsAst'; @@ -14,7 +14,9 @@ import { CreateRenderFunctionPara, AddComponentExportPara, Identifier, - BaseNode + BaseNode, + WithName, + Let } from './interfaces'; import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters'; import { ExportedNames } from './nodes/ExportedNames'; @@ -290,6 +292,46 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { } }; + const handleComponentLet = (component: Node) => { + templateScope = templateScope.child(); + const lets = slotHandler.getSlotConsumerOfComponent(component); + + for (const { letNode, slotName } of lets) { + const { expression } = letNode; + // + if (!expression) { + templateScope.add(letNode, component); + slotHandler.resolveLet(letNode, letNode, component, slotName); + } else { + if (astUtil.isIdentifier(expression)) { + templateScope.add(expression, component); + slotHandler.resolveLet(letNode, expression, component, slotName); + } + const expForExtract = {...expression}; + + // https://github.com/sveltejs/svelte/blob/3a37de364bfbe75202d8e9fcef9e76b9ce6faaa2/src/compiler/compile/nodes/Let.ts#L37 + if (expression.type === 'ArrayExpression') { + expForExtract.type = 'ArrayPattern'; + } + if (expression.type === 'ObjectExpression') { + expForExtract.type = 'ObjectPattern'; + } + if (astUtil.isDestructuringPatterns(expForExtract)) { + const identifiers = extractIdentifiers(expForExtract as any); + templateScope.addMany(identifiers, component); + + slotHandler.resolveDestructuringAssignmentForLet( + expForExtract, + identifiers, + letNode, + component, + slotName + ); + } + } + } + }; + const handleScopeAndResolveForSlot = ( identifierDef: BaseNode, initExpression: Node, @@ -362,6 +404,9 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { case 'AwaitBlock': handleAwait(node); break; + case 'InlineComponent': + handleComponentLet(node); + break; } }; diff --git a/packages/svelte2tsx/src/utils/svelteAst.ts b/packages/svelte2tsx/src/utils/svelteAst.ts index 0046ba3ff..5ab340f85 100644 --- a/packages/svelte2tsx/src/utils/svelteAst.ts +++ b/packages/svelte2tsx/src/utils/svelteAst.ts @@ -38,3 +38,9 @@ export function isIdentifier(node: any): node is Identifier { export function isIdentifierWithRange(node: any): node is IdentifierWithRange { return node.type === 'Identifier' && 'start' in node && 'end' in node; } + +export function getSlotName(child: Node): string | undefined { + const slot = (child.attributes as Node[])?.find((a) => a.name == 'slot'); + + return slot?.value?.[0].raw; +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/expected.tsx new file mode 100644 index 000000000..7fa4d05a5 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/expected.tsx @@ -0,0 +1,11 @@ +/// +<>;function render() { +<> +
{() => { let {a} = __sveltets_instanceOf(Component).$$slot_def.b;<> + + }}
+
+return { props: {}, slots: {default: {a:__sveltets_instanceOf(Component).$$slot_def.b.a}}, getters: {}, events: {} }} + +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/input.svelte new file mode 100644 index 000000000..ffb917928 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/input.svelte @@ -0,0 +1,5 @@ + +
+ +
+
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/expected.tsx new file mode 100644 index 000000000..27af8c3b5 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/expected.tsx @@ -0,0 +1,9 @@ +/// +<>;function render() { +<>{() => { let {name:n, thing, whatever:{ bla }} = __sveltets_instanceOf(Component).$$slot_def.default;<> + +}} +return { props: {}, slots: {default: {n:__sveltets_instanceOf(Component).$$slot_def.default.name, thing:__sveltets_instanceOf(Component).$$slot_def.default.thing, bla:(({ bla }) => bla)(__sveltets_instanceOf(Component).$$slot_def.default.whatever)}}, getters: {}, events: {} }} + +export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { +} diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/input.svelte new file mode 100644 index 000000000..a2a46d22f --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward/input.svelte @@ -0,0 +1,3 @@ + + + From ffca00c3fcc4fedbba50662705533a3067666eb5 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 23 Aug 2020 14:52:46 +0800 Subject: [PATCH 14/21] lint --- packages/svelte2tsx/src/svelte2tsx.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index 4bdb5bbf0..095bbc31b 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -4,7 +4,7 @@ import MagicString from 'magic-string'; import path from 'path'; import { parseHtmlx } from './htmlxparser'; import { convertHtmlxToJsx } from './htmlxtojsx'; -import { Node, walk } from 'estree-walker'; +import { Node } from 'estree-walker'; import * as ts from 'typescript'; import { extract_identifiers as extractIdentifiers } from 'periscopic'; import { findExportKeyword, getBinaryAssignmentExpr } from './utils/tsAst'; @@ -13,10 +13,7 @@ import { InstanceScriptProcessResult, CreateRenderFunctionPara, AddComponentExportPara, - Identifier, BaseNode, - WithName, - Let } from './interfaces'; import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters'; import { ExportedNames } from './nodes/ExportedNames'; From 8a872b64e4671b2cda400d4ed2d7bedb72edd0bc Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 23 Aug 2020 14:57:02 +0800 Subject: [PATCH 15/21] leave component scope --- packages/svelte2tsx/src/svelte2tsx.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index 095bbc31b..5ff5f1ae3 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -418,6 +418,9 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { if (prop == 'id' && parent.type == 'VariableDeclarator') { isDeclaration = false; } + const onTemplateScopeLeave = () => { + templateScope = templateScope.parent; + }; switch (node.type) { case 'BlockStatement': @@ -430,10 +433,13 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { leaveArrowFunctionExpression(); break; case 'EachBlock': - templateScope = templateScope.parent; + onTemplateScopeLeave(); break; case 'AwaitBlock': - templateScope = templateScope.parent; + onTemplateScopeLeave(); + break; + case 'InlineComponent': + onTemplateScopeLeave(); break; } }; From 699818602604350c1ea1ef7a25916bc66cf01a74 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Sun, 23 Aug 2020 17:19:53 +0800 Subject: [PATCH 16/21] more test about scope --- .../samples/component-slot-nest-scope/expected.tsx | 10 ++++++++-- .../samples/component-slot-nest-scope/input.svelte | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx index 45e9cb088..8318853bb 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx @@ -4,8 +4,14 @@ {__sveltets_each(item, ({ a }) => <> Hello )} -)} -return { props: {}, slots: {default: {a:(({ a }) => a)(__sveltets_unwrapArr(__sveltets_unwrapArr(items)))}}, getters: {}, events: {} }} + +)} +{() => { let {c} = __sveltets_instanceOf(Component).$$slot_def.default;<>{ c }}} +{() => {let _$$p = (promise); __sveltets_awaitThen(_$$p, (d) => {<> + {d} +})}} + +return { props: {}, slots: {default: {a:(({ a }) => a)(__sveltets_unwrapArr(__sveltets_unwrapArr(items)))}, second: {a:a}, third: {d:d, c:c}}, getters: {}, events: {} }} export default class Input__SvelteComponent_ extends createSvelte2TsxComponent(__sveltets_partial(__sveltets_with_any_event(render))) { } diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte index a46a99e0f..d6675da23 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte @@ -2,4 +2,10 @@ {#each item as { a }} Hello {/each} + {/each} +{ c } +{#await promise then d} + {d} +{/await} + From 039e1fb68dbd7c238fd3342bb83579d57b66ec4b Mon Sep 17 00:00:00 2001 From: "Lyu, Wei-Da" <36730922+jasonlyu123@users.noreply.github.com> Date: Mon, 24 Aug 2020 13:28:06 +0800 Subject: [PATCH 17/21] TemplateScope optional chaining Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- packages/svelte2tsx/src/nodes/TemplateScope.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte2tsx/src/nodes/TemplateScope.ts b/packages/svelte2tsx/src/nodes/TemplateScope.ts index 4730bf8de..a6d8c9ac4 100644 --- a/packages/svelte2tsx/src/nodes/TemplateScope.ts +++ b/packages/svelte2tsx/src/nodes/TemplateScope.ts @@ -34,7 +34,7 @@ export default class TemplateScope { } getOwner(name: string): Node { - return this.owners.get(name) || (this.parent && this.parent.getOwner(name)); + return this.owners.get(name) || this.parent?.getOwner(name); } getInit(name: string): WithName { From 9f1f871cbcc1e124d7743135bf9053daa9a576fe Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Mon, 24 Aug 2020 16:17:48 +0800 Subject: [PATCH 18/21] null check for resolving and getOwner --- packages/svelte2tsx/src/nodes/slot.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 8f9eb4849..0cb863d54 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -43,8 +43,9 @@ export class SlotHandler { } resolved = this.getResolveExpressionStr(identifierDef, scope, initExpression); - - this.resolved.set(identifierDef, resolved); + if (resolved) { + this.resolved.set(identifierDef, resolved); + } return resolved; } @@ -58,18 +59,17 @@ export class SlotHandler { const owner = scope.getOwner(name); - if (owner.type === 'CatchBlock') { + if (owner?.type === 'CatchBlock') { return '__sveltets_any({})'; } // list.map(list => list.someProperty) // initExpression's scope should the parent scope of identifier scope - else if (owner.type === 'ThenBlock') { + else if (owner?.type === 'ThenBlock') { const resolvedExpression = this.resolveExpression(initExpression, scope.parent); return `__sveltets_unwrapPromiseLike(${resolvedExpression})`; - } - else if (owner.type === 'EachBlock') { + } else if (owner?.type === 'EachBlock') { const resolvedExpression = this.resolveExpression(initExpression, scope.parent); return `__sveltets_unwrapArr(${resolvedExpression})`; @@ -86,10 +86,12 @@ export class SlotHandler { const destructuring = this.htmlx.slice(destructuringNode.start, destructuringNode.end); identifiers.forEach((identifier) => { const resolved = this.getResolveExpressionStr(identifier, scope, initExpression); - this.resolved.set( - identifier, - `((${destructuring}) => ${identifier.name})(${resolved})` - ); + if (resolved) { + this.resolved.set( + identifier, + `((${destructuring}) => ${identifier.name})(${resolved})`, + ); + } }); } From 54a77dc2320ce2a9c26358a34ed685b45778fc42 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Mon, 24 Aug 2020 16:18:29 +0800 Subject: [PATCH 19/21] better typing for ast nodes 1. extend def from estree and add range to it 2. remove identifier without range, it seems to be an old behaviour --- packages/svelte2tsx/src/interfaces.ts | 42 +++++++++++----------- packages/svelte2tsx/src/nodes/slot.ts | 40 +++++++++++---------- packages/svelte2tsx/src/svelte2tsx.ts | 11 +++--- packages/svelte2tsx/src/utils/svelteAst.ts | 21 ++++++----- 4 files changed, 59 insertions(+), 55 deletions(-) diff --git a/packages/svelte2tsx/src/interfaces.ts b/packages/svelte2tsx/src/interfaces.ts index cdda12863..27b517aff 100644 --- a/packages/svelte2tsx/src/interfaces.ts +++ b/packages/svelte2tsx/src/interfaces.ts @@ -1,5 +1,6 @@ import MagicString from 'magic-string'; import { Node } from 'estree-walker'; +import { ArrayPattern, ObjectPattern, Identifier } from 'estree'; import { ExportedNames } from './nodes/ExportedNames'; import { ComponentEvents } from './nodes/ComponentEvents'; @@ -21,40 +22,37 @@ export interface CreateRenderFunctionPara extends InstanceScriptProcessResult { isTsFile: boolean; } -export interface Identifier { - name: string; - type: 'Identifier'; -} - -export interface IdentifierWithRange extends Identifier, Node { - type: 'Identifier'; -} - -export interface ArrayPattern { - type: 'ArrayPattern'; +export interface NodeRange { start: number; end: number; } -export interface ObjectPattern { - type: 'ArrayPattern'; - start: number; - end: number; -} +export interface SvelteIdentifier extends Identifier, NodeRange {} -export interface BaseNode { - type: string; -} +export interface SvelteArrayPattern extends ArrayPattern, NodeRange {} + +export interface SvelteObjectPattern extends ObjectPattern, NodeRange {} export interface WithName { type: string; name: string; } -export interface Let { - type: 'Let'; +export type DirectiveType = + | 'Action' + | 'Animation' + | 'Binding' + | 'Class' + | 'EventHandler' + | 'Let' + | 'Ref' + | 'Transition'; + +export interface BaseDirective extends Node { + type: DirectiveType; + expression: null | Node; name: string; - expression: Node | null; + modifiers: string[]; } export interface AddComponentExportPara { diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts index 0cb863d54..470e336e6 100644 --- a/packages/svelte2tsx/src/nodes/slot.ts +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -9,7 +9,7 @@ import { getSlotName, } from '../utils/svelteAst'; import TemplateScope from './TemplateScope'; -import { Identifier, WithName, Let } from '../interfaces'; +import { SvelteIdentifier, WithName, BaseDirective } from '../interfaces'; function attributeStrValueAsJsExpression(attr: Node): string { if (attr.value.length == 0) return "''"; //wut? @@ -29,14 +29,13 @@ function attributeStrValueAsJsExpression(attr: Node): string { } export class SlotHandler { - - constructor(private readonly str: MagicString, private readonly htmlx: string) { } + constructor(private readonly htmlx: string) {} slots = new Map>(); resolved = new Map(); resolvedExpression = new Map(); - resolve(identifierDef: Identifier, initExpression: Node, scope: TemplateScope) { + resolve(identifierDef: SvelteIdentifier, initExpression: Node, scope: TemplateScope) { let resolved = this.resolved.get(identifierDef); if (resolved) { return resolved; @@ -51,9 +50,9 @@ export class SlotHandler { } private getResolveExpressionStr( - identifierDef: Identifier, + identifierDef: SvelteIdentifier, scope: TemplateScope, - initExpression: Node + initExpression: Node, ) { const { name } = identifierDef; @@ -79,7 +78,7 @@ export class SlotHandler { resolveDestructuringAssignment( destructuringNode: Node, - identifiers: Identifier[], + identifiers: SvelteIdentifier[], initExpression: Node, scope: TemplateScope, ) { @@ -97,8 +96,8 @@ export class SlotHandler { resolveDestructuringAssignmentForLet( destructuringNode: Node, - identifiers: Identifier[], - letNode: Let, + identifiers: SvelteIdentifier[], + letNode: BaseDirective, component: Node, slotName: string, ) { @@ -107,18 +106,22 @@ export class SlotHandler { const resolved = this.getResolveExpressionStrForLet(letNode, component, slotName); this.resolved.set( identifier, - `((${destructuring}) => ${identifier.name})(${resolved})` + `((${destructuring}) => ${identifier.name})(${resolved})`, ); }); } - private getResolveExpressionStrForLet(letNode: Let, component: Node, slotName: string) { + private getResolveExpressionStrForLet( + letNode: BaseDirective, + component: Node, + slotName: string, + ) { const componentTypeStr = `__sveltets_instanceOf(${component.name})`; return `${componentTypeStr}.$$slot_def.${slotName}.${letNode.name}`; } - resolveLet(letNode: Let, identifierDef: WithName, component: Node, slotName: string) { + resolveLet(letNode: BaseDirective, identifierDef: WithName, component: Node, slotName: string) { let resolved = this.resolved.get(identifierDef); if (resolved) { return resolved; @@ -149,13 +152,13 @@ export class SlotHandler { } private getLetNodes(child: Node, slotName: string) { - const letNodes = (child?.attributes as Node[] ?? []).filter( - (attr) => attr.type === 'Let' - ) as unknown as Let[]; + const letNodes = ((child?.attributes as Node[]) ?? []).filter( + (attr) => attr.type === 'Let', + ) as BaseDirective[]; return letNodes?.map((letNode) => ({ letNode, - slotName + slotName, })); } @@ -173,8 +176,7 @@ export class SlotHandler { enter(node, parent, prop) { if (node.type === 'Identifier') { if (parent) { - if (isMember(parent, prop)) - return; + if (isMember(parent, prop)) return; if (isObjectKey(parent, prop)) { return; } @@ -191,7 +193,7 @@ export class SlotHandler { this.skip(); identifiers.push(node); } - } + }, }); const getOverwrite = (name: string) => { diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index 5ff5f1ae3..db1ad62dc 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -13,7 +13,7 @@ import { InstanceScriptProcessResult, CreateRenderFunctionPara, AddComponentExportPara, - BaseNode, + SvelteIdentifier, } from './interfaces'; import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters'; import { ExportedNames } from './nodes/ExportedNames'; @@ -268,7 +268,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { str.remove(node.start, node.end); }; - const slotHandler = new SlotHandler(str, str.original); + const slotHandler = new SlotHandler(str.original); let templateScope = new TemplateScope(); const handleEach = (node: Node) => { @@ -314,7 +314,8 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { expForExtract.type = 'ObjectPattern'; } if (astUtil.isDestructuringPatterns(expForExtract)) { - const identifiers = extractIdentifiers(expForExtract as any); + // the node object is returned as-it no mutation + const identifiers = extractIdentifiers(expForExtract) as SvelteIdentifier[]; templateScope.addMany(identifiers, component); slotHandler.resolveDestructuringAssignmentForLet( @@ -330,7 +331,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { }; const handleScopeAndResolveForSlot = ( - identifierDef: BaseNode, + identifierDef: Node, initExpression: Node, owner: Node ) => { @@ -340,7 +341,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { slotHandler.resolve(identifierDef, initExpression, templateScope); } if (astUtil.isDestructuringPatterns(identifierDef)) { - const identifiers = extractIdentifiers(identifierDef as any); + const identifiers = extractIdentifiers(identifierDef) as SvelteIdentifier[]; templateScope.addMany(identifiers, owner); slotHandler.resolveDestructuringAssignment( diff --git a/packages/svelte2tsx/src/utils/svelteAst.ts b/packages/svelte2tsx/src/utils/svelteAst.ts index 5ab340f85..22d0087fe 100644 --- a/packages/svelte2tsx/src/utils/svelteAst.ts +++ b/packages/svelte2tsx/src/utils/svelteAst.ts @@ -1,5 +1,9 @@ import { Node } from 'estree-walker'; -import { Identifier, BaseNode, ArrayPattern, ObjectPattern, IdentifierWithRange } from '../interfaces'; +import { + SvelteIdentifier, + SvelteArrayPattern, + SvelteObjectPattern, +} from '../interfaces'; export function isMember(parent: Node, prop: string) { return parent.type == 'MemberExpression' && prop == 'property'; @@ -15,8 +19,9 @@ export function isObjectValue(parent: Node, prop: string) { export function isObjectValueShortHand(property: Node) { const { value, key } = property; - return value && isIdentifierWithRange(value) - && key.start === value.start && key.end == value.end; + return ( + value && isIdentifier(value) && key.start === value.start && key.end == value.end + ); } export function isText(node: Node) { @@ -27,18 +32,16 @@ export function attributeValueIsString(attr: Node) { return attr.value.length !== 1 || attr.value[0]?.type === 'Text'; } -export function isDestructuringPatterns(node: BaseNode): node is ArrayPattern | ObjectPattern { +export function isDestructuringPatterns( + node: Node, +): node is SvelteArrayPattern | SvelteObjectPattern { return node.type === 'ArrayPattern' || node.type === 'ObjectPattern'; } -export function isIdentifier(node: any): node is Identifier { +export function isIdentifier(node: Node): node is SvelteIdentifier { return node.type === 'Identifier'; } -export function isIdentifierWithRange(node: any): node is IdentifierWithRange { - return node.type === 'Identifier' && 'start' in node && 'end' in node; -} - export function getSlotName(child: Node): string | undefined { const slot = (child.attributes as Node[])?.find((a) => a.name == 'slot'); From 7aa64f60cc15636a58ecb2ee0ba9a64acb30d8ba Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Mon, 24 Aug 2020 17:17:19 +0800 Subject: [PATCH 20/21] (refactor) move handle scope and resolve to new file --- .../src/nodes/handleScopeAndResolveForSlot.ts | 84 +++++++++++++++++++ packages/svelte2tsx/src/svelte2tsx.ts | 78 +++++------------ 2 files changed, 107 insertions(+), 55 deletions(-) create mode 100644 packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts diff --git a/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts b/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts new file mode 100644 index 000000000..56de11349 --- /dev/null +++ b/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts @@ -0,0 +1,84 @@ +import { Node } from 'estree-walker'; +import { BaseDirective, SvelteIdentifier } from '../interfaces'; +import TemplateScope from './TemplateScope'; +import { SlotHandler } from './slot'; +import { isIdentifier, isDestructuringPatterns } from '../utils/svelteAst'; +import { extract_identifiers as extractIdentifiers } from 'periscopic'; + +export function handleScopeAndResolveForSlot({ + identifierDef, + initExpression, + owner, + slotHandler, + templateScope, +}: { + identifierDef: Node; + initExpression: Node; + owner: Node; + slotHandler: SlotHandler; + templateScope: TemplateScope; +}) { + if (isIdentifier(identifierDef)) { + templateScope.add(identifierDef, owner); + + slotHandler.resolve(identifierDef, initExpression, templateScope); + } + if (isDestructuringPatterns(identifierDef)) { + const identifiers = extractIdentifiers(identifierDef) as SvelteIdentifier[]; + templateScope.addMany(identifiers, owner); + + slotHandler.resolveDestructuringAssignment( + identifierDef, + identifiers, + initExpression, + templateScope, + ); + } +} + +export function handleScopeAndResolveLetVarForSlotFor({ + letNode, + component, + slotName, + templateScope, + slotHandler, +}: { + letNode: BaseDirective; + slotName: string; + component: Node; + templateScope: TemplateScope; + slotHandler: SlotHandler; +}) { + const { expression } = letNode; + // + if (!expression) { + templateScope.add(letNode, component); + slotHandler.resolveLet(letNode, letNode, component, slotName); + } else { + if (isIdentifier(expression)) { + templateScope.add(expression, component); + slotHandler.resolveLet(letNode, expression, component, slotName); + } + const expForExtract = { ...expression }; + + // https://github.com/sveltejs/svelte/blob/3a37de364bfbe75202d8e9fcef9e76b9ce6faaa2/src/compiler/compile/nodes/Let.ts#L37 + if (expression.type === 'ArrayExpression') { + expForExtract.type = 'ArrayPattern'; + } else if (expression.type === 'ObjectExpression') { + expForExtract.type = 'ObjectPattern'; + } + if (isDestructuringPatterns(expForExtract)) { + // the node object is returned as-it no mutation + const identifiers = extractIdentifiers(expForExtract) as SvelteIdentifier[]; + templateScope.addMany(identifiers, component); + + slotHandler.resolveDestructuringAssignmentForLet( + expForExtract, + identifiers, + letNode, + component, + slotName, + ); + } + } +} diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index db1ad62dc..ee00f5e1b 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -6,14 +6,12 @@ import { parseHtmlx } from './htmlxparser'; import { convertHtmlxToJsx } from './htmlxtojsx'; import { Node } from 'estree-walker'; import * as ts from 'typescript'; -import { extract_identifiers as extractIdentifiers } from 'periscopic'; import { findExportKeyword, getBinaryAssignmentExpr } from './utils/tsAst'; import { EventHandler } from './nodes/event-handler'; import { InstanceScriptProcessResult, CreateRenderFunctionPara, AddComponentExportPara, - SvelteIdentifier, } from './interfaces'; import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters'; import { ExportedNames } from './nodes/ExportedNames'; @@ -26,6 +24,10 @@ import { ComponentEventsFromInterface, ComponentEventsFromEventsMap, } from './nodes/ComponentEvents'; +import { + handleScopeAndResolveLetVarForSlotFor as handleScopeAndResolveLetVarForSlot, + handleScopeAndResolveForSlot +} from './nodes/handleScopeAndResolveForSlot'; type TemplateProcessResult = { uses$$props: boolean; @@ -275,17 +277,17 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { templateScope = templateScope.child(); if (node.context) { - handleScopeAndResolveForSlot(node.context, node.expression, node); + handleScopeAndResolveForSlotInner(node.context, node.expression, node); } }; const handleAwait = (node: Node) => { templateScope = templateScope.child(); if (node.value) { - handleScopeAndResolveForSlot(node.value, node.expression, node.then); + handleScopeAndResolveForSlotInner(node.value, node.expression, node.then); } if (node.error) { - handleScopeAndResolveForSlot(node.error, node.expression, node.catch); + handleScopeAndResolveForSlotInner(node.error, node.expression, node.catch); } }; @@ -294,64 +296,30 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { const lets = slotHandler.getSlotConsumerOfComponent(component); for (const { letNode, slotName } of lets) { - const { expression } = letNode; - // - if (!expression) { - templateScope.add(letNode, component); - slotHandler.resolveLet(letNode, letNode, component, slotName); - } else { - if (astUtil.isIdentifier(expression)) { - templateScope.add(expression, component); - slotHandler.resolveLet(letNode, expression, component, slotName); - } - const expForExtract = {...expression}; - - // https://github.com/sveltejs/svelte/blob/3a37de364bfbe75202d8e9fcef9e76b9ce6faaa2/src/compiler/compile/nodes/Let.ts#L37 - if (expression.type === 'ArrayExpression') { - expForExtract.type = 'ArrayPattern'; - } - if (expression.type === 'ObjectExpression') { - expForExtract.type = 'ObjectPattern'; - } - if (astUtil.isDestructuringPatterns(expForExtract)) { - // the node object is returned as-it no mutation - const identifiers = extractIdentifiers(expForExtract) as SvelteIdentifier[]; - templateScope.addMany(identifiers, component); - - slotHandler.resolveDestructuringAssignmentForLet( - expForExtract, - identifiers, - letNode, - component, - slotName - ); - } - } + handleScopeAndResolveLetVarForSlot({ + letNode, + slotName, + slotHandler, + templateScope, + component + }); } }; - const handleScopeAndResolveForSlot = ( + const handleScopeAndResolveForSlotInner = ( identifierDef: Node, initExpression: Node, owner: Node ) => { - if (astUtil.isIdentifier(identifierDef)) { - templateScope.add(identifierDef, owner); - - slotHandler.resolve(identifierDef, initExpression, templateScope); - } - if (astUtil.isDestructuringPatterns(identifierDef)) { - const identifiers = extractIdentifiers(identifierDef) as SvelteIdentifier[]; - templateScope.addMany(identifiers, owner); - - slotHandler.resolveDestructuringAssignment( - identifierDef, - identifiers, - initExpression, - templateScope - ); - } + handleScopeAndResolveForSlot({ + identifierDef, + initExpression, + slotHandler, + templateScope, + owner, + }); }; + const eventHandler = new EventHandler(); const onHtmlxWalk = (node: Node, parent: Node, prop: string) => { From b5c4af119015fc238e7e2b77be3071d68e0d37b9 Mon Sep 17 00:00:00 2001 From: Lyu Jason Date: Tue, 25 Aug 2020 08:29:41 +0800 Subject: [PATCH 21/21] cleanup --- packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts | 4 ++-- packages/svelte2tsx/src/svelte2tsx.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts b/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts index 56de11349..a207b6a4e 100644 --- a/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts +++ b/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts @@ -24,6 +24,7 @@ export function handleScopeAndResolveForSlot({ slotHandler.resolve(identifierDef, initExpression, templateScope); } if (isDestructuringPatterns(identifierDef)) { + // the node object is returned as-it with no mutation const identifiers = extractIdentifiers(identifierDef) as SvelteIdentifier[]; templateScope.addMany(identifiers, owner); @@ -36,7 +37,7 @@ export function handleScopeAndResolveForSlot({ } } -export function handleScopeAndResolveLetVarForSlotFor({ +export function handleScopeAndResolveLetVarForSlot({ letNode, component, slotName, @@ -68,7 +69,6 @@ export function handleScopeAndResolveLetVarForSlotFor({ expForExtract.type = 'ObjectPattern'; } if (isDestructuringPatterns(expForExtract)) { - // the node object is returned as-it no mutation const identifiers = extractIdentifiers(expForExtract) as SvelteIdentifier[]; templateScope.addMany(identifiers, component); diff --git a/packages/svelte2tsx/src/svelte2tsx.ts b/packages/svelte2tsx/src/svelte2tsx.ts index ee00f5e1b..06b7e8432 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -25,7 +25,7 @@ import { ComponentEventsFromEventsMap, } from './nodes/ComponentEvents'; import { - handleScopeAndResolveLetVarForSlotFor as handleScopeAndResolveLetVarForSlot, + handleScopeAndResolveLetVarForSlot, handleScopeAndResolveForSlot } from './nodes/handleScopeAndResolveForSlot';