diff --git a/sites/svelte.dev/.gitignore b/sites/svelte.dev/.gitignore index 1dbff35f98..514f05e83e 100644 --- a/sites/svelte.dev/.gitignore +++ b/sites/svelte.dev/.gitignore @@ -11,3 +11,4 @@ /src/routes/_components/Supporters/donors.js .vercel examples-data.js +type-info.js \ No newline at end of file diff --git a/sites/svelte.dev/scripts/type-gen/compile-types.js b/sites/svelte.dev/scripts/type-gen/compile-types.js index 3c2aad96e2..4ad75cd358 100644 --- a/sites/svelte.dev/scripts/type-gen/compile-types.js +++ b/sites/svelte.dev/scripts/type-gen/compile-types.js @@ -1,8 +1,9 @@ // @ts-check +import MagicString from 'magic-string'; +import fs from 'node:fs'; import { rollup } from 'rollup'; import dts from 'rollup-plugin-dts'; -import fs from 'node:fs'; -import ts from 'typescript' +import ts from 'typescript'; export async function get_bundled_types() { const dtsSources = fs.readdirSync(new URL('./dts-sources', import.meta.url)); @@ -19,96 +20,58 @@ export async function get_bundled_types() { codes.set( (file === 'index.d.ts' ? 'svelte' : `svelte/${file}`).replace('.d.ts', ''), - await bundle.generate({ format: 'esm' }).then(({ output }) => output[0].code) + useExportDeclarations( + await bundle.generate({ format: 'esm' }).then(({ output }) => output[0].code) + ) ); } - console.log(codes.get('svelte/action')); - return codes; } +/** @param {string} str */ +function useExportDeclarations(str) { + const magicStr = new MagicString(str); + + const sourceFile = ts.createSourceFile( + 'index.d.ts', + str, + ts.ScriptTarget.ESNext, + true, + ts.ScriptKind.TS + ); + + // There's only gonna be one because of the output of dts-plugin + const exportDeclaration = sourceFile.statements.find( + (statement) => statement.kind === ts.SyntaxKind.ExportDeclaration + ); + + if (exportDeclaration && !ts.isExportDeclaration(exportDeclaration)) return str; + + // @ts-ignore Why does TS not identify `elements` + const exportedSymbols = exportDeclaration?.exportClause?.elements.map( + (element) => element.name.text + ); + + for (const statement of sourceFile.statements) { + if ( + !( + ts.isFunctionDeclaration(statement) || + ts.isInterfaceDeclaration(statement) || + ts.isTypeAliasDeclaration(statement) || + ts.isVariableDeclaration(statement) + ) + ) + continue; + + for (const exportedSymbol of exportedSymbols) { + if (statement.name?.getText() === exportedSymbol) { + magicStr.appendLeft(statement.getStart(), 'export '); + } + } + } -/** - * Modify a TypeScript SourceFile to use export declarations instead of export specifiers. - * @param {ts.SourceFile} sourceFile - The TypeScript SourceFile to modify - * @returns {string} The modified TypeScript code - */ -function useExportDeclarations(sourceFile) { - const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); - - /** - * Process the given node, modifying it if necessary. - * @param {ts.Node} node - The TypeScript node to process - * @returns {ts.Node} The processed TypeScript node - */ - function processNode(node) { - if (ts.isExportDeclaration(node)) { - const namedExports = node.exportClause; - - if (namedExports && ts.isNamedExports(namedExports)) { - /** @type {ts.Statement[]} */ - const newNodes = []; - - namedExports.elements.forEach((exportSpecifier) => { - const exportedIdentifier = exportSpecifier.name; - const exportedName = exportedIdentifier.text; - const exportedDeclaration = sourceFile.statements.find((statement) => { - if (ts.isInterfaceDeclaration(statement) && statement.name.text === exportedName) { - return true; - } - - if (ts.isFunctionDeclaration(statement) && statement.name && statement.name.text === exportedName) { - return true; - } - - if (ts.isVariableStatement(statement)) { - return statement.declarationList.declarations.some( - (declaration) => ts.isIdentifier(declaration.name) && declaration.name.text === exportedName - ); - } - - return false; - }); - - if (exportedDeclaration) { - const newDeclaration = ts.factory.updateExportDeclaration( - exportedDeclaration, - exportedDeclaration.modifiers, -false, - - - ts.factory.createNodeArray([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], exportedDeclaration.modifiers) - ); - newNodes.push(newDeclaration as ts.Statement); - } - }); - - return ts.factory.createBlock(newNodes, true); - } - } - - return ts.visitEachChild(node, processNode, nullTransformationContext); - } - - const nullTransformationContext: ts.TransformationContext = { - enableEmitNotification: () => {}, - enableSubstitution: () => {}, - endLexicalEnvironment: () => [], - getCompilerOptions: () => ({}), - getEmitHost: () => ({}), - getEmitResolver: () => ({}), - hoistFunctionDeclaration: () => {}, - hoistVariableDeclaration: () => {}, - isEmitNotificationEnabled: () => false, - isSubstitutionEnabled: () => false, - onEmitNode: () => {}, - onSubstituteNode: () => node => node, - startLexicalEnvironment: () => {}, - }; - - const resultFile = ts.visitNode(sourceFile, processNode); - const resultCode = printer.printFile(resultFile); + magicStr.remove(exportDeclaration?.getStart() ?? 0, exportDeclaration?.getEnd() ?? 0); - return resultCode; -} \ No newline at end of file + return magicStr.toString() ?? str; +} diff --git a/sites/svelte.dev/scripts/type-gen/index.js b/sites/svelte.dev/scripts/type-gen/index.js index 21405951ec..c990750ed8 100644 --- a/sites/svelte.dev/scripts/type-gen/index.js +++ b/sites/svelte.dev/scripts/type-gen/index.js @@ -9,46 +9,6 @@ import { get_bundled_types } from './compile-types.js'; /** @type {Array<{ name: string; comment: string; exports: Extracted[]; types: Extracted[]; exempt?: boolean; }>} */ const modules = []; -/** @param {string} code */ -function findExportedDeclarations(code) { - const sourceFile = ts.createSourceFile('', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - /** @type {Map} */ - const exportedIdentifiers = new Map(); - const typeChecker = ts.createProgram([code], {}).getTypeChecker(); - - /** - * Recursively visit all nodes in the syntax tree, looking for export specifiers. - * When it finds an export specifier, it retrieves the symbol at the specifier location - * and locates the first declaration of the exported identifier. - * @param {ts.Node} node - The current TypeScript node to visit - */ - function visit(node) { - if (ts.isExportSpecifier(node)) { - const exportedIdentifier = node.name.getText(); - const symbol = typeChecker.getSymbolAtLocation(node.name); - - console.log(symbol); - - if (symbol && symbol.declarations && symbol.declarations.length > 0) { - const firstDeclaration = symbol.declarations[0]; - let parent = firstDeclaration; - while (parent && !ts.isStatement(parent)) { - parent = parent.parent; - } - if (parent) { - exportedIdentifiers.set(exportedIdentifier, parent); - } - } - } - - ts.forEachChild(node, visit); - } - - visit(sourceFile); - - return exportedIdentifiers; -} - /** * @param {string} code * @param {ts.NodeArray} statements @@ -61,22 +21,11 @@ function get_types(code, statements) { const types = []; if (statements) { - console.log(findExportedDeclarations(code)); - console.log(1); for (const statement of statements) { - if (!ts.isExportDeclaration(statement)) continue; - if (!statement.exportClause) continue; - - // console.log(statement.exportClause.elements); + const modifiers = ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : undefined; - const exports = statement - .getChildAt(1) - .getChildAt(1) - .getFullText() - .replace(/ /g, '') - .split(','); - - if (exports.length === 0) continue; + const export_modifier = modifiers?.find((modifier) => modifier.kind === 93); + if (!export_modifier) continue; if ( ts.isClassDeclaration(statement) || @@ -86,7 +35,6 @@ function get_types(code, statements) { ts.isVariableStatement(statement) || ts.isFunctionDeclaration(statement) ) { - console.log(3); const name_node = ts.isVariableStatement(statement) ? statement.declarationList.declarations[0] : statement; @@ -99,7 +47,6 @@ function get_types(code, statements) { // @ts-ignore i think typescript is bad at typescript if (statement.jsDoc) { - console.log(4); // @ts-ignore comment = statement.jsDoc[0].comment; // @ts-ignore @@ -115,9 +62,7 @@ function get_types(code, statements) { let snippet_unformatted = code.slice(start, statement.end).trim(); if (ts.isInterfaceDeclaration(statement)) { - console.log(5); if (statement.members.length > 0) { - console.log(6); for (const member of statement.members) { children.push(munge_type_element(member)); } @@ -150,8 +95,6 @@ function get_types(code, statements) { .replace(/\s*(\/\*…\*\/)\s*/g, '/*…*/') .trim(); - // console.log(ts.isVariableStatement(statement) || ts.isFunctionDeclaration(statement)); - const collection = ts.isVariableStatement(statement) || ts.isFunctionDeclaration(statement) ? exports @@ -273,17 +216,16 @@ const bundled_types = await get_bundled_types(); // }); // } -// // TODO: This is not working yet -// { -// const code = bundled_types.get('svelte/runtime') ?? ''; -// const node = ts.createSourceFile('runtime/index.d.ts', code, ts.ScriptTarget.Latest, true); +{ + const code = bundled_types.get('svelte/runtime') ?? ''; + const node = ts.createSourceFile('runtime/index.d.ts', code, ts.ScriptTarget.Latest, true); -// modules.push({ -// name: 'svelte', -// comment: '', -// ...get_types(code, node.statements) -// }); -// } + modules.push({ + name: 'svelte', + comment: '', + ...get_types(code, node.statements) + }); +} { const code = bundled_types.get('svelte/action') ?? ''; @@ -296,88 +238,86 @@ const bundled_types = await get_bundled_types(); }); } -// { -// const code = bundled_types.get('svelte/animate') ?? ''; -// const node = ts.createSourceFile( -// 'runtime/animate/index.d.ts', -// code, -// ts.ScriptTarget.Latest, -// true -// ); - -// modules.push({ -// name: 'svelte/animate', -// comment: '', -// ...get_types(code, node.statements) -// }); -// } +{ + const code = bundled_types.get('svelte/animate') ?? ''; + const node = ts.createSourceFile( + 'runtime/animate/index.d.ts', + code, + ts.ScriptTarget.Latest, + true + ); -// { -// const code = bundled_types.get('svelte/easing') ?? ''; -// const node = ts.createSourceFile('runtime/easing/index.d.ts', code, ts.ScriptTarget.Latest, true); + modules.push({ + name: 'svelte/animate', + comment: '', + ...get_types(code, node.statements) + }); +} -// console.log(node.statements); +{ + const code = bundled_types.get('svelte/easing') ?? ''; + const node = ts.createSourceFile('runtime/easing/index.d.ts', code, ts.ScriptTarget.Latest, true); -// modules.push({ -// name: 'svelte/easing', -// comment: '', -// ...get_types(code, node.statements) -// }); -// } + modules.push({ + name: 'svelte/easing', + comment: '', + ...get_types(code, node.statements) + }); +} -// { -// const code = bundled_types.get('svelte/motion') ?? ''; -// const node = ts.createSourceFile('runtime/motion/index.d.ts', code, ts.ScriptTarget.Latest, true); +{ + const code = bundled_types.get('svelte/motion') ?? ''; + const node = ts.createSourceFile('runtime/motion/index.d.ts', code, ts.ScriptTarget.Latest, true); -// modules.push({ -// name: 'svelte/motion', -// comment: '', -// ...get_types(code, node.statements) -// }); -// } + modules.push({ + name: 'svelte/motion', + comment: '', + ...get_types(code, node.statements) + }); +} -// { -// const code = bundled_types.get('svelte/store') ?? ''; -// const node = ts.createSourceFile('runtime/store/index.d.ts', code, ts.ScriptTarget.Latest, true); +{ + const code = bundled_types.get('svelte/store') ?? ''; + const node = ts.createSourceFile('runtime/store/index.d.ts', code, ts.ScriptTarget.Latest, true); -// modules.push({ -// name: 'svelte/store', -// comment: '', -// ...get_types(code, node.statements) -// }); -// } + modules.push({ + name: 'svelte/store', + comment: '', + ...get_types(code, node.statements) + }); +} -// { -// const code = bundled_types.get('svelte/transition') ?? ''; -// const node = ts.createSourceFile( -// 'runtime/transition/index.d.ts', -// code, -// ts.ScriptTarget.Latest, -// true -// ); +{ + const code = bundled_types.get('svelte/transition') ?? ''; + const node = ts.createSourceFile( + 'runtime/transition/index.d.ts', + code, + ts.ScriptTarget.Latest, + true + ); -// modules.push({ -// name: 'svelte/transition', -// comment: '', -// ...get_types(code, node.statements) -// }); -// } + modules.push({ + name: 'svelte/transition', + comment: '', + ...get_types(code, node.statements) + }); +} -// { -// const code = bundled_types.get('svelte/internal') ?? ''; -// const node = ts.createSourceFile( -// 'runtime/internal/index.d.ts', -// code, -// ts.ScriptTarget.Latest, -// true -// ); +{ + const code = bundled_types.get('svelte/internal') ?? ''; + const node = ts.createSourceFile( + 'runtime/internal/index.d.ts', + code, + ts.ScriptTarget.Latest, + true + ); -// modules.push({ -// name: 'svelte/internal', -// comment: '', -// ...get_types(code, node.statements) -// }); -// } + modules.push({ + name: 'svelte/internal', + comment: '', + ...get_types(code, node.statements) + }); +} // const dir = fileURLToPath( // new URL('../../../../packages/kit/types/synthetic', import.meta.url).href @@ -396,27 +336,27 @@ const bundled_types = await get_bundled_types(); // }); // } -// { -// const code = read_d_ts_file('types/ambient.d.ts'); -// const node = ts.createSourceFile('ambient.d.ts', code, ts.ScriptTarget.Latest, true); - -// for (const statement of node.statements) { -// if (ts.isModuleDeclaration(statement)) { -// // @ts-ignore -// const name = statement.name.text || statement.name.escapedText; - -// // @ts-ignore -// const comment = strip_origin(statement.jsDoc?.[0].comment ?? ''); - -// modules.push({ -// name, -// comment, -// // @ts-ignore -// ...get_types(code, statement.body?.statements) -// }); -// } -// } -// } +{ + const code = read_d_ts_file('types/ambient.d.ts'); + const node = ts.createSourceFile('ambient.d.ts', code, ts.ScriptTarget.Latest, true); + + for (const statement of node.statements) { + if (ts.isModuleDeclaration(statement)) { + // @ts-ignore + const name = statement.name.text || statement.name.escapedText; + + // @ts-ignore + const comment = strip_origin(statement.jsDoc?.[0].comment ?? ''); + + modules.push({ + name, + comment, + // @ts-ignore + ...get_types(code, statement.body?.statements) + }); + } + } +} modules.sort((a, b) => (a.name < b.name ? -1 : 1));