@mui/internal-code-infra 0.0.4-canary.73 → 0.0.4-canary.75

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 (52) hide show
  1. package/package.json +7 -5
  2. package/src/brokenLinksChecker/crawlWorker.mjs +23 -0
  3. package/src/cli/cmdPublish.mjs +32 -3
  4. package/src/brokenLinksChecker/__fixtures__/static-site/broken-links.html +0 -20
  5. package/src/brokenLinksChecker/__fixtures__/static-site/broken-targets.html +0 -22
  6. package/src/brokenLinksChecker/__fixtures__/static-site/example.md +0 -20
  7. package/src/brokenLinksChecker/__fixtures__/static-site/external-links.html +0 -21
  8. package/src/brokenLinksChecker/__fixtures__/static-site/ignored-page.html +0 -17
  9. package/src/brokenLinksChecker/__fixtures__/static-site/index.html +0 -29
  10. package/src/brokenLinksChecker/__fixtures__/static-site/invalid-html.html +0 -15
  11. package/src/brokenLinksChecker/__fixtures__/static-site/known-targets.json +0 -5
  12. package/src/brokenLinksChecker/__fixtures__/static-site/nested/page.html +0 -21
  13. package/src/brokenLinksChecker/__fixtures__/static-site/orphaned-page.html +0 -20
  14. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-api-links.html +0 -20
  15. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-custom-targets.html +0 -24
  16. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-ignored-content.html +0 -28
  17. package/src/brokenLinksChecker/__fixtures__/static-site/page-with-known-target-links.html +0 -19
  18. package/src/brokenLinksChecker/__fixtures__/static-site/unclosed-tags.html +0 -1
  19. package/src/brokenLinksChecker/__fixtures__/static-site/valid.html +0 -20
  20. package/src/brokenLinksChecker/__fixtures__/static-site/with-anchors.html +0 -31
  21. package/src/brokenLinksChecker/index.test.ts +0 -301
  22. package/src/changelog/categorizeCommits.test.ts +0 -319
  23. package/src/changelog/filterCommits.test.ts +0 -363
  24. package/src/changelog/parseCommitLabels.test.ts +0 -509
  25. package/src/changelog/renderChangelog.test.ts +0 -378
  26. package/src/changelog/sortSections.test.ts +0 -199
  27. package/src/cli/cmdVale.test.mjs +0 -644
  28. package/src/eslint/mui/rules/add-undef-to-optional.test.mjs +0 -361
  29. package/src/eslint/mui/rules/consistent-production-guard.test.mjs +0 -162
  30. package/src/eslint/mui/rules/disallow-active-elements-as-key-event-target.test.mjs +0 -66
  31. package/src/eslint/mui/rules/disallow-react-api-in-server-components.test.mjs +0 -305
  32. package/src/eslint/mui/rules/docgen-ignore-before-comment.test.mjs +0 -52
  33. package/src/eslint/mui/rules/flatten-parentheses.test.mjs +0 -245
  34. package/src/eslint/mui/rules/mui-name-matches-component-name.test.mjs +0 -247
  35. package/src/eslint/mui/rules/no-empty-box.test.mjs +0 -40
  36. package/src/eslint/mui/rules/no-floating-cleanup.test.mjs +0 -141
  37. package/src/eslint/mui/rules/no-guarded-throw.test.mjs +0 -206
  38. package/src/eslint/mui/rules/no-presentation-role.test.mjs +0 -33
  39. package/src/eslint/mui/rules/no-styled-box.test.mjs +0 -73
  40. package/src/eslint/mui/rules/require-dev-wrapper.test.mjs +0 -265
  41. package/src/eslint/mui/rules/rules-of-use-theme-variants.test.mjs +0 -149
  42. package/src/eslint/mui/rules/straight-quotes.test.mjs +0 -67
  43. package/src/remark/firstBlockHeading.test.mjs +0 -107
  44. package/src/remark/gitDiff.test.mjs +0 -45
  45. package/src/remark/noSpaceInLinks.test.mjs +0 -22
  46. package/src/remark/straightQuotes.test.mjs +0 -25
  47. package/src/remark/tableAlignment.test.mjs +0 -28
  48. package/src/remark/terminalLanguage.test.mjs +0 -17
  49. package/src/utils/build.test.mjs +0 -1263
  50. package/src/utils/pnpm.test.mjs +0 -731
  51. package/src/utils/template.test.mjs +0 -133
  52. package/src/utils/typescript.test.mjs +0 -341
@@ -1,1263 +0,0 @@
1
- import * as fs from 'node:fs/promises';
2
- import * as path from 'node:path';
3
- import { describe, expect, it, vi } from 'vitest';
4
-
5
- import { makeTempDir } from './testUtils.mjs';
6
- import { createPackageBin, createPackageExports, createPackageImports } from './build.mjs';
7
-
8
- /**
9
- * @param {string} filePath
10
- * @param {string} [contents]
11
- */
12
- async function createFile(filePath, contents = '') {
13
- await fs.mkdir(path.dirname(filePath), { recursive: true });
14
- await fs.writeFile(filePath, contents, 'utf8');
15
- }
16
-
17
- describe('createPackageExports', () => {
18
- it('always puts import before require regardless of bundle array order', async () => {
19
- const cwd = await makeTempDir();
20
- const outputDir = path.join(cwd, 'build');
21
-
22
- await Promise.all([
23
- createFile(path.join(cwd, 'src/index.ts')),
24
- createFile(path.join(cwd, 'src/feature.ts')),
25
- createFile(path.join(outputDir, 'index.js')),
26
- createFile(path.join(outputDir, 'index.cjs')),
27
- createFile(path.join(outputDir, 'feature.js')),
28
- createFile(path.join(outputDir, 'feature.cjs')),
29
- ]);
30
-
31
- // Pass cjs before esm to verify the key order is still 'import' then 'require'
32
- const { exports: packageExports } = await createPackageExports(
33
- {
34
- '.': './src/index.ts',
35
- './feature': './src/feature.ts',
36
- },
37
- {
38
- bundles: ['cjs', 'esm'],
39
- outputDir,
40
- cwd,
41
- packageType: 'module',
42
- },
43
- );
44
-
45
- expect(Object.keys(/** @type {Record<string, unknown>} */ (packageExports['.']))).toEqual([
46
- 'import',
47
- 'require',
48
- 'default',
49
- ]);
50
- expect(
51
- Object.keys(/** @type {Record<string, unknown>} */ (packageExports['./feature'])),
52
- ).toEqual(['import', 'require', 'default']);
53
- });
54
-
55
- it('creates exports for a dual bundle module package', async () => {
56
- const cwd = await makeTempDir();
57
- const outputDir = path.join(cwd, 'build');
58
- /**
59
- * @type {import('./build.mjs').BundleType[]}
60
- */
61
- const bundles = ['esm', 'cjs'];
62
-
63
- await Promise.all([
64
- createFile(path.join(cwd, 'src/index.ts')),
65
- createFile(path.join(cwd, 'src/feature.ts')),
66
-
67
- // Create output files for index
68
- createFile(path.join(outputDir, `index.js`)),
69
- createFile(path.join(outputDir, `index.cjs`)),
70
- createFile(path.join(outputDir, `index.d.ts`)),
71
- createFile(path.join(outputDir, `index.d.cts`)),
72
-
73
- // Create output files for feature
74
- createFile(path.join(outputDir, `feature.js`)),
75
- createFile(path.join(outputDir, `feature.cjs`)),
76
- createFile(path.join(outputDir, `feature.d.ts`)),
77
- createFile(path.join(outputDir, `feature.d.cts`)),
78
- ]);
79
-
80
- const {
81
- exports: packageExports,
82
- main,
83
- types,
84
- } = await createPackageExports(
85
- {
86
- '.': './src/index.ts',
87
- './feature': './src/feature.ts',
88
- },
89
- {
90
- bundles,
91
- outputDir,
92
- cwd,
93
- addTypes: true,
94
- packageType: 'module',
95
- },
96
- );
97
-
98
- expect(main).toBe('./index.cjs');
99
- expect(types).toBe('./index.d.cts');
100
-
101
- expect(packageExports['.']).toEqual({
102
- import: { types: './index.d.ts', default: './index.js' },
103
- require: { types: './index.d.cts', default: './index.cjs' },
104
- default: {
105
- types: './index.d.ts',
106
- default: './index.js',
107
- },
108
- });
109
- expect(packageExports['./feature']).toEqual({
110
- import: { types: './feature.d.ts', default: './feature.js' },
111
- require: { types: './feature.d.cts', default: './feature.cjs' },
112
- default: {
113
- types: './feature.d.ts',
114
- default: './feature.js',
115
- },
116
- });
117
-
118
- const {
119
- exports: packageExports2,
120
- main: main2,
121
- types: types2,
122
- } = await createPackageExports(
123
- {
124
- '.': './src/index.ts',
125
- './feature': './src/feature.ts',
126
- },
127
- {
128
- bundles: [bundles[1]], // only CJS bundle
129
- outputDir,
130
- cwd,
131
- addTypes: true,
132
- },
133
- );
134
-
135
- expect(main2).toBe('./index.js');
136
- expect(types2).toBe('./index.d.ts');
137
-
138
- expect(packageExports2['.']).toEqual({
139
- require: { types: './index.d.ts', default: './index.js' },
140
- default: {
141
- types: './index.d.ts',
142
- default: './index.js',
143
- },
144
- });
145
- expect(packageExports2['./feature']).toEqual({
146
- require: { types: './feature.d.ts', default: './feature.js' },
147
- default: {
148
- types: './feature.d.ts',
149
- default: './feature.js',
150
- },
151
- });
152
- });
153
-
154
- describe('glob expansion', () => {
155
- it('expands glob patterns in export keys and values', async () => {
156
- const cwd = await makeTempDir();
157
- const outputDir = path.join(cwd, 'build');
158
- /**
159
- * @type {import('./build.mjs').BundleType[]}
160
- */
161
- const bundles = ['esm', 'cjs'];
162
-
163
- await Promise.all([
164
- createFile(path.join(cwd, 'src/Button.ts')),
165
- createFile(path.join(cwd, 'src/TextField.ts')),
166
-
167
- // Output files
168
- createFile(path.join(outputDir, 'Button.js')),
169
- createFile(path.join(outputDir, 'Button.cjs')),
170
- createFile(path.join(outputDir, 'Button.d.ts')),
171
- createFile(path.join(outputDir, 'Button.d.cts')),
172
- createFile(path.join(outputDir, 'TextField.js')),
173
- createFile(path.join(outputDir, 'TextField.cjs')),
174
- createFile(path.join(outputDir, 'TextField.d.ts')),
175
- createFile(path.join(outputDir, 'TextField.d.cts')),
176
- ]);
177
-
178
- const { exports: packageExports } = await createPackageExports(
179
- {
180
- './*': './src/*.ts',
181
- },
182
- {
183
- bundles,
184
- outputDir,
185
- cwd,
186
- addTypes: true,
187
- packageType: 'module',
188
- },
189
- );
190
-
191
- expect(packageExports['./Button']).toEqual({
192
- import: { types: './Button.d.ts', default: './Button.js' },
193
- require: { types: './Button.d.cts', default: './Button.cjs' },
194
- default: { types: './Button.d.ts', default: './Button.js' },
195
- });
196
- expect(packageExports['./TextField']).toEqual({
197
- import: { types: './TextField.d.ts', default: './TextField.js' },
198
- require: { types: './TextField.d.cts', default: './TextField.cjs' },
199
- default: { types: './TextField.d.ts', default: './TextField.js' },
200
- });
201
- // glob key should not appear in the output
202
- expect(packageExports['./*']).toBeUndefined();
203
- });
204
-
205
- it('expands glob with commonjs package type', async () => {
206
- const cwd = await makeTempDir();
207
- const outputDir = path.join(cwd, 'build');
208
- /**
209
- * @type {import('./build.mjs').BundleType[]}
210
- */
211
- const bundles = ['esm', 'cjs'];
212
-
213
- await Promise.all([
214
- createFile(path.join(cwd, 'src/Button.ts')),
215
-
216
- createFile(path.join(outputDir, 'Button.js')),
217
- createFile(path.join(outputDir, 'Button.mjs')),
218
- createFile(path.join(outputDir, 'Button.d.ts')),
219
- createFile(path.join(outputDir, 'Button.d.mts')),
220
- ]);
221
-
222
- const { exports: packageExports } = await createPackageExports(
223
- {
224
- './*': './src/*.ts',
225
- },
226
- {
227
- bundles,
228
- outputDir,
229
- cwd,
230
- addTypes: true,
231
- packageType: 'commonjs',
232
- },
233
- );
234
-
235
- expect(packageExports['./Button']).toEqual({
236
- import: { types: './Button.d.mts', default: './Button.mjs' },
237
- require: { types: './Button.d.ts', default: './Button.js' },
238
- default: { types: './Button.d.mts', default: './Button.mjs' },
239
- });
240
- });
241
-
242
- it('expands glob with single CJS bundle', async () => {
243
- const cwd = await makeTempDir();
244
- const outputDir = path.join(cwd, 'build');
245
-
246
- await Promise.all([
247
- createFile(path.join(cwd, 'src/Button.ts')),
248
-
249
- createFile(path.join(outputDir, 'Button.js')),
250
- createFile(path.join(outputDir, 'Button.d.ts')),
251
- ]);
252
-
253
- const { exports: packageExports } = await createPackageExports(
254
- {
255
- './*': './src/*.ts',
256
- },
257
- {
258
- bundles: ['cjs'],
259
- outputDir,
260
- cwd,
261
- addTypes: true,
262
- packageType: 'commonjs',
263
- },
264
- );
265
-
266
- expect(packageExports['./Button']).toEqual({
267
- require: { types: './Button.d.ts', default: './Button.js' },
268
- default: { types: './Button.d.ts', default: './Button.js' },
269
- });
270
- });
271
-
272
- it('rewrites source paths inside sibling conditions when expanding globs', async () => {
273
- const cwd = await makeTempDir();
274
- const outputDir = path.join(cwd, 'build');
275
- /**
276
- * @type {import('./build.mjs').BundleType[]}
277
- */
278
- const bundles = ['esm', 'cjs'];
279
-
280
- await Promise.all([
281
- createFile(path.join(cwd, 'src/Alert.ts')),
282
- createFile(path.join(cwd, 'src/node/Alert.ts')),
283
- ]);
284
-
285
- const { exports: packageExports } = await createPackageExports(
286
- {
287
- './*': { node: './src/node/*.ts', default: './src/*.ts' },
288
- },
289
- {
290
- bundles,
291
- outputDir,
292
- cwd,
293
- packageType: 'module',
294
- },
295
- );
296
-
297
- // Every condition's source path is rewritten (no verbatim `./src/...`),
298
- // and conditions stay outer with the import/require split at the leaves.
299
- expect(packageExports['./Alert']).toEqual({
300
- node: {
301
- import: './node/Alert.js',
302
- require: './node/Alert.cjs',
303
- default: './node/Alert.js',
304
- },
305
- default: {
306
- import: './Alert.js',
307
- require: './Alert.cjs',
308
- default: './Alert.js',
309
- },
310
- });
311
- });
312
-
313
- it('omits a sibling condition for stems its glob does not match', async () => {
314
- const cwd = await makeTempDir();
315
- const outputDir = path.join(cwd, 'build');
316
- /**
317
- * @type {import('./build.mjs').BundleType[]}
318
- */
319
- const bundles = ['esm', 'cjs'];
320
-
321
- await Promise.all([
322
- createFile(path.join(cwd, 'src/Alert.ts')),
323
- createFile(path.join(cwd, 'src/Button.ts')),
324
- // Only Alert has a node variant.
325
- createFile(path.join(cwd, 'src/node/Alert.ts')),
326
- ]);
327
-
328
- const { exports: packageExports } = await createPackageExports(
329
- {
330
- './*': { node: './src/node/*.ts', default: './src/*.ts' },
331
- },
332
- {
333
- bundles,
334
- outputDir,
335
- cwd,
336
- packageType: 'module',
337
- },
338
- );
339
-
340
- // Alert keeps its node condition.
341
- expect(packageExports['./Alert']).toEqual({
342
- node: {
343
- import: './node/Alert.js',
344
- require: './node/Alert.cjs',
345
- default: './node/Alert.js',
346
- },
347
- default: { import: './Alert.js', require: './Alert.cjs', default: './Alert.js' },
348
- });
349
- // Button has no node variant, so the node condition is dropped rather than
350
- // pointing at a non-existent source (which would fail the build).
351
- expect(packageExports['./Button']).toEqual({
352
- default: { import: './Button.js', require: './Button.cjs', default: './Button.js' },
353
- });
354
- });
355
-
356
- it('enumerates a wildcard directory pattern', async () => {
357
- const cwd = await makeTempDir();
358
- const outputDir = path.join(cwd, 'build');
359
-
360
- await Promise.all([
361
- createFile(path.join(cwd, 'src/tabs/script.ts')),
362
- createFile(path.join(cwd, 'src/menu/script.ts')),
363
- ]);
364
-
365
- const { exports: packageExports } = await createPackageExports(
366
- {
367
- './*': './src/*/script.ts',
368
- },
369
- {
370
- bundles: ['cjs'],
371
- outputDir,
372
- cwd,
373
- },
374
- );
375
-
376
- expect(packageExports['./tabs']).toEqual({
377
- require: './tabs/script.js',
378
- default: './tabs/script.js',
379
- });
380
- expect(packageExports['./menu']).toEqual({
381
- require: './menu/script.js',
382
- default: './menu/script.js',
383
- });
384
- });
385
-
386
- it('warns and produces nothing when a glob matches no files', async () => {
387
- const cwd = await makeTempDir();
388
- const outputDir = path.join(cwd, 'build');
389
- createFile(path.join(cwd, 'src/.gitkeep'));
390
-
391
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
392
- try {
393
- const { exports: packageExports } = await createPackageExports(
394
- {
395
- './*': './src/*.ts',
396
- },
397
- {
398
- bundles: ['cjs'],
399
- outputDir,
400
- cwd,
401
- },
402
- );
403
-
404
- expect(Object.keys(packageExports)).toEqual(['./package.json']);
405
- expect(warn).toHaveBeenCalled();
406
- } finally {
407
- warn.mockRestore();
408
- }
409
- });
410
-
411
- it('mixes glob and non-glob exports', async () => {
412
- const cwd = await makeTempDir();
413
- const outputDir = path.join(cwd, 'build');
414
- /**
415
- * @type {import('./build.mjs').BundleType[]}
416
- */
417
- const bundles = ['esm', 'cjs'];
418
-
419
- await Promise.all([
420
- createFile(path.join(cwd, 'src/index.ts')),
421
- createFile(path.join(cwd, 'src/Chip.ts')),
422
-
423
- createFile(path.join(outputDir, 'index.js')),
424
- createFile(path.join(outputDir, 'index.cjs')),
425
- createFile(path.join(outputDir, 'Chip.js')),
426
- createFile(path.join(outputDir, 'Chip.cjs')),
427
- ]);
428
-
429
- const { exports: packageExports } = await createPackageExports(
430
- {
431
- '.': './src/index.ts',
432
- './*': './src/*.ts',
433
- },
434
- {
435
- bundles,
436
- outputDir,
437
- cwd,
438
- packageType: 'module',
439
- },
440
- );
441
-
442
- // Explicit export still works
443
- expect(packageExports['.']).toBeDefined();
444
- // Glob-expanded export
445
- expect(packageExports['./Chip']).toEqual({
446
- import: './Chip.js',
447
- require: './Chip.cjs',
448
- default: './Chip.js',
449
- });
450
- });
451
-
452
- it('expands glob with subdirectory pattern', async () => {
453
- const cwd = await makeTempDir();
454
- const outputDir = path.join(cwd, 'build');
455
- /**
456
- * @type {import('./build.mjs').BundleType[]}
457
- */
458
- const bundles = ['esm', 'cjs'];
459
-
460
- await Promise.all([
461
- createFile(path.join(cwd, 'src/utils/color.ts')),
462
- createFile(path.join(cwd, 'src/utils/size.ts')),
463
-
464
- createFile(path.join(outputDir, 'utils/color.js')),
465
- createFile(path.join(outputDir, 'utils/color.cjs')),
466
- createFile(path.join(outputDir, 'utils/size.js')),
467
- createFile(path.join(outputDir, 'utils/size.cjs')),
468
- ]);
469
-
470
- const { exports: packageExports } = await createPackageExports(
471
- {
472
- './utils/*': './src/utils/*.ts',
473
- },
474
- {
475
- bundles,
476
- outputDir,
477
- cwd,
478
- packageType: 'module',
479
- },
480
- );
481
-
482
- expect(packageExports['./utils/color']).toEqual({
483
- import: './utils/color.js',
484
- require: './utils/color.cjs',
485
- default: './utils/color.js',
486
- });
487
- expect(packageExports['./utils/size']).toEqual({
488
- import: './utils/size.js',
489
- require: './utils/size.cjs',
490
- default: './utils/size.js',
491
- });
492
- });
493
-
494
- it('produces no entries when glob matches nothing', async () => {
495
- const cwd = await makeTempDir();
496
- const outputDir = path.join(cwd, 'build');
497
-
498
- // Create the src directory but no .ts files in it
499
- createFile(path.join(cwd, 'src/.gitkeep'));
500
-
501
- const { exports: packageExports } = await createPackageExports(
502
- {
503
- './*': './src/*.ts',
504
- },
505
- {
506
- bundles: ['cjs'],
507
- outputDir,
508
- cwd,
509
- },
510
- );
511
-
512
- // Only the default ./package.json entry should be present
513
- expect(Object.keys(packageExports)).toEqual(['./package.json']);
514
- });
515
-
516
- it('expands globs in sorted order', async () => {
517
- const cwd = await makeTempDir();
518
- const outputDir = path.join(cwd, 'build');
519
-
520
- await Promise.all([
521
- createFile(path.join(cwd, 'src/Zebra.ts')),
522
- createFile(path.join(cwd, 'src/Apple.ts')),
523
- createFile(path.join(cwd, 'src/Mango.ts')),
524
-
525
- createFile(path.join(outputDir, 'Zebra.js')),
526
- createFile(path.join(outputDir, 'Apple.js')),
527
- createFile(path.join(outputDir, 'Mango.js')),
528
- ]);
529
-
530
- const { exports: packageExports } = await createPackageExports(
531
- {
532
- './*': './src/*.ts',
533
- },
534
- {
535
- bundles: ['cjs'],
536
- outputDir,
537
- cwd,
538
- },
539
- );
540
-
541
- const exportKeys = Object.keys(packageExports).filter((k) => k !== './package.json');
542
- expect(exportKeys).toEqual(['./Apple', './Mango', './Zebra']);
543
- });
544
-
545
- it('removes expanded entries matching a null-valued glob (negation)', async () => {
546
- const cwd = await makeTempDir();
547
- const outputDir = path.join(cwd, 'build');
548
- /**
549
- * @type {import('./build.mjs').BundleType[]}
550
- */
551
- const bundles = ['esm', 'cjs'];
552
-
553
- await Promise.all([
554
- createFile(path.join(cwd, 'src/Accordion.ts')),
555
- createFile(path.join(cwd, 'src/Button.ts')),
556
- createFile(path.join(cwd, 'src/ButtonBase.ts')),
557
-
558
- createFile(path.join(outputDir, 'Accordion.js')),
559
- createFile(path.join(outputDir, 'Accordion.cjs')),
560
- createFile(path.join(outputDir, 'Button.js')),
561
- createFile(path.join(outputDir, 'Button.cjs')),
562
- createFile(path.join(outputDir, 'ButtonBase.js')),
563
- createFile(path.join(outputDir, 'ButtonBase.cjs')),
564
- ]);
565
-
566
- const { exports: packageExports } = await createPackageExports(
567
- {
568
- './*': './src/*.ts',
569
- './Button*': null,
570
- },
571
- {
572
- bundles,
573
- outputDir,
574
- cwd,
575
- packageType: 'module',
576
- },
577
- );
578
-
579
- expect(packageExports['./Accordion']).toBeDefined();
580
- expect(packageExports['./Button']).toBeUndefined();
581
- expect(packageExports['./ButtonBase']).toBeUndefined();
582
- // The negation glob key itself should not appear
583
- expect(packageExports['./Button*']).toBeUndefined();
584
- });
585
-
586
- it('negation with null removes only matching keys', async () => {
587
- const cwd = await makeTempDir();
588
- const outputDir = path.join(cwd, 'build');
589
-
590
- await Promise.all([
591
- createFile(path.join(cwd, 'src/Alert.ts')),
592
- createFile(path.join(cwd, 'src/AlertTitle.ts')),
593
- createFile(path.join(cwd, 'src/Button.ts')),
594
-
595
- createFile(path.join(outputDir, 'Alert.js')),
596
- createFile(path.join(outputDir, 'AlertTitle.js')),
597
- createFile(path.join(outputDir, 'Button.js')),
598
- ]);
599
-
600
- const { exports: packageExports } = await createPackageExports(
601
- {
602
- './*': './src/*.ts',
603
- './Alert*': null,
604
- },
605
- {
606
- bundles: ['cjs'],
607
- outputDir,
608
- cwd,
609
- },
610
- );
611
-
612
- const exportKeys = Object.keys(packageExports).filter((k) => k !== './package.json');
613
- expect(exportKeys).toEqual(['./Button']);
614
- });
615
-
616
- it('preserves null glob pattern when no keys match', async () => {
617
- const cwd = await makeTempDir();
618
- const outputDir = path.join(cwd, 'build');
619
-
620
- await Promise.all([
621
- createFile(path.join(cwd, 'src/Button.ts')),
622
- createFile(path.join(outputDir, 'Button.js')),
623
- ]);
624
-
625
- const { exports: packageExports } = await createPackageExports(
626
- {
627
- './*': './src/*.ts',
628
- './internal/*': null,
629
- },
630
- {
631
- bundles: ['cjs'],
632
- outputDir,
633
- cwd,
634
- },
635
- );
636
-
637
- // Button is kept since it doesn't match the negation
638
- expect(packageExports['./Button']).toBeDefined();
639
- // The negation pattern is preserved as null since nothing matched it
640
- expect(packageExports['./internal/*']).toBeNull();
641
- });
642
-
643
- it('does not expand glob patterns when expand is false', async () => {
644
- const cwd = await makeTempDir();
645
- const outputDir = path.join(cwd, 'build');
646
-
647
- await Promise.all([
648
- createFile(path.join(cwd, 'src/Button.ts')),
649
- createFile(path.join(cwd, 'src/TextField.ts')),
650
- ]);
651
-
652
- const { exports: packageExports } = await createPackageExports(
653
- {
654
- './*': './src/*.ts',
655
- },
656
- {
657
- bundles: ['cjs'],
658
- outputDir,
659
- cwd,
660
- expand: false,
661
- },
662
- );
663
-
664
- // Glob should NOT be expanded to individual files
665
- expect(packageExports['./Button']).toBeUndefined();
666
- expect(packageExports['./TextField']).toBeUndefined();
667
- // The raw glob pattern is passed through as-is
668
- expect(packageExports['./*']).toEqual({
669
- require: './*.js',
670
- default: './*.js',
671
- });
672
- });
673
-
674
- it('passes through glob key when value has no wildcard', async () => {
675
- const cwd = await makeTempDir();
676
- const outputDir = path.join(cwd, 'build');
677
-
678
- await Promise.all([
679
- createFile(path.join(cwd, 'src/index.ts')),
680
- createFile(path.join(outputDir, 'index.js')),
681
- ]);
682
-
683
- const { exports: packageExports } = await createPackageExports(
684
- {
685
- './*': './src/index.ts',
686
- },
687
- {
688
- bundles: ['cjs'],
689
- outputDir,
690
- cwd,
691
- },
692
- );
693
-
694
- // When the value has no *, the glob key is passed through as-is
695
- expect(packageExports['./*']).toBeDefined();
696
- });
697
- });
698
-
699
- it('uses require/import and default for single bundle package', async () => {
700
- const cwd = await makeTempDir();
701
- const outputDir = path.join(cwd, 'build');
702
-
703
- await Promise.all([
704
- createFile(path.join(cwd, 'src/index.ts')),
705
- createFile(path.join(outputDir, 'index.js')),
706
- createFile(path.join(outputDir, 'index.d.ts')),
707
- ]);
708
-
709
- const { exports: packageExports } = await createPackageExports(
710
- {
711
- '.': './src/index.ts',
712
- },
713
- {
714
- bundles: ['cjs'],
715
- outputDir,
716
- cwd,
717
- addTypes: true,
718
- packageType: 'commonjs',
719
- },
720
- );
721
-
722
- // Single CJS bundle should have both require and default pointing to the same files
723
- expect(packageExports['.']).toEqual({
724
- require: {
725
- types: './index.d.ts',
726
- default: './index.js',
727
- },
728
- default: {
729
- types: './index.d.ts',
730
- default: './index.js',
731
- },
732
- });
733
- });
734
- });
735
-
736
- describe('createPackageExports leaf rewriting', () => {
737
- it('rewrites a standard conditions object (conditions-outer)', async () => {
738
- const cwd = await makeTempDir();
739
- const outputDir = path.join(cwd, 'build');
740
- /**
741
- * @type {import('./build.mjs').BundleType[]}
742
- */
743
- const bundles = ['esm', 'cjs'];
744
-
745
- await Promise.all([
746
- createFile(path.join(cwd, 'src/feature.ts')),
747
- createFile(path.join(cwd, 'src/node/feature.ts')),
748
- ]);
749
-
750
- const { exports: packageExports } = await createPackageExports(
751
- {
752
- './feature': { node: './src/node/feature.ts', default: './src/feature.ts' },
753
- },
754
- {
755
- bundles,
756
- outputDir,
757
- cwd,
758
- packageType: 'module',
759
- },
760
- );
761
-
762
- expect(packageExports['./feature']).toEqual({
763
- node: {
764
- import: './node/feature.js',
765
- require: './node/feature.cjs',
766
- default: './node/feature.js',
767
- },
768
- default: {
769
- import: './feature.js',
770
- require: './feature.cjs',
771
- default: './feature.js',
772
- },
773
- });
774
- });
775
-
776
- it('passes through paths that are not under src/ verbatim', async () => {
777
- const cwd = await makeTempDir();
778
- const outputDir = path.join(cwd, 'build');
779
-
780
- await Promise.all([
781
- createFile(path.join(outputDir, 'assets/logo.svg')),
782
- createFile(path.join(outputDir, 'vendor/legacy.js')),
783
- ]);
784
-
785
- const { exports: packageExports } = await createPackageExports(
786
- {
787
- './logo': './assets/logo.svg',
788
- './legacy': './vendor/legacy.js',
789
- },
790
- {
791
- bundles: ['cjs'],
792
- outputDir,
793
- cwd,
794
- },
795
- );
796
-
797
- // Already present in the published output: emitted unchanged.
798
- expect(packageExports['./logo']).toBe('./assets/logo.svg');
799
- expect(packageExports['./legacy']).toBe('./vendor/legacy.js');
800
- });
801
-
802
- it('treats src/ without a leading ./ as a source path', async () => {
803
- const cwd = await makeTempDir();
804
- const outputDir = path.join(cwd, 'build');
805
-
806
- await createFile(path.join(cwd, 'src/index.ts'));
807
-
808
- const { exports: packageExports } = await createPackageExports(
809
- {
810
- './entry': 'src/index.ts',
811
- },
812
- {
813
- bundles: ['cjs'],
814
- outputDir,
815
- cwd,
816
- },
817
- );
818
-
819
- expect(packageExports['./entry']).toEqual({
820
- require: './index.js',
821
- default: './index.js',
822
- });
823
- });
824
-
825
- it('rewrites the prefix of a non-JS asset under src/ without an extension swap', async () => {
826
- const cwd = await makeTempDir();
827
- const outputDir = path.join(cwd, 'build');
828
-
829
- await createFile(path.join(cwd, 'src/theme.css'));
830
-
831
- const { exports: packageExports } = await createPackageExports(
832
- {
833
- './theme.css': './src/theme.css',
834
- },
835
- {
836
- bundles: ['cjs'],
837
- outputDir,
838
- cwd,
839
- },
840
- );
841
-
842
- expect(packageExports['./theme.css']).toBe('./theme.css');
843
- });
844
-
845
- it('preserves a user-authored default alongside import/require conditions', async () => {
846
- const cwd = await makeTempDir();
847
- const outputDir = path.join(cwd, 'build');
848
- /**
849
- * @type {import('./build.mjs').BundleType[]}
850
- */
851
- const bundles = ['esm', 'cjs'];
852
-
853
- await Promise.all([
854
- createFile(path.join(cwd, 'src/esm.ts')),
855
- createFile(path.join(cwd, 'src/fallback.ts')),
856
- ]);
857
-
858
- const { exports: packageExports } = await createPackageExports(
859
- {
860
- './x': { import: './src/esm.ts', default: './src/fallback.ts' },
861
- },
862
- {
863
- bundles,
864
- outputDir,
865
- cwd,
866
- packageType: 'module',
867
- },
868
- );
869
-
870
- // The synthesized default must not clobber the user's `default` branch.
871
- expect(packageExports['./x']).toEqual({
872
- import: { import: './esm.js', require: './esm.cjs', default: './esm.js' },
873
- default: { import: './fallback.js', require: './fallback.cjs', default: './fallback.js' },
874
- });
875
- });
876
- });
877
-
878
- describe('createPackageExports resolution semantics', () => {
879
- /**
880
- * Seeds the worked example with files so the shallow and deep patterns match.
881
- * @param {string} cwd
882
- */
883
- async function seedWorkedExample(cwd) {
884
- await Promise.all([
885
- createFile(path.join(cwd, 'src/foo/x.ts')),
886
- createFile(path.join(cwd, 'src/foo/bar/baz/z.ts')),
887
- ]);
888
- }
889
-
890
- const workedExampleExports = {
891
- './foo/*': './src/foo/*.ts',
892
- './foo/bar/*': null,
893
- './foo/bar/baz/*': './src/foo/bar/baz/*.ts',
894
- };
895
-
896
- it('enumerates with Node-faithful, non-cascading negation (expand: true)', async () => {
897
- const cwd = await makeTempDir();
898
- const outputDir = path.join(cwd, 'build');
899
- await seedWorkedExample(cwd);
900
-
901
- const { exports: packageExports } = await createPackageExports(workedExampleExports, {
902
- bundles: ['cjs'],
903
- outputDir,
904
- cwd,
905
- });
906
-
907
- // ./foo/x resolves (matched only by the shallow pattern)
908
- expect(packageExports['./foo/x']).toEqual({
909
- require: './foo/x.js',
910
- default: './foo/x.js',
911
- });
912
- // ./foo/bar/baz/z resolves even though it sits under the null'd ./foo/bar/*
913
- expect(packageExports['./foo/bar/baz/z']).toEqual({
914
- require: './foo/bar/baz/z.js',
915
- default: './foo/bar/baz/z.js',
916
- });
917
- // nothing whose most-specific match is the null is emitted as a concrete entry
918
- expect(packageExports['./foo/bar/z']).toBeUndefined();
919
- // the null pattern is carried through to keep blocking its subtree at runtime
920
- expect(packageExports['./foo/bar/*']).toBeNull();
921
- });
922
-
923
- it('keeps patterns and the null verbatim (expand: false)', async () => {
924
- const cwd = await makeTempDir();
925
- const outputDir = path.join(cwd, 'build');
926
- await seedWorkedExample(cwd);
927
-
928
- const { exports: packageExports } = await createPackageExports(workedExampleExports, {
929
- bundles: ['cjs'],
930
- outputDir,
931
- cwd,
932
- expand: false,
933
- });
934
-
935
- expect(packageExports['./foo/*']).toEqual({
936
- require: './foo/*.js',
937
- default: './foo/*.js',
938
- });
939
- expect(packageExports['./foo/bar/baz/*']).toEqual({
940
- require: './foo/bar/baz/*.js',
941
- default: './foo/bar/baz/*.js',
942
- });
943
- expect(packageExports['./foo/bar/*']).toBeNull();
944
- });
945
-
946
- it('lets an exact key win over a matching pattern', async () => {
947
- const cwd = await makeTempDir();
948
- const outputDir = path.join(cwd, 'build');
949
-
950
- await Promise.all([
951
- createFile(path.join(cwd, 'src/Other.ts')),
952
- createFile(path.join(cwd, 'src/override.ts')),
953
- ]);
954
-
955
- const { exports: packageExports } = await createPackageExports(
956
- {
957
- // Exact key collides with what `./*` would expand to, but points elsewhere.
958
- './Other': './src/override.ts',
959
- './*': './src/*.ts',
960
- },
961
- {
962
- bundles: ['cjs'],
963
- outputDir,
964
- cwd,
965
- },
966
- );
967
-
968
- // The exact key keeps its own target rather than the pattern's stem.
969
- expect(packageExports['./Other']).toEqual({
970
- require: './override.js',
971
- default: './override.js',
972
- });
973
- // The pattern still expands its other matches.
974
- expect(packageExports['./override']).toEqual({
975
- require: './override.js',
976
- default: './override.js',
977
- });
978
- });
979
- });
980
-
981
- describe('createPackageBin', () => {
982
- it('prefers the ESM bundle when available', async () => {
983
- const cwd = await makeTempDir();
984
- /**
985
- * @type {import('./build.mjs').BundleType[]}
986
- */
987
- const bundles = ['esm', 'cjs'];
988
-
989
- await Promise.all([createFile(path.join(cwd, 'src/cli.ts'))]);
990
-
991
- let bin = await createPackageBin('./src/cli.ts', {
992
- bundles,
993
- cwd,
994
- packageType: 'module',
995
- });
996
-
997
- expect(bin).toBe('./cli.js');
998
-
999
- bin = await createPackageBin('./src/cli.ts', {
1000
- bundles: [bundles[1]], // only CJS bundle
1001
- cwd,
1002
- });
1003
-
1004
- expect(bin).toBe('./cli.js');
1005
-
1006
- bin = await createPackageBin('./src/cli.ts', {
1007
- bundles, // only CJS bundle
1008
- cwd,
1009
- packageType: 'commonjs',
1010
- });
1011
-
1012
- expect(bin).toBe('./cli.mjs');
1013
- });
1014
- });
1015
-
1016
- describe('createPackageImports', () => {
1017
- it('returns undefined when there is no imports field', async () => {
1018
- const cwd = await makeTempDir();
1019
-
1020
- const imports = await createPackageImports(undefined, {
1021
- bundles: ['esm'],
1022
- cwd,
1023
- packageType: 'module',
1024
- });
1025
-
1026
- expect(imports).toBeUndefined();
1027
- });
1028
-
1029
- it('throws when an import key does not start with "#"', async () => {
1030
- const cwd = await makeTempDir();
1031
-
1032
- await expect(
1033
- createPackageImports(
1034
- /** @type {any} */ ({
1035
- 'internal/utils': './src/internal/utils.ts',
1036
- }),
1037
- {
1038
- bundles: ['esm'],
1039
- cwd,
1040
- packageType: 'module',
1041
- },
1042
- ),
1043
- ).rejects.toThrow('must start with "#"');
1044
- });
1045
-
1046
- it('rewrites internal subpath imports for a dual bundle module package', async () => {
1047
- const cwd = await makeTempDir();
1048
- const outputDir = path.join(cwd, 'build');
1049
- /**
1050
- * @type {import('./build.mjs').BundleType[]}
1051
- */
1052
- const bundles = ['esm', 'cjs'];
1053
-
1054
- await Promise.all([
1055
- createFile(path.join(cwd, 'src/internal/utils.ts')),
1056
- createFile(path.join(outputDir, 'internal/utils.js')),
1057
- createFile(path.join(outputDir, 'internal/utils.cjs')),
1058
- createFile(path.join(outputDir, 'internal/utils.d.ts')),
1059
- createFile(path.join(outputDir, 'internal/utils.d.cts')),
1060
- ]);
1061
-
1062
- const imports = await createPackageImports(
1063
- {
1064
- '#internal/utils': './src/internal/utils.ts',
1065
- },
1066
- {
1067
- bundles,
1068
- cwd,
1069
- addTypes: true,
1070
- packageType: 'module',
1071
- },
1072
- );
1073
-
1074
- expect(imports).toEqual({
1075
- '#internal/utils': {
1076
- import: { types: './internal/utils.d.ts', default: './internal/utils.js' },
1077
- require: { types: './internal/utils.d.cts', default: './internal/utils.cjs' },
1078
- default: { types: './internal/utils.d.ts', default: './internal/utils.js' },
1079
- },
1080
- });
1081
- });
1082
-
1083
- it('puts import before require regardless of bundle array order', async () => {
1084
- const cwd = await makeTempDir();
1085
- const outputDir = path.join(cwd, 'build');
1086
-
1087
- await Promise.all([
1088
- createFile(path.join(cwd, 'src/internal/utils.ts')),
1089
- createFile(path.join(outputDir, 'internal/utils.js')),
1090
- createFile(path.join(outputDir, 'internal/utils.cjs')),
1091
- ]);
1092
-
1093
- const imports = await createPackageImports(
1094
- {
1095
- '#internal/utils': './src/internal/utils.ts',
1096
- },
1097
- {
1098
- // Pass cjs before esm to verify the key order is still 'import' then 'require'
1099
- bundles: ['cjs', 'esm'],
1100
- cwd,
1101
- packageType: 'module',
1102
- },
1103
- );
1104
-
1105
- expect(
1106
- Object.keys(/** @type {Record<string, unknown>} */ (imports?.['#internal/utils'])),
1107
- ).toEqual(['import', 'require', 'default']);
1108
- });
1109
-
1110
- it('passes bare specifiers through unchanged', async () => {
1111
- const cwd = await makeTempDir();
1112
-
1113
- const imports = await createPackageImports(
1114
- {
1115
- '#error-formatter': '@custom/error-formatter',
1116
- },
1117
- {
1118
- bundles: ['esm', 'cjs'],
1119
- cwd,
1120
- packageType: 'module',
1121
- },
1122
- );
1123
-
1124
- expect(imports).toEqual({
1125
- '#error-formatter': '@custom/error-formatter',
1126
- });
1127
- });
1128
-
1129
- it('expands glob patterns in import keys and values', async () => {
1130
- const cwd = await makeTempDir();
1131
- const outputDir = path.join(cwd, 'build');
1132
-
1133
- await Promise.all([
1134
- createFile(path.join(cwd, 'src/internal/foo.ts')),
1135
- createFile(path.join(cwd, 'src/internal/bar.ts')),
1136
- createFile(path.join(outputDir, 'internal/foo.js')),
1137
- createFile(path.join(outputDir, 'internal/bar.js')),
1138
- ]);
1139
-
1140
- const imports = await createPackageImports(
1141
- {
1142
- '#internal/*': './src/internal/*.ts',
1143
- },
1144
- {
1145
- bundles: ['esm'],
1146
- cwd,
1147
- packageType: 'module',
1148
- },
1149
- );
1150
-
1151
- expect(imports).toEqual({
1152
- '#internal/bar': { import: './internal/bar.js', default: './internal/bar.js' },
1153
- '#internal/foo': { import: './internal/foo.js', default: './internal/foo.js' },
1154
- });
1155
- });
1156
-
1157
- it('rewrites every condition of a conditions object (conditions-outer)', async () => {
1158
- const cwd = await makeTempDir();
1159
- /**
1160
- * @type {import('./build.mjs').BundleType[]}
1161
- */
1162
- const bundles = ['esm', 'cjs'];
1163
-
1164
- await Promise.all([
1165
- createFile(path.join(cwd, 'src/internal/utils.ts')),
1166
- createFile(path.join(cwd, 'src/internal/node/utils.ts')),
1167
- ]);
1168
-
1169
- const imports = await createPackageImports(
1170
- {
1171
- '#internal/utils': {
1172
- node: './src/internal/node/utils.ts',
1173
- default: './src/internal/utils.ts',
1174
- },
1175
- },
1176
- {
1177
- bundles,
1178
- cwd,
1179
- packageType: 'module',
1180
- },
1181
- );
1182
-
1183
- expect(imports).toEqual({
1184
- '#internal/utils': {
1185
- node: {
1186
- import: './internal/node/utils.js',
1187
- require: './internal/node/utils.cjs',
1188
- default: './internal/node/utils.js',
1189
- },
1190
- default: {
1191
- import: './internal/utils.js',
1192
- require: './internal/utils.cjs',
1193
- default: './internal/utils.js',
1194
- },
1195
- },
1196
- });
1197
- });
1198
-
1199
- it('passes a conditions object of bare specifiers through unchanged, preserving condition order', async () => {
1200
- const cwd = await makeTempDir();
1201
-
1202
- const imports = await createPackageImports(
1203
- {
1204
- '#mui/TransitionGroupContext': {
1205
- node: 'react-transition-group/cjs/TransitionGroupContext.js',
1206
- browser: 'react-transition-group/esm/TransitionGroupContext.js',
1207
- default: 'react-transition-group/esm/TransitionGroupContext.js',
1208
- },
1209
- },
1210
- {
1211
- bundles: ['esm', 'cjs'],
1212
- cwd,
1213
- packageType: 'module',
1214
- },
1215
- );
1216
-
1217
- expect(imports).toEqual({
1218
- '#mui/TransitionGroupContext': {
1219
- node: 'react-transition-group/cjs/TransitionGroupContext.js',
1220
- browser: 'react-transition-group/esm/TransitionGroupContext.js',
1221
- default: 'react-transition-group/esm/TransitionGroupContext.js',
1222
- },
1223
- });
1224
- expect(
1225
- Object.keys(
1226
- /** @type {Record<string, unknown>} */ (imports?.['#mui/TransitionGroupContext']),
1227
- ),
1228
- ).toEqual(['node', 'browser', 'default']);
1229
- });
1230
-
1231
- it('passes a nested import/require/default object of bare specifiers through unchanged', async () => {
1232
- const cwd = await makeTempDir();
1233
-
1234
- const imports = await createPackageImports(
1235
- {
1236
- '#mui/TransitionGroupContext': {
1237
- node: 'react-transition-group/cjs/TransitionGroupContext.js',
1238
- default: {
1239
- import: 'react-transition-group/esm/TransitionGroupContext.js',
1240
- require: 'react-transition-group/cjs/TransitionGroupContext.js',
1241
- default: 'react-transition-group/esm/TransitionGroupContext.js',
1242
- },
1243
- },
1244
- },
1245
- {
1246
- bundles: ['esm', 'cjs'],
1247
- cwd,
1248
- packageType: 'module',
1249
- },
1250
- );
1251
-
1252
- expect(imports).toEqual({
1253
- '#mui/TransitionGroupContext': {
1254
- node: 'react-transition-group/cjs/TransitionGroupContext.js',
1255
- default: {
1256
- import: 'react-transition-group/esm/TransitionGroupContext.js',
1257
- require: 'react-transition-group/cjs/TransitionGroupContext.js',
1258
- default: 'react-transition-group/esm/TransitionGroupContext.js',
1259
- },
1260
- },
1261
- });
1262
- });
1263
- });