linter-bundle 4.0.3 → 5.0.0
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.
- package/.linter-bundle.js +8 -8
- package/CHANGELOG.md +15 -1
- package/README.md +1 -1
- package/eslint/{index.js → index.cjs} +7 -7
- package/eslint/{overrides-javascript-lazy.js → overrides-javascript-lazy.cjs} +1 -1
- package/eslint/{overrides-javascript.js → overrides-javascript.cjs} +1 -0
- package/eslint/{overrides-react.js → overrides-react.cjs} +4 -4
- package/eslint/rules/no-unnecessary-typeof.js +12 -1
- package/eslint/rules/package.json +8 -0
- package/eslint/rules/restricted-filenames.js +4 -4
- package/eslint/rules/restricted-filenames.md +1 -1
- package/files/index.js +54 -24
- package/helper/{ensure-type.js → ensure-type.cjs} +2 -0
- package/helper/get-git-files.js +3 -7
- package/helper/get-outdated-dependencies.js +48 -0
- package/helper/{validate-package-overrides.js → get-outdated-overrides.js} +9 -10
- package/helper/get-stylelint-path.js +15 -12
- package/helper/is-npm-or-yarn.js +12 -12
- package/helper/linter-bundle-config.cjs +70 -0
- package/helper/linter-bundle-config.d.ts +46 -0
- package/helper/linter-bundle-config.js +36 -0
- package/helper/run-process.js +14 -10
- package/lint.js +32 -26
- package/package.json +8 -7
- package/stylelint/index.cjs +1007 -0
- package/stylelint/plugins/stylelint-high-performance-animation.js +4 -5
- package/stylelint/plugins/stylelint-selector-no-empty.js +2 -2
- package/stylelint/plugins/stylelint-selector-tag-no-without-class.js +6 -7
- package/stylelint/plugins/stylelint-stylistic.js +7 -6
- package/helper/config.js +0 -48
- package/helper/find-missing-overrides.js +0 -49
- package/stylelint/index.js +0 -1007
- package/types.d.ts +0 -43
- /package/eslint/{overrides-gatsby.js → overrides-gatsby.cjs} +0 -0
- /package/eslint/{overrides-jest.js → overrides-jest.cjs} +0 -0
- /package/eslint/{overrides-jsdoc.js → overrides-jsdoc.cjs} +0 -0
- /package/eslint/{overrides-storybook.js → overrides-storybook.cjs} +0 -0
- /package/eslint/{overrides-type-declarations.js → overrides-type-declarations.cjs} +0 -0
- /package/eslint/{overrides-worker.js → overrides-worker.cjs} +0 -0
package/.linter-bundle.js
CHANGED
|
@@ -8,7 +8,7 @@ const snippets = {
|
|
|
8
8
|
pascalCase: '[A-Z]*([a-zA-Z0-9])'
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
export default {
|
|
12
12
|
files: {
|
|
13
13
|
restrictions: [
|
|
14
14
|
{
|
|
@@ -17,16 +17,17 @@ module.exports = {
|
|
|
17
17
|
'.git/**',
|
|
18
18
|
'.github/FUNDING.yml',
|
|
19
19
|
'.vscode/settings.json',
|
|
20
|
+
`eslint/rules/package.json`,
|
|
20
21
|
`eslint/rules/${snippets.kebabCase}.{js,md}`,
|
|
21
|
-
`eslint/{index.
|
|
22
|
+
`eslint/{index.cjs,overrides-${snippets.kebabCase}.cjs}`,
|
|
22
23
|
'files/index.js',
|
|
23
|
-
`helper/${snippets.kebabCase}.js`,
|
|
24
|
+
`helper/${snippets.kebabCase}.{js,cjs,d.ts}`,
|
|
24
25
|
'markdownlint/base.json',
|
|
25
26
|
'node_modules/**',
|
|
26
|
-
'stylelint/index.
|
|
27
|
+
'stylelint/index.cjs',
|
|
27
28
|
`stylelint/plugins/stylelint-${snippets.kebabCase}.js`,
|
|
28
29
|
'.editorconfig',
|
|
29
|
-
'.eslintrc.
|
|
30
|
+
'.eslintrc.cjs',
|
|
30
31
|
'.gitattributes',
|
|
31
32
|
'.gitignore',
|
|
32
33
|
'.linter-bundle.js',
|
|
@@ -37,10 +38,9 @@ module.exports = {
|
|
|
37
38
|
'lint.js',
|
|
38
39
|
'package-lock.json',
|
|
39
40
|
'package.json',
|
|
40
|
-
'stylelint.config.
|
|
41
|
+
'stylelint.config.cjs',
|
|
42
|
+
'test-stylelint.js',
|
|
41
43
|
'tsconfig.json',
|
|
42
|
-
'types.d.ts',
|
|
43
|
-
'validate-stylelint-options.mjs',
|
|
44
44
|
'vscode-eslint-1.png',
|
|
45
45
|
'vscode-eslint-2.png'
|
|
46
46
|
]
|
package/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
[Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/
|
|
9
|
+
[Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v5.0.0...HEAD)
|
|
10
|
+
|
|
11
|
+
## [5.0.0] - 2023-09-03
|
|
12
|
+
|
|
13
|
+
## Changed
|
|
14
|
+
|
|
15
|
+
- [general] Migrated the code base from CommonJS to ESModules (as far as possible, because ESLint currently does not support ESModules)
|
|
16
|
+
- [general] The linter-bundle configuration can now be a ESModule (`.linter-bundle.mjs` is supported)
|
|
17
|
+
- [files] Report unmatched `allowed` pattern
|
|
18
|
+
- [markdownlint] Updated `markdownlint-cli` from `0.35.0` to `0.36.0`
|
|
19
|
+
- [eslint] Disabled `considerComments` option of [`import/new-after-import`](https://github.com/import-js/eslint-plugin-import/blob/v2.28.1/docs/rules/newline-after-import.md) rule
|
|
20
|
+
as it conflicts with the [`import/order`](https://github.com/import-js/eslint-plugin-import/blob/v2.28.1/docs/rules/order.md) rule
|
|
21
|
+
- [eslint/overrides-javascript] Disable [`import/dynamic-import-chunkname`](https://github.com/import-js/eslint-plugin-import/blob/v2.28.1/docs/rules/dynamic-import-chunkname.md) for JavaScript files
|
|
22
|
+
|
|
23
|
+
[Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v4.0.3...v5.0.0)
|
|
10
24
|
|
|
11
25
|
## [4.0.3] - 2023-08-31
|
|
12
26
|
|
package/README.md
CHANGED
|
@@ -622,7 +622,7 @@ This can be done by adding these options to your `.vscode/settings.json`:
|
|
|
622
622
|
{
|
|
623
623
|
"eslint.nodePath": "./node_modules/linter-bundle/node_modules/eslint",
|
|
624
624
|
"eslint.options": {
|
|
625
|
-
"overrideConfigFile": "./.eslintrc.js",
|
|
625
|
+
"overrideConfigFile": "./.eslintrc.js", // or "./.eslintrc.cjs" or "./eslintrc.json"
|
|
626
626
|
"resolvePluginsRelativeTo": "./node_modules/linter-bundle",
|
|
627
627
|
"rulePaths": ["./node_modules/linter-bundle/eslint/rules"],
|
|
628
628
|
"reportUnusedDisableDirectives": "error",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
const fs = require('node:fs');
|
|
9
9
|
const path = require('node:path');
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const ensureType = require('../helper/ensure-type.cjs');
|
|
12
|
+
const { linterBundleConfig } = require('../helper/linter-bundle-config.cjs');
|
|
13
13
|
|
|
14
14
|
module.exports = {
|
|
15
15
|
ignorePatterns: [
|
|
@@ -283,16 +283,16 @@ module.exports = {
|
|
|
283
283
|
name: 'isNaN',
|
|
284
284
|
message: 'Use Number.isNaN() instead, and ensure that the input value is of type number. isNaN(undefined) !== Number.isNaN(undefined)'
|
|
285
285
|
},
|
|
286
|
-
...ensureType.array(
|
|
286
|
+
...ensureType.array(linterBundleConfig.ts?.overrides?.general?.['no-restricted-globals']?.additionalRestrictions)
|
|
287
287
|
],
|
|
288
288
|
'no-restricted-imports': 'off', // Covered by @typescript-eslint/no-restricted-imports
|
|
289
289
|
'no-restricted-properties': [
|
|
290
290
|
'error',
|
|
291
|
-
...ensureType.array(
|
|
291
|
+
...ensureType.array(linterBundleConfig.ts?.overrides?.general?.['no-restricted-properties']?.additionalRestrictions)
|
|
292
292
|
],
|
|
293
293
|
'no-restricted-syntax': [
|
|
294
294
|
'error',
|
|
295
|
-
...ensureType.array(
|
|
295
|
+
...ensureType.array(linterBundleConfig.ts?.overrides?.general?.['no-restricted-syntax']?.additionalRestrictions)
|
|
296
296
|
],
|
|
297
297
|
'no-return-assign': 'error',
|
|
298
298
|
'no-script-url': 'error',
|
|
@@ -821,7 +821,7 @@ module.exports = {
|
|
|
821
821
|
'import/max-dependencies': ['error', { max: 20, ignoreTypeImports: true }],
|
|
822
822
|
'import/named': 'error',
|
|
823
823
|
'import/namespace': ['error', { allowComputed: true }],
|
|
824
|
-
'import/newline-after-import': ['error', { considerComments:
|
|
824
|
+
'import/newline-after-import': ['error', { considerComments: false }],
|
|
825
825
|
'import/no-absolute-path': 'error',
|
|
826
826
|
'import/no-amd': 'error',
|
|
827
827
|
'import/no-anonymous-default-export': 'error',
|
|
@@ -866,7 +866,7 @@ module.exports = {
|
|
|
866
866
|
'newlines-between': 'always',
|
|
867
867
|
'pathGroupsExcludedImportTypes': [],
|
|
868
868
|
'pathGroups': [
|
|
869
|
-
...ensureType.array(
|
|
869
|
+
...ensureType.array(linterBundleConfig.ts?.overrides?.general?.['import/order']?.additionalExternalPatterns).map(
|
|
870
870
|
/**
|
|
871
871
|
* Creates an "external" group using the additional external pattern configuration.
|
|
872
872
|
* @param {string} pattern - A given pattern
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @file Settings for React code in TypeScript (TSX) files.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const ensureType = require('../helper/ensure-type.cjs');
|
|
6
|
+
const { linterBundleConfig } = require('../helper/linter-bundle-config.cjs');
|
|
7
7
|
|
|
8
8
|
module.exports = {
|
|
9
9
|
overrides: [
|
|
@@ -91,11 +91,11 @@ module.exports = {
|
|
|
91
91
|
'react/forbid-component-props': ['error', { forbid: [
|
|
92
92
|
{
|
|
93
93
|
propName: 'className',
|
|
94
|
-
allowedFor: ensureType.array(
|
|
94
|
+
allowedFor: ensureType.array(linterBundleConfig.ts?.overrides?.react?.['react/forbid-component-props']?.allowClassNameFor)
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
propName: 'style',
|
|
98
|
-
allowedFor: ensureType.array(
|
|
98
|
+
allowedFor: ensureType.array(linterBundleConfig.ts?.overrides?.react?.['react/forbid-component-props']?.allowStyleFor)
|
|
99
99
|
}
|
|
100
100
|
] }],
|
|
101
101
|
'react/forbid-dom-props': 'error',
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
const ts = require('typescript');
|
|
8
8
|
|
|
9
|
-
// @ts-expect-error -- TypeScript is not able to resolve the folder since @typescript-eslint v6.0.0
|
|
10
9
|
const { ESLintUtils } = require('@typescript-eslint/utils');
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -22,8 +21,20 @@ module.exports = {
|
|
|
22
21
|
text: 'Unnecessary `typeof`, because the only possible type of {{ variableName }} is `{{ typeName }}`.'
|
|
23
22
|
}
|
|
24
23
|
},
|
|
24
|
+
/**
|
|
25
|
+
* Create a new rule.
|
|
26
|
+
*
|
|
27
|
+
* @param {Readonly<import('@typescript-eslint/utils/ts-eslint').RuleContext<'text', []>>} context - RuleContext of @typescript-eslint instead of ESlint
|
|
28
|
+
* @returns {import('@typescript-eslint/utils/ts-eslint').RuleListener} RuleListener of @typescript-eslint, instead of ESlint
|
|
29
|
+
*/
|
|
25
30
|
create (context) {
|
|
26
31
|
return {
|
|
32
|
+
/**
|
|
33
|
+
* Rule function to handle unary expressions.
|
|
34
|
+
*
|
|
35
|
+
* @param {import('@typescript-eslint/typescript-estree').TSESTree.UnaryExpression} node - UnaryExpression of @typescript-eslint instead of ESlint
|
|
36
|
+
* @returns {import('@typescript-eslint/utils/ts-eslint').RuleFunction<import('@typescript-eslint/typescript-estree').TSESTree.UnaryExpression> | void} RuleFunction of @typescript-eslint instead of ESlint
|
|
37
|
+
*/
|
|
27
38
|
UnaryExpression (node) {
|
|
28
39
|
if (node.operator !== 'typeof') {
|
|
29
40
|
return;
|
|
@@ -6,7 +6,8 @@ const path = require('node:path');
|
|
|
6
6
|
|
|
7
7
|
const micromatch = require('micromatch');
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// eslint-disable-next-line n/no-process-env -- Only merge the linter-bundle config, if the linting is not started by the linter-bundle CLI tool (e.g. if ESlint is running separately in VSCode), to get warnings shown there too
|
|
10
|
+
const linterBundleConfig = (!process.env['LINTER_BUNDLE'] ? require('../../helper/linter-bundle-config.cjs').linterBundleConfig : undefined);
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* @type {import('eslint').Rule.RuleModule}
|
|
@@ -50,10 +51,9 @@ module.exports = {
|
|
|
50
51
|
}
|
|
51
52
|
},
|
|
52
53
|
create: (context) => {
|
|
53
|
-
const filePath = context.
|
|
54
|
+
const filePath = context.filename;
|
|
54
55
|
/** @type {{ basePath: string, allowed?: string[]; disallowed?: string[]; }[]} */
|
|
55
|
-
|
|
56
|
-
const options = (!process.env['LINTER_BUNDLE'] && config.files?.restrictions ? [...config.files.restrictions, ...context.options] : context.options);
|
|
56
|
+
const options = linterBundleConfig?.files?.restrictions ? [...linterBundleConfig.files.restrictions, ...context.options] : context.options;
|
|
57
57
|
|
|
58
58
|
for (const { basePath, allowed, disallowed } of options) {
|
|
59
59
|
const normalizedName = path.relative(path.join(process.cwd(), basePath), filePath);
|
|
@@ -64,7 +64,7 @@ If both are set, `disallowed` wins over `allowed` and all unspecified files are
|
|
|
64
64
|
|
|
65
65
|
Instead of defining the same complex patterns over and over again, e.g. for casing, you can write them into a variable and use them within the pattern, for example by using template strings.
|
|
66
66
|
|
|
67
|
-
Example of `.eslintrc.
|
|
67
|
+
Example of `.eslintrc.cjs`:
|
|
68
68
|
|
|
69
69
|
```js
|
|
70
70
|
const snippets = {
|
package/files/index.js
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
* @file Ensures that only files which match given glob patterns are part of the project.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import micromatch from 'micromatch';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
import { linterBundleConfig } from '../helper/linter-bundle-config.js';
|
|
11
11
|
|
|
12
|
-
const restrictions =
|
|
12
|
+
const restrictions = linterBundleConfig.files?.restrictions;
|
|
13
|
+
const matcherOptions = { dot: true };
|
|
13
14
|
|
|
14
15
|
if (Array.isArray(restrictions)) {
|
|
15
|
-
let
|
|
16
|
+
let noOfDisallowedFiles = 0;
|
|
16
17
|
|
|
17
18
|
const argFiles = process.argv.splice(2).map((filePath) => path.join(process.cwd(), filePath));
|
|
18
19
|
|
|
@@ -25,7 +26,24 @@ if (Array.isArray(restrictions)) {
|
|
|
25
26
|
const report = (filePath) => {
|
|
26
27
|
process.stderr.write(`Disallowed filename "${filePath}"\n`);
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
noOfDisallowedFiles++;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reports the error to the `console` and increase the error counter.
|
|
34
|
+
*
|
|
35
|
+
* @param {[string, (str: string) => boolean][]} matchers - Array of micromatch matchers
|
|
36
|
+
* @param {string} filePath - Path to the file to check
|
|
37
|
+
* @returns {string | undefined} On match returns the matching pattern, otherwise `undefined`
|
|
38
|
+
*/
|
|
39
|
+
const isMatch = (matchers, filePath) => {
|
|
40
|
+
for (const [pattern, matcher] of matchers) {
|
|
41
|
+
if (matcher(filePath)) {
|
|
42
|
+
return pattern;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return;
|
|
29
47
|
};
|
|
30
48
|
|
|
31
49
|
for (const { basePath, allowed, disallowed } of restrictions) {
|
|
@@ -33,38 +51,50 @@ if (Array.isArray(restrictions)) {
|
|
|
33
51
|
|
|
34
52
|
const files = (argFiles.length > 0 ? argFiles.filter((filePath) => !path.relative(absoluteBasePath, filePath).startsWith('..')) : getFiles(absoluteBasePath));
|
|
35
53
|
|
|
54
|
+
const allowedMatchers = allowed?.map((pattern) => /** @type {[string, (str: string) => boolean]} */([pattern, micromatch.matcher(pattern, matcherOptions)]));
|
|
55
|
+
const disallowedMatchers = disallowed?.map((pattern) => /** @type {[string, (str: string) => boolean]} */([pattern, micromatch.matcher(pattern, matcherOptions)]));
|
|
56
|
+
|
|
57
|
+
const unmatchedAllowedPatterns = new Set(allowed);
|
|
58
|
+
|
|
36
59
|
for (const filePath of files) {
|
|
37
60
|
const normalizedFilePath = path.relative(absoluteBasePath, filePath);
|
|
38
61
|
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
report(normalizedFilePath);
|
|
62
|
+
if (allowedMatchers && !disallowedMatchers) {
|
|
63
|
+
const pattern = isMatch(allowedMatchers, normalizedFilePath);
|
|
42
64
|
|
|
43
|
-
|
|
65
|
+
if (pattern) {
|
|
66
|
+
unmatchedAllowedPatterns.delete(pattern);
|
|
44
67
|
}
|
|
45
|
-
|
|
46
|
-
else if (!allowed && disallowed) {
|
|
47
|
-
if (micromatch.isMatch(normalizedFilePath, disallowed, { dot: true })) {
|
|
68
|
+
else {
|
|
48
69
|
report(normalizedFilePath);
|
|
49
|
-
|
|
50
|
-
continue;
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
|
-
else if (
|
|
54
|
-
if (
|
|
55
|
-
micromatch.isMatch(normalizedFilePath, disallowed, { dot: true }) ||
|
|
56
|
-
!micromatch.isMatch(normalizedFilePath, allowed, { dot: true })
|
|
57
|
-
) {
|
|
72
|
+
else if (!allowedMatchers && disallowedMatchers) {
|
|
73
|
+
if (isMatch(disallowedMatchers, normalizedFilePath)) {
|
|
58
74
|
report(normalizedFilePath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (allowedMatchers && disallowedMatchers) {
|
|
78
|
+
const pattern = isMatch(allowedMatchers, normalizedFilePath);
|
|
59
79
|
|
|
60
|
-
|
|
80
|
+
if (pattern) {
|
|
81
|
+
unmatchedAllowedPatterns.delete(pattern);
|
|
61
82
|
}
|
|
83
|
+
else if (isMatch(disallowedMatchers, normalizedFilePath)) {
|
|
84
|
+
report(normalizedFilePath);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (unmatchedAllowedPatterns.size > 0) {
|
|
90
|
+
for (const [pattern] of unmatchedAllowedPatterns.entries()) {
|
|
91
|
+
process.stderr.write(`Unmatched "allowed" pattern: "${pattern}"\n`);
|
|
62
92
|
}
|
|
63
93
|
}
|
|
64
94
|
}
|
|
65
95
|
|
|
66
|
-
if (
|
|
67
|
-
process.stderr.write(`\nFound ${
|
|
96
|
+
if (noOfDisallowedFiles > 0) {
|
|
97
|
+
process.stderr.write(`\nFound ${noOfDisallowedFiles} disallowed file${(noOfDisallowedFiles !== 0 ? 's' : '')}\n`);
|
|
68
98
|
|
|
69
99
|
process.exitCode = -1;
|
|
70
100
|
}
|
package/helper/get-git-files.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* @file Handling of changed files from Git.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import { runProcess } from './run-process.js';
|
|
6
6
|
|
|
7
|
-
/** @typedef {import('./run-process').ProcessResult} ProcessResult */
|
|
7
|
+
/** @typedef {import('./run-process.js').ProcessResult} ProcessResult */
|
|
8
8
|
|
|
9
9
|
/** @type {{ diff: Promise<ProcessResult>; modified: Promise<ProcessResult>; deleted: Promise<ProcessResult>; } | undefined} */
|
|
10
10
|
let gitFilesProcessPromise;
|
|
@@ -17,7 +17,7 @@ let gitFiles;
|
|
|
17
17
|
* @public
|
|
18
18
|
* @returns {Promise<string[]>} The list of changed files.
|
|
19
19
|
*/
|
|
20
|
-
async function getGitFiles () {
|
|
20
|
+
export async function getGitFiles () {
|
|
21
21
|
if (!gitFilesProcessPromise) {
|
|
22
22
|
gitFilesProcessPromise = {
|
|
23
23
|
// Returns changed files, also stashed and committed
|
|
@@ -46,7 +46,3 @@ async function getGitFiles () {
|
|
|
46
46
|
|
|
47
47
|
return gitFiles;
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
module.exports = {
|
|
51
|
-
getGitFiles
|
|
52
|
-
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Ensures that the installed dependencies of the project matches to the versions used by the linter-bundle.
|
|
3
|
+
*
|
|
4
|
+
* @see https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides
|
|
5
|
+
* @see https://classic.yarnpkg.com/en/docs/selective-version-resolutions/
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
/** @typedef {{ name: string; configuredVersion: string; expectedVersion: string; }} Dependency */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Detects if installed versions of dependencies don't match to the required dependencies.
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
* @returns {Promise<Dependency[]>} An array of missing overrides (=wrong versions).
|
|
21
|
+
*/
|
|
22
|
+
export async function getOutdatedDependencies () {
|
|
23
|
+
const linterBundleDependencies = JSON.parse(await fs.readFile(path.resolve(dirname, '../package.json'), 'utf8')).dependencies;
|
|
24
|
+
|
|
25
|
+
const outdatedDependencies = await Promise.all(Object.entries(linterBundleDependencies).map(async ([name, expectedVersion]) => {
|
|
26
|
+
let dependencyPackageJson;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
dependencyPackageJson = JSON.parse(await fs.readFile(path.join(process.cwd(), 'node_modules', name, 'package.json'), 'utf8'));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// If the file does not exist, we ignore it, because in this case it's most likely a linter-bundle-only dependency.
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (dependencyPackageJson.version !== expectedVersion) {
|
|
37
|
+
return {
|
|
38
|
+
name,
|
|
39
|
+
configuredVersion: dependencyPackageJson.version,
|
|
40
|
+
expectedVersion
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return;
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
return /** @type {Dependency[]} */(outdatedDependencies.filter((dependency) => dependency !== undefined));
|
|
48
|
+
}
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
* @see https://classic.yarnpkg.com/en/docs/selective-version-resolutions/
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import * as fs from 'node:fs/promises';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
13
|
|
|
11
14
|
/** @typedef {{ name: string; configuredVersion: string; expectedVersion: string; }} Dependency */
|
|
12
15
|
|
|
@@ -14,11 +17,11 @@ const path = require('node:path');
|
|
|
14
17
|
* Detects outdated "overrides"/"resolutions" dependencies.
|
|
15
18
|
*
|
|
16
19
|
* @public
|
|
17
|
-
* @returns {{ overrides: Dependency[]; resolutions: Dependency[]; }} Either the input array, or an empty array, if the input array is not an array.
|
|
20
|
+
* @returns {Promise<{ overrides: Dependency[]; resolutions: Dependency[]; }>} Either the input array, or an empty array, if the input array is not an array.
|
|
18
21
|
*/
|
|
19
|
-
function
|
|
20
|
-
const linterBundleDependencies = JSON.parse(fs.
|
|
21
|
-
const projectPackageJson = JSON.parse(fs.
|
|
22
|
+
export async function getOutdatedOverrides () {
|
|
23
|
+
const linterBundleDependencies = JSON.parse(await fs.readFile(path.resolve(dirname, '../package.json'), 'utf8')).dependencies;
|
|
24
|
+
const projectPackageJson = JSON.parse(await fs.readFile(path.join(process.cwd(), 'package.json'), 'utf8'));
|
|
22
25
|
|
|
23
26
|
const overrides = [];
|
|
24
27
|
const resolutions = [];
|
|
@@ -57,7 +60,3 @@ function validatePackageOverrides () {
|
|
|
57
60
|
|
|
58
61
|
return { overrides, resolutions };
|
|
59
62
|
}
|
|
60
|
-
|
|
61
|
-
module.exports = {
|
|
62
|
-
validatePackageOverrides
|
|
63
|
-
};
|
|
@@ -2,31 +2,34 @@
|
|
|
2
2
|
* @file Returns the path to the Stylelint CLI script.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import * as fs from 'node:fs/promises';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import * as path from 'node:path';
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Returns if the project is using npm or yarn.
|
|
10
13
|
*
|
|
11
14
|
* @public
|
|
12
|
-
* @returns {string | null} Return the path to the Stylelint CLI script, or `null` if it can't be found.
|
|
15
|
+
* @returns {Promise<string | null>} Return the path to the Stylelint CLI script, or `null` if it can't be found.
|
|
13
16
|
*/
|
|
14
|
-
function getStylelintPath () {
|
|
17
|
+
export async function getStylelintPath () {
|
|
15
18
|
const stylelintLibPath = path.dirname(require.resolve('stylelint'));
|
|
16
19
|
|
|
17
|
-
for (const stylelintBinPath of [
|
|
20
|
+
for await (const stylelintBinPath of [
|
|
18
21
|
path.join(stylelintLibPath, '../bin/stylelint.mjs'),
|
|
19
22
|
path.join(stylelintLibPath, '../bin/stylelint.js')
|
|
20
23
|
]) {
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
try {
|
|
25
|
+
const stat = await fs.stat(stylelintBinPath);
|
|
26
|
+
|
|
27
|
+
if (stat.isFile()) {
|
|
28
|
+
return stylelintBinPath;
|
|
29
|
+
}
|
|
23
30
|
}
|
|
31
|
+
catch { /* Do nothing */ }
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
return null;
|
|
27
35
|
}
|
|
28
|
-
|
|
29
|
-
module.exports = {
|
|
30
|
-
getStylelintPath
|
|
31
|
-
};
|
|
32
|
-
|
package/helper/is-npm-or-yarn.js
CHANGED
|
@@ -2,30 +2,34 @@
|
|
|
2
2
|
* @file Check if the project is using npm or yarn by checking the existence of a `package-lock.json` or a `yarn.lock`.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import * as fs from 'node:fs/promises';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Returns if the project is using npm or yarn.
|
|
10
10
|
*
|
|
11
11
|
* @public
|
|
12
|
-
* @returns {'none' | 'npm' | 'yarn' | 'both'} Returns which package manager name.
|
|
12
|
+
* @returns {Promise<'none' | 'npm' | 'yarn' | 'both'>} Returns which package manager name.
|
|
13
13
|
*/
|
|
14
|
-
function isNpmOrYarn () {
|
|
14
|
+
export async function isNpmOrYarn () {
|
|
15
15
|
let npm = false;
|
|
16
16
|
let yarn = false;
|
|
17
17
|
|
|
18
18
|
try {
|
|
19
|
-
fs.
|
|
19
|
+
const stat = await fs.stat(path.join(process.cwd(), 'package-lock.json'));
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
if (stat.isFile()) {
|
|
22
|
+
npm = true;
|
|
23
|
+
}
|
|
22
24
|
}
|
|
23
25
|
catch { /* `package-lock.json` cannot be accessed. */ }
|
|
24
26
|
|
|
25
27
|
try {
|
|
26
|
-
fs.
|
|
28
|
+
const stat = await fs.stat(path.join(process.cwd(), 'yarn.lock'));
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
if (stat.isFile()) {
|
|
31
|
+
yarn = true;
|
|
32
|
+
}
|
|
29
33
|
}
|
|
30
34
|
catch { /* `yarn.lock` cannot be accessed. */ }
|
|
31
35
|
|
|
@@ -43,7 +47,3 @@ function isNpmOrYarn () {
|
|
|
43
47
|
|
|
44
48
|
return 'none';
|
|
45
49
|
}
|
|
46
|
-
|
|
47
|
-
module.exports = {
|
|
48
|
-
isNpmOrYarn
|
|
49
|
-
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file CommonJS loader for `linter-bundle-config.js`.
|
|
3
|
+
*
|
|
4
|
+
* This module has to be CommonJS as it is only used by `eslint` which does not support ESModules.
|
|
5
|
+
*
|
|
6
|
+
* This workaround is necessary to load async ESModules in sync CommonJS code.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { Worker, isMainThread, parentPort } = require('node:worker_threads');
|
|
10
|
+
|
|
11
|
+
if (isMainThread) {
|
|
12
|
+
const worker = new Worker(__filename);
|
|
13
|
+
|
|
14
|
+
const sizeSharedArrayBuffer = new SharedArrayBuffer(4);
|
|
15
|
+
const sizeInt32 = new Int32Array(sizeSharedArrayBuffer);
|
|
16
|
+
|
|
17
|
+
worker.postMessage({ sizeInt32 });
|
|
18
|
+
|
|
19
|
+
if (Atomics.wait(sizeInt32, 0, 0, 10_000) !== 'ok') {
|
|
20
|
+
throw new Error('No size received');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const dataSharedArrayBuffer = new SharedArrayBuffer((Math.ceil(sizeInt32[0] / 4) << 2));
|
|
24
|
+
const dataInt32 = new Int32Array(dataSharedArrayBuffer);
|
|
25
|
+
|
|
26
|
+
worker.postMessage({ dataInt32 });
|
|
27
|
+
|
|
28
|
+
if (Atomics.wait(dataInt32, 0, 0, 250) !== 'ok') {
|
|
29
|
+
throw new Error('No data received');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void worker.terminate();
|
|
33
|
+
|
|
34
|
+
const textDecoder = new TextDecoder('utf8');
|
|
35
|
+
const content = textDecoder.decode(new Uint8Array(dataSharedArrayBuffer).slice(0, sizeInt32[0]));
|
|
36
|
+
|
|
37
|
+
// @ts-expect-error TypeScript complains about the ESModule import, but correctly resolves it and applies the type.
|
|
38
|
+
/** @type {import('./linter-bundle-config.js')} */
|
|
39
|
+
module.exports = JSON.parse(content);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
/** @type {Uint8Array} */
|
|
43
|
+
let content;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Handles requests from the main thread.
|
|
47
|
+
*
|
|
48
|
+
* @param {{ sizeInt32?: Int32Array; dataInt32?: Int32Array; }} data - Data received from the main thread
|
|
49
|
+
* @returns {Promise<void>}
|
|
50
|
+
*/
|
|
51
|
+
const onData = async (data) => {
|
|
52
|
+
if (data.sizeInt32) {
|
|
53
|
+
const json = JSON.stringify(await import('./linter-bundle-config.js'));
|
|
54
|
+
|
|
55
|
+
const textEncoder = new TextEncoder();
|
|
56
|
+
|
|
57
|
+
content = textEncoder.encode(json);
|
|
58
|
+
|
|
59
|
+
Atomics.store(data.sizeInt32, 0, content.byteLength);
|
|
60
|
+
Atomics.notify(data.sizeInt32, 0);
|
|
61
|
+
}
|
|
62
|
+
else if (data.dataInt32) {
|
|
63
|
+
new Uint8Array(data.dataInt32.buffer).set(content);
|
|
64
|
+
|
|
65
|
+
Atomics.notify(data.dataInt32, 0);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
parentPort?.on('message', onData);
|
|
70
|
+
}
|