NodeStuffPlugin.js 5.3 KB

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