diff --git a/packages/svelte2tsx/package.json b/packages/svelte2tsx/package.json index 456e1c9b7..6ab3723a6 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/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 d4a9d520d..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,6 +22,39 @@ export interface CreateRenderFunctionPara extends InstanceScriptProcessResult { isTsFile: boolean; } +export interface NodeRange { + start: number; + end: number; +} + +export interface SvelteIdentifier extends Identifier, NodeRange {} + +export interface SvelteArrayPattern extends ArrayPattern, NodeRange {} + +export interface SvelteObjectPattern extends ObjectPattern, NodeRange {} + +export interface WithName { + type: string; + name: string; +} + +export type DirectiveType = + | 'Action' + | 'Animation' + | 'Binding' + | 'Class' + | 'EventHandler' + | 'Let' + | 'Ref' + | 'Transition'; + +export interface BaseDirective extends Node { + type: DirectiveType; + expression: null | Node; + name: string; + modifiers: string[]; +} + 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 new file mode 100644 index 000000000..a6d8c9ac4 --- /dev/null +++ b/packages/svelte2tsx/src/nodes/TemplateScope.ts @@ -0,0 +1,48 @@ +import { Node } from 'estree-walker'; +import { WithName } from '../interfaces'; + +/** + * adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/shared/TemplateScope.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 : []); + } + + addMany(inits: WithName[], owner: Node) { + inits.forEach((item) => this.add(item, owner)); + return this; + } + + add(init: WithName, 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?.getOwner(name); + } + + getInit(name: string): WithName { + 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/handleScopeAndResolveForSlot.ts b/packages/svelte2tsx/src/nodes/handleScopeAndResolveForSlot.ts new file mode 100644 index 000000000..a207b6a4e --- /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)) { + // the node object is returned as-it with no mutation + const identifiers = extractIdentifiers(identifierDef) as SvelteIdentifier[]; + templateScope.addMany(identifiers, owner); + + slotHandler.resolveDestructuringAssignment( + identifierDef, + identifiers, + initExpression, + templateScope, + ); + } +} + +export function handleScopeAndResolveLetVarForSlot({ + 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)) { + const identifiers = extractIdentifiers(expForExtract) as SvelteIdentifier[]; + templateScope.addMany(identifiers, component); + + slotHandler.resolveDestructuringAssignmentForLet( + expForExtract, + identifiers, + letNode, + component, + slotName, + ); + } + } +} diff --git a/packages/svelte2tsx/src/nodes/slot.ts b/packages/svelte2tsx/src/nodes/slot.ts new file mode 100644 index 000000000..470e336e6 --- /dev/null +++ b/packages/svelte2tsx/src/nodes/slot.ts @@ -0,0 +1,261 @@ +import { Node, walk } from 'estree-walker'; +import MagicString from 'magic-string'; +import { + attributeValueIsString, + isMember, + isObjectKey, + isObjectValueShortHand, + isObjectValue, + getSlotName, +} from '../utils/svelteAst'; +import TemplateScope from './TemplateScope'; +import { SvelteIdentifier, WithName, BaseDirective } from '../interfaces'; + +function attributeStrValueAsJsExpression(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 htmlx: string) {} + + slots = new Map>(); + resolved = new Map(); + resolvedExpression = new Map(); + + resolve(identifierDef: SvelteIdentifier, initExpression: Node, scope: TemplateScope) { + let resolved = this.resolved.get(identifierDef); + if (resolved) { + return resolved; + } + + resolved = this.getResolveExpressionStr(identifierDef, scope, initExpression); + if (resolved) { + this.resolved.set(identifierDef, resolved); + } + + return resolved; + } + + private getResolveExpressionStr( + identifierDef: SvelteIdentifier, + scope: TemplateScope, + initExpression: Node, + ) { + const { name } = identifierDef; + + const owner = scope.getOwner(name); + + 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.parent); + + return `__sveltets_unwrapPromiseLike(${resolvedExpression})`; + } else if (owner?.type === 'EachBlock') { + const resolvedExpression = this.resolveExpression(initExpression, scope.parent); + + return `__sveltets_unwrapArr(${resolvedExpression})`; + } + return null; + } + + resolveDestructuringAssignment( + destructuringNode: Node, + identifiers: SvelteIdentifier[], + initExpression: Node, + scope: TemplateScope, + ) { + const destructuring = this.htmlx.slice(destructuringNode.start, destructuringNode.end); + identifiers.forEach((identifier) => { + const resolved = this.getResolveExpressionStr(identifier, scope, initExpression); + if (resolved) { + this.resolved.set( + identifier, + `((${destructuring}) => ${identifier.name})(${resolved})`, + ); + } + }); + } + + resolveDestructuringAssignmentForLet( + destructuringNode: Node, + identifiers: SvelteIdentifier[], + letNode: BaseDirective, + 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: BaseDirective, + component: Node, + slotName: string, + ) { + const componentTypeStr = `__sveltets_instanceOf(${component.name})`; + + return `${componentTypeStr}.$$slot_def.${slotName}.${letNode.name}`; + } + + resolveLet(letNode: BaseDirective, 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 BaseDirective[]; + + return letNodes?.map((letNode) => ({ + letNode, + slotName, + })); + } + + private resolveExpression(expression: Node, scope: TemplateScope) { + let resolved = this.resolvedExpression.get(expression); + if (resolved) { + return resolved; + } + + const strForExpression = new MagicString(this.htmlx); + + const identifiers: Node[] = []; + const objectShortHands: Node[] = []; + walk(expression, { + enter(node, 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)) { + 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 value = getOverwrite(name); + strForExpression.overwrite(start, end, value); + } + + resolved = strForExpression.slice(expression.start, expression.end); + this.resolvedExpression.set(expression, resolved); + + return resolved; + } + + 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(attr)); + continue; + } + attributes.set(attr.name, this.resolveAttr(attr, scope)); + } + this.slots.set(slotName, attributes); + } + + getSlotDef() { + return this.slots; + } + + resolveAttr(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.resolved.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 83757768e..06b7e8432 100644 --- a/packages/svelte2tsx/src/svelte2tsx.ts +++ b/packages/svelte2tsx/src/svelte2tsx.ts @@ -6,8 +6,8 @@ import { parseHtmlx } from './htmlxparser'; import { convertHtmlxToJsx } from './htmlxtojsx'; import { Node } from 'estree-walker'; import * as ts from 'typescript'; +import { findExportKeyword, getBinaryAssignmentExpr } from './utils/tsAst'; import { EventHandler } from './nodes/event-handler'; -import { findExortKeyword, getBinaryAssignmentExpr } from './utils/tsAst'; import { InstanceScriptProcessResult, CreateRenderFunctionPara, @@ -15,38 +15,19 @@ import { } 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 { ImplicitTopLevelNames } from './nodes/ImplicitTopLevelNames'; import { ComponentEvents, ComponentEventsFromInterface, ComponentEventsFromEventsMap, } from './nodes/ComponentEvents'; - -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 { + handleScopeAndResolveLetVarForSlot, + handleScopeAndResolveForSlot +} from './nodes/handleScopeAndResolveForSlot'; type TemplateProcessResult = { uses$$props: boolean; @@ -234,12 +215,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; @@ -284,24 +265,61 @@ 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.original); + let templateScope = new TemplateScope(); + + const handleEach = (node: Node) => { + templateScope = templateScope.child(); + + if (node.context) { + handleScopeAndResolveForSlotInner(node.context, node.expression, node); + } + }; + + const handleAwait = (node: Node) => { + templateScope = templateScope.child(); + if (node.value) { + handleScopeAndResolveForSlotInner(node.value, node.expression, node.then); + } + if (node.error) { + handleScopeAndResolveForSlotInner(node.error, node.expression, node.catch); + } + }; + + const handleComponentLet = (component: Node) => { + templateScope = templateScope.child(); + const lets = slotHandler.getSlotConsumerOfComponent(component); + + for (const { letNode, slotName } of lets) { + handleScopeAndResolveLetVarForSlot({ + letNode, + slotName, + slotHandler, + templateScope, + component + }); + } + }; + + const handleScopeAndResolveForSlotInner = ( + identifierDef: Node, + initExpression: Node, + owner: Node + ) => { + handleScopeAndResolveForSlot({ + identifierDef, + initExpression, + slotHandler, + templateScope, + owner, + }); + }; + const eventHandler = new EventHandler(); const onHtmlxWalk = (node: Node, parent: Node, prop: string) => { @@ -323,7 +341,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { handleIdentifier(node, parent, prop); break; case 'Slot': - handleSlot(node); + slotHandler.handleSlot(node, templateScope); break; case 'Style': handleStyleTag(node); @@ -346,6 +364,15 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { case 'VariableDeclarator': isDeclaration = true; break; + case 'EachBlock': + handleEach(node); + break; + case 'AwaitBlock': + handleAwait(node); + break; + case 'InlineComponent': + handleComponentLet(node); + break; } }; @@ -360,6 +387,9 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { if (prop == 'id' && parent.type == 'VariableDeclarator') { isDeclaration = false; } + const onTemplateScopeLeave = () => { + templateScope = templateScope.parent; + }; switch (node.type) { case 'BlockStatement': @@ -371,6 +401,15 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { case 'ArrowFunctionExpression': leaveArrowFunctionExpression(); break; + case 'EachBlock': + onTemplateScopeLeave(); + break; + case 'AwaitBlock': + onTemplateScopeLeave(); + break; + case 'InlineComponent': + onTemplateScopeLeave(); + break; } }; @@ -386,7 +425,7 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult { return { moduleScriptTag, scriptTag, - slots, + slots: slotHandler.getSlotDef(), events: new ComponentEventsFromEventsMap(eventHandler), uses$$props, uses$$restProps, @@ -694,7 +733,7 @@ function processInstanceScriptContent( } 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; @@ -715,7 +754,7 @@ function processInstanceScriptContent( 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); @@ -727,7 +766,7 @@ function processInstanceScriptContent( } 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/svelteAst.ts b/packages/svelte2tsx/src/utils/svelteAst.ts new file mode 100644 index 000000000..22d0087fe --- /dev/null +++ b/packages/svelte2tsx/src/utils/svelteAst.ts @@ -0,0 +1,49 @@ +import { Node } from 'estree-walker'; +import { + SvelteIdentifier, + SvelteArrayPattern, + SvelteObjectPattern, +} from '../interfaces'; + +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 isObjectValue(parent: Node, prop: string) { + return parent.type == 'Property' && prop == 'value'; +} + +export function isObjectValueShortHand(property: Node) { + const { value, key } = property; + return ( + value && isIdentifier(value) && key.start === value.start && key.end == value.end + ); +} + +export function isText(node: Node) { + return node.type === 'Text'; +} + +export function attributeValueIsString(attr: Node) { + return attr.value.length !== 1 || attr.value[0]?.type === 'Text'; +} + +export function isDestructuringPatterns( + node: Node, +): node is SvelteArrayPattern | SvelteObjectPattern { + return node.type === 'ArrayPattern' || node.type === 'ObjectPattern'; +} + +export function isIdentifier(node: Node): node is SvelteIdentifier { + return node.type === 'Identifier'; +} + +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/src/utils/tsAst.ts b/packages/svelte2tsx/src/utils/tsAst.ts index 9f8dc2cd9..8c7d66583 100644 --- a/packages/svelte2tsx/src/utils/tsAst.ts +++ b/packages/svelte2tsx/src/utils/tsAst.ts @@ -1,6 +1,6 @@ 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); } diff --git a/packages/svelte2tsx/svelte-shims.d.ts b/packages/svelte2tsx/svelte-shims.d.ts index 2bd7180b8..54bc513d6 100644 --- a/packages/svelte2tsx/svelte-shims.d.ts +++ b/packages/svelte2tsx/svelte-shims.d.ts @@ -168,3 +168,6 @@ declare function __sveltets_each( declare function createSvelte2TsxComponent( render: () => {props?: Props, events?: Events, slots?: Slots } ): AConstructorTypeOf>; + +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..0a939fe2d --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/expected.tsx @@ -0,0 +1,14 @@ +/// +<>;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)}, err: {err:__sveltets_any({})}, second: {a:(({ b }) => b)(__sveltets_unwrapPromiseLike(promise2))}}, 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-inside-await/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte new file mode 100644 index 000000000..b052aafee --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-await/input.svelte @@ -0,0 +1,8 @@ +{#await promise then value} + Hello +{:catch err} + 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 new file mode 100644 index 000000000..9d2be705f --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/expected.tsx @@ -0,0 +1,12 @@ +/// +<>;function render() { +<>{__sveltets_each(items, (item) => <> + Hello +)} +{__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(__sveltets_with_any_event(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..008f1e50b --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-inside-each/input.svelte @@ -0,0 +1,6 @@ +{#each items as item} + Hello +{/each} +{#each items2 as { a }} + Hello +{/each} 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 @@ + + + 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..8318853bb --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/expected.tsx @@ -0,0 +1,17 @@ +/// +<>;function render() { +<>{__sveltets_each(items, (item) => <> + {__sveltets_each(item, ({ a }) => <> + Hello + )} + +)} +{() => { 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 new file mode 100644 index 000000000..d6675da23 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-nest-scope/input.svelte @@ -0,0 +1,11 @@ +{#each items as item} + {#each item as { a }} + Hello + {/each} + +{/each} +{ c } +{#await promise then d} + {d} +{/await} + 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..2c468ef81 --- /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, 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 new file mode 100644 index 000000000..61411a164 --- /dev/null +++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-object-key/input.svelte @@ -0,0 +1,3 @@ +{#each items as item} + Hello +{/each} 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..6cf4d4ed5 --- /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(__sveltets_with_any_event(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} diff --git a/yarn.lock b/yarn.lock index 8121db3c5..843dae136 100644 --- a/yarn.lock +++ b/yarn.lock @@ -836,6 +836,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" @@ -1149,6 +1154,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" @@ -1575,6 +1587,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"