ExternalModule.js 33 KB

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