@mui/internal-code-infra 0.0.4-canary.8 → 0.0.4-canary.80

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 (140) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +19 -8
  3. package/build/babel-config.d.mts +5 -1
  4. package/build/brokenLinksChecker/crawlWorker.d.mts +1 -0
  5. package/build/brokenLinksChecker/index.d.mts +45 -2
  6. package/build/changelog/types.d.ts +1 -1
  7. package/build/cli/cmdArgosPush.d.mts +2 -2
  8. package/build/cli/cmdBuild.d.mts +3 -2
  9. package/build/cli/cmdCopyFiles.d.mts +2 -2
  10. package/build/cli/cmdExtractErrorCodes.d.mts +2 -2
  11. package/build/cli/cmdGenerateChangelog.d.mts +2 -2
  12. package/build/cli/cmdGithubAuth.d.mts +2 -2
  13. package/build/cli/cmdListWorkspaces.d.mts +6 -4
  14. package/build/cli/cmdNetlifyIgnore.d.mts +3 -2
  15. package/build/cli/cmdPublish.d.mts +4 -2
  16. package/build/cli/cmdPublishCanary.d.mts +3 -3
  17. package/build/cli/cmdPublishNewPackage.d.mts +4 -2
  18. package/build/cli/cmdSetVersionOverrides.d.mts +2 -2
  19. package/build/cli/cmdVale.d.mts +46 -0
  20. package/build/cli/cmdValidateBuiltTypes.d.mts +2 -2
  21. package/build/eslint/baseConfig.d.mts +3 -1
  22. package/build/eslint/mui/rules/disallow-react-api-in-server-components.d.mts +2 -2
  23. package/build/eslint/mui/rules/docgen-ignore-before-comment.d.mts +2 -2
  24. package/build/eslint/mui/rules/no-floating-cleanup.d.mts +5 -0
  25. package/build/eslint/mui/rules/no-guarded-throw.d.mts +31 -0
  26. package/build/eslint/mui/rules/no-presentation-role.d.mts +5 -0
  27. package/build/eslint/mui/rules/no-restricted-resolved-imports.d.mts +2 -2
  28. package/build/eslint/mui/rules/nodeEnvUtils.d.mts +18 -0
  29. package/build/remark/config.d.mts +43 -0
  30. package/build/remark/createLintTester.d.mts +10 -0
  31. package/build/remark/firstBlockHeading.d.mts +4 -0
  32. package/build/remark/gitDiff.d.mts +2 -0
  33. package/build/remark/noSpaceInLinks.d.mts +2 -0
  34. package/build/remark/straightQuotes.d.mts +2 -0
  35. package/build/remark/tableAlignment.d.mts +2 -0
  36. package/build/remark/terminalLanguage.d.mts +2 -0
  37. package/build/utils/babel.d.mts +1 -1
  38. package/build/utils/build.d.mts +56 -37
  39. package/build/utils/git.d.mts +7 -0
  40. package/build/utils/github.d.mts +1 -1
  41. package/build/utils/pnpm.d.mts +81 -2
  42. package/build/utils/testUtils.d.mts +7 -0
  43. package/build/utils/typescript.d.mts +6 -16
  44. package/package.json +65 -41
  45. package/src/babel-config.mjs +3 -1
  46. package/src/brokenLinksChecker/crawlWorker.mjs +240 -0
  47. package/src/brokenLinksChecker/index.mjs +263 -188
  48. package/src/changelog/fetchChangelogs.mjs +6 -2
  49. package/src/changelog/renderChangelog.mjs +1 -1
  50. package/src/changelog/types.ts +1 -1
  51. package/src/cli/cmdBuild.mjs +51 -85
  52. package/src/cli/cmdListWorkspaces.mjs +12 -27
  53. package/src/cli/cmdNetlifyIgnore.mjs +34 -93
  54. package/src/cli/cmdPublish.mjs +95 -20
  55. package/src/cli/cmdPublishCanary.mjs +128 -132
  56. package/src/cli/cmdPublishNewPackage.mjs +27 -6
  57. package/src/cli/cmdSetVersionOverrides.mjs +8 -9
  58. package/src/cli/cmdVale.mjs +513 -0
  59. package/src/cli/index.mjs +2 -0
  60. package/src/cli/packageJson.d.ts +1 -1
  61. package/src/eslint/baseConfig.mjs +51 -19
  62. package/src/eslint/docsConfig.mjs +2 -1
  63. package/src/eslint/mui/config.mjs +24 -4
  64. package/src/eslint/mui/index.mjs +6 -0
  65. package/src/eslint/mui/rules/no-floating-cleanup.mjs +187 -0
  66. package/src/eslint/mui/rules/no-guarded-throw.mjs +115 -0
  67. package/src/eslint/mui/rules/no-presentation-role.mjs +60 -0
  68. package/src/eslint/mui/rules/nodeEnvUtils.mjs +52 -0
  69. package/src/eslint/mui/rules/require-dev-wrapper.mjs +25 -40
  70. package/src/estree-typescript.d.ts +1 -1
  71. package/src/remark/config.mjs +157 -0
  72. package/src/remark/createLintTester.mjs +19 -0
  73. package/src/remark/firstBlockHeading.mjs +87 -0
  74. package/src/remark/gitDiff.mjs +43 -0
  75. package/src/remark/noSpaceInLinks.mjs +42 -0
  76. package/src/remark/straightQuotes.mjs +31 -0
  77. package/src/remark/tableAlignment.mjs +23 -0
  78. package/src/remark/terminalLanguage.mjs +19 -0
  79. package/src/untyped-plugins.d.ts +11 -11
  80. package/src/utils/build.mjs +604 -270
  81. package/src/utils/git.mjs +27 -7
  82. package/src/utils/pnpm.mjs +277 -10
  83. package/src/utils/testUtils.mjs +18 -0
  84. package/src/utils/typescript.mjs +23 -42
  85. package/vale/.vale.ini +1 -0
  86. package/vale/styles/MUI/CorrectReferenceAllCases.yml +42 -0
  87. package/vale/styles/MUI/CorrectRererenceCased.yml +14 -0
  88. package/vale/styles/MUI/GoogleLatin.yml +11 -0
  89. package/vale/styles/MUI/MuiBrandName.yml +22 -0
  90. package/vale/styles/MUI/NoBritish.yml +112 -0
  91. package/vale/styles/MUI/NoCompanyName.yml +17 -0
  92. package/build/markdownlint/duplicate-h1.d.mts +0 -56
  93. package/build/markdownlint/git-diff.d.mts +0 -11
  94. package/build/markdownlint/index.d.mts +0 -52
  95. package/build/markdownlint/straight-quotes.d.mts +0 -11
  96. package/build/markdownlint/table-alignment.d.mts +0 -11
  97. package/build/markdownlint/terminal-language.d.mts +0 -11
  98. package/src/brokenLinksChecker/__fixtures__/static-site/broken-links.html +0 -20
  99. package/src/brokenLinksChecker/__fixtures__/static-site/broken-targets.html +0 -22
  100. package/src/brokenLinksChecker/__fixtures__/static-site/example.md +0 -20
  101. package/src/brokenLinksChecker/__fixtures__/static-site/external-links.html +0 -21
  102. package/src/brokenLinksChecker/__fixtures__/static-site/ignored-page.html +0 -17
  103. package/src/brokenLinksChecker/__fixtures__/static-site/index.html +0 -28
  104. package/src/brokenLinksChecker/__fixtures__/static-site/known-targets.json +0 -5
  105. package/src/brokenLinksChecker/__fixtures__/static-site/nested/page.html +0 -21
  106. package/src/brokenLinksChecker/__fixtures__/static-site/orphaned-page.html +0 -20
  107. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-api-links.html +0 -20
  108. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-custom-targets.html +0 -24
  109. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-ignored-content.html +0 -28
  110. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-known-target-links.html +0 -19
  111. package/src/brokenLinksChecker/__fixtures__/static-site/unclosed-tags.html +0 -1
  112. package/src/brokenLinksChecker/__fixtures__/static-site/valid.html +0 -20
  113. package/src/brokenLinksChecker/__fixtures__/static-site/with-anchors.html +0 -31
  114. package/src/brokenLinksChecker/index.test.ts +0 -261
  115. package/src/changelog/categorizeCommits.test.ts +0 -319
  116. package/src/changelog/filterCommits.test.ts +0 -363
  117. package/src/changelog/parseCommitLabels.test.ts +0 -509
  118. package/src/changelog/renderChangelog.test.ts +0 -378
  119. package/src/changelog/sortSections.test.ts +0 -199
  120. package/src/eslint/mui/rules/add-undef-to-optional.test.mjs +0 -361
  121. package/src/eslint/mui/rules/consistent-production-guard.test.mjs +0 -162
  122. package/src/eslint/mui/rules/disallow-active-elements-as-key-event-target.test.mjs +0 -66
  123. package/src/eslint/mui/rules/disallow-react-api-in-server-components.test.mjs +0 -305
  124. package/src/eslint/mui/rules/docgen-ignore-before-comment.test.mjs +0 -52
  125. package/src/eslint/mui/rules/flatten-parentheses.test.mjs +0 -245
  126. package/src/eslint/mui/rules/mui-name-matches-component-name.test.mjs +0 -247
  127. package/src/eslint/mui/rules/no-empty-box.test.mjs +0 -40
  128. package/src/eslint/mui/rules/no-styled-box.test.mjs +0 -73
  129. package/src/eslint/mui/rules/require-dev-wrapper.test.mjs +0 -265
  130. package/src/eslint/mui/rules/rules-of-use-theme-variants.test.mjs +0 -149
  131. package/src/eslint/mui/rules/straight-quotes.test.mjs +0 -67
  132. package/src/markdownlint/duplicate-h1.mjs +0 -69
  133. package/src/markdownlint/git-diff.mjs +0 -31
  134. package/src/markdownlint/index.mjs +0 -62
  135. package/src/markdownlint/straight-quotes.mjs +0 -26
  136. package/src/markdownlint/table-alignment.mjs +0 -42
  137. package/src/markdownlint/terminal-language.mjs +0 -19
  138. package/src/utils/build.test.mjs +0 -705
  139. package/src/utils/template.test.mjs +0 -133
  140. package/src/utils/typescript.test.mjs +0 -380
@@ -10,6 +10,7 @@ import * as semver from 'semver';
10
10
  import {
11
11
  createPackageBin,
12
12
  createPackageExports,
13
+ createPackageImports,
13
14
  getOutExtension,
14
15
  mapConcurrently,
15
16
  validatePkgJson,
@@ -30,7 +31,8 @@ import {
30
31
  * @property {string[]} [copy] - Files/Directories to be copied. Can be a glob pattern.
31
32
  * @property {boolean} [enableReactCompiler] - Whether to use the React compiler.
32
33
  * @property {boolean} [tsgo] - Whether to build types using typescript native (tsgo).
33
- * @property {boolean} [flat] - Builds the package in a flat structure without subdirectories for each module type.
34
+ * @property {boolean} [flat] - Deprecated no-op; flat builds are always used.
35
+ * @property {boolean} expand - Whether to enumerate glob patterns in exports/imports into concrete entries.
34
36
  */
35
37
 
36
38
  const validBundles = [
@@ -45,13 +47,12 @@ const validBundles = [
45
47
  * @param {string} options.name - The name of the package.
46
48
  * @param {string} options.version - The version of the package.
47
49
  * @param {string} options.license - The license of the package.
48
- * @param {boolean} options.isFlat - Whether the build is flat structure.
49
50
  * @param {'module' | 'commonjs'} options.packageType - The package.json type field.
50
51
  * @param {import('../utils/build.mjs').BundleType} options.bundle
51
52
  * @param {string} options.outputDir
52
53
  */
53
- async function addLicense({ name, version, license, bundle, outputDir, isFlat, packageType }) {
54
- const outExtension = getOutExtension(bundle, { isFlat, packageType });
54
+ async function addLicense({ name, version, license, bundle, outputDir, packageType }) {
55
+ const outExtension = getOutExtension(bundle, { packageType });
55
56
  const file = path.join(outputDir, `index${outExtension}`);
56
57
  if (
57
58
  !(await fs.stat(file).then(
@@ -80,11 +81,11 @@ ${content}`,
80
81
  /**
81
82
  * @param {Object} param0
82
83
  * @param {import('./packageJson').PackageJson} param0.packageJson - The package.json content.
83
- * @param {{type: import('../utils/build.mjs').BundleType; dir: string}[]} param0.bundles
84
+ * @param {import('../utils/build.mjs').BundleType[]} param0.bundles
84
85
  * @param {string} param0.outputDir
85
86
  * @param {string} param0.cwd
86
87
  * @param {boolean} param0.addTypes - Whether to add type declarations for the package.
87
- * @param {boolean} param0.isFlat - Whether the build is flat structure.
88
+ * @param {boolean} [param0.expand] - Whether to enumerate glob patterns into concrete entries.
88
89
  * @param {'module' | 'commonjs'} param0.packageType - The package.json type field.
89
90
  */
90
91
  async function writePackageJson({
@@ -93,37 +94,48 @@ async function writePackageJson({
93
94
  outputDir,
94
95
  cwd,
95
96
  addTypes = false,
96
- isFlat = false,
97
+ expand = true,
97
98
  packageType,
98
99
  }) {
99
100
  delete packageJson.scripts;
100
101
  delete packageJson.publishConfig?.directory;
101
102
  delete packageJson.devDependencies;
102
- delete packageJson.imports;
103
103
 
104
104
  const resolvedPackageType = packageType || packageJson.type || 'commonjs';
105
105
  packageJson.type = resolvedPackageType;
106
106
 
107
107
  const originalExports = packageJson.exports;
108
108
  delete packageJson.exports;
109
+ const originalImports = packageJson.imports;
110
+ delete packageJson.imports;
109
111
  const originalBin = packageJson.bin;
110
112
  delete packageJson.bin;
111
113
 
112
- const {
113
- exports: packageExports,
114
- main,
115
- types,
116
- } = await createPackageExports({
117
- exports: originalExports,
118
- bundles,
119
- outputDir,
120
- cwd,
121
- addTypes,
122
- isFlat,
123
- packageType: resolvedPackageType,
124
- });
114
+ const [{ exports: packageExports, main, types }, packageImports] = await Promise.all([
115
+ createPackageExports(originalExports, {
116
+ bundles,
117
+ outputDir,
118
+ cwd,
119
+ addTypes,
120
+ expand,
121
+ packageType: resolvedPackageType,
122
+ }),
123
+ originalImports
124
+ ? createPackageImports(originalImports, {
125
+ bundles,
126
+ cwd,
127
+ outputDir,
128
+ addTypes,
129
+ expand,
130
+ packageType: resolvedPackageType,
131
+ })
132
+ : Promise.resolve(undefined),
133
+ ]);
125
134
 
126
135
  packageJson.exports = packageExports;
136
+ if (packageImports) {
137
+ packageJson.imports = packageImports;
138
+ }
127
139
  if (main) {
128
140
  packageJson.main = main;
129
141
  }
@@ -131,11 +143,9 @@ async function writePackageJson({
131
143
  packageJson.types = types;
132
144
  }
133
145
 
134
- const bin = await createPackageBin({
135
- bin: originalBin,
146
+ const bin = await createPackageBin(originalBin, {
136
147
  bundles,
137
148
  cwd,
138
- isFlat,
139
149
  packageType: resolvedPackageType,
140
150
  });
141
151
  if (bin) {
@@ -225,16 +235,23 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
225
235
  })
226
236
  .option('flat', {
227
237
  type: 'boolean',
228
- default: process.env.MUI_BUILD_FLAT === '1',
238
+ deprecated:
239
+ 'Flat builds are now always used; this flag is a no-op and will be removed in a future release.',
240
+ default: true,
241
+ description:
242
+ '@deprecated Flat builds are always used now. The package is always built in a flat structure without subdirectories for each module type.',
243
+ })
244
+ .option('expand', {
245
+ type: 'boolean',
246
+ default: true,
229
247
  description:
230
- 'Builds the package in a flat structure without subdirectories for each module type.',
248
+ 'Enumerate glob patterns in the package.json "exports"/"imports" into concrete entries. Use --no-expand to keep them as Node runtime subpath patterns.',
231
249
  });
232
250
  },
233
251
  async handler(args) {
234
252
  const {
235
253
  bundle: bundles,
236
254
  hasLargeFiles,
237
- skipBundlePackageJson,
238
255
  verbose = false,
239
256
  ignore: extraIgnores,
240
257
  buildTypes,
@@ -255,9 +272,6 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
255
272
  const packageType = packageJson.type === 'module' ? 'module' : 'commonjs';
256
273
 
257
274
  console.log(`Selected output directory: "${buildDirBase}"`);
258
- if (args.flat) {
259
- console.log('Building package in flat structure.');
260
- }
261
275
 
262
276
  await fs.rm(buildDir, { recursive: true, force: true });
263
277
 
@@ -282,15 +296,6 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
282
296
  }
283
297
 
284
298
  const { build: babelBuild, cjsCopy } = await import('../utils/babel.mjs');
285
- const relativeOutDirs = !args.flat
286
- ? {
287
- cjs: '.',
288
- esm: 'esm',
289
- }
290
- : {
291
- cjs: '.',
292
- esm: '.',
293
- };
294
299
  const sourceDir = path.join(cwd, 'src');
295
300
  const reactVersion =
296
301
  semver.minVersion(packageJson.peerDependencies?.react || '')?.version ?? 'latest';
@@ -306,13 +311,10 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
306
311
  await Promise.all(
307
312
  bundles.map(async (bundle) => {
308
313
  const outExtension = getOutExtension(bundle, {
309
- isFlat: !!args.flat,
310
314
  isType: false,
311
315
  packageType,
312
316
  });
313
- const relativeOutDir = relativeOutDirs[bundle];
314
- const outputDir = path.join(buildDir, relativeOutDir);
315
- await fs.mkdir(outputDir, { recursive: true });
317
+ await fs.mkdir(buildDir, { recursive: true });
316
318
 
317
319
  const promises = [];
318
320
 
@@ -320,7 +322,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
320
322
  babelBuild({
321
323
  cwd,
322
324
  sourceDir,
323
- outDir: outputDir,
325
+ outDir: buildDir,
324
326
  babelRuntimeVersion,
325
327
  hasLargeFiles,
326
328
  bundle,
@@ -340,61 +342,28 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
340
342
  }),
341
343
  );
342
344
 
343
- if (buildDir !== outputDir && !skipBundlePackageJson && !args.flat) {
344
- // @TODO - Not needed if the output extension is .mjs. Remove this before PR merge.
345
- promises.push(
346
- fs.writeFile(
347
- path.join(outputDir, 'package.json'),
348
- JSON.stringify({
349
- type: bundle === 'esm' ? 'module' : 'commonjs',
350
- sideEffects: packageJson.sideEffects ?? false,
351
- }),
352
- ),
353
- );
354
- }
355
-
356
- if (!args.flat) {
357
- // cjs for reexporting from commons only modules.
358
- // @NOTE: If we need to rely more on this we can think about setting up
359
- // a separate commonjs => commonjs build for .cjs files to .cjs
360
- // `--extensions-.cjs --out-file-extension .cjs`
361
- promises.push(cjsCopy({ from: sourceDir, to: outputDir }));
362
- }
363
-
364
345
  await Promise.all(promises);
365
346
  await addLicense({
366
347
  bundle,
367
348
  license: packageJson.license,
368
349
  name: packageJson.name,
369
350
  version: packageJson.version,
370
- outputDir,
371
- isFlat: !!args.flat,
351
+ outputDir: buildDir,
372
352
  packageType,
373
353
  });
374
354
  }),
375
355
  );
376
356
 
377
- if (args.flat) {
378
- await cjsCopy({ from: sourceDir, to: buildDir });
379
- }
357
+ await cjsCopy({ from: sourceDir, to: buildDir });
380
358
  // js build end
381
359
 
382
360
  if (buildTypes) {
383
361
  const tsMod = await import('../utils/typescript.mjs');
384
- /**
385
- * @type {{type: import('../utils/build.mjs').BundleType, dir: string}[]};
386
- */
387
- const bundleMap = bundles.map((type) => ({
388
- type,
389
- dir: relativeOutDirs[type],
390
- }));
391
-
392
362
  await tsMod.createTypes({
393
- bundles: bundleMap,
363
+ bundles,
394
364
  srcDir: sourceDir,
395
365
  cwd,
396
366
  skipTsc,
397
- isFlat: !!args.flat,
398
367
  buildDir,
399
368
  useTsgo,
400
369
  packageType,
@@ -409,13 +378,10 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
409
378
  await writePackageJson({
410
379
  cwd,
411
380
  packageJson,
412
- bundles: bundles.map((type) => ({
413
- type,
414
- dir: relativeOutDirs[type],
415
- })),
381
+ bundles,
416
382
  outputDir: buildDir,
417
383
  addTypes: buildTypes,
418
- isFlat: !!args.flat,
384
+ expand: args.expand,
419
385
  packageType,
420
386
  });
421
387
 
@@ -6,15 +6,14 @@
6
6
  * @typedef {import('../utils/pnpm.mjs').PublicPackage} PublicPackage
7
7
  */
8
8
 
9
- import * as fs from 'node:fs/promises';
10
- import * as path from 'node:path';
11
9
  import { getWorkspacePackages } from '../utils/pnpm.mjs';
12
10
 
13
11
  /**
14
12
  * @typedef {Object} Args
15
13
  * @property {boolean} [publicOnly] - Whether to filter to only public packages
16
- * @property {'json'|'path'|'name'|'publish-dir'} [output] - Output format (name, path, or json)
14
+ * @property {'json'|'path'|'name'} [output] - Output format (name, path, or json)
17
15
  * @property {string} [sinceRef] - Git reference to filter changes since
16
+ * @property {string[]} [filter] - Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.
18
17
  */
19
18
 
20
19
  export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
@@ -29,21 +28,27 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
29
28
  })
30
29
  .option('output', {
31
30
  type: 'string',
32
- choices: ['json', 'path', 'name', 'publish-dir'],
31
+ choices: ['json', 'path', 'name'],
33
32
  default: 'path',
34
33
  description:
35
- 'Output format: name (package names), path (package paths), publish-dir (publish directories), or json (full JSON)',
34
+ 'Output format: name (package names), path (package paths), or json (full JSON)',
36
35
  })
37
36
  .option('since-ref', {
38
37
  type: 'string',
39
38
  description: 'Filter packages changed since git reference',
39
+ })
40
+ .option('filter', {
41
+ type: 'string',
42
+ array: true,
43
+ description:
44
+ 'Same as filtering packages with --filter in pnpm. Only include packages matching the filter. See https://pnpm.io/filtering.',
40
45
  });
41
46
  },
42
47
  handler: async (argv) => {
43
- const { publicOnly = false, output = 'name', sinceRef } = argv;
48
+ const { publicOnly = false, output = 'name', sinceRef, filter = [] } = argv;
44
49
 
45
50
  // Get packages using our helper function
46
- const packages = await getWorkspacePackages({ sinceRef, publicOnly });
51
+ const packages = await getWorkspacePackages({ sinceRef, publicOnly, filter });
47
52
 
48
53
  if (output === 'json') {
49
54
  // Serialize packages to JSON
@@ -53,26 +58,6 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
53
58
  packages.forEach((pkg) => {
54
59
  console.log(pkg.path);
55
60
  });
56
- } else if (output === 'publish-dir') {
57
- // TODO: Remove this option once https://github.com/stackblitz-labs/pkg.pr.new/issues/389 is resolved
58
- // Print publish directories (package.json publishConfig.directory or package path)
59
- const publishDirs = await Promise.all(
60
- packages.map(async (pkg) => {
61
- const packageJsonPath = path.join(pkg.path, 'package.json');
62
- const packageJsonContent = await fs.readFile(packageJsonPath, 'utf8');
63
- const packageJson = JSON.parse(packageJsonContent);
64
-
65
- if (packageJson.publishConfig?.directory) {
66
- return path.join(pkg.path, packageJson.publishConfig.directory);
67
- }
68
-
69
- return pkg.path;
70
- }),
71
- );
72
-
73
- publishDirs.forEach((dir) => {
74
- console.log(dir);
75
- });
76
61
  } else if (output === 'name') {
77
62
  // Print package names (default)
78
63
  packages.forEach((pkg) => {
@@ -6,111 +6,39 @@
6
6
  * @typedef {Object} Args
7
7
  * @property {string[]} workspaces - List of workspace names to process
8
8
  * @property {boolean} [check] - Check mode - error if the generated content differs from current
9
+ * @property {string} [baseBranch] - Branch to compare PRs against (default: master)
9
10
  */
10
11
 
11
12
  import * as fs from 'node:fs/promises';
12
13
  import * as path from 'node:path';
13
14
  import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
14
15
  import { toPosixPath } from '../utils/path.mjs';
15
- import { getWorkspacePackages } from '../utils/pnpm.mjs';
16
+ import { getTransitiveDependencies, getWorkspacePackages } from '../utils/pnpm.mjs';
16
17
 
17
18
  /**
18
- * Get all workspace dependencies (direct and transitive) from a package
19
- * @param {string} packageName - Package name
20
- * @param {Map<string, string>} workspaceMap - Map of workspace name to path
21
- * @param {Map<string, Promise<Set<string>>>} cache - Cache of package resolution promises
22
- * @returns {Promise<Set<string>>} Set of workspace package names (dependencies only, not including the package itself)
23
- */
24
- async function getWorkspaceDependenciesRecursive(packageName, workspaceMap, cache) {
25
- // Check cache first
26
- const cached = cache.get(packageName);
27
- if (cached) {
28
- return cached;
29
- }
30
-
31
- // Create the resolution promise
32
- const promise = (async () => {
33
- const packagePath = workspaceMap.get(packageName);
34
- if (!packagePath) {
35
- throw new Error(`Workspace "${packageName}" not found in the repository`);
36
- }
37
-
38
- const packageJsonPath = path.join(packagePath, 'package.json');
39
- const content = await fs.readFile(packageJsonPath, 'utf8');
40
- const packageJson = JSON.parse(content);
41
-
42
- // Collect all dependency names
43
- /** @type {Set<string>} */
44
- const allDeps = new Set();
45
- if (packageJson.dependencies) {
46
- Object.keys(packageJson.dependencies).forEach((dep) => allDeps.add(dep));
47
- }
48
- if (packageJson.devDependencies) {
49
- Object.keys(packageJson.devDependencies).forEach((dep) => allDeps.add(dep));
50
- }
51
- if (packageJson.peerDependencies) {
52
- Object.keys(packageJson.peerDependencies).forEach((dep) => allDeps.add(dep));
53
- }
54
-
55
- // Filter to only workspace dependencies
56
- const workspaceDeps = Array.from(allDeps).filter((dep) => workspaceMap.has(dep));
57
-
58
- // Recursively process workspace dependencies in parallel
59
- const recursiveResults = await Promise.all(
60
- workspaceDeps.map(async (dep) => {
61
- return getWorkspaceDependenciesRecursive(dep, workspaceMap, cache);
62
- }),
63
- );
64
-
65
- // Merge all results using flatMap
66
- return new Set(recursiveResults.flatMap((result) => Array.from(result)).concat(workspaceDeps));
67
- })();
68
-
69
- // Store in cache before returning
70
- cache.set(packageName, promise);
71
-
72
- return promise;
73
- }
74
-
75
- /**
76
- * Get transitive workspace dependencies for a list of workspace names
77
- * @param {string[]} workspaceNames - Array of workspace names
78
- * @param {Map<string, string>} workspaceMap - Map of workspace name to path
79
- * @returns {Promise<Set<string>>} Set of workspace package names (including requested packages and all their dependencies)
80
- */
81
- async function getTransitiveDependencies(workspaceNames, workspaceMap) {
82
- // Shared cache for all workspace dependency resolution
83
- const cache = new Map();
84
-
85
- // Validate all workspace names exist
86
- for (const workspaceName of workspaceNames) {
87
- if (!workspaceMap.has(workspaceName)) {
88
- throw new Error(`Workspace "${workspaceName}" not found in the repository`);
89
- }
90
- }
91
-
92
- // Process each requested workspace in parallel
93
- const workspaceResults = await Promise.all(
94
- workspaceNames.map((workspaceName) =>
95
- getWorkspaceDependenciesRecursive(workspaceName, workspaceMap, cache),
96
- ),
97
- );
98
-
99
- // Merge all results using flatMap and add the original package names
100
- return new Set(workspaceNames.concat(workspaceResults.flatMap((result) => Array.from(result))));
101
- }
102
-
103
- /**
104
- * Generate the ignore command string for netlify.toml
19
+ * Generate the ignore command string for netlify.toml.
20
+ *
21
+ * Production and branch deploys (every Netlify $CONTEXT other than
22
+ * "deploy-preview") always build, so downstream plugins (e.g. e2e triggers)
23
+ * run on every deploy and catch regressions in external dependencies even
24
+ * when the commit doesn't touch the watched paths.
25
+ *
26
+ * Only deploy-previews (PR previews) are eligible to be skipped: they diff
27
+ * against the merge-base with origin/<baseBranch>. This way a PR rebase whose
28
+ * head commit doesn't touch the watched paths still rebuilds when the PR as a
29
+ * whole introduces changes to them — otherwise downstream plugins silently
30
+ * never run.
31
+ *
105
32
  * @param {string[]} paths - Array of paths to include in the ignore command
106
33
  * @param {string} packagePath - Absolute path to the package directory
107
34
  * @param {string} workspaceRoot - Absolute path to the workspace root
35
+ * @param {string} baseBranch - Branch to compare PRs against
108
36
  * @returns {string} The ignore command string
109
37
  */
110
- function generateIgnoreCommand(paths, packagePath, workspaceRoot) {
38
+ function generateIgnoreCommand(paths, packagePath, workspaceRoot, baseBranch) {
111
39
  const relFromBase = `${toPosixPath(path.relative(packagePath, workspaceRoot))}/`;
112
40
  const pathsStr = paths.join(' ');
113
- return ` ignore = "cd ${relFromBase} && git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF ${pathsStr}"`;
41
+ return ` ignore = """cd ${relFromBase} && [ "$CONTEXT" = "deploy-preview" ] && git fetch origin ${baseBranch} --depth=500 -q && git diff --quiet FETCH_HEAD...$COMMIT_REF -- ${pathsStr}"""`;
114
42
  }
115
43
 
116
44
  /**
@@ -180,6 +108,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
180
108
  default: false,
181
109
  describe: 'Check if the netlify.toml needs updating without modifying it',
182
110
  })
111
+ .option('base-branch', {
112
+ type: 'string',
113
+ default: 'master',
114
+ describe:
115
+ "Branch to compare PRs against (the site's production branch on Netlify). Production and branch deploys always rebuild regardless of this value.",
116
+ })
183
117
  .example('$0 netlify-ignore @mui/internal-docs-infra', 'Update netlify.toml for a workspace')
184
118
  .example(
185
119
  '$0 netlify-ignore @mui/internal-docs-infra @mui/internal-code-infra',
@@ -191,7 +125,7 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
191
125
  );
192
126
  },
193
127
  handler: async (argv) => {
194
- const { workspaces, check = false } = argv;
128
+ const { workspaces, check = false, baseBranch = 'master' } = argv;
195
129
 
196
130
  // Get the workspace root
197
131
  const workspaceRoot = await findWorkspaceDir(process.cwd());
@@ -220,7 +154,9 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
220
154
  console.log(`Processing ${workspaceName}...`);
221
155
 
222
156
  // Get transitive dependencies for this specific workspace
223
- const dependencyNames = await getTransitiveDependencies([workspaceName], workspaceMap);
157
+ const dependencyNames = await getTransitiveDependencies([workspaceName], {
158
+ workspacePathByName: workspaceMap,
159
+ });
224
160
 
225
161
  // Convert package names to relative paths (normalize to POSIX separators for git)
226
162
  const relativePaths = Array.from(dependencyNames)
@@ -241,7 +177,12 @@ export default /** @type {import('yargs').CommandModule<{}, Args>} */ ({
241
177
  const allPaths = [...relativePaths, 'pnpm-lock.yaml'];
242
178
 
243
179
  // Generate the ignore command for this workspace
244
- const newIgnoreCommand = generateIgnoreCommand(allPaths, workspacePath, workspaceRoot);
180
+ const newIgnoreCommand = generateIgnoreCommand(
181
+ allPaths,
182
+ workspacePath,
183
+ workspaceRoot,
184
+ baseBranch,
185
+ );
245
186
 
246
187
  // Update or check the netlify.toml file
247
188
  await updateNetlifyToml(tomlPath, newIgnoreCommand, check);