@@ -23,15 +23,70 @@ const transformExtensions = [
2323 '.mjs' ,
2424] as const ;
2525
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+
2684export const createExtensions = (
2785 extendExtensions : NodeJS . RequireExtensions ,
2886 namespace ?: string ,
2987) => {
3088 // 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 ) ;
3590
3691 const defaultLoader = extensions [ '.js' ] ;
3792
@@ -105,22 +160,20 @@ export const createExtensions = (
105160 * Any file requested with an explicit extension will be loaded using the .js loader:
106161 * https://github.com/nodejs/node/blob/e339e9c5d71b72fd09e6abd38b10678e0c592ae7/lib/internal/modules/cjs/loader.js#L430
107162 */
108- extensions [ '.js' ] = transformer ;
163+ safeSet ( extensions , '.js' , transformer ) ;
109164
110165 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 , {
115167 /**
116168 * Registeration needs to be enumerable for some 3rd party libraries
117169 * https://github.com/gulpjs/rechoir/blob/v0.8.0/index.js#L21 (used by Webpack CLI)
118170 *
119171 * If the extension already exists, inherit its enumerable property
120172 * If not, only expose if it's not namespaced
121173 */
122- enumerable : descriptor ?. enumerable || ! namespace ,
174+ enumerable : ! namespace ,
123175 writable : true ,
176+ configurable : true ,
124177 } ) ;
125178 }
126179
@@ -133,15 +186,16 @@ export const createExtensions = (
133186 * That said, it's actually ".js" and ".mjs" that get special treatment
134187 * rather than ".cjs" (it might as well be ".random-ext")
135188 */
136- Object . defineProperty ( extensions , '.mjs' , {
137- value : transformer ,
138-
189+ safeSet ( extensions , '.mjs' , transformer , {
139190 /**
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
141194 * when CJS loader iterates over the possible extensions
142195 * https://github.com/nodejs/node/blob/v22.2.0/lib/internal/modules/cjs/loader.js#L609
143196 */
144- enumerable : false ,
197+ writable : true ,
198+ configurable : true ,
145199 } ) ;
146200
147201 return extensions ;
0 commit comments