Skip to content

Commit

Permalink
Error if field has invalid value type
Browse files Browse the repository at this point in the history
fixes #73
  • Loading branch information
bluwy committed Sep 27, 2023
1 parent fa7c321 commit 4949ae4
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 4 deletions.
7 changes: 7 additions & 0 deletions pkg/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ export type Message =
expectExtension: string
}
>
| BaseMessage<
'FIELD_INVALID_VALUE_TYPE',
{
actualType: string
expectTypes: string[]
}
>

export interface Options {
/**
Expand Down
35 changes: 31 additions & 4 deletions pkg/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export async function publint({ pkgDir, vfs, level, strict, _packedFiles }) {
// Relies on default node resolution
// https://nodejs.org/api/modules.html#all-together
// LOAD_INDEX(X)
if (!main && !module && !exports) {
if (main == null && module == null && exports == null) {
promiseQueue.push(async () => {
// check index.js only, others aren't our problem
const defaultPath = vfs.pathJoin(pkgDir, 'index.js')
Expand Down Expand Up @@ -83,8 +83,9 @@ export async function publint({ pkgDir, vfs, level, strict, _packedFiles }) {
* - It's mostly used for CJS
* - It can be used for ESM, but if you're doing so, might as well use exports
*/
if (main) {
if (main != null) {
promiseQueue.push(async () => {
if (!ensureTypeOfField(main, ['string'], mainPkgPath)) return
const mainPath = vfs.pathJoin(pkgDir, main)
const mainContent = await readFile(mainPath, mainPkgPath, [
'.js',
Expand Down Expand Up @@ -130,8 +131,9 @@ export async function publint({ pkgDir, vfs, level, strict, _packedFiles }) {
* - Is not a way to support dual packages in NodeJS
* - Should be MJS always!!
*/
if (module) {
if (module != null) {
promiseQueue.push(async () => {
if (!ensureTypeOfField(module, ['string'], modulePkgPath)) return
const modulePath = vfs.pathJoin(pkgDir, module)
const moduleContent = await readFile(modulePath, modulePkgPath, [
'.js',
Expand Down Expand Up @@ -176,7 +178,10 @@ export async function publint({ pkgDir, vfs, level, strict, _packedFiles }) {
}
for (const field of knownFields) {
const [fieldValue, fieldPkgPath] = getPublishedField(rootPkg, field)
if (typeof fieldValue === 'string') {
if (
fieldValue != null &&
ensureTypeOfField(fieldValue, ['string'], fieldPkgPath)
) {
promiseQueue.push(async () => {
const fieldPath = vfs.pathJoin(pkgDir, fieldValue)
await readFile(fieldPath, fieldPkgPath, ['.js', '/index.js'])
Expand Down Expand Up @@ -347,6 +352,28 @@ export async function publint({ pkgDir, vfs, level, strict, _packedFiles }) {
})
}

/**
* @param {any} fieldValue
* @param {('string' | 'number' | 'boolean' | 'object')[]} expectTypes
* @param {string[]} pkgPath
*/
function ensureTypeOfField(fieldValue, expectTypes, pkgPath) {
// @ts-expect-error typeof doesn't need to match `expectedTypes` type but TS panics
if (!expectTypes.includes(typeof fieldValue)) {
messages.push({
code: 'FIELD_INVALID_VALUE_TYPE',
args: {
actualType: typeof fieldValue,
expectTypes
},
path: pkgPath,
type: 'error'
})
return false
}
return true
}

/**
* @param {string | Record<string, any>} fieldValue
* @param {string[]} currentPath
Expand Down
12 changes: 12 additions & 0 deletions pkg/src/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ export function formatMessage(m, pkg) {
+ `Consider splitting out two ${c.bold('"types"')} conditions for ${c.bold('"import"')} and ${c.bold('"require"')}, and use the ${c.yellow(m.args.expectExtension)} extension, `
+ `e.g. ${c.bold(fp(expectPath))}: "${c.bold(replaceLast(pv(m.path), m.args.actualExtension, m.args.expectExtension))}"`
}
case 'FIELD_INVALID_VALUE_TYPE': {
let expectStr = m.args.expectTypes[0]
for (let i = 1; i < m.args.expectTypes.length; i++) {
if (i === m.args.expectTypes.length - 1) {
expectStr += ` or ${m.args.expectTypes[i]}`
} else {
expectStr += `, ${m.args.expectTypes[i]}`
}
}
// prettier-ignore
return `${c.bold(fp(m.path))} is ${c.bold(pv(m.path))} which is an invalid ${c.bold(m.args.actualType)} type. Expected a ${c.bold(expectStr)} type instead.`
}
default:
return
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/tests/fixtures/invalid-field-types/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "publint-invalid-field-types",
"version": "0.0.1",
"private": true,
"main": 0,
"module": true,
"jsnext:main": false
}
6 changes: 6 additions & 0 deletions pkg/tests/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ testFixture('glob-deprecated', [
'EXPORTS_GLOB_NO_MATCHED_FILES'
])

testFixture('invalid-field-types', [
'FIELD_INVALID_VALUE_TYPE',
'FIELD_INVALID_VALUE_TYPE',
'FIELD_INVALID_VALUE_TYPE'
])

testFixture('missing-files', [
...Array(7).fill('FILE_DOES_NOT_EXIST'),
'FILE_NOT_PUBLISHED',
Expand Down
4 changes: 4 additions & 0 deletions site/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,7 @@ Depending on your setup, you can also use the `"exports"` field to directly expo
}
}
```

## `FIELD_INVALID_VALUE_TYPE`

Some `package.json` fields has a set of allowed types, e.g. `string` or `object` only. If an invalid type is passed, this error message will be showed.
12 changes: 12 additions & 0 deletions site/src/utils/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ function messageToString(m, pkg) {
+ `Consider splitting out two ${bold('"types"')} conditions for ${bold('"import"')} and ${bold('"require"')}, and use the ${warn(m.args.expectExtension)} extension, `
+ `e.g. ${bold(fp(expectPath))}: "${bold(replaceLast(pv(m.path), m.args.actualExtension, m.args.expectExtension))}"`
}
case 'FIELD_INVALID_VALUE_TYPE': {
let expectStr = m.args.expectTypes[0]
for (let i = 1; i < m.args.expectTypes.length; i++) {
if (i === m.args.expectTypes.length - 1) {
expectStr += ` or ${m.args.expectTypes[i]}`
} else {
expectStr += `, ${m.args.expectTypes[i]}`
}
}
// prettier-ignore
return `${bold(fp(m.path))} is ${bold(pv(m.path))} which is an invalid ${bold(m.args.actualType)} type. Expected a ${bold(expectStr)} type instead.`
}
default:
return
}
Expand Down

0 comments on commit 4949ae4

Please sign in to comment.