Comparing version 0.1.8 to 0.1.9
import { publint as _publint } from '../src/index.js' | ||
/** | ||
* @type {import('.').publint} | ||
* @type {import('..').publint} | ||
*/ | ||
export function publint(options) { | ||
if (!options?.vfs) { | ||
throw new Error('The vfs option is required in the browser') | ||
} | ||
return _publint({ | ||
pkgDir: options.pkgDir ?? '/', | ||
vfs: options.vfs, | ||
level: options?.level | ||
level: options.level ?? 'suggestion' | ||
}) | ||
} |
@@ -28,3 +28,3 @@ #!/usr/bin/env node | ||
const pkgDir = dir ? path.resolve(dir) : process.cwd() | ||
const { logs } = await lintDir(pkgDir, opts.level) | ||
const logs = await lintDir(pkgDir, opts.level) | ||
logs.forEach((l) => console.log(l)) | ||
@@ -74,3 +74,3 @@ }) | ||
const depDir = await findDepPath(deps[i], pkgDir) | ||
const { logs } = await lintDir(depDir, opts.level, true) | ||
const logs = depDir ? await lintDir(depDir, opts.level, true) : [] | ||
// log this lint result | ||
@@ -104,3 +104,3 @@ const log = () => { | ||
* @param {string} pkgDir | ||
* @param {import('./index.js').Options['level']} level | ||
* @param {import('../index.js').Options['level']} level | ||
* @param {boolean} [compact] | ||
@@ -118,4 +118,5 @@ */ | ||
}) | ||
if (!rootPkgContent) return { logs } | ||
if (!rootPkgContent) return logs | ||
const rootPkg = JSON.parse(rootPkgContent) | ||
const pkgName = rootPkg.name || path.basename(pkgDir) | ||
const messages = await publint({ pkgDir, level }) | ||
@@ -150,17 +151,17 @@ | ||
if (compact) { | ||
logs.unshift(`${c.red('x')} ${c.bold(rootPkg.name)}`) | ||
logs.unshift(`${c.red('x')} ${c.bold(pkgName)}`) | ||
} else { | ||
logs.unshift(`${c.bold(rootPkg.name)} lint results:`) | ||
logs.unshift(`${c.bold(pkgName)} lint results:`) | ||
} | ||
return { logs } | ||
return logs | ||
} else { | ||
if (compact) { | ||
logs.unshift(`${c.green('✓')} ${c.bold(rootPkg.name)}`) | ||
logs.unshift(`${c.green('✓')} ${c.bold(pkgName)}`) | ||
} else { | ||
logs.unshift(`${c.bold(rootPkg.name)} lint results:`) | ||
logs.unshift(`${c.bold(pkgName)} lint results:`) | ||
logs.push(c.bold(c.green('All good!'))) | ||
} | ||
return { logs } | ||
return logs | ||
} | ||
@@ -179,6 +180,4 @@ } | ||
/** | ||
* | ||
* @param {string} dep | ||
* @param {string} parent | ||
* @returns | ||
*/ | ||
@@ -185,0 +184,0 @@ async function findDepPath(dep, parent) { |
@@ -8,20 +8,9 @@ import fs from 'node:fs/promises' | ||
/** | ||
* @type {import('.').publint} | ||
* @type {import('..').publint} | ||
*/ | ||
export async function publint(options) { | ||
const pkgDir = options?.pkgDir ?? process.cwd() | ||
// Find packed files for `publint` `_include` if no `exports` field. | ||
// This is because without `exports` field, all files can be accessed | ||
// by the consumer, but we need to know which files are packed, | ||
// since those are the ones getting published. | ||
const rootPkgContent = await fs.readFile( | ||
path.join(pkgDir, 'package.json'), | ||
'utf8' | ||
const packedFiles = (await packlist({ path: pkgDir })).map((file) => | ||
path.join(pkgDir, file) | ||
) | ||
const rootPkg = JSON.parse(rootPkgContent) | ||
const hasExports = !!rootPkg.exports | ||
const packedFiles = hasExports | ||
? [] | ||
: (await packlist({ path: pkgDir })).map((file) => path.join(pkgDir, file)) | ||
@@ -31,7 +20,5 @@ return _publint({ | ||
vfs: options?.vfs ?? createNodeVfs(), | ||
level: options?.level, | ||
_include: hasExports | ||
? undefined | ||
: (p) => packedFiles.some((file) => file.startsWith(p)) | ||
level: options?.level ?? 'suggestion', | ||
_packedFiles: packedFiles | ||
}) | ||
} |
export { formatMessagePath, getPkgPathValue } from '../src/utils.js' | ||
/** | ||
* no-op. leave users to implement themselves. | ||
* @type {import('../utils').printMessage} | ||
*/ | ||
export function printMessage() { | ||
return | ||
} |
{ | ||
"name": "publint", | ||
"version": "0.1.8", | ||
"version": "0.1.9", | ||
"description": "Lint packaging errors", | ||
@@ -9,6 +9,6 @@ "type": "module", | ||
"bin": "./lib/cli.js", | ||
"types": "./lib/index.d.ts", | ||
"types": "./index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./lib/index.d.ts", | ||
"types": "./index.d.ts", | ||
"import": { | ||
@@ -21,4 +21,7 @@ "browser": "./lib/browser.js", | ||
"./utils": { | ||
"types": "./lib/utils.d.ts", | ||
"import": "./lib/utils.js" | ||
"types": "./utils.d.ts", | ||
"import": { | ||
"node": "./lib/utils-node.js", | ||
"default": "./lib/utils.js" | ||
} | ||
} | ||
@@ -31,3 +34,4 @@ }, | ||
"lib", | ||
"src" | ||
"src", | ||
"*.d.ts" | ||
], | ||
@@ -54,2 +58,3 @@ "funding": "https://bjornlu.com/sponsor", | ||
"devDependencies": { | ||
"@types/npm-packlist": "3.0.0", | ||
"uvu": "^0.5.6" | ||
@@ -56,0 +61,0 @@ }, |
@@ -57,3 +57,10 @@ <br> | ||
*/ | ||
vfs: createCustomVfsObj() | ||
vfs: createCustomVfsObj(), | ||
/** | ||
* The level of messages to log (default: `'suggestion'`). | ||
* - `suggestion`: logs all messages | ||
* - `warning`: logs only `warning` and `error` messages | ||
* - `error`: logs only `error` messages | ||
*/ | ||
level: 'warning' | ||
}) | ||
@@ -64,4 +71,16 @@ | ||
Extra utilities are exported under `publint/utils`: | ||
```js | ||
import { printMessage } from 'publint/utils' | ||
for (const message of messages) { | ||
// Prints default message in Node.js. Always a no-op in browsers. | ||
// Useful for re-implementing the CLI in a programmatic way. | ||
printMessage(message) | ||
} | ||
``` | ||
## License | ||
MIT |
@@ -14,4 +14,4 @@ import { | ||
* Currently only used if pkg has no `exports` | ||
* @typedef {Required<import('../lib').Options> & { | ||
* _include?: (filePath: string) => boolean | ||
* @typedef {Required<import('..').Options> & { | ||
* _packedFiles?: string[] | ||
* }} Options | ||
@@ -22,6 +22,6 @@ */ | ||
* @param {Options} options | ||
* @returns {Promise<import('../lib').Message[]>} | ||
* @returns {Promise<import('..').Message[]>} | ||
*/ | ||
export async function publint({ pkgDir, vfs, level, _include }) { | ||
/** @type {import('../lib').Message[]} */ | ||
export async function publint({ pkgDir, vfs, level, _packedFiles }) { | ||
/** @type {import('..').Message[]} */ | ||
const messages = [] | ||
@@ -58,7 +58,15 @@ /** | ||
try { | ||
return await vfs.readFile(path) | ||
const content = await vfs.readFile(path) | ||
if (_packedFiles && !_packedFiles.includes(path)) { | ||
fileNotPublished(pkgPath) | ||
} | ||
return content | ||
} catch { | ||
for (const ext of tryExtensions) { | ||
try { | ||
return await vfs.readFile(path + ext) | ||
const content = await vfs.readFile(path + ext) | ||
if (_packedFiles && !_packedFiles.includes(path)) { | ||
fileNotPublished(pkgPath) | ||
} | ||
return content | ||
} catch {} | ||
@@ -76,2 +84,14 @@ } | ||
/** | ||
* @param {string[]} pkgPath | ||
*/ | ||
function fileNotPublished(pkgPath) { | ||
messages.push({ | ||
code: 'FILE_NOT_PUBLISHED', | ||
args: {}, | ||
path: pkgPath, | ||
type: 'error' | ||
}) | ||
} | ||
// Relies on default node resolution | ||
@@ -144,3 +164,3 @@ // https://nodejs.org/api/modules.html#all-together | ||
} | ||
if (expectFormat === 'ESM') { | ||
if (expectFormat === 'ESM' && !exports) { | ||
messages.push({ | ||
@@ -227,3 +247,3 @@ code: 'HAS_ESM_MAIN_BUT_NO_EXPORTS', | ||
vfs, | ||
_include | ||
_packedFiles | ||
) | ||
@@ -230,0 +250,0 @@ const pq = createPromiseQueue() |
@@ -5,3 +5,3 @@ import c from 'picocolors' | ||
/** | ||
* @param {import('../lib').Message} m | ||
* @param {import('..').Message} m | ||
* @param {import('./utils').Pkg} pkg | ||
@@ -13,3 +13,2 @@ */ | ||
// TODO: verbose mode | ||
switch (m.code) { | ||
@@ -40,6 +39,9 @@ case 'IMPLICIT_INDEX_JS_INVALID_FORMAT': | ||
// prettier-ignore | ||
return `${c.bold(fp(m.path))} is ${pv(m.path)} but file does not exist.` | ||
return `${c.bold(fp(m.path))} is ${pv(m.path)} but the file does not exist.` | ||
case 'FILE_NOT_PUBLISHED': | ||
// prettier-ignore | ||
return `${c.bold(fp(m.path))} is ${pv(m.path)} but the file is not published. Is it specified in ${c.bold('pkg.files')}?` | ||
case 'HAS_ESM_MAIN_BUT_NO_EXPORTS': | ||
// prettier-ignore | ||
return `${c.bold('pkg.main')} is an ESM file, but it is usually better to use ${c.bold('pkg.exports')} instead, and remove ${c.bold('pkg.main')} alongside, as compatible NodeJS versions support it as well. (This will be a breaking change)` | ||
return `${c.bold('pkg.main')} is an ESM file, but it is usually better to use ${c.bold('pkg.exports')} instead. If you don't support NodeJS 12.6 and below, you can also remove ${c.bold('pkg.main')}. (This will be a breaking change)` | ||
case 'HAS_MODULE_BUT_NO_EXPORTS': | ||
@@ -73,4 +75,4 @@ // prettier-ignore | ||
default: | ||
// TODO | ||
return | ||
} | ||
} |
@@ -64,7 +64,7 @@ /** | ||
* @param {string} globStr An absolute glob string that must contain one `*` | ||
* @param {import('../lib').Vfs} vfs | ||
* @param {(filePath: string) => boolean} [include] | ||
* @param {import('..').Vfs} vfs | ||
* @param {string[]} [packedFiles] | ||
* @returns {Promise<string[]>} Matched file paths | ||
*/ | ||
export async function exportsGlob(globStr, vfs, include) { | ||
export async function exportsGlob(globStr, vfs, packedFiles) { | ||
let filePaths = [] | ||
@@ -84,3 +84,6 @@ const [dir, ext] = globStr.split('*') | ||
const itemPath = vfs.pathJoin(dirPath, item) | ||
if (!include || include(itemPath)) { | ||
if ( | ||
!packedFiles || | ||
packedFiles.some((file) => file.startsWith(itemPath)) | ||
) { | ||
if (await vfs.isPathDir(itemPath)) { | ||
@@ -98,3 +101,3 @@ await scanDir(itemPath) | ||
* @param {string} filePath | ||
* @param {import('../lib').Vfs} vfs | ||
* @param {import('..').Vfs} vfs | ||
* @returns {Promise<CodeFormat>} | ||
@@ -106,3 +109,3 @@ */ | ||
const nearestPkg = await getNearestPkg(filePath, vfs) | ||
return nearestPkg.type === 'module' ? 'ESM' : 'CJS' | ||
return nearestPkg?.type === 'module' ? 'ESM' : 'CJS' | ||
} | ||
@@ -134,4 +137,4 @@ | ||
* @param {string} filePath | ||
* @param {import('../lib').Vfs} vfs | ||
* @returns {Promise<Pkg>} | ||
* @param {import('..').Vfs} vfs | ||
* @returns {Promise<Pkg | undefined>} | ||
*/ | ||
@@ -138,0 +141,0 @@ export async function getNearestPkg(filePath, vfs) { |
@@ -7,3 +7,3 @@ import fs from 'node:fs' | ||
* Creates a node-compatible Vfs object | ||
* @returns {import('../lib').Vfs} | ||
* @returns {import('..').Vfs} | ||
*/ | ||
@@ -10,0 +10,0 @@ export function createNodeVfs() { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38494
14
1050
85
2