vue3-core/rollup.dts.config.js

256 lines
7.8 KiB
JavaScript
Raw Normal View History

// @ts-check
2023-02-04 11:59:42 +08:00
import { parse } from '@babel/parser'
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs'
2023-02-04 11:59:42 +08:00
import MagicString from 'magic-string'
import dts from 'rollup-plugin-dts'
2023-02-04 11:59:42 +08:00
import { walk } from 'estree-walker'
if (!existsSync('temp/packages')) {
console.warn(
'no temp dts files found. run `tsc -p tsconfig.build.json` first.'
)
process.exit(1)
}
const packages = readdirSync('temp/packages')
const targets = process.env.TARGETS ? process.env.TARGETS.split(',') : null
const targetPackages = targets
? packages.filter(pkg => targets.includes(pkg))
: packages
export default targetPackages.map(pkg => {
return {
input: `./temp/packages/${pkg}/src/index.d.ts`,
output: {
file: `packages/${pkg}/dist/${pkg}.d.ts`,
format: 'es'
},
plugins: [dts(), patchTypes(pkg), ...(pkg === 'vue' ? [copyMts()] : [])],
onwarn(warning, warn) {
// during dts rollup, everything is externalized by default
if (
warning.code === 'UNRESOLVED_IMPORT' &&
!warning.exporter.startsWith('.')
) {
return
}
warn(warning)
}
}
})
/**
2023-02-04 11:59:42 +08:00
* Patch the dts generated by rollup-plugin-dts
* 1. remove exports marked as @internal
* 2. Convert all types to inline exports
* and remove them from the big export {} declaration
* otherwise it gets weird in vitepress `defineComponent` call with
* "the inferred type cannot be named without a reference"
2023-06-10 17:17:41 +08:00
* 3. Append custom augmentations (jsx, macros)
* @returns {import('rollup').Plugin}
*/
function patchTypes(pkg) {
return {
name: 'patch-types',
2023-03-30 18:02:27 +08:00
renderChunk(code, chunk) {
2023-02-04 11:59:42 +08:00
const s = new MagicString(code)
const ast = parse(code, {
plugins: ['typescript'],
sourceType: 'module'
})
2023-03-30 18:02:27 +08:00
/**
* @param {import('@babel/types').VariableDeclarator | import('@babel/types').TSTypeAliasDeclaration | import('@babel/types').TSInterfaceDeclaration | import('@babel/types').TSDeclareFunction | import('@babel/types').TSInterfaceDeclaration | import('@babel/types').TSEnumDeclaration | import('@babel/types').ClassDeclaration} node
* @param {import('@babel/types').VariableDeclaration} [parentDecl]
*/
function processDeclaration(node, parentDecl) {
if (!node.id) {
return
}
// @ts-ignore
const name = node.id.name
if (name.startsWith('_')) {
return
}
shouldRemoveExport.add(name)
if (!removeInternal(parentDecl || node)) {
if (isExported.has(name)) {
// @ts-ignore
s.prependLeft((parentDecl || node).start, `export `)
}
// traverse further for internal properties
if (
node.type === 'TSInterfaceDeclaration' ||
node.type === 'ClassDeclaration'
) {
node.body.body.forEach(removeInternal)
} else if (node.type === 'TSTypeAliasDeclaration') {
// @ts-ignore
walk(node.typeAnnotation, {
enter(node) {
// @ts-ignore
if (removeInternal(node)) this.skip()
}
})
}
}
}
2023-02-04 11:59:42 +08:00
/**
* @param {import('@babel/types').Node} node
* @returns {boolean}
*/
function removeInternal(node) {
if (
node.leadingComments &&
node.leadingComments.some(c => {
return c.type === 'CommentBlock' && /@internal\b/.test(c.value)
})
) {
/** @type {any} */
const n = node
let id
if (n.id && n.id.type === 'Identifier') {
id = n.id.name
} else if (n.key && n.key.type === 'Identifier') {
id = n.key.name
}
if (id) {
s.overwrite(
// @ts-ignore
node.leadingComments[0].start,
node.end,
`/* removed internal: ${id} */`
)
} else {
// @ts-ignore
s.remove(node.leadingComments[0].start, node.end)
}
return true
}
return false
}
const isExported = new Set()
2023-02-04 11:59:42 +08:00
const shouldRemoveExport = new Set()
// pass 0: check all exported types
for (const node of ast.program.body) {
if (node.type === 'ExportNamedDeclaration' && !node.source) {
for (let i = 0; i < node.specifiers.length; i++) {
const spec = node.specifiers[i]
if (spec.type === 'ExportSpecifier') {
isExported.add(spec.local.name)
}
}
}
}
2023-02-04 11:59:42 +08:00
// pass 1: remove internals + add exports
for (const node of ast.program.body) {
2023-03-30 18:02:27 +08:00
if (node.type === 'VariableDeclaration') {
processDeclaration(node.declarations[0], node)
if (node.declarations.length > 1) {
2023-02-04 11:59:42 +08:00
throw new Error(
2023-03-30 18:02:27 +08:00
`unhandled declare const with more than one declarators:\n${code.slice(
// @ts-ignore
node.start,
node.end
)}`
2023-02-04 11:59:42 +08:00
)
}
2023-03-30 18:02:27 +08:00
} else if (
node.type === 'TSTypeAliasDeclaration' ||
node.type === 'TSInterfaceDeclaration' ||
node.type === 'TSDeclareFunction' ||
node.type === 'TSEnumDeclaration' ||
node.type === 'ClassDeclaration'
) {
processDeclaration(node)
} else if (removeInternal(node)) {
throw new Error(
`unhandled export type marked as @internal: ${node.type}`
)
2023-02-04 11:59:42 +08:00
}
}
2023-03-30 18:02:27 +08:00
2023-02-04 11:59:42 +08:00
// pass 2: remove exports
for (const node of ast.program.body) {
if (node.type === 'ExportNamedDeclaration' && !node.source) {
2023-03-30 18:02:27 +08:00
let removed = 0
2023-02-04 11:59:42 +08:00
for (let i = 0; i < node.specifiers.length; i++) {
const spec = node.specifiers[i]
if (
spec.type === 'ExportSpecifier' &&
shouldRemoveExport.has(spec.local.name)
) {
// @ts-ignore
const exported = spec.exported.name
if (exported !== spec.local.name) {
// this only happens if we have something like
// type Foo
// export { Foo as Bar }
2023-03-30 18:02:27 +08:00
continue
}
2023-02-04 11:59:42 +08:00
const next = node.specifiers[i + 1]
if (next) {
// @ts-ignore
s.remove(spec.start, next.start)
} else {
// last one
const prev = node.specifiers[i - 1]
// @ts-ignore
s.remove(prev ? prev.end : spec.start, spec.end)
}
2023-03-30 18:02:27 +08:00
removed++
2023-02-04 11:59:42 +08:00
}
}
2023-03-30 18:02:27 +08:00
if (removed === node.specifiers.length) {
// @ts-ignore
s.remove(node.start, node.end)
}
2023-02-04 11:59:42 +08:00
}
}
code = s.toString()
2023-03-30 18:02:27 +08:00
if (/@internal/.test(code)) {
throw new Error(
`unhandled @internal declarations detected in ${chunk.fileName}.`
)
}
2023-02-04 11:59:42 +08:00
// append pkg specific types
const additionalTypeDir = `packages/${pkg}/types`
if (existsSync(additionalTypeDir)) {
code +=
'\n' +
readdirSync(additionalTypeDir)
.map(file => readFileSync(`${additionalTypeDir}/${file}`, 'utf-8'))
.join('\n')
}
return code
}
}
}
/**
* According to https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#packagejson-exports-imports-and-self-referencing
* the only way to correct provide types for both Node ESM and CJS is to have
* two separate declaration files, so we need to copy vue.d.ts to vue.d.mts
* upon build.
*
* @returns {import('rollup').Plugin}
*/
function copyMts() {
return {
name: 'copy-vue-mts',
writeBundle(_, bundle) {
writeFileSync(
'packages/vue/dist/vue.d.mts',
// @ts-ignore
bundle['vue.d.ts'].code
)
}
}
}