11import { readFileSync } from 'node:fs'
22import { fileURLToPath } from 'node:url'
3+ import { findStaticImports } from 'mlly'
34import { type Plugin , defineConfig } from 'rollup'
45import dts from 'rollup-plugin-dts'
56
@@ -28,12 +29,34 @@ const multilineCommentsRE = /\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//g
2829const singlelineCommentsRE = / \/ \/ [ ^ / ] .* / g
2930const licenseCommentsRE = / M I T L i c e n s e | M I T l i c e n s e | B S D l i c e n s e /
3031const consecutiveNewlinesRE = / \n { 2 , } / g
32+ const identifierWithTrailingDollarRE = / \b ( \w + ) \$ \d + \b / g
33+
34+ /**
35+ * Replace specific identifiers with a more readable name, grouped by
36+ * the module that imports the identifer as a named import alias
37+ */
38+ const identifierReplacements : Record < string , Record < string , string > > = {
39+ rollup : {
40+ Plugin$1 : 'rollup.Plugin' ,
41+ TransformResult$2 : 'rollup.TransformResult' ,
42+ } ,
43+ esbuild : {
44+ TransformResult$1 : 'esbuild_TransformResult' ,
45+ TransformOptions$1 : 'esbuild_TransformOptions' ,
46+ BuildOptions$1 : 'esbuild_BuildOptions' ,
47+ } ,
48+ 'node:https' : {
49+ Server$1 : 'HttpsServer' ,
50+ ServerOptions$1 : 'HttpsServerOptions' ,
51+ } ,
52+ }
3153
3254/**
3355 * Patch the types files before passing to dts plugin
3456 * 1. Resolve `dep-types/*` and `types/*` imports
3557 * 2. Validate unallowed dependency imports
36- * 3. Clean unnecessary comments
58+ * 3. Replace confusing type names
59+ * 4. Clean unnecessary comments
3760 */
3861function patchTypes ( ) : Plugin {
3962 return {
@@ -56,8 +79,8 @@ function patchTypes(): Plugin {
5679 }
5780 } ,
5881 renderChunk ( code , chunk ) {
59- const deps = new Set ( Object . keys ( pkg . dependencies ) )
6082 // Validate that chunk imports do not import dev deps
83+ const deps = new Set ( Object . keys ( pkg . dependencies ) )
6184 for ( const id of chunk . imports ) {
6285 if (
6386 ! id . startsWith ( './' ) &&
@@ -72,13 +95,79 @@ function patchTypes(): Plugin {
7295 }
7396 }
7497
98+ // Rollup deduplicate type names with a trailing `$1` or `$2`, which can be
99+ // confusing when showed in autocompletions. Try to replace with a better name
100+ const imports = findStaticImports ( code )
101+ for ( const modName in identifierReplacements ) {
102+ const imp = imports . find (
103+ ( imp ) => imp . specifier === modName && imp . imports . includes ( '{' ) ,
104+ )
105+ // Validate that `identifierReplacements` is not outdated if there's no match
106+ if ( ! imp ) {
107+ this . warn (
108+ `${ chunk . fileName } does not import "${ modName } " for replacement` ,
109+ )
110+ process . exitCode = 1
111+ continue
112+ }
113+
114+ const replacements = identifierReplacements [ modName ]
115+ for ( const id in replacements ) {
116+ // Validate that `identifierReplacements` is not outdated if there's no match
117+ if ( ! imp . imports . includes ( id ) ) {
118+ this . warn (
119+ `${ chunk . fileName } does not import "${ id } " from "${ modName } " for replacement` ,
120+ )
121+ process . exitCode = 1
122+ continue
123+ }
124+
125+ const betterId = replacements [ id ]
126+ const regexEscapedId = escapeRegex ( id )
127+ // If the better id accesses a namespace, the existing `Foo as Foo$1`
128+ // named import cannot be replaced with `Foo as Namespace.Foo`, so we
129+ // pre-emptively remove the whole named import
130+ if ( betterId . includes ( '.' ) ) {
131+ code = code . replace (
132+ new RegExp ( `\\b\\w+\\b as ${ regexEscapedId } ,?\\s?` ) ,
133+ '' ,
134+ )
135+ }
136+ code = code . replace (
137+ new RegExp ( `\\b${ regexEscapedId } \\b` , 'g' ) ,
138+ betterId ,
139+ )
140+ }
141+ }
142+ const unreplacedIds = unique (
143+ Array . from ( code . matchAll ( identifierWithTrailingDollarRE ) , ( m ) => m [ 0 ] ) ,
144+ )
145+ if ( unreplacedIds . length ) {
146+ const unreplacedStr = unreplacedIds . map ( ( id ) => `\n- ${ id } ` ) . join ( '' )
147+ this . warn (
148+ `${ chunk . fileName } contains confusing identifier names${ unreplacedStr } ` ,
149+ )
150+ process . exitCode = 1
151+ }
152+
75153 // Clean unnecessary comments
76- return code
154+ code = code
77155 . replace ( singlelineCommentsRE , '' )
78156 . replace ( multilineCommentsRE , ( m ) => {
79157 return licenseCommentsRE . test ( m ) ? '' : m
80158 } )
81159 . replace ( consecutiveNewlinesRE , '\n\n' )
160+
161+ return code
82162 } ,
83163 }
84164}
165+
166+ const escapeRegexRE = / [ - / \\ ^ $ * + ? . ( ) | [ \] { } ] / g
167+ function escapeRegex ( str : string ) : string {
168+ return str . replace ( escapeRegexRE , '\\$&' )
169+ }
170+
171+ function unique < T > ( arr : T [ ] ) : T [ ] {
172+ return Array . from ( new Set ( arr ) )
173+ }
0 commit comments