From 1046daba6a9c9b51ef4f25ed6c8f9fba59a92946 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 8 Jun 2023 09:48:11 -0400 Subject: [PATCH] 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 Co-authored-by: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> --- .changeset/green-sheep-learn.md | 5 + packages/svelte/.gitignore | 9 - packages/svelte/generate-types.js | 162 ------------------ packages/svelte/package.json | 25 +-- packages/svelte/rollup.config.js | 17 -- packages/svelte/scripts/generate-dts.js | 16 ++ packages/svelte/src/compiler/public.d.ts | 1 + packages/svelte/src/runtime/action/index.js | 1 - .../svelte/src/runtime/animate/public.d.ts | 2 + .../svelte/src/runtime/internal/public.d.ts | 2 + .../svelte/src/runtime/motion/public.d.ts | 2 + packages/svelte/src/runtime/public.d.ts | 2 + packages/svelte/src/runtime/store/public.d.ts | 2 + .../svelte/src/runtime/transition/public.d.ts | 2 + pnpm-lock.yaml | 43 ++++- 15 files changed, 84 insertions(+), 207 deletions(-) create mode 100644 .changeset/green-sheep-learn.md delete mode 100644 packages/svelte/generate-types.js create mode 100644 packages/svelte/scripts/generate-dts.js delete mode 100644 packages/svelte/src/runtime/action/index.js diff --git a/.changeset/green-sheep-learn.md b/.changeset/green-sheep-learn.md new file mode 100644 index 0000000000..b600474144 --- /dev/null +++ b/.changeset/green-sheep-learn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +Generate type declarations with dts-buddy diff --git a/packages/svelte/.gitignore b/packages/svelte/.gitignore index 4b6e382652..13a9e40999 100644 --- a/packages/svelte/.gitignore +++ b/packages/svelte/.gitignore @@ -1,15 +1,6 @@ *.map /src/compiler/compile/internal_exports.js -/compiler.d.ts /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/ /test/*/samples/_ /test/runtime/shards diff --git a/packages/svelte/generate-types.js b/packages/svelte/generate-types.js deleted file mode 100644 index 707bf746fd..0000000000 --- a/packages/svelte/generate-types.js +++ /dev/null @@ -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): 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): void;\n}', '$set(props: Partial): 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`); diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ade672ce35..7267a91afb 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -24,42 +24,42 @@ "exports": { "./package.json": "./package.json", ".": { - "types": "./types/runtime/index.d.ts", + "types": "./types/index.d.ts", "browser": { "import": "./src/runtime/index.js" }, "import": "./src/runtime/ssr.js" }, "./compiler": { - "types": "./types/compiler/index.d.ts", + "types": "./types/index.d.ts", "import": "./src/compiler/index.js", "require": "./compiler.cjs" }, "./action": { - "types": "./types/runtime/action/index.d.ts" + "types": "./types/index.d.ts" }, "./animate": { - "types": "./types/runtime/animate/index.d.ts", + "types": "./types/index.d.ts", "import": "./src/runtime/animate/index.js" }, "./easing": { - "types": "./types/runtime/easing/index.d.ts", + "types": "./types/index.d.ts", "import": "./src/runtime/easing/index.js" }, "./internal": { - "types": "./types/runtime/internal/index.d.ts", + "types": "./types/index.d.ts", "import": "./src/runtime/internal/index.js" }, "./motion": { - "types": "./types/runtime/motion/index.d.ts", + "types": "./types/index.d.ts", "import": "./src/runtime/motion/index.js" }, "./store": { - "types": "./types/runtime/store/index.d.ts", + "types": "./types/index.d.ts", "import": "./src/runtime/store/index.js" }, "./transition": { - "types": "./types/runtime/transition/index.d.ts", + "types": "./types/index.d.ts", "import": "./src/runtime/transition/index.js" }, "./elements": { @@ -69,18 +69,18 @@ "engines": { "node": ">=16" }, - "types": "types/runtime/index.d.ts", + "types": "types/index.d.ts", "scripts": { "format": "prettier . --cache --plugin-search-dir=. --write", "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\"", - "build": "rollup -c && pnpm tsd", + "build": "rollup -c && pnpm types", "prepare": "pnpm build", "generate:version": "node ./scripts/generate-version.js", "dev": "rollup -cw", "posttest": "agadoo src/internal/index.js", "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" }, "repository": { @@ -126,6 +126,7 @@ "@types/node": "^14.14.31", "@typescript-eslint/eslint-plugin": "^5.58.0", "agadoo": "^3.0.0", + "dts-buddy": "^0.1.2", "esbuild": "^0.17.19", "happy-dom": "^9.18.3", "jsdom": "^21.1.1", diff --git a/packages/svelte/rollup.config.js b/packages/svelte/rollup.config.js index 927f8e9495..1bba3c607e 100644 --- a/packages/svelte/rollup.config.js +++ b/packages/svelte/rollup.config.js @@ -17,23 +17,6 @@ fs.writeFileSync( `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[]} */ diff --git a/packages/svelte/scripts/generate-dts.js b/packages/svelte/scripts/generate-dts.js new file mode 100644 index 0000000000..2139118de2 --- /dev/null +++ b/packages/svelte/scripts/generate-dts.js @@ -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' + } +}); diff --git a/packages/svelte/src/compiler/public.d.ts b/packages/svelte/src/compiler/public.d.ts index 32d712afa2..202582789d 100644 --- a/packages/svelte/src/compiler/public.d.ts +++ b/packages/svelte/src/compiler/public.d.ts @@ -1,2 +1,3 @@ export { CompileOptions, EnableSourcemap, CssHashGetter } from './interfaces'; export * from './preprocess/public.js'; +export * from './index.js'; diff --git a/packages/svelte/src/runtime/action/index.js b/packages/svelte/src/runtime/action/index.js deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/packages/svelte/src/runtime/action/index.js +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/packages/svelte/src/runtime/animate/public.d.ts b/packages/svelte/src/runtime/animate/public.d.ts index 8c8e5565ea..8a6cf5ebf6 100644 --- a/packages/svelte/src/runtime/animate/public.d.ts +++ b/packages/svelte/src/runtime/animate/public.d.ts @@ -12,3 +12,5 @@ export interface FlipParams { duration?: number | ((len: number) => number); easing?: (t: number) => number; } + +export * from './index.js'; diff --git a/packages/svelte/src/runtime/internal/public.d.ts b/packages/svelte/src/runtime/internal/public.d.ts index 6eab766077..64a9a04675 100644 --- a/packages/svelte/src/runtime/internal/public.d.ts +++ b/packages/svelte/src/runtime/internal/public.d.ts @@ -91,3 +91,5 @@ export interface EventDispatcher> { : [type: Type, parameter: EventMap[Type], options?: DispatchOptions] ): boolean; } + +export * from './index.js'; diff --git a/packages/svelte/src/runtime/motion/public.d.ts b/packages/svelte/src/runtime/motion/public.d.ts index 372d31636f..bedbac8d22 100644 --- a/packages/svelte/src/runtime/motion/public.d.ts +++ b/packages/svelte/src/runtime/motion/public.d.ts @@ -13,3 +13,5 @@ export interface Tweened extends Readable { set(value: T, opts?: TweenedOptions): Promise; update(updater: Updater, opts?: TweenedOptions): Promise; } + +export * from './index.js'; diff --git a/packages/svelte/src/runtime/public.d.ts b/packages/svelte/src/runtime/public.d.ts index 8cfa2e6bf8..3024ba3a38 100644 --- a/packages/svelte/src/runtime/public.d.ts +++ b/packages/svelte/src/runtime/public.d.ts @@ -5,3 +5,5 @@ export type { ComponentProps, ComponentEvents } from './internal/public.js'; + +export * from './index.js'; diff --git a/packages/svelte/src/runtime/store/public.d.ts b/packages/svelte/src/runtime/store/public.d.ts index b7368137ac..9655a15674 100644 --- a/packages/svelte/src/runtime/store/public.d.ts +++ b/packages/svelte/src/runtime/store/public.d.ts @@ -47,3 +47,5 @@ export interface Writable extends Readable { */ update(this: void, updater: Updater): void; } + +export * from './index.js'; diff --git a/packages/svelte/src/runtime/transition/public.d.ts b/packages/svelte/src/runtime/transition/public.d.ts index 0b158f9c2e..454840ad79 100644 --- a/packages/svelte/src/runtime/transition/public.d.ts +++ b/packages/svelte/src/runtime/transition/public.d.ts @@ -58,3 +58,5 @@ export interface CrossfadeParams { duration?: number | ((len: number) => number); easing?: EasingFunction; } + +export * from './index.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbec4fcac6..e85c9eecf5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -120,6 +120,9 @@ importers: agadoo: specifier: ^3.0.0 version: 3.0.0 + dts-buddy: + specifier: ^0.1.2 + version: 0.1.2 esbuild: specifier: ^0.17.19 version: 0.17.19 @@ -644,21 +647,24 @@ packages: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.18 - dev: false /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} - dev: false /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 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: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - dev: false /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} @@ -668,7 +674,6 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 - dev: false /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -1666,6 +1671,21 @@ packages: engines: {node: '>=12'} 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: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -2900,7 +2920,6 @@ packages: /locate-character@2.0.5: resolution: {integrity: sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==} - dev: false /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} @@ -3075,6 +3094,11 @@ packages: ufo: 1.1.2 dev: true + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -3608,6 +3632,13 @@ packages: queue-microtask: 1.2.3 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: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: