webpack.conf.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. const { modifyWebpackConfig, shallowMerge, defaultOptions } = require('@gera2ld/plaid');
  2. const { isProd } = require('@gera2ld/plaid/util');
  3. const fs = require('fs');
  4. const webpack = require('webpack');
  5. const WrapperWebpackPlugin = require('wrapper-webpack-plugin');
  6. const HTMLInlineCSSWebpackPlugin = require('html-inline-css-webpack-plugin').default;
  7. const projectConfig = require('./plaid.conf');
  8. const mergedConfig = shallowMerge(defaultOptions, projectConfig);
  9. const INIT_FUNC_NAME = 'VMInitInjection';
  10. // Copied from gulpfile.js: strip alphabetic suffix
  11. const VM_VER = require('../package.json').version.replace(/-[^.]*/, '');
  12. const pickEnvs = (items) => {
  13. return Object.assign({}, ...items.map(x => ({
  14. [`process.env.${x.key}`]: JSON.stringify(
  15. 'val' in x ? x.val
  16. : process.env[x.key] ?? x.def
  17. ),
  18. })));
  19. };
  20. const definitions = new webpack.DefinePlugin({
  21. ...pickEnvs([
  22. { key: 'DEBUG', def: false },
  23. { key: 'VM_VER', val: VM_VER },
  24. { key: 'SYNC_GOOGLE_CLIENT_ID' },
  25. { key: 'SYNC_GOOGLE_CLIENT_SECRET' },
  26. { key: 'SYNC_ONEDRIVE_CLIENT_ID' },
  27. { key: 'SYNC_ONEDRIVE_CLIENT_SECRET' },
  28. ]),
  29. 'process.env.INIT_FUNC_NAME': JSON.stringify(INIT_FUNC_NAME),
  30. });
  31. const modify = (page, entry, init) => modifyWebpackConfig(
  32. (config) => {
  33. config.plugins.push(definitions);
  34. if (!entry) init = page;
  35. if (init) init(config);
  36. return config;
  37. }, {
  38. projectConfig: {
  39. ...mergedConfig,
  40. ...entry && { pages: { [page]: { entry }} },
  41. },
  42. },
  43. );
  44. // avoid running webpack bootstrap in a potentially hacked environment
  45. // after documentElement was replaced which triggered reinjection of content scripts
  46. const skipReinjectionHeader = `if (window['${INIT_FUNC_NAME}'] !== 1)`;
  47. const [globalsCommonHeader, globalsInjectedHeader] = [
  48. './src/common/safe-globals.js',
  49. './src/injected/safe-injected-globals.js',
  50. ].map(path =>
  51. require('fs').readFileSync(path, {encoding: 'utf8'}).replace(/export const/g, 'const'));
  52. const globalWrapper = new WrapperWebpackPlugin({
  53. header: `{ ${globalsCommonHeader}`,
  54. footer: `}`,
  55. test: /^(?!injected|public).*\.js$/,
  56. });
  57. module.exports = Promise.all([
  58. modify((config) => {
  59. config.output.publicPath = '/';
  60. config.plugins.push(globalWrapper);
  61. /* Embedding as <style> to ensure uiTheme option doesn't cause FOUC.
  62. * Note that in production build there's no <head> in html but document.head is still
  63. * auto-created per the specification so our styles will be placed correctly anyway. */
  64. config.plugins.push(new HTMLInlineCSSWebpackPlugin({
  65. replace: {
  66. target: '<body>',
  67. position: 'before',
  68. },
  69. }));
  70. }),
  71. modify('background', './src/background', (config) => {
  72. config.plugins.push(globalWrapper);
  73. config.plugins.push(new class ListBackgroundScripts {
  74. apply(compiler) {
  75. compiler.hooks.afterEmit.tap(this.constructor.name, compilation => {
  76. const path = `${compilation.outputOptions.path}/manifest.json`;
  77. const manifest = JSON.parse(fs.readFileSync(path, {encoding: 'utf8'}));
  78. const scripts = [...compilation.entrypoints.values()][0].chunks.map(c => c.files[0]);
  79. if (`${manifest.background.scripts}` !== `${scripts}`) {
  80. manifest.background.scripts = scripts;
  81. fs.writeFileSync(path,
  82. JSON.stringify(manifest, null, isProd ? 0 : 2),
  83. {encoding: 'utf8'});
  84. }
  85. });
  86. }
  87. });
  88. }),
  89. modify('injected', './src/injected', (config) => {
  90. config.plugins.push(
  91. new WrapperWebpackPlugin({
  92. header: `${skipReinjectionHeader} { ${globalsCommonHeader};${globalsInjectedHeader}`,
  93. footer: `}`,
  94. }));
  95. }),
  96. modify('injected-web', './src/injected/web', (config) => {
  97. config.output.libraryTarget = 'commonjs2';
  98. config.plugins.push(
  99. new WrapperWebpackPlugin({
  100. header: `${skipReinjectionHeader}
  101. window['${INIT_FUNC_NAME}'] = function () {
  102. var module = { exports: {} };
  103. ${globalsCommonHeader}
  104. ${globalsInjectedHeader}
  105. `,
  106. footer: `
  107. var exports = module.exports;
  108. return exports.__esModule ? exports['default'] : exports;
  109. };0;`,
  110. }),
  111. );
  112. }),
  113. ]);