@mui/internal-code-infra 0.0.3-canary.2 → 0.0.3-canary.20

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/internal-code-infra",
3
- "version": "0.0.3-canary.2",
3
+ "version": "0.0.3-canary.20",
4
4
  "description": "Infra scripts and configs to be used across MUI repos.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "sideEffects": false,
13
13
  "exports": {
14
+ "./stylelint": "./src/stylelint/index.mjs",
14
15
  "./package.json": "./package.json",
15
16
  "./prettier": "./src/prettier.mjs",
16
17
  "./eslint": "./src/eslint/index.mjs",
@@ -21,25 +22,26 @@
21
22
  "code-infra": "./bin/code-infra.mjs"
22
23
  },
23
24
  "dependencies": {
24
- "@argos-ci/core": "^4.1.0",
25
+ "@argos-ci/core": "^4.1.6",
25
26
  "@babel/cli": "^7.28.3",
26
- "@babel/core": "^7.28.3",
27
+ "@babel/core": "^7.28.4",
28
+ "@babel/plugin-syntax-jsx": "^7.27.1",
27
29
  "@babel/plugin-syntax-typescript": "^7.27.1",
28
30
  "@babel/plugin-transform-runtime": "^7.28.3",
29
31
  "@babel/preset-env": "^7.28.3",
30
32
  "@babel/preset-react": "^7.27.1",
31
33
  "@babel/preset-typescript": "^7.27.1",
32
34
  "@eslint/compat": "^1.3.2",
33
- "@eslint/js": "^9.34.0",
34
- "@next/eslint-plugin-next": "^15.5.0",
35
+ "@eslint/js": "^9.36.0",
36
+ "@next/eslint-plugin-next": "^15.5.3",
35
37
  "@octokit/auth-action": "^6.0.1",
36
38
  "@octokit/rest": "^22.0.0",
37
- "@pnpm/find-workspace-dir": "^1000.1.2",
39
+ "@pnpm/find-workspace-dir": "^1000.1.3",
38
40
  "babel-plugin-optimize-clsx": "^2.6.2",
39
41
  "babel-plugin-transform-inline-environment-variables": "^0.4.4",
40
42
  "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
41
- "babel-plugin-transform-remove-imports": "^1.8.0",
42
- "chalk": "^5.6.0",
43
+ "babel-plugin-transform-remove-imports": "^1.8.1",
44
+ "chalk": "^5.6.2",
43
45
  "eslint-config-prettier": "^10.1.8",
44
46
  "eslint-import-resolver-typescript": "^4.4.4",
45
47
  "eslint-module-utils": "^2.12.1",
@@ -49,19 +51,22 @@
49
51
  "eslint-plugin-react": "^7.37.5",
50
52
  "eslint-plugin-react-compiler": "^19.1.0-rc.2",
51
53
  "eslint-plugin-react-hooks": "^6.0.0-rc1",
52
- "eslint-plugin-testing-library": "^7.6.6",
54
+ "eslint-plugin-testing-library": "^7.10.0",
53
55
  "execa": "^9.6.0",
54
56
  "git-url-parse": "^16.1.0",
55
- "globals": "^16.3.0",
56
- "globby": "^14.1.0",
57
- "lodash-es": "^4.17.21",
57
+ "globals": "^16.4.0",
58
+ "globby": "^15.0.0",
58
59
  "minimatch": "^10.0.3",
60
+ "postcss-styled-syntax": "^0.7.1",
61
+ "regexp.escape": "^2.0.1",
62
+ "resolve-pkg-maps": "^1.0.0",
59
63
  "semver": "^7.7.2",
60
- "typescript-eslint": "^8.40.0",
64
+ "stylelint-config-standard": "^39.0.0",
65
+ "typescript-eslint": "^8.44.1",
61
66
  "yargs": "^18.0.0",
62
- "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.7",
63
- "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.19",
64
- "@mui/internal-babel-plugin-display-name": "1.0.4-canary.6"
67
+ "@mui/internal-babel-plugin-resolve-imports": "2.0.7-canary.23",
68
+ "@mui/internal-babel-plugin-display-name": "1.0.4-canary.7",
69
+ "@mui/internal-babel-plugin-minify-errors": "2.0.8-canary.10"
65
70
  },
66
71
  "peerDependencies": {
67
72
  "eslint": "^9.0.0",
@@ -74,13 +79,13 @@
74
79
  "@types/eslint-plugin-jsx-a11y": "^6.10.0",
75
80
  "@types/estree": "^1.0.8",
76
81
  "@types/estree-jsx": "^1.0.5",
77
- "@types/lodash-es": "^4.17.12",
82
+ "@types/regexp.escape": "^2.0.0",
78
83
  "@types/yargs": "^17.0.33",
79
- "@typescript-eslint/parser": "^8.40.0",
80
- "@typescript-eslint/rule-tester": "^8.40.0",
81
- "eslint": "^9.34.0",
84
+ "@typescript-eslint/parser": "^8.44.1",
85
+ "@typescript-eslint/rule-tester": "^8.44.1",
86
+ "eslint": "^9.36.0",
82
87
  "prettier": "^3.6.2",
83
- "typescript-eslint": "^8.40.0"
88
+ "typescript-eslint": "^8.44.1"
84
89
  },
85
90
  "files": [
86
91
  "bin",
@@ -92,7 +97,7 @@
92
97
  "publishConfig": {
93
98
  "access": "public"
94
99
  },
95
- "gitSha": "545a2df5000e8d2297f9cf65db4ae578eb29dad3",
100
+ "gitSha": "8ad4cd1aec96d0977bb91fd233e4fd7cd3ebd298",
96
101
  "scripts": {
97
102
  "typescript": "tsc -p tsconfig.json",
98
103
  "test": "pnpm -w test --project @mui/internal-code-infra",
@@ -13,7 +13,7 @@ import pluginRemovePropTypes from 'babel-plugin-transform-react-remove-prop-type
13
13
  * @param {boolean} [param0.debug]
14
14
  * @param {boolean} [param0.optimizeClsx]
15
15
  * @param {boolean} [param0.removePropTypes]
16
- * @param {boolean} [param0.isTest]
16
+ * @param {boolean} [param0.noResolveImports]
17
17
  * @param {'cjs' | 'esm'} param0.bundle
18
18
  * @param {string | null} param0.outExtension - Specify the output file extension.
19
19
  * @param {string} param0.runtimeVersion
@@ -23,7 +23,7 @@ export function getBaseConfig({
23
23
  debug = false,
24
24
  optimizeClsx = false,
25
25
  removePropTypes = false,
26
- isTest = false,
26
+ noResolveImports = false,
27
27
  bundle,
28
28
  runtimeVersion,
29
29
  outExtension,
@@ -81,7 +81,7 @@ export function getBaseConfig({
81
81
  plugins.push([pluginOptimizeClsx, {}, 'babel-plugin-optimize-clsx']);
82
82
  }
83
83
 
84
- if (bundle === 'esm' && !isTest) {
84
+ if (bundle === 'esm' && !noResolveImports) {
85
85
  plugins.push([
86
86
  pluginResolveImports,
87
87
  { outExtension },
@@ -119,20 +119,39 @@ export function getBaseConfig({
119
119
  }
120
120
 
121
121
  /**
122
- * @type {import('@babel/core').ConfigFunction}
122
+ * @typedef {Object} Options
123
+ * @prop {'esm' | 'cjs'} [Options.bundle]
124
+ * @prop {boolean} [Options.noResolveImports]
125
+ * @prop {undefined} [options.env]
126
+ */
127
+
128
+ /**
129
+ * @param {import('@babel/core').ConfigAPI | Options} api
130
+ * @returns {import('@babel/core').TransformOptions}
123
131
  */
124
132
  export default function getBabelConfig(api) {
125
- const isStable = api.env(['regressions', 'stable']);
126
- const isTest = api.env('test') || process.env.NODE_ENV === 'test';
133
+ /** @type {'esm' | 'cjs'} */
134
+ let bundle;
135
+ /** @type {boolean} */
136
+ let noResolveImports;
137
+
138
+ if (api.env) {
139
+ // legacy
140
+ bundle = api.env(['regressions', 'stable']) ? 'esm' : 'cjs';
141
+ noResolveImports = api.env('test') || process.env.NODE_ENV === 'test';
142
+ } else {
143
+ bundle = api.bundle || 'esm';
144
+ noResolveImports = api.noResolveImports || false;
145
+ }
127
146
 
128
147
  return getBaseConfig({
129
148
  debug: process.env.MUI_BUILD_VERBOSE === 'true',
130
- bundle: isStable ? 'esm' : 'cjs',
149
+ bundle,
131
150
  outExtension: process.env.MUI_OUT_FILE_EXTENSION || null,
132
151
  // any package needs to declare 7.25.0 as a runtime dependency. default is ^7.0.0
133
152
  runtimeVersion: process.env.MUI_BABEL_RUNTIME_VERSION || '^7.25.0',
134
153
  optimizeClsx: process.env.MUI_OPTIMIZE_CLSX === 'true',
135
154
  removePropTypes: process.env.MUI_REMOVE_PROP_TYPES === 'true',
136
- isTest,
155
+ noResolveImports,
137
156
  });
138
157
  }
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.
@@ -1,9 +1,12 @@
1
1
  /* eslint-disable no-console */
2
+ import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
2
3
  import { $ } from 'execa';
3
- import set from 'lodash-es/set.js';
4
+ import { globby } from 'globby';
4
5
  import * as fs from 'node:fs/promises';
5
6
  import * as path from 'node:path';
6
- import { getOutExtension, isMjsBuild, validatePkgJson } from '../utils/build.mjs';
7
+ import { sep as posixSep } from 'node:path/posix';
8
+
9
+ import { getOutExtension, isMjsBuild, mapConcurrently, validatePkgJson } from '../utils/build.mjs';
7
10
 
8
11
  /**
9
12
  * @typedef {Object} Args
@@ -18,6 +21,7 @@ import { getOutExtension, isMjsBuild, validatePkgJson } from '../utils/build.mjs
18
21
  * @property {boolean} skipPackageJson - Whether to skip generating the package.json file in the bundle output.
19
22
  * @property {boolean} skipMainCheck - Whether to skip checking for main field in package.json.
20
23
  * @property {string[]} ignore - Globs to be ignored by Babel.
24
+ * @property {string[]} [copy] - Files/Directories to be copied. Can be a glob pattern.
21
25
  */
22
26
 
23
27
  const validBundles = [
@@ -64,16 +68,16 @@ ${content}`,
64
68
 
65
69
  /**
66
70
  * @param {Object} param0
67
- * @param {string | Record<string, string>} param0.importPath
71
+ * @param {NonNullable<import('./packageJson').PackageJson.Exports>} param0.importPath
68
72
  * @param {string} param0.key
69
73
  * @param {string} param0.cwd
70
74
  * @param {string} param0.dir
71
75
  * @param {string} param0.type
72
- * @param {Object} param0.newExports
76
+ * @param {import('./packageJson').PackageJson.ExportConditions} param0.newExports
73
77
  * @param {string} param0.typeOutExtension
74
78
  * @param {string} param0.outExtension
75
79
  * @param {boolean} param0.addTypes
76
- * @returns {Promise<{path: string[], importPath: string | Record<string, string | undefined>}>}
80
+ * @returns {Promise<void>}
77
81
  */
78
82
  async function createExportsFor({
79
83
  importPath,
@@ -86,10 +90,22 @@ async function createExportsFor({
86
90
  outExtension,
87
91
  addTypes,
88
92
  }) {
93
+ if (Array.isArray(importPath)) {
94
+ throw new Error(
95
+ `Array form of package.json exports is not supported yet. Found in export "${key}".`,
96
+ );
97
+ }
98
+
89
99
  let srcPath = typeof importPath === 'string' ? importPath : importPath['mui-src'];
90
100
  const rest = typeof importPath === 'string' ? {} : { ...importPath };
91
101
  delete rest['mui-src'];
92
102
 
103
+ if (typeof srcPath !== 'string') {
104
+ throw new Error(
105
+ `Unsupported export for "${key}". Only a string or an object with "mui-src" field is supported for now.`,
106
+ );
107
+ }
108
+
93
109
  const exportFileExists = srcPath.includes('*')
94
110
  ? true
95
111
  : await fs.stat(path.join(cwd, srcPath)).then(
@@ -105,25 +121,25 @@ async function createExportsFor({
105
121
  const ext = path.extname(srcPath);
106
122
 
107
123
  if (ext === '.css') {
108
- set(newExports, [key], srcPath);
109
- return {
110
- path: [key],
111
- importPath: srcPath,
112
- };
124
+ newExports[key] = srcPath;
125
+ return;
113
126
  }
114
- return {
115
- path: [key, type === 'cjs' ? 'require' : 'import'],
116
- importPath: {
117
- ...rest,
118
- types: addTypes ? srcPath.replace(ext, typeOutExtension) : undefined,
119
- default: srcPath.replace(ext, outExtension),
120
- },
127
+
128
+ if (typeof newExports[key] === 'string' || Array.isArray(newExports[key])) {
129
+ throw new Error(`The export "${key}" is already defined as a string or Array.`);
130
+ }
131
+
132
+ newExports[key] ??= {};
133
+ newExports[key][type === 'cjs' ? 'require' : 'import'] = {
134
+ ...rest,
135
+ ...(addTypes ? { types: srcPath.replace(ext, typeOutExtension) } : {}),
136
+ default: srcPath.replace(ext, outExtension),
121
137
  };
122
138
  }
123
139
 
124
140
  /**
125
141
  * @param {Object} param0
126
- * @param {any} param0.packageJson - The package.json content.
142
+ * @param {import('./packageJson').PackageJson} param0.packageJson - The package.json content.
127
143
  * @param {{type: import('../utils/build.mjs').BundleType; dir: string}[]} param0.bundles
128
144
  * @param {string} param0.outputDir
129
145
  * @param {string} param0.cwd
@@ -138,12 +154,15 @@ async function writePackageJson({ packageJson, bundles, outputDir, cwd, addTypes
138
154
  packageJson.type = packageJson.type || 'commonjs';
139
155
 
140
156
  /**
141
- * @type {Record<string, string | Record<string, string> | null>}
157
+ * @type {import('./packageJson').PackageJson.ExportConditions}
142
158
  */
143
- const originalExports = packageJson.exports || {};
159
+ const originalExports =
160
+ typeof packageJson.exports === 'string' || Array.isArray(packageJson.exports)
161
+ ? { '.': packageJson.exports }
162
+ : packageJson.exports || {};
144
163
  delete packageJson.exports;
145
164
  /**
146
- * @type {Record<string, string | Record<string, string> | null>}
165
+ * @type {import('./packageJson').PackageJson.ExportConditions}
147
166
  */
148
167
  const newExports = {
149
168
  './package.json': './package.json',
@@ -172,10 +191,16 @@ async function writePackageJson({ packageJson, bundles, outputDir, cwd, addTypes
172
191
  if (type === 'cjs') {
173
192
  packageJson.main = exportDir;
174
193
  }
175
- set(newExports, ['.', type === 'cjs' ? 'require' : 'import'], {
176
- types: typeFileExists ? typeExportDir : undefined,
194
+
195
+ if (typeof newExports['.'] === 'string' || Array.isArray(newExports['.'])) {
196
+ throw new Error(`The export "." is already defined as a string or Array.`);
197
+ }
198
+
199
+ newExports['.'] ??= {};
200
+ newExports['.'][type === 'cjs' ? 'require' : 'import'] = {
201
+ ...(typeFileExists ? { types: typeExportDir } : {}),
177
202
  default: exportDir,
178
- });
203
+ };
179
204
  }
180
205
  if (typeFileExists && type === 'cjs') {
181
206
  packageJson.types = typeExportDir;
@@ -185,11 +210,11 @@ async function writePackageJson({ packageJson, bundles, outputDir, cwd, addTypes
185
210
  for (const key of exportKeys) {
186
211
  const importPath = originalExports[key];
187
212
  if (!importPath) {
188
- set(newExports, [key], null);
189
- return;
213
+ newExports[key] = null;
214
+ continue;
190
215
  }
191
216
  // eslint-disable-next-line no-await-in-loop
192
- const res = await createExportsFor({
217
+ await createExportsFor({
193
218
  importPath,
194
219
  key,
195
220
  cwd,
@@ -200,7 +225,6 @@ async function writePackageJson({ packageJson, bundles, outputDir, cwd, addTypes
200
225
  outExtension,
201
226
  addTypes,
202
227
  });
203
- set(newExports, res.path, res.importPath);
204
228
  }
205
229
  }),
206
230
  );
@@ -213,6 +237,11 @@ async function writePackageJson({ packageJson, bundles, outputDir, cwd, addTypes
213
237
  // default condition should come last
214
238
  Object.keys(newExports).forEach((key) => {
215
239
  const exportVal = newExports[key];
240
+ if (Array.isArray(exportVal)) {
241
+ throw new Error(
242
+ `Array form of package.json exports is not supported yet. Found in export "${key}".`,
243
+ );
244
+ }
216
245
  if (exportVal && typeof exportVal === 'object' && (exportVal.import || exportVal.require)) {
217
246
  const defaultExport = exportVal.import || exportVal.require;
218
247
  if (exportVal.import) {
@@ -298,6 +327,13 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
298
327
  type: 'boolean',
299
328
  default: false,
300
329
  description: 'Skip checking for main field in package.json.',
330
+ })
331
+ .option('copy', {
332
+ type: 'string',
333
+ array: true,
334
+ description:
335
+ 'Files/Directories to be copied to the output directory. Can be a glob pattern.',
336
+ default: [],
301
337
  });
302
338
  },
303
339
  async handler(args) {
@@ -442,5 +478,144 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
442
478
  outputDir: buildDir,
443
479
  addTypes: buildTypes,
444
480
  });
481
+
482
+ await copyHandler({
483
+ cwd,
484
+ globs: args.copy ?? [],
485
+ buildDir,
486
+ verbose: args.verbose,
487
+ });
445
488
  },
446
489
  });
490
+
491
+ /**
492
+ * @param {Object} param0
493
+ * @param {string} param0.cwd - The current working directory.
494
+ * @param {string[]} [param0.globs=[]] - Extra files to copy, can be specified as `source:target` pairs or just `source`.
495
+ * @param {string} param0.buildDir - The build directory to copy to.
496
+ * @param {boolean} [param0.verbose=false] - Whether to suppress output.
497
+ * @returns {Promise<void>}
498
+ */
499
+ async function copyHandler({ cwd, globs = [], buildDir, verbose = false }) {
500
+ /**
501
+ * @type {(string|{targetPath: string; sourcePath: string})[]}
502
+ */
503
+ const defaultFiles = [];
504
+ const workspaceDir = await findWorkspaceDir(cwd);
505
+ if (!workspaceDir) {
506
+ throw new Error('Workspace directory not found');
507
+ }
508
+
509
+ const localOrRootFiles = [
510
+ [path.join(cwd, 'README.md'), path.join(workspaceDir, 'README.md')],
511
+ [path.join(cwd, 'LICENSE'), path.join(workspaceDir, 'LICENSE')],
512
+ [path.join(cwd, 'CHANGELOG.md'), path.join(workspaceDir, 'CHANGELOG.md')],
513
+ ];
514
+ await Promise.all(
515
+ localOrRootFiles.map(async (filesToCopy) => {
516
+ for (const file of filesToCopy) {
517
+ if (
518
+ // eslint-disable-next-line no-await-in-loop
519
+ await fs.stat(file).then(
520
+ () => true,
521
+ () => false,
522
+ )
523
+ ) {
524
+ defaultFiles.push(file);
525
+ break;
526
+ }
527
+ }
528
+ }),
529
+ );
530
+
531
+ if (globs.length) {
532
+ const res = globs.map((globPattern) => {
533
+ const [pattern, baseDir] = globPattern.split(':');
534
+ return { pattern, baseDir };
535
+ });
536
+ /**
537
+ * Avoids redundant globby calls for the same pattern.
538
+ *
539
+ * @type {Map<string, Promise<string[]>>}
540
+ */
541
+ const globToResMap = new Map();
542
+
543
+ const result = await Promise.all(
544
+ res.map(async ({ pattern, baseDir }) => {
545
+ if (!globToResMap.has(pattern)) {
546
+ const promise = globby(pattern, { cwd });
547
+ globToResMap.set(pattern, promise);
548
+ }
549
+ const files = await globToResMap.get(pattern);
550
+ return { files: files ?? [], baseDir };
551
+ }),
552
+ );
553
+ globToResMap.clear();
554
+
555
+ result.forEach(({ files, baseDir }) => {
556
+ files.forEach((file) => {
557
+ const sourcePath = path.resolve(cwd, file);
558
+ // Use posix separator for the relative paths. So devs can only specify globs with `/` even on Windows.
559
+ const pathSegments = file.split(posixSep);
560
+ const relativePath =
561
+ // Use index 2 (when required) since users can also specify paths like `./src/index.js`
562
+ pathSegments.slice(pathSegments[0] === '.' ? 2 : 1).join(posixSep) || file;
563
+ const targetPath = baseDir
564
+ ? path.resolve(buildDir, baseDir, relativePath)
565
+ : path.resolve(buildDir, relativePath);
566
+ defaultFiles.push({ sourcePath, targetPath });
567
+ });
568
+ });
569
+ }
570
+
571
+ if (!defaultFiles.length) {
572
+ if (verbose) {
573
+ console.log('⓿ No files to copy.');
574
+ }
575
+ }
576
+ await mapConcurrently(
577
+ defaultFiles,
578
+ async (file) => {
579
+ if (typeof file === 'string') {
580
+ const sourcePath = file;
581
+ const fileName = path.basename(file);
582
+ const targetPath = path.join(buildDir, fileName);
583
+ await recursiveCopy({ source: sourcePath, target: targetPath, verbose });
584
+ } else {
585
+ await fs.mkdir(path.dirname(file.targetPath), { recursive: true });
586
+ await recursiveCopy({ source: file.sourcePath, target: file.targetPath, verbose });
587
+ }
588
+ },
589
+ 20,
590
+ );
591
+ console.log(`📋 Copied ${defaultFiles.length} files.`);
592
+ }
593
+
594
+ /**
595
+ * Recursively copies files and directories from a source path to a target path.
596
+ *
597
+ * @async
598
+ * @param {Object} options - The options for copying files.
599
+ * @param {string} options.source - The source path to copy from.
600
+ * @param {string} options.target - The target path to copy to.
601
+ * @param {boolean} [options.verbose=true] - If true, suppresses console output.
602
+ * @returns {Promise<boolean>} Resolves when the copy operation is complete.
603
+ * @throws {Error} Throws if an error occurs other than the source not existing.
604
+ */
605
+ async function recursiveCopy({ source, target, verbose = true }) {
606
+ try {
607
+ await fs.cp(source, target, { recursive: true });
608
+ if (verbose) {
609
+ console.log(`Copied ${source} to ${target}`);
610
+ }
611
+ return true;
612
+ } catch (err) {
613
+ if (/** @type {{ code: string }} */ (err).code !== 'ENOENT') {
614
+ throw err;
615
+ }
616
+ if (verbose) {
617
+ console.warn(`Source does not exist: ${source}`);
618
+ }
619
+ throw err;
620
+ }
621
+ }
@@ -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
- const concurrency = filesToProcess.length > 100 ? 100 : filesToProcess.length;
109
- const iterator = filesToProcess[Symbol.iterator]();
110
- const workers = [];
111
- for (let i = 0; i < concurrency; i += 1) {
112
- workers.push(
113
- Promise.resolve().then(async () => {
114
- for (const file of iterator) {
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
+ });
@@ -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
- for (let i = 0; i < concurrency; i += 1) {
51
- // eslint-disable-next-line @typescript-eslint/no-loop-func
52
- const worker = Promise.resolve().then(async () => {
53
- for (const filename of fileIterator) {
54
- // eslint-disable-next-line no-await-in-loop
55
- const content = await fs.readFile(path.join(cwd, filename), { encoding: 'utf8' });
56
- try {
57
- JSON.parse(content);
58
- if (!args.silent) {
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
- workers.push(worker);
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
  }