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