ExternalModule.js 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { OriginalSource, RawSource } = require("webpack-sources");
  7. const ConcatenationScope = require("./ConcatenationScope");
  8. const EnvironmentNotSupportAsyncWarning = require("./EnvironmentNotSupportAsyncWarning");
  9. const { UsageState } = require("./ExportsInfo");
  10. const InitFragment = require("./InitFragment");
  11. const Module = require("./Module");
  12. const {
  13. JS_TYPES,
  14. CSS_URL_TYPES,
  15. CSS_IMPORT_TYPES
  16. } = require("./ModuleSourceTypesConstants");
  17. const { JAVASCRIPT_MODULE_TYPE_DYNAMIC } = require("./ModuleTypeConstants");
  18. const RuntimeGlobals = require("./RuntimeGlobals");
  19. const Template = require("./Template");
  20. const { DEFAULTS } = require("./config/defaults");
  21. const StaticExportsDependency = require("./dependencies/StaticExportsDependency");
  22. const createHash = require("./util/createHash");
  23. const extractUrlAndGlobal = require("./util/extractUrlAndGlobal");
  24. const makeSerializable = require("./util/makeSerializable");
  25. const propertyAccess = require("./util/propertyAccess");
  26. const { register } = require("./util/serialization");
  27. /** @typedef {import("webpack-sources").Source} Source */
  28. /** @typedef {import("../declarations/WebpackOptions").HashFunction} HashFunction */
  29. /** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
  30. /** @typedef {import("./Chunk")} Chunk */
  31. /** @typedef {import("./ChunkGraph")} ChunkGraph */
  32. /** @typedef {import("./Compilation")} Compilation */
  33. /** @typedef {import("./Compilation").UnsafeCacheData} UnsafeCacheData */
  34. /** @typedef {import("./Dependency").UpdateHashContext} UpdateHashContext */
  35. /** @typedef {import("./DependencyTemplates")} DependencyTemplates */
  36. /** @typedef {import("./ExportsInfo")} ExportsInfo */
  37. /** @typedef {import("./Generator").GenerateContext} GenerateContext */
  38. /** @typedef {import("./Generator").SourceTypes} SourceTypes */
  39. /** @typedef {import("./Module").BuildCallback} BuildCallback */
  40. /** @typedef {import("./Module").BuildInfo} BuildInfo */
  41. /** @typedef {import("./Module").CodeGenerationContext} CodeGenerationContext */
  42. /** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
  43. /** @typedef {import("./Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
  44. /** @typedef {import("./Module").LibIdentOptions} LibIdentOptions */
  45. /** @typedef {import("./Module").NeedBuildCallback} NeedBuildCallback */
  46. /** @typedef {import("./Module").NeedBuildContext} NeedBuildContext */
  47. /** @typedef {import("./Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
  48. /** @typedef {import("./ModuleGraph")} ModuleGraph */
  49. /** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
  50. /** @typedef {import("./RequestShortener")} RequestShortener */
  51. /** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
  52. /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
  53. /** @typedef {import("./WebpackError")} WebpackError */
  54. /** @typedef {import("./javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */
  55. /** @typedef {import("./javascript/JavascriptParser").ImportAttributes} ImportAttributes */
  56. /** @typedef {import("./serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
  57. /** @typedef {import("./serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
  58. /** @typedef {import("./util/Hash")} Hash */
  59. /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
  60. /** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
  61. /** @typedef {{ attributes?: ImportAttributes, externalType: "import" | "module" | undefined }} ImportDependencyMeta */
  62. /** @typedef {{ layer?: string, supports?: string, media?: string }} CssImportDependencyMeta */
  63. /** @typedef {{ sourceType: "css-url" }} AssetDependencyMeta */
  64. /** @typedef {ImportDependencyMeta | CssImportDependencyMeta | AssetDependencyMeta} DependencyMeta */
  65. /**
  66. * @typedef {object} SourceData
  67. * @property {boolean=} iife
  68. * @property {string=} init
  69. * @property {string} expression
  70. * @property {InitFragment<ChunkRenderContext>[]=} chunkInitFragments
  71. * @property {ReadOnlyRuntimeRequirements=} runtimeRequirements
  72. */
  73. const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]);
  74. const RUNTIME_REQUIREMENTS_FOR_SCRIPT = new Set([RuntimeGlobals.loadScript]);
  75. const RUNTIME_REQUIREMENTS_FOR_MODULE = new Set([
  76. RuntimeGlobals.definePropertyGetters
  77. ]);
  78. const EMPTY_RUNTIME_REQUIREMENTS = new Set([]);
  79. /**
  80. * @param {string|string[]} variableName the variable name or path
  81. * @param {string} type the module system
  82. * @returns {SourceData} the generated source
  83. */
  84. const getSourceForGlobalVariableExternal = (variableName, type) => {
  85. if (!Array.isArray(variableName)) {
  86. // make it an array as the look up works the same basically
  87. variableName = [variableName];
  88. }
  89. // needed for e.g. window["some"]["thing"]
  90. const objectLookup = variableName.map(r => `[${JSON.stringify(r)}]`).join("");
  91. return {
  92. iife: type === "this",
  93. expression: `${type}${objectLookup}`
  94. };
  95. };
  96. /**
  97. * @param {string|string[]} moduleAndSpecifiers the module request
  98. * @returns {SourceData} the generated source
  99. */
  100. const getSourceForCommonJsExternal = moduleAndSpecifiers => {
  101. if (!Array.isArray(moduleAndSpecifiers)) {
  102. return {
  103. expression: `require(${JSON.stringify(moduleAndSpecifiers)})`
  104. };
  105. }
  106. const moduleName = moduleAndSpecifiers[0];
  107. return {
  108. expression: `require(${JSON.stringify(moduleName)})${propertyAccess(
  109. moduleAndSpecifiers,
  110. 1
  111. )}`
  112. };
  113. };
  114. /**
  115. * @param {string|string[]} moduleAndSpecifiers the module request
  116. * @param {string} importMetaName import.meta name
  117. * @param {boolean} needPrefix need to use `node:` prefix for `module` import
  118. * @returns {SourceData} the generated source
  119. */
  120. const getSourceForCommonJsExternalInNodeModule = (
  121. moduleAndSpecifiers,
  122. importMetaName,
  123. needPrefix
  124. ) => {
  125. const chunkInitFragments = [
  126. new InitFragment(
  127. `import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "${
  128. needPrefix ? "node:" : ""
  129. }module";\n`,
  130. InitFragment.STAGE_HARMONY_IMPORTS,
  131. 0,
  132. "external module node-commonjs"
  133. )
  134. ];
  135. if (!Array.isArray(moduleAndSpecifiers)) {
  136. return {
  137. chunkInitFragments,
  138. expression: `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)(${JSON.stringify(
  139. moduleAndSpecifiers
  140. )})`
  141. };
  142. }
  143. const moduleName = moduleAndSpecifiers[0];
  144. return {
  145. chunkInitFragments,
  146. expression: `__WEBPACK_EXTERNAL_createRequire(${importMetaName}.url)(${JSON.stringify(
  147. moduleName
  148. )})${propertyAccess(moduleAndSpecifiers, 1)}`
  149. };
  150. };
  151. /**
  152. * @param {string|string[]} moduleAndSpecifiers the module request
  153. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  154. * @param {ImportDependencyMeta=} dependencyMeta the dependency meta
  155. * @returns {SourceData} the generated source
  156. */
  157. const getSourceForImportExternal = (
  158. moduleAndSpecifiers,
  159. runtimeTemplate,
  160. dependencyMeta
  161. ) => {
  162. const importName = runtimeTemplate.outputOptions.importFunctionName;
  163. if (
  164. !runtimeTemplate.supportsDynamicImport() &&
  165. (importName === "import" || importName === "module-import")
  166. ) {
  167. throw new Error(
  168. "The target environment doesn't support 'import()' so it's not possible to use external type 'import'"
  169. );
  170. }
  171. const attributes =
  172. dependencyMeta && dependencyMeta.attributes
  173. ? dependencyMeta.attributes._isLegacyAssert
  174. ? `, { assert: ${JSON.stringify(
  175. dependencyMeta.attributes,
  176. importAssertionReplacer
  177. )} }`
  178. : `, { with: ${JSON.stringify(dependencyMeta.attributes)} }`
  179. : "";
  180. if (!Array.isArray(moduleAndSpecifiers)) {
  181. return {
  182. expression: `${importName}(${JSON.stringify(
  183. moduleAndSpecifiers
  184. )}${attributes});`
  185. };
  186. }
  187. if (moduleAndSpecifiers.length === 1) {
  188. return {
  189. expression: `${importName}(${JSON.stringify(
  190. moduleAndSpecifiers[0]
  191. )}${attributes});`
  192. };
  193. }
  194. const moduleName = moduleAndSpecifiers[0];
  195. return {
  196. expression: `${importName}(${JSON.stringify(
  197. moduleName
  198. )}${attributes}).then(${runtimeTemplate.returningFunction(
  199. `module${propertyAccess(moduleAndSpecifiers, 1)}`,
  200. "module"
  201. )});`
  202. };
  203. };
  204. /**
  205. * @template {{ [key: string]: string }} T
  206. * @param {keyof T} key key
  207. * @param {T[keyof T]} value value
  208. * @returns {undefined | T[keyof T]} replaced value
  209. */
  210. const importAssertionReplacer = (key, value) => {
  211. if (key === "_isLegacyAssert") {
  212. return;
  213. }
  214. return value;
  215. };
  216. /**
  217. * @extends {InitFragment<ChunkRenderContext>}
  218. */
  219. class ModuleExternalInitFragment extends InitFragment {
  220. /**
  221. * @param {string} request import source
  222. * @param {string=} ident recomputed ident
  223. * @param {ImportDependencyMeta=} dependencyMeta the dependency meta
  224. * @param {HashFunction=} hashFunction the hash function to use
  225. */
  226. constructor(
  227. request,
  228. ident,
  229. dependencyMeta,
  230. hashFunction = DEFAULTS.HASH_FUNCTION
  231. ) {
  232. if (ident === undefined) {
  233. ident = Template.toIdentifier(request);
  234. if (ident !== request) {
  235. ident += `_${createHash(hashFunction)
  236. .update(request)
  237. .digest("hex")
  238. .slice(0, 8)}`;
  239. }
  240. }
  241. const identifier = `__WEBPACK_EXTERNAL_MODULE_${ident}__`;
  242. super(
  243. `import * as ${identifier} from ${JSON.stringify(request)}${
  244. dependencyMeta && dependencyMeta.attributes
  245. ? dependencyMeta.attributes._isLegacyAssert
  246. ? ` assert ${JSON.stringify(
  247. dependencyMeta.attributes,
  248. importAssertionReplacer
  249. )}`
  250. : ` with ${JSON.stringify(dependencyMeta.attributes)}`
  251. : ""
  252. };\n`,
  253. InitFragment.STAGE_HARMONY_IMPORTS,
  254. 0,
  255. `external module import ${ident}`
  256. );
  257. this._ident = ident;
  258. this._request = request;
  259. this._dependencyMeta = request;
  260. this._identifier = identifier;
  261. }
  262. getNamespaceIdentifier() {
  263. return this._identifier;
  264. }
  265. }
  266. register(
  267. ModuleExternalInitFragment,
  268. "webpack/lib/ExternalModule",
  269. "ModuleExternalInitFragment",
  270. {
  271. serialize(obj, { write }) {
  272. write(obj._request);
  273. write(obj._ident);
  274. write(obj._dependencyMeta);
  275. },
  276. deserialize({ read }) {
  277. return new ModuleExternalInitFragment(read(), read(), read());
  278. }
  279. }
  280. );
  281. /**
  282. * @param {string} input input
  283. * @param {ExportsInfo} exportsInfo the exports info
  284. * @param {RuntimeSpec=} runtime the runtime
  285. * @param {RuntimeTemplate=} runtimeTemplate the runtime template
  286. * @returns {string | undefined} the module remapping
  287. */
  288. const generateModuleRemapping = (
  289. input,
  290. exportsInfo,
  291. runtime,
  292. runtimeTemplate
  293. ) => {
  294. if (exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused) {
  295. const properties = [];
  296. for (const exportInfo of exportsInfo.orderedExports) {
  297. const used = exportInfo.getUsedName(exportInfo.name, runtime);
  298. if (!used) continue;
  299. const nestedInfo = exportInfo.getNestedExportsInfo();
  300. if (nestedInfo) {
  301. const nestedExpr = generateModuleRemapping(
  302. `${input}${propertyAccess([exportInfo.name])}`,
  303. nestedInfo
  304. );
  305. if (nestedExpr) {
  306. properties.push(`[${JSON.stringify(used)}]: y(${nestedExpr})`);
  307. continue;
  308. }
  309. }
  310. properties.push(
  311. `[${JSON.stringify(used)}]: ${
  312. /** @type {RuntimeTemplate} */ (runtimeTemplate).returningFunction(
  313. `${input}${propertyAccess([exportInfo.name])}`
  314. )
  315. }`
  316. );
  317. }
  318. return `x({ ${properties.join(", ")} })`;
  319. }
  320. };
  321. /**
  322. * @param {string|string[]} moduleAndSpecifiers the module request
  323. * @param {ExportsInfo} exportsInfo exports info of this module
  324. * @param {RuntimeSpec} runtime the runtime
  325. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  326. * @param {ImportDependencyMeta} dependencyMeta the dependency meta
  327. * @returns {SourceData} the generated source
  328. */
  329. const getSourceForModuleExternal = (
  330. moduleAndSpecifiers,
  331. exportsInfo,
  332. runtime,
  333. runtimeTemplate,
  334. dependencyMeta
  335. ) => {
  336. if (!Array.isArray(moduleAndSpecifiers))
  337. moduleAndSpecifiers = [moduleAndSpecifiers];
  338. const initFragment = new ModuleExternalInitFragment(
  339. moduleAndSpecifiers[0],
  340. undefined,
  341. dependencyMeta,
  342. runtimeTemplate.outputOptions.hashFunction
  343. );
  344. const baseAccess = `${initFragment.getNamespaceIdentifier()}${propertyAccess(
  345. moduleAndSpecifiers,
  346. 1
  347. )}`;
  348. const moduleRemapping = generateModuleRemapping(
  349. baseAccess,
  350. exportsInfo,
  351. runtime,
  352. runtimeTemplate
  353. );
  354. const expression = moduleRemapping || baseAccess;
  355. return {
  356. expression,
  357. init: moduleRemapping
  358. ? `var x = ${runtimeTemplate.basicFunction(
  359. "y",
  360. `var x = {}; ${RuntimeGlobals.definePropertyGetters}(x, y); return x`
  361. )} \nvar y = ${runtimeTemplate.returningFunction(
  362. runtimeTemplate.returningFunction("x"),
  363. "x"
  364. )}`
  365. : undefined,
  366. runtimeRequirements: moduleRemapping
  367. ? RUNTIME_REQUIREMENTS_FOR_MODULE
  368. : undefined,
  369. chunkInitFragments: [initFragment]
  370. };
  371. };
  372. /**
  373. * @param {string|string[]} urlAndGlobal the script request
  374. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  375. * @returns {SourceData} the generated source
  376. */
  377. const getSourceForScriptExternal = (urlAndGlobal, runtimeTemplate) => {
  378. if (typeof urlAndGlobal === "string") {
  379. urlAndGlobal = extractUrlAndGlobal(urlAndGlobal);
  380. }
  381. const url = urlAndGlobal[0];
  382. const globalName = urlAndGlobal[1];
  383. return {
  384. init: "var __webpack_error__ = new Error();",
  385. expression: `new Promise(${runtimeTemplate.basicFunction(
  386. "resolve, reject",
  387. [
  388. `if(typeof ${globalName} !== "undefined") return resolve();`,
  389. `${RuntimeGlobals.loadScript}(${JSON.stringify(
  390. url
  391. )}, ${runtimeTemplate.basicFunction("event", [
  392. `if(typeof ${globalName} !== "undefined") return resolve();`,
  393. "var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
  394. "var realSrc = event && event.target && event.target.src;",
  395. "__webpack_error__.message = 'Loading script failed.\\n(' + errorType + ': ' + realSrc + ')';",
  396. "__webpack_error__.name = 'ScriptExternalLoadError';",
  397. "__webpack_error__.type = errorType;",
  398. "__webpack_error__.request = realSrc;",
  399. "reject(__webpack_error__);"
  400. ])}, ${JSON.stringify(globalName)});`
  401. ]
  402. )}).then(${runtimeTemplate.returningFunction(
  403. `${globalName}${propertyAccess(urlAndGlobal, 2)}`
  404. )})`,
  405. runtimeRequirements: RUNTIME_REQUIREMENTS_FOR_SCRIPT
  406. };
  407. };
  408. /**
  409. * @param {string} variableName the variable name to check
  410. * @param {string} request the request path
  411. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  412. * @returns {string} the generated source
  413. */
  414. const checkExternalVariable = (variableName, request, runtimeTemplate) =>
  415. `if(typeof ${variableName} === 'undefined') { ${runtimeTemplate.throwMissingModuleErrorBlock(
  416. { request }
  417. )} }\n`;
  418. /**
  419. * @param {string|number} id the module id
  420. * @param {boolean} optional true, if the module is optional
  421. * @param {string|string[]} request the request path
  422. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  423. * @returns {SourceData} the generated source
  424. */
  425. const getSourceForAmdOrUmdExternal = (
  426. id,
  427. optional,
  428. request,
  429. runtimeTemplate
  430. ) => {
  431. const externalVariable = `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
  432. `${id}`
  433. )}__`;
  434. return {
  435. init: optional
  436. ? checkExternalVariable(
  437. externalVariable,
  438. Array.isArray(request) ? request.join(".") : request,
  439. runtimeTemplate
  440. )
  441. : undefined,
  442. expression: externalVariable
  443. };
  444. };
  445. /**
  446. * @param {boolean} optional true, if the module is optional
  447. * @param {string|string[]} request the request path
  448. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  449. * @returns {SourceData} the generated source
  450. */
  451. const getSourceForDefaultCase = (optional, request, runtimeTemplate) => {
  452. if (!Array.isArray(request)) {
  453. // make it an array as the look up works the same basically
  454. request = [request];
  455. }
  456. const variableName = request[0];
  457. const objectLookup = propertyAccess(request, 1);
  458. return {
  459. init: optional
  460. ? checkExternalVariable(variableName, request.join("."), runtimeTemplate)
  461. : undefined,
  462. expression: `${variableName}${objectLookup}`
  463. };
  464. };
  465. /** @typedef {Record<string, string | string[]>} RequestRecord */
  466. class ExternalModule extends Module {
  467. /**
  468. * @param {string | string[] | RequestRecord} request request
  469. * @param {string} type type
  470. * @param {string} userRequest user request
  471. * @param {DependencyMeta=} dependencyMeta dependency meta
  472. */
  473. constructor(request, type, userRequest, dependencyMeta) {
  474. super(JAVASCRIPT_MODULE_TYPE_DYNAMIC, null);
  475. // Info from Factory
  476. /** @type {string | string[] | Record<string, string | string[]>} */
  477. this.request = request;
  478. /** @type {string} */
  479. this.externalType = type;
  480. /** @type {string} */
  481. this.userRequest = userRequest;
  482. /** @type {DependencyMeta=} */
  483. this.dependencyMeta = dependencyMeta;
  484. }
  485. /**
  486. * @returns {SourceTypes} types available (do not mutate)
  487. */
  488. getSourceTypes() {
  489. if (
  490. this.externalType === "asset" &&
  491. this.dependencyMeta &&
  492. /** @type {AssetDependencyMeta} */
  493. (this.dependencyMeta).sourceType === "css-url"
  494. ) {
  495. return CSS_URL_TYPES;
  496. } else if (this.externalType === "css-import") {
  497. return CSS_IMPORT_TYPES;
  498. }
  499. return JS_TYPES;
  500. }
  501. /**
  502. * @param {LibIdentOptions} options options
  503. * @returns {string | null} an identifier for library inclusion
  504. */
  505. libIdent(options) {
  506. return this.userRequest;
  507. }
  508. /**
  509. * @param {Chunk} chunk the chunk which condition should be checked
  510. * @param {Compilation} compilation the compilation
  511. * @returns {boolean} true, if the chunk is ok for the module
  512. */
  513. chunkCondition(chunk, { chunkGraph }) {
  514. return this.externalType === "css-import"
  515. ? true
  516. : chunkGraph.getNumberOfEntryModules(chunk) > 0;
  517. }
  518. /**
  519. * @returns {string} a unique identifier of the module
  520. */
  521. identifier() {
  522. return `external ${this._resolveExternalType(this.externalType)} ${JSON.stringify(this.request)}`;
  523. }
  524. /**
  525. * @param {RequestShortener} requestShortener the request shortener
  526. * @returns {string} a user readable identifier of the module
  527. */
  528. readableIdentifier(requestShortener) {
  529. return `external ${JSON.stringify(this.request)}`;
  530. }
  531. /**
  532. * @param {NeedBuildContext} context context info
  533. * @param {NeedBuildCallback} callback callback function, returns true, if the module needs a rebuild
  534. * @returns {void}
  535. */
  536. needBuild(context, callback) {
  537. return callback(null, !this.buildMeta);
  538. }
  539. /**
  540. * @param {WebpackOptions} options webpack options
  541. * @param {Compilation} compilation the compilation
  542. * @param {ResolverWithOptions} resolver the resolver
  543. * @param {InputFileSystem} fs the file system
  544. * @param {BuildCallback} callback callback function
  545. * @returns {void}
  546. */
  547. build(options, compilation, resolver, fs, callback) {
  548. this.buildMeta = {
  549. async: false,
  550. exportsType: undefined
  551. };
  552. this.buildInfo = {
  553. strict: true,
  554. topLevelDeclarations: new Set(),
  555. module: compilation.outputOptions.module
  556. };
  557. const { request, externalType } = this._getRequestAndExternalType();
  558. this.buildMeta.exportsType = "dynamic";
  559. let canMangle = false;
  560. this.clearDependenciesAndBlocks();
  561. switch (externalType) {
  562. case "this":
  563. this.buildInfo.strict = false;
  564. break;
  565. case "system":
  566. if (!Array.isArray(request) || request.length === 1) {
  567. this.buildMeta.exportsType = "namespace";
  568. canMangle = true;
  569. }
  570. break;
  571. case "module":
  572. if (this.buildInfo.module) {
  573. if (!Array.isArray(request) || request.length === 1) {
  574. this.buildMeta.exportsType = "namespace";
  575. canMangle = true;
  576. }
  577. } else {
  578. this.buildMeta.async = true;
  579. EnvironmentNotSupportAsyncWarning.check(
  580. this,
  581. compilation.runtimeTemplate,
  582. "external module"
  583. );
  584. if (!Array.isArray(request) || request.length === 1) {
  585. this.buildMeta.exportsType = "namespace";
  586. canMangle = false;
  587. }
  588. }
  589. break;
  590. case "script":
  591. this.buildMeta.async = true;
  592. EnvironmentNotSupportAsyncWarning.check(
  593. this,
  594. compilation.runtimeTemplate,
  595. "external script"
  596. );
  597. break;
  598. case "promise":
  599. this.buildMeta.async = true;
  600. EnvironmentNotSupportAsyncWarning.check(
  601. this,
  602. compilation.runtimeTemplate,
  603. "external promise"
  604. );
  605. break;
  606. case "import":
  607. this.buildMeta.async = true;
  608. EnvironmentNotSupportAsyncWarning.check(
  609. this,
  610. compilation.runtimeTemplate,
  611. "external import"
  612. );
  613. if (!Array.isArray(request) || request.length === 1) {
  614. this.buildMeta.exportsType = "namespace";
  615. canMangle = false;
  616. }
  617. break;
  618. }
  619. this.addDependency(new StaticExportsDependency(true, canMangle));
  620. callback();
  621. }
  622. /**
  623. * restore unsafe cache data
  624. * @param {UnsafeCacheData} unsafeCacheData data from getUnsafeCacheData
  625. * @param {NormalModuleFactory} normalModuleFactory the normal module factory handling the unsafe caching
  626. */
  627. restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory) {
  628. this._restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory);
  629. }
  630. /**
  631. * @param {ConcatenationBailoutReasonContext} context context
  632. * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
  633. */
  634. getConcatenationBailoutReason({ moduleGraph }) {
  635. switch (this.externalType) {
  636. case "amd":
  637. case "amd-require":
  638. case "umd":
  639. case "umd2":
  640. case "system":
  641. case "jsonp":
  642. return `${this.externalType} externals can't be concatenated`;
  643. }
  644. return undefined;
  645. }
  646. _getRequestAndExternalType() {
  647. let { request, externalType } = this;
  648. if (typeof request === "object" && !Array.isArray(request))
  649. request = request[externalType];
  650. externalType = this._resolveExternalType(externalType);
  651. return { request, externalType };
  652. }
  653. /**
  654. * Resolve the detailed external type from the raw external type.
  655. * e.g. resolve "module" or "import" from "module-import" type
  656. * @param {string} externalType raw external type
  657. * @returns {string} resolved external type
  658. */
  659. _resolveExternalType(externalType) {
  660. if (externalType === "module-import") {
  661. if (
  662. this.dependencyMeta &&
  663. /** @type {ImportDependencyMeta} */
  664. (this.dependencyMeta).externalType
  665. ) {
  666. return /** @type {ImportDependencyMeta} */ (this.dependencyMeta)
  667. .externalType;
  668. }
  669. return "module";
  670. } else if (externalType === "asset") {
  671. if (
  672. this.dependencyMeta &&
  673. /** @type {AssetDependencyMeta} */
  674. (this.dependencyMeta).sourceType
  675. ) {
  676. return /** @type {AssetDependencyMeta} */ (this.dependencyMeta)
  677. .sourceType;
  678. }
  679. return "asset";
  680. }
  681. return externalType;
  682. }
  683. /**
  684. * @private
  685. * @param {string | string[]} request request
  686. * @param {string} externalType the external type
  687. * @param {RuntimeTemplate} runtimeTemplate the runtime template
  688. * @param {ModuleGraph} moduleGraph the module graph
  689. * @param {ChunkGraph} chunkGraph the chunk graph
  690. * @param {RuntimeSpec} runtime the runtime
  691. * @param {DependencyMeta | undefined} dependencyMeta the dependency meta
  692. * @returns {SourceData} the source data
  693. */
  694. _getSourceData(
  695. request,
  696. externalType,
  697. runtimeTemplate,
  698. moduleGraph,
  699. chunkGraph,
  700. runtime,
  701. dependencyMeta
  702. ) {
  703. switch (externalType) {
  704. case "this":
  705. case "window":
  706. case "self":
  707. return getSourceForGlobalVariableExternal(request, this.externalType);
  708. case "global":
  709. return getSourceForGlobalVariableExternal(
  710. request,
  711. runtimeTemplate.globalObject
  712. );
  713. case "commonjs":
  714. case "commonjs2":
  715. case "commonjs-module":
  716. case "commonjs-static":
  717. return getSourceForCommonJsExternal(request);
  718. case "node-commonjs":
  719. return /** @type {BuildInfo} */ (this.buildInfo).module
  720. ? getSourceForCommonJsExternalInNodeModule(
  721. request,
  722. /** @type {string} */
  723. (runtimeTemplate.outputOptions.importMetaName),
  724. /** @type {boolean} */
  725. (runtimeTemplate.supportNodePrefixForCoreModules())
  726. )
  727. : getSourceForCommonJsExternal(request);
  728. case "amd":
  729. case "amd-require":
  730. case "umd":
  731. case "umd2":
  732. case "system":
  733. case "jsonp": {
  734. const id = chunkGraph.getModuleId(this);
  735. return getSourceForAmdOrUmdExternal(
  736. id !== null ? id : this.identifier(),
  737. this.isOptional(moduleGraph),
  738. request,
  739. runtimeTemplate
  740. );
  741. }
  742. case "import":
  743. return getSourceForImportExternal(
  744. request,
  745. runtimeTemplate,
  746. /** @type {ImportDependencyMeta} */ (dependencyMeta)
  747. );
  748. case "script":
  749. return getSourceForScriptExternal(request, runtimeTemplate);
  750. case "module": {
  751. if (!(/** @type {BuildInfo} */ (this.buildInfo).module)) {
  752. if (!runtimeTemplate.supportsDynamicImport()) {
  753. throw new Error(
  754. `The target environment doesn't support dynamic import() syntax so it's not possible to use external type 'module' within a script${
  755. runtimeTemplate.supportsEcmaScriptModuleSyntax()
  756. ? "\nDid you mean to build a EcmaScript Module ('output.module: true')?"
  757. : ""
  758. }`
  759. );
  760. }
  761. return getSourceForImportExternal(
  762. request,
  763. runtimeTemplate,
  764. /** @type {ImportDependencyMeta} */ (dependencyMeta)
  765. );
  766. }
  767. if (!runtimeTemplate.supportsEcmaScriptModuleSyntax()) {
  768. throw new Error(
  769. "The target environment doesn't support EcmaScriptModule syntax so it's not possible to use external type 'module'"
  770. );
  771. }
  772. return getSourceForModuleExternal(
  773. request,
  774. moduleGraph.getExportsInfo(this),
  775. runtime,
  776. runtimeTemplate,
  777. /** @type {ImportDependencyMeta} */ (dependencyMeta)
  778. );
  779. }
  780. case "var":
  781. case "promise":
  782. case "const":
  783. case "let":
  784. case "assign":
  785. default:
  786. return getSourceForDefaultCase(
  787. this.isOptional(moduleGraph),
  788. request,
  789. runtimeTemplate
  790. );
  791. }
  792. }
  793. /**
  794. * @param {CodeGenerationContext} context context for code generation
  795. * @returns {CodeGenerationResult} result
  796. */
  797. codeGeneration({
  798. runtimeTemplate,
  799. moduleGraph,
  800. chunkGraph,
  801. runtime,
  802. concatenationScope
  803. }) {
  804. const { request, externalType } = this._getRequestAndExternalType();
  805. switch (externalType) {
  806. case "asset": {
  807. const sources = new Map();
  808. sources.set(
  809. "javascript",
  810. new RawSource(`module.exports = ${JSON.stringify(request)};`)
  811. );
  812. const data = new Map();
  813. data.set("url", { javascript: request });
  814. return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS, data };
  815. }
  816. case "css-url": {
  817. const sources = new Map();
  818. const data = new Map();
  819. data.set("url", { "css-url": request });
  820. return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS, data };
  821. }
  822. case "css-import": {
  823. const sources = new Map();
  824. const dependencyMeta = /** @type {CssImportDependencyMeta} */ (
  825. this.dependencyMeta
  826. );
  827. const layer =
  828. dependencyMeta.layer !== undefined
  829. ? ` layer(${dependencyMeta.layer})`
  830. : "";
  831. const supports = dependencyMeta.supports
  832. ? ` supports(${dependencyMeta.supports})`
  833. : "";
  834. const media = dependencyMeta.media ? ` ${dependencyMeta.media}` : "";
  835. sources.set(
  836. "css-import",
  837. new RawSource(
  838. `@import url(${JSON.stringify(
  839. request
  840. )})${layer}${supports}${media};`
  841. )
  842. );
  843. return {
  844. sources,
  845. runtimeRequirements: EMPTY_RUNTIME_REQUIREMENTS
  846. };
  847. }
  848. default: {
  849. const sourceData = this._getSourceData(
  850. request,
  851. externalType,
  852. runtimeTemplate,
  853. moduleGraph,
  854. chunkGraph,
  855. runtime,
  856. this.dependencyMeta
  857. );
  858. let sourceString = sourceData.expression;
  859. if (sourceData.iife)
  860. sourceString = `(function() { return ${sourceString}; }())`;
  861. if (concatenationScope) {
  862. sourceString = `${
  863. runtimeTemplate.supportsConst() ? "const" : "var"
  864. } ${ConcatenationScope.NAMESPACE_OBJECT_EXPORT} = ${sourceString};`;
  865. concatenationScope.registerNamespaceExport(
  866. ConcatenationScope.NAMESPACE_OBJECT_EXPORT
  867. );
  868. } else {
  869. sourceString = `module.exports = ${sourceString};`;
  870. }
  871. if (sourceData.init)
  872. sourceString = `${sourceData.init}\n${sourceString}`;
  873. let data;
  874. if (sourceData.chunkInitFragments) {
  875. data = new Map();
  876. data.set("chunkInitFragments", sourceData.chunkInitFragments);
  877. }
  878. const sources = new Map();
  879. if (this.useSourceMap || this.useSimpleSourceMap) {
  880. sources.set(
  881. "javascript",
  882. new OriginalSource(sourceString, this.identifier())
  883. );
  884. } else {
  885. sources.set("javascript", new RawSource(sourceString));
  886. }
  887. let runtimeRequirements = sourceData.runtimeRequirements;
  888. if (!concatenationScope) {
  889. if (!runtimeRequirements) {
  890. runtimeRequirements = RUNTIME_REQUIREMENTS;
  891. } else {
  892. const set = new Set(runtimeRequirements);
  893. set.add(RuntimeGlobals.module);
  894. runtimeRequirements = set;
  895. }
  896. }
  897. return {
  898. sources,
  899. runtimeRequirements:
  900. runtimeRequirements || EMPTY_RUNTIME_REQUIREMENTS,
  901. data
  902. };
  903. }
  904. }
  905. }
  906. /**
  907. * @param {string=} type the source type for which the size should be estimated
  908. * @returns {number} the estimated size of the module (must be non-zero)
  909. */
  910. size(type) {
  911. return 42;
  912. }
  913. /**
  914. * @param {Hash} hash the hash used to track dependencies
  915. * @param {UpdateHashContext} context context
  916. * @returns {void}
  917. */
  918. updateHash(hash, context) {
  919. const { chunkGraph } = context;
  920. hash.update(
  921. `${this._resolveExternalType(this.externalType)}${JSON.stringify(this.request)}${this.isOptional(
  922. chunkGraph.moduleGraph
  923. )}`
  924. );
  925. super.updateHash(hash, context);
  926. }
  927. /**
  928. * @param {ObjectSerializerContext} context context
  929. */
  930. serialize(context) {
  931. const { write } = context;
  932. write(this.request);
  933. write(this.externalType);
  934. write(this.userRequest);
  935. write(this.dependencyMeta);
  936. super.serialize(context);
  937. }
  938. /**
  939. * @param {ObjectDeserializerContext} context context
  940. */
  941. deserialize(context) {
  942. const { read } = context;
  943. this.request = read();
  944. this.externalType = read();
  945. this.userRequest = read();
  946. this.dependencyMeta = read();
  947. super.deserialize(context);
  948. }
  949. }
  950. makeSerializable(ExternalModule, "webpack/lib/ExternalModule");
  951. module.exports = ExternalModule;