From 89c001b5ccf5fe5c69d229ee4872234e84203603 Mon Sep 17 00:00:00 2001 From: "Lyu, Wei-Da" <36730922+jasonlyu123@users.noreply.github.com> Date: Tue, 10 Dec 2024 04:51:36 +0800 Subject: [PATCH 01/13] fix: don't patch solution project in ts plugin (#2617) part of #2612 --- packages/typescript-plugin/src/utils.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/typescript-plugin/src/utils.ts b/packages/typescript-plugin/src/utils.ts index 1b14a6dbe..71425bc31 100644 --- a/packages/typescript-plugin/src/utils.ts +++ b/packages/typescript-plugin/src/utils.ts @@ -255,6 +255,14 @@ export function hasNodeModule(startPath: string, module: string) { } export function isSvelteProject(project: ts.server.Project) { + // internal api, the way to check requires checking the files config in tsconfig.json + // so we can't reimplement it without reading the tsconfig.json again + // The solution project is mostly just a container we don't need to patch it + // and having any files in this project cause TSServer to send config error while it originally won't + if ((project as any).isSolution?.()) { + return false; + } + const projectDirectory = getProjectDirectory(project); if (projectDirectory) { return hasNodeModule(projectDirectory, 'svelte'); From 29497af7a8e8633f3532f68237aad13950c6534a Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 11 Dec 2024 00:04:59 +0100 Subject: [PATCH 02/13] chore: fix tests for Svelte 5 (#2626) --- .../test/plugins/svelte/SveltePlugin.test.ts | 10 ++++++---- .../plugins/svelte/features/getDiagnostics.test.ts | 11 +++++++---- .../fixtures/parser-error/expected_svelte_5.json | 2 +- .../svelte-element-error/expected_svelte_5.json | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts b/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts index 1e57caea1..911e195e8 100644 --- a/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts +++ b/packages/language-server/test/plugins/svelte/SveltePlugin.test.ts @@ -39,7 +39,7 @@ describe('Svelte Plugin', () => { const diagnostic = Diagnostic.create( Range.create(1, 0, 1, 21), isSvelte5Plus - ? '`` element should have an alt attribute' + ? '`` element should have an alt attribute\nhttps://svelte.dev/e/a11y_missing_attribute' : 'A11y: element should have an alt attribute', DiagnosticSeverity.Warning, isSvelte5Plus ? 'a11y_missing_attribute' : 'a11y-missing-attribute', @@ -54,10 +54,12 @@ describe('Svelte Plugin', () => { const diagnostics = await plugin.getDiagnostics(document); const diagnostic = Diagnostic.create( - Range.create(0, 10, 0, 18), - isSvelte5Plus ? 'Can only bind to state or props' : 'whatever is not declared', + Range.create(0, isSvelte5Plus ? 5 : 10, 0, 18), + isSvelte5Plus + ? '`bind:whatever` is not a valid binding\nhttps://svelte.dev/e/bind_invalid_name' + : 'whatever is not declared', DiagnosticSeverity.Error, - isSvelte5Plus ? 'bind_invalid_value' : 'binding-undeclared', + isSvelte5Plus ? 'bind_invalid_name' : 'binding-undeclared', 'svelte' ); diff --git a/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts b/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts index e4e72b497..b41958228 100644 --- a/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts +++ b/packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts @@ -471,7 +471,8 @@ describe('SveltePlugin#getDiagnostics', () => { { range: { start: { line: 1, character: 15 }, end: { line: 1, character: 27 } }, message: - "Component has unused export property 'name'. If it is for external reference only, please consider using `export const name`", + "Component has unused export property 'name'. If it is for external reference only, please consider using `export const name`" + + (isSvelte5Plus ? '\nhttps://svelte.dev/e/export_let_unused' : ''), severity: 2, source: 'svelte', code: isSvelte5Plus ? 'export_let_unused' : 'unused-export-let' @@ -489,7 +490,7 @@ describe('SveltePlugin#getDiagnostics', () => { { range: { start: { line: 1, character: 4 }, end: { line: 1, character: 26 } }, message: isSvelte5Plus - ? 'Reactive declarations only exist at the top level of the instance script' + ? 'Reactive declarations only exist at the top level of the instance script\nhttps://svelte.dev/e/reactive_declaration_invalid_placement' : '$: has no effect in a module script', severity: 2, source: 'svelte', @@ -511,7 +512,8 @@ describe('SveltePlugin#getDiagnostics', () => { { code: isSvelte5Plus ? 'export_let_unused' : 'unused-export-let', message: - "Component has unused export property 'unused1'. If it is for external reference only, please consider using `export const unused1`", + "Component has unused export property 'unused1'. If it is for external reference only, please consider using `export const unused1`" + + (isSvelte5Plus ? '\nhttps://svelte.dev/e/export_let_unused' : ''), range: { start: { line: 5, @@ -528,7 +530,8 @@ describe('SveltePlugin#getDiagnostics', () => { { code: isSvelte5Plus ? 'export_let_unused' : 'unused-export-let', message: - "Component has unused export property 'unused2'. If it is for external reference only, please consider using `export const unused2`", + "Component has unused export property 'unused2'. If it is for external reference only, please consider using `export const unused2`" + + (isSvelte5Plus ? '\nhttps://svelte.dev/e/export_let_unused' : ''), range: { start: { line: 6, diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/parser-error/expected_svelte_5.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/parser-error/expected_svelte_5.json index c0fcc5b6d..65bcc0afb 100644 --- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/parser-error/expected_svelte_5.json +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/parser-error/expected_svelte_5.json @@ -3,7 +3,7 @@ "range": { "start": { "line": 1, "character": 0 }, "end": { "line": 1, "character": 0 } }, "severity": 1, "source": "js", - "message": "A component can have a single top-level `'); + + const highlight = plugin.findDocumentHighlight(document, Position.create(0, 9)); + + assert.deepStrictEqual(highlight, [ + { + range: { + start: { + line: 0, + character: 7 + }, + end: { + line: 0, + character: 10 + } + }, + kind: DocumentHighlightKind.Write + }, + { + range: { + start: { + line: 0, + character: 20 + }, + end: { + line: 0, + character: 23 + } + }, + kind: DocumentHighlightKind.Read + } + ]); + }); + + it('provide document highlight for style attribute', () => { + const { plugin, document } = setup('
'); + + const highlight = plugin.findDocumentHighlight(document, Position.create(0, 13)); + + assert.deepStrictEqual(highlight, [ + { + range: { + start: { + line: 0, + character: 12 + }, + end: { + line: 0, + character: 20 + } + }, + kind: DocumentHighlightKind.Read + } + ]); + }); + + it('provide word highlight for unsupported languages', () => { + const { plugin, document } = setup(''); + + const highlight = plugin.findDocumentHighlight(document, Position.create(0, 25)); + + assert.deepStrictEqual(highlight, [ + { + range: { + start: { + line: 0, + character: 22 + }, + end: { + line: 0, + character: 25 + } + }, + kind: DocumentHighlightKind.Text + } + ]); + }); + }); }); diff --git a/packages/language-server/test/plugins/html/HTMLPlugin.test.ts b/packages/language-server/test/plugins/html/HTMLPlugin.test.ts index aaad0f52c..f112baec3 100644 --- a/packages/language-server/test/plugins/html/HTMLPlugin.test.ts +++ b/packages/language-server/test/plugins/html/HTMLPlugin.test.ts @@ -8,11 +8,13 @@ import { CompletionItemKind, InsertTextFormat, CompletionTriggerKind, - FoldingRange + FoldingRange, + DocumentHighlightKind } from 'vscode-languageserver'; import { HTMLPlugin } from '../../../src/plugins'; import { DocumentManager, Document } from '../../../src/lib/documents'; import { LSConfigManager } from '../../../src/ls-config'; +import { DocumentHighlight } from 'vscode-languageserver-types'; import { VERSION } from 'svelte/compiler'; const isSvelte5Plus = Number(VERSION.split('.')[0]) >= 5; @@ -294,4 +296,61 @@ describe('HTML Plugin', () => { { startLine: 1, endLine: 2 } ]); }); + + it('provide document highlight', () => { + const { plugin, document } = setup('
'); + + const highlight = plugin.findDocumentHighlight(document, Position.create(0, 1)); + + assert.deepStrictEqual(highlight, [ + { + range: { + start: { + line: 0, + character: 1 + }, + end: { + line: 0, + character: 4 + } + }, + kind: DocumentHighlightKind.Read + }, + { + range: { + start: { + line: 0, + character: 7 + }, + end: { + line: 0, + character: 10 + } + }, + kind: DocumentHighlightKind.Read + } + ]); + }); + + it('provide word highlight for unsupported languages', () => { + const { plugin, document } = setup(''); + + const highlight = plugin.findDocumentHighlight(document, Position.create(1, 5)); + + assert.deepStrictEqual(highlight, [ + { + range: { + start: { + line: 1, + character: 2 + }, + end: { + line: 1, + character: 5 + } + }, + kind: DocumentHighlightKind.Text + } + ]); + }); }); diff --git a/packages/language-server/test/plugins/typescript/features/DocumentHighlightProvider.test.ts b/packages/language-server/test/plugins/typescript/features/DocumentHighlightProvider.test.ts new file mode 100644 index 000000000..8cab8c662 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/features/DocumentHighlightProvider.test.ts @@ -0,0 +1,274 @@ +import assert from 'assert'; +import path from 'path'; +import ts from 'typescript'; +import { DocumentHighlight, DocumentHighlightKind } from 'vscode-languageserver'; +import { Document, DocumentManager } from '../../../../src/lib/documents'; +import { LSConfigManager } from '../../../../src/ls-config'; +import { LSAndTSDocResolver } from '../../../../src/plugins'; +import { DocumentHighlightProviderImpl } from '../../../../src/plugins/typescript/features/DocumentHighlightProvider'; +import { pathToUrl } from '../../../../src/utils'; +import { serviceWarmup } from '../test-utils'; + +const testDir = path.join(__dirname, '..'); + +describe('DocumentHighlightProvider', function () { + const highlightTestDir = path.join(testDir, 'testfiles', 'document-highlight'); + serviceWarmup(this, highlightTestDir); + + function getFullPath(filename: string) { + return path.join(highlightTestDir, filename); + } + + function setup(filename: string) { + const docManager = new DocumentManager( + (textDocument) => new Document(textDocument.uri, textDocument.text) + ); + const lsAndTsDocResolver = new LSAndTSDocResolver( + docManager, + [testDir], + new LSConfigManager() + ); + const provider = new DocumentHighlightProviderImpl(lsAndTsDocResolver); + const filePath = getFullPath(filename); + const document = docManager.openClientDocument({ + uri: pathToUrl(filePath), + text: ts.sys.readFile(filePath) || '' + }); + return { provider, document }; + } + + it('find document highlight', async () => { + const { document, provider } = setup('document-highlight.svelte'); + + const highlight = await provider.findDocumentHighlight(document, { + line: 1, + character: 9 + }); + + assert.deepStrictEqual(highlight, [ + { + range: { + start: { + line: 1, + character: 8 + }, + end: { + line: 1, + character: 12 + } + }, + kind: DocumentHighlightKind.Write + }, + { + range: { + start: { + line: 3, + character: 8 + }, + end: { + line: 3, + character: 12 + } + }, + kind: DocumentHighlightKind.Read + }, + { + range: { + start: { + line: 8, + character: 1 + }, + end: { + line: 8, + character: 5 + } + }, + kind: DocumentHighlightKind.Read + } + ]); + }); + + describe('DocumentHighlightProvider (svelte blocks/tags)', () => { + async function testSameHighlight( + content: string, + tests: number[], + expected: Array<[start: number, end: number]> + ) { + const { provider, document } = setup(content); + + for (const position of tests) { + await testOne(document, provider, position, expected); + } + } + + function setup(content: string) { + const docManager = new DocumentManager( + (textDocument) => new Document(textDocument.uri, textDocument.text) + ); + const lsAndTsDocResolver = new LSAndTSDocResolver( + docManager, + [testDir], + new LSConfigManager() + ); + const provider = new DocumentHighlightProviderImpl(lsAndTsDocResolver); + const filePath = getFullPath(`svelte.virtual${Math.random().toFixed(16)}.svelte`); + const document = docManager.openClientDocument({ + uri: pathToUrl(filePath), + text: content + }); + return { provider, document }; + } + + async function testOne( + document: Document, + provider: DocumentHighlightProviderImpl, + character: number, + expected: Array<[start: number, end: number]> | null + ) { + const documentHighlight = await provider.findDocumentHighlight(document, { + line: 0, + character + }); + + assert.deepStrictEqual( + documentHighlight?.sort( + (a, b) => a.range.start.character - b.range.start.character + ), + expected?.map( + ([start, end]): DocumentHighlight => ({ + kind: DocumentHighlightKind.Read, + range: { + start: { line: 0, character: start }, + end: { line: 0, character: end } + } + }) + ) + ); + } + + it('should return null for style and script', async () => { + const { provider, document } = setup(''); + await testOne(document, provider, 7, null); + await testOne(document, provider, 15, null); + }); + + it('get highlight for key block', async () => { + await testSameHighlight( + '{#key foo}{/key}', + [2, 12], + [ + [1, 5], + [11, 15] + ] + ); + }); + + it('get highlight for each block', async () => { + await testSameHighlight( + '{#each expression as name}{:else}{/each}', + [2, 28, 35], + [ + [1, 6], + [27, 32], + [34, 39] + ] + ); + }); + + it('get highlight for if block', async () => { + await testSameHighlight( + '{#if expression}{:else}{/if}', + [2, 18, 25], + [ + [1, 4], + [17, 22], + [24, 27] + ] + ); + }); + + it('get highlight for if block with else if', async () => { + await testSameHighlight( + '{#if expression}{:else if foo}{:else}{/if}', + [2, 18, 32, 39], + [ + [1, 4], + [17, 25], + [31, 36], + [38, 41] + ] + ); + }); + + it("doesn't get highlights from another if block nested inside", async () => { + const { provider, document } = setup('{#if expression}{:else if hi}{#if hi}{/if}{/if}'); + await testOne(document, provider, 2, [ + [1, 4], + [17, 25], + [43, 46] + ]); + }); + + it('highlight nested if block', async () => { + const { provider, document } = setup('{#if expression}{:else if hi}{#if hi}{/if}{/if}'); + await testOne(document, provider, 31, [ + [30, 33], + [38, 41] + ]); + }); + + it('get highlight for await block', async () => { + await testSameHighlight( + '{#await expression}{:then name}{:catch name}{/await}', + [2, 21, 33, 46], + [ + [1, 7], + [20, 25], + [32, 38], + [45, 51] + ] + ); + }); + + it('get highlight for await block (skip pending)', async () => { + await testSameHighlight( + '{#await expression then name} {/await}', + [2, 20, 31], + [ + [1, 7], + [19, 23], + [31, 37] + ] + ); + }); + + it('get highlight for await block (skip pending and then)', async () => { + await testSameHighlight( + '{#await expression catch name}{/await}', + [2, 20, 31], + [ + [1, 7], + [19, 24], + [31, 37] + ] + ); + }); + + it('get highlight for debug tag', async () => { + const { provider, document } = setup('{@debug name}'); + await testOne(document, provider, 2, [[1, 7]]); + }); + + it('get highlight for html tag', async () => { + const { provider, document } = setup('{@html name}'); + await testOne(document, provider, 2, [[1, 6]]); + }); + + it('get highlight for const tag', async () => { + const { provider, document } = setup( + '{#each expression as item}{@const name = item}{/each}' + ); + await testOne(document, provider, 28, [[27, 33]]); + }); + }); +}); diff --git a/packages/language-server/test/plugins/typescript/testfiles/document-highlight/document-highlight.svelte b/packages/language-server/test/plugins/typescript/testfiles/document-highlight/document-highlight.svelte new file mode 100644 index 000000000..22626b885 --- /dev/null +++ b/packages/language-server/test/plugins/typescript/testfiles/document-highlight/document-highlight.svelte @@ -0,0 +1,9 @@ + + +{prop} \ No newline at end of file diff --git a/packages/svelte-vscode/package.json b/packages/svelte-vscode/package.json index a357e9637..de78f975b 100644 --- a/packages/svelte-vscode/package.json +++ b/packages/svelte-vscode/package.json @@ -399,6 +399,12 @@ "none", "ts" ] + }, + "svelte.plugin.svelte.documentHighlight.enable": { + "type": "boolean", + "default": true, + "title": "Svelte: Document Highlight", + "description": "Enable document highlight support. Requires a restart." } } }, From d5fbdfeb69b5c2fdd9ee15ad785cc1050eafb506 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:31:31 +0100 Subject: [PATCH 13/13] fix: better hoistability analysis (#2655) Instead of collecting the types/values that are allowed, we collect the types/values that are _disallowed_ - this makes it possible to reference global values/types and still have them properly be declared as hoistable #2640 --- packages/svelte2tsx/src/svelte2tsx/index.ts | 6 +- .../svelte2tsx/nodes/HoistableInterfaces.ts | 122 +++++++----------- .../snippet-module-hoist-1.v5/expectedv2.ts | 11 ++ .../snippet-module-hoist-1.v5/input.svelte | 13 ++ 4 files changed, 74 insertions(+), 78 deletions(-) diff --git a/packages/svelte2tsx/src/svelte2tsx/index.ts b/packages/svelte2tsx/src/svelte2tsx/index.ts index 68a688bef..b5e2a9e65 100644 --- a/packages/svelte2tsx/src/svelte2tsx/index.ts +++ b/packages/svelte2tsx/src/svelte2tsx/index.ts @@ -166,11 +166,13 @@ export function svelte2tsx( } if (moduleScriptTag || scriptTag) { - const allowed = exportedNames.hoistableInterfaces.getAllowedValues(); for (const [start, end, globals] of rootSnippets) { const hoist_to_module = moduleScriptTag && - (globals.size === 0 || [...globals.keys()].every((id) => allowed.has(id))); + (globals.size === 0 || + [...globals.keys()].every((id) => + exportedNames.hoistableInterfaces.isAllowedReference(id) + )); if (hoist_to_module) { str.move( diff --git a/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts b/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts index 7ee4de9b9..bca4a25ce 100644 --- a/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts +++ b/packages/svelte2tsx/src/svelte2tsx/nodes/HoistableInterfaces.ts @@ -5,8 +5,9 @@ import MagicString from 'magic-string'; * Collects all imports and module-level declarations to then find out which interfaces/types are hoistable. */ export class HoistableInterfaces { - private import_value_set: Set = new Set(); - private import_type_set: Set = new Set(); + private module_types: Set = new Set(); + private disallowed_types = new Set(); + private disallowed_values = new Set(); private interface_map: Map< string, { type_deps: Set; value_deps: Set; node: ts.Node } @@ -18,6 +19,7 @@ export class HoistableInterfaces { value_deps: new Set() }; + /** should be called before analyzeInstanceScriptNode */ analyzeModuleScriptNode(node: ts.Node) { // Handle Import Declarations if (ts.isImportDeclaration(node) && node.importClause) { @@ -30,9 +32,7 @@ export class HoistableInterfaces { node.importClause.namedBindings.elements.forEach((element) => { const import_name = element.name.text; if (is_type_only || element.isTypeOnly) { - this.import_type_set.add(import_name); - } else { - this.import_value_set.add(import_name); + this.module_types.add(import_name); } }); } @@ -41,9 +41,7 @@ export class HoistableInterfaces { if (node.importClause.name) { const default_import = node.importClause.name.text; if (is_type_only) { - this.import_type_set.add(default_import); - } else { - this.import_value_set.add(default_import); + this.module_types.add(default_import); } } @@ -54,40 +52,17 @@ export class HoistableInterfaces { ) { const namespace_import = node.importClause.namedBindings.name.text; if (is_type_only) { - this.import_type_set.add(namespace_import); - } else { - this.import_value_set.add(namespace_import); + this.module_types.add(namespace_import); } } } - // Handle top-level declarations - if (ts.isVariableStatement(node)) { - node.declarationList.declarations.forEach((declaration) => { - if (ts.isIdentifier(declaration.name)) { - this.import_value_set.add(declaration.name.text); - } - }); - } - - if (ts.isFunctionDeclaration(node) && node.name) { - this.import_value_set.add(node.name.text); - } - - if (ts.isClassDeclaration(node) && node.name) { - this.import_value_set.add(node.name.text); - } - - if (ts.isEnumDeclaration(node)) { - this.import_value_set.add(node.name.text); - } - if (ts.isTypeAliasDeclaration(node)) { - this.import_type_set.add(node.name.text); + this.module_types.add(node.name.text); } if (ts.isInterfaceDeclaration(node)) { - this.import_type_set.add(node.name.text); + this.module_types.add(node.name.text); } } @@ -103,9 +78,7 @@ export class HoistableInterfaces { node.importClause.namedBindings.elements.forEach((element) => { const import_name = element.name.text; if (is_type_only) { - this.import_type_set.add(import_name); - } else { - this.import_value_set.add(import_name); + this.module_types.add(import_name); } }); } @@ -114,9 +87,7 @@ export class HoistableInterfaces { if (node.importClause.name) { const default_import = node.importClause.name.text; if (is_type_only) { - this.import_type_set.add(default_import); - } else { - this.import_value_set.add(default_import); + this.module_types.add(default_import); } } @@ -127,9 +98,7 @@ export class HoistableInterfaces { ) { const namespace_import = node.importClause.namedBindings.name.text; if (is_type_only) { - this.import_type_set.add(namespace_import); - } else { - this.import_value_set.add(namespace_import); + this.module_types.add(namespace_import); } } } @@ -167,9 +136,9 @@ export class HoistableInterfaces { } }); - if (this.import_type_set.has(interface_name)) { - // shadowed; delete because we can't hoist - this.import_type_set.delete(interface_name); + if (this.module_types.has(interface_name)) { + // shadowed; we can't hoist + this.disallowed_types.add(interface_name); } else { this.interface_map.set(interface_name, { type_deps: type_dependencies, @@ -193,9 +162,9 @@ export class HoistableInterfaces { generics ); - if (this.import_type_set.has(alias_name)) { - // shadowed; delete because we can't hoist - this.import_type_set.delete(alias_name); + if (this.module_types.has(alias_name)) { + // shadowed; we can't hoist + this.disallowed_types.add(alias_name); } else { this.interface_map.set(alias_name, { type_deps: type_dependencies, @@ -209,29 +178,21 @@ export class HoistableInterfaces { if (ts.isVariableStatement(node)) { node.declarationList.declarations.forEach((declaration) => { if (ts.isIdentifier(declaration.name)) { - this.import_value_set.delete(declaration.name.text); + this.disallowed_values.add(declaration.name.text); } }); } if (ts.isFunctionDeclaration(node) && node.name) { - this.import_value_set.delete(node.name.text); + this.disallowed_values.add(node.name.text); } if (ts.isClassDeclaration(node) && node.name) { - this.import_value_set.delete(node.name.text); + this.disallowed_values.add(node.name.text); } if (ts.isEnumDeclaration(node)) { - this.import_value_set.delete(node.name.text); - } - - if (ts.isTypeAliasDeclaration(node)) { - this.import_type_set.delete(node.name.text); - } - - if (ts.isInterfaceDeclaration(node)) { - this.import_type_set.delete(node.name.text); + this.disallowed_values.add(node.name.text); } } @@ -281,13 +242,26 @@ export class HoistableInterfaces { continue; } - const can_hoist = [...deps.type_deps, ...deps.value_deps].every((dep) => { - return ( - this.import_type_set.has(dep) || - this.import_value_set.has(dep) || - hoistable_interfaces.has(dep) - ); - }); + let can_hoist = true; + + for (const dep of deps.type_deps) { + if (this.disallowed_types.has(dep)) { + this.disallowed_types.add(interface_name); + can_hoist = false; + break; + } + if (this.interface_map.has(dep) && !hoistable_interfaces.has(dep)) { + can_hoist = false; + } + } + + for (const dep of deps.value_deps) { + if (this.disallowed_values.has(dep)) { + this.disallowed_types.add(interface_name); + can_hoist = false; + break; + } + } if (can_hoist) { hoistable_interfaces.set(interface_name, deps.node); @@ -301,11 +275,7 @@ export class HoistableInterfaces { ...this.props_interface.type_deps, ...this.props_interface.value_deps ].every((dep) => { - return ( - this.import_type_set.has(dep) || - this.import_value_set.has(dep) || - hoistable_interfaces.has(dep) - ); + return !this.disallowed_types.has(dep) && !this.disallowed_values.has(dep); }); if (can_hoist) { @@ -328,7 +298,7 @@ export class HoistableInterfaces { if (!this.props_interface.name) return; for (const generic of generics) { - this.import_type_set.delete(generic); + this.disallowed_types.add(generic); } const hoistable = this.determineHoistableInterfaces(); @@ -362,8 +332,8 @@ export class HoistableInterfaces { } } - getAllowedValues() { - return this.import_value_set; + isAllowedReference(reference: string) { + return !this.disallowed_values.has(reference); } /** diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts index 69587fe79..0c2610728 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/expectedv2.ts @@ -18,6 +18,11 @@ import { imported } from './x'; { svelteHTML.createElement("div", {});module; } };return __sveltets_2_any(0)}; const hoistable7/*Ωignore_positionΩ*/ = ()/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { { svelteHTML.createElement("div", {});imported; } +};return __sveltets_2_any(0)}; const hoistable8/*Ωignore_positionΩ*/ = ()/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { + { svelteHTML.createElement("div", {});global; } +};return __sveltets_2_any(0)}; const hoistable9/*Ωignore_positionΩ*/ = (props: HTMLAttributes)/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { };return __sveltets_2_any(0)}; const hoistable10/*Ωignore_positionΩ*/ = (foo)/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { + const bar = foo; + bar; };return __sveltets_2_any(0)};function render() { const not_hoistable/*Ωignore_positionΩ*/ = ()/*Ωignore_startΩ*/: ReturnType/*Ωignore_endΩ*/ => { async ()/*Ωignore_positionΩ*/ => { { svelteHTML.createElement("div", {});foo; } @@ -40,6 +45,12 @@ async () => { + + + + + + diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte index 924dc61aa..fd7c1c4fd 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte +++ b/packages/svelte2tsx/test/svelte2tsx/samples/snippet-module-hoist-1.v5/input.svelte @@ -35,6 +35,19 @@
{imported}
{/snippet} +{#snippet hoistable8()} +
{global}
+{/snippet} + +{#snippet hoistable9(props: HTMLAttributes)} + Referencing global types +{/snippet} + +{#snippet hoistable10(foo)} + {@const bar = foo} + {bar} +{/snippet} + {#snippet not_hoistable()}
{foo}
{/snippet} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy