Skip to content

Commit a56d82e

Browse files
authored
(fix) named slot element can reference let'ed variable (#828)
#817
1 parent f5b68f6 commit a56d82e

File tree

9 files changed

+147
-26
lines changed

9 files changed

+147
-26
lines changed

packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,29 @@ import * as path from 'path';
33
import ts from 'typescript';
44
import { Document, DocumentManager } from '../../../../src/lib/documents';
55
import { LSConfigManager } from '../../../../src/ls-config';
6-
import { TypeScriptPlugin } from '../../../../src/plugins';
6+
import { DiagnosticsProviderImpl } from '../../../../src/plugins/typescript/features/DiagnosticsProvider';
7+
import { LSAndTSDocResolver } from '../../../../src/plugins/typescript/LSAndTSDocResolver';
78
import { pathToUrl } from '../../../../src/utils';
89

10+
const testDir = path.join(__dirname, '..', 'testfiles');
11+
912
describe('DiagnosticsProvider', () => {
1013
function setup(filename: string) {
11-
const docManager = new DocumentManager(() => document);
12-
const testDir = path.join(__dirname, '..');
13-
const filePath = path.join(testDir, 'testfiles', filename);
14-
const document = new Document(pathToUrl(filePath), ts.sys.readFile(filePath)!);
15-
const pluginManager = new LSConfigManager();
16-
const plugin = new TypeScriptPlugin(docManager, pluginManager, [pathToUrl(testDir)]);
17-
docManager.openDocument(<any>'some doc');
18-
return { plugin, document };
14+
const docManager = new DocumentManager(
15+
(textDocument) => new Document(textDocument.uri, textDocument.text)
16+
);
17+
const lsAndTsDocResolver = new LSAndTSDocResolver(
18+
docManager,
19+
[pathToUrl(testDir)],
20+
new LSConfigManager()
21+
);
22+
const plugin = new DiagnosticsProviderImpl(lsAndTsDocResolver);
23+
const filePath = path.join(testDir, filename);
24+
const document = docManager.openDocument(<any>{
25+
uri: pathToUrl(filePath),
26+
text: ts.sys.readFile(filePath) || ''
27+
});
28+
return { plugin, document, docManager };
1929
}
2030

2131
it('provides diagnostics', async () => {
@@ -311,4 +321,82 @@ describe('DiagnosticsProvider', () => {
311321
}
312322
]);
313323
});
324+
325+
it('type-checks slots', async () => {
326+
const { plugin, document } = setup('diagnostics-slots.svelte');
327+
const diagnostics = await plugin.getDiagnostics(document);
328+
329+
assert.deepStrictEqual(diagnostics, [
330+
{
331+
code: 2304,
332+
message: "Cannot find name 'defaultSlotProp'.",
333+
range: {
334+
end: {
335+
character: 48,
336+
line: 4
337+
},
338+
start: {
339+
character: 33,
340+
line: 4
341+
}
342+
},
343+
severity: 1,
344+
source: 'ts',
345+
tags: []
346+
},
347+
{
348+
code: 2367,
349+
message:
350+
"This condition will always return 'false' since the types 'number' and 'boolean' have no overlap.",
351+
range: {
352+
end: {
353+
character: 28,
354+
line: 6
355+
},
356+
start: {
357+
character: 3,
358+
line: 6
359+
}
360+
},
361+
severity: 1,
362+
source: 'ts',
363+
tags: []
364+
},
365+
{
366+
code: 2367,
367+
message:
368+
"This condition will always return 'false' since the types 'boolean' and 'number' have no overlap.",
369+
range: {
370+
end: {
371+
character: 24,
372+
line: 8
373+
},
374+
start: {
375+
character: 5,
376+
line: 8
377+
}
378+
},
379+
severity: 1,
380+
source: 'ts',
381+
tags: []
382+
},
383+
{
384+
code: 2304,
385+
message: "Cannot find name 'namedSlotProp'.",
386+
range: {
387+
end: {
388+
character: 16,
389+
line: 12
390+
},
391+
start: {
392+
character: 3,
393+
line: 12
394+
}
395+
},
396+
severity: 1,
397+
source: 'ts',
398+
tags: []
399+
}
400+
]);
401+
});
314402
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script lang="ts">
2+
export let prop: any;
3+
const defaultSlotProp = 1;
4+
const namedSlotProp = true;
5+
</script>
6+
7+
{prop}
8+
<slot {defaultSlotProp} />
9+
<slot name="named" {namedSlotProp} />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script lang="ts">
2+
import Slots from './diagnostics-slots-imported.svelte';
3+
</script>
4+
5+
<Slots let:defaultSlotProp prop={defaultSlotProp}>
6+
{defaultSlotProp === 1}
7+
{defaultSlotProp === false}
8+
<p slot="named" let:namedSlotProp class:namedSlotProp>
9+
{namedSlotProp === 1}
10+
{namedSlotProp === false}
11+
{defaultSlotProp}
12+
</p>
13+
{namedSlotProp}
14+
</Slots>

packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,26 @@ function handleSlot(
4242
slotName: string
4343
): void {
4444
//collect "let" definitions
45+
const slotElIsComponent = slotEl === component;
4546
let hasMoved = false;
46-
let afterTag: number;
47+
let slotDefInsertionPoint: number;
4748
for (const attr of slotEl.attributes) {
4849
if (attr.type != 'Let') {
4950
continue;
5051
}
5152

52-
if (slotEl.children.length == 0) {
53+
if (slotElIsComponent && slotEl.children.length == 0) {
5354
//no children anyway, just wipe out the attribute
5455
str.remove(attr.start, attr.end);
5556
continue;
5657
}
5758

58-
afterTag = afterTag || htmlx.lastIndexOf('>', slotEl.children[0].start) + 1;
59+
slotDefInsertionPoint =
60+
slotDefInsertionPoint || slotElIsComponent
61+
? htmlx.lastIndexOf('>', slotEl.children[0].start) + 1
62+
: slotEl.start;
5963

60-
str.move(attr.start, attr.end, afterTag);
64+
str.move(attr.start, attr.end, slotDefInsertionPoint);
6165

6266
//remove let:
6367
if (hasMoved) {
@@ -77,9 +81,11 @@ function handleSlot(
7781
if (!hasMoved) {
7882
return;
7983
}
80-
str.appendLeft(afterTag, '{() => { let {');
81-
str.appendRight(afterTag, `} = ${getSingleSlotDef(component, slotName)}` + ';<>');
84+
str.appendLeft(slotDefInsertionPoint, '{() => { let {');
85+
str.appendRight(slotDefInsertionPoint, `} = ${getSingleSlotDef(component, slotName)}` + ';<>');
8286

83-
const closeTagStart = htmlx.lastIndexOf('<', slotEl.end - 1);
84-
str.appendLeft(closeTagStart, '</>}}');
87+
const closeSlotDefInsertionPoint = slotElIsComponent
88+
? htmlx.lastIndexOf('<', slotEl.end - 1)
89+
: slotEl.end;
90+
str.appendLeft(closeSlotDefInsertionPoint, '</>}}');
8591
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<><Component ></Component></>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Component let:var={new_var} let:other_var></Component>

packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/expected.jsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<><Component >{() => { let {var:new_var} = __sveltets_instanceOf(Component).$$slot_def['default'];<>
2-
<h1>Hello</h1>
3-
<div >{() => { let {slotvar:newvar} = __sveltets_instanceOf(Component).$$slot_def['someslot'];<>
4-
<h2>Hi Slot</h2>
5-
</>}}</div>
2+
<h1>Hello {new_var}</h1>
3+
{() => { let {slotvar:newvar} = __sveltets_instanceOf(Component).$$slot_def['someslot'];<><div {...__sveltets_ensureType(Boolean, !!(newvar))}>
4+
<h2>Hi Slot {newvar}</h2>
5+
</div></>}}
6+
{() => { let {newvar2} = __sveltets_instanceOf(Component).$$slot_def['slotwithoutchildren'];<><div {...__sveltets_ensureType(Boolean, !!(newvar2))}></div></>}}
67
<p >
78
Test
89
</p>

packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/input.svelte

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<Component let:var={new_var}>
2-
<h1>Hello</h1>
3-
<div slot="someslot" let:slotvar={newvar}>
4-
<h2>Hi Slot</h2>
2+
<h1>Hello {new_var}</h1>
3+
<div slot="someslot" let:slotvar={newvar} class:newvar>
4+
<h2>Hi Slot {newvar}</h2>
55
</div>
6+
<div slot="slotwithoutchildren" let:newvar2 class:newvar2></div>
67
<p slot=desc>
78
Test
89
</p>

packages/svelte2tsx/test/svelte2tsx/samples/component-slot-let-forward-named-slot/expected.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
///<reference types="svelte" />
22
<></>;function render() {
33
<><Component>
4-
<div >{() => { let {a} = __sveltets_instanceOf(Component).$$slot_def['b'];<>
4+
{() => { let {a} = __sveltets_instanceOf(Component).$$slot_def['b'];<><div >
55
<slot a={a}></slot>
6-
</>}}</div>
6+
</div></>}}
77
</Component></>
88
return { props: {}, slots: {'default': {a:__sveltets_instanceOf(Component).$$slot_def['b'].a}}, getters: {}, events: {} }}
99

0 commit comments

Comments
 (0)