11import {
2+ ES_IMPORT_SUFFIX ,
23 EXTERNAL_SUFFIX ,
34 IS_WRAPPED_COMMONJS ,
5+ isWrappedId ,
46 PROXY_SUFFIX ,
57 wrapId ,
68 WRAPPED_SUFFIX
@@ -27,28 +29,28 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
2729 return false ;
2830 } ;
2931
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.
3035 const fullyAnalyzedModules = Object . create ( null ) ;
3136
3237 const getTypeForFullyAnalyzedModule = ( id ) => {
3338 const knownType = knownCjsModuleTypes [ id ] ;
3439 if ( knownType !== true || ! detectCyclesAndConditional || fullyAnalyzedModules [ id ] ) {
3540 return knownType ;
3641 }
37- fullyAnalyzedModules [ id ] = true ;
3842 if ( isCyclic ( id ) ) {
3943 return ( knownCjsModuleTypes [ id ] = IS_WRAPPED_COMMONJS ) ;
4044 }
4145 return knownType ;
4246 } ;
4347
4448 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 ;
5254 if (
5355 detectCyclesAndConditional &&
5456 knownCjsModuleTypes [ id ] === true &&
@@ -59,7 +61,7 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
5961 }
6062 } ;
6163
62- const setTypesForRequiredModules = async ( parentId , resolved , isConditional , loadModule ) => {
64+ const analyzeRequiredModule = async ( parentId , resolved , isConditional , loadModule ) => {
6365 const childId = resolved . id ;
6466 requiredIds [ childId ] = true ;
6567 if ( ! ( isConditional || knownCjsModuleTypes [ parentId ] === IS_WRAPPED_COMMONJS ) ) {
@@ -68,41 +70,85 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
6870
6971 getDependencies ( parentId ) . add ( childId ) ;
7072 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.
7477 await loadModule ( resolved ) ;
7578 }
7679 } ;
7780
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+
7892 return {
7993 getWrappedIds : ( ) =>
8094 Object . keys ( knownCjsModuleTypes ) . filter (
8195 ( id ) => knownCjsModuleTypes [ id ] === IS_WRAPPED_COMMONJS
8296 ) ,
8397 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 ) {
102115 return true ;
103116 }
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+ }
104132 }
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 ) ;
106152 } ,
107153 /* eslint-disable no-param-reassign */
108154 resolveRequireSourcesAndUpdateMeta : ( rollupContext ) => async (
@@ -133,16 +179,18 @@ export function getRequireResolver(extensions, detectCyclesAndConditional) {
133179 return { id : wrapId ( childId , EXTERNAL_SUFFIX ) , allowProxy : false } ;
134180 }
135181 parentMeta . requires . push ( { resolved, isConditional } ) ;
136- await setTypesForRequiredModules ( parentId , resolved , isConditional , rollupContext . load ) ;
182+ await analyzeRequiredModule ( parentId , resolved , isConditional , rollupContext . load ) ;
137183 return { id : childId , allowProxy : true } ;
138184 } )
139185 ) ;
140186 parentMeta . isCommonJS = getTypeForFullyAnalyzedModule ( parentId ) ;
187+ fullyAnalyzedModules [ parentId ] = true ;
141188 return requireTargets . map ( ( { id : dependencyId , allowProxy } , index ) => {
142189 // eslint-disable-next-line no-multi-assign
143190 const isCommonJS = ( parentMeta . isRequiredCommonJS [
144191 dependencyId
145192 ] = getTypeForFullyAnalyzedModule ( dependencyId ) ) ;
193+ fullyAnalyzedModules [ dependencyId ] = true ;
146194 return {
147195 source : sources [ index ] . source ,
148196 id : allowProxy
0 commit comments