提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1160 lines
33 KiB

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