ExternalModule.js 29 KB

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