publint
Version:
Lint packaging errors
102 lines (90 loc) • 3.45 kB
JavaScript
import path from 'node:path'
import { detect } from 'package-manager-detector/detect'
import { packAsList, unpack } from '@publint/pack'
import { createNodeVfs } from './node/vfs-node.js'
import { core } from './shared/core.js'
import { createTarballVfs } from './shared/vfs-tarball.js'
/**
* @type {import('./index.d.ts').publint}
*/
export async function publint(options) {
const pack = options?.pack ?? 'auto'
// @ts-expect-error internal property passed from cli.js
const log = options?._log ?? false
/** @type {import('./shared/core.js').Vfs} */
let vfs
/** @type {string | undefined} */
let overridePkgDir
/** @type {string[] | undefined} */
let packedFiles
// If a pack object is provided, that means the user declares its own virtual
// file system, e.g. for cases where they have the tarball file in hand or prefers
if (typeof pack === 'object') {
if ('tarball' in pack) {
const result = await unpack(pack.tarball)
vfs = createTarballVfs(result.files)
overridePkgDir = result.rootDir
} else {
vfs = createTarballVfs(pack.files)
}
}
// The rest options are used for manual packing and falls back to node vfs.
// Only this flow allows publint to differentiate files that exist but not published.
else {
if (pack !== false) {
const pkgDir = options?.pkgDir ?? process.cwd()
packedFiles = await detectAndPack(pkgDir, pack, log)
}
vfs = createNodeVfs()
}
if (log) {
console.log('Linting...')
}
return core({
pkgDir: options?.pkgDir ?? overridePkgDir ?? process.cwd(),
vfs,
level: options?.level ?? 'suggestion',
strict: options?.strict ?? false,
_packedFiles: packedFiles,
})
}
/**
* @typedef {T extends string ? T : never} ExtractStringLiteral
* @template T
*/
/**
* @param {string} pkgDir
* @param {ExtractStringLiteral<import('./index.d.ts').Options['pack']>} pack
* @param {boolean} log
*/
async function detectAndPack(pkgDir, pack, log) {
let packageManager = pack
if (packageManager === 'auto') {
let detected = (await detect({ cwd: pkgDir }))?.name ?? 'npm'
// Deno is not supported in `@publint/pack` (doesn't have a pack command)
if (detected === 'deno') {
detected = 'npm'
}
packageManager = detected
}
// When packing, we want to ignore scripts as `publint` itself could be used in one of them and could
// cause an infinite loop. Also, running scripts might be slow and unexpected.
// Yarn does not support ignoring scripts. If we know we're in a lifecycle event, try to warn about an infinite loop.
// NOTE: this is not foolproof as one could invoke `yarn run <something>` within the lifecycle event, causing us
// to unable to check if we're in a problematic lifecycle event.
if (
packageManager === 'yarn' &&
['prepack', 'postpack'].includes(process.env.npm_lifecycle_event ?? '')
) {
throw new Error(
`[publint] publint requires running \`yarn pack\` to lint the package, however, ` +
`it is also being executed in the "${process.env.npm_lifecycle_event}" lifecycle event, ` +
`which causes an infinite loop. Try to run publint outside of the lifecycle event instead.`,
)
}
if (log) {
console.log(`Packing files with \`${packageManager} pack\`...`)
}
const list = await packAsList(pkgDir, { packageManager, ignoreScripts: true })
return list.map((file) => path.join(pkgDir, file))
}