Skip to content

Commit

Permalink
refactor(types): simplify type exports (#10243)
Browse files Browse the repository at this point in the history
Co-authored-by: 翠 / green <[email protected]>
  • Loading branch information
bluwy and sapphi-red committed Sep 30, 2022
1 parent 931d69b commit 291174d
Show file tree
Hide file tree
Showing 40 changed files with 386 additions and 510 deletions.
1 change: 0 additions & 1 deletion .eslintignore
@@ -1,4 +1,3 @@
dist
playground-temp
temp
packages/vite/client/types.d.ts
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Expand Up @@ -131,7 +131,7 @@ module.exports = defineConfig({
}
},
{
files: ['packages/vite/src/dep-types/**', '*.spec.ts'],
files: ['packages/vite/src/types/**', '*.spec.ts'],
rules: {
'node/no-extraneous-import': 'off'
}
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -5,7 +5,6 @@
*.local
*.log
/.vscode/
/packages/vite/client/types.d.ts
/packages/vite/LICENSE
dist
dist-ssr
Expand Down
1 change: 0 additions & 1 deletion .prettierignore
Expand Up @@ -9,4 +9,3 @@ playground/tsconfig-json-load-error/has-error/tsconfig.json
playground/html/invalid.html
playground/html/valid.html
playground/worker/classic-worker.js
packages/vite/client/types.d.ts
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Expand Up @@ -218,10 +218,12 @@ Avoid deps with large transitive dependencies that result in bloated size compar

Vite aims to be fully usable as a dependency in a TypeScript project (e.g. it should provide proper typings for VitePress), and also in `vite.config.ts`. This means technically a dependency whose types are exposed needs to be part of `dependencies` instead of `devDependencies`. However, this also means we won't be able to bundle it.

To get around this, we inline some of these dependencies' types in `packages/vite/src/dep-types`. This way, we can still expose the typing but bundle the dependency's source code.
To get around this, we inline some of these dependencies' types in `packages/vite/src/types`. This way, we can still expose the typing but bundle the dependency's source code.

Use `pnpm run check-dist-types` to check that the bundled types do not rely on types in `devDependencies`. If you are adding `dependencies`, make sure to configure `tsconfig.check.json`.

For types shared between client and node, they should be added into `packages/vite/types`. These types are not bundled and are published as is (though they are still considered internal). Dependency types within this directory (e.g. `packages/vite/types/chokidar.d.ts`) are deprecated and should be added to `packages/vite/src/types` instead.

### Think Before Adding Yet Another Option

We already have many config options, and we should avoid fixing an issue by adding yet another one. Before adding an option, consider whether the problem:
Expand Down
21 changes: 6 additions & 15 deletions docs/guide/api-plugin.md
Expand Up @@ -595,21 +595,12 @@ It is possible to type custom events by extending the `CustomEventMap` interface
```ts
// events.d.ts
import 'vite'
import 'vite/client/types'
import 'vite/types/customEvent'

interface MyCustomEventMap {
'custom:foo': { msg: string }
// 'event-key': payload
}

// extend interface for server-side
declare module 'vite' {
interface CustomEventMap extends MyCustomEventMap {}
}

// extend interface for client-side
declare module 'vite/client/types' {
interface CustomEventMap extends MyCustomEventMap {}
declare module 'vite/types/customEvent' {
interface CustomEventMap {
'custom:foo': { msg: string }
// 'event-key': payload
}
}
```
54 changes: 0 additions & 54 deletions packages/vite/api-extractor.client.json

This file was deleted.

2 changes: 1 addition & 1 deletion packages/vite/client.d.ts
@@ -1,4 +1,4 @@
/// <reference path="./import-meta.d.ts" />
/// <reference path="./types/importMeta.d.ts" />

// CSS modules
type CSSModuleClasses = { readonly [key: string]: string }
Expand Down
10 changes: 0 additions & 10 deletions packages/vite/import-meta.d.ts

This file was deleted.

27 changes: 9 additions & 18 deletions packages/vite/package.json
Expand Up @@ -20,24 +20,16 @@
"./client": {
"types": "./client.d.ts"
},
"./import-meta": {
"types": "./import-meta.d.ts"
},
"./client/types": {
"types": "./client/types.d.ts"
},
"./dist/client/*": "./dist/client/*",
"./package.json": "./package.json"
},
"files": [
"bin",
"dist",
"client.d.ts",
"import-meta.d.ts",
"index.cjs",
"src/client",
"types",
"client/types.d.ts"
"types"
],
"engines": {
"node": "^14.18.0 || >=16.0.0"
Expand All @@ -55,13 +47,12 @@
"dev": "rimraf dist && pnpm run build-bundle -w",
"build": "rimraf dist && run-s build-bundle build-types",
"build-bundle": "rollup --config rollup.config.ts --configPlugin typescript",
"build-types": "run-p build-node-types build-client-types",
"build-node-types": "run-s build-node-types-temp build-node-types-patch build-node-types-roll build-node-types-check",
"build-node-types-temp": "tsc --emitDeclarationOnly --outDir temp/node -p src/node",
"build-node-types-patch": "tsx scripts/patchTypes.ts",
"build-node-types-roll": "api-extractor run && rimraf temp",
"build-node-types-check": "tsc --project tsconfig.check.json",
"build-client-types": "api-extractor run -c api-extractor.client.json",
"build-types": "run-s build-types-temp build-types-pre-patch build-types-roll build-types-post-patch build-types-check",
"build-types-temp": "tsc --emitDeclarationOnly --outDir temp/node -p src/node",
"build-types-pre-patch": "tsx scripts/prePatchTypes.ts",
"build-types-roll": "api-extractor run && rimraf temp",
"build-types-post-patch": "tsx scripts/postPatchTypes.ts",
"build-types-check": "tsc --project tsconfig.check.json",
"lint": "eslint --cache --ext .ts src/**",
"format": "prettier --write --cache --parser typescript \"src/**/*.ts\"",
"prepublishOnly": "npm run build"
Expand Down Expand Up @@ -127,8 +118,8 @@
"strip-literal": "^0.4.2",
"tsconfck": "^2.0.1",
"tslib": "^2.4.0",
"dep-types": "link:./src/dep-types",
"types": "link:./src/types",
"dep-types": "link:./src/types",
"types": "link:./types",
"ufo": "^0.8.5",
"ws": "^8.9.0"
},
Expand Down
83 changes: 0 additions & 83 deletions packages/vite/scripts/patchTypes.ts

This file was deleted.

16 changes: 16 additions & 0 deletions packages/vite/scripts/postPatchTypes.ts
@@ -0,0 +1,16 @@
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import colors from 'picocolors'
import { rewriteImports } from './util'

const dir = dirname(fileURLToPath(import.meta.url))
const nodeDts = resolve(dir, '../dist/node/index.d.ts')

// rewrite `types/*` import to relative import
rewriteImports(nodeDts, (importPath) => {
if (importPath.startsWith('types/')) {
return '../../' + importPath
}
})

console.log(colors.green(colors.bold(`patched types/* imports`)))
23 changes: 23 additions & 0 deletions packages/vite/scripts/prePatchTypes.ts
@@ -0,0 +1,23 @@
import { dirname, relative, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import colors from 'picocolors'
import { rewriteImports, slash } from './util'

const dir = dirname(fileURLToPath(import.meta.url))
const tempDir = resolve(dir, '../temp/node')
const depTypesDir = resolve(dir, '../src/types')

// walk through the temp dts dir, find all import/export of, deps-types/*
// and rewrite them into relative imports - so that api-extractor actually
// includes them in the rolled-up final d.ts file.
rewriteImports(tempDir, (importPath, currentFile) => {
if (importPath.startsWith('dep-types/')) {
const absoluteTypePath = resolve(
depTypesDir,
importPath.slice('dep-types/'.length)
)
return slash(relative(dirname(currentFile), absoluteTypePath))
}
})

console.log(colors.green(colors.bold(`patched deps-types/* imports`)))
72 changes: 72 additions & 0 deletions packages/vite/scripts/util.ts
@@ -0,0 +1,72 @@
import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import type { ParseResult } from '@babel/parser'
import { parse } from '@babel/parser'
import type { File } from '@babel/types'
import colors from 'picocolors'
import MagicString from 'magic-string'

export function rewriteImports(
fileOrDir: string,
rewrite: (importPath: string, currentFile: string) => string | void
): void {
walkDir(fileOrDir, (file) => {
rewriteFileImports(file, (importPath) => {
return rewrite(importPath, file)
})
})
}

export function slash(p: string): string {
return p.replace(/\\/g, '/')
}

function walkDir(dir: string, handleFile: (file: string) => void): void {
if (statSync(dir).isDirectory()) {
const files = readdirSync(dir)
for (const file of files) {
const resolved = resolve(dir, file)
walkDir(resolved, handleFile)
}
} else {
handleFile(dir)
}
}

function rewriteFileImports(
file: string,
rewrite: (importPath: string) => string | void
): void {
const content = readFileSync(file, 'utf-8')
const str = new MagicString(content)
let ast: ParseResult<File>
try {
ast = parse(content, {
sourceType: 'module',
plugins: ['typescript', 'classProperties']
})
} catch (e) {
console.log(colors.red(`failed to parse ${file}`))
throw e
}
for (const statement of ast.program.body) {
if (
statement.type === 'ImportDeclaration' ||
statement.type === 'ExportNamedDeclaration' ||
statement.type === 'ExportAllDeclaration'
) {
const source = statement.source
if (source?.value) {
const newImportPath = rewrite(source.value)
if (newImportPath) {
str.overwrite(
source.start!,
source.end!,
JSON.stringify(newImportPath)
)
}
}
}
}
writeFileSync(file, str.toString())
}

0 comments on commit 291174d

Please sign in to comment.