@mui/internal-code-infra 0.0.3-canary.6 → 0.0.3-canary.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +55 -0
  2. package/build/babel-config.d.mts +40 -0
  3. package/build/brokenLinksChecker/index.d.mts +138 -0
  4. package/build/cli/cmdArgosPush.d.mts +13 -0
  5. package/build/cli/cmdBuild.d.mts +56 -0
  6. package/build/cli/cmdCopyFiles.d.mts +20 -0
  7. package/build/cli/cmdExtractErrorCodes.d.mts +3 -0
  8. package/build/cli/cmdGithubAuth.d.mts +6 -0
  9. package/build/cli/cmdListWorkspaces.d.mts +18 -0
  10. package/build/cli/cmdPublish.d.mts +27 -0
  11. package/build/cli/cmdPublishCanary.d.mts +30 -0
  12. package/build/cli/cmdPublishNewPackage.d.mts +8 -0
  13. package/build/cli/cmdSetVersionOverrides.d.mts +9 -0
  14. package/build/cli/cmdValidateBuiltTypes.d.mts +2 -0
  15. package/build/cli/index.d.mts +1 -0
  16. package/build/eslint/baseConfig.d.mts +10 -0
  17. package/build/eslint/docsConfig.d.mts +4 -0
  18. package/build/eslint/extensions.d.mts +8 -0
  19. package/build/eslint/index.d.mts +4 -0
  20. package/build/eslint/jsonConfig.d.mts +4 -0
  21. package/build/eslint/material-ui/config.d.mts +8 -0
  22. package/build/eslint/material-ui/index.d.mts +2 -0
  23. package/build/eslint/material-ui/rules/disallow-active-element-as-key-event-target.d.mts +5 -0
  24. package/build/eslint/material-ui/rules/disallow-react-api-in-server-components.d.mts +2 -0
  25. package/build/eslint/material-ui/rules/docgen-ignore-before-comment.d.mts +2 -0
  26. package/build/eslint/material-ui/rules/mui-name-matches-component-name.d.mts +5 -0
  27. package/build/eslint/material-ui/rules/no-empty-box.d.mts +5 -0
  28. package/build/eslint/material-ui/rules/no-restricted-resolved-imports.d.mts +12 -0
  29. package/build/eslint/material-ui/rules/no-styled-box.d.mts +5 -0
  30. package/build/eslint/material-ui/rules/rules-of-use-theme-variants.d.mts +9 -0
  31. package/build/eslint/material-ui/rules/straight-quotes.d.mts +5 -0
  32. package/build/eslint/testConfig.d.mts +14 -0
  33. package/build/markdownlint/duplicate-h1.d.mts +27 -0
  34. package/build/markdownlint/git-diff.d.mts +8 -0
  35. package/build/markdownlint/index.d.mts +56 -0
  36. package/build/markdownlint/straight-quotes.d.mts +8 -0
  37. package/build/markdownlint/table-alignment.d.mts +8 -0
  38. package/build/markdownlint/terminal-language.d.mts +8 -0
  39. package/build/prettier.d.mts +20 -0
  40. package/build/stylelint/index.d.mts +32 -0
  41. package/build/utils/babel.d.mts +71 -0
  42. package/build/utils/build.d.mts +50 -0
  43. package/build/utils/changelog.d.mts +64 -0
  44. package/build/utils/credentials.d.mts +17 -0
  45. package/build/utils/extractErrorCodes.d.mts +19 -0
  46. package/build/utils/git.d.mts +26 -0
  47. package/build/utils/github.d.mts +41 -0
  48. package/build/utils/pnpm.d.mts +238 -0
  49. package/build/utils/typescript.d.mts +35 -0
  50. package/package.json +92 -42
  51. package/src/babel-config.mjs +52 -8
  52. package/src/brokenLinksChecker/__fixtures__/static-site/broken-links.html +20 -0
  53. package/src/brokenLinksChecker/__fixtures__/static-site/broken-targets.html +22 -0
  54. package/src/brokenLinksChecker/__fixtures__/static-site/example.md +9 -0
  55. package/src/brokenLinksChecker/__fixtures__/static-site/external-links.html +21 -0
  56. package/src/brokenLinksChecker/__fixtures__/static-site/ignored-page.html +17 -0
  57. package/src/brokenLinksChecker/__fixtures__/static-site/index.html +26 -0
  58. package/src/brokenLinksChecker/__fixtures__/static-site/known-targets.json +5 -0
  59. package/src/brokenLinksChecker/__fixtures__/static-site/nested/page.html +19 -0
  60. package/src/brokenLinksChecker/__fixtures__/static-site/orphaned-page.html +20 -0
  61. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-api-links.html +20 -0
  62. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-custom-targets.html +24 -0
  63. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-ignored-content.html +28 -0
  64. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-known-target-links.html +19 -0
  65. package/src/brokenLinksChecker/__fixtures__/static-site/valid.html +20 -0
  66. package/src/brokenLinksChecker/__fixtures__/static-site/with-anchors.html +31 -0
  67. package/src/brokenLinksChecker/index.mjs +641 -0
  68. package/src/brokenLinksChecker/index.test.ts +178 -0
  69. package/src/cli/cmdArgosPush.mjs +13 -2
  70. package/src/cli/cmdBuild.mjs +228 -31
  71. package/src/cli/cmdGithubAuth.mjs +36 -0
  72. package/src/cli/cmdListWorkspaces.mjs +2 -2
  73. package/src/cli/cmdPublish.mjs +203 -49
  74. package/src/cli/cmdPublishCanary.mjs +404 -46
  75. package/src/cli/cmdPublishNewPackage.mjs +86 -0
  76. package/src/cli/cmdSetVersionOverrides.mjs +17 -1
  77. package/src/cli/cmdValidateBuiltTypes.mjs +49 -0
  78. package/src/cli/index.mjs +6 -2
  79. package/src/cli/packageJson.d.ts +729 -0
  80. package/src/eslint/baseConfig.mjs +96 -78
  81. package/src/eslint/docsConfig.mjs +26 -13
  82. package/src/eslint/extensions.mjs +8 -8
  83. package/src/eslint/jsonConfig.mjs +40 -0
  84. package/src/eslint/material-ui/config.mjs +8 -9
  85. package/src/eslint/material-ui/rules/mui-name-matches-component-name.mjs +4 -2
  86. package/src/eslint/material-ui/rules/rules-of-use-theme-variants.mjs +2 -1
  87. package/src/eslint/testConfig.mjs +72 -66
  88. package/src/stylelint/index.mjs +46 -0
  89. package/src/untyped-plugins.d.ts +13 -0
  90. package/src/{cli → utils}/babel.mjs +10 -3
  91. package/src/utils/build.mjs +27 -1
  92. package/src/utils/changelog.mjs +157 -0
  93. package/src/utils/credentials.mjs +71 -0
  94. package/src/utils/extractErrorCodes.mjs +2 -2
  95. package/src/utils/git.mjs +67 -0
  96. package/src/utils/github.mjs +263 -0
  97. package/src/{cli → utils}/pnpm.mjs +23 -13
  98. package/src/{cli → utils}/typescript.mjs +13 -7
  99. package/src/cli/cmdJsonLint.mjs +0 -69
@@ -1,102 +1,120 @@
1
1
  import { includeIgnoreFile } from '@eslint/compat';
2
2
  import eslintJs from '@eslint/js';
3
+ import { defineConfig } from 'eslint/config';
3
4
  import prettier from 'eslint-config-prettier/flat';
5
+ import compatPlugin from 'eslint-plugin-compat';
4
6
  import importPlugin from 'eslint-plugin-import';
5
7
  import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
6
8
  import reactPlugin from 'eslint-plugin-react';
7
9
  import { configs as reactCompilerPluginConfigs } from 'eslint-plugin-react-compiler';
8
- import { configs as reactHookConfigs } from 'eslint-plugin-react-hooks';
10
+ import reactHooks from 'eslint-plugin-react-hooks';
9
11
  import globals from 'globals';
10
- import * as fs from 'node:fs';
11
12
  import * as path from 'node:path';
12
13
  import * as tseslint from 'typescript-eslint';
13
-
14
+ import fs from 'node:fs';
14
15
  import { createCoreConfig } from './material-ui/config.mjs';
15
16
  import muiPlugin from './material-ui/index.mjs';
17
+ import { EXTENSION_TS } from './extensions.mjs';
18
+ import { createJsonConfig } from './jsonConfig.mjs';
19
+
20
+ /**
21
+ * @param {string} filePath
22
+ * @param {string | undefined} description
23
+ */
24
+ function includeIgnoreIfExists(filePath, description) {
25
+ if (fs.existsSync(filePath)) {
26
+ return includeIgnoreFile(filePath, description);
27
+ }
28
+ return [];
29
+ }
30
+
16
31
  /**
17
32
  * @param {Object} [params]
18
33
  * @param {boolean} [params.enableReactCompiler] - Whether the config is for spec files.
19
- * @param {string} params.baseDirectory - The base directory for the configuration.
34
+ * @param {string} [params.baseDirectory] - The base directory for the configuration.
20
35
  * @returns {import('eslint').Linter.Config[]}
21
36
  */
22
- export function createBaseConfig(
23
- { enableReactCompiler = false, baseDirectory } = { baseDirectory: process.cwd() },
24
- ) {
25
- const ignoreRules = /** @type {import('@eslint/compat').FlatConfig[]} */ (
26
- // All repos should use .lintignore going forward.
27
- // .eslintignore is for backward compatibility. Should be removed in future.
28
- ['.gitignore', '.lintignore', '.eslintignore']
29
- .map((file) => {
30
- if (fs.existsSync(`${baseDirectory}/${file}`)) {
31
- return includeIgnoreFile(path.join(baseDirectory, file), `Ignore rules from ${file}`);
32
- }
33
- return null;
34
- })
35
- .filter(Boolean)
36
- );
37
-
38
- return /** @type {import('eslint').Linter.Config[]} */ (
39
- tseslint.config(
40
- ...ignoreRules,
41
- eslintJs.configs.recommended,
42
- importPlugin.flatConfigs.recommended,
43
- importPlugin.flatConfigs.react,
44
- jsxA11yPlugin.flatConfigs.recommended,
45
- reactPlugin.configs.flat.recommended,
46
- reactHookConfigs.recommended,
47
- tseslint.configs.recommended,
48
- importPlugin.flatConfigs.typescript,
49
- enableReactCompiler ? reactCompilerPluginConfigs.recommended : {},
50
- prettier,
51
- {
52
- name: 'typescript-eslint-parser',
53
- languageOptions: {
54
- ecmaVersion: 7,
55
- globals: {
56
- ...globals.es2020,
57
- ...globals.browser,
58
- ...globals.node,
37
+ export function createBaseConfig({
38
+ enableReactCompiler = false,
39
+ baseDirectory = process.cwd(),
40
+ } = {}) {
41
+ return defineConfig([
42
+ includeIgnoreIfExists(path.join(baseDirectory, '.gitignore'), `Ignore rules from .gitignore`),
43
+ includeIgnoreIfExists(path.join(baseDirectory, '.lintignore'), `Ignore rules from .lintignore`),
44
+ createJsonConfig(),
45
+ prettier,
46
+ {
47
+ name: 'Base config',
48
+ files: [`**/*${EXTENSION_TS}`],
49
+ extends: defineConfig([
50
+ eslintJs.configs.recommended,
51
+ importPlugin.flatConfigs.recommended,
52
+ importPlugin.flatConfigs.react,
53
+ jsxA11yPlugin.flatConfigs.recommended,
54
+ reactPlugin.configs.flat.recommended,
55
+ reactHooks.configs.flat.recommended,
56
+ tseslint.configs.recommended,
57
+ importPlugin.flatConfigs.typescript,
58
+ enableReactCompiler ? reactCompilerPluginConfigs.recommended : {},
59
+ compatPlugin.configs['flat/recommended'],
60
+ {
61
+ name: 'typescript-eslint-parser',
62
+ languageOptions: {
63
+ ecmaVersion: 7,
64
+ globals: {
65
+ ...globals.es2020,
66
+ ...globals.browser,
67
+ ...globals.node,
68
+ },
59
69
  },
60
- },
61
- plugins: {
62
- 'material-ui': muiPlugin,
63
- },
64
- extends: createCoreConfig({ reactCompilerEnabled: enableReactCompiler }),
65
- },
66
- {
67
- files: ['**/*.mjs'],
68
- rules: {
69
- 'import/extensions': [
70
- 'error',
71
- 'ignorePackages',
72
- {
73
- js: 'always',
74
- mjs: 'always',
70
+ plugins: {
71
+ 'material-ui': muiPlugin,
72
+ },
73
+ settings: {
74
+ browserslistOpts: {
75
+ config: path.join(baseDirectory, '.browserslistrc'),
76
+ env: 'stable',
77
+ ignoreUnknownVersions: true,
75
78
  },
76
- ],
79
+ },
80
+ extends: createCoreConfig({ enableReactCompiler }),
77
81
  },
78
- },
79
- // Lint rule to disallow usage of typescript namespaces.We've seen at least two problems with them:
80
- // * Creates non-portable types in base ui. [1]
81
- // * This pattern [2] leads to broken bundling in codesandbox [3].
82
- // Gauging the ecosystem it also looks like support for namespaces in tooling is poor and tends to
83
- // be treated as a deprecated feature.
84
- // [1] https://github.com/mui/base-ui/pull/2324
85
- // [2] https://github.com/mui/mui-x/blob/1cf853ed45cf301211ece1c0ca21981ea208edfb/packages/x-virtualizer/src/models/core.ts#L4-L10
86
- // [3] https://codesandbox.io/embed/kgylpd?module=/src/Demo.tsx&fontsize=12
87
- {
88
- rules: {
89
- '@typescript-eslint/no-namespace': 'error',
82
+ // Lint rule to disallow usage of typescript namespaces.We've seen at least two problems with them:
83
+ // * Creates non-portable types in base ui. [1]
84
+ // * This pattern [2] leads to broken bundling in codesandbox [3].
85
+ // Gauging the ecosystem it also looks like support for namespaces in tooling is poor and tends to
86
+ // be treated as a deprecated feature.
87
+ // [1] https://github.com/mui/base-ui/pull/2324
88
+ // [2] https://github.com/mui/mui-x/blob/1cf853ed45cf301211ece1c0ca21981ea208edfb/packages/x-virtualizer/src/models/core.ts#L4-L10
89
+ // [3] https://codesandbox.io/embed/kgylpd?module=/src/Demo.tsx&fontsize=12
90
+ {
91
+ rules: {
92
+ '@typescript-eslint/no-namespace': 'error',
93
+ },
90
94
  },
91
- },
92
- // Part of the migration away from airbnb config. Turned of initially.
93
- {
94
- rules: {
95
- '@typescript-eslint/no-explicit-any': 'off',
96
- '@typescript-eslint/no-unsafe-function-type': 'off',
97
- '@typescript-eslint/no-empty-object-type': 'off',
95
+ // Part of the migration away from airbnb config. Turned off initially.
96
+ {
97
+ rules: {
98
+ '@typescript-eslint/no-explicit-any': 'off',
99
+ '@typescript-eslint/no-unsafe-function-type': 'off',
100
+ '@typescript-eslint/no-empty-object-type': 'off',
101
+ },
98
102
  },
103
+ ]),
104
+ },
105
+ {
106
+ name: 'ESM JS files',
107
+ files: ['**/*.mjs'],
108
+ rules: {
109
+ 'import/extensions': [
110
+ 'error',
111
+ 'ignorePackages',
112
+ {
113
+ js: 'always',
114
+ mjs: 'always',
115
+ },
116
+ ],
99
117
  },
100
- )
101
- );
118
+ },
119
+ ]);
102
120
  }
@@ -1,21 +1,34 @@
1
1
  import nextjs from '@next/eslint-plugin-next';
2
- import * as tseslint from 'typescript-eslint';
2
+ import { defineConfig } from 'eslint/config';
3
3
 
4
4
  /**
5
5
  * @returns {import('eslint').Linter.Config[]}
6
6
  */
7
7
  export function createDocsConfig() {
8
- return /** @type {import('eslint').Linter.Config[]} */ (
9
- tseslint.config(nextjs.flatConfig.recommended, {
10
- settings: {
11
- next: {
12
- rootDir: 'docs',
13
- },
14
- },
15
- rules: {
16
- 'jsx-a11y/anchor-is-valid': 'off',
17
- 'no-irregular-whitespace': ['error', { skipJSXText: true, skipStrings: true }],
8
+ /**
9
+ * @type {any}
10
+ */
11
+ const nextjsAlias = nextjs;
12
+
13
+ // `nextjs.flatConfig.recommended` for Next.js v15 supports.
14
+ // `nextjs.configs.recommended` for Next.js v16 support
15
+ // See https://github.com/vercel/next.js/pull/83763 for the breaking change details
16
+ // TODO Migrate to Next.js 16+ so we can remove `nextjs.flatConfig`.
17
+ const recommendedConfig = nextjs.flatConfig
18
+ ? nextjsAlias.flatConfig.recommended
19
+ : nextjsAlias.configs.recommended;
20
+
21
+ return defineConfig(recommendedConfig, {
22
+ settings: {
23
+ next: {
24
+ rootDir: 'docs',
18
25
  },
19
- })
20
- );
26
+ },
27
+ files: ['**/*.js', '**/*.mjs', '**/*.jsx', '**/*.ts', '**/*.tsx'],
28
+ rules: {
29
+ 'compat/compat': 'off',
30
+ 'jsx-a11y/anchor-is-valid': 'off',
31
+ 'no-irregular-whitespace': ['error', { skipJSXText: true, skipStrings: true }],
32
+ },
33
+ });
21
34
  }
@@ -1,8 +1,8 @@
1
- export const EXTENSION_JS = '?(c|m)js?(x)';
2
- export const EXTENSION_JS_NO_MODULE = 'js?(x)';
3
- export const EXTENSION_TS = '?(c|m)[jt]s?(x)';
4
- export const EXTENSION_TS_NO_MODULE = '[jt]s?(x)';
5
- export const EXTENSION_TS_ONLY = '?(c|m)ts?(x)';
6
- export const EXTENSION_TS_ONLY_NO_MODULE = 'ts?(x)';
7
- export const EXTENSION_DTS = `.d.${EXTENSION_TS_ONLY}`;
8
- export const EXTENSION_TEST_FILE = `.test.${EXTENSION_TS}`;
1
+ export const EXTENSION_JS = '.?(c|m)js?(x)';
2
+ export const EXTENSION_JS_NO_MODULE = '.js?(x)';
3
+ export const EXTENSION_TS = '.?(c|m)[jt]s?(x)';
4
+ export const EXTENSION_TS_NO_MODULE = '.[jt]s?(x)';
5
+ export const EXTENSION_TS_ONLY = '.?(c|m)ts?(x)';
6
+ export const EXTENSION_TS_ONLY_NO_MODULE = '.ts?(x)';
7
+ export const EXTENSION_DTS = `.d${EXTENSION_TS_ONLY}`;
8
+ export const EXTENSION_TEST_FILE = `.test${EXTENSION_TS}`;
@@ -0,0 +1,40 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import json from '@eslint/json';
3
+
4
+ /**
5
+ * @returns {import('eslint').Linter.Config[]}
6
+ */
7
+ export function createJsonConfig() {
8
+ return defineConfig([
9
+ {
10
+ name: 'JSON files',
11
+ files: ['**/*.json'],
12
+ ignores: ['package-lock.json'],
13
+ plugins: { json },
14
+ language: 'json/json',
15
+ extends: [json.configs.recommended],
16
+ },
17
+
18
+ {
19
+ name: 'JSONC files',
20
+ files: [
21
+ '**/*.jsonc',
22
+ '**/tsconfig.json',
23
+ '**/tsconfig.*.json',
24
+ '.vscode/**/*.json',
25
+ 'renovate.json',
26
+ ],
27
+ plugins: { json },
28
+ language: 'json/jsonc',
29
+ extends: [json.configs.recommended],
30
+ },
31
+
32
+ {
33
+ name: 'JSON5 files',
34
+ files: ['**/*.json5'],
35
+ plugins: { json },
36
+ language: 'json/json5',
37
+ extends: [json.configs.recommended],
38
+ },
39
+ ]);
40
+ }
@@ -1,3 +1,5 @@
1
+ import { defineConfig } from 'eslint/config';
2
+
1
3
  const restrictedMethods = ['setTimeout', 'setInterval', 'clearTimeout', 'clearInterval'];
2
4
 
3
5
  const restrictedSyntaxRules = restrictedMethods.map((method) => ({
@@ -42,9 +44,6 @@ const criticalAirbnbRules = {
42
44
  'no-restricted-globals': ['error', 'isFinite', 'isNaN'],
43
45
 
44
46
  // Styles
45
- // require or disallow an empty line between class members
46
- // https://eslint.org/docs/rules/lines-between-class-members
47
- 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: false }],
48
47
  // require a capital letter for constructors
49
48
  'new-cap': [
50
49
  'error',
@@ -302,11 +301,11 @@ const airbnbJsxA11y = {
302
301
 
303
302
  /**
304
303
  * @param {Object} [options]
305
- * @param {boolean} [options.reactCompilerEnabled] - Whether the config is for spec files.
306
- * @returns {import('typescript-eslint').InfiniteDepthConfigWithExtends[]}
304
+ * @param {boolean} [options.enableReactCompiler] - Whether the config is for spec files.
305
+ * @returns {import('eslint').Linter.Config[]}
307
306
  */
308
307
  export function createCoreConfig(options = {}) {
309
- return [
308
+ return defineConfig([
310
309
  {
311
310
  name: 'material-ui-base',
312
311
  settings: {
@@ -433,7 +432,7 @@ export function createCoreConfig(options = {}) {
433
432
  'react/forbid-prop-types': 'off', // Too strict, no time for that
434
433
  'react/jsx-curly-brace-presence': 'off', // broken
435
434
  // airbnb is using .jsx
436
- 'react/jsx-filename-extension': ['error', { extensions: ['.js', '.tsx'] }],
435
+ 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }],
437
436
  // Prefer <React.Fragment> over <>.
438
437
  'react/jsx-fragments': ['error', 'element'],
439
438
  // Enforces premature optimization
@@ -501,11 +500,11 @@ export function createCoreConfig(options = {}) {
501
500
 
502
501
  'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }],
503
502
  'lines-around-directive': 'off',
504
- ...(options.reactCompilerEnabled ? { 'react-compiler/react-compiler': 'error' } : {}),
503
+ ...(options.enableReactCompiler ? { 'react-compiler/react-compiler': 'error' } : {}),
505
504
  // Prevent the use of `e` as a shorthand for `event`, `error`, etc.
506
505
  'id-denylist': ['error', 'e'],
507
506
  '@typescript-eslint/return-await': 'off',
508
507
  },
509
508
  },
510
- ];
509
+ ]);
511
510
  }
@@ -88,7 +88,8 @@ const rule = {
88
88
  callee.name === 'useDefaultProps' || callee.name === 'useThemeProps';
89
89
  if (isUseDefaultPropsCall) {
90
90
  let isCalledFromCustomHook = false;
91
- let { parent } = node;
91
+ /** @type {import('eslint').Rule.Node | null} */
92
+ let parent = node.parent;
92
93
  while (parent != null) {
93
94
  if (parent.type === 'FunctionExpression' || parent.type === 'FunctionDeclaration') {
94
95
  if (
@@ -112,7 +113,8 @@ const rule = {
112
113
 
113
114
  if (nameLiteral !== null) {
114
115
  let componentName = null;
115
- let { parent } = node;
116
+ /** @type {import('eslint').Rule.Node | null} */
117
+ let parent = node.parent;
116
118
  while (parent != null && componentName === null) {
117
119
  if (parent.type === 'FunctionExpression' || parent.type === 'FunctionDeclaration') {
118
120
  componentName = /** @type {import('estree').Identifier} */ (parent.id).name;
@@ -32,8 +32,9 @@ export default {
32
32
  * @param {import("estree").CallExpression & import("eslint").Rule.NodeParentExtension} hookCallNode
33
33
  */
34
34
  function getComponentBlockNode(hookCallNode) {
35
+ /** @type {import("eslint").Rule.Node | null} */
35
36
  let node = hookCallNode.parent;
36
- while (node !== undefined) {
37
+ while (node) {
37
38
  if (node.type === 'BlockStatement') {
38
39
  return node;
39
40
  }
@@ -1,14 +1,19 @@
1
1
  import mochaPlugin from 'eslint-plugin-mocha';
2
+ import vitestPlugin from '@vitest/eslint-plugin';
2
3
  import testingLibrary from 'eslint-plugin-testing-library';
4
+ import { defineConfig } from 'eslint/config';
3
5
  import globals from 'globals';
4
6
  import * as tseslint from 'typescript-eslint';
7
+ import { EXTENSION_TS } from './extensions.mjs';
5
8
 
6
9
  /**
7
10
  * @type {import('eslint').Linter.Config}
8
11
  */
9
12
  export const baseSpecRules = {
10
- files: ['**/*.spec.*'],
13
+ name: 'Spec files rules',
14
+ files: [`**/*.spec${EXTENSION_TS}`],
11
15
  rules: {
16
+ 'compat/compat': 'off',
12
17
  'no-alert': 'off',
13
18
  'no-console': 'off',
14
19
  'no-empty-pattern': 'off',
@@ -41,79 +46,80 @@ export const baseSpecRules = {
41
46
  /**
42
47
  * @param {Object} [options]
43
48
  * @param {boolean} [options.useMocha]
49
+ * @param {boolean} [options.useVitest]
44
50
  * @returns {import('eslint').Linter.Config[]}
45
51
  */
46
52
  export function createTestConfig(options = {}) {
47
- const { useMocha = true } = options;
48
- return /** @type {import('eslint').Linter.Config[]} */ (
49
- tseslint.config(
50
- // @ts-expect-error The types don't make sense here.
51
- useMocha ? mochaPlugin.configs.recommended : {},
52
- testingLibrary.configs['flat/dom'],
53
- testingLibrary.configs['flat/react'],
54
- {
55
- languageOptions: {
56
- parser: tseslint.parser,
57
- parserOptions: {
58
- ecmaVersion: 7,
59
- },
60
- globals: globals.mocha,
53
+ const { useMocha = true, useVitest = false } = options;
54
+ return defineConfig(
55
+ useMocha ? mochaPlugin.configs.recommended : {},
56
+ useVitest ? vitestPlugin.configs.recommended : {},
57
+ testingLibrary.configs['flat/dom'],
58
+ testingLibrary.configs['flat/react'],
59
+ {
60
+ name: 'Test files',
61
+ languageOptions: {
62
+ parser: tseslint.parser,
63
+ parserOptions: {
64
+ ecmaVersion: 7,
61
65
  },
62
- rules: {
63
- // does not work with wildcard imports. Mistakes will throw at runtime anyway
64
- 'import/named': 'off',
65
- 'material-ui/disallow-active-element-as-key-event-target': 'error',
66
+ globals: globals.mocha,
67
+ },
68
+ rules: {
69
+ 'compat/compat': 'off',
70
+ // does not work with wildcard imports. Mistakes will throw at runtime anyway
71
+ 'import/named': 'off',
72
+ 'material-ui/disallow-active-element-as-key-event-target': 'error',
66
73
 
67
- // disable eslint-plugin-jsx-a11y
68
- // tests are not driven by assistive technology
69
- // add `jsx-a11y` rules once you encounter them in tests
70
- 'jsx-a11y/click-events-have-key-events': 'off',
71
- 'jsx-a11y/control-has-associated-label': 'off',
72
- 'jsx-a11y/iframe-has-title': 'off',
73
- 'jsx-a11y/label-has-associated-control': 'off',
74
- 'jsx-a11y/mouse-events-have-key-events': 'off',
75
- 'jsx-a11y/no-noninteractive-tabindex': 'off',
76
- 'jsx-a11y/no-static-element-interactions': 'off',
77
- 'jsx-a11y/tabindex-no-positive': 'off',
78
- 'jsx-a11y/anchor-is-valid': 'off',
74
+ // disable eslint-plugin-jsx-a11y
75
+ // tests are not driven by assistive technology
76
+ // add `jsx-a11y` rules once you encounter them in tests
77
+ 'jsx-a11y/click-events-have-key-events': 'off',
78
+ 'jsx-a11y/control-has-associated-label': 'off',
79
+ 'jsx-a11y/iframe-has-title': 'off',
80
+ 'jsx-a11y/label-has-associated-control': 'off',
81
+ 'jsx-a11y/mouse-events-have-key-events': 'off',
82
+ 'jsx-a11y/no-noninteractive-tabindex': 'off',
83
+ 'jsx-a11y/no-static-element-interactions': 'off',
84
+ 'jsx-a11y/tabindex-no-positive': 'off',
85
+ 'jsx-a11y/anchor-is-valid': 'off',
79
86
 
80
- // In tests this is generally intended.
81
- 'react/button-has-type': 'off',
82
- // They are accessed to test custom validator implementation with PropTypes.checkPropTypes
83
- 'react/forbid-foreign-prop-types': 'off',
84
- // components that are defined in test are isolated enough
85
- // that they don't need type-checking
86
- 'react/prop-types': 'off',
87
- 'react/no-unused-prop-types': 'off',
88
- // Part of the migration away from airbnb config. Turned of initially.
89
- '@typescript-eslint/no-empty-function': 'off',
90
- '@typescript-eslint/ban-ts-comment': 'off',
91
- 'testing-library/no-node-access': 'off',
92
- '@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
93
- // end migration
94
- ...(useMocha
95
- ? {
96
- 'mocha/consistent-spacing-between-blocks': 'off',
87
+ // In tests this is generally intended.
88
+ 'react/button-has-type': 'off',
89
+ // They are accessed to test custom validator implementation with PropTypes.checkPropTypes
90
+ 'react/forbid-foreign-prop-types': 'off',
91
+ // components that are defined in test are isolated enough
92
+ // that they don't need type-checking
93
+ 'react/prop-types': 'off',
94
+ 'react/no-unused-prop-types': 'off',
95
+ // Part of the migration away from airbnb config. Turned of initially.
96
+ '@typescript-eslint/no-empty-function': 'off',
97
+ '@typescript-eslint/ban-ts-comment': 'off',
98
+ 'testing-library/no-node-access': 'off',
99
+ '@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
100
+ // end migration
101
+ ...(useMocha
102
+ ? {
103
+ 'mocha/consistent-spacing-between-blocks': 'off',
97
104
 
98
- // upgraded level from recommended
99
- 'mocha/no-pending-tests': 'error',
105
+ // upgraded level from recommended
106
+ 'mocha/no-pending-tests': 'error',
100
107
 
101
- // no rationale provided in /recommended
102
- 'mocha/no-mocha-arrows': 'off',
103
- // definitely a useful rule but too many false positives
104
- // due to `describeConformance`
105
- // "If you're using dynamically generated tests, you should disable this rule.""
106
- 'mocha/no-setup-in-describe': 'off',
107
- // `beforeEach` for a single case is optimized for change
108
- // when we add a test we don't have to refactor the existing
109
- // test to `beforeEach`.
110
- // `beforeEach`+`afterEach` also means that the `beforeEach`
111
- // is cleaned up in `afterEach` if the test causes a crash
112
- 'mocha/no-hooks-for-single-case': 'off',
113
- }
114
- : {}),
115
- },
108
+ // no rationale provided in /recommended
109
+ 'mocha/no-mocha-arrows': 'off',
110
+ // definitely a useful rule but too many false positives
111
+ // due to `describeConformance`
112
+ // "If you're using dynamically generated tests, you should disable this rule.""
113
+ 'mocha/no-setup-in-describe': 'off',
114
+ // `beforeEach` for a single case is optimized for change
115
+ // when we add a test we don't have to refactor the existing
116
+ // test to `beforeEach`.
117
+ // `beforeEach`+`afterEach` also means that the `beforeEach`
118
+ // is cleaned up in `afterEach` if the test causes a crash
119
+ 'mocha/no-hooks-for-single-case': 'off',
120
+ }
121
+ : {}),
116
122
  },
117
- )
123
+ },
118
124
  );
119
125
  }
@@ -0,0 +1,46 @@
1
+ import postcssStylesSyntax from 'postcss-styled-syntax';
2
+ import standardConfig from 'stylelint-config-standard';
3
+
4
+ /** @type {import('stylelint').Config} */
5
+ export default {
6
+ extends: standardConfig,
7
+ rules: {
8
+ // Too opinionated?
9
+ 'no-descending-specificity': null,
10
+
11
+ // Adopted from mui/material-ui repo
12
+ 'alpha-value-notation': null,
13
+ 'custom-property-pattern': null,
14
+ 'media-feature-range-notation': null,
15
+ 'no-empty-source': null,
16
+ 'selector-class-pattern': null,
17
+ 'string-no-newline': null, // not compatible with prettier
18
+ 'value-keyword-case': null,
19
+ 'import-notation': null,
20
+
21
+ // Responsibility of prettier:
22
+ 'at-rule-empty-line-before': null,
23
+ 'comment-empty-line-before': null,
24
+ 'custom-property-empty-line-before': null,
25
+ 'declaration-empty-line-before': null,
26
+ 'rule-empty-line-before': null,
27
+
28
+ // Tailwind
29
+ 'at-rule-no-unknown': [true, { ignoreAtRules: ['theme', 'config'] }],
30
+
31
+ // Don't assume we use a preprocessor
32
+ 'property-no-vendor-prefix': null,
33
+ 'property-no-deprecated': null,
34
+ 'declaration-property-value-keyword-no-deprecated': null,
35
+
36
+ // Responsibility of a minifier
37
+ 'color-hex-length': null,
38
+ 'declaration-block-no-redundant-longhand-properties': null,
39
+ },
40
+ overrides: [
41
+ {
42
+ files: ['**/*.?(c|m)[jt]s?(x)'],
43
+ customSyntax: /** @type {any} */ (postcssStylesSyntax),
44
+ },
45
+ ],
46
+ };
@@ -154,3 +154,16 @@ declare module '@babel/preset-typescript' {
154
154
  declare const preset: PluginItem;
155
155
  export default preset;
156
156
  }
157
+
158
+ declare module 'stylelint-config-standard' {
159
+ import type { Config } from 'stylelint';
160
+
161
+ declare const configExtends: Config['extends'];
162
+ export default configExtends;
163
+ }
164
+ declare module 'postcss-styled-syntax' {
165
+ import type { Syntax } from 'postcss';
166
+
167
+ declare const syntax: Syntax;
168
+ export default syntax;
169
+ }