Skip to content

Commit e7b9338

Browse files
authoredApr 11, 2024··
feat: validate types, files and main fields (#730)
1 parent 0b6c80b commit e7b9338

File tree

19 files changed

+92
-103
lines changed

19 files changed

+92
-103
lines changed
 

‎playground/babel-plugin/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"./package.json": "./package.json"
1616
},
1717
"main": "./dist/index.cjs",
18+
"files": ["dist"],
1819
"types": "./dist/index.d.ts",
1920
"scripts": {
2021
"build": "pkg build --strict --check --clean",

‎playground/browser-bundle/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"./package.json": "./package.json"
1616
},
17+
"files": ["dist"],
1718
"main": "./dist/index.cjs",
1819
"scripts": {
1920
"build": "pkg build --strict --check --clean",

‎playground/custom-dist/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"./package.json": "./package.json"
1616
},
17+
"files": ["lib"],
1718
"main": "./lib/index.cjs",
1819
"module": "./lib/index.esm.js",
1920
"types": "./lib/index.d.ts",

‎playground/default-export/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"./package.json": "./package.json"
1616
},
17+
"files": ["dist"],
1718
"main": "./dist/index.cjs",
1819
"scripts": {
1920
"build": "pkg build --strict --check --clean",

‎playground/extra-bundles/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"./package.json": "./package.json"
1515
},
1616
"main": "./dist/index.js",
17+
"files": ["dist"],
1718
"scripts": {
1819
"build": "pkg build --strict --check --clean",
1920
"clean": "rimraf dist"

‎playground/js/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"./package.json": "./package.json"
1515
},
16+
"files": ["dist"],
1617
"main": "./dist/index.js",
1718
"scripts": {
1819
"build": "pkg build --strict --check --clean",

‎playground/multi-export/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
},
4545
"./package.json": "./package.json"
4646
},
47+
"files": ["dist"],
4748
"main": "./dist/index.cjs",
4849
"types": "./dist/index.d.ts",
4950
"typesVersions": {

‎playground/multi-exports-commonjs/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
},
2525
"./package.json": "./package.json"
2626
},
27+
"files": ["dist"],
2728
"main": "./dist/index.js",
2829
"module": "./dist/index.esm.js",
2930
"types": "./dist/index.d.ts",

‎playground/node-api/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
}
1313
},
1414
"main": "./dist/index.js",
15+
"files": ["dist"],
1516
"scripts": {
1617
"build": "node scripts/build.js"
1718
}

‎playground/ts-bundler/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"main": "./dist/index.cjs",
1818
"types": "./dist/index.d.ts",
19+
"files": ["dist"],
1920
"scripts": {
2021
"build": "pkg build --strict --check --clean",
2122
"clean": "rimraf dist"

‎playground/ts-node16/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"main": "./dist/index.cjs",
1818
"types": "./dist/index.d.ts",
19+
"files": ["dist"],
1920
"scripts": {
2021
"build": "pkg build --strict --check --clean",
2122
"clean": "rimraf dist"

‎playground/ts/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"./package.json": "./package.json"
1616
},
17+
"files": ["dist"],
1718
"main": "./dist/index.cjs",
1819
"types": "./dist/index.d.ts",
1920
"scripts": {

‎playground/use-client-directive/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
},
1818
"main": "./dist/index.cjs",
1919
"types": "./dist/index.d.ts",
20+
"files": ["dist"],
2021
"scripts": {
2122
"build": "pkg build --strict --check --clean",
2223
"clean": "rimraf dist"

‎src/node/__snapshots__/strict.test.ts.snap

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
exports[`{'noPackageJsonTypings': 'error'}} 1`] = `
44
{
5+
"alwaysPackageJsonFiles": "error",
6+
"alwaysPackageJsonMain": "error",
7+
"alwaysPackageJsonTypes": "error",
58
"noImplicitBrowsersList": "warn",
69
"noImplicitSideEffects": "warn",
710
"noPackageJsonTypings": "error",
@@ -10,6 +13,9 @@ exports[`{'noPackageJsonTypings': 'error'}} 1`] = `
1013

1114
exports[`{'noPackageJsonTypings': 'off'}} 1`] = `
1215
{
16+
"alwaysPackageJsonFiles": "error",
17+
"alwaysPackageJsonMain": "error",
18+
"alwaysPackageJsonTypes": "error",
1319
"noImplicitBrowsersList": "warn",
1420
"noImplicitSideEffects": "warn",
1521
"noPackageJsonTypings": "off",
@@ -18,6 +24,9 @@ exports[`{'noPackageJsonTypings': 'off'}} 1`] = `
1824

1925
exports[`{'noPackageJsonTypings': 'warn'}} 1`] = `
2026
{
27+
"alwaysPackageJsonFiles": "error",
28+
"alwaysPackageJsonMain": "error",
29+
"alwaysPackageJsonTypes": "error",
2130
"noImplicitBrowsersList": "warn",
2231
"noImplicitSideEffects": "warn",
2332
"noPackageJsonTypings": "warn",

‎src/node/core/pkg/parseExports.ts

+21-30
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ export function parseExports(options: {
2727
}
2828
}
2929

30+
if (!pkg.main && strict && strictOptions.alwaysPackageJsonMain !== 'off') {
31+
report(strictOptions.alwaysPackageJsonMain, 'package.json: `main` must be declared')
32+
}
33+
34+
if (!Array.isArray(pkg.files) && strict && strictOptions.alwaysPackageJsonFiles !== 'off') {
35+
report(
36+
strictOptions.alwaysPackageJsonFiles,
37+
'package.json: `files` should be used over `.npmignore`',
38+
)
39+
}
40+
3041
if (pkg.source) {
3142
if (
3243
strict &&
@@ -125,34 +136,6 @@ export function parseExports(options: {
125136
)
126137
}
127138

128-
/*
129-
const rootExports: PkgExport & {_path: string} = {
130-
_exported: true,
131-
_path: '.',
132-
source: pkg.source || '',
133-
browser: pkg.browser && {
134-
source: pkg.source || '',
135-
require: pkg.main && pkg.browser[pkg.main],
136-
import: pkg.module && pkg.browser[pkg.module],
137-
},
138-
require: legacyExports
139-
? pkg.main
140-
: typeof pkg.exports?.['.'] === 'object' && 'require' in pkg.exports['.']
141-
? pkg.exports['.'].require
142-
: '',
143-
import: legacyExports
144-
? pkg.module
145-
: typeof pkg.exports?.['.'] === 'object' && 'import' in pkg.exports['.']
146-
? pkg.exports['.'].import
147-
: '',
148-
default: legacyExports
149-
? pkg.module || pkg.main || ''
150-
: typeof pkg.exports?.['.'] === 'object' && 'default' in pkg.exports['.']
151-
? pkg.exports['.'].default || ''
152-
: pkg.module || pkg.main || '',
153-
}
154-
// */
155-
156139
const _exports: (PkgExport & {_path: string})[] = []
157140

158141
// @TODO validate typesVersions when legacyExports is true
@@ -161,8 +144,16 @@ export function parseExports(options: {
161144
report(strictOptions.noPackageJsonTypings, 'package.json: `typings` should be `types`')
162145
}
163146

164-
if (strict && !pkg.types && pkg.source?.endsWith('.ts')) {
165-
errors.push(
147+
if (
148+
strict &&
149+
strictOptions.alwaysPackageJsonTypes !== 'off' &&
150+
!pkg.types &&
151+
typeof pkg.exports?.['.'] === 'object' &&
152+
'source' in pkg.exports['.'] &&
153+
pkg.exports['.'].source?.endsWith('.ts')
154+
) {
155+
report(
156+
strictOptions.alwaysPackageJsonTypes,
166157
'package.json: `types` must be declared for the npm listing to show as a TypeScript module.',
167158
)
168159
}

‎src/node/strict.ts

+18
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export const strictOptions = z
1919
noPackageJsonTypings: toggle.default('error'),
2020
noImplicitSideEffects: toggle.default('warn'),
2121
noImplicitBrowsersList: toggle.default('warn'),
22+
alwaysPackageJsonTypes: toggle.default('error'),
23+
alwaysPackageJsonMain: toggle.default('error'),
24+
alwaysPackageJsonFiles: toggle.default('error'),
2225
})
2326
.strict()
2427

@@ -58,6 +61,21 @@ export interface StrictOptions {
5861
* @defaultValue 'warn'
5962
*/
6063
noImplicitBrowsersList?: ToggleType
64+
/**
65+
* If typescript is used then `types` in `package.json` should be specified for npm listings to show the TS icon.
66+
* @defaultValue 'error'
67+
*/
68+
alwaysPackageJsonTypes?: ToggleType
69+
/**
70+
* A lot of analysis tooling requiers the `main` field to work (like bundlephobia) and so it's best practice to always include it
71+
* @defaultValue 'error'
72+
*/
73+
alwaysPackageJsonMain?: ToggleType
74+
/**
75+
* Using `.npmignore` is error prone, it's best practice to always declare `files` instead
76+
* @defaultValue 'error'
77+
*/
78+
alwaysPackageJsonFiles?: ToggleType
6179
}
6280

6381
/** @internal */

‎test/__snapshots__/parseExports.test.ts.snap

-72
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,6 @@ exports[`parseExports({type: 'commonjs', legacyExports: false}) > invalid packag
1818
- }]
1919
`;
2020

21-
exports[`parseExports({type: 'commonjs', legacyExports: false}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "main" field 1`] = `
22-
[Error:
23-
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
24-
- "exports": {
25-
- ".": {
26-
- "source": "./src/index.js",
27-
- "default": "./dist/index.js"
28-
- },
29-
- "./package.json": "./package.json"
30-
- }]
31-
`;
32-
3321
exports[`parseExports({type: 'commonjs', legacyExports: false}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "source" field either 1`] = `
3422
[Error:
3523
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
@@ -162,18 +150,6 @@ exports[`parseExports({type: 'commonjs', legacyExports: true}) > invalid package
162150
- }]
163151
`;
164152

165-
exports[`parseExports({type: 'commonjs', legacyExports: true}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "main" field 1`] = `
166-
[Error:
167-
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
168-
- "exports": {
169-
- ".": {
170-
- "source": "./src/index.js",
171-
- "default": "./dist/index.js"
172-
- },
173-
- "./package.json": "./package.json"
174-
- }]
175-
`;
176-
177153
exports[`parseExports({type: 'commonjs', legacyExports: true}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "source" field either 1`] = `
178154
[Error:
179155
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
@@ -242,18 +218,6 @@ exports[`parseExports({type: 'module', legacyExports: false}) > invalid packages
242218
- }]
243219
`;
244220

245-
exports[`parseExports({type: 'module', legacyExports: false}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "main" field 1`] = `
246-
[Error:
247-
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
248-
- "exports": {
249-
- ".": {
250-
- "source": "./src/index.js",
251-
- "default": "./dist/index.js"
252-
- },
253-
- "./package.json": "./package.json"
254-
- }]
255-
`;
256-
257221
exports[`parseExports({type: 'module', legacyExports: false}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "source" field either 1`] = `
258222
[Error:
259223
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
@@ -384,18 +348,6 @@ exports[`parseExports({type: 'module', legacyExports: true}) > invalid packages
384348
- }]
385349
`;
386350

387-
exports[`parseExports({type: 'module', legacyExports: true}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "main" field 1`] = `
388-
[Error:
389-
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
390-
- "exports": {
391-
- ".": {
392-
- "source": "./src/index.js",
393-
- "default": "./dist/index.js"
394-
- },
395-
- "./package.json": "./package.json"
396-
- }]
397-
`;
398-
399351
exports[`parseExports({type: 'module', legacyExports: true}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "source" field either 1`] = `
400352
[Error:
401353
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
@@ -464,18 +416,6 @@ exports[`parseExports({type: undefined, legacyExports: false}) > invalid package
464416
- }]
465417
`;
466418

467-
exports[`parseExports({type: undefined, legacyExports: false}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "main" field 1`] = `
468-
[Error:
469-
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
470-
- "exports": {
471-
- ".": {
472-
- "source": "./src/index.js",
473-
- "default": "./dist/index.js"
474-
- },
475-
- "./package.json": "./package.json"
476-
- }]
477-
`;
478-
479419
exports[`parseExports({type: undefined, legacyExports: false}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "source" field either 1`] = `
480420
[Error:
481421
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
@@ -608,18 +548,6 @@ exports[`parseExports({type: undefined, legacyExports: true}) > invalid packages
608548
- }]
609549
`;
610550

611-
exports[`parseExports({type: undefined, legacyExports: true}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "main" field 1`] = `
612-
[Error:
613-
- package.json: \`exports\` are missing, please set a minimal configuration, for example:
614-
- "exports": {
615-
- ".": {
616-
- "source": "./src/index.js",
617-
- "default": "./dist/index.js"
618-
- },
619-
- "./package.json": "./package.json"
620-
- }]
621-
`;
622-
623551
exports[`parseExports({type: undefined, legacyExports: true}) > invalid packages > the "exports" key is required > shows a best effort message if there is no "source" field either 1`] = `
624552
[Error:
625553
- package.json: \`exports\` are missing, please set a minimal configuration, for example:

‎test/parseExports.test.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const defaults = {
1313
default: './dist/index.js',
1414
},
1515
} as const
16+
const files = ['dist']
1617
const strictOptions = parseStrictOptions({})
1718
const logger = createLogger()
1819

@@ -46,6 +47,7 @@ describe.each([
4647
type,
4748
name,
4849
version,
50+
files,
4951
main: reference['.'].require,
5052
types: './dist/index.d.ts',
5153
exports: reference,
@@ -67,6 +69,7 @@ describe.each([
6769
type,
6870
name,
6971
version,
72+
files,
7073
main: './dist/index.js',
7174
types: './dist/index.d.ts',
7275
exports: {
@@ -95,6 +98,7 @@ describe.each([
9598
type,
9699
name,
97100
version,
101+
files,
98102
main: './dist/index.js',
99103
types: './dist/index.d.ts',
100104
exports: {
@@ -124,6 +128,7 @@ describe.each([
124128
type,
125129
name,
126130
version,
131+
files,
127132
main: './dist/index.cjs',
128133
types: './dist/index.d.ts',
129134
exports: {
@@ -152,6 +157,7 @@ describe.each([
152157
type,
153158
name,
154159
version,
160+
files,
155161
main: './dist/index.js',
156162
types: './dist/index.d.ts',
157163
exports: {
@@ -183,6 +189,7 @@ describe.each([
183189
type,
184190
name,
185191
version,
192+
files,
186193
main: reference['.'].require,
187194
module: './dist/index.esm.js',
188195
types: './dist/index.d.ts',
@@ -205,6 +212,7 @@ describe.each([
205212
type,
206213
name,
207214
version,
215+
files,
208216
main: './dist/index.js',
209217
module: './dist/index.esm.js',
210218
types: './dist/index.d.ts',
@@ -234,6 +242,7 @@ describe.each([
234242
type,
235243
name,
236244
version,
245+
files,
237246
main: './dist/index.js',
238247
module: './dist/index.esm.js',
239248
types: './dist/index.d.ts',
@@ -265,6 +274,7 @@ describe.each([
265274
type,
266275
name,
267276
version,
277+
files,
268278
main: './dist/index.cjs',
269279
module: './dist/index.esm.js',
270280
types: './dist/index.d.ts',
@@ -295,6 +305,7 @@ describe.each([
295305
type,
296306
name,
297307
version,
308+
files,
298309
main: './dist/index.js',
299310
module: './dist/index.esm.js',
300311
types: './dist/index.d.ts',
@@ -329,6 +340,7 @@ describe.each([
329340
type,
330341
name,
331342
version,
343+
files,
332344
source: './src/index.ts',
333345
main: './lib/index.js',
334346
module: './lib/index.esm.js',
@@ -358,6 +370,7 @@ describe.each([
358370
type,
359371
name,
360372
version,
373+
files,
361374
source: './src/index.ts',
362375
main: './lib/index.js',
363376
module: './lib/index.esm.js',
@@ -390,6 +403,7 @@ describe.each([
390403
type,
391404
name,
392405
version,
406+
files,
393407
source: './src/index.ts',
394408
main: './lib/index.js',
395409
types: './dist/index.d.ts',
@@ -399,11 +413,12 @@ describe.each([
399413
expect(() => testParseExports({pkg})).toThrowErrorMatchingSnapshot()
400414
})
401415

402-
test('shows a best effort message if there is no "main" field', () => {
416+
test.skip('shows a best effort message if there is no "main" field', () => {
403417
const pkg = {
404418
type,
405419
name,
406420
version,
421+
files,
407422
source: './src/index.ts',
408423
types: './dist/index.d.ts',
409424
} satisfies PackageJSON
@@ -417,6 +432,8 @@ describe.each([
417432
type,
418433
name,
419434
version,
435+
files,
436+
main: './dist/index.js',
420437
types: './dist/index.d.ts',
421438
} satisfies PackageJSON
422439

@@ -430,6 +447,7 @@ describe.each([
430447
type,
431448
name,
432449
version,
450+
files,
433451
source: './src/index.ts',
434452
main: './lib/index.js',
435453
types: './lib/index.d.ts',
@@ -452,6 +470,7 @@ describe.each([
452470
type,
453471
name,
454472
version,
473+
files,
455474
source: './src/index.ts',
456475
main: './lib/index.js',
457476
module: './lib/index.esm.js',
@@ -471,6 +490,7 @@ describe.each([
471490
type,
472491
name,
473492
version,
493+
files,
474494
source: './src/index.ts',
475495
main: './lib/index.js',
476496
module: './lib/index.esm.js',
@@ -488,6 +508,7 @@ describe.each([
488508
type,
489509
name,
490510
version,
511+
files,
491512
main: reference['.'].require,
492513
module: reference['.'].import,
493514
types: './dist/index.d.ts',
@@ -502,6 +523,7 @@ describe.each([
502523
type,
503524
name,
504525
version,
526+
files,
505527
main: reference['.'].require,
506528
types: './dist/index.d.ts',
507529
exports: reference,
@@ -517,6 +539,7 @@ describe.each([
517539
type,
518540
name,
519541
version,
542+
files,
520543
main: './dist/index.js',
521544
types: './dist/index.d.ts',
522545
exports: {
@@ -544,6 +567,7 @@ describe.each([
544567
type,
545568
name,
546569
version,
570+
files,
547571
main: './dist/index.js',
548572
types: './dist/index.d.ts',
549573
exports: {
@@ -568,6 +592,7 @@ describe.each([
568592
type,
569593
name,
570594
version,
595+
files,
571596
main: './dist/index.cjs',
572597
module: './dist/index.esm.js',
573598
types: './dist/index.d.ts',
@@ -596,6 +621,7 @@ describe.each([
596621
type,
597622
name,
598623
version,
624+
files,
599625
main: './dist/index.js',
600626
module: './dist/index.esm.js',
601627
types: './dist/index.d.ts',
@@ -621,6 +647,7 @@ describe.each([
621647
type,
622648
name,
623649
version,
650+
files,
624651
main: defaults['.'].default,
625652
module: './dist/index.esm.js',
626653
types: './dist/index.d.ts',
@@ -648,6 +675,7 @@ describe.each([
648675
type,
649676
name,
650677
version,
678+
files,
651679
main: defaults['.'].default,
652680
module: './dist/index.esm.js',
653681
types: './dist/index.d.ts',

‎test/parseTasks.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ test('should parse tasks (type: module)', () => {
2424
'./dist/index.cjs': './dist/index.browser.cjs',
2525
'./dist/index.js': './dist/index.browser.js',
2626
},
27+
files: ['dist'],
2728
exports: {
2829
'.': {
2930
source: './src/index.ts',
@@ -159,6 +160,7 @@ test('should parse tasks (type: commonjs, legacyExports: true)', () => {
159160
'./dist/index.mjs': './dist/index.browser.mjs',
160161
'./dist/index.esm.js': './dist/index.browser.esm.js',
161162
},
163+
files: ['dist'],
162164
exports: {
163165
'.': {
164166
source: './src/index.ts',

0 commit comments

Comments
 (0)
Please sign in to comment.