@@ -23,15 +23,70 @@ const transformExtensions = [
23
23
'.mjs' ,
24
24
] as const ;
25
25
26
+ const cloneExtensions = < ObjectType extends object > (
27
+ extensions : ObjectType ,
28
+ ) => {
29
+ const cloneTo : ObjectType = Object . create ( Object . getPrototypeOf ( extensions ) ) ;
30
+
31
+ // Preserves setters if they exist (e.g. nyc via append-transform)
32
+ const descriptors = Object . getOwnPropertyDescriptors ( extensions ) ;
33
+ for ( const property in descriptors ) {
34
+ if ( Object . hasOwn ( descriptors , property ) ) {
35
+ Object . defineProperty ( cloneTo , property , descriptors [ property ] ) ;
36
+ }
37
+ }
38
+
39
+ return cloneTo ;
40
+ } ;
41
+
42
+ const safeSet = < T extends Record < string , unknown > > (
43
+ object : T ,
44
+ property : keyof T ,
45
+ value : T [ keyof T ] ,
46
+ descriptor ?: {
47
+ enumerable ?: boolean ;
48
+ configurable ?: boolean ;
49
+ writable ?: boolean ;
50
+ } ,
51
+ ) => {
52
+ const existingDescriptor = Object . getOwnPropertyDescriptor ( object , property ) ;
53
+
54
+ // If setter is provided, use it
55
+ if ( existingDescriptor ?. set ) {
56
+ object [ property ] = value ;
57
+ } else if (
58
+ ! existingDescriptor
59
+ || existingDescriptor . configurable
60
+ ) {
61
+ Object . defineProperty ( object , property , {
62
+ value,
63
+ enumerable : existingDescriptor ?. enumerable || descriptor ?. enumerable ,
64
+ writable : (
65
+ descriptor ?. writable
66
+ ?? (
67
+ existingDescriptor
68
+ ? existingDescriptor . writable
69
+ : true
70
+ )
71
+ ) ,
72
+ configurable : (
73
+ descriptor ?. configurable
74
+ ?? (
75
+ existingDescriptor
76
+ ? existingDescriptor . configurable
77
+ : true
78
+ )
79
+ ) ,
80
+ } ) ;
81
+ }
82
+ } ;
83
+
26
84
export const createExtensions = (
27
85
extendExtensions : NodeJS . RequireExtensions ,
28
86
namespace ?: string ,
29
87
) => {
30
88
// Clone Module._extensions with null prototype
31
- const extensions : NodeJS . RequireExtensions = Object . assign (
32
- Object . create ( null ) ,
33
- extendExtensions ,
34
- ) ;
89
+ const extensions = cloneExtensions ( extendExtensions ) ;
35
90
36
91
const defaultLoader = extensions [ '.js' ] ;
37
92
@@ -105,22 +160,20 @@ export const createExtensions = (
105
160
* Any file requested with an explicit extension will be loaded using the .js loader:
106
161
* https://github.com/nodejs/node/blob/e339e9c5d71b72fd09e6abd38b10678e0c592ae7/lib/internal/modules/cjs/loader.js#L430
107
162
*/
108
- extensions [ '.js' ] = transformer ;
163
+ safeSet ( extensions , '.js' , transformer ) ;
109
164
110
165
for ( const extension of implicitlyResolvableExtensions ) {
111
- const descriptor = Object . getOwnPropertyDescriptor ( extensions , extension ) ;
112
- Object . defineProperty ( extensions , extension , {
113
- value : transformer ,
114
-
166
+ safeSet ( extensions , extension , transformer , {
115
167
/**
116
168
* Registeration needs to be enumerable for some 3rd party libraries
117
169
* https://github.com/gulpjs/rechoir/blob/v0.8.0/index.js#L21 (used by Webpack CLI)
118
170
*
119
171
* If the extension already exists, inherit its enumerable property
120
172
* If not, only expose if it's not namespaced
121
173
*/
122
- enumerable : descriptor ?. enumerable || ! namespace ,
174
+ enumerable : ! namespace ,
123
175
writable : true ,
176
+ configurable : true ,
124
177
} ) ;
125
178
}
126
179
@@ -133,15 +186,16 @@ export const createExtensions = (
133
186
* That said, it's actually ".js" and ".mjs" that get special treatment
134
187
* rather than ".cjs" (it might as well be ".random-ext")
135
188
*/
136
- Object . defineProperty ( extensions , '.mjs' , {
137
- value : transformer ,
138
-
189
+ safeSet ( extensions , '.mjs' , transformer , {
139
190
/**
140
- * Prevent Object.keys from detecting these extensions
191
+ * enumerable defaults to whatever is already set, but if not set, it's false
192
+ *
193
+ * This prevent Object.keys from detecting these extensions
141
194
* when CJS loader iterates over the possible extensions
142
195
* https://github.com/nodejs/node/blob/v22.2.0/lib/internal/modules/cjs/loader.js#L609
143
196
*/
144
- enumerable : false ,
197
+ writable : true ,
198
+ configurable : true ,
145
199
} ) ;
146
200
147
201
return extensions ;
0 commit comments