NodeStuffPlugin.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const {
  7. JAVASCRIPT_MODULE_TYPE_AUTO,
  8. JAVASCRIPT_MODULE_TYPE_DYNAMIC
  9. } = require("./ModuleTypeConstants");
  10. const NodeStuffInWebError = require("./NodeStuffInWebError");
  11. const RuntimeGlobals = require("./RuntimeGlobals");
  12. const CachedConstDependency = require("./dependencies/CachedConstDependency");
  13. const ConstDependency = require("./dependencies/ConstDependency");
  14. const {
  15. evaluateToString,
  16. expressionIsUnsupported
  17. } = require("./javascript/JavascriptParserHelpers");
  18. const { relative } = require("./util/fs");
  19. const { parseResource } = require("./util/identifier");
  20. /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
  21. /** @typedef {import("./Compiler")} Compiler */
  22. /** @typedef {import("./Dependency")} Dependency */
  23. /** @typedef {import("./DependencyTemplates")} DependencyTemplates */
  24. /** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
  25. const PLUGIN_NAME = "NodeStuffPlugin";
  26. class NodeStuffPlugin {
  27. constructor(options) {
  28. this.options = options;
  29. }
  30. /**
  31. * Apply the plugin
  32. * @param {Compiler} compiler the compiler instance
  33. * @returns {void}
  34. */
  35. apply(compiler) {
  36. const options = this.options;
  37. compiler.hooks.compilation.tap(
  38. PLUGIN_NAME,
  39. (compilation, { normalModuleFactory }) => {
  40. const handler = (parser, parserOptions) => {
  41. if (parserOptions.node === false) return;
  42. let localOptions = options;
  43. if (parserOptions.node) {
  44. localOptions = { ...localOptions, ...parserOptions.node };
  45. }
  46. if (localOptions.global !== false) {
  47. const withWarning = localOptions.global === "warn";
  48. parser.hooks.expression.for("global").tap(PLUGIN_NAME, expr => {
  49. const dep = new ConstDependency(
  50. RuntimeGlobals.global,
  51. expr.range,
  52. [RuntimeGlobals.global]
  53. );
  54. dep.loc = expr.loc;
  55. parser.state.module.addPresentationalDependency(dep);
  56. // TODO webpack 6 remove
  57. if (withWarning) {
  58. parser.state.module.addWarning(
  59. new NodeStuffInWebError(
  60. dep.loc,
  61. "global",
  62. "The global namespace object is a Node.js feature and isn't available in browsers."
  63. )
  64. );
  65. }
  66. });
  67. parser.hooks.rename.for("global").tap(PLUGIN_NAME, expr => {
  68. const dep = new ConstDependency(
  69. RuntimeGlobals.global,
  70. expr.range,
  71. [RuntimeGlobals.global]
  72. );
  73. dep.loc = expr.loc;
  74. parser.state.module.addPresentationalDependency(dep);
  75. return false;
  76. });
  77. }
  78. const setModuleConstant = (expressionName, fn, warning) => {
  79. parser.hooks.expression
  80. .for(expressionName)
  81. .tap(PLUGIN_NAME, expr => {
  82. const dep = new CachedConstDependency(
  83. JSON.stringify(fn(parser.state.module)),
  84. expr.range,
  85. expressionName
  86. );
  87. dep.loc = expr.loc;
  88. parser.state.module.addPresentationalDependency(dep);
  89. // TODO webpack 6 remove
  90. if (warning) {
  91. parser.state.module.addWarning(
  92. new NodeStuffInWebError(dep.loc, expressionName, warning)
  93. );
  94. }
  95. return true;
  96. });
  97. };
  98. const setConstant = (expressionName, value, warning) =>
  99. setModuleConstant(expressionName, () => value, warning);
  100. const context = compiler.context;
  101. if (localOptions.__filename) {
  102. switch (localOptions.__filename) {
  103. case "mock":
  104. setConstant("__filename", "/index.js");
  105. break;
  106. case "warn-mock":
  107. setConstant(
  108. "__filename",
  109. "/index.js",
  110. "__filename is a Node.js feature and isn't available in browsers."
  111. );
  112. break;
  113. case true:
  114. setModuleConstant("__filename", module =>
  115. relative(compiler.inputFileSystem, context, module.resource)
  116. );
  117. break;
  118. }
  119. parser.hooks.evaluateIdentifier
  120. .for("__filename")
  121. .tap(PLUGIN_NAME, expr => {
  122. if (!parser.state.module) return;
  123. const resource = parseResource(parser.state.module.resource);
  124. return evaluateToString(resource.path)(expr);
  125. });
  126. }
  127. if (localOptions.__dirname) {
  128. switch (localOptions.__dirname) {
  129. case "mock":
  130. setConstant("__dirname", "/");
  131. break;
  132. case "warn-mock":
  133. setConstant(
  134. "__dirname",
  135. "/",
  136. "__dirname is a Node.js feature and isn't available in browsers."
  137. );
  138. break;
  139. case true:
  140. setModuleConstant("__dirname", module =>
  141. relative(compiler.inputFileSystem, context, module.context)
  142. );
  143. break;
  144. }
  145. parser.hooks.evaluateIdentifier
  146. .for("__dirname")
  147. .tap(PLUGIN_NAME, expr => {
  148. if (!parser.state.module) return;
  149. return evaluateToString(parser.state.module.context)(expr);
  150. });
  151. }
  152. parser.hooks.expression
  153. .for("require.extensions")
  154. .tap(
  155. PLUGIN_NAME,
  156. expressionIsUnsupported(
  157. parser,
  158. "require.extensions is not supported by webpack. Use a loader instead."
  159. )
  160. );
  161. };
  162. normalModuleFactory.hooks.parser
  163. .for(JAVASCRIPT_MODULE_TYPE_AUTO)
  164. .tap(PLUGIN_NAME, handler);
  165. normalModuleFactory.hooks.parser
  166. .for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
  167. .tap(PLUGIN_NAME, handler);
  168. }
  169. );
  170. }
  171. }
  172. module.exports = NodeStuffPlugin;