Skip to content

Commit fd559c5

Browse files
Slot props reference block level variable (#458)
* basic await and each * basic support for destructuring * fix TemplateScope reference code url * indent * fix typos * camelCase * test for nest * var shadowing * catch block * fix test * test for member aceess, object key and object shorthand * fix non object shorthand property * support for component let:prop * lint * leave component scope * more test about scope * TemplateScope optional chaining Co-authored-by: Simon H <[email protected]> * null check for resolving and getOwner * 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 * (refactor) move handle scope and resolve to new file * cleanup Co-authored-by: Simon H <[email protected]>
1 parent afcd008 commit fd559c5

File tree

25 files changed

+712
-55
lines changed

25 files changed

+712
-55
lines changed

packages/svelte2tsx/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@types/vfile": "^3.0.2",
2424
"magic-string": "^0.25.4",
2525
"mocha": "^6.2.2",
26+
"periscopic": "^2.0.2",
2627
"rollup": "^1.12.0",
2728
"rollup-plugin-commonjs": "^10.0.0",
2829
"rollup-plugin-delete": "^1.1.0",

packages/svelte2tsx/src/htmlxtojsx/index.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { parseHtmlx } from '../htmlxparser';
55
import svgAttributes from './svgattributes';
66
import { getTypeForComponent } from './nodes/component-type';
77
import { handleAwait } from './nodes/await-block';
8+
import { getSlotName } from '../utils/svelteAst';
89

910
type ElementType = string;
1011
const oneWayBindingAttributes: Map<string, ElementType> = new Map(
@@ -344,12 +345,9 @@ export function convertHtmlxToJsx(
344345
//we could lean on leave/enter, but I am lazy
345346
if (!el.children) return;
346347
for (const child of el.children) {
347-
if (!child.attributes) continue;
348-
const slot = child.attributes.find((a) => a.name == 'slot');
349-
if (slot) {
350-
if (slot.value && slot.value.length) {
351-
handleSlot(child, el.name, slot.value[0].raw);
352-
}
348+
const slotName = getSlotName(child);
349+
if (slotName) {
350+
handleSlot(child, el.name, slotName);
353351
}
354352
}
355353
};

packages/svelte2tsx/src/interfaces.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import MagicString from 'magic-string';
22
import { Node } from 'estree-walker';
3+
import { ArrayPattern, ObjectPattern, Identifier } from 'estree';
34
import { ExportedNames } from './nodes/ExportedNames';
45
import { ComponentEvents } from './nodes/ComponentEvents';
56

@@ -21,6 +22,39 @@ export interface CreateRenderFunctionPara extends InstanceScriptProcessResult {
2122
isTsFile: boolean;
2223
}
2324

25+
export interface NodeRange {
26+
start: number;
27+
end: number;
28+
}
29+
30+
export interface SvelteIdentifier extends Identifier, NodeRange {}
31+
32+
export interface SvelteArrayPattern extends ArrayPattern, NodeRange {}
33+
34+
export interface SvelteObjectPattern extends ObjectPattern, NodeRange {}
35+
36+
export interface WithName {
37+
type: string;
38+
name: string;
39+
}
40+
41+
export type DirectiveType =
42+
| 'Action'
43+
| 'Animation'
44+
| 'Binding'
45+
| 'Class'
46+
| 'EventHandler'
47+
| 'Let'
48+
| 'Ref'
49+
| 'Transition';
50+
51+
export interface BaseDirective extends Node {
52+
type: DirectiveType;
53+
expression: null | Node;
54+
name: string;
55+
modifiers: string[];
56+
}
57+
2458
export interface AddComponentExportPara {
2559
str: MagicString;
2660
uses$$propsOr$$restProps: boolean;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Node } from 'estree-walker';
2+
import { WithName } from '../interfaces';
3+
4+
/**
5+
* adopted from https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/nodes/shared/TemplateScope.ts
6+
*/
7+
export default class TemplateScope {
8+
names: Set<string>;
9+
owners: Map<string, Node> = new Map();
10+
inits: Map<string, WithName> = new Map();
11+
parent?: TemplateScope;
12+
13+
constructor(parent?: TemplateScope) {
14+
this.parent = parent;
15+
this.names = new Set(parent ? parent.names : []);
16+
}
17+
18+
addMany(inits: WithName[], owner: Node) {
19+
inits.forEach((item) => this.add(item, owner));
20+
return this;
21+
}
22+
23+
add(init: WithName, owner: Node) {
24+
const { name } = init;
25+
this.names.add(name);
26+
this.inits.set(name, init);
27+
this.owners.set(name, owner);
28+
return this;
29+
}
30+
31+
child() {
32+
const child = new TemplateScope(this);
33+
return child;
34+
}
35+
36+
getOwner(name: string): Node {
37+
return this.owners.get(name) || this.parent?.getOwner(name);
38+
}
39+
40+
getInit(name: string): WithName {
41+
return this.inits.get(name) || this.parent?.getInit(name);
42+
}
43+
44+
isLet(name: string) {
45+
const owner = this.getOwner(name);
46+
return owner && (owner.type === 'Element' || owner.type === 'InlineComponent');
47+
}
48+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { Node } from 'estree-walker';
2+
import { BaseDirective, SvelteIdentifier } from '../interfaces';
3+
import TemplateScope from './TemplateScope';
4+
import { SlotHandler } from './slot';
5+
import { isIdentifier, isDestructuringPatterns } from '../utils/svelteAst';
6+
import { extract_identifiers as extractIdentifiers } from 'periscopic';
7+
8+
export function handleScopeAndResolveForSlot({
9+
identifierDef,
10+
initExpression,
11+
owner,
12+
slotHandler,
13+
templateScope,
14+
}: {
15+
identifierDef: Node;
16+
initExpression: Node;
17+
owner: Node;
18+
slotHandler: SlotHandler;
19+
templateScope: TemplateScope;
20+
}) {
21+
if (isIdentifier(identifierDef)) {
22+
templateScope.add(identifierDef, owner);
23+
24+
slotHandler.resolve(identifierDef, initExpression, templateScope);
25+
}
26+
if (isDestructuringPatterns(identifierDef)) {
27+
// the node object is returned as-it with no mutation
28+
const identifiers = extractIdentifiers(identifierDef) as SvelteIdentifier[];
29+
templateScope.addMany(identifiers, owner);
30+
31+
slotHandler.resolveDestructuringAssignment(
32+
identifierDef,
33+
identifiers,
34+
initExpression,
35+
templateScope,
36+
);
37+
}
38+
}
39+
40+
export function handleScopeAndResolveLetVarForSlot({
41+
letNode,
42+
component,
43+
slotName,
44+
templateScope,
45+
slotHandler,
46+
}: {
47+
letNode: BaseDirective;
48+
slotName: string;
49+
component: Node;
50+
templateScope: TemplateScope;
51+
slotHandler: SlotHandler;
52+
}) {
53+
const { expression } = letNode;
54+
// <Component let:a>
55+
if (!expression) {
56+
templateScope.add(letNode, component);
57+
slotHandler.resolveLet(letNode, letNode, component, slotName);
58+
} else {
59+
if (isIdentifier(expression)) {
60+
templateScope.add(expression, component);
61+
slotHandler.resolveLet(letNode, expression, component, slotName);
62+
}
63+
const expForExtract = { ...expression };
64+
65+
// https://github.com/sveltejs/svelte/blob/3a37de364bfbe75202d8e9fcef9e76b9ce6faaa2/src/compiler/compile/nodes/Let.ts#L37
66+
if (expression.type === 'ArrayExpression') {
67+
expForExtract.type = 'ArrayPattern';
68+
} else if (expression.type === 'ObjectExpression') {
69+
expForExtract.type = 'ObjectPattern';
70+
}
71+
if (isDestructuringPatterns(expForExtract)) {
72+
const identifiers = extractIdentifiers(expForExtract) as SvelteIdentifier[];
73+
templateScope.addMany(identifiers, component);
74+
75+
slotHandler.resolveDestructuringAssignmentForLet(
76+
expForExtract,
77+
identifiers,
78+
letNode,
79+
component,
80+
slotName,
81+
);
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)