linter-bundle 4.0.3 → 5.0.1

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 (41) hide show
  1. package/.linter-bundle.js +10 -8
  2. package/CHANGELOG.md +28 -1
  3. package/README.md +24 -24
  4. package/eslint/{index.js → index.cjs} +7 -7
  5. package/eslint/{overrides-javascript-lazy.js → overrides-javascript-lazy.cjs} +1 -1
  6. package/eslint/{overrides-javascript.js → overrides-javascript.cjs} +1 -0
  7. package/eslint/{overrides-react.js → overrides-react.cjs} +4 -4
  8. package/eslint/rules/no-unnecessary-typeof.js +12 -1
  9. package/eslint/rules/package.json +8 -0
  10. package/eslint/rules/restricted-filenames.js +4 -4
  11. package/eslint/rules/restricted-filenames.md +1 -1
  12. package/eslint.cjs +5 -0
  13. package/files/index.js +54 -24
  14. package/helper/{ensure-type.js → ensure-type.cjs} +2 -0
  15. package/helper/get-git-files.js +3 -7
  16. package/helper/get-outdated-dependencies.js +48 -0
  17. package/helper/{validate-package-overrides.js → get-outdated-overrides.js} +9 -10
  18. package/helper/get-stylelint-path.js +15 -12
  19. package/helper/is-npm-or-yarn.js +12 -12
  20. package/helper/linter-bundle-config.cjs +70 -0
  21. package/helper/linter-bundle-config.d.ts +46 -0
  22. package/helper/linter-bundle-config.js +36 -0
  23. package/helper/run-process.js +14 -10
  24. package/lint.js +32 -26
  25. package/package.json +8 -7
  26. package/stylelint/index.cjs +1007 -0
  27. package/stylelint/plugins/stylelint-high-performance-animation.js +4 -5
  28. package/stylelint/plugins/stylelint-selector-no-empty.js +2 -2
  29. package/stylelint/plugins/stylelint-selector-tag-no-without-class.js +6 -7
  30. package/stylelint/plugins/stylelint-stylistic.js +7 -6
  31. package/stylelint.cjs +5 -0
  32. package/helper/config.js +0 -48
  33. package/helper/find-missing-overrides.js +0 -49
  34. package/stylelint/index.js +0 -1007
  35. package/types.d.ts +0 -43
  36. /package/eslint/{overrides-gatsby.js → overrides-gatsby.cjs} +0 -0
  37. /package/eslint/{overrides-jest.js → overrides-jest.cjs} +0 -0
  38. /package/eslint/{overrides-jsdoc.js → overrides-jsdoc.cjs} +0 -0
  39. /package/eslint/{overrides-storybook.js → overrides-storybook.cjs} +0 -0
  40. /package/eslint/{overrides-type-declarations.js → overrides-type-declarations.cjs} +0 -0
  41. /package/eslint/{overrides-worker.js → overrides-worker.cjs} +0 -0
@@ -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
- const fs = require('node:fs');
6
- const path = require('node:path');
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.accessSync(path.join(process.cwd(), 'package-lock.json'), fs.constants.R_OK);
19
+ const stat = await fs.stat(path.join(process.cwd(), 'package-lock.json'));
20
20
 
21
- npm = true;
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.accessSync(path.join(process.cwd(), 'yarn.lock'), fs.constants.R_OK);
28
+ const stat = await fs.stat(path.join(process.cwd(), 'yarn.lock'));
27
29
 
28
- yarn = true;
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
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @file Type definition of `.linter-bundle.js` configuration.
3
+ */
4
+
5
+ export interface LinterBundleConfig {
6
+ verbose?: boolean;
7
+ timing?: boolean;
8
+ git?: boolean;
9
+ tsc?: {
10
+ tsconfig?: string;
11
+ };
12
+ ts?: {
13
+ tsconfig?: string;
14
+ include?: string[];
15
+ exclude?: string[];
16
+ overrides?: {
17
+ /* eslint-disable @typescript-eslint/naming-convention -- `overrides` should match the original rule names */
18
+ general?: {
19
+ 'no-restricted-globals'?: { additionalRestrictions?: { name: string; message: string; }[]; };
20
+ 'no-restricted-properties'?: { additionalRestrictions?: { object: string; property: string; message: string; }[]; };
21
+ 'no-restricted-syntax'?: { additionalRestrictions?: { selector: string; message: string; }[]; };
22
+ 'import/order'?: { additionalExternalPatterns?: string[]; };
23
+ };
24
+ react?: {
25
+ 'react/forbid-component-props'?: { allowClassNameFor?: string[]; allowStyleFor?: string[]; };
26
+ };
27
+ /* eslint-enable @typescript-eslint/naming-convention */
28
+ };
29
+ };
30
+ sass?: {
31
+ patternPrefix?: string;
32
+ };
33
+ audit?: {
34
+ minSeverity?: 'info' | 'low' | 'moderate' | 'high' | 'critical';
35
+ exclude?: string[];
36
+ };
37
+ files?: {
38
+ restrictions: {
39
+ basePath: string;
40
+ allowed?: string[];
41
+ disallowed?: string[];
42
+ }[];
43
+ };
44
+ }
45
+
46
+ export const linterBundleConfig: LinterBundleConfig;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @file Returns the `.linter-bundle.js` configuration result.
3
+ */
4
+
5
+ import * as path from 'node:path';
6
+
7
+ export const linterBundleConfig = (
8
+ await loadConfig('.linter-bundle.json') ??
9
+ await loadConfig('.linter-bundle.cjs') ??
10
+ await loadConfig('.linter-bundle.mjs') ??
11
+ await loadConfig('.linter-bundle.js') ??
12
+ {}
13
+ );
14
+
15
+ /**
16
+ * Load a config file if it exist.
17
+ *
18
+ * @param {string} fileName - The name of the config file
19
+ * @returns {Promise<import('./linter-bundle-config.js').LinterBundleConfig | undefined>} - Either the file content for `undefined` if the file does not exist.
20
+ * */
21
+ async function loadConfig (fileName) {
22
+ const filePath = path.join('file://', process.cwd(), fileName);
23
+
24
+ try {
25
+ const config = await import(filePath);
26
+
27
+ if ('default' in config) {
28
+ return config.default;
29
+ }
30
+
31
+ return config;
32
+ }
33
+ catch {
34
+ return;
35
+ }
36
+ }
@@ -2,10 +2,10 @@
2
2
  * @file Executes a process asynchronously.
3
3
  */
4
4
 
5
- /** @typedef {{ code: number; stdout: string; stderr: string; runtime: number; }} ProcessResult */
5
+ import * as childProcess from 'node:child_process';
6
+ import * as os from 'node:os';
6
7
 
7
- const childProcess = require('node:child_process');
8
- const os = require('node:os');
8
+ /** @typedef {{ code: number; stdout: string; stderr: string; runtime: number; }} ProcessResult */
9
9
 
10
10
  /**
11
11
  * Executes a process asynchronously.
@@ -15,7 +15,7 @@ const os = require('node:os');
15
15
  * @param {childProcess.ExecOptions | undefined} [options] - The options of the `childProcess.exec()` method.
16
16
  * @returns {Promise<ProcessResult>} An object containing the result of the process execution
17
17
  */
18
- async function runProcess (command, options) {
18
+ export async function runProcess (command, options) {
19
19
  return new Promise((resolve) => {
20
20
  const startTimestamp = performance.now();
21
21
 
@@ -25,8 +25,16 @@ async function runProcess (command, options) {
25
25
  /** @type {string[]} */
26
26
  const stderr = [];
27
27
 
28
- // eslint-disable-next-line n/no-process-env -- We need to access `process.env`, because this is the default value if `env` is not set.
29
- const lintingProcess = childProcess.exec(command, { ...options, env: { ...process.env, ...options?.env, LINTER_BUNDLE: '1' }, shell: os.userInfo().shell });
28
+ const lintingProcess = childProcess.exec(command, {
29
+ ...options,
30
+ env: {
31
+ // eslint-disable-next-line n/no-process-env -- We need to access `process.env`, because this is the default value if `env` is not set.
32
+ ...process.env,
33
+ ...options?.env,
34
+ LINTER_BUNDLE: '1'
35
+ },
36
+ shell: os.userInfo().shell
37
+ });
30
38
 
31
39
  lintingProcess.stdout?.on('data', (/** @type {string} */data) => {
32
40
  stdout.push(data);
@@ -44,7 +52,3 @@ async function runProcess (command, options) {
44
52
  }));
45
53
  });
46
54
  }
47
-
48
- module.exports = {
49
- runProcess
50
- };
package/lint.js CHANGED
@@ -4,32 +4,37 @@
4
4
  * @file Entry point of the linter-bundle.
5
5
  */
6
6
 
7
- const path = require('node:path');
8
- const tty = require('node:tty');
7
+ import { createRequire } from 'node:module';
8
+ import * as path from 'node:path';
9
+ import * as tty from 'node:tty';
10
+ import { fileURLToPath } from 'node:url';
9
11
 
10
- const micromatch = require('micromatch');
12
+ import micromatch from 'micromatch';
11
13
 
12
- const config = require('./helper/config.js');
13
- const { findMissingOverrides } = require('./helper/find-missing-overrides.js');
14
- const { getGitFiles } = require('./helper/get-git-files.js');
15
- const { getStylelintPath } = require('./helper/get-stylelint-path.js');
16
- const { isNpmOrYarn } = require('./helper/is-npm-or-yarn.js');
17
- const { runProcess } = require('./helper/run-process.js');
18
- const { validatePackageOverrides } = require('./helper/validate-package-overrides.js');
14
+ import { getGitFiles } from './helper/get-git-files.js';
15
+ import { getOutdatedDependencies } from './helper/get-outdated-dependencies.js';
16
+ import { getOutdatedOverrides } from './helper/get-outdated-overrides.js';
17
+ import { getStylelintPath } from './helper/get-stylelint-path.js';
18
+ import { isNpmOrYarn } from './helper/is-npm-or-yarn.js';
19
+ import { linterBundleConfig } from './helper/linter-bundle-config.js';
20
+ import { runProcess } from './helper/run-process.js';
21
+
22
+ const require = createRequire(import.meta.url);
23
+ const dirname = path.dirname(fileURLToPath(import.meta.url));
19
24
 
20
25
  /** @typedef {'files' | 'tsc' | 'ts' | 'sass' | 'md' | 'audit'} TaskNames */
21
26
  /** @typedef {Partial<Record<string, (string | boolean)[]>>} TaskConfig */
22
- /** @typedef {import('./helper/run-process').ProcessResult} ProcessResult */
27
+ /** @typedef {import('./helper/run-process.js').ProcessResult} ProcessResult */
23
28
  /** @typedef {{ taskName: TaskNames; taskConfig: TaskConfig; }} TaskNameAndConfig */
24
29
  /** @typedef {TaskNameAndConfig & { command: string; options?: import('child_process').ExecOptions; }} TaskSetup */
25
30
  /** @typedef {{ jobTitle: string; taskSetup: TaskSetup; job: Promise<ProcessResult>; }} Job */
26
31
 
27
32
  const isTerminal = tty.isatty(1);
28
33
 
29
- const npmOrYarn = isNpmOrYarn();
34
+ const npmOrYarn = await isNpmOrYarn();
30
35
 
31
- void (async () => {
32
- if (!validateEnvironment()) {
36
+ await (async () => {
37
+ if (!await validateEnvironment()) {
33
38
  return;
34
39
  }
35
40
 
@@ -135,7 +140,7 @@ async function runFilesTask (taskName, taskConfig) {
135
140
  return runTask({
136
141
  taskName,
137
142
  taskConfig: newTaskConfig,
138
- command: `node "${path.resolve(__dirname, './files/index.js')}" ${includes}`
143
+ command: `node "${path.resolve(dirname, './files/index.js')}" ${includes}`
139
144
  });
140
145
  }
141
146
 
@@ -196,9 +201,9 @@ async function runESLintTask (taskName, taskConfig) {
196
201
  `"${path.join(path.dirname(require.resolve('eslint')), '../bin/eslint.js')}"`,
197
202
  includes,
198
203
  newTaskConfig.exclude?.map((exclude) => `--ignore-pattern ${exclude}`).join(' '),
199
- `--rulesdir "${path.resolve(__dirname, './eslint/rules/')}"`,
204
+ `--rulesdir "${path.resolve(dirname, './eslint/rules/')}"`,
200
205
  '--format unix',
201
- `--resolve-plugins-relative-to "${__dirname}"`
206
+ `--resolve-plugins-relative-to "${dirname}"`
202
207
  ].filter((argument) => Boolean(argument)).join(' '),
203
208
  taskConfig: newTaskConfig,
204
209
  options: {
@@ -232,7 +237,7 @@ async function runStylelintTask (taskName, taskConfig) {
232
237
  });
233
238
  }
234
239
 
235
- const stylelintBinPath = getStylelintPath();
240
+ const stylelintBinPath = await getStylelintPath();
236
241
 
237
242
  if (stylelintBinPath === null) {
238
243
  return generateDummyJobOutput(taskName, newTaskConfig, {
@@ -352,10 +357,10 @@ async function runAuditTask (taskName, taskConfig) {
352
357
  /**
353
358
  * Ensures that the environment in which the linter is running has the correct versions of the required dependencies.
354
359
  *
355
- * @returns {boolean} Returns `true` if the environment is valid, otherwise `false` is returned.
360
+ * @returns {Promise<boolean>} Returns `true` if the environment is valid, otherwise `false` is returned.
356
361
  */
357
- function validateEnvironment () {
358
- const outdatedOverrides = validatePackageOverrides();
362
+ async function validateEnvironment () {
363
+ const outdatedOverrides = await getOutdatedOverrides();
359
364
 
360
365
  if (outdatedOverrides.overrides.length > 0 || outdatedOverrides.resolutions.length > 0) {
361
366
  if (outdatedOverrides.overrides.length > 0) {
@@ -371,7 +376,8 @@ function validateEnvironment () {
371
376
  return false;
372
377
  }
373
378
 
374
- const missingOverrides = findMissingOverrides().filter(({ name }) => !(npmOrYarn === 'npm' && outdatedOverrides.overrides.some((override) => name === override.name)) && !(npmOrYarn === 'yarn' && outdatedOverrides.resolutions.some((override) => name === override.name)));
379
+ const outdatedDependencies = await getOutdatedDependencies();
380
+ const missingOverrides = outdatedDependencies.filter(({ name }) => !(npmOrYarn === 'npm' && outdatedOverrides.overrides.some((override) => name === override.name)) && !(npmOrYarn === 'yarn' && outdatedOverrides.resolutions.some((override) => name === override.name)));
375
381
 
376
382
  if (missingOverrides.length > 0) {
377
383
  let installCommand;
@@ -567,8 +573,8 @@ function getConfigValue (taskName, taskConfig, optionName) {
567
573
  return taskConfig[optionName];
568
574
  }
569
575
 
570
- if (taskName in config) {
571
- const specificConfig = config[/** @type {keyof typeof config} */(taskName)];
576
+ if (taskName in linterBundleConfig) {
577
+ const specificConfig = linterBundleConfig[/** @type {keyof typeof linterBundleConfig} */(taskName)];
572
578
 
573
579
  if (typeof specificConfig === 'object' && optionName in specificConfig) {
574
580
  // eslint-disable-next-line jsdoc/no-undefined-types -- False positive "The type 'specificConfig' is undefined."
@@ -583,8 +589,8 @@ function getConfigValue (taskName, taskConfig, optionName) {
583
589
  }
584
590
  }
585
591
 
586
- if (optionName in config) {
587
- const configValue = config[/** @type {keyof typeof config} */(optionName)];
592
+ if (optionName in linterBundleConfig) {
593
+ const configValue = linterBundleConfig[/** @type {keyof typeof linterBundleConfig} */(optionName)];
588
594
 
589
595
  if (Array.isArray(configValue)) {
590
596
  return configValue;
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "linter-bundle",
3
- "version": "4.0.3",
3
+ "version": "5.0.1",
4
+ "type": "module",
4
5
  "description": "Ready-to use bundle of linting tools, containing configurations for ESLint, stylelint and markdownlint.",
5
6
  "keywords": [
6
7
  "eslint",
@@ -32,12 +33,12 @@
32
33
  "publish:major": "npm version major",
33
34
  "publish:minor": "npm version minor",
34
35
  "publish:patch": "npm version patch",
35
- "lint": "npm run _validate-stylelint-options && npm run _stylelint-find-rules && node ./lint files tsc ts md audit --min-severity=critical",
36
+ "lint": "npm run _test-stylelint && npm run _stylelint-find-rules && node ./lint files tsc ts md audit --min-severity=critical",
36
37
  "preversion": "npm run check-outdated && npm run lint",
37
38
  "postversion": "git push && git push --tags && npm publish",
38
39
  "check-outdated": "npx --yes -- check-outdated --ignore-pre-releases",
39
- "_stylelint-find-rules": "stylelint-find-new-rules ./stylelint/index.js",
40
- "_validate-stylelint-options": "node ./validate-stylelint-options.mjs"
40
+ "_stylelint-find-rules": "stylelint-find-new-rules ./stylelint/index.cjs",
41
+ "_test-stylelint": "node ./test-stylelint.js"
41
42
  },
42
43
  "dependencies": {
43
44
  "@typescript-eslint/eslint-plugin": "6.5.0",
@@ -57,7 +58,7 @@
57
58
  "eslint-plugin-react": "7.33.2",
58
59
  "eslint-plugin-react-hooks": "4.6.0",
59
60
  "eslint-plugin-unicorn": "48.0.1",
60
- "markdownlint-cli": "0.35.0",
61
+ "markdownlint-cli": "0.36.0",
61
62
  "micromatch": "4.0.5",
62
63
  "postcss-scss": "4.0.7",
63
64
  "stylelint": "15.10.3",
@@ -73,8 +74,8 @@
73
74
  "devDependencies": {
74
75
  "@types/eslint": "8.44.2",
75
76
  "@types/micromatch": "4.0.2",
76
- "@types/node": "20.5.7",
77
- "stylelint-find-new-rules": "4.1.1",
77
+ "@types/node": "20.5.9",
78
+ "stylelint-find-new-rules": "4.1.2",
78
79
  "typescript": "5.2.2"
79
80
  }
80
81
  }