InlineExports.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Haijie Xie @hai-x
  4. */
  5. "use strict";
  6. const makeSerializable = require("../util/makeSerializable");
  7. const { propertyAccess } = require("../util/property");
  8. /**
  9. * @import {
  10. * ObjectDeserializerContext,
  11. * ObjectSerializerContext
  12. * } from "../serialization/ObjectMiddleware"
  13. */
  14. /** @import ModuleGraph from "../ModuleGraph" */
  15. /** @import Module from "../Module" */
  16. /** @import { RuntimeSpec } from "../util/runtime" */
  17. const MAX_INLINE_BYTES = 6;
  18. /** @typedef {"null" | "undefined" | "boolean" | "number" | "string"} InlinedValueKind */
  19. class InlinedValue {
  20. /**
  21. * @param {InlinedValueKind} kind value kind
  22. * @param {null | undefined | boolean | number | string} value raw value
  23. */
  24. constructor(kind, value) {
  25. /** @type {InlinedValueKind} */
  26. this.kind = kind;
  27. /** @type {string | number | boolean | null | undefined} */
  28. this.value = value;
  29. }
  30. /**
  31. * @returns {string} rendered literal source
  32. */
  33. renderLiteral() {
  34. switch (this.kind) {
  35. case "null":
  36. return "null";
  37. case "undefined":
  38. return "undefined";
  39. case "boolean":
  40. return this.value ? "true" : "false";
  41. case "number":
  42. return String(this.value);
  43. case "string":
  44. return JSON.stringify(this.value);
  45. }
  46. }
  47. /**
  48. * @param {string} comment leading comment text
  49. * @returns {string} parenthesized literal with comment
  50. */
  51. render(comment) {
  52. return `(${comment}${this.renderLiteral()})`;
  53. }
  54. /**
  55. * @param {ObjectSerializerContext} context context
  56. */
  57. serialize({ write }) {
  58. write(this.kind);
  59. write(this.value);
  60. }
  61. /**
  62. * @param {ObjectDeserializerContext} context context
  63. */
  64. deserialize({ read }) {
  65. this.kind = read();
  66. this.value = read();
  67. }
  68. }
  69. makeSerializable(
  70. InlinedValue,
  71. "webpack/lib/optimize/InlineExports",
  72. "InlinedValue"
  73. );
  74. class InlinedUsedName {
  75. /**
  76. * @param {InlinedValue} value inlined value
  77. * @param {string[]=} suffix nested property access after the literal
  78. */
  79. constructor(value, suffix) {
  80. /** @type {InlinedValue} */
  81. this.value = value;
  82. /** @type {string[]} */
  83. this.suffix = suffix || [];
  84. }
  85. /**
  86. * @param {string} comment leading comment text
  87. * @returns {string} rendered literal with property access suffix
  88. */
  89. render(comment) {
  90. return this.value.render(comment) + propertyAccess(this.suffix);
  91. }
  92. }
  93. /**
  94. * Convert an evaluated expression into an inlined primitive value if it qualifies.
  95. * @param {import("../javascript/BasicEvaluatedExpression")} evaluated evaluated expression
  96. * @returns {InlinedValue | undefined} inlined value or undefined
  97. */
  98. const toInlinedValue = (evaluated) => {
  99. if (!evaluated) return;
  100. if (evaluated.isNull()) return new InlinedValue("null", null);
  101. if (evaluated.isUndefined()) return new InlinedValue("undefined", undefined);
  102. if (evaluated.isBoolean()) {
  103. return new InlinedValue("boolean", /** @type {boolean} */ (evaluated.bool));
  104. }
  105. if (evaluated.isNumber()) {
  106. const num = /** @type {number} */ (evaluated.number);
  107. if (String(num).length > MAX_INLINE_BYTES) return;
  108. return new InlinedValue("number", num);
  109. }
  110. if (evaluated.isString()) {
  111. const str = /** @type {string} */ (evaluated.string);
  112. if (str.length > MAX_INLINE_BYTES) return;
  113. return new InlinedValue("string", str);
  114. }
  115. };
  116. /** @type {WeakSet<ModuleGraph>} */
  117. const inlineEnabledModuleGraphs = new WeakSet();
  118. /**
  119. * Marks export inlining as active for this module graph (set by InlineExportsPlugin).
  120. * @param {ModuleGraph} moduleGraph module graph
  121. * @returns {void}
  122. */
  123. const enableInlineExports = (moduleGraph) => {
  124. inlineEnabledModuleGraphs.add(moduleGraph);
  125. };
  126. /**
  127. * @param {ModuleGraph} moduleGraph module graph
  128. * @returns {boolean} true when InlineExportsPlugin is active for this module graph
  129. */
  130. const isInlineExportsEnabled = (moduleGraph) =>
  131. inlineEnabledModuleGraphs.has(moduleGraph);
  132. /**
  133. * @param {Module} module the target module
  134. * @returns {boolean} true when inlining is enabled for this module
  135. */
  136. const isInlineEnabled = (module) =>
  137. Boolean(
  138. module.buildInfo &&
  139. /** @type {import("../Module").BuildInfo} */
  140. (module.buildInfo).inlineExports
  141. );
  142. /**
  143. * Check whether an import to `module` for `ids` resolves to an inlined value.
  144. * @param {ModuleGraph} moduleGraph module graph
  145. * @param {Module} module the target module
  146. * @param {string[]} ids import ids
  147. * @param {RuntimeSpec} runtime runtime
  148. * @returns {boolean} true if the export is inlined
  149. */
  150. const isExportInlined = (moduleGraph, module, ids, runtime) => {
  151. if (!ids || ids.length === 0) return false;
  152. if (!isInlineEnabled(module)) return false;
  153. return moduleGraph.getExportsInfo(module).hasInlinedUsedName(ids, runtime);
  154. };
  155. module.exports.InlinedUsedName = InlinedUsedName;
  156. module.exports.InlinedValue = InlinedValue;
  157. module.exports.MAX_INLINE_BYTES = MAX_INLINE_BYTES;
  158. module.exports.enableInlineExports = enableInlineExports;
  159. module.exports.isExportInlined = isExportInlined;
  160. module.exports.isInlineEnabled = isInlineEnabled;
  161. module.exports.isInlineExportsEnabled = isInlineExportsEnabled;
  162. module.exports.toInlinedValue = toInlinedValue;