2020-02-20 03:25:49 +08:00
/ *
MIT License http : //www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @ sokra
* /
"use strict" ;
const { ConcatSource } = require ( "webpack-sources" ) ;
2021-02-11 02:14:50 +08:00
const { UsageState } = require ( "../ExportsInfo" ) ;
2023-05-27 00:06:16 +08:00
const RuntimeGlobals = require ( "../RuntimeGlobals" ) ;
2021-01-06 22:01:59 +08:00
const Template = require ( "../Template" ) ;
2020-02-20 03:25:49 +08:00
const propertyAccess = require ( "../util/propertyAccess" ) ;
2021-02-11 02:14:50 +08:00
const { getEntryRuntime } = require ( "../util/runtime" ) ;
2020-02-20 03:25:49 +08:00
const AbstractLibraryPlugin = require ( "./AbstractLibraryPlugin" ) ;
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
/** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */
2025-09-11 08:10:10 +08:00
/** @typedef {import("../../declarations/WebpackOptions").LibraryExport} LibraryExport */
2020-02-20 03:25:49 +08:00
/** @typedef {import("../Chunk")} Chunk */
2023-06-22 08:59:10 +08:00
/** @typedef {import("../Compilation")} Compilation */
2020-02-20 03:25:49 +08:00
/** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */
2021-02-11 02:14:50 +08:00
/** @typedef {import("../Module")} Module */
2025-08-28 18:34:30 +08:00
/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
2025-09-11 08:10:10 +08:00
/** @typedef {import("../ExportsInfo").ExportInfoName} ExportInfoName */
2020-02-20 03:25:49 +08:00
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
2021-02-11 02:14:50 +08:00
/** @typedef {import("../javascript/JavascriptModulesPlugin").StartupRenderContext} StartupRenderContext */
2020-02-20 03:25:49 +08:00
/** @typedef {import("../util/Hash")} Hash */
2025-09-11 08:10:10 +08:00
/ * *
* @ template T
* @ typedef { import ( "./AbstractLibraryPlugin" ) . LibraryContext < T > } LibraryContext < T >
* /
2020-02-20 03:25:49 +08:00
2021-05-11 15:31:46 +08:00
const KEYWORD _REGEX =
/^(await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|super|switch|static|this|throw|try|true|typeof|var|void|while|with|yield)$/ ;
const IDENTIFIER _REGEX =
/^[\p{L}\p{Nl}$_][\p{L}\p{Nl}$\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$/iu ;
2021-01-06 22:01:59 +08:00
/ * *
* Validates the library name by checking for keywords and valid characters
* @ param { string } name name to be validated
* @ returns { boolean } true , when valid
* /
2025-07-17 00:13:14 +08:00
const isNameValid = ( name ) =>
2024-07-31 11:31:11 +08:00
! KEYWORD _REGEX . test ( name ) && IDENTIFIER _REGEX . test ( name ) ;
2020-11-06 01:33:19 +08:00
2020-02-20 03:25:49 +08:00
/ * *
* @ param { string [ ] } accessor variable plus properties
* @ param { number } existingLength items of accessor that are existing already
* @ param { boolean = } initLast if the last property should also be initialized to an object
* @ returns { string } code to access the accessor while initializing
* /
const accessWithInit = ( accessor , existingLength , initLast = false ) => {
// This generates for [a, b, c, d]:
// (((a = typeof a === "undefined" ? {} : a).b = a.b || {}).c = a.b.c || {}).d
const base = accessor [ 0 ] ;
2020-02-26 23:13:38 +08:00
if ( accessor . length === 1 && ! initLast ) return base ;
2020-02-20 03:25:49 +08:00
let current =
2020-02-26 23:13:38 +08:00
existingLength > 0
2020-02-20 03:25:49 +08:00
? base
: ` ( ${ base } = typeof ${ base } === "undefined" ? {} : ${ base } ) ` ;
2020-02-26 23:13:38 +08:00
// i is the current position in accessor that has been printed
let i = 1 ;
// all properties printed so far (excluding base)
2023-06-22 08:59:10 +08:00
/** @type {string[] | undefined} */
2020-02-26 23:13:38 +08:00
let propsSoFar ;
// if there is existingLength, print all properties until this position as property access
if ( existingLength > i ) {
propsSoFar = accessor . slice ( 1 , existingLength ) ;
i = existingLength ;
current += propertyAccess ( propsSoFar ) ;
} else {
propsSoFar = [ ] ;
}
2020-03-10 09:59:46 +08:00
// all remaining properties (except the last one when initLast is not set)
2020-02-26 23:13:38 +08:00
// should be printed as initializer
const initUntil = initLast ? accessor . length : accessor . length - 1 ;
for ( ; i < initUntil ; i ++ ) {
2020-02-20 03:25:49 +08:00
const prop = accessor [ i ] ;
propsSoFar . push ( prop ) ;
current = ` ( ${ current } ${ propertyAccess ( [ prop ] ) } = ${ base } ${ propertyAccess (
propsSoFar
) } || { } ) ` ;
}
2020-02-26 23:13:38 +08:00
// print the last property as property access if not yet printed
2025-07-02 20:10:54 +08:00
if ( i < accessor . length ) {
2020-02-26 23:13:38 +08:00
current = ` ${ current } ${ propertyAccess ( [ accessor [ accessor . length - 1 ] ] ) } ` ;
2025-07-02 20:10:54 +08:00
}
2020-02-26 23:13:38 +08:00
return current ;
2020-02-20 03:25:49 +08:00
} ;
2025-09-11 08:10:10 +08:00
/** @typedef {string[] | "global"} LibraryPrefix */
2020-02-20 03:25:49 +08:00
/ * *
2024-06-11 21:09:50 +08:00
* @ typedef { object } AssignLibraryPluginOptions
2020-02-20 03:25:49 +08:00
* @ property { LibraryType } type
2025-09-11 08:10:10 +08:00
* @ property { LibraryPrefix } prefix name prefix
2020-02-20 03:25:49 +08:00
* @ property { string | false } declare declare name as variable
2025-09-11 08:10:10 +08:00
* @ property { "error" | "static" | "copy" | "assign" } unnamed behavior for unnamed library name
* @ property { "copy" | "assign" = } named behavior for named library name
2020-02-20 03:25:49 +08:00
* /
2025-09-11 08:10:10 +08:00
/** @typedef {string | string[]} LibraryName */
2020-02-20 03:25:49 +08:00
/ * *
2024-06-11 21:09:50 +08:00
* @ typedef { object } AssignLibraryPluginParsed
2025-09-11 08:10:10 +08:00
* @ property { LibraryName } name
* @ property { LibraryExport = } export
2020-02-20 03:25:49 +08:00
* /
/ * *
* @ typedef { AssignLibraryPluginParsed } T
* @ extends { AbstractLibraryPlugin < AssignLibraryPluginParsed > }
* /
class AssignLibraryPlugin extends AbstractLibraryPlugin {
/ * *
* @ param { AssignLibraryPluginOptions } options the plugin options
* /
constructor ( options ) {
super ( {
pluginName : "AssignLibraryPlugin" ,
type : options . type
} ) ;
this . prefix = options . prefix ;
this . declare = options . declare ;
this . unnamed = options . unnamed ;
2021-01-04 23:12:48 +08:00
this . named = options . named || "assign" ;
2020-02-20 03:25:49 +08:00
}
/ * *
* @ param { LibraryOptions } library normalized library option
* @ returns { T | false } preprocess as needed by overriding
* /
parseOptions ( library ) {
2020-02-27 00:20:50 +08:00
const { name } = library ;
2020-02-20 03:25:49 +08:00
if ( this . unnamed === "error" ) {
if ( typeof name !== "string" && ! Array . isArray ( name ) ) {
2021-01-18 23:22:07 +08:00
throw new Error (
2021-02-05 06:11:11 +08:00
` Library name must be a string or string array. ${ AbstractLibraryPlugin . COMMON _LIBRARY _NAME _MESSAGE } `
2021-01-18 23:22:07 +08:00
) ;
2020-02-20 03:25:49 +08:00
}
2024-07-31 04:54:55 +08:00
} else if ( name && typeof name !== "string" && ! Array . isArray ( name ) ) {
throw new Error (
` Library name must be a string, string array or unset. ${ AbstractLibraryPlugin . COMMON _LIBRARY _NAME _MESSAGE } `
) ;
2020-02-20 03:25:49 +08:00
}
2025-09-11 08:10:10 +08:00
const _name = /** @type {LibraryName} */ ( name ) ;
2020-02-20 03:25:49 +08:00
return {
2024-07-31 04:09:42 +08:00
name : _name ,
2021-02-11 02:14:50 +08:00
export : library . export
2020-02-20 03:25:49 +08:00
} ;
}
/ * *
2021-02-11 02:14:50 +08:00
* @ param { Module } module the exporting entry module
* @ param { string } entryName the name of the entrypoint
2020-02-20 03:25:49 +08:00
* @ param { LibraryContext < T > } libraryContext context
2021-02-11 02:14:50 +08:00
* @ returns { void }
2020-02-20 03:25:49 +08:00
* /
2021-02-11 02:14:50 +08:00
finishEntryModule (
module ,
entryName ,
{ options , compilation , compilation : { moduleGraph } }
) {
const runtime = getEntryRuntime ( compilation , entryName ) ;
if ( options . export ) {
const exportsInfo = moduleGraph . getExportInfo (
module ,
Array . isArray ( options . export ) ? options . export [ 0 ] : options . export
) ;
exportsInfo . setUsed ( UsageState . Used , runtime ) ;
exportsInfo . canMangleUse = false ;
} else {
const exportsInfo = moduleGraph . getExportsInfo ( module ) ;
exportsInfo . setUsedInUnknownWay ( runtime ) ;
}
moduleGraph . addExtraReason ( module , "used as library export" ) ;
}
2023-06-22 08:59:10 +08:00
/ * *
* @ param { Compilation } compilation the compilation
2025-09-11 08:10:10 +08:00
* @ returns { LibraryPrefix } the prefix
2023-06-22 08:59:10 +08:00
* /
2021-02-11 02:14:50 +08:00
_getPrefix ( compilation ) {
return this . prefix === "global"
2021-11-24 01:40:03 +08:00
? [ compilation . runtimeTemplate . globalObject ]
2021-02-11 02:14:50 +08:00
: this . prefix ;
}
2023-06-22 08:59:10 +08:00
/ * *
* @ param { AssignLibraryPluginParsed } options the library options
* @ param { Chunk } chunk the chunk
* @ param { Compilation } compilation the compilation
2025-08-28 18:34:30 +08:00
* @ returns { string [ ] } the resolved full name
2023-06-22 08:59:10 +08:00
* /
2021-02-11 02:14:50 +08:00
_getResolvedFullName ( options , chunk , compilation ) {
const prefix = this . _getPrefix ( compilation ) ;
2025-07-03 17:06:45 +08:00
const fullName = options . name
? [
... prefix ,
... ( Array . isArray ( options . name ) ? options . name : [ options . name ] )
]
2025-09-11 08:10:10 +08:00
: /** @type {string[]} */ ( prefix ) ;
2025-07-17 00:13:14 +08:00
return fullName . map ( ( n ) =>
2020-02-20 03:25:49 +08:00
compilation . getPath ( n , {
chunk
} )
) ;
2021-02-11 02:14:50 +08:00
}
/ * *
* @ param { Source } source source
* @ param { RenderContext } renderContext render context
* @ param { LibraryContext < T > } libraryContext context
* @ returns { Source } source with library export
* /
render ( source , { chunk } , { options , compilation } ) {
const fullNameResolved = this . _getResolvedFullName (
options ,
chunk ,
compilation
) ;
2020-02-20 03:25:49 +08:00
if ( this . declare ) {
const base = fullNameResolved [ 0 ] ;
2020-11-06 01:33:19 +08:00
if ( ! isNameValid ( base ) ) {
throw new Error (
2021-01-06 22:01:59 +08:00
` Library name base ( ${ base } ) must be a valid identifier when using a var declaring library type. Either use a valid identifier (e. g. ${ Template . toIdentifier (
base
2021-02-05 06:11:11 +08:00
) } ) or use a different library type ( e . g . 'type: "global"' , which assign a property on the global scope instead of declaring a variable ) . $ {
AbstractLibraryPlugin . COMMON _LIBRARY _NAME _MESSAGE
} `
2020-11-06 01:33:19 +08:00
) ;
}
2021-02-11 02:14:50 +08:00
source = new ConcatSource ( ` ${ this . declare } ${ base } ; \n ` , source ) ;
2020-02-20 03:25:49 +08:00
}
2021-02-11 02:14:50 +08:00
return source ;
}
/ * *
* @ param { Module } module the exporting entry module
* @ param { RenderContext } renderContext render context
* @ param { LibraryContext < T > } libraryContext context
* @ returns { string | undefined } bailout reason
* /
2022-02-07 22:21:36 +08:00
embedInRuntimeBailout (
module ,
{ chunk , codeGenerationResults } ,
{ options , compilation }
) {
2022-02-11 22:59:35 +08:00
const { data } = codeGenerationResults . get ( module , chunk . runtime ) ;
const topLevelDeclarations =
( data && data . get ( "topLevelDeclarations" ) ) ||
( module . buildInfo && module . buildInfo . topLevelDeclarations ) ;
2025-07-02 20:10:54 +08:00
if ( ! topLevelDeclarations ) {
2021-02-11 02:14:50 +08:00
return "it doesn't tell about top level declarations." ;
2025-07-02 20:10:54 +08:00
}
2021-02-11 02:14:50 +08:00
const fullNameResolved = this . _getResolvedFullName (
options ,
chunk ,
compilation
) ;
const base = fullNameResolved [ 0 ] ;
2025-07-02 20:10:54 +08:00
if ( topLevelDeclarations . has ( base ) ) {
2022-02-11 22:59:35 +08:00
return ` it declares ' ${ base } ' on top-level, which conflicts with the current library output. ` ;
2025-07-02 20:10:54 +08:00
}
2021-02-11 02:14:50 +08:00
}
2021-03-15 23:37:19 +08:00
/ * *
* @ param { RenderContext } renderContext render context
* @ param { LibraryContext < T > } libraryContext context
* @ returns { string | undefined } bailout reason
* /
strictRuntimeBailout ( { chunk } , { options , compilation } ) {
if (
this . declare ||
this . prefix === "global" ||
this . prefix . length > 0 ||
! options . name
) {
return ;
}
return "a global variable is assign and maybe created" ;
}
2021-02-11 02:14:50 +08:00
/ * *
* @ param { Source } source source
* @ param { Module } module module
* @ param { StartupRenderContext } renderContext render context
* @ param { LibraryContext < T > } libraryContext context
* @ returns { Source } source with library export
* /
2022-01-10 21:33:34 +08:00
renderStartup (
source ,
module ,
{ moduleGraph , chunk } ,
{ options , compilation }
) {
2021-02-11 02:14:50 +08:00
const fullNameResolved = this . _getResolvedFullName (
options ,
chunk ,
compilation
) ;
2022-01-10 21:33:34 +08:00
const staticExports = this . unnamed === "static" ;
2021-02-11 02:14:50 +08:00
const exportAccess = options . export
? propertyAccess (
Array . isArray ( options . export ) ? options . export : [ options . export ]
2024-07-31 05:43:19 +08:00
)
2021-02-11 02:14:50 +08:00
: "" ;
const result = new ConcatSource ( source ) ;
2021-12-19 03:17:50 +08:00
if ( staticExports ) {
2021-12-16 06:54:31 +08:00
const exportsInfo = moduleGraph . getExportsInfo ( module ) ;
2022-01-10 21:33:34 +08:00
const exportTarget = accessWithInit (
fullNameResolved ,
this . _getPrefix ( compilation ) . length ,
true
) ;
2025-03-11 14:18:11 +08:00
2025-09-11 08:10:10 +08:00
/** @type {ExportInfoName[]} */
2025-03-11 14:18:11 +08:00
const provided = [ ] ;
2021-12-16 06:54:31 +08:00
for ( const exportInfo of exportsInfo . orderedExports ) {
if ( ! exportInfo . provided ) continue ;
2022-01-10 21:33:34 +08:00
const nameAccess = propertyAccess ( [ exportInfo . name ] ) ;
2021-12-16 06:54:31 +08:00
result . add (
2023-05-26 23:34:26 +08:00
` ${ exportTarget } ${ nameAccess } = ${ RuntimeGlobals . exports } ${ exportAccess } ${ nameAccess } ; \n `
2021-12-16 06:54:31 +08:00
) ;
2025-03-11 14:18:11 +08:00
provided . push ( exportInfo . name ) ;
}
const webpackExportTarget = accessWithInit (
fullNameResolved ,
this . _getPrefix ( compilation ) . length ,
true
) ;
/** @type {string} */
let exports = RuntimeGlobals . exports ;
if ( exportAccess ) {
result . add (
` var __webpack_exports_export__ = ${ RuntimeGlobals . exports } ${ exportAccess } ; \n `
) ;
2025-07-02 20:10:54 +08:00
2025-03-11 14:18:11 +08:00
exports = "__webpack_exports_export__" ;
2021-12-16 06:54:31 +08:00
}
2025-03-11 14:18:11 +08:00
result . add ( ` for(var __webpack_i__ in ${ exports } ) { \n ` ) ;
const hasProvided = provided . length > 0 ;
if ( hasProvided ) {
result . add (
` if ( ${ JSON . stringify ( provided ) } .indexOf(__webpack_i__) === -1) { \n `
) ;
}
result . add (
2025-07-09 01:23:18 +08:00
` ${
hasProvided ? " " : ""
} $ { webpackExportTarget } [ _ _webpack _i _ _ ] = $ { exports } [ _ _webpack _i _ _ ] ; \ n `
2025-03-11 14:18:11 +08:00
) ;
2025-07-09 01:23:18 +08:00
if ( hasProvided ) {
result . add ( " }\n" ) ;
}
result . add ( "}\n" ) ;
2021-12-16 06:54:31 +08:00
result . add (
2022-01-10 21:33:34 +08:00
` Object.defineProperty( ${ exportTarget } , "__esModule", { value: true }); \n `
2021-12-16 06:54:31 +08:00
) ;
} else if ( options . name ? this . named === "copy" : this . unnamed === "copy" ) {
2020-02-20 03:25:49 +08:00
result . add (
2021-02-11 02:14:50 +08:00
` var __webpack_export_target__ = ${ accessWithInit (
2020-02-20 03:25:49 +08:00
fullNameResolved ,
2021-02-11 02:14:50 +08:00
this . _getPrefix ( compilation ) . length ,
2020-02-20 03:25:49 +08:00
true
2021-02-11 02:14:50 +08:00
) } ; \ n `
) ;
2024-06-11 21:09:50 +08:00
/** @type {string} */
2023-05-26 23:34:26 +08:00
let exports = RuntimeGlobals . exports ;
2021-02-11 02:14:50 +08:00
if ( exportAccess ) {
result . add (
2023-05-26 23:34:26 +08:00
` var __webpack_exports_export__ = ${ RuntimeGlobals . exports } ${ exportAccess } ; \n `
2021-02-11 02:14:50 +08:00
) ;
2025-07-02 20:10:54 +08:00
2021-02-11 02:14:50 +08:00
exports = "__webpack_exports_export__" ;
}
result . add (
2024-11-05 23:29:13 +08:00
` for(var __webpack_i__ in ${ exports } ) __webpack_export_target__[__webpack_i__] = ${ exports } [__webpack_i__]; \n `
2021-02-11 02:14:50 +08:00
) ;
result . add (
` if( ${ exports } .__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true }); \n `
2020-02-20 03:25:49 +08:00
) ;
} else {
result . add (
2021-02-11 02:14:50 +08:00
` ${ accessWithInit (
fullNameResolved ,
this . _getPrefix ( compilation ) . length ,
false
2023-05-26 23:34:26 +08:00
) } = $ { RuntimeGlobals . exports } $ { exportAccess } ; \ n `
2020-02-20 03:25:49 +08:00
) ;
}
return result ;
}
2021-02-11 02:14:50 +08:00
/ * *
* @ param { Chunk } chunk the chunk
2025-08-28 18:34:30 +08:00
* @ param { RuntimeRequirements } set runtime requirements
2021-02-11 02:14:50 +08:00
* @ param { LibraryContext < T > } libraryContext context
* @ returns { void }
* /
runtimeRequirements ( chunk , set , libraryContext ) {
2024-07-09 20:02:25 +08:00
set . add ( RuntimeGlobals . exports ) ;
2021-02-11 02:14:50 +08:00
}
2020-02-20 03:25:49 +08:00
/ * *
* @ param { Chunk } chunk the chunk
* @ param { Hash } hash hash
* @ param { ChunkHashContext } chunkHashContext chunk hash context
* @ param { LibraryContext < T > } libraryContext context
* @ returns { void }
* /
chunkHash ( chunk , hash , chunkHashContext , { options , compilation } ) {
hash . update ( "AssignLibraryPlugin" ) ;
2021-11-24 01:40:03 +08:00
const fullNameResolved = this . _getResolvedFullName (
options ,
chunk ,
compilation
2020-02-20 03:25:49 +08:00
) ;
2021-02-11 02:14:50 +08:00
if ( options . name ? this . named === "copy" : this . unnamed === "copy" ) {
2020-02-20 03:25:49 +08:00
hash . update ( "copy" ) ;
}
if ( this . declare ) {
hash . update ( this . declare ) ;
}
hash . update ( fullNameResolved . join ( "." ) ) ;
2021-02-11 02:14:50 +08:00
if ( options . export ) {
hash . update ( ` ${ options . export } ` ) ;
}
2020-02-20 03:25:49 +08:00
}
}
module . exports = AssignLibraryPlugin ;