@mui/internal-code-infra 0.0.4-canary.9 → 0.0.4-canary.91

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 +3 -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 +74 -50
  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 +8 -2
  49. package/src/changelog/renderChangelog.mjs +1 -1
  50. package/src/changelog/types.ts +3 -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 +2 -3
  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
@@ -1,27 +1,51 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
3
  import { globby } from 'globby';
4
- import { minimatch } from 'minimatch';
5
4
  import * as semver from 'semver';
6
5
 
7
6
  /**
8
7
  * @typedef {'esm' | 'cjs'} BundleType
9
8
  */
10
9
 
10
+ /**
11
+ * @typedef {Object} BundleMeta
12
+ * @property {BundleType} type
13
+ * @property {string} dir
14
+ * @property {'import' | 'require'} condition - The package.json condition this bundle maps to.
15
+ * @property {string} outExtension
16
+ * @property {string} typeOutExtension
17
+ */
18
+
19
+ /**
20
+ * Source files in JS/TS-like languages that the build compiles. These are the
21
+ * only leaves that get an extension swap + `import`/`require`/`types` conditions.
22
+ * Other files under `src/` (e.g. `.css`, `.json`) are copied verbatim and only
23
+ * get their `./src/` prefix rewritten.
24
+ */
25
+ const JS_TS_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs']);
26
+
27
+ export const BASE_IGNORES = [
28
+ '**/*.test.js',
29
+ '**/*.test.ts',
30
+ '**/*.test.tsx',
31
+ '**/*.spec.js',
32
+ '**/*.spec.ts',
33
+ '**/*.spec.tsx',
34
+ '**/*.d.ts',
35
+ '**/*.test/*.*',
36
+ '**/test-cases/*.*',
37
+ ];
38
+
11
39
  /**
12
40
  * @param {BundleType} bundle
13
41
  * @param {Object} [options]
14
42
  * @param {boolean} [options.isType=false] - Whether to get the extension for type declaration files.
15
- * @param {boolean} [options.isFlat=false] - Whether to get the extension for a flat build structure.
16
43
  * @param {'module' | 'commonjs'} [options.packageType='commonjs'] - The package.json type field.
17
44
  * @returns {string}
18
45
  */
19
46
  export function getOutExtension(bundle, options = {}) {
20
- const { isType = false, isFlat = false, packageType = 'commonjs' } = options;
47
+ const { isType = false, packageType = 'commonjs' } = options;
21
48
  const normalizedPackageType = packageType === 'module' ? 'module' : 'commonjs';
22
- if (!isFlat) {
23
- return isType ? '.d.ts' : '.js';
24
- }
25
49
  if (isType) {
26
50
  if (normalizedPackageType === 'module') {
27
51
  return bundle === 'esm' ? '.d.ts' : '.d.cts';
@@ -35,215 +59,524 @@ export function getOutExtension(bundle, options = {}) {
35
59
  }
36
60
 
37
61
  /**
38
- * @param {Object} param0
39
- * @param {NonNullable<import('../cli/packageJson').PackageJson.Exports>} param0.importPath
40
- * @param {string} param0.key
41
- * @param {string} param0.cwd
42
- * @param {string} param0.dir
43
- * @param {string} param0.type
44
- * @param {import('../cli/packageJson').PackageJson.ExportConditions} param0.newExports
45
- * @param {string} param0.typeOutExtension
46
- * @param {string} param0.outExtension
47
- * @param {boolean} param0.addTypes
48
- * @returns {Promise<void>}
62
+ * Returns a new object with `import` first, `require` second, `default` last,
63
+ * and any other condition keys preserved in their original relative order in between.
64
+ * @param {Record<string, any>} conditions
65
+ * @returns {Record<string, any>}
66
+ */
67
+ function sortExportConditions(conditions) {
68
+ /** @type {Record<string, number | undefined>} */
69
+ const order = { import: 0, require: 1, default: 3 };
70
+ return Object.fromEntries(
71
+ Object.entries(conditions).sort(([a], [b]) => (order[a] ?? 2) - (order[b] ?? 2)),
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Recursively fills in the `default` condition and stabilizes key order for a
77
+ * single condition value. Bundles run in parallel, so `import`/`require`
78
+ * insertion order would otherwise depend on Promise timing. Nested condition
79
+ * objects (e.g. `{ node: {...}, default: {...} }`) are handled at every level
80
+ * that carries `import`/`require`. Non-condition values (plain strings, bare
81
+ * specifiers, `null`) are returned untouched.
82
+ * @param {any} value
83
+ * @param {boolean} addTypes
84
+ * @param {string} kind - Used for error messages, e.g. `export` or `import`.
85
+ * @param {string} key - Used for error messages.
86
+ * @returns {any}
49
87
  */
50
- async function createExportsFor({
51
- importPath,
52
- key,
53
- cwd,
54
- dir,
55
- type,
56
- newExports,
57
- typeOutExtension,
58
- outExtension,
59
- addTypes,
60
- }) {
61
- if (Array.isArray(importPath)) {
88
+ function finalizeConditionValue(value, addTypes, kind, key) {
89
+ if (value === null || typeof value !== 'object') {
90
+ return value;
91
+ }
92
+ if (Array.isArray(value)) {
62
93
  throw new Error(
63
- `Array form of package.json exports is not supported yet. Found in export "${key}".`,
94
+ `Array form of package.json ${kind}s is not supported yet. Found in ${kind} "${key}".`,
64
95
  );
65
96
  }
66
97
 
67
- let srcPath = typeof importPath === 'string' ? importPath : importPath['mui-src'];
68
- const rest = typeof importPath === 'string' ? {} : { ...importPath };
69
- delete rest['mui-src'];
98
+ for (const childKey of Object.keys(value)) {
99
+ value[childKey] = finalizeConditionValue(value[childKey], addTypes, kind, key);
100
+ }
70
101
 
71
- if (typeof srcPath !== 'string') {
72
- throw new Error(
73
- `Unsupported export for "${key}". Only a string or an object with "mui-src" field is supported for now.`,
74
- );
102
+ if (value.import || value.require) {
103
+ // Synthesize `default` from import/require (preferring ESM), but never clobber
104
+ // a user-authored `default` condition that sits alongside them.
105
+ if (value.default === undefined) {
106
+ const defaultExport = value.import || value.require;
107
+ if (addTypes) {
108
+ value.default = defaultExport;
109
+ } else {
110
+ value.default =
111
+ defaultExport && typeof defaultExport === 'object' && 'default' in defaultExport
112
+ ? defaultExport.default
113
+ : defaultExport;
114
+ }
115
+ }
116
+ return sortExportConditions(value);
75
117
  }
76
118
 
77
- const exportFileExists = srcPath.includes('*')
78
- ? true
79
- : await fs.stat(path.join(cwd, srcPath)).then(
80
- (stats) => stats.isFile() || stats.isDirectory(),
81
- () => false,
119
+ return value;
120
+ }
121
+
122
+ /**
123
+ * Applies {@link finalizeConditionValue} to every entry of a conditions map.
124
+ * @param {import('../cli/packageJson').PackageJson.ExportConditions} conditionsMap
125
+ * @param {boolean} addTypes
126
+ * @param {string} kind - Used for error messages, e.g. `export` or `import`.
127
+ * @returns {void}
128
+ */
129
+ function finalizeConditions(conditionsMap, addTypes, kind) {
130
+ for (const key of Object.keys(conditionsMap)) {
131
+ conditionsMap[key] = finalizeConditionValue(conditionsMap[key], addTypes, kind, key);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Returns the path relative to `src/` if `leaf` points inside the source tree
137
+ * (accepting both `./src/…` and `src/…`), otherwise `null`.
138
+ * @param {string} leaf
139
+ * @returns {string | null}
140
+ */
141
+ function srcRelative(leaf) {
142
+ if (leaf.startsWith('./src/')) {
143
+ return leaf.slice('./src/'.length);
144
+ }
145
+ if (leaf.startsWith('src/')) {
146
+ return leaf.slice('src/'.length);
147
+ }
148
+ return null;
149
+ }
150
+
151
+ /**
152
+ * Maps a source-relative path to its built location, optionally swapping the
153
+ * extension. `*` wildcards are preserved verbatim (Node resolves them at runtime).
154
+ * @param {string} rel - Path relative to `src/`.
155
+ * @param {string} dir - The bundle output dir (`.` for the root).
156
+ * @param {string} ext - The current extension (from `path.extname`).
157
+ * @param {string | null} newExt - The replacement extension, or `null` to keep `ext`.
158
+ * @returns {string}
159
+ */
160
+ function buildOutPath(rel, dir, ext, newExt) {
161
+ const dirPrefix = dir === '.' ? '' : `${dir}/`;
162
+ const base = `./${dirPrefix}${rel}`;
163
+ return newExt ? `${base.slice(0, base.length - ext.length)}${newExt}` : base;
164
+ }
165
+
166
+ /**
167
+ * @param {string} target
168
+ * @returns {Promise<boolean>}
169
+ */
170
+ function fileExists(target) {
171
+ return fs.stat(target).then(
172
+ (stats) => stats.isFile(),
173
+ () => false,
174
+ );
175
+ }
176
+
177
+ /**
178
+ * @param {string} target
179
+ * @returns {Promise<boolean>}
180
+ */
181
+ function fileOrDirExists(target) {
182
+ return fs.stat(target).then(
183
+ (stats) => stats.isFile() || stats.isDirectory(),
184
+ () => false,
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Rewrites a single leaf path for every bundle. A source JS/TS path becomes a
190
+ * per-bundle `{ types?, default }` (or bare out-path string when not adding
191
+ * types). A source asset (e.g. `.css`) gets only its `./src/` prefix rewritten.
192
+ * Anything else (a path already in the build output, or a bare specifier) is
193
+ * passed through verbatim — the escape hatch for `--copy`'d/custom paths.
194
+ * @param {string} leaf
195
+ * @param {Object} ctx
196
+ * @param {BundleMeta[]} ctx.bundleMetas
197
+ * @param {boolean} ctx.addTypes
198
+ * @param {string} ctx.cwd
199
+ * @param {string} [ctx.outputDir]
200
+ * @param {string} ctx.key
201
+ * @param {string} ctx.kind
202
+ * @returns {Promise<any>}
203
+ */
204
+ async function rewriteLeaf(leaf, ctx) {
205
+ const { bundleMetas, addTypes, cwd, outputDir, key, kind } = ctx;
206
+ const rel = srcRelative(leaf);
207
+
208
+ if (rel === null) {
209
+ // Not a source path: it already names a path present in the published
210
+ // output (via `--copy` or because it's there literally), or a bare
211
+ // specifier (external package). Emit verbatim.
212
+ if (outputDir && leaf.startsWith('.') && !leaf.includes('*')) {
213
+ const exists = await fileOrDirExists(path.join(outputDir, leaf));
214
+ if (!exists) {
215
+ // `--copy` runs after package.json generation, so the file may legitimately
216
+ // not be on disk yet — warn rather than fail.
217
+ console.warn(
218
+ `The ${kind} path "${leaf}" for "${key}" was not found in the build output. Ensure it is generated or copied into the output directory.`,
219
+ );
220
+ }
221
+ }
222
+ return leaf;
223
+ }
224
+
225
+ const hasGlob = leaf.includes('*');
226
+ if (!hasGlob) {
227
+ const exists = await fileOrDirExists(path.join(cwd, leaf));
228
+ if (!exists) {
229
+ throw new Error(
230
+ `The path "${leaf}" for ${kind} "${key}" does not exist in the package. Either remove the ${kind} or add the file/folder to the package.`,
82
231
  );
83
- if (!exportFileExists) {
232
+ }
233
+ }
234
+
235
+ const ext = path.extname(rel);
236
+ if (!JS_TS_EXTENSIONS.has(ext)) {
237
+ // Asset copied verbatim into the output: only rewrite the `./src/` prefix.
238
+ const meta = bundleMetas.find((bundle) => bundle.dir === '.') ?? bundleMetas[0];
239
+ return buildOutPath(rel, meta.dir, ext, null);
240
+ }
241
+
242
+ /** @type {Record<string, any>} */
243
+ const result = {};
244
+ for (const meta of bundleMetas) {
245
+ const outPath = buildOutPath(rel, meta.dir, ext, meta.outExtension);
246
+ result[meta.condition] = addTypes
247
+ ? { types: buildOutPath(rel, meta.dir, ext, meta.typeOutExtension), default: outPath }
248
+ : outPath;
249
+ }
250
+ return result;
251
+ }
252
+
253
+ /**
254
+ * Recursively rewrites an export/import entry value, rewriting every source-path
255
+ * leaf (including those nested inside standard condition objects) into its built
256
+ * equivalent. Condition keys and their order are preserved (conditions stay
257
+ * outer; the build-owned `import`/`require` split lives at the leaves).
258
+ * @param {import('../cli/packageJson').PackageJson.Exports} value
259
+ * @param {Object} ctx
260
+ * @param {BundleMeta[]} ctx.bundleMetas
261
+ * @param {boolean} ctx.addTypes
262
+ * @param {string} ctx.cwd
263
+ * @param {string} [ctx.outputDir]
264
+ * @param {string} ctx.key
265
+ * @param {string} ctx.kind
266
+ * @returns {Promise<any>}
267
+ */
268
+ async function rewriteEntryValue(value, ctx) {
269
+ if (value === null) {
270
+ return null;
271
+ }
272
+ if (Array.isArray(value)) {
84
273
  throw new Error(
85
- `The import path "${srcPath}" for export "${key}" does not exist in the package. Either remove the export or add the file/folder to the package.`,
274
+ `Array form of package.json ${ctx.kind}s is not supported yet. Found in ${ctx.kind} "${ctx.key}".`,
86
275
  );
87
276
  }
88
- srcPath = srcPath.replace(/\.\/src\//, `./${dir === '.' ? '' : `${dir}/`}`);
89
- const ext = path.extname(srcPath);
277
+ if (typeof value === 'string') {
278
+ return rewriteLeaf(value, ctx);
279
+ }
280
+ if (typeof value === 'object') {
281
+ const entries = Object.entries(value);
282
+ const results = await Promise.all(entries.map(([, child]) => rewriteEntryValue(child, ctx)));
283
+ /** @type {Record<string, any>} */
284
+ const out = {};
285
+ entries.forEach(([condition], index) => {
286
+ out[condition] = results[index];
287
+ });
288
+ return out;
289
+ }
290
+ throw new Error(`Unsupported ${ctx.kind} value for "${ctx.key}".`);
291
+ }
292
+
293
+ /**
294
+ * Rewrites every entry of a conditions map, preserving declared key order.
295
+ * @param {import('../cli/packageJson').PackageJson.ExportConditions} conditionsMap
296
+ * @param {Object} baseCtx - The {@link rewriteEntryValue} context minus `key`.
297
+ * @param {BundleMeta[]} baseCtx.bundleMetas
298
+ * @param {boolean} baseCtx.addTypes
299
+ * @param {string} baseCtx.cwd
300
+ * @param {string} [baseCtx.outputDir]
301
+ * @param {string} baseCtx.kind
302
+ * @returns {Promise<import('../cli/packageJson').PackageJson.ExportConditions>}
303
+ */
304
+ async function rewriteConditionsMap(conditionsMap, baseCtx) {
305
+ const keys = Object.keys(conditionsMap);
306
+ const rewritten = await Promise.all(
307
+ keys.map((key) => rewriteEntryValue(conditionsMap[key], { ...baseCtx, key })),
308
+ );
309
+ /** @type {import('../cli/packageJson').PackageJson.ExportConditions} */
310
+ const result = {};
311
+ keys.forEach((key, index) => {
312
+ result[key] = rewritten[index];
313
+ });
314
+ return result;
315
+ }
316
+
317
+ /**
318
+ * Splits a pattern around its first `*` wildcard.
319
+ * @param {string} pattern
320
+ * @returns {{ prefix: string; suffix: string }}
321
+ */
322
+ function splitStar(pattern) {
323
+ const star = pattern.indexOf('*');
324
+ return { prefix: pattern.slice(0, star), suffix: pattern.slice(star + 1) };
325
+ }
90
326
 
91
- if (ext === '.css') {
92
- newExports[key] = srcPath;
93
- return;
327
+ /**
328
+ * Finds the source glob to expand inside an entry value: the plain string, else
329
+ * the `default` condition, else the first leaf that carries a `*`. The captured
330
+ * stem is applied to every sibling leaf so all conditions expand consistently.
331
+ * @param {import('../cli/packageJson').PackageJson.Exports} value
332
+ * @returns {string | undefined}
333
+ */
334
+ function primarySourcePattern(value) {
335
+ if (typeof value === 'string') {
336
+ return value.includes('*') ? value : undefined;
337
+ }
338
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
339
+ if (typeof value.default === 'string' && value.default.includes('*')) {
340
+ return value.default;
341
+ }
342
+ for (const child of Object.values(value)) {
343
+ const found = primarySourcePattern(child);
344
+ if (found) {
345
+ return found;
346
+ }
347
+ }
94
348
  }
349
+ return undefined;
350
+ }
95
351
 
96
- if (typeof newExports[key] === 'string' || Array.isArray(newExports[key])) {
97
- throw new Error(`The export "${key}" is already defined as a string or Array.`);
352
+ /**
353
+ * Substitutes the matched stem into the `*` of every leaf string in a value.
354
+ * @param {import('../cli/packageJson').PackageJson.Exports} value
355
+ * @param {string} stem
356
+ * @returns {any}
357
+ */
358
+ function substituteStem(value, stem) {
359
+ if (typeof value === 'string') {
360
+ return value.replace('*', stem);
98
361
  }
362
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
363
+ /** @type {Record<string, any>} */
364
+ const out = {};
365
+ for (const [condition, child] of Object.entries(value)) {
366
+ out[condition] = substituteStem(child, stem);
367
+ }
368
+ return out;
369
+ }
370
+ return value;
371
+ }
99
372
 
100
- newExports[key] ??= {};
101
- const exportPath = srcPath.replace(ext, outExtension);
102
- // eslint-disable-next-line no-nested-ternary
103
- newExports[key][type === 'cjs' ? 'require' : 'import'] = addTypes
104
- ? {
105
- ...rest,
106
- types: srcPath.replace(ext, typeOutExtension),
107
- default: exportPath,
108
- }
109
- : Object.keys(rest).length
110
- ? {
111
- ...rest,
112
- default: exportPath,
373
+ /**
374
+ * Drops source-path conditions whose file is missing for a glob-expanded entry,
375
+ * so a sibling glob that doesn't match every stem of the primary pattern omits
376
+ * that condition for the unmatched stems instead of failing the build. Only used
377
+ * on glob-expanded values; explicit (non-glob) entries still validate strictly.
378
+ * @param {import('../cli/packageJson').PackageJson.Exports} value
379
+ * @param {string} cwd
380
+ * @returns {Promise<any>}
381
+ */
382
+ async function pruneMissingSourceConditions(value, cwd) {
383
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
384
+ return value;
385
+ }
386
+ const entries = Object.entries(value);
387
+ const resolved = await Promise.all(
388
+ entries.map(async ([condition, child]) => {
389
+ if (typeof child === 'string') {
390
+ const rel = srcRelative(child);
391
+ if (
392
+ rel !== null &&
393
+ !child.includes('*') &&
394
+ !(await fileOrDirExists(path.join(cwd, child)))
395
+ ) {
396
+ return undefined;
113
397
  }
114
- : exportPath;
398
+ return /** @type {[string, any]} */ ([condition, child]);
399
+ }
400
+ const pruned = await pruneMissingSourceConditions(child, cwd);
401
+ if (
402
+ pruned &&
403
+ typeof pruned === 'object' &&
404
+ !Array.isArray(pruned) &&
405
+ Object.keys(pruned).length === 0
406
+ ) {
407
+ return undefined;
408
+ }
409
+ return /** @type {[string, any]} */ ([condition, pruned]);
410
+ }),
411
+ );
412
+ /** @type {Record<string, any>} */
413
+ const out = {};
414
+ for (const entry of resolved) {
415
+ if (entry) {
416
+ out[entry[0]] = entry[1];
417
+ }
418
+ }
419
+ return out;
420
+ }
421
+
422
+ /**
423
+ * Tests whether a subpath-pattern (or exact) key matches a concrete subpath.
424
+ * @param {string} key
425
+ * @param {string} subpath
426
+ * @returns {boolean}
427
+ */
428
+ function keyMatches(key, subpath) {
429
+ const star = key.indexOf('*');
430
+ if (star === -1) {
431
+ return key === subpath;
432
+ }
433
+ const base = key.slice(0, star);
434
+ const suffix = key.slice(star + 1);
435
+ return (
436
+ subpath.length >= base.length + suffix.length &&
437
+ subpath.startsWith(base) &&
438
+ subpath.endsWith(suffix)
439
+ );
440
+ }
441
+
442
+ /**
443
+ * Selects the most-specific key that matches `subpath`, mirroring Node's
444
+ * resolution: an exact key beats any pattern, and among patterns the longest
445
+ * base (substring before `*`) wins, tie-broken by the longest suffix.
446
+ * @param {string} subpath
447
+ * @param {string[]} keys
448
+ * @returns {string | null}
449
+ */
450
+ function selectMostSpecificKey(subpath, keys) {
451
+ /** @type {string | null} */
452
+ let best = null;
453
+ let bestBase = -1;
454
+ let bestSuffix = -1;
455
+ for (const key of keys) {
456
+ if (!keyMatches(key, subpath)) {
457
+ continue;
458
+ }
459
+ const star = key.indexOf('*');
460
+ const base = star === -1 ? Infinity : star;
461
+ const suffix = star === -1 ? 0 : key.length - star - 1;
462
+ if (base > bestBase || (base === bestBase && suffix > bestSuffix)) {
463
+ best = key;
464
+ bestBase = base;
465
+ bestSuffix = suffix;
466
+ }
467
+ }
468
+ return best;
115
469
  }
116
470
 
117
471
  /**
118
- * Expands glob patterns (containing `*`) in package.json export keys/values
119
- * into concrete entries by resolving them against actual files on disk.
472
+ * Expands glob patterns (containing `*`) in export/import keys into concrete
473
+ * entries resolved against files on disk. Each `*` matches a single path segment
474
+ * (use `--no-expand` to keep the pattern as a Node runtime subpath, whose `*`
475
+ * matches across `/`). Negation (`null`) keys are applied via most-specific-key
476
+ * selection rather than cascade-subtraction, so a deeper positive pattern still
477
+ * resolves under a shallower `null`, and a zero-match pattern warns instead of
478
+ * silently dropping.
120
479
  * @param {import('../cli/packageJson').PackageJson.ExportConditions} originalExports
121
480
  * @param {string} cwd
122
481
  * @returns {Promise<import('../cli/packageJson').PackageJson.ExportConditions>}
123
482
  */
124
483
  async function expandExportGlobs(originalExports, cwd) {
484
+ const allKeys = Object.keys(originalExports);
125
485
  /** @type {import('../cli/packageJson').PackageJson.ExportConditions} */
126
486
  const expandedExports = {};
127
487
 
128
- /**
129
- * @typedef {{
130
- * value: import('../cli/packageJson').PackageJson.Exports;
131
- * srcPattern: string;
132
- * srcPrefix: string;
133
- * srcSuffix: string;
134
- * keyPrefix: string;
135
- * keySuffix: string;
136
- * }} GlobEntry
137
- */
138
-
139
- // Collect entries that need glob expansion
140
- /** @type {GlobEntry[]} */
488
+ /** @type {{ key: string; value: import('../cli/packageJson').PackageJson.Exports; srcPattern: string }[]} */
141
489
  const globEntries = [];
142
490
 
143
- // Collect negation patterns (glob keys with null values)
144
- /** @type {string[]} */
145
- const negationPatterns = [];
146
-
147
491
  for (const [key, value] of Object.entries(originalExports)) {
148
- // Null value acts as a negation/exclusion
149
- if (value === null) {
150
- if (key.includes('*')) {
151
- negationPatterns.push(key);
152
- } else {
153
- delete expandedExports[key];
154
- }
155
- continue;
156
- }
157
-
158
492
  if (!key.includes('*')) {
493
+ // Exact keys (including exact `null` blocks) pass straight through.
159
494
  expandedExports[key] = value;
160
495
  continue;
161
496
  }
162
-
163
- // Extract the source pattern from the value
164
- /** @type {string | undefined} */
165
- let srcPattern;
166
- if (typeof value === 'string') {
167
- srcPattern = value;
168
- } else if (value && typeof value === 'object' && !Array.isArray(value)) {
169
- srcPattern = /** @type {string | undefined} */ (value['mui-src']);
497
+ if (value === null) {
498
+ // Negation pattern: carried through after expansion (see below).
499
+ continue;
170
500
  }
171
-
172
- if (typeof srcPattern !== 'string' || !srcPattern.includes('*')) {
501
+ const srcPattern = primarySourcePattern(value);
502
+ if (typeof srcPattern !== 'string') {
503
+ // Glob key whose value has no wildcard: pass through unchanged.
173
504
  expandedExports[key] = value;
174
505
  continue;
175
506
  }
176
-
177
- // Split patterns around the * wildcard
178
- const srcStarIndex = srcPattern.indexOf('*');
179
- const srcPrefix = srcPattern.substring(0, srcStarIndex);
180
- const srcSuffix = srcPattern.substring(srcStarIndex + 1);
181
-
182
- const keyStarIndex = key.indexOf('*');
183
- const keyPrefix = key.substring(0, keyStarIndex);
184
- const keySuffix = key.substring(keyStarIndex + 1);
185
-
186
- globEntries.push({
187
- value,
188
- srcPattern,
189
- srcPrefix,
190
- srcSuffix,
191
- keyPrefix,
192
- keySuffix,
193
- });
507
+ if (srcPattern.indexOf('*') !== srcPattern.lastIndexOf('*')) {
508
+ console.warn(
509
+ `The pattern "${srcPattern}" for "${key}" contains multiple "*" wildcards; only the first is supported.`,
510
+ );
511
+ }
512
+ globEntries.push({ key, value, srcPattern });
194
513
  }
195
514
 
196
- // Resolve all globby calls in parallel
197
515
  const globResults = await Promise.all(
198
- globEntries.map(({ srcPattern }) => globby(srcPattern, { cwd })),
516
+ globEntries.map(({ srcPattern }) => globby(srcPattern, { cwd, ignore: BASE_IGNORES })),
199
517
  );
200
518
 
519
+ /** @type {Set<string>} */
520
+ const usedNegations = new Set();
521
+ /** @type {string[]} */
522
+ const expandedObjectKeys = [];
523
+
201
524
  for (let i = 0; i < globEntries.length; i += 1) {
202
- const { value, srcPrefix, srcSuffix, keyPrefix, keySuffix } = globEntries[i];
203
- const matches = globResults[i];
525
+ const { key, value, srcPattern } = globEntries[i];
526
+ const { prefix: srcPrefix, suffix: srcSuffix } = splitStar(srcPattern);
527
+ const { prefix: keyPrefix, suffix: keySuffix } = splitStar(key);
204
528
 
205
529
  const stems = [];
206
- for (const match of matches) {
530
+ for (const match of globResults[i]) {
207
531
  if (match.startsWith(srcPrefix) && match.endsWith(srcSuffix)) {
208
- const stem =
209
- srcSuffix.length > 0
210
- ? match.substring(srcPrefix.length, match.length - srcSuffix.length)
211
- : match.substring(srcPrefix.length);
212
- if (stem.length > 0) {
532
+ const stem = match.slice(srcPrefix.length, match.length - srcSuffix.length);
533
+ if (stem) {
213
534
  stems.push(stem);
214
535
  }
215
536
  }
216
537
  }
217
-
218
538
  stems.sort();
219
539
 
220
- for (const stem of stems) {
221
- const expandedKey = `${keyPrefix}${stem}${keySuffix}`;
222
- const expandedSrcPath = `${srcPrefix}${stem}${srcSuffix}`;
540
+ if (stems.length === 0) {
541
+ console.warn(`No files matched the pattern "${srcPattern}" for "${key}".`);
542
+ continue;
543
+ }
223
544
 
224
- if (typeof value === 'string') {
225
- expandedExports[expandedKey] = expandedSrcPath;
226
- } else {
227
- expandedExports[expandedKey] = {
228
- ...value,
229
- 'mui-src': expandedSrcPath,
230
- };
545
+ for (const stem of stems) {
546
+ const concreteKey = `${keyPrefix}${stem}${keySuffix}`;
547
+ // Honour Node's most-specific-wins resolution: only emit this concrete
548
+ // entry when this positive pattern is the most-specific match. A deeper
549
+ // positive pattern emits its own entries; a `null` blocks it entirely.
550
+ const winner = selectMostSpecificKey(concreteKey, allKeys);
551
+ if (winner !== key) {
552
+ if (winner !== null && originalExports[winner] === null) {
553
+ usedNegations.add(winner);
554
+ }
555
+ continue;
556
+ }
557
+ expandedExports[concreteKey] = substituteStem(value, stem);
558
+ if (value && typeof value === 'object') {
559
+ expandedObjectKeys.push(concreteKey);
231
560
  }
232
561
  }
233
562
  }
234
563
 
235
- // Apply negation patterns: remove any expanded keys that match a null-valued glob.
236
- // If no keys matched, preserve the pattern itself with null to block that path at runtime.
237
- for (const pattern of negationPatterns) {
238
- let matched = false;
239
- for (const expandedKey of Object.keys(expandedExports)) {
240
- if (minimatch(expandedKey, pattern)) {
241
- delete expandedExports[expandedKey];
242
- matched = true;
243
- }
244
- }
245
- if (!matched) {
246
- expandedExports[pattern] = null;
564
+ // For glob-expanded condition objects, drop any condition whose source file is
565
+ // absent for this stem (a sibling glob need not match every primary stem).
566
+ await Promise.all(
567
+ expandedObjectKeys.map(async (concreteKey) => {
568
+ expandedExports[concreteKey] = await pruneMissingSourceConditions(
569
+ expandedExports[concreteKey],
570
+ cwd,
571
+ );
572
+ }),
573
+ );
574
+
575
+ // Carry through negation patterns that didn't claim any expanded entry so they
576
+ // still block their subtree at runtime.
577
+ for (const [key, value] of Object.entries(originalExports)) {
578
+ if (value === null && key.includes('*') && !usedNegations.has(key)) {
579
+ expandedExports[key] = null;
247
580
  }
248
581
  }
249
582
 
@@ -251,24 +584,36 @@ async function expandExportGlobs(originalExports, cwd) {
251
584
  }
252
585
 
253
586
  /**
254
- * @param {Object} param0
255
- * @param {import('../cli/packageJson').PackageJson['exports']} param0.exports
256
- * @param {{type: BundleType; dir: string}[]} param0.bundles
257
- * @param {string} param0.outputDir
258
- * @param {string} param0.cwd
259
- * @param {boolean} [param0.addTypes]
260
- * @param {boolean} [param0.isFlat]
261
- * @param {'module' | 'commonjs'} [param0.packageType]
587
+ * Builds the per-bundle metadata (output extensions + the `import`/`require`
588
+ * condition each bundle maps to).
589
+ * @param {BundleType[]} bundles
590
+ * @param {'module' | 'commonjs'} packageType
591
+ * @returns {BundleMeta[]}
592
+ */
593
+ function createBundleMetas(bundles, packageType) {
594
+ return bundles.map((type) => ({
595
+ type,
596
+ dir: '.',
597
+ condition: type === 'cjs' ? 'require' : 'import',
598
+ outExtension: getOutExtension(type, { packageType }),
599
+ typeOutExtension: getOutExtension(type, { isType: true, packageType }),
600
+ }));
601
+ }
602
+
603
+ /**
604
+ * @param {import('../cli/packageJson').PackageJson['exports']} packageExports
605
+ * @param {Object} options
606
+ * @param {BundleType[]} options.bundles
607
+ * @param {string} options.outputDir
608
+ * @param {string} options.cwd
609
+ * @param {boolean} [options.addTypes]
610
+ * @param {boolean} [options.expand] - Whether to enumerate glob patterns into concrete entries.
611
+ * @param {'module' | 'commonjs'} [options.packageType]
262
612
  */
263
- export async function createPackageExports({
264
- exports: packageExports,
265
- bundles,
266
- outputDir,
267
- cwd,
268
- addTypes = false,
269
- isFlat = false,
270
- packageType = 'commonjs',
271
- }) {
613
+ export async function createPackageExports(
614
+ packageExports,
615
+ { bundles, outputDir, cwd, addTypes = false, expand = true, packageType = 'commonjs' },
616
+ ) {
272
617
  const resolvedPackageType = packageType === 'module' ? 'module' : 'commonjs';
273
618
  /**
274
619
  * @type {import('../cli/packageJson').PackageJson.ExportConditions}
@@ -277,7 +622,8 @@ export async function createPackageExports({
277
622
  typeof packageExports === 'string' || Array.isArray(packageExports)
278
623
  ? { '.': packageExports }
279
624
  : packageExports || {};
280
- const originalExports = isFlat ? await expandExportGlobs(rawExports, cwd) : rawExports;
625
+ const originalExports = expand ? await expandExportGlobs(rawExports, cwd) : rawExports;
626
+ const bundleMetas = createBundleMetas(bundles, resolvedPackageType);
281
627
  /**
282
628
  * @type {import('../cli/packageJson').PackageJson.ExportConditions}
283
629
  */
@@ -291,124 +637,124 @@ export async function createPackageExports({
291
637
  exports: newExports,
292
638
  };
293
639
 
640
+ // Derive the `.` index entry (plus `main`/`types`) from the built index files.
294
641
  await Promise.all(
295
- bundles.map(async ({ type, dir }) => {
296
- const outExtension = getOutExtension(type, {
297
- isFlat,
298
- packageType: resolvedPackageType,
299
- });
300
- const typeOutExtension = getOutExtension(type, {
301
- isFlat,
302
- isType: true,
303
- packageType: resolvedPackageType,
304
- });
305
- const indexFileExists = await fs.stat(path.join(outputDir, dir, `index${outExtension}`)).then(
306
- (stats) => stats.isFile(),
307
- () => false,
642
+ bundleMetas.map(async (meta) => {
643
+ const indexFileExists = await fileExists(
644
+ path.join(outputDir, meta.dir, `index${meta.outExtension}`),
308
645
  );
309
646
  const typeFileExists =
310
647
  addTypes &&
311
- (await fs.stat(path.join(outputDir, dir, `index${typeOutExtension}`)).then(
312
- (stats) => stats.isFile(),
313
- () => false,
314
- ));
315
- const dirPrefix = dir === '.' ? '' : `${dir}/`;
316
- const exportDir = `./${dirPrefix}index${outExtension}`;
317
- const typeExportDir = `./${dirPrefix}index${typeOutExtension}`;
648
+ (await fileExists(path.join(outputDir, meta.dir, `index${meta.typeOutExtension}`)));
649
+ const dirPrefix = meta.dir === '.' ? '' : `${meta.dir}/`;
650
+ const exportDir = `./${dirPrefix}index${meta.outExtension}`;
651
+ const typeExportDir = `./${dirPrefix}index${meta.typeOutExtension}`;
318
652
 
319
653
  if (indexFileExists) {
320
654
  // skip `packageJson.module` to support parcel and some older bundlers
321
- if (type === 'cjs') {
655
+ if (meta.type === 'cjs') {
322
656
  result.main = exportDir;
323
657
  }
324
-
325
658
  if (typeof newExports['.'] === 'string' || Array.isArray(newExports['.'])) {
326
659
  throw new Error(`The export "." is already defined as a string or Array.`);
327
660
  }
328
-
329
661
  newExports['.'] ??= {};
330
- newExports['.'][type === 'cjs' ? 'require' : 'import'] = typeFileExists
331
- ? {
332
- types: typeExportDir,
333
- default: exportDir,
334
- }
662
+ newExports['.'][meta.condition] = typeFileExists
663
+ ? { types: typeExportDir, default: exportDir }
335
664
  : exportDir;
336
665
  }
337
- if (typeFileExists && type === 'cjs') {
666
+ if (typeFileExists && meta.type === 'cjs') {
338
667
  result.types = typeExportDir;
339
668
  }
340
- const exportKeys = Object.keys(originalExports);
341
- // need to maintain the order of exports
342
- for (const key of exportKeys) {
343
- const importPath = originalExports[key];
344
- if (!importPath) {
345
- newExports[key] = null;
346
- continue;
347
- }
348
- // eslint-disable-next-line no-await-in-loop
349
- await createExportsFor({
350
- importPath,
351
- key,
352
- cwd,
353
- dir,
354
- type,
355
- newExports,
356
- typeOutExtension,
357
- outExtension,
358
- addTypes,
359
- });
360
- }
361
669
  }),
362
670
  );
363
671
 
364
- bundles.forEach(({ dir }) => {
365
- if (dir !== '.') {
366
- newExports[`./${dir}`] = null;
367
- }
368
- });
672
+ // Rewrite the user-configured entries, preserving their declared order.
673
+ Object.assign(
674
+ newExports,
675
+ await rewriteConditionsMap(originalExports, {
676
+ bundleMetas,
677
+ addTypes,
678
+ cwd,
679
+ outputDir,
680
+ kind: 'export',
681
+ }),
682
+ );
369
683
 
370
- // Transform import/require to default/require pattern
371
- Object.keys(newExports).forEach((key) => {
372
- const exportVal = newExports[key];
373
- if (Array.isArray(exportVal)) {
684
+ finalizeConditions(newExports, addTypes, 'export');
685
+
686
+ return result;
687
+ }
688
+
689
+ /**
690
+ * Generates the package.json `imports` field for the built package, rewriting
691
+ * internal subpath imports (keys starting with `#`) that point at source files
692
+ * into their built equivalents with `import`/`require`/`types` conditions.
693
+ *
694
+ * Mirrors {@link createPackageExports} but without the `.` / `package.json` /
695
+ * `main` / `types` index handling that only applies to public exports. Entries
696
+ * that resolve to bare specifiers (e.g. an external package) are passed through
697
+ * unchanged, since the `imports` field commonly aliases dependencies.
698
+ * @param {import('../cli/packageJson').PackageJson['imports']} packageImports
699
+ * @param {Object} options
700
+ * @param {BundleType[]} options.bundles
701
+ * @param {string} options.cwd
702
+ * @param {string} [options.outputDir] - Used to verify non-source passthrough paths exist in the build output.
703
+ * @param {boolean} [options.addTypes]
704
+ * @param {boolean} [options.expand] - Whether to enumerate glob patterns into concrete entries.
705
+ * @param {'module' | 'commonjs'} [options.packageType]
706
+ * @returns {Promise<import('../cli/packageJson').PackageJson.Imports | undefined>}
707
+ */
708
+ export async function createPackageImports(
709
+ packageImports,
710
+ { bundles, cwd, outputDir, addTypes = false, expand = true, packageType = 'commonjs' },
711
+ ) {
712
+ if (!packageImports || Object.keys(packageImports).length === 0) {
713
+ return undefined;
714
+ }
715
+ for (const key of Object.keys(packageImports)) {
716
+ if (!key.startsWith('#')) {
374
717
  throw new Error(
375
- `Array form of package.json exports is not supported yet. Found in export "${key}".`,
718
+ `Invalid import "${key}": all package.json "imports" keys must start with "#".`,
376
719
  );
377
720
  }
378
- if (exportVal && typeof exportVal === 'object' && (exportVal.import || exportVal.require)) {
379
- // Use ESM (import) for default if available, otherwise use require
380
- const defaultExport = exportVal.import || exportVal.require;
381
-
382
- if (addTypes) {
383
- exportVal.default = defaultExport;
384
- } else {
385
- exportVal.default =
386
- defaultExport && typeof defaultExport === 'object' && 'default' in defaultExport
387
- ? defaultExport.default
388
- : defaultExport;
389
- }
390
- }
721
+ }
722
+ const resolvedPackageType = packageType === 'module' ? 'module' : 'commonjs';
723
+ // `Imports` uses `#`-prefixed keys; treat it as a generic conditions map so the
724
+ // same glob/condition helpers used for exports can process it.
725
+ const rawImports = /** @type {import('../cli/packageJson').PackageJson.ExportConditions} */ (
726
+ packageImports
727
+ );
728
+ const originalImports = expand ? await expandExportGlobs(rawImports, cwd) : rawImports;
729
+ const bundleMetas = createBundleMetas(bundles, resolvedPackageType);
730
+
731
+ const newImports = await rewriteConditionsMap(originalImports, {
732
+ bundleMetas,
733
+ addTypes,
734
+ cwd,
735
+ outputDir,
736
+ kind: 'import',
391
737
  });
392
738
 
393
- return result;
739
+ finalizeConditions(newImports, addTypes, 'import');
740
+
741
+ return newImports;
394
742
  }
395
743
 
396
744
  /**
397
- * @param {Object} param0
398
- * @param {import('../cli/packageJson').PackageJson['bin']} param0.bin
399
- * @param {{type: BundleType; dir: string}[]} param0.bundles
400
- * @param {string} param0.cwd
401
- * @param {boolean} [param0.isFlat]
402
- * @param {'module' | 'commonjs'} [param0.packageType]
745
+ * @param {import('../cli/packageJson').PackageJson['bin']} bin
746
+ * @param {Object} options
747
+ * @param {BundleType[]} options.bundles
748
+ * @param {string} options.cwd
749
+ * @param {'module' | 'commonjs'} [options.packageType]
403
750
  */
404
- export async function createPackageBin({ bin, bundles, cwd, isFlat = false, packageType }) {
751
+ export async function createPackageBin(bin, { bundles, cwd, packageType }) {
405
752
  if (!bin) {
406
753
  return undefined;
407
754
  }
408
755
  // Use mjs files if present, otherwise fallback to the first bundle type
409
- const bundleToUse = bundles.find((b) => b.type === 'esm') || bundles[0];
410
- const binOutExtension = getOutExtension(bundleToUse.type, {
411
- isFlat,
756
+ const bundleToUse = bundles.find((type) => type === 'esm') || bundles[0];
757
+ const binOutExtension = getOutExtension(bundleToUse, {
412
758
  packageType,
413
759
  });
414
760
 
@@ -436,7 +782,7 @@ export async function createPackageBin({ bin, bundles, cwd, isFlat = false, pack
436
782
  }
437
783
  const ext = path.extname(binPath);
438
784
  newBin[binKey] = binPath
439
- .replace(/(\.\/)?src\//, bundleToUse.dir === '.' ? './' : `./${bundleToUse.dir}/`)
785
+ .replace(/(\.\/)?src\//, './')
440
786
  .replace(new RegExp(`\\${ext}$`), binOutExtension);
441
787
  }
442
788
  // eslint-disable-next-line no-underscore-dangle
@@ -549,18 +895,6 @@ export function measureFn(label) {
549
895
  return performance.measure(label, startMark, endMark);
550
896
  }
551
897
 
552
- export const BASE_IGNORES = [
553
- '**/*.test.js',
554
- '**/*.test.ts',
555
- '**/*.test.tsx',
556
- '**/*.spec.js',
557
- '**/*.spec.ts',
558
- '**/*.spec.tsx',
559
- '**/*.d.ts',
560
- '**/*.test/*.*',
561
- '**/test-cases/*.*',
562
- ];
563
-
564
898
  /**
565
899
  * A utility to map a function over an array of items in a worker pool.
566
900
  *