Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: disambiguate render in module script #2667

Merged
merged 16 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from 14 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 @@ -27,6 +27,7 @@ import {
toGeneratedSvelteComponentName
} from '../utils';
import { findNodeAtSpan, gatherDescendants, SnapshotMap } from './utils';
import { RENDER_NAME } from 'svelte2tsx';

const ENSURE_COMPONENT_HELPER = '__sveltets_2_ensureComponent';

Expand Down Expand Up @@ -200,7 +201,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider {
.find(
(statement): statement is ts.FunctionDeclaration =>
ts.isFunctionDeclaration(statement) &&
statement.name?.getText() === 'render'
statement.name?.getText() === RENDER_NAME
)
?.name?.getStart()
: -1;
Expand Down Expand Up @@ -304,7 +305,7 @@ export class CallHierarchyProviderImpl implements CallHierarchyProvider {
return this.toComponentCallHierarchyItem(snapshot, item);
}

if (item.name === 'render') {
if (item.name === RENDER_NAME) {
const end = item.selectionSpan.start + item.selectionSpan.length;
const renderFunction = sourceFile.statements.find(
(statement) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
import { DiagnosticCode } from './DiagnosticsProvider';
import { createGetCanonicalFileName } from '../../../utils';
import { LanguageServiceContainer } from '../service';
import { RENDER_NAME } from 'svelte2tsx';

/**
* TODO change this to protocol constant if it's part of the protocol
Expand Down Expand Up @@ -1319,10 +1320,13 @@ export class CodeActionsProviderImpl implements CodeActionsProvider {
...refactor,
title: refactor.title
.replace(
"Extract to inner function in function 'render'",
`Extract to inner function in function '${RENDER_NAME}'`,
'Extract to function'
)
.replace("Extract to constant in function 'render'", 'Extract to constant')
.replace(
`Extract to constant in function '${RENDER_NAME}'`,
'Extract to constant'
)
}))
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SvelteDocumentSnapshot } from '../DocumentSnapshot';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertRange } from '../utils';
import { isTextSpanInGeneratedCode } from './utils';
import { RENDER_NAME } from 'svelte2tsx';

type CodeLensType = 'reference' | 'implementation';

Expand Down Expand Up @@ -79,7 +80,9 @@ export class CodeLensProviderImpl implements CodeLensProvider {
}

const navigationTree = lang.getNavigationTree(tsDoc.filePath);
const renderFunction = navigationTree?.childItems?.find((item) => item.text === 'render');
const renderFunction = navigationTree?.childItems?.find(
(item) => item.text === RENDER_NAME
);
if (renderFunction) {
// pretty rare that there is anything to show in the template, so we skip it
const notTemplate = renderFunction.childItems?.filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { not, flatten, passMap, swapRangeStartEndIfNecessary, memoize } from '../../../utils';
import { LSConfigManager } from '../../../ls-config';
import { isAttributeName, isEventHandler } from '../svelte-ast-utils';
import { RENDER_NAME } from 'svelte2tsx';

export enum DiagnosticCode {
MODIFIERS_CANNOT_APPEAR_HERE = 1184, // "Modifiers cannot appear here."
Expand Down Expand Up @@ -546,7 +547,7 @@ function get$$PropsDef(lang: ts.LanguageService, snapshot: SvelteDocumentSnapsho

const renderFunction = sourceFile.statements.find(
(statement): statement is ts.FunctionDeclaration =>
ts.isFunctionDeclaration(statement) && statement.name?.getText() === 'render'
ts.isFunctionDeclaration(statement) && statement.name?.getText() === RENDER_NAME
);
return renderFunction?.body?.statements.find(
(node): node is ts.TypeAliasDeclaration | ts.InterfaceDeclaration =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { SvelteDocumentSnapshot } from '../DocumentSnapshot';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertToTextSpan } from '../utils';
import { isInGeneratedCode } from './utils';
import { RENDER_NAME } from 'svelte2tsx';

const CONTENT_LENGTH_LIMIT = 50000;

Expand Down Expand Up @@ -111,7 +112,7 @@ export class SemanticTokensProviderImpl implements SemanticTokensProvider {
if (
isInGeneratedCode(text, generatedOffset, generatedOffset + generatedLength) ||
(encodedClassification === 2817 /* top level function */ &&
text.substring(generatedOffset, generatedOffset + generatedLength) === 'render')
text.substring(generatedOffset, generatedOffset + generatedLength) === RENDER_NAME)
) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { or } from '../../../utils';
import { FileMap } from '../../../lib/documents/fileCollection';
import { LSConfig } from '../../../ls-config';
import { LanguageServiceContainer } from '../service';
import { RENDER_NAME } from 'svelte2tsx';

type NodePredicate = (node: ts.Node) => boolean;

Expand Down Expand Up @@ -299,7 +300,10 @@ function nodeAndParentsSatisfyRespectivePredicates<T extends ts.Node>(

const isRenderFunction = nodeAndParentsSatisfyRespectivePredicates<
ts.FunctionDeclaration & { name: ts.Identifier }
>((node) => ts.isFunctionDeclaration(node) && node?.name?.getText() === 'render', ts.isSourceFile);
>(
(node) => ts.isFunctionDeclaration(node) && node?.name?.getText() === RENDER_NAME,
ts.isSourceFile
);

const isRenderFunctionBody = nodeAndParentsSatisfyRespectivePredicates(
ts.isBlock,
Expand All @@ -309,11 +313,11 @@ const isRenderFunctionBody = nodeAndParentsSatisfyRespectivePredicates(
export const isReactiveStatement = nodeAndParentsSatisfyRespectivePredicates<ts.LabeledStatement>(
(node) => ts.isLabeledStatement(node) && node.label.getText() === '$',
or(
// function render() {
// function $$render() {
// $: x2 = __sveltets_2_invalidate(() => x * x)
// }
isRenderFunctionBody,
// function render() {
// function $$render() {
// ;() => {$: x, update();
// }
nodeAndParentsSatisfyRespectivePredicates(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { __resetCache } from '../../../src/plugins/typescript/service';
import { ignoredBuildDirectories } from '../../../src/plugins/typescript/SnapshotManager';
import { pathToUrl } from '../../../src/utils';
import { serviceWarmup } from './test-utils';
import { RENDER_NAME } from 'svelte2tsx';

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

Expand Down Expand Up @@ -89,7 +90,7 @@ describe('TypescriptPlugin', function () {
}
}
},
containerName: 'render'
containerName: RENDER_NAME
},
{
name: 'hello',
Expand All @@ -107,7 +108,7 @@ describe('TypescriptPlugin', function () {
}
}
},
containerName: 'render'
containerName: RENDER_NAME
},
{
name: "$: if (hello) {\n console.log('hi');\n }",
Expand All @@ -125,7 +126,7 @@ describe('TypescriptPlugin', function () {
}
}
},
containerName: 'render'
containerName: RENDER_NAME
},
{
name: '$on("click") callback',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { pathToUrl } from '../../../../src/utils';
import { recursiveServiceWarmup } from '../test-utils';
import { DiagnosticCode } from '../../../../src/plugins/typescript/features/DiagnosticsProvider';
import { VERSION } from 'svelte/compiler';
import { RENDER_NAME } from 'svelte2tsx';

const testDir = path.join(__dirname, '..');
const indent = ' '.repeat(4);
Expand Down Expand Up @@ -1880,8 +1881,8 @@ describe('CodeActionsProvider', function () {
},
// is from generated code
textRange: {
pos: 179,
end: 213
pos: 181,
end: 215
}
}
],
Expand Down Expand Up @@ -2028,13 +2029,13 @@ describe('CodeActionsProvider', function () {
},
// is from generated code
textRange: {
pos: 179,
end: 213
pos: 181,
end: 215
}
}
],
command: 'function_scope_0',
title: "Extract to inner function in function 'render'"
title: `Extract to inner function in function '${RENDER_NAME}'`
},
title: 'Extract to function'
});
Expand Down
2 changes: 2 additions & 0 deletions packages/svelte2tsx/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,5 @@ export namespace InternalHelpers {
paramsPath: string;
}
}

export const RENDER_NAME:string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we expose this from a sub module, or via the helpers object that is not subject to semver? Wanna make sure that the "public public" API stays clean.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah better...just did it in a new commit

15 changes: 9 additions & 6 deletions packages/svelte2tsx/repl/index.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<input bind:value={get, set} />
<input bind:value={() => v, new_v => v = new_v} />
<script module lang="ts">
let foo = true;
</script>

<div bind:clientWidth={null, set} />
<div bind:contentRect={null, set} />
{#snippet hoistable1()}
<div>hello</div>
{/snippet}

<Input bind:value={get, set} />
<Input bind:value={() => v, new_v => v = new_v} />
{#snippet hoistable2()}
<div>{foo}</div>
{/snippet}
2 changes: 1 addition & 1 deletion packages/svelte2tsx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { svelte2tsx } from './svelte2tsx';
export { svelte2tsx, RENDER_NAME } from './svelte2tsx';
export { emitDts } from './emitDts';
export { internalHelpers } from './helpers';
21 changes: 11 additions & 10 deletions packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ComponentDocumentation } from './nodes/ComponentDocumentation';
import { Generics } from './nodes/Generics';
import { surroundWithIgnoreComments } from '../utils/ignore';
import { ComponentEvents } from './nodes/ComponentEvents';
import { RENDER_NAME } from '.';

export interface AddComponentExportPara {
str: MagicString;
Expand Down Expand Up @@ -71,29 +72,29 @@ function addGenericsComponentExport({
let statement = `
class __sveltets_Render${genericsDef} {
props() {
return ${props(true, canHaveAnyProp, exportedNames, `render${genericsRef}()`)}.props;
return ${props(true, canHaveAnyProp, exportedNames, `${RENDER_NAME}${genericsRef}()`)}.props;
}
events() {
return ${_events(events.hasStrictEvents() || exportedNames.usesRunes(), `render${genericsRef}()`)}.events;
return ${_events(events.hasStrictEvents() || exportedNames.usesRunes(), `${RENDER_NAME}${genericsRef}()`)}.events;
}
slots() {
return render${genericsRef}().slots;
return ${RENDER_NAME}${genericsRef}().slots;
}
`;

// For Svelte 5+ we assume TS > 4.7
if (isSvelte5 && !isTsFile && exportedNames.usesRunes()) {
statement = `
class __sveltets_Render${genericsDef} {
props(): ReturnType<typeof render${genericsRef}>['props'] { return null as any; }
events(): ReturnType<typeof render${genericsRef}>['events'] { return null as any; }
slots(): ReturnType<typeof render${genericsRef}>['slots'] { return null as any; }
props(): ReturnType<typeof ${RENDER_NAME}${genericsRef}>['props'] { return null as any; }
events(): ReturnType<typeof ${RENDER_NAME}${genericsRef}>['events'] { return null as any; }
slots(): ReturnType<typeof ${RENDER_NAME}${genericsRef}>['slots'] { return null as any; }
`;
}

statement += isSvelte5
? ` bindings() { return ${exportedNames.createBindingsStr()}; }
exports() { return ${exportedNames.hasExports() ? `render${genericsRef}().exports` : '{}'}; }
exports() { return ${exportedNames.hasExports() ? `${RENDER_NAME}${genericsRef}().exports` : '{}'}; }
}\n`
: '}\n';

Expand Down Expand Up @@ -181,7 +182,7 @@ function addSimpleComponentExport({
isTsFile,
canHaveAnyProp,
exportedNames,
_events(events.hasStrictEvents(), 'render()')
_events(events.hasStrictEvents(), `${RENDER_NAME}()`)
);

const doc = componentDocumentation.getFormatted();
Expand All @@ -192,7 +193,7 @@ function addSimpleComponentExport({
if (mode === 'dts') {
if (isSvelte5 && exportedNames.usesRunes() && !usesSlots && !events.hasEvents()) {
statement =
`\n${doc}const ${componentName} = __sveltets_2_fn_component(render());\n` +
`\n${doc}const ${componentName} = __sveltets_2_fn_component(${RENDER_NAME}());\n` +
`type ${componentName} = ReturnType<typeof ${componentName}>;\n` +
`export default ${componentName};`;
} else if (isSvelte5) {
Expand Down Expand Up @@ -258,7 +259,7 @@ declare function $$__sveltets_2_isomorphic_component<
if (isSvelte5) {
if (exportedNames.usesRunes() && !usesSlots && !events.hasEvents()) {
statement =
`\n${doc}const ${componentName} = __sveltets_2_fn_component(render());\n` +
`\n${doc}const ${componentName} = __sveltets_2_fn_component(${RENDER_NAME}());\n` +
`type ${componentName} = ReturnType<typeof ${componentName}>;\n` +
`export default ${componentName};`;
} else {
Expand Down
8 changes: 5 additions & 3 deletions packages/svelte2tsx/src/svelte2tsx/createRenderFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IGNORE_START_COMMENT,
surroundWithIgnoreComments
} from '../utils/ignore';
import { RENDER_NAME } from '.';

export interface CreateRenderFunctionPara extends InstanceScriptProcessResult {
str: MagicString;
Expand Down Expand Up @@ -75,7 +76,8 @@ export function createRenderFunction({
start++;
end--;
}
str.overwrite(scriptTag.start + 1, start - 1, `function render`);

str.overwrite(scriptTag.start + 1, start - 1, `function ${RENDER_NAME}`);
str.overwrite(start - 1, start, isTsFile ? '<' : `<${IGNORE_START_COMMENT}`); // if the generics are unused, only this char is colored opaque
str.overwrite(
end,
Expand All @@ -86,7 +88,7 @@ export function createRenderFunction({
str.overwrite(
scriptTag.start + 1,
scriptTagEnd,
`function render${generics.toDefinitionString(true)}() {${propsDecl}\n`
`function ${RENDER_NAME}${generics.toDefinitionString(true)}() {${propsDecl}\n`
);
}

Expand All @@ -98,7 +100,7 @@ export function createRenderFunction({
} else {
str.prependRight(
scriptDestination,
`;function render() {` + `${propsDecl}${slotsDeclaration}\nasync () => {`
`;function ${RENDER_NAME}() {` + `${propsDecl}${slotsDeclaration}\nasync () => {`
);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/svelte2tsx/src/svelte2tsx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { createModuleAst, ModuleAst, processModuleScriptTag } from './processMod
import path from 'path';
import { parse, VERSION } from 'svelte/compiler';

export const RENDER_NAME = '$$render';

function processSvelteTemplate(
str: MagicString,
parse: typeof import('svelte/compiler').parse,
Expand Down
6 changes: 5 additions & 1 deletion packages/svelte2tsx/src/svelte2tsx/nodes/ExportedNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { surroundWithIgnoreComments } from '../../utils/ignore';
import { preprendStr, overwriteStr } from '../../utils/magic-string';
import { findExportKeyword, getLastLeadingDoc, isInterfaceOrTypeDeclaration } from '../utils/tsAst';
import { HoistableInterfaces } from './HoistableInterfaces';
import { RENDER_NAME } from '..';

export function is$$PropsDeclaration(
node: ts.Node
Expand Down Expand Up @@ -506,7 +507,10 @@ export class ExportedNames {
if (this.usesRunes()) {
// In runes mode, exports are no longer part of props
return Array.from(this.getters)
.map((name) => `\n get ${name}() { return render${generics}().exports.${name} }`)
.map(
(name) =>
`\n get ${name}() { return ${RENDER_NAME}${generics}().exports.${name} }`
)
.join('');
} else {
return Array.from(this.getters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function createModuleAst(str: MagicString, script: Node): ModuleAst {
true,
ts.ScriptKind.TS
);

const astOffset = script.content.start;

return { htmlx, tsAst, astOffset };
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading