2020-05-26 05:14:19 +08:00
/ *
MIT License http : //www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @ sokra
* /
"use strict" ;
const ModuleNotFoundError = require ( "../ModuleNotFoundError" ) ;
const RuntimeGlobals = require ( "../RuntimeGlobals" ) ;
2020-06-13 20:45:37 +08:00
const WebpackError = require ( "../WebpackError" ) ;
2020-05-26 05:14:19 +08:00
const { parseOptions } = require ( "../container/options" ) ;
const LazySet = require ( "../util/LazySet" ) ;
2021-04-16 21:35:18 +08:00
const createSchemaValidation = require ( "../util/create-schema-validation" ) ;
2020-06-30 21:08:13 +08:00
const { parseRange } = require ( "../util/semver" ) ;
2020-06-18 21:11:29 +08:00
const ConsumeSharedFallbackDependency = require ( "./ConsumeSharedFallbackDependency" ) ;
2020-05-26 05:14:19 +08:00
const ConsumeSharedModule = require ( "./ConsumeSharedModule" ) ;
const ConsumeSharedRuntimeModule = require ( "./ConsumeSharedRuntimeModule" ) ;
2020-06-18 21:11:29 +08:00
const ProvideForSharedDependency = require ( "./ProvideForSharedDependency" ) ;
2020-06-13 20:45:37 +08:00
const { resolveMatchedConfigs } = require ( "./resolveMatchedConfigs" ) ;
const {
isRequiredVersion ,
getDescriptionFile ,
getRequiredVersionFromDescriptionFile
} = require ( "./utils" ) ;
2020-05-26 05:14:19 +08:00
/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumeSharedPluginOptions} ConsumeSharedPluginOptions */
/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumesConfig} ConsumesConfig */
/** @typedef {import("../Compiler")} Compiler */
2020-06-17 02:01:06 +08:00
/** @typedef {import("../ResolverFactory").ResolveOptionsWithDependencyType} ResolveOptionsWithDependencyType */
2020-05-26 05:14:19 +08:00
/** @typedef {import("./ConsumeSharedModule").ConsumeOptions} ConsumeOptions */
2021-04-16 21:35:18 +08:00
const validate = createSchemaValidation (
require ( "../../schemas/plugins/sharing/ConsumeSharedPlugin.check.js" ) ,
( ) => require ( "../../schemas/plugins/sharing/ConsumeSharedPlugin.json" ) ,
{
name : "Consume Shared Plugin" ,
baseDataPath : "options"
}
) ;
2020-06-17 02:01:06 +08:00
/** @type {ResolveOptionsWithDependencyType} */
2020-06-09 06:32:24 +08:00
const RESOLVE _OPTIONS = { dependencyType : "esm" } ;
2020-05-26 05:14:19 +08:00
const PLUGIN _NAME = "ConsumeSharedPlugin" ;
class ConsumeSharedPlugin {
/ * *
* @ param { ConsumeSharedPluginOptions } options options
* /
constructor ( options ) {
if ( typeof options !== "string" ) {
2021-04-16 21:35:18 +08:00
validate ( options ) ;
2020-05-26 05:14:19 +08:00
}
2020-06-30 21:08:13 +08:00
/** @type {[string, ConsumeOptions][]} */
2020-05-26 05:14:19 +08:00
this . _consumes = parseOptions (
options . consumes ,
( item , key ) => {
if ( Array . isArray ( item ) ) throw new Error ( "Unexpected array in options" ) ;
2020-06-30 21:08:13 +08:00
/** @type {ConsumeOptions} */
2024-07-31 04:09:42 +08:00
const result =
2020-05-27 17:53:59 +08:00
item === key || ! isRequiredVersion ( item )
? // item is a request/key
2024-07-31 05:43:19 +08:00
{
2020-05-27 17:53:59 +08:00
import : key ,
shareScope : options . shareScope || "default" ,
shareKey : key ,
requiredVersion : undefined ,
2020-06-13 20:45:37 +08:00
packageName : undefined ,
2020-05-27 17:53:59 +08:00
strictVersion : false ,
2020-05-27 21:10:57 +08:00
singleton : false ,
2020-05-27 17:53:59 +08:00
eager : false
2024-07-31 05:43:19 +08:00
}
2020-05-27 17:53:59 +08:00
: // key is a request/key
2024-07-31 05:43:19 +08:00
// item is a version
{
2020-05-27 17:53:59 +08:00
import : key ,
shareScope : options . shareScope || "default" ,
shareKey : key ,
2020-06-30 21:08:13 +08:00
requiredVersion : parseRange ( item ) ,
2020-05-27 17:53:59 +08:00
strictVersion : true ,
2020-06-13 20:45:37 +08:00
packageName : undefined ,
2020-05-27 21:10:57 +08:00
singleton : false ,
2020-05-27 17:53:59 +08:00
eager : false
2024-07-31 05:43:19 +08:00
} ;
2020-05-26 23:11:21 +08:00
return result ;
2020-05-26 05:14:19 +08:00
} ,
( item , key ) => ( {
import : item . import === false ? undefined : item . import || key ,
shareScope : item . shareScope || options . shareScope || "default" ,
shareKey : item . shareKey || key ,
requiredVersion :
typeof item . requiredVersion === "string"
2020-06-30 21:08:13 +08:00
? parseRange ( item . requiredVersion )
2020-05-26 05:14:19 +08:00
: item . requiredVersion ,
strictVersion :
2020-06-13 20:45:37 +08:00
typeof item . strictVersion === "boolean"
2020-05-26 05:14:19 +08:00
? item . strictVersion
2020-06-13 20:45:37 +08:00
: item . import !== false && ! item . singleton ,
packageName : item . packageName ,
2024-07-31 11:11:11 +08:00
singleton : Boolean ( item . singleton ) ,
eager : Boolean ( item . eager )
2020-05-26 05:14:19 +08:00
} )
) ;
}
/ * *
* Apply the plugin
* @ param { Compiler } compiler the compiler instance
* @ returns { void }
* /
apply ( compiler ) {
compiler . hooks . thisCompilation . tap (
PLUGIN _NAME ,
( compilation , { normalModuleFactory } ) => {
compilation . dependencyFactories . set (
2020-06-18 21:11:29 +08:00
ConsumeSharedFallbackDependency ,
2020-05-26 05:14:19 +08:00
normalModuleFactory
) ;
2024-03-18 23:28:40 +08:00
/** @type {Map<string, ConsumeOptions>} */
let unresolvedConsumes ;
/** @type {Map<string, ConsumeOptions>} */
let resolvedConsumes ;
/** @type {Map<string, ConsumeOptions>} */
let prefixedConsumes ;
2020-06-13 20:45:37 +08:00
const promise = resolveMatchedConfigs ( compilation , this . _consumes ) . then (
( { resolved , unresolved , prefixed } ) => {
resolvedConsumes = resolved ;
unresolvedConsumes = unresolved ;
prefixedConsumes = prefixed ;
}
) ;
2020-06-01 20:16:14 +08:00
const resolver = compilation . resolverFactory . get (
"normal" ,
2020-06-09 06:32:24 +08:00
RESOLVE _OPTIONS
2020-06-01 20:16:14 +08:00
) ;
2020-06-13 20:45:37 +08:00
2020-05-26 05:14:19 +08:00
/ * *
2020-06-13 20:45:37 +08:00
* @ param { string } context issuer directory
* @ param { string } request request
* @ param { ConsumeOptions } config options
* @ returns { Promise < ConsumeSharedModule > } create module
2020-05-26 05:14:19 +08:00
* /
2020-06-13 20:45:37 +08:00
const createConsumeSharedModule = ( context , request , config ) => {
2024-03-18 23:28:40 +08:00
/ * *
* @ param { string } details details
* /
2020-06-13 20:45:37 +08:00
const requiredVersionWarning = details => {
const error = new WebpackError (
` No required version specified and unable to automatically determine one. ${ details } `
) ;
error . file = ` shared module ${ request } ` ;
compilation . warnings . push ( error ) ;
} ;
2020-06-16 00:46:38 +08:00
const directFallback =
config . import &&
/^(\.\.?(\/|$)|\/|[A-Za-z]:|\\\\)/ . test ( config . import ) ;
2020-06-13 20:45:37 +08:00
return Promise . all ( [
new Promise ( resolve => {
2024-07-31 09:37:24 +08:00
if ( ! config . import ) {
resolve ( ) ;
return ;
}
2020-06-13 20:45:37 +08:00
const resolveContext = {
/** @type {LazySet<string>} */
fileDependencies : new LazySet ( ) ,
/** @type {LazySet<string>} */
contextDependencies : new LazySet ( ) ,
/** @type {LazySet<string>} */
missingDependencies : new LazySet ( )
} ;
2020-05-26 05:14:19 +08:00
resolver . resolve (
{ } ,
2020-06-16 00:46:38 +08:00
directFallback ? compiler . context : context ,
2020-06-13 20:45:37 +08:00
config . import ,
2020-05-26 05:14:19 +08:00
resolveContext ,
( err , result ) => {
2020-06-13 20:45:37 +08:00
compilation . contextDependencies . addAll (
resolveContext . contextDependencies
) ;
compilation . fileDependencies . addAll (
resolveContext . fileDependencies
) ;
compilation . missingDependencies . addAll (
resolveContext . missingDependencies
) ;
2020-05-26 05:14:19 +08:00
if ( err ) {
compilation . errors . push (
new ModuleNotFoundError ( null , err , {
2020-06-16 00:46:38 +08:00
name : ` resolving fallback for shared module ${ request } `
2020-05-26 05:14:19 +08:00
} )
) ;
return resolve ( ) ;
}
2020-06-13 20:45:37 +08:00
resolve ( result ) ;
2020-05-26 05:14:19 +08:00
}
) ;
2020-06-13 20:45:37 +08:00
} ) ,
new Promise ( resolve => {
2024-07-31 09:37:24 +08:00
if ( config . requiredVersion !== undefined ) {
resolve ( config . requiredVersion ) ;
return ;
}
2020-06-13 20:45:37 +08:00
let packageName = config . packageName ;
if ( packageName === undefined ) {
2020-06-15 22:20:34 +08:00
if ( /^(\/|[A-Za-z]:|\\\\)/ . test ( request ) ) {
// For relative or absolute requests we don't automatically use a packageName.
// If wished one can specify one with the packageName option.
2024-07-31 09:37:24 +08:00
resolve ( ) ;
return ;
2020-06-15 22:20:34 +08:00
}
2020-06-13 20:45:37 +08:00
const match = /^((?:@[^\\/]+[\\/])?[^\\/]+)/ . exec ( request ) ;
if ( ! match ) {
requiredVersionWarning (
"Unable to extract the package name from request."
) ;
2024-07-31 09:37:24 +08:00
resolve ( ) ;
return ;
2020-06-13 20:45:37 +08:00
}
packageName = match [ 0 ] ;
}
getDescriptionFile (
compilation . inputFileSystem ,
context ,
[ "package.json" ] ,
( err , result ) => {
if ( err ) {
requiredVersionWarning (
` Unable to read description file: ${ err } `
) ;
2020-06-15 22:20:34 +08:00
return resolve ( ) ;
2020-06-13 20:45:37 +08:00
}
const { data , path : descriptionPath } = result ;
if ( ! data ) {
requiredVersionWarning (
` Unable to find description file in ${ context } . `
) ;
2020-06-15 22:20:34 +08:00
return resolve ( ) ;
2020-06-13 20:45:37 +08:00
}
2023-01-31 09:56:58 +08:00
if ( data . name === packageName ) {
// Package self-referencing
return resolve ( ) ;
}
2020-06-13 20:45:37 +08:00
const requiredVersion = getRequiredVersionFromDescriptionFile (
data ,
packageName
) ;
if ( typeof requiredVersion !== "string" ) {
requiredVersionWarning (
` Unable to find required version for " ${ packageName } " in description file ( ${ descriptionPath } ). It need to be in dependencies, devDependencies or peerDependencies. `
) ;
2020-06-15 22:20:34 +08:00
return resolve ( ) ;
2020-06-13 20:45:37 +08:00
}
2020-06-30 21:08:13 +08:00
resolve ( parseRange ( requiredVersion ) ) ;
2020-05-26 05:14:19 +08:00
}
) ;
2020-06-13 20:45:37 +08:00
} )
] ) . then ( ( [ importResolved , requiredVersion ] ) => {
2020-06-16 00:46:38 +08:00
return new ConsumeSharedModule (
directFallback ? compiler . context : context ,
{
... config ,
importResolved ,
import : importResolved ? config . import : undefined ,
requiredVersion
}
) ;
2020-06-13 20:45:37 +08:00
} ) ;
2020-05-26 05:14:19 +08:00
} ;
2020-06-13 20:45:37 +08:00
2020-05-26 05:14:19 +08:00
normalModuleFactory . hooks . factorize . tapPromise (
PLUGIN _NAME ,
( { context , request , dependencies } ) =>
// wait for resolving to be complete
promise . then ( ( ) => {
2020-05-26 06:46:09 +08:00
if (
2020-06-18 21:11:29 +08:00
dependencies [ 0 ] instanceof ConsumeSharedFallbackDependency ||
dependencies [ 0 ] instanceof ProvideForSharedDependency
2020-05-26 06:46:09 +08:00
) {
2020-05-26 05:14:19 +08:00
return ;
}
const match = unresolvedConsumes . get ( request ) ;
if ( match !== undefined ) {
2020-06-13 20:45:37 +08:00
return createConsumeSharedModule ( context , request , match ) ;
2020-05-26 05:14:19 +08:00
}
2020-06-13 20:45:37 +08:00
for ( const [ prefix , options ] of prefixedConsumes ) {
2020-05-26 05:14:19 +08:00
if ( request . startsWith ( prefix ) ) {
const remainder = request . slice ( prefix . length ) ;
2020-06-13 20:45:37 +08:00
return createConsumeSharedModule ( context , request , {
2020-05-26 05:14:19 +08:00
... options ,
import : options . import
? options . import + remainder
: undefined ,
shareKey : options . shareKey + remainder
} ) ;
}
}
} )
) ;
2020-06-13 20:45:37 +08:00
normalModuleFactory . hooks . createModule . tapPromise (
2020-05-26 05:14:19 +08:00
PLUGIN _NAME ,
2020-06-13 20:45:37 +08:00
( { resource } , { context , dependencies } ) => {
2020-05-26 06:46:09 +08:00
if (
2020-06-18 21:11:29 +08:00
dependencies [ 0 ] instanceof ConsumeSharedFallbackDependency ||
dependencies [ 0 ] instanceof ProvideForSharedDependency
2020-05-26 06:46:09 +08:00
) {
2020-06-13 20:45:37 +08:00
return Promise . resolve ( ) ;
2020-05-26 05:14:19 +08:00
}
2024-03-18 23:28:40 +08:00
const options = resolvedConsumes . get (
/** @type {string} */ ( resource )
) ;
2020-05-26 05:14:19 +08:00
if ( options !== undefined ) {
2024-03-18 23:28:40 +08:00
return createConsumeSharedModule (
context ,
/** @type {string} */ ( resource ) ,
options
) ;
2020-05-26 05:14:19 +08:00
}
2020-06-13 20:45:37 +08:00
return Promise . resolve ( ) ;
2020-05-26 05:14:19 +08:00
}
) ;
compilation . hooks . additionalTreeRuntimeRequirements . tap (
PLUGIN _NAME ,
( chunk , set ) => {
set . add ( RuntimeGlobals . module ) ;
2021-03-08 08:03:05 +08:00
set . add ( RuntimeGlobals . moduleCache ) ;
2020-05-26 23:11:21 +08:00
set . add ( RuntimeGlobals . moduleFactoriesAddOnly ) ;
set . add ( RuntimeGlobals . shareScopeMap ) ;
set . add ( RuntimeGlobals . initializeSharing ) ;
2020-05-26 05:14:19 +08:00
set . add ( RuntimeGlobals . hasOwnProperty ) ;
compilation . addRuntimeModule (
chunk ,
new ConsumeSharedRuntimeModule ( set )
) ;
}
) ;
}
) ;
}
}
module . exports = ConsumeSharedPlugin ;