ExternalModule.js 32 KB

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