.eslintrc.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. const acorn = require('acorn');
  2. const FILES_INJECTED = [`src/injected/**/*.js`];
  3. const FILES_CONTENT = [`src/injected/content/**/*.js`];
  4. const FILES_WEB = [`src/injected/web/**/*.js`];
  5. // some functions are used by `injected`
  6. const FILES_SHARED = [
  7. 'src/common/browser.js',
  8. 'src/common/consts.js',
  9. 'src/common/index.js',
  10. 'src/common/object.js',
  11. 'src/common/util.js',
  12. ];
  13. const GLOBALS_COMMON = getGlobals('src/common/safe-globals.js');
  14. const GLOBALS_INJECTED = getGlobals(`src/injected/safe-globals-injected.js`);
  15. const GLOBALS_CONTENT = {
  16. ...getGlobals(`src/injected/content/safe-globals-content.js`),
  17. ...GLOBALS_INJECTED,
  18. };
  19. const GLOBALS_WEB = {
  20. ...getGlobals(`src/injected/web/safe-globals-web.js`),
  21. ...GLOBALS_INJECTED,
  22. IS_FIREFOX: false, // passed as a parameter to VMInitInjection in webpack.conf.js
  23. };
  24. module.exports = {
  25. root: true,
  26. extends: [
  27. require.resolve('@gera2ld/plaid/eslint'),
  28. require.resolve('@gera2ld/plaid-vue/eslint/vue'),
  29. ],
  30. parserOptions: {
  31. ecmaFeatures: {
  32. legacyDecorators: true,
  33. },
  34. },
  35. overrides: [{
  36. // `browser` is a local variable since we remove the global `chrome` and `browser` in injected*
  37. // to prevent exposing them to userscripts with `@inject-into content`
  38. files: ['*'],
  39. excludedFiles: [...FILES_INJECTED, ...FILES_SHARED],
  40. globals: {
  41. browser: false,
  42. ...GLOBALS_COMMON,
  43. },
  44. }, {
  45. files: FILES_SHARED,
  46. globals: GLOBALS_COMMON,
  47. }, {
  48. files: FILES_WEB,
  49. globals: GLOBALS_WEB,
  50. }, {
  51. files: FILES_CONTENT,
  52. globals: GLOBALS_CONTENT,
  53. }, {
  54. files: FILES_INJECTED,
  55. excludedFiles: [...FILES_CONTENT, ...FILES_WEB],
  56. // intersection of globals in CONTENT and WEB
  57. globals: Object.keys(GLOBALS_CONTENT).reduce((res, key) => (
  58. Object.assign(res, key in GLOBALS_WEB && { [key]: false })
  59. ), {}),
  60. }, {
  61. files: [...FILES_INJECTED, ...FILES_SHARED],
  62. rules: {
  63. /* Our .browserslistrc targets old browsers so the compiled code for {...objSpread} uses
  64. babel's polyfill that calls methods like `Object.assign` instead of our safe `assign`.
  65. Ideally, `eslint-plugin-compat` should be used but I couldn't make it work. */
  66. 'no-restricted-syntax': ['error', {
  67. selector: 'ObjectExpression > ExperimentalSpreadProperty',
  68. message: 'Object spread adds a polyfill in injected* even if unused by it',
  69. }, {
  70. selector: 'OptionalCallExpression',
  71. message: 'Optional call uses .call(), which may be spoofed/broken in an unsafe environment',
  72. // TODO: write a Babel plugin to use safeCall for this.
  73. }, {
  74. selector: 'ArrayPattern',
  75. message: 'Destructuring via Symbol.iterator may be spoofed/broken in an unsafe environment',
  76. }, {
  77. selector: ':matches(ArrayExpression, CallExpression) > SpreadElement',
  78. message: 'Spreading via Symbol.iterator may be spoofed/broken in an unsafe environment',
  79. }],
  80. },
  81. }, {
  82. // build scripts
  83. files: [
  84. '*.js',
  85. 'scripts/*.js',
  86. ],
  87. env: { node: true },
  88. rules: {
  89. 'global-require': 0,
  90. 'import/newline-after-import': 0,
  91. 'import/no-extraneous-dependencies': 0, // spits errors in github action
  92. }
  93. }],
  94. rules: {
  95. 'import/extensions': ['error', 'ignorePackages', {
  96. js: 'never',
  97. vue: 'never',
  98. }],
  99. // copied from airbnb-base, replaced 4 with 8
  100. 'object-curly-newline': ['error', {
  101. ObjectExpression: { minProperties: 8, multiline: true, consistent: true },
  102. ObjectPattern: { minProperties: 8, multiline: true, consistent: true },
  103. ImportDeclaration: { minProperties: 8, multiline: true, consistent: true },
  104. ExportDeclaration: { minProperties: 8, multiline: true, consistent: true },
  105. }],
  106. },
  107. };
  108. function getGlobals(fileName) {
  109. const text = require('fs').readFileSync(fileName, { encoding: 'utf8' });
  110. const res = {};
  111. const tree = acorn.parse(text, { ecmaVersion: 2018, sourceType: 'module' });
  112. tree.body.forEach(body => {
  113. const { declarations } = body.declaration || body;
  114. if (!declarations) return;
  115. declarations.forEach(function processId({ id: { left, properties, name = left && left.name } }) {
  116. if (name) {
  117. // const NAME = whatever
  118. res[name] = false;
  119. } else if (properties) {
  120. // const { NAME1, prototype: { NAME2: ALIAS2 } } = whatever
  121. properties.forEach(({ value }) => processId({ id: value }));
  122. }
  123. });
  124. });
  125. return res;
  126. }