From dda63dfd6ee74f3029399f273e144d1cb989c663 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 19 Feb 2021 14:18:13 +0100 Subject: [PATCH] (fix) named slot element can reference let'ed variable #817 --- .../features/DiagnosticsProvider.test.ts | 106 ++++++++++++++++-- .../diagnostics-slots-imported.svelte | 9 ++ .../testfiles/diagnostics-slots.svelte | 14 +++ .../src/htmlxtojsx/nodes/component.ts | 22 ++-- .../component-default-slot-empty/expected.jsx | 1 + .../component-default-slot-empty/input.svelte | 1 + .../samples/component-multi-slot/expected.jsx | 9 +- .../samples/component-multi-slot/input.svelte | 7 +- .../expected.tsx | 4 +- 9 files changed, 147 insertions(+), 26 deletions(-) create mode 100644 packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots-imported.svelte create mode 100644 packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots.svelte create mode 100644 packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/expected.jsx create mode 100644 packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/input.svelte diff --git a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts index a1bda880f..4a7a2027c 100644 --- a/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts +++ b/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.test.ts @@ -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('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({ + uri: pathToUrl(filePath), + text: ts.sys.readFile(filePath) || '' + }); + return { plugin, document, docManager }; } it('provides diagnostics', async () => { @@ -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: [] + } + ]); + }); }); diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots-imported.svelte b/packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots-imported.svelte new file mode 100644 index 000000000..2e7530a2c --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots-imported.svelte @@ -0,0 +1,9 @@ + + +{prop} + + diff --git a/packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots.svelte b/packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots.svelte new file mode 100644 index 000000000..dbbf396d0 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/diagnostics-slots.svelte @@ -0,0 +1,14 @@ + + + + {defaultSlotProp === 1} + {defaultSlotProp === false} +

+ {namedSlotProp === 1} + {namedSlotProp === false} + {defaultSlotProp} +

+ {namedSlotProp} +
diff --git a/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts b/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts index 5dad5709b..235d5df08 100644 --- a/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts +++ b/packages/svelte2tsx/src/htmlxtojsx/nodes/component.ts @@ -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) { @@ -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, '}}'); } diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/expected.jsx new file mode 100644 index 000000000..a065bd29d --- /dev/null +++ b/packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/expected.jsx @@ -0,0 +1 @@ +<> \ No newline at end of file diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/input.svelte b/packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/input.svelte new file mode 100644 index 000000000..ae3ec6651 --- /dev/null +++ b/packages/svelte2tsx/test/htmlx2jsx/samples/component-default-slot-empty/input.svelte @@ -0,0 +1 @@ + diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/expected.jsx index 3f9dc7431..6928705eb 100644 --- a/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/expected.jsx +++ b/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/expected.jsx @@ -1,8 +1,9 @@ <>{() => { let {var:new_var} = __sveltets_instanceOf(Component).$$slot_def['default'];<> -

Hello

-
{() => { let {slotvar:newvar} = __sveltets_instanceOf(Component).$$slot_def['someslot'];<> -

Hi Slot

- }}
+

Hello {new_var}

+ {() => { let {slotvar:newvar} = __sveltets_instanceOf(Component).$$slot_def['someslot'];<>
+

Hi Slot {newvar}

+
}} + {() => { let {newvar2} = __sveltets_instanceOf(Component).$$slot_def['slotwithoutchildren'];<>
}}

Test

diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/input.svelte b/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/input.svelte index 7b87be787..b7535cdc4 100644 --- a/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/input.svelte +++ b/packages/svelte2tsx/test/htmlx2jsx/samples/component-multi-slot/input.svelte @@ -1,8 +1,9 @@ -

Hello

-
-

Hi Slot

+

Hello {new_var}

+
+

Hi Slot {newvar}

+

Test

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 index 0b4cf7248..c44873831 100644 --- 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 @@ -1,9 +1,9 @@ /// <>;function render() { <> -
{() => { let {a} = __sveltets_instanceOf(Component).$$slot_def['b'];<> + {() => { let {a} = __sveltets_instanceOf(Component).$$slot_def['b'];<>
- }}
+
}}
return { props: {}, slots: {'default': {a:__sveltets_instanceOf(Component).$$slot_def['b'].a}}, getters: {}, events: {} }}