Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@ import * as path from 'path';
import ts from 'typescript';
import { Document, DocumentManager } from '../../../../src/lib/documents';
import { LSConfigManager } from '../../../../src/ls-config';
import { TypeScriptPlugin } from '../../../../src/plugins';
import { DiagnosticsProviderImpl } from '../../../../src/plugins/typescript/features/DiagnosticsProvider';
import { LSAndTSDocResolver } from '../../../../src/plugins/typescript/LSAndTSDocResolver';
import { pathToUrl } from '../../../../src/utils';

const testDir = path.join(__dirname, '..', 'testfiles');

describe('DiagnosticsProvider', () => {
function setup(filename: string) {
const docManager = new DocumentManager(() => document);
const testDir = path.join(__dirname, '..');
const filePath = path.join(testDir, 'testfiles', filename);
const document = new Document(pathToUrl(filePath), ts.sys.readFile(filePath)!);
const pluginManager = new LSConfigManager();
const plugin = new TypeScriptPlugin(docManager, pluginManager, [pathToUrl(testDir)]);
docManager.openDocument(<any>'some doc');
return { plugin, document };
const docManager = new DocumentManager(
(textDocument) => new Document(textDocument.uri, textDocument.text)
);
const lsAndTsDocResolver = new LSAndTSDocResolver(
docManager,
[pathToUrl(testDir)],
new LSConfigManager()
);
const plugin = new DiagnosticsProviderImpl(lsAndTsDocResolver);
const filePath = path.join(testDir, filename);
const document = docManager.openDocument(<any>{
uri: pathToUrl(filePath),
text: ts.sys.readFile(filePath) || ''
});
return { plugin, document, docManager };
}

it('provides diagnostics', async () => {
Expand Down Expand Up @@ -311,4 +321,82 @@ describe('DiagnosticsProvider', () => {
}
]);
});

it('type-checks slots', async () => {
const { plugin, document } = setup('diagnostics-slots.svelte');
const diagnostics = await plugin.getDiagnostics(document);

assert.deepStrictEqual(diagnostics, [
{
code: 2304,
message: "Cannot find name 'defaultSlotProp'.",
range: {
end: {
character: 48,
line: 4
},
start: {
character: 33,
line: 4
}
},
severity: 1,
source: 'ts',
tags: []
},
{
code: 2367,
message:
"This condition will always return 'false' since the types 'number' and 'boolean' have no overlap.",
range: {
end: {
character: 28,
line: 6
},
start: {
character: 3,
line: 6
}
},
severity: 1,
source: 'ts',
tags: []
},
{
code: 2367,
message:
"This condition will always return 'false' since the types 'boolean' and 'number' have no overlap.",
range: {
end: {
character: 24,
line: 8
},
start: {
character: 5,
line: 8
}
},
severity: 1,
source: 'ts',
tags: []
},
{
code: 2304,
message: "Cannot find name 'namedSlotProp'.",
range: {
end: {
character: 16,
line: 12
},
start: {
character: 3,
line: 12
}
},
severity: 1,
source: 'ts',
tags: []
}
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script lang="ts">
export let prop: any;
const defaultSlotProp = 1;
const namedSlotProp = true;
</script>

{prop}
<slot {defaultSlotProp} />
<slot name="named" {namedSlotProp} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import Slots from './diagnostics-slots-imported.svelte';
</script>

<Slots let:defaultSlotProp prop={defaultSlotProp}>
{defaultSlotProp === 1}
{defaultSlotProp === false}
<p slot="named" let:namedSlotProp class:namedSlotProp>
{namedSlotProp === 1}
{namedSlotProp === false}
{defaultSlotProp}
</p>
{namedSlotProp}
</Slots>
22 changes: 14 additions & 8 deletions packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,26 @@ function handleSlot(
slotName: string
): void {
//collect "let" definitions
const slotElIsComponent = slotEl === component;
let hasMoved = false;
let afterTag: number;
let slotDefInsertionPoint: number;
for (const attr of slotEl.attributes) {
if (attr.type != 'Let') {
continue;
}

if (slotEl.children.length == 0) {
if (slotElIsComponent && slotEl.children.length == 0) {
//no children anyway, just wipe out the attribute
str.remove(attr.start, attr.end);
continue;
}

afterTag = afterTag || htmlx.lastIndexOf('>', slotEl.children[0].start) + 1;
slotDefInsertionPoint =
slotDefInsertionPoint || slotElIsComponent
? htmlx.lastIndexOf('>', slotEl.children[0].start) + 1
: slotEl.start;

str.move(attr.start, attr.end, afterTag);
str.move(attr.start, attr.end, slotDefInsertionPoint);

//remove let:
if (hasMoved) {
Expand All @@ -77,9 +81,11 @@ function handleSlot(
if (!hasMoved) {
return;
}
str.appendLeft(afterTag, '{() => { let {');
str.appendRight(afterTag, `} = ${getSingleSlotDef(component, slotName)}` + ';<>');
str.appendLeft(slotDefInsertionPoint, '{() => { let {');
str.appendRight(slotDefInsertionPoint, `} = ${getSingleSlotDef(component, slotName)}` + ';<>');

const closeTagStart = htmlx.lastIndexOf('<', slotEl.end - 1);
str.appendLeft(closeTagStart, '</>}}');
const closeSlotDefInsertionPoint = slotElIsComponent
? htmlx.lastIndexOf('<', slotEl.end - 1)
: slotEl.end;
str.appendLeft(closeSlotDefInsertionPoint, '</>}}');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<><Component ></Component></>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Component let:var={new_var} let:other_var></Component>
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<><Component >{() => { let {var:new_var} = __sveltets_instanceOf(Component).$$slot_def['default'];<>
<h1>Hello</h1>
<div >{() => { let {slotvar:newvar} = __sveltets_instanceOf(Component).$$slot_def['someslot'];<>
<h2>Hi Slot</h2>
</>}}</div>
<h1>Hello {new_var}</h1>
{() => { let {slotvar:newvar} = __sveltets_instanceOf(Component).$$slot_def['someslot'];<><div {...__sveltets_ensureType(Boolean, !!(newvar))}>
<h2>Hi Slot {newvar}</h2>
</div></>}}
{() => { let {newvar2} = __sveltets_instanceOf(Component).$$slot_def['slotwithoutchildren'];<><div {...__sveltets_ensureType(Boolean, !!(newvar2))}></div></>}}
<p >
Test
</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Component let:var={new_var}>
<h1>Hello</h1>
<div slot="someslot" let:slotvar={newvar}>
<h2>Hi Slot</h2>
<h1>Hello {new_var}</h1>
<div slot="someslot" let:slotvar={newvar} class:newvar>
<h2>Hi Slot {newvar}</h2>
</div>
<div slot="slotwithoutchildren" let:newvar2 class:newvar2></div>
<p slot=desc>
Test
</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
///<reference types="svelte" />
<></>;function render() {
<><Component>
<div >{() => { let {a} = __sveltets_instanceOf(Component).$$slot_def['b'];<>
{() => { let {a} = __sveltets_instanceOf(Component).$$slot_def['b'];<><div >
<slot a={a}></slot>
</>}}</div>
</div></>}}
</Component></>
return { props: {}, slots: {'default': {a:__sveltets_instanceOf(Component).$$slot_def['b'].a}}, getters: {}, events: {} }}

Expand Down