@mui/internal-code-infra 0.0.3-canary.4 → 0.0.3-canary.6
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/package.json +5 -4
- package/src/cli/babel.mjs +1 -12
- package/src/cli/cmdCopyFiles.mjs +8 -14
- package/src/cli/cmdExtractErrorCodes.mjs +41 -0
- package/src/cli/cmdJsonLint.mjs +16 -23
- package/src/cli/index.mjs +8 -5
- package/src/eslint/material-ui/config.mjs +4 -1
- package/src/untyped-plugins.d.ts +7 -0
- package/src/utils/build.mjs +79 -0
- package/src/utils/extractErrorCodes.mjs +168 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/internal-code-infra",
|
|
3
|
-
"version": "0.0.3-canary.
|
|
3
|
+
"version": "0.0.3-canary.6",
|
|
4
4
|
"description": "Infra scripts and configs to be used across MUI repos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@argos-ci/core": "^4.1.2",
|
|
25
25
|
"@babel/cli": "^7.28.3",
|
|
26
26
|
"@babel/core": "^7.28.4",
|
|
27
|
+
"@babel/plugin-syntax-jsx": "^7.27.1",
|
|
27
28
|
"@babel/plugin-syntax-typescript": "^7.27.1",
|
|
28
29
|
"@babel/plugin-transform-runtime": "^7.28.3",
|
|
29
30
|
"@babel/preset-env": "^7.28.3",
|
|
@@ -60,8 +61,8 @@
|
|
|
60
61
|
"typescript-eslint": "^8.42.0",
|
|
61
62
|
"yargs": "^18.0.0",
|
|
62
63
|
"@mui/internal-babel-plugin-display-name": "1.0.4-canary.7",
|
|
63
|
-
"@mui/internal-babel-plugin-
|
|
64
|
-
"@mui/internal-babel-plugin-
|
|
64
|
+
"@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.10",
|
|
65
|
+
"@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.20"
|
|
65
66
|
},
|
|
66
67
|
"peerDependencies": {
|
|
67
68
|
"eslint": "^9.0.0",
|
|
@@ -92,7 +93,7 @@
|
|
|
92
93
|
"publishConfig": {
|
|
93
94
|
"access": "public"
|
|
94
95
|
},
|
|
95
|
-
"gitSha": "
|
|
96
|
+
"gitSha": "b76c4e6f0a445cd70cf69bb4513802a108d24e2c",
|
|
96
97
|
"scripts": {
|
|
97
98
|
"typescript": "tsc -p tsconfig.json",
|
|
98
99
|
"test": "pnpm -w test --project @mui/internal-code-infra",
|
package/src/cli/babel.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { globby } from 'globby';
|
|
|
6
6
|
import * as fs from 'node:fs/promises';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import { $ } from 'execa';
|
|
9
|
+
import { BASE_IGNORES } from '../utils/build.mjs';
|
|
9
10
|
|
|
10
11
|
const TO_TRANSFORM_EXTENSIONS = ['.js', '.ts', '.tsx'];
|
|
11
12
|
|
|
@@ -64,18 +65,6 @@ export async function cjsCopy({ from, to }) {
|
|
|
64
65
|
* @property {string} [runtimeModule] - The runtime module to replace the errors with.
|
|
65
66
|
*/
|
|
66
67
|
|
|
67
|
-
const BASE_IGNORES = [
|
|
68
|
-
'**/*.test.js',
|
|
69
|
-
'**/*.test.ts',
|
|
70
|
-
'**/*.test.tsx',
|
|
71
|
-
'**/*.spec.js',
|
|
72
|
-
'**/*.spec.ts',
|
|
73
|
-
'**/*.spec.tsx',
|
|
74
|
-
'**/*.d.ts',
|
|
75
|
-
'**/*.test/*.*',
|
|
76
|
-
'**/test-cases/*.*',
|
|
77
|
-
];
|
|
78
|
-
|
|
79
68
|
/**
|
|
80
69
|
* @param {Object} options
|
|
81
70
|
* @param {boolean} [options.verbose=false] - Whether to enable verbose logging.
|
package/src/cli/cmdCopyFiles.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
|
|
|
2
2
|
import { globby } from 'globby';
|
|
3
3
|
import fs from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { mapConcurrently } from '../utils/build.mjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @typedef {Object} Args
|
|
@@ -105,20 +106,13 @@ async function processGlobs({ globs, cwd, silent = true, buildDir }) {
|
|
|
105
106
|
});
|
|
106
107
|
});
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// eslint-disable-next-line no-await-in-loop
|
|
116
|
-
await recursiveCopy({ source: file.sourcePath, target: file.targetPath, silent });
|
|
117
|
-
}
|
|
118
|
-
}),
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
await Promise.all(workers);
|
|
109
|
+
await mapConcurrently(
|
|
110
|
+
filesToProcess,
|
|
111
|
+
async (file) => {
|
|
112
|
+
await recursiveCopy({ source: file.sourcePath, target: file.targetPath, silent });
|
|
113
|
+
},
|
|
114
|
+
50,
|
|
115
|
+
);
|
|
122
116
|
return filesToProcess.length;
|
|
123
117
|
}
|
|
124
118
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import { markFn, measureFn } from '../utils/build.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {import('../utils/extractErrorCodes.mjs').Args} Args
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
10
|
+
command: 'extract-error-codes',
|
|
11
|
+
describe: 'Extracts error codes from package(s).',
|
|
12
|
+
builder(yargs) {
|
|
13
|
+
return yargs
|
|
14
|
+
.option('errorCodesPath', {
|
|
15
|
+
type: 'string',
|
|
16
|
+
describe: 'The output path to a json file to write the extracted error codes.',
|
|
17
|
+
demandOption: true,
|
|
18
|
+
})
|
|
19
|
+
.option('detection', {
|
|
20
|
+
type: 'string',
|
|
21
|
+
describe: 'The detection strategy to use when extracting error codes.',
|
|
22
|
+
choices: ['opt-in', 'opt-out'],
|
|
23
|
+
default: 'opt-in',
|
|
24
|
+
})
|
|
25
|
+
.option('skip', {
|
|
26
|
+
type: 'array',
|
|
27
|
+
describe: 'List of package names to skip.',
|
|
28
|
+
default: [],
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
async handler(args) {
|
|
32
|
+
const commandName = /** @type {string} */ (args._[0]);
|
|
33
|
+
await markFn(commandName, async () => {
|
|
34
|
+
const module = await import('../utils/extractErrorCodes.mjs');
|
|
35
|
+
await module.default(args);
|
|
36
|
+
});
|
|
37
|
+
console.log(
|
|
38
|
+
`✅ Extracted error codes in ${(measureFn(commandName).duration / 1000.0).toFixed(3)}s`,
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
});
|
package/src/cli/cmdJsonLint.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import fs from 'node:fs/promises';
|
|
5
5
|
import { globby } from 'globby';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import { mapConcurrently } from '../utils/build.mjs';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @typedef {Object} Args
|
|
@@ -42,33 +43,25 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
|
|
|
42
43
|
followSymbolicLinks: false,
|
|
43
44
|
});
|
|
44
45
|
|
|
45
|
-
const fileIterator = filenames[Symbol.iterator]();
|
|
46
|
-
const concurrency = Math.min(20, filenames.length);
|
|
47
46
|
let passed = true;
|
|
48
|
-
const workers = [];
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// eslint-disable-next-line no-console
|
|
60
|
-
console.log(passMessage(filename));
|
|
61
|
-
}
|
|
62
|
-
} catch (error) {
|
|
63
|
-
passed = false;
|
|
64
|
-
console.error(failMessage(`Error parsing ${filename}:\n\n${String(error)}`));
|
|
48
|
+
await mapConcurrently(
|
|
49
|
+
filenames,
|
|
50
|
+
async (filename) => {
|
|
51
|
+
const content = await fs.readFile(path.join(cwd, filename), { encoding: 'utf8' });
|
|
52
|
+
try {
|
|
53
|
+
JSON.parse(content);
|
|
54
|
+
if (!args.silent) {
|
|
55
|
+
// eslint-disable-next-line no-console
|
|
56
|
+
console.log(passMessage(filename));
|
|
65
57
|
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
passed = false;
|
|
60
|
+
console.error(failMessage(`Error parsing ${filename}:\n\n${String(error)}`));
|
|
66
61
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
await Promise.allSettled(workers);
|
|
62
|
+
},
|
|
63
|
+
20,
|
|
64
|
+
);
|
|
72
65
|
if (!passed) {
|
|
73
66
|
throw new Error('❌ At least one file did not pass. Check the console output');
|
|
74
67
|
}
|
package/src/cli/index.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { hideBin } from 'yargs/helpers';
|
|
|
5
5
|
import cmdArgosPush from './cmdArgosPush.mjs';
|
|
6
6
|
import cmdBuild from './cmdBuild.mjs';
|
|
7
7
|
import cmdCopyFiles from './cmdCopyFiles.mjs';
|
|
8
|
+
import cmdExtractErrorCodes from './cmdExtractErrorCodes.mjs';
|
|
8
9
|
import cmdJsonLint from './cmdJsonLint.mjs';
|
|
9
10
|
import cmdListWorkspaces from './cmdListWorkspaces.mjs';
|
|
10
11
|
import cmdPublish from './cmdPublish.mjs';
|
|
@@ -15,14 +16,16 @@ const pkgJson = createRequire(import.meta.url)('../../package.json');
|
|
|
15
16
|
|
|
16
17
|
yargs()
|
|
17
18
|
.scriptName('code-infra')
|
|
19
|
+
.usage('$0 <command> [args]')
|
|
20
|
+
.command(cmdArgosPush)
|
|
21
|
+
.command(cmdBuild)
|
|
22
|
+
.command(cmdCopyFiles)
|
|
23
|
+
.command(cmdExtractErrorCodes)
|
|
24
|
+
.command(cmdJsonLint)
|
|
25
|
+
.command(cmdListWorkspaces)
|
|
18
26
|
.command(cmdPublish)
|
|
19
27
|
.command(cmdPublishCanary)
|
|
20
|
-
.command(cmdListWorkspaces)
|
|
21
|
-
.command(cmdJsonLint)
|
|
22
|
-
.command(cmdArgosPush)
|
|
23
28
|
.command(cmdSetVersionOverrides)
|
|
24
|
-
.command(cmdCopyFiles)
|
|
25
|
-
.command(cmdBuild)
|
|
26
29
|
.demandCommand(1, 'You need at least one command before moving on')
|
|
27
30
|
.strict()
|
|
28
31
|
.help()
|
|
@@ -411,7 +411,10 @@ export function createCoreConfig(options = {}) {
|
|
|
411
411
|
'material-ui/no-styled-box': 'error',
|
|
412
412
|
'material-ui/straight-quotes': 'off',
|
|
413
413
|
|
|
414
|
-
'react-hooks/exhaustive-deps': [
|
|
414
|
+
'react-hooks/exhaustive-deps': [
|
|
415
|
+
'error',
|
|
416
|
+
{ additionalHooks: '(useEnhancedEffect|useIsoLayoutEffect)' },
|
|
417
|
+
],
|
|
415
418
|
'react-hooks/rules-of-hooks': 'error',
|
|
416
419
|
|
|
417
420
|
'react/default-props-match-prop-types': [
|
package/src/untyped-plugins.d.ts
CHANGED
|
@@ -102,6 +102,13 @@ declare module '@babel/plugin-transform-runtime' {
|
|
|
102
102
|
export default plugin;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
declare module '@babel/plugin-syntax-jsx' {
|
|
106
|
+
import type { PluginItem } from '@babel/core';
|
|
107
|
+
|
|
108
|
+
declare const plugin: PluginItem;
|
|
109
|
+
export default plugin;
|
|
110
|
+
}
|
|
111
|
+
|
|
105
112
|
declare module '@babel/plugin-syntax-typescript' {
|
|
106
113
|
import type { PluginItem } from '@babel/core';
|
|
107
114
|
|
package/src/utils/build.mjs
CHANGED
|
@@ -68,3 +68,82 @@ export function validatePkgJson(packageJson, options = {}) {
|
|
|
68
68
|
throw error;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Marks the start and end of a function execution for performance measurement.
|
|
74
|
+
* Uses the Performance API to create marks and measure the duration.
|
|
75
|
+
* @function
|
|
76
|
+
* @template {() => Promise<any>} F
|
|
77
|
+
* @param {string} label
|
|
78
|
+
* @param {() => ReturnType<F>} fn
|
|
79
|
+
* @returns {Promise<ReturnType<F>>}
|
|
80
|
+
*/
|
|
81
|
+
export async function markFn(label, fn) {
|
|
82
|
+
const startMark = `${label}-start`;
|
|
83
|
+
const endMark = `${label}-end`;
|
|
84
|
+
performance.mark(startMark);
|
|
85
|
+
const result = await fn();
|
|
86
|
+
performance.mark(endMark);
|
|
87
|
+
performance.measure(label, startMark, endMark);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {string} label
|
|
93
|
+
*/
|
|
94
|
+
export function measureFn(label) {
|
|
95
|
+
const startMark = `${label}-start`;
|
|
96
|
+
const endMark = `${label}-end`;
|
|
97
|
+
return performance.measure(label, startMark, endMark);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const BASE_IGNORES = [
|
|
101
|
+
'**/*.test.js',
|
|
102
|
+
'**/*.test.ts',
|
|
103
|
+
'**/*.test.tsx',
|
|
104
|
+
'**/*.spec.js',
|
|
105
|
+
'**/*.spec.ts',
|
|
106
|
+
'**/*.spec.tsx',
|
|
107
|
+
'**/*.d.ts',
|
|
108
|
+
'**/*.test/*.*',
|
|
109
|
+
'**/test-cases/*.*',
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* A utility to map a function over an array of items in a worker pool.
|
|
114
|
+
*
|
|
115
|
+
* This function will create a pool of workers and distribute the items to be processed among them.
|
|
116
|
+
* Each worker will process items sequentially, but multiple workers will run in parallel.
|
|
117
|
+
*
|
|
118
|
+
* @function
|
|
119
|
+
* @template T
|
|
120
|
+
* @template R
|
|
121
|
+
* @param {T[]} items
|
|
122
|
+
* @param {(item: T) => Promise<R>} mapper
|
|
123
|
+
* @param {number} concurrency
|
|
124
|
+
* @returns {Promise<(R|Error)[]>}
|
|
125
|
+
*/
|
|
126
|
+
export async function mapConcurrently(items, mapper, concurrency) {
|
|
127
|
+
if (!items.length) {
|
|
128
|
+
return Promise.resolve([]); // nothing to do
|
|
129
|
+
}
|
|
130
|
+
const itemIterator = items.entries();
|
|
131
|
+
const count = Math.min(concurrency, items.length);
|
|
132
|
+
const workers = [];
|
|
133
|
+
/**
|
|
134
|
+
* @type {(R|Error)[]}
|
|
135
|
+
*/
|
|
136
|
+
const results = new Array(items.length);
|
|
137
|
+
for (let i = 0; i < count; i += 1) {
|
|
138
|
+
const worker = Promise.resolve().then(async () => {
|
|
139
|
+
for (const [index, item] of itemIterator) {
|
|
140
|
+
// eslint-disable-next-line no-await-in-loop
|
|
141
|
+
const res = await mapper(item);
|
|
142
|
+
results[index] = res;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
workers.push(worker);
|
|
146
|
+
}
|
|
147
|
+
await Promise.all(workers);
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { types as babelTypes, parseAsync, traverse } from '@babel/core';
|
|
3
|
+
import babelSyntaxJsx from '@babel/plugin-syntax-jsx';
|
|
4
|
+
import babelSyntaxTypescript from '@babel/plugin-syntax-typescript';
|
|
5
|
+
import { findMessageNode } from '@mui/internal-babel-plugin-minify-errors';
|
|
6
|
+
import { globby } from 'globby';
|
|
7
|
+
import * as fs from 'node:fs/promises';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
|
|
10
|
+
import { getWorkspacePackages } from '../cli/pnpm.mjs';
|
|
11
|
+
import { BASE_IGNORES, mapConcurrently } from './build.mjs';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} Args
|
|
15
|
+
* @property {string} errorCodesPath - The output path to write the extracted error codes.
|
|
16
|
+
* @property {string[]} [skip=[]] - List of package names to skip. By default, all workspace packages are considered.
|
|
17
|
+
* @property {import('@mui/internal-babel-plugin-minify-errors').Options['detection']} [detection='opt-in'] - The detection strategy to use when extracting error codes.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Gets all relevant files for a package to parse.
|
|
22
|
+
*
|
|
23
|
+
* @param {import('../cli/pnpm.mjs').PublicPackage} pkg
|
|
24
|
+
* @returns {Promise<string[]>} An array of file paths.
|
|
25
|
+
*/
|
|
26
|
+
async function getFilesForPackage(pkg) {
|
|
27
|
+
const srcPath = path.join(pkg.path, 'src');
|
|
28
|
+
const srcPathExists = await fs
|
|
29
|
+
.stat(srcPath)
|
|
30
|
+
.then((stat) => stat.isDirectory())
|
|
31
|
+
.catch(() => false);
|
|
32
|
+
// Implementation to extract error codes from all files in the directory
|
|
33
|
+
const cwd = srcPathExists ? srcPath : pkg.path;
|
|
34
|
+
const files = await globby('**/*.{js,ts,jsx,tsx,cjs,mjs,cts}', {
|
|
35
|
+
ignore: [
|
|
36
|
+
'**/node_modules/**',
|
|
37
|
+
'**/dist/**',
|
|
38
|
+
'**/build/**',
|
|
39
|
+
'scripts',
|
|
40
|
+
'**/__{tests,fixtures,mock,mocks}__/**',
|
|
41
|
+
...BASE_IGNORES,
|
|
42
|
+
],
|
|
43
|
+
cwd,
|
|
44
|
+
});
|
|
45
|
+
return files.map((file) => path.join(cwd, file));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Extracts error codes from all files in a directory.
|
|
50
|
+
* @param {string[]} files
|
|
51
|
+
* @param {Set<string>} errors
|
|
52
|
+
* @param {import('@mui/internal-babel-plugin-minify-errors').Options['detection']} [detection='opt-in']
|
|
53
|
+
*/
|
|
54
|
+
async function extractErrorCodesForWorkspace(files, errors, detection = 'opt-in') {
|
|
55
|
+
await mapConcurrently(
|
|
56
|
+
files,
|
|
57
|
+
async (fullPath) => {
|
|
58
|
+
const code = await fs.readFile(fullPath, 'utf8');
|
|
59
|
+
const ast = await parseAsync(code, {
|
|
60
|
+
filename: fullPath,
|
|
61
|
+
sourceType: 'module',
|
|
62
|
+
plugins: [[babelSyntaxTypescript, { isTSX: true }], [babelSyntaxJsx]],
|
|
63
|
+
configFile: false,
|
|
64
|
+
babelrc: false,
|
|
65
|
+
browserslistConfigFile: false,
|
|
66
|
+
code: false,
|
|
67
|
+
});
|
|
68
|
+
if (!ast) {
|
|
69
|
+
throw new Error(`Failed to parse ${fullPath}`);
|
|
70
|
+
}
|
|
71
|
+
traverse(ast, {
|
|
72
|
+
NewExpression(newExpressionPath) {
|
|
73
|
+
const { message } =
|
|
74
|
+
findMessageNode(babelTypes, newExpressionPath, {
|
|
75
|
+
detection,
|
|
76
|
+
missingError: 'annotate',
|
|
77
|
+
}) ?? {};
|
|
78
|
+
if (message) {
|
|
79
|
+
errors.add(message.message);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
30,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Extracts error codes from all workspace packages.
|
|
90
|
+
* @param {Args} args
|
|
91
|
+
*/
|
|
92
|
+
export default async function extractErrorCodes(args) {
|
|
93
|
+
/**
|
|
94
|
+
* @type {Set<string>}
|
|
95
|
+
*/
|
|
96
|
+
const errors = new Set();
|
|
97
|
+
|
|
98
|
+
// Find relevant files
|
|
99
|
+
|
|
100
|
+
const basePackages = await getWorkspacePackages({
|
|
101
|
+
publicOnly: true,
|
|
102
|
+
});
|
|
103
|
+
const { skip: skipPackages = [], errorCodesPath, detection = 'opt-in' } = args;
|
|
104
|
+
const packages = basePackages.filter(
|
|
105
|
+
(pkg) =>
|
|
106
|
+
// Ignore obvious packages that do not have user-facing errors
|
|
107
|
+
!pkg.name.startsWith('@mui/internal-') &&
|
|
108
|
+
!pkg.name.startsWith('@mui-internal/') &&
|
|
109
|
+
!skipPackages.includes(pkg.name),
|
|
110
|
+
);
|
|
111
|
+
const files = await Promise.all(packages.map((pkg) => getFilesForPackage(pkg)));
|
|
112
|
+
packages.forEach((pkg, index) => {
|
|
113
|
+
console.log(
|
|
114
|
+
`🔍 ${pkg.name}: Found ${files[index].length} file${files[index].length > 1 ? 's' : ''}`,
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Extract error codes from said files.
|
|
119
|
+
const filesToProcess = files.flat();
|
|
120
|
+
console.log(`🔍 Extracting error codes from ${filesToProcess.length} files...`);
|
|
121
|
+
await extractErrorCodesForWorkspace(filesToProcess, errors, detection);
|
|
122
|
+
|
|
123
|
+
// Write error codes to the specified file.
|
|
124
|
+
const errorCodeFilePath = path.resolve(errorCodesPath);
|
|
125
|
+
const fileExists = await fs
|
|
126
|
+
.stat(errorCodeFilePath)
|
|
127
|
+
.then((stat) => stat.isFile())
|
|
128
|
+
.catch((ex) => {
|
|
129
|
+
if (ex.code === 'ENOENT') {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return new Error(ex.message);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (fileExists instanceof Error) {
|
|
136
|
+
throw fileExists;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* @type {Record<string, string>}
|
|
140
|
+
*/
|
|
141
|
+
const existingErrorCodes =
|
|
142
|
+
fileExists === true ? JSON.parse(await fs.readFile(errorCodeFilePath, 'utf-8')) : {};
|
|
143
|
+
const inverseLookupCode = new Map(
|
|
144
|
+
Object.entries(existingErrorCodes).map(([key, value]) => [value, Number(key)]),
|
|
145
|
+
);
|
|
146
|
+
const originalErrorCount = inverseLookupCode.size;
|
|
147
|
+
Array.from(errors).forEach((error) => {
|
|
148
|
+
if (!inverseLookupCode.has(error)) {
|
|
149
|
+
inverseLookupCode.set(error, inverseLookupCode.size + 1);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const finalErrorCodes = Array.from(inverseLookupCode.entries()).reduce((acc, [message, code]) => {
|
|
153
|
+
acc[code] = message;
|
|
154
|
+
return acc;
|
|
155
|
+
}, /** @type {Record<string, string>} */ ({}));
|
|
156
|
+
if (!fileExists) {
|
|
157
|
+
await fs.mkdir(path.dirname(errorCodeFilePath), { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
const newErrorCount = inverseLookupCode.size - originalErrorCount;
|
|
160
|
+
if (newErrorCount === 0) {
|
|
161
|
+
console.log(`✅ No new error codes found.`);
|
|
162
|
+
} else {
|
|
163
|
+
console.log(
|
|
164
|
+
`📝 Wrote ${newErrorCount} new error code${newErrorCount > 1 ? 's' : ''} to "${errorCodesPath}"`,
|
|
165
|
+
);
|
|
166
|
+
await fs.writeFile(errorCodeFilePath, `${JSON.stringify(finalErrorCodes, null, 2)}\n`);
|
|
167
|
+
}
|
|
168
|
+
}
|