ExternalModule.js 37 KB

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