Generate type declarations with `dts-buddy` (#8702)

* use dts-buddy

* remove debug output

* remove existing type generation script

* fix package.json

* update gitignore

* bump dts-buddy

* remove unused action entry point

* add svelte/compiler and svelte/types/compiler/preprocess modules

* bump dts-buddy

* annoying

* changeset

* bump dts-buddy

* get rid of .d.ts files

* another one

* Update packages/svelte/package.json

Co-authored-by: gtmnayan <50981692+gtm-nayan@users.noreply.github.com>

---------

Co-authored-by: Rich Harris <git@rich-harris.dev>
Co-authored-by: gtmnayan <50981692+gtm-nayan@users.noreply.github.com>
pull/8710/head
Rich Harris 1 year ago committed by GitHub
parent 15bdadb2ae
commit 1046daba6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
Generate type declarations with dts-buddy

@ -1,15 +1,6 @@
*.map *.map
/src/compiler/compile/internal_exports.js /src/compiler/compile/internal_exports.js
/compiler.d.ts
/compiler.cjs /compiler.cjs
/index.d.ts
/action.d.ts
/internal.d.ts
/store.d.ts
/easing.d.ts
/motion.d.ts
/transition.d.ts
/animate.d.ts
/scratch/ /scratch/
/test/*/samples/_ /test/*/samples/_
/test/runtime/shards /test/runtime/shards

@ -1,162 +0,0 @@
// This script generates the TypeScript definitions
import { execSync } from 'child_process';
import { readFileSync, writeFileSync, readdirSync, existsSync, copyFileSync, statSync } from 'fs';
execSync('tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly', { stdio: 'inherit' });
function modify(path, modifyFn) {
const content = readFileSync(path, 'utf8');
writeFileSync(path, modifyFn(content));
}
function adjust(input) {
// Remove typedef jsdoc (duplicated in the type definition)
input = input.replace(/\/\*\*\n(\r)? \* @typedef .+?\*\//gs, '');
input = input.replace(/\/\*\* @typedef .+?\*\//gs, '');
// Extract the import paths and types
const import_regex = /import\(("|')(.+?)("|')\)\.(\w+)/g;
let import_match;
const import_map = new Map();
while ((import_match = import_regex.exec(input)) !== null) {
const imports = import_map.get(import_match[2]) || new Map();
let name = import_match[4];
if ([...imports.keys()].includes(name)) continue;
let i = 1;
if (name === 'default') {
name = import_match[2].split('/').pop().split('.').shift().replace(/[^a-z0-9]/gi, '_');
}
while ([...import_map].some(([path, names]) => path !== import_match[2] && names.has(name))) {
name = `${name}${i++}`;
}
imports.set(import_match[4], name);
import_map.set(import_match[2], imports);
}
// Replace inline imports with their type names
const transformed = input.replace(import_regex, (_match, _quote, path, _quote2, name) => {
return import_map.get(path).get(name);
});
// Remove/adjust @template, @param and @returns lines
// TODO rethink if we really need to do this for @param and @returns, doesn't show up in hover so unnecessary
const lines = transformed.split("\n");
let filtered_lines = [];
let removing = null;
let openCount = 1;
let closedCount = 0;
for (let line of lines) {
let start_removing = false;
if (line.trim().startsWith("* @template")) {
removing = "template";
start_removing = true;
}
if (line.trim().startsWith("* @param {")) {
openCount = 1;
closedCount = 0;
removing = "param";
start_removing = true;
}
if (line.trim().startsWith("* @returns {")) {
openCount = 1;
closedCount = 0;
removing = "returns";
start_removing = true;
}
if (removing === "returns" || removing === "param") {
let i = start_removing ? line.indexOf('{') + 1 : 0;
for (; i < line.length; i++) {
if (line[i] === "{") openCount++;
if (line[i] === "}") closedCount++;
if (openCount === closedCount) break;
}
if (openCount === closedCount) {
line = start_removing ? (line.slice(0, line.indexOf('{')) + line.slice(i + 1)) : (` * @${removing} ` + line.slice(i + 1));
removing = null;
}
}
if (removing && !start_removing && (line.trim().startsWith("* @") || line.trim().startsWith("*/"))) {
removing = null;
}
if (!removing) {
filtered_lines.push(line);
}
}
// Replace generic type names with their plain versions
const renamed_generics = filtered_lines.map(line => {
return line.replace(/(\W|\s)([A-Z][\w\d$]*)_\d+(\W|\s)/g, "$1$2$3");
});
// Generate the import statement for the types used
const import_statements = Array.from(import_map.entries())
.map(([path, names]) => {
const default_name = names.get('default');
names.delete('default');
const default_import = default_name ? (default_name + (names.size ? ', ' : ' ')) : '';
const named_imports = names.size ? `{ ${[...names.values()].join(', ')} } ` : '';
return `import ${default_import}${named_imports}from '${path}';`
})
.join("\n");
return [import_statements, ...renamed_generics].join("\n");
}
let did_replace = false;
function walk(dir) {
const files = readdirSync(dir);
const _dir = dir.slice('types/'.length)
for (const file of files) {
const path = `${dir}/${file}`;
if (file.endsWith('.d.ts')) {
modify(path, content => {
content = adjust(content);
if (file === 'index.d.ts' && existsSync(`src/${_dir}/public.d.ts`)) {
copyFileSync(`src/${_dir}/public.d.ts`, `${dir}/public.d.ts`);
content = "export * from './public.js';\n" + content;
}
if (file === 'Component.d.ts' && dir.includes('runtime')) {
if (!content.includes('$set(props: Partial<Props>): void;\n}')) {
throw new Error('Component.js was modified in a way that automatic patching of d.ts file no longer works. Please adjust it');
} else {
content = content.replace('$set(props: Partial<Props>): void;\n}', '$set(props: Partial<Props>): void;\n [accessor:string]: any;\n}');
did_replace = true;
}
}
return content;
});
} else if (statSync(path).isDirectory()) {
if (existsSync(`src/${_dir}/${file}/private.d.ts`)) {
copyFileSync(`src/${_dir}/${file}/private.d.ts`, `${path}/private.d.ts`);
}
if (existsSync(`src/${_dir}/${file}/interfaces.d.ts`)) {
copyFileSync(`src/${_dir}/${file}/interfaces.d.ts`, `${path}/interfaces.d.ts`);
}
walk(path);
}
}
}
walk('types');
if (!did_replace) {
throw new Error('Component.js file in runtime does no longer exist so that automatic patching of the d.ts file no longer works. Please adjust it');
}
copyFileSync(`src/runtime/ambient.d.ts`, `types/runtime/ambient.d.ts`);

@ -24,42 +24,42 @@
"exports": { "exports": {
"./package.json": "./package.json", "./package.json": "./package.json",
".": { ".": {
"types": "./types/runtime/index.d.ts", "types": "./types/index.d.ts",
"browser": { "browser": {
"import": "./src/runtime/index.js" "import": "./src/runtime/index.js"
}, },
"import": "./src/runtime/ssr.js" "import": "./src/runtime/ssr.js"
}, },
"./compiler": { "./compiler": {
"types": "./types/compiler/index.d.ts", "types": "./types/index.d.ts",
"import": "./src/compiler/index.js", "import": "./src/compiler/index.js",
"require": "./compiler.cjs" "require": "./compiler.cjs"
}, },
"./action": { "./action": {
"types": "./types/runtime/action/index.d.ts" "types": "./types/index.d.ts"
}, },
"./animate": { "./animate": {
"types": "./types/runtime/animate/index.d.ts", "types": "./types/index.d.ts",
"import": "./src/runtime/animate/index.js" "import": "./src/runtime/animate/index.js"
}, },
"./easing": { "./easing": {
"types": "./types/runtime/easing/index.d.ts", "types": "./types/index.d.ts",
"import": "./src/runtime/easing/index.js" "import": "./src/runtime/easing/index.js"
}, },
"./internal": { "./internal": {
"types": "./types/runtime/internal/index.d.ts", "types": "./types/index.d.ts",
"import": "./src/runtime/internal/index.js" "import": "./src/runtime/internal/index.js"
}, },
"./motion": { "./motion": {
"types": "./types/runtime/motion/index.d.ts", "types": "./types/index.d.ts",
"import": "./src/runtime/motion/index.js" "import": "./src/runtime/motion/index.js"
}, },
"./store": { "./store": {
"types": "./types/runtime/store/index.d.ts", "types": "./types/index.d.ts",
"import": "./src/runtime/store/index.js" "import": "./src/runtime/store/index.js"
}, },
"./transition": { "./transition": {
"types": "./types/runtime/transition/index.d.ts", "types": "./types/index.d.ts",
"import": "./src/runtime/transition/index.js" "import": "./src/runtime/transition/index.js"
}, },
"./elements": { "./elements": {
@ -69,18 +69,18 @@
"engines": { "engines": {
"node": ">=16" "node": ">=16"
}, },
"types": "types/runtime/index.d.ts", "types": "types/index.d.ts",
"scripts": { "scripts": {
"format": "prettier . --cache --plugin-search-dir=. --write", "format": "prettier . --cache --plugin-search-dir=. --write",
"check": "prettier . --cache --plugin-search-dir=. --check", "check": "prettier . --cache --plugin-search-dir=. --check",
"test": "vitest run && echo \"manually check that there are no type errors in test/types by opening the files in there\"", "test": "vitest run && echo \"manually check that there are no type errors in test/types by opening the files in there\"",
"build": "rollup -c && pnpm tsd", "build": "rollup -c && pnpm types",
"prepare": "pnpm build", "prepare": "pnpm build",
"generate:version": "node ./scripts/generate-version.js", "generate:version": "node ./scripts/generate-version.js",
"dev": "rollup -cw", "dev": "rollup -cw",
"posttest": "agadoo src/internal/index.js", "posttest": "agadoo src/internal/index.js",
"prepublishOnly": "pnpm lint && pnpm build && pnpm test", "prepublishOnly": "pnpm lint && pnpm build && pnpm test",
"tsd": "node ./generate-types.js", "types": "node ./scripts/generate-dts.js",
"lint": "eslint \"{src,test}/**/*.{ts,js}\" --cache" "lint": "eslint \"{src,test}/**/*.{ts,js}\" --cache"
}, },
"repository": { "repository": {
@ -126,6 +126,7 @@
"@types/node": "^14.14.31", "@types/node": "^14.14.31",
"@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/eslint-plugin": "^5.58.0",
"agadoo": "^3.0.0", "agadoo": "^3.0.0",
"dts-buddy": "^0.1.2",
"esbuild": "^0.17.19", "esbuild": "^0.17.19",
"happy-dom": "^9.18.3", "happy-dom": "^9.18.3",
"jsdom": "^21.1.1", "jsdom": "^21.1.1",

@ -17,23 +17,6 @@ fs.writeFileSync(
`export default new Set(${JSON.stringify(Object.keys(internal))});` `export default new Set(${JSON.stringify(Object.keys(internal))});`
); );
// Generate d.ts files for runtime entrypoints so that TS can find it also without `"moduleResolution": "bundler"`
fs.readdirSync('src/runtime', { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.forEach((dirent) =>
fs.writeFileSync(
`${dirent.name}.d.ts`,
`export * from './types/runtime/${dirent.name}/index.js';`
)
);
fs.writeFileSync('./index.d.ts', `export * from './types/runtime/index.js';`);
fs.writeFileSync(
'./compiler.d.ts',
`export { compile, parse, preprocess, walk, VERSION } from './types/compiler/index.js';`
);
/** /**
* @type {import("rollup").RollupOptions[]} * @type {import("rollup").RollupOptions[]}
*/ */

@ -0,0 +1,16 @@
import { createBundle } from 'dts-buddy';
await createBundle({
output: 'types/index.d.ts',
modules: {
svelte: 'src/runtime/public.d.ts',
'svelte/compiler': 'src/compiler/public.d.ts',
'svelte/types/compiler/preprocess': 'src/compiler/preprocess/public.d.ts',
'svelte/action': 'src/runtime/action/public.d.ts',
'svelte/animate': 'src/runtime/animate/public.d.ts',
'svelte/easing': 'src/runtime/easing/index.js',
'svelte/motion': 'src/runtime/motion/public.d.ts',
'svelte/store': 'src/runtime/store/public.d.ts',
'svelte/transition': 'src/runtime/transition/public.d.ts'
}
});

@ -1,2 +1,3 @@
export { CompileOptions, EnableSourcemap, CssHashGetter } from './interfaces'; export { CompileOptions, EnableSourcemap, CssHashGetter } from './interfaces';
export * from './preprocess/public.js'; export * from './preprocess/public.js';
export * from './index.js';

@ -12,3 +12,5 @@ export interface FlipParams {
duration?: number | ((len: number) => number); duration?: number | ((len: number) => number);
easing?: (t: number) => number; easing?: (t: number) => number;
} }
export * from './index.js';

@ -91,3 +91,5 @@ export interface EventDispatcher<EventMap extends Record<string, any>> {
: [type: Type, parameter: EventMap[Type], options?: DispatchOptions] : [type: Type, parameter: EventMap[Type], options?: DispatchOptions]
): boolean; ): boolean;
} }
export * from './index.js';

@ -13,3 +13,5 @@ export interface Tweened<T> extends Readable<T> {
set(value: T, opts?: TweenedOptions<T>): Promise<void>; set(value: T, opts?: TweenedOptions<T>): Promise<void>;
update(updater: Updater<T>, opts?: TweenedOptions<T>): Promise<void>; update(updater: Updater<T>, opts?: TweenedOptions<T>): Promise<void>;
} }
export * from './index.js';

@ -5,3 +5,5 @@ export type {
ComponentProps, ComponentProps,
ComponentEvents ComponentEvents
} from './internal/public.js'; } from './internal/public.js';
export * from './index.js';

@ -47,3 +47,5 @@ export interface Writable<T> extends Readable<T> {
*/ */
update(this: void, updater: Updater<T>): void; update(this: void, updater: Updater<T>): void;
} }
export * from './index.js';

@ -58,3 +58,5 @@ export interface CrossfadeParams {
duration?: number | ((len: number) => number); duration?: number | ((len: number) => number);
easing?: EasingFunction; easing?: EasingFunction;
} }
export * from './index.js';

@ -120,6 +120,9 @@ importers:
agadoo: agadoo:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
dts-buddy:
specifier: ^0.1.2
version: 0.1.2
esbuild: esbuild:
specifier: ^0.17.19 specifier: ^0.17.19
version: 0.17.19 version: 0.17.19
@ -644,21 +647,24 @@ packages:
'@jridgewell/set-array': 1.1.2 '@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping': 0.3.18 '@jridgewell/trace-mapping': 0.3.18
dev: false
/@jridgewell/resolve-uri@3.1.0: /@jridgewell/resolve-uri@3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/set-array@1.1.2: /@jridgewell/set-array@1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/source-map@0.3.3:
resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==}
dependencies:
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.18
dev: true
/@jridgewell/sourcemap-codec@1.4.14: /@jridgewell/sourcemap-codec@1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: false
/@jridgewell/sourcemap-codec@1.4.15: /@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
@ -668,7 +674,6 @@ packages:
dependencies: dependencies:
'@jridgewell/resolve-uri': 3.1.0 '@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14 '@jridgewell/sourcemap-codec': 1.4.14
dev: false
/@manypkg/find-root@1.1.0: /@manypkg/find-root@1.1.0:
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
@ -1666,6 +1671,21 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true dev: true
/dts-buddy@0.1.2:
resolution: {integrity: sha512-CLDbDXtcrNjuWLYljJuCL4l//mvDZzjtFkmr4yGyCAk58szuzmjzoWKG+7NBFWeeajOiISu/IG96QNMb0CPtdw==}
hasBin: true
dependencies:
'@jridgewell/source-map': 0.3.3
'@jridgewell/sourcemap-codec': 1.4.15
globrex: 0.1.2
kleur: 4.1.5
locate-character: 2.0.5
magic-string: 0.30.0
sade: 1.8.1
tiny-glob: 0.2.9
typescript: 5.0.4
dev: true
/emoji-regex@8.0.0: /emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true dev: true
@ -2900,7 +2920,6 @@ packages:
/locate-character@2.0.5: /locate-character@2.0.5:
resolution: {integrity: sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==} resolution: {integrity: sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==}
dev: false
/locate-path@5.0.0: /locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
@ -3075,6 +3094,11 @@ packages:
ufo: 1.1.2 ufo: 1.1.2
dev: true dev: true
/mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
dev: true
/ms@2.1.2: /ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true dev: true
@ -3608,6 +3632,13 @@ packages:
queue-microtask: 1.2.3 queue-microtask: 1.2.3
dev: true dev: true
/sade@1.8.1:
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
engines: {node: '>=6'}
dependencies:
mri: 1.2.0
dev: true
/safe-regex-test@1.0.0: /safe-regex-test@1.0.0:
resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
dependencies: dependencies:

Loading…
Cancel
Save