Push working type generation code

pull/8452/head
Puru Vijay 3 years ago
parent 78fb9cd900
commit 145b1e3777

@ -11,3 +11,4 @@
/src/routes/_components/Supporters/donors.js /src/routes/_components/Supporters/donors.js
.vercel .vercel
examples-data.js examples-data.js
type-info.js

@ -1,8 +1,9 @@
// @ts-check // @ts-check
import MagicString from 'magic-string';
import fs from 'node:fs';
import { rollup } from 'rollup'; import { rollup } from 'rollup';
import dts from 'rollup-plugin-dts'; 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() { export async function get_bundled_types() {
const dtsSources = fs.readdirSync(new URL('./dts-sources', import.meta.url)); const dtsSources = fs.readdirSync(new URL('./dts-sources', import.meta.url));
@ -19,96 +20,58 @@ export async function get_bundled_types() {
codes.set( codes.set(
(file === 'index.d.ts' ? 'svelte' : `svelte/${file}`).replace('.d.ts', ''), (file === 'index.d.ts' ? 'svelte' : `svelte/${file}`).replace('.d.ts', ''),
useExportDeclarations(
await bundle.generate({ format: 'esm' }).then(({ output }) => output[0].code) await bundle.generate({ format: 'esm' }).then(({ output }) => output[0].code)
)
); );
} }
console.log(codes.get('svelte/action'));
return codes; return codes;
} }
/** @param {string} str */
function useExportDeclarations(str) {
const magicStr = new MagicString(str);
/** const sourceFile = ts.createSourceFile(
* Modify a TypeScript SourceFile to use export declarations instead of export specifiers. 'index.d.ts',
* @param {ts.SourceFile} sourceFile - The TypeScript SourceFile to modify str,
* @returns {string} The modified TypeScript code ts.ScriptTarget.ESNext,
*/ true,
function useExportDeclarations(sourceFile) { ts.ScriptKind.TS
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; // 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 (exportedDeclaration) { );
const newDeclaration = ts.factory.updateExportDeclaration(
exportedDeclaration,
exportedDeclaration.modifiers,
false,
if (exportDeclaration && !ts.isExportDeclaration(exportDeclaration)) return str;
ts.factory.createNodeArray([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], exportedDeclaration.modifiers) // @ts-ignore Why does TS not identify `elements`
const exportedSymbols = exportDeclaration?.exportClause?.elements.map(
(element) => element.name.text
); );
newNodes.push(newDeclaration as ts.Statement);
}
});
return ts.factory.createBlock(newNodes, true); 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 ');
} }
} }
return ts.visitEachChild(node, processNode, nullTransformationContext);
} }
const nullTransformationContext: ts.TransformationContext = { magicStr.remove(exportDeclaration?.getStart() ?? 0, exportDeclaration?.getEnd() ?? 0);
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);
return resultCode; return magicStr.toString() ?? str;
} }

@ -9,46 +9,6 @@ import { get_bundled_types } from './compile-types.js';
/** @type {Array<{ name: string; comment: string; exports: Extracted[]; types: Extracted[]; exempt?: boolean; }>} */ /** @type {Array<{ name: string; comment: string; exports: Extracted[]; types: Extracted[]; exempt?: boolean; }>} */
const modules = []; const modules = [];
/** @param {string} code */
function findExportedDeclarations(code) {
const sourceFile = ts.createSourceFile('', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
/** @type {Map<string, ts.Statement>} */
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 {string} code
* @param {ts.NodeArray<ts.Statement>} statements * @param {ts.NodeArray<ts.Statement>} statements
@ -61,22 +21,11 @@ function get_types(code, statements) {
const types = []; const types = [];
if (statements) { if (statements) {
console.log(findExportedDeclarations(code));
console.log(1);
for (const statement of statements) { for (const statement of statements) {
if (!ts.isExportDeclaration(statement)) continue; const modifiers = ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : undefined;
if (!statement.exportClause) continue;
// console.log(statement.exportClause.elements);
const exports = statement const export_modifier = modifiers?.find((modifier) => modifier.kind === 93);
.getChildAt(1) if (!export_modifier) continue;
.getChildAt(1)
.getFullText()
.replace(/ /g, '')
.split(',');
if (exports.length === 0) continue;
if ( if (
ts.isClassDeclaration(statement) || ts.isClassDeclaration(statement) ||
@ -86,7 +35,6 @@ function get_types(code, statements) {
ts.isVariableStatement(statement) || ts.isVariableStatement(statement) ||
ts.isFunctionDeclaration(statement) ts.isFunctionDeclaration(statement)
) { ) {
console.log(3);
const name_node = ts.isVariableStatement(statement) const name_node = ts.isVariableStatement(statement)
? statement.declarationList.declarations[0] ? statement.declarationList.declarations[0]
: statement; : statement;
@ -99,7 +47,6 @@ function get_types(code, statements) {
// @ts-ignore i think typescript is bad at typescript // @ts-ignore i think typescript is bad at typescript
if (statement.jsDoc) { if (statement.jsDoc) {
console.log(4);
// @ts-ignore // @ts-ignore
comment = statement.jsDoc[0].comment; comment = statement.jsDoc[0].comment;
// @ts-ignore // @ts-ignore
@ -115,9 +62,7 @@ function get_types(code, statements) {
let snippet_unformatted = code.slice(start, statement.end).trim(); let snippet_unformatted = code.slice(start, statement.end).trim();
if (ts.isInterfaceDeclaration(statement)) { if (ts.isInterfaceDeclaration(statement)) {
console.log(5);
if (statement.members.length > 0) { if (statement.members.length > 0) {
console.log(6);
for (const member of statement.members) { for (const member of statement.members) {
children.push(munge_type_element(member)); children.push(munge_type_element(member));
} }
@ -150,8 +95,6 @@ function get_types(code, statements) {
.replace(/\s*(\/\*…\*\/)\s*/g, '/*…*/') .replace(/\s*(\/\*…\*\/)\s*/g, '/*…*/')
.trim(); .trim();
// console.log(ts.isVariableStatement(statement) || ts.isFunctionDeclaration(statement));
const collection = const collection =
ts.isVariableStatement(statement) || ts.isFunctionDeclaration(statement) ts.isVariableStatement(statement) || ts.isFunctionDeclaration(statement)
? exports ? 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 code = bundled_types.get('svelte/runtime') ?? ''; const node = ts.createSourceFile('runtime/index.d.ts', code, ts.ScriptTarget.Latest, true);
// const node = ts.createSourceFile('runtime/index.d.ts', code, ts.ScriptTarget.Latest, true);
// modules.push({ modules.push({
// name: 'svelte', name: 'svelte',
// comment: '', comment: '',
// ...get_types(code, node.statements) ...get_types(code, node.statements)
// }); });
// } }
{ {
const code = bundled_types.get('svelte/action') ?? ''; 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 code = bundled_types.get('svelte/animate') ?? '';
// const node = ts.createSourceFile( const node = ts.createSourceFile(
// 'runtime/animate/index.d.ts', 'runtime/animate/index.d.ts',
// code, code,
// ts.ScriptTarget.Latest, ts.ScriptTarget.Latest,
// true true
// ); );
// modules.push({
// name: 'svelte/animate',
// comment: '',
// ...get_types(code, node.statements)
// });
// }
// { modules.push({
// const code = bundled_types.get('svelte/easing') ?? ''; name: 'svelte/animate',
// const node = ts.createSourceFile('runtime/easing/index.d.ts', code, ts.ScriptTarget.Latest, true); 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({ modules.push({
// name: 'svelte/easing', name: 'svelte/easing',
// comment: '', comment: '',
// ...get_types(code, node.statements) ...get_types(code, node.statements)
// }); });
// } }
// { {
// const code = bundled_types.get('svelte/motion') ?? ''; const code = bundled_types.get('svelte/motion') ?? '';
// const node = ts.createSourceFile('runtime/motion/index.d.ts', code, ts.ScriptTarget.Latest, true); const node = ts.createSourceFile('runtime/motion/index.d.ts', code, ts.ScriptTarget.Latest, true);
// modules.push({ modules.push({
// name: 'svelte/motion', name: 'svelte/motion',
// comment: '', comment: '',
// ...get_types(code, node.statements) ...get_types(code, node.statements)
// }); });
// } }
// { {
// const code = bundled_types.get('svelte/store') ?? ''; const code = bundled_types.get('svelte/store') ?? '';
// const node = ts.createSourceFile('runtime/store/index.d.ts', code, ts.ScriptTarget.Latest, true); const node = ts.createSourceFile('runtime/store/index.d.ts', code, ts.ScriptTarget.Latest, true);
// modules.push({ modules.push({
// name: 'svelte/store', name: 'svelte/store',
// comment: '', comment: '',
// ...get_types(code, node.statements) ...get_types(code, node.statements)
// }); });
// } }
// { {
// const code = bundled_types.get('svelte/transition') ?? ''; const code = bundled_types.get('svelte/transition') ?? '';
// const node = ts.createSourceFile( const node = ts.createSourceFile(
// 'runtime/transition/index.d.ts', 'runtime/transition/index.d.ts',
// code, code,
// ts.ScriptTarget.Latest, ts.ScriptTarget.Latest,
// true true
// ); );
// modules.push({ modules.push({
// name: 'svelte/transition', name: 'svelte/transition',
// comment: '', comment: '',
// ...get_types(code, node.statements) ...get_types(code, node.statements)
// }); });
// } }
// { {
// const code = bundled_types.get('svelte/internal') ?? ''; const code = bundled_types.get('svelte/internal') ?? '';
// const node = ts.createSourceFile( const node = ts.createSourceFile(
// 'runtime/internal/index.d.ts', 'runtime/internal/index.d.ts',
// code, code,
// ts.ScriptTarget.Latest, ts.ScriptTarget.Latest,
// true true
// ); );
// modules.push({ modules.push({
// name: 'svelte/internal', name: 'svelte/internal',
// comment: '', comment: '',
// ...get_types(code, node.statements) ...get_types(code, node.statements)
// }); });
// } }
// const dir = fileURLToPath( // const dir = fileURLToPath(
// new URL('../../../../packages/kit/types/synthetic', import.meta.url).href // 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 code = read_d_ts_file('types/ambient.d.ts');
// const node = ts.createSourceFile('ambient.d.ts', code, ts.ScriptTarget.Latest, true); const node = ts.createSourceFile('ambient.d.ts', code, ts.ScriptTarget.Latest, true);
// for (const statement of node.statements) { for (const statement of node.statements) {
// if (ts.isModuleDeclaration(statement)) { if (ts.isModuleDeclaration(statement)) {
// // @ts-ignore // @ts-ignore
// const name = statement.name.text || statement.name.escapedText; const name = statement.name.text || statement.name.escapedText;
// // @ts-ignore // @ts-ignore
// const comment = strip_origin(statement.jsDoc?.[0].comment ?? ''); const comment = strip_origin(statement.jsDoc?.[0].comment ?? '');
// modules.push({ modules.push({
// name, name,
// comment, comment,
// // @ts-ignore // @ts-ignore
// ...get_types(code, statement.body?.statements) ...get_types(code, statement.body?.statements)
// }); });
// } }
// } }
// } }
modules.sort((a, b) => (a.name < b.name ? -1 : 1)); modules.sort((a, b) => (a.name < b.name ? -1 : 1));

Loading…
Cancel
Save