rollup.dts.config.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // @ts-check
  2. import { parse } from '@babel/parser'
  3. import { existsSync, readdirSync, readFileSync } from 'fs'
  4. import MagicString from 'magic-string'
  5. import dts from 'rollup-plugin-dts'
  6. import { walk } from 'estree-walker'
  7. if (!existsSync('temp/packages')) {
  8. console.warn(
  9. 'no temp dts files found. run `tsc -p tsconfig.build.json` first.'
  10. )
  11. process.exit(1)
  12. }
  13. export default readdirSync('temp/packages').map(pkg => {
  14. return {
  15. input: `./temp/packages/${pkg}/src/index.d.ts`,
  16. output: {
  17. file: `packages/${pkg}/dist/${pkg}.d.ts`,
  18. format: 'es'
  19. },
  20. plugins: [dts(), patchTypes(pkg)],
  21. onwarn(warning, warn) {
  22. // during dts rollup, everything is externalized by default
  23. if (
  24. warning.code === 'UNRESOLVED_IMPORT' &&
  25. !warning.exporter.startsWith('.')
  26. ) {
  27. return
  28. }
  29. warn(warning)
  30. }
  31. }
  32. })
  33. /**
  34. * Patch the dts generated by rollup-plugin-dts
  35. * 1. remove exports marked as @internal
  36. * 2. Convert all types to inline exports
  37. * and remove them from the big export {} declaration
  38. * otherwise it gets weird in vitepress `defineComponent` call with
  39. * "the inferred type cannot be named without a reference"
  40. * 3. Append custom agumentations (jsx, macros)
  41. * @returns {import('rollup').Plugin}
  42. */
  43. function patchTypes(pkg) {
  44. return {
  45. name: 'patch-types',
  46. renderChunk(code) {
  47. const s = new MagicString(code)
  48. const ast = parse(code, {
  49. plugins: ['typescript'],
  50. sourceType: 'module'
  51. })
  52. /**
  53. * @param {import('@babel/types').Node} node
  54. * @returns {boolean}
  55. */
  56. function removeInternal(node) {
  57. if (
  58. node.leadingComments &&
  59. node.leadingComments.some(c => {
  60. return c.type === 'CommentBlock' && /@internal\b/.test(c.value)
  61. })
  62. ) {
  63. /** @type {any} */
  64. const n = node
  65. let id
  66. if (n.id && n.id.type === 'Identifier') {
  67. id = n.id.name
  68. } else if (n.key && n.key.type === 'Identifier') {
  69. id = n.key.name
  70. }
  71. if (id) {
  72. s.overwrite(
  73. // @ts-ignore
  74. node.leadingComments[0].start,
  75. node.end,
  76. `/* removed internal: ${id} */`
  77. )
  78. } else {
  79. // @ts-ignore
  80. s.remove(node.leadingComments[0].start, node.end)
  81. }
  82. return true
  83. }
  84. return false
  85. }
  86. const isExported = new Set()
  87. const shouldRemoveExport = new Set()
  88. // pass 0: check all exported types
  89. for (const node of ast.program.body) {
  90. if (node.type === 'ExportNamedDeclaration' && !node.source) {
  91. for (let i = 0; i < node.specifiers.length; i++) {
  92. const spec = node.specifiers[i]
  93. if (spec.type === 'ExportSpecifier') {
  94. isExported.add(spec.local.name)
  95. }
  96. }
  97. }
  98. }
  99. // pass 1: remove internals + add exports
  100. for (const node of ast.program.body) {
  101. if (
  102. (node.type === 'TSTypeAliasDeclaration' ||
  103. node.type === 'TSInterfaceDeclaration') &&
  104. !node.id.name.startsWith(`_`)
  105. ) {
  106. const name = node.id.name
  107. shouldRemoveExport.add(name)
  108. if (!removeInternal(node)) {
  109. if (isExported.has(name)) {
  110. // @ts-ignore
  111. s.prependLeft(node.start, `export `)
  112. }
  113. // traverse further for internal properties
  114. if (node.type === 'TSInterfaceDeclaration') {
  115. node.body.body.forEach(removeInternal)
  116. } else if (node.type === 'TSTypeAliasDeclaration') {
  117. // @ts-ignore
  118. walk(node.typeAnnotation, {
  119. enter(node) {
  120. // @ts-ignore
  121. if (removeInternal(node)) this.skip()
  122. }
  123. })
  124. }
  125. }
  126. } else if (removeInternal(node)) {
  127. if (node.type === 'VariableDeclaration') {
  128. // declare const x
  129. for (const decl of node.declarations) {
  130. // @ts-ignore
  131. shouldRemoveExport.add(decl.id.name)
  132. }
  133. } else if (
  134. node.type === 'TSDeclareFunction' ||
  135. node.type === 'TSEnumDeclaration'
  136. ) {
  137. // declare function
  138. // @ts-ignore
  139. shouldRemoveExport.add(node.id.name)
  140. } else {
  141. throw new Error(
  142. `unhandled export type marked as @internal: ${node.type}`
  143. )
  144. }
  145. }
  146. }
  147. // pass 2: remove exports
  148. for (const node of ast.program.body) {
  149. if (node.type === 'ExportNamedDeclaration' && !node.source) {
  150. for (let i = 0; i < node.specifiers.length; i++) {
  151. const spec = node.specifiers[i]
  152. if (
  153. spec.type === 'ExportSpecifier' &&
  154. shouldRemoveExport.has(spec.local.name)
  155. ) {
  156. // @ts-ignore
  157. const exported = spec.exported.name
  158. if (exported !== spec.local.name) {
  159. // this only happens if we have something like
  160. // type Foo
  161. // export { Foo as Bar }
  162. // there are no such cases atm, so it is unhandled for now.
  163. throw new Error(
  164. `removed export ${exported} has different local name: ${spec.local.name}`
  165. )
  166. }
  167. const next = node.specifiers[i + 1]
  168. if (next) {
  169. // @ts-ignore
  170. s.remove(spec.start, next.start)
  171. } else {
  172. // last one
  173. const prev = node.specifiers[i - 1]
  174. // @ts-ignore
  175. s.remove(prev ? prev.end : spec.start, spec.end)
  176. }
  177. }
  178. }
  179. }
  180. }
  181. code = s.toString()
  182. // append pkg specific types
  183. const additionalTypeDir = `packages/${pkg}/types`
  184. if (existsSync(additionalTypeDir)) {
  185. code +=
  186. '\n' +
  187. readdirSync(additionalTypeDir)
  188. .map(file => readFileSync(`${additionalTypeDir}/${file}`, 'utf-8'))
  189. .join('\n')
  190. }
  191. return code
  192. }
  193. }
  194. }