1
1
import { readFileSync } from 'node:fs'
2
2
import { fileURLToPath } from 'node:url'
3
+ import { findStaticImports } from 'mlly'
3
4
import { type Plugin , defineConfig } from 'rollup'
4
5
import dts from 'rollup-plugin-dts'
5
6
@@ -28,12 +29,34 @@ const multilineCommentsRE = /\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//g
28
29
const singlelineCommentsRE = / \/ \/ [ ^ / ] .* / g
29
30
const 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 /
30
31
const 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
+ }
31
53
32
54
/**
33
55
* Patch the types files before passing to dts plugin
34
56
* 1. Resolve `dep-types/*` and `types/*` imports
35
57
* 2. Validate unallowed dependency imports
36
- * 3. Clean unnecessary comments
58
+ * 3. Replace confusing type names
59
+ * 4. Clean unnecessary comments
37
60
*/
38
61
function patchTypes ( ) : Plugin {
39
62
return {
@@ -56,8 +79,8 @@ function patchTypes(): Plugin {
56
79
}
57
80
} ,
58
81
renderChunk ( code , chunk ) {
59
- const deps = new Set ( Object . keys ( pkg . dependencies ) )
60
82
// Validate that chunk imports do not import dev deps
83
+ const deps = new Set ( Object . keys ( pkg . dependencies ) )
61
84
for ( const id of chunk . imports ) {
62
85
if (
63
86
! id . startsWith ( './' ) &&
@@ -72,13 +95,79 @@ function patchTypes(): Plugin {
72
95
}
73
96
}
74
97
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
+
75
153
// Clean unnecessary comments
76
- return code
154
+ code = code
77
155
. replace ( singlelineCommentsRE , '' )
78
156
. replace ( multilineCommentsRE , ( m ) => {
79
157
return licenseCommentsRE . test ( m ) ? '' : m
80
158
} )
81
159
. replace ( consecutiveNewlinesRE , '\n\n' )
160
+
161
+ return code
82
162
} ,
83
163
}
84
164
}
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