1
1
import {
2
+ ES_IMPORT_SUFFIX ,
2
3
EXTERNAL_SUFFIX ,
3
4
IS_WRAPPED_COMMONJS ,
5
+ isWrappedId ,
4
6
PROXY_SUFFIX ,
5
7
wrapId ,
6
8
WRAPPED_SUFFIX
@@ -27,28 +29,28 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
27
29
return false ;
28
30
} ;
29
31
32
+ // Once a module is listed here, its type (wrapped or not) is fixed and may
33
+ // not change for the rest of the current build, to not break already
34
+ // transformed modules.
30
35
const fullyAnalyzedModules = Object . create ( null ) ;
31
36
32
37
const getTypeForFullyAnalyzedModule = ( id ) => {
33
38
const knownType = knownCjsModuleTypes [ id ] ;
34
39
if ( knownType !== true || ! detectCyclesAndConditional || fullyAnalyzedModules [ id ] ) {
35
40
return knownType ;
36
41
}
37
- fullyAnalyzedModules [ id ] = true ;
38
42
if ( isCyclic ( id ) ) {
39
43
return ( knownCjsModuleTypes [ id ] = IS_WRAPPED_COMMONJS ) ;
40
44
}
41
45
return knownType ;
42
46
} ;
43
47
44
48
const setInitialParentType = ( id , initialCommonJSType ) => {
45
- // It is possible a transformed module is already fully analyzed when using
46
- // the cache and one dependency introduces a new cycle. Then transform is
47
- // run for a fully analzyed module again. Fully analyzed modules may never
48
- // change their type as importers already trust their type.
49
- knownCjsModuleTypes [ id ] = fullyAnalyzedModules [ id ]
50
- ? knownCjsModuleTypes [ id ]
51
- : initialCommonJSType ;
49
+ // Fully analyzed modules may never change type
50
+ if ( fullyAnalyzedModules [ id ] ) {
51
+ return ;
52
+ }
53
+ knownCjsModuleTypes [ id ] = initialCommonJSType ;
52
54
if (
53
55
detectCyclesAndConditional &&
54
56
knownCjsModuleTypes [ id ] === true &&
@@ -59,7 +61,7 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
59
61
}
60
62
} ;
61
63
62
- const setTypesForRequiredModules = async ( parentId , resolved , isConditional , loadModule ) => {
64
+ const analyzeRequiredModule = async ( parentId , resolved , isConditional , loadModule ) => {
63
65
const childId = resolved . id ;
64
66
requiredIds [ childId ] = true ;
65
67
if ( ! ( isConditional || knownCjsModuleTypes [ parentId ] === IS_WRAPPED_COMMONJS ) ) {
@@ -68,41 +70,85 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
68
70
69
71
getDependencies ( parentId ) . add ( childId ) ;
70
72
if ( ! isCyclic ( childId ) ) {
71
- // This makes sure the current transform handler waits for all direct dependencies to be
72
- // loaded and transformed and therefore for all transitive CommonJS dependencies to be
73
- // loaded as well so that all cycles have been found and knownCjsModuleTypes is reliable.
73
+ // This makes sure the current transform handler waits for all direct
74
+ // dependencies to be loaded and transformed and therefore for all
75
+ // transitive CommonJS dependencies to be loaded as well so that all
76
+ // cycles have been found and knownCjsModuleTypes is reliable.
74
77
await loadModule ( resolved ) ;
75
78
}
76
79
} ;
77
80
81
+ const getTypeForImportedModule = async ( resolved , loadModule ) => {
82
+ if ( resolved . id in knownCjsModuleTypes ) {
83
+ // This handles cyclic ES dependencies
84
+ return knownCjsModuleTypes [ resolved . id ] ;
85
+ }
86
+ const {
87
+ meta : { commonjs }
88
+ } = await loadModule ( resolved ) ;
89
+ return ( commonjs && commonjs . isCommonJS ) || false ;
90
+ } ;
91
+
78
92
return {
79
93
getWrappedIds : ( ) =>
80
94
Object . keys ( knownCjsModuleTypes ) . filter (
81
95
( id ) => knownCjsModuleTypes [ id ] === IS_WRAPPED_COMMONJS
82
96
) ,
83
97
isRequiredId : ( id ) => requiredIds [ id ] ,
84
- async shouldTransformCachedModule ( { id : parentId , meta : { commonjs : parentMeta } } ) {
85
- // Ignore modules that did not pass through the original transformer in a previous build
86
- if ( ! ( parentMeta && parentMeta . requires ) ) {
87
- return false ;
88
- }
89
- setInitialParentType ( parentId , parentMeta . initialCommonJSType ) ;
90
- await Promise . all (
91
- parentMeta . requires . map ( ( { resolved, isConditional } ) =>
92
- setTypesForRequiredModules ( parentId , resolved , isConditional , this . load )
93
- )
94
- ) ;
95
- if ( getTypeForFullyAnalyzedModule ( parentId ) !== parentMeta . isCommonJS ) {
96
- return true ;
97
- }
98
- for ( const {
99
- resolved : { id }
100
- } of parentMeta . requires ) {
101
- if ( getTypeForFullyAnalyzedModule ( id ) !== parentMeta . isRequiredCommonJS [ id ] ) {
98
+ async shouldTransformCachedModule ( {
99
+ id : parentId ,
100
+ resolvedSources,
101
+ meta : { commonjs : parentMeta }
102
+ } ) {
103
+ // We explicitly track ES modules to handle ciruclar imports
104
+ if ( ! ( parentMeta && parentMeta . isCommonJS ) ) knownCjsModuleTypes [ parentId ] = false ;
105
+ if ( isWrappedId ( parentId , ES_IMPORT_SUFFIX ) ) return false ;
106
+ const parentRequires = parentMeta && parentMeta . requires ;
107
+ if ( parentRequires ) {
108
+ setInitialParentType ( parentId , parentMeta . initialCommonJSType ) ;
109
+ await Promise . all (
110
+ parentRequires . map ( ( { resolved, isConditional } ) =>
111
+ analyzeRequiredModule ( parentId , resolved , isConditional , this . load )
112
+ )
113
+ ) ;
114
+ if ( getTypeForFullyAnalyzedModule ( parentId ) !== parentMeta . isCommonJS ) {
102
115
return true ;
103
116
}
117
+ for ( const {
118
+ resolved : { id }
119
+ } of parentRequires ) {
120
+ if ( getTypeForFullyAnalyzedModule ( id ) !== parentMeta . isRequiredCommonJS [ id ] ) {
121
+ return true ;
122
+ }
123
+ }
124
+ // Now that we decided to go with the cached copy, neither the parent
125
+ // module nor any of its children may change types anymore
126
+ fullyAnalyzedModules [ parentId ] = true ;
127
+ for ( const {
128
+ resolved : { id }
129
+ } of parentRequires ) {
130
+ fullyAnalyzedModules [ id ] = true ;
131
+ }
104
132
}
105
- return false ;
133
+ const parentRequireSet = new Set ( ( parentRequires || [ ] ) . map ( ( { resolved : { id } } ) => id ) ) ;
134
+ return (
135
+ await Promise . all (
136
+ Object . keys ( resolvedSources )
137
+ . map ( ( source ) => resolvedSources [ source ] )
138
+ . filter ( ( { id } ) => ! parentRequireSet . has ( id ) )
139
+ . map ( async ( resolved ) => {
140
+ if ( isWrappedId ( resolved . id , ES_IMPORT_SUFFIX ) ) {
141
+ return (
142
+ ( await getTypeForImportedModule (
143
+ ( await this . load ( { id : resolved . id } ) ) . meta . commonjs . resolved ,
144
+ this . load
145
+ ) ) !== IS_WRAPPED_COMMONJS
146
+ ) ;
147
+ }
148
+ return ( await getTypeForImportedModule ( resolved , this . load ) ) === IS_WRAPPED_COMMONJS ;
149
+ } )
150
+ )
151
+ ) . some ( ( shouldTransform ) => shouldTransform ) ;
106
152
} ,
107
153
/* eslint-disable no-param-reassign */
108
154
resolveRequireSourcesAndUpdateMeta : ( rollupContext ) => async (
@@ -133,16 +179,18 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
133
179
return { id : wrapId ( childId , EXTERNAL_SUFFIX ) , allowProxy : false } ;
134
180
}
135
181
parentMeta . requires . push ( { resolved, isConditional } ) ;
136
- await setTypesForRequiredModules ( parentId , resolved , isConditional , rollupContext . load ) ;
182
+ await analyzeRequiredModule ( parentId , resolved , isConditional , rollupContext . load ) ;
137
183
return { id : childId , allowProxy : true } ;
138
184
} )
139
185
) ;
140
186
parentMeta . isCommonJS = getTypeForFullyAnalyzedModule ( parentId ) ;
187
+ fullyAnalyzedModules [ parentId ] = true ;
141
188
return requireTargets . map ( ( { id : dependencyId , allowProxy } , index ) => {
142
189
// eslint-disable-next-line no-multi-assign
143
190
const isCommonJS = ( parentMeta . isRequiredCommonJS [
144
191
dependencyId
145
192
] = getTypeForFullyAnalyzedModule ( dependencyId ) ) ;
193
+ fullyAnalyzedModules [ dependencyId ] = true ;
146
194
return {
147
195
source : sources [ index ] . source ,
148
196
id : allowProxy
0 commit comments