ExternalModule.js 30 KB

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