@@ -135,7 +135,7 @@ const { BuiltinModule } = require('internal/bootstrap/realm');
135135const {
136136 maybeCacheSourceMap,
137137} = require ( 'internal/source_map/source_map_cache' ) ;
138- const { pathToFileURL, fileURLToPath, isURL, URL } = require ( 'internal/url' ) ;
138+ const { pathToFileURL, fileURLToPath, isURL, URL , URLParse } = require ( 'internal/url' ) ;
139139const {
140140 pendingDeprecate,
141141 emitExperimentalWarning,
@@ -200,7 +200,11 @@ const {
200200 } ,
201201 setArrowMessage,
202202} = require ( 'internal/errors' ) ;
203- const { validateString } = require ( 'internal/validators' ) ;
203+ const {
204+ validateObject,
205+ validateOneOf,
206+ validateString,
207+ } = require ( 'internal/validators' ) ;
204208
205209const {
206210 CHAR_BACKWARD_SLASH ,
@@ -2037,6 +2041,193 @@ function createRequire(filenameOrURL) {
20372041 return createRequireFromPath ( filepath , fileURL ) ;
20382042}
20392043
2044+ /**
2045+ * Normalize the parent URL/path for cache clearing.
2046+ * @param {string|URL|undefined } parentURL
2047+ * @returns {{ parentURL: string|undefined, parentPath: string|undefined } }
2048+ */
2049+ function normalizeClearCacheParent ( parentURL ) {
2050+ if ( parentURL === undefined ) {
2051+ return { __proto__ : null , parentURL : undefined , parentPath : undefined } ;
2052+ }
2053+
2054+ if ( isURL ( parentURL ) ) {
2055+ const parentPath =
2056+ parentURL . protocol === 'file:' && parentURL . search === '' && parentURL . hash === '' ?
2057+ fileURLToPath ( parentURL ) :
2058+ undefined ;
2059+ return { __proto__ : null , parentURL : parentURL . href , parentPath } ;
2060+ }
2061+
2062+ validateString ( parentURL , 'options.parentURL' ) ;
2063+ if ( path . isAbsolute ( parentURL ) ) {
2064+ return {
2065+ __proto__ : null ,
2066+ parentURL : pathToFileURL ( parentURL ) . href ,
2067+ parentPath : parentURL ,
2068+ } ;
2069+ }
2070+
2071+ const url = URLParse ( parentURL ) ;
2072+ if ( ! url ) {
2073+ throw new ERR_INVALID_ARG_VALUE ( 'options.parentURL' , parentURL ,
2074+ 'must be an absolute path or URL' ) ;
2075+ }
2076+
2077+ const parentPath =
2078+ url . protocol === 'file:' && url . search === '' && url . hash === '' ?
2079+ fileURLToPath ( url ) :
2080+ undefined ;
2081+ return { __proto__ : null , parentURL : url . href , parentPath } ;
2082+ }
2083+
2084+ /**
2085+ * Parse a specifier as a URL when possible.
2086+ * @param {string|URL } specifier
2087+ * @returns {URL|null }
2088+ */
2089+ function getURLFromClearCacheSpecifier ( specifier ) {
2090+ if ( isURL ( specifier ) ) {
2091+ return specifier ;
2092+ }
2093+
2094+ if ( typeof specifier !== 'string' || path . isAbsolute ( specifier ) ) {
2095+ return null ;
2096+ }
2097+
2098+ return URLParse ( specifier ) ?? null ;
2099+ }
2100+
2101+ /**
2102+ * Create a synthetic parent module for CJS resolution.
2103+ * @param {string } parentPath
2104+ * @returns {Module }
2105+ */
2106+ function createParentModuleForClearCache ( parentPath ) {
2107+ const parent = new Module ( parentPath ) ;
2108+ parent . filename = parentPath ;
2109+ parent . paths = Module . _nodeModulePaths ( path . dirname ( parentPath ) ) ;
2110+ return parent ;
2111+ }
2112+
2113+ /**
2114+ * Resolve a cache filename for CommonJS.
2115+ * @param {string|URL } specifier
2116+ * @param {string|undefined } parentPath
2117+ * @returns {string|null }
2118+ */
2119+ function resolveClearCacheFilename ( specifier , parentPath ) {
2120+ if ( ! parentPath && typeof specifier === 'string' && isRelative ( specifier ) ) {
2121+ return null ;
2122+ }
2123+
2124+ const parsedURL = getURLFromClearCacheSpecifier ( specifier ) ;
2125+ let request = specifier ;
2126+ if ( parsedURL ) {
2127+ if ( parsedURL . protocol !== 'file:' || parsedURL . search !== '' || parsedURL . hash !== '' ) {
2128+ return null ;
2129+ }
2130+ request = fileURLToPath ( parsedURL ) ;
2131+ }
2132+
2133+ const parent = parentPath ? createParentModuleForClearCache ( parentPath ) : null ;
2134+ return Module . _resolveFilename ( request , parent , false ) ;
2135+ }
2136+
2137+ /**
2138+ * Resolve a cache URL for ESM.
2139+ * @param {string|URL } specifier
2140+ * @param {string|undefined } parentURL
2141+ * @param {Record<string, string>|undefined } importAttributes
2142+ * @returns {string }
2143+ */
2144+ function resolveClearCacheURL ( specifier , parentURL , importAttributes ) {
2145+ const parsedURL = getURLFromClearCacheSpecifier ( specifier ) ;
2146+ if ( parsedURL ) {
2147+ return parsedURL . href ;
2148+ }
2149+
2150+ if ( path . isAbsolute ( specifier ) ) {
2151+ return pathToFileURL ( specifier ) . href ;
2152+ }
2153+
2154+ if ( parentURL === undefined ) {
2155+ throw new ERR_INVALID_ARG_VALUE ( 'options.parentURL' , parentURL ,
2156+ 'must be provided for non-URL ESM specifiers' ) ;
2157+ }
2158+
2159+ const cascadedLoader =
2160+ require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
2161+ const request = { specifier, attributes : importAttributes , __proto__ : null } ;
2162+ return cascadedLoader . resolveSync ( parentURL , request ) . url ;
2163+ }
2164+
2165+ /**
2166+ * Clear CommonJS and/or ESM module cache entries.
2167+ * @param {string|URL } specifier
2168+ * @param {object } [options]
2169+ * @param {'all'|'cjs'|'esm' } [options.mode]
2170+ * @param {string|URL } [options.parentURL]
2171+ * @param {string } [options.type]
2172+ * @param {Record<string, string> } [options.importAttributes]
2173+ * @returns {{ cjs: boolean, esm: boolean } }
2174+ */
2175+ function clearCache ( specifier , options = kEmptyObject ) {
2176+ const isSpecifierURL = isURL ( specifier ) ;
2177+ if ( ! isSpecifierURL ) {
2178+ validateString ( specifier , 'specifier' ) ;
2179+ }
2180+
2181+ validateObject ( options , 'options' ) ;
2182+ const mode = options . mode === undefined ? 'all' : options . mode ;
2183+ validateOneOf ( mode , 'options.mode' , [ 'all' , 'cjs' , 'esm' ] ) ;
2184+
2185+ if ( options . importAttributes !== undefined && options . type !== undefined ) {
2186+ throw new ERR_INVALID_ARG_VALUE ( 'options.importAttributes' , options . importAttributes ,
2187+ 'cannot be used with options.type' ) ;
2188+ }
2189+
2190+ let importAttributes = options . importAttributes ;
2191+ if ( options . type !== undefined ) {
2192+ validateString ( options . type , 'options.type' ) ;
2193+ importAttributes = { __proto__ : null , type : options . type } ;
2194+ } else if ( importAttributes !== undefined ) {
2195+ validateObject ( importAttributes , 'options.importAttributes' ) ;
2196+ }
2197+
2198+ const { parentURL, parentPath } = normalizeClearCacheParent ( options . parentURL ) ;
2199+ const result = { __proto__ : null , cjs : false , esm : false } ;
2200+
2201+ if ( mode !== 'esm' ) {
2202+ try {
2203+ const filename = resolveClearCacheFilename ( specifier , parentPath ) ;
2204+ if ( filename && Module . _cache [ filename ] !== undefined ) {
2205+ delete Module . _cache [ filename ] ;
2206+ result . cjs = true ;
2207+ }
2208+ } catch ( err ) {
2209+ if ( mode === 'cjs' ) {
2210+ throw err ;
2211+ }
2212+ }
2213+ }
2214+
2215+ if ( mode !== 'cjs' ) {
2216+ try {
2217+ const url = resolveClearCacheURL ( specifier , parentURL , importAttributes ) ;
2218+ const cascadedLoader =
2219+ require ( 'internal/modules/esm/loader' ) . getOrInitializeCascadedLoader ( ) ;
2220+ result . esm = cascadedLoader . loadCache . deleteAll ( url ) ;
2221+ } catch ( err ) {
2222+ if ( mode === 'esm' ) {
2223+ throw err ;
2224+ }
2225+ }
2226+ }
2227+
2228+ return result ;
2229+ }
2230+
20402231/**
20412232 * Checks if a path is relative
20422233 * @param {string } path the target path
@@ -2053,6 +2244,7 @@ function isRelative(path) {
20532244}
20542245
20552246Module . createRequire = createRequire ;
2247+ Module . clearCache = clearCache ;
20562248
20572249/**
20582250 * Define the paths to use for resolving a module.
0 commit comments