.eslintrc.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. const { readGlobalsFile, restrictedSyntax } = require('./scripts/webpack-util');
  2. const ovr = makeOverrides();
  3. module.exports = {
  4. root: true,
  5. extends: [
  6. 'eslint:recommended',
  7. 'plugin:vue/vue3-essential',
  8. 'prettier',
  9. ],
  10. env: {
  11. browser: true,
  12. node: true,
  13. es2021: true,
  14. },
  15. parserOptions: {
  16. parser: '@babel/eslint-parser',
  17. ecmaVersion: 'latest',
  18. sourceType: 'module',
  19. },
  20. plugins: ['jest'],
  21. rules: {
  22. 'prettier/prettier': 'off',
  23. 'no-shadow': 2,
  24. 'no-unused-expressions': 2,
  25. 'no-use-before-define': ['error', {
  26. 'functions': false,
  27. 'classes': true,
  28. 'variables': false, // allowing for upper scopes
  29. 'allowNamedExports': true,
  30. }],
  31. // copied from airbnb-base, replaced 4 with 8
  32. 'object-curly-newline': ['error', {
  33. ObjectExpression: { minProperties: 8, multiline: true, consistent: true },
  34. ObjectPattern: { minProperties: 8, multiline: true, consistent: true },
  35. ImportDeclaration: { minProperties: 8, multiline: true, consistent: true },
  36. ExportDeclaration: { minProperties: 8, multiline: true, consistent: true },
  37. }],
  38. 'semi': ['error'],
  39. },
  40. overrides: [{
  41. // `browser` is a local variable since we remove the global `chrome` and `browser` in injected*
  42. // to prevent exposing them to userscripts with `@inject-into content`
  43. files: ['*'],
  44. excludedFiles: [...ovr.FILES_INJECTED, ...ovr.FILES_SHARED],
  45. globals: {
  46. browser: false,
  47. ...ovr.GLOBALS_COMMON,
  48. },
  49. }, {
  50. files: ovr.FILES_SHARED,
  51. globals: ovr.GLOBALS_COMMON,
  52. }, {
  53. files: ovr.FILES_WEB,
  54. globals: ovr.GLOBALS_WEB,
  55. }, {
  56. files: ovr.FILES_CONTENT,
  57. globals: ovr.GLOBALS_CONTENT,
  58. }, {
  59. files: ovr.FILES_INJECTED,
  60. excludedFiles: [...ovr.FILES_CONTENT, ...ovr.FILES_WEB],
  61. // intersection of globals in CONTENT and WEB
  62. globals: Object.keys(ovr.GLOBALS_CONTENT).reduce((res, key) => (
  63. Object.assign(res, key in ovr.GLOBALS_WEB && { [key]: false })
  64. ), {}),
  65. }, {
  66. files: [...ovr.FILES_INJECTED, ...ovr.FILES_SHARED],
  67. rules: ovr.INJECTED_RULES,
  68. }, {
  69. files: ovr.FILES_WEB,
  70. rules: {
  71. ...ovr.INJECTED_RULES,
  72. 'no-restricted-syntax': [
  73. ...ovr.INJECTED_RULES['no-restricted-syntax'],
  74. {
  75. selector: '[regex], NewExpression[callee.name="RegExp"]',
  76. message: 'RegExp internally depends on a *ton* of stuff that may be spoofed or broken',
  77. // https://262.ecma-international.org/12.0/#sec-regexpexec
  78. },
  79. ],
  80. },
  81. }, {
  82. // build scripts
  83. files: [
  84. '*.js',
  85. 'scripts/*.js',
  86. 'scripts/*.mjs',
  87. ],
  88. env: { node: true },
  89. rules: {
  90. 'global-require': 0,
  91. 'import/newline-after-import': 0,
  92. 'import/no-extraneous-dependencies': 0, // spits errors in github action
  93. 'import/extensions': 0,
  94. }
  95. }, {
  96. files: ['*.vue'],
  97. rules: {
  98. 'vue/multi-word-component-names': 0,
  99. },
  100. }, {
  101. files: ['test/**'],
  102. env: {
  103. 'jest/globals': true,
  104. },
  105. }],
  106. };
  107. function makeOverrides() {
  108. /* Note that `injected` uses several more `common` files indirectly, but we check just these
  109. * two automatically because they are trivial by design and must always pass the check */
  110. const GLOBALS_SHARED = getGlobals('*');
  111. const GLOBALS_INJECTED = {
  112. ...getGlobals('injected'),
  113. PAGE_MODE_HANDSHAKE: false,
  114. VAULT_ID: false,
  115. };
  116. function getGlobals(path) {
  117. const res = {};
  118. const { ast } = readGlobalsFile(path, { ast: true });
  119. ast.program.body.forEach(body => {
  120. const { declarations } = body.declaration || body;
  121. if (!declarations) return;
  122. declarations.forEach(function processId({
  123. id: {
  124. left,
  125. properties,
  126. name = left && left.name,
  127. },
  128. }) {
  129. if (name) {
  130. // const NAME = whatever
  131. // We consider `let` immutable too to avoid unintentional reassignment
  132. res[name] = false;
  133. } else if (properties) {
  134. // const { NAME1, prototype: { NAME2: ALIAS2 } } = whatever
  135. properties.forEach(({ value }) => processId({ id: value }));
  136. }
  137. });
  138. });
  139. return res;
  140. }
  141. return {
  142. FILES_CONTENT: [
  143. 'src/injected/index.js',
  144. 'src/injected/content/**/*.js',
  145. ],
  146. FILES_INJECTED: [
  147. 'src/injected/**/*.js',
  148. ],
  149. FILES_SHARED: [
  150. 'src/common/browser.js',
  151. 'src/common/consts.js',
  152. 'src/common/safe-globals-shared.js',
  153. ],
  154. FILES_WEB: [
  155. 'src/injected/web/**/*.js',
  156. ],
  157. GLOBALS_INJECTED,
  158. GLOBALS_SHARED,
  159. GLOBALS_COMMON: {
  160. ...GLOBALS_SHARED,
  161. ...getGlobals('common'),
  162. re: false, // transform-modern-regexp with useRe option
  163. },
  164. GLOBALS_CONTENT: {
  165. INIT_FUNC_NAME: false,
  166. ...GLOBALS_SHARED,
  167. ...getGlobals('injected/content'),
  168. ...GLOBALS_INJECTED,
  169. },
  170. GLOBALS_WEB: {
  171. ...GLOBALS_SHARED,
  172. ...getGlobals('injected/web'),
  173. ...GLOBALS_INJECTED,
  174. IS_FIREFOX: false, // passed as a parameter to VMInitInjection in webpack.conf.js
  175. },
  176. INJECTED_RULES: {
  177. 'no-restricted-imports': [
  178. 'error', {
  179. patterns: ['*/common', '*/common/*'],
  180. }
  181. ],
  182. 'no-restricted-syntax': ['error', ...restrictedSyntax],
  183. },
  184. };
  185. }