@@ -4,15 +4,15 @@ import path from "path";
44import json5 from "json5" ;
55import gensync from "gensync" ;
66import type { Handler } from "gensync" ;
7- import { makeStrongCache , makeWeakCacheSync } from "../caching" ;
7+ import { makeWeakCache , makeWeakCacheSync } from "../caching" ;
88import type { CacheConfigurator } from "../caching" ;
99import { makeConfigAPI } from "../helpers/config-api" ;
1010import type { ConfigAPI } from "../helpers/config-api" ;
1111import { makeStaticFileCache } from "./utils" ;
1212import loadCodeDefault from "./module-types" ;
1313import pathPatternToRegex from "../pattern-to-regex" ;
1414import type { FilePackageData , RelativeConfig , ConfigFile } from "./types" ;
15- import type { CallerMetadata } from "../validation/options" ;
15+ import type { CallerMetadata , InputOptions } from "../validation/options" ;
1616import ConfigError from "../../errors/config-error" ;
1717
1818import * as fs from "../../gensync-utils/fs" ;
@@ -43,30 +43,41 @@ const BABELIGNORE_FILENAME = ".babelignore";
4343
4444const LOADING_CONFIGS = new Set ( ) ;
4545
46- const readConfigCode = makeStrongCache ( function * readConfigCode (
46+ type ConfigCacheData = {
47+ envName : string ;
48+ caller : CallerMetadata | undefined ;
49+ } ;
50+
51+ const runConfig = makeWeakCache ( function * runConfig (
52+ options : Function ,
53+ cache : CacheConfigurator < ConfigCacheData > ,
54+ ) : Handler < {
55+ options : InputOptions | null ;
56+ cacheNeedsConfiguration : boolean ;
57+ } > {
58+ // @ts -expect-error - if we want to make it possible to use async configs
59+ yield * [ ] ;
60+
61+ return {
62+ options : endHiddenCallStack ( options as any as ( api : ConfigAPI ) => { } ) (
63+ makeConfigAPI ( cache ) ,
64+ ) ,
65+ cacheNeedsConfiguration : ! cache . configured ( ) ,
66+ } ;
67+ } ) ;
68+
69+ function * readConfigCode (
4770 filepath : string ,
48- cache : CacheConfigurator < {
49- envName : string ;
50- caller : CallerMetadata | undefined ;
51- } > ,
71+ data : ConfigCacheData ,
5272) : Handler < ConfigFile | null > {
53- if ( ! nodeFs . existsSync ( filepath ) ) {
54- cache . never ( ) ;
55- return null ;
56- }
73+ if ( ! nodeFs . existsSync ( filepath ) ) return null ;
5774
5875 // The `require()` call below can make this code reentrant if a require hook like @babel/register has been
5976 // loaded into the system. That would cause Babel to attempt to compile the `.babelrc.js` file as it loads
6077 // below. To cover this case, we auto-ignore re-entrant config processing.
6178 if ( LOADING_CONFIGS . has ( filepath ) ) {
62- cache . never ( ) ;
63-
6479 debug ( "Auto-ignoring usage of config %o." , filepath ) ;
65- return {
66- filepath,
67- dirname : path . dirname ( filepath ) ,
68- options : { } ,
69- } ;
80+ return buildConfigFileObject ( { } , filepath ) ;
7081 }
7182
7283 let options : unknown ;
@@ -81,16 +92,9 @@ const readConfigCode = makeStrongCache(function* readConfigCode(
8192 LOADING_CONFIGS . delete ( filepath ) ;
8293 }
8394
84- let assertCache = false ;
95+ let cacheNeedsConfiguration = false ;
8596 if ( typeof options === "function" ) {
86- // @ts -expect-error - if we want to make it possible to use async configs
87- yield * [ ] ;
88-
89- options = endHiddenCallStack ( options as any as ( api : ConfigAPI ) => { } ) (
90- makeConfigAPI ( cache ) ,
91- ) ;
92-
93- assertCache = true ;
97+ ( { options, cacheNeedsConfiguration } = yield * runConfig ( options , data ) ) ;
9498 }
9599
96100 if ( ! options || typeof options !== "object" || Array . isArray ( options ) ) {
@@ -102,6 +106,10 @@ const readConfigCode = makeStrongCache(function* readConfigCode(
102106
103107 // @ts -expect-error todo(flow->ts)
104108 if ( typeof options . then === "function" ) {
109+ // @ts -expect-error We use ?. in case options is a thenable
110+ // but not a promise
111+ options . catch ?.( ( ) => { } ) ;
112+
105113 throw new ConfigError (
106114 `You appear to be using an async configuration, ` +
107115 `which your current version of Babel does not support. ` +
@@ -112,14 +120,38 @@ const readConfigCode = makeStrongCache(function* readConfigCode(
112120 ) ;
113121 }
114122
115- if ( assertCache && ! cache . configured ( ) ) throwConfigError ( filepath ) ;
123+ if ( cacheNeedsConfiguration ) throwConfigError ( filepath ) ;
116124
117- return {
118- filepath,
119- dirname : path . dirname ( filepath ) ,
120- options,
121- } ;
122- } ) ;
125+ return buildConfigFileObject ( options , filepath ) ;
126+ }
127+
128+ // We cache the generated ConfigFile object rather than creating a new one
129+ // every time, so that it can be used as a cache key in other functions.
130+ const cfboaf /* configFilesByOptionsAndFilepath */ = new WeakMap <
131+ InputOptions ,
132+ Map < string , ConfigFile >
133+ > ( ) ;
134+ function buildConfigFileObject (
135+ options : InputOptions ,
136+ filepath : string ,
137+ ) : ConfigFile {
138+ let configFilesByFilepath = cfboaf . get ( options ) ;
139+ if ( ! configFilesByFilepath ) {
140+ cfboaf . set ( options , ( configFilesByFilepath = new Map ( ) ) ) ;
141+ }
142+
143+ let configFile = configFilesByFilepath . get ( filepath ) ;
144+ if ( ! configFile ) {
145+ configFile = {
146+ filepath,
147+ dirname : path . dirname ( filepath ) ,
148+ options,
149+ } ;
150+ configFilesByFilepath . set ( filepath , configFile ) ;
151+ }
152+
153+ return configFile ;
154+ }
123155
124156const packageToBabelConfig = makeWeakCacheSync (
125157 ( file : ConfigFile ) : ConfigFile | null => {
0 commit comments