1
0

webpack-protect-bootstrap-plugin.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. const escapeStringRegexp = require('escape-string-regexp');
  2. const webpack = require('webpack');
  3. const G = webpack.RuntimeGlobals;
  4. const OPTIONAL = false;
  5. const OBJ_RULE = [
  6. /([[(,=:]\s*{)(?!__proto__:)\s*(.)/g,
  7. (_, str, next) => `${str}__proto__: null${next === '}' ? '' : ','}${next}`
  8. ];
  9. const BOOTSTRAP_RULES = [
  10. OBJ_RULE,
  11. ["typeof Symbol !== 'undefined' && Symbol.toStringTag",
  12. 'true'],
  13. ['Symbol.toStringTag',
  14. 'toStringTagSym'],
  15. ['Object.defineProperty(',
  16. 'defineProperty('],
  17. ['Object.prototype.hasOwnProperty.call(',
  18. 'safeCall(hasOwnProperty, '],
  19. [`${G.hasOwnProperty}(definition, key) && !${G.hasOwnProperty}(exports, key)`,
  20. '!(key in exports)'], // these objects have null proto
  21. ];
  22. const MAIN_RULES = [
  23. [
  24. /(__webpack_modules__\[moduleId])\.call\(/g,
  25. 'safeCall($1, ',
  26. OPTIONAL,
  27. ], [
  28. new RegExp(`var (__webpack_module_cache__|${G.require}) = {};.*?var ${G.exports} =`, 's'),
  29. patchBootstrap,
  30. ], [
  31. new RegExp(`(${[
  32. `${G.definePropertyGetters}\\(${G.exports}, {`,
  33. `var ${G.exports} = {`,
  34. `var __webpack_modules__ = \\({`,
  35. ].join('|')})(?!__proto__:)\\s*(.)`, 'g'),
  36. OBJ_RULE[1],
  37. ],
  38. ];
  39. /**
  40. * WARNING! The following globals must be correctly assigned using wrapper-webpack-plugin.
  41. * toStringTagSym = Symbol.toStringTag
  42. * defineProperty = Object.defineProperty
  43. * hasOwnProperty = Object.prototype.hasOwnProperty
  44. * safeCall = Function.prototype.call.bind(Function.prototype.call)
  45. */
  46. class WebpackProtectBootstrapPlugin {
  47. apply(compiler) {
  48. const NAME = WebpackProtectBootstrapPlugin.name;
  49. compiler.hooks.compilation.tap(NAME, (compilation) => {
  50. const hooks = webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
  51. hooks.renderMain.tap(NAME, replace.bind(null, MAIN_RULES));
  52. });
  53. }
  54. }
  55. function patchBootstrap(src) {
  56. const props = src.match(new RegExp(`(?<=\\b${G.require}\\.)(\\w+)`, 'g'));
  57. const uniq = [...new Set(props)].join('');
  58. const guard = uniq
  59. ? `for (let i = 0, props=${JSON.stringify(uniq)}; i < props.length; i++)
  60. defineProperty(${G.require}, props[i], {__proto__: null, value: 0, writable: 1});\n`
  61. : '';
  62. const rules = uniq ? BOOTSTRAP_RULES : [OBJ_RULE];
  63. return guard + replace(rules, src, this);
  64. }
  65. function replace(rules, src, info) {
  66. src = src.source?.() || src;
  67. let res = src;
  68. for (const rule of rules) {
  69. const [from, to, mandatory = true] = rule;
  70. const fromRe = typeof from === 'string'
  71. ? new RegExp(escapeStringRegexp(from), 'g')
  72. : from;
  73. const dst = res.replace(fromRe, to.bind?.(info) || to);
  74. if (dst === res && mandatory) {
  75. const err = `[${WebpackProtectBootstrapPlugin.name}] `
  76. + `"${from}" not found in ${info.chunk.name || 'bootstrap'}`;
  77. console.log(`${err}:\n${src}`); // this prints immediately
  78. throw new Error(err); // this prints at the end of build
  79. }
  80. res = dst;
  81. }
  82. return res;
  83. }
  84. module.exports = WebpackProtectBootstrapPlugin;