Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(types): simplify type exports #10243

Merged
merged 11 commits into from Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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())
}