123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- import type { CAC } from 'cac';
- import type { Result } from 'publint';
- import { basename, dirname, join } from 'node:path';
- import {
- colors,
- consola,
- ensureFile,
- findMonorepoRoot,
- generatorContentHash,
- getPackages,
- outputJSON,
- readJSON,
- UNICODE,
- } from '@vben/node-utils';
- import { publint } from 'publint';
- import { formatMessage } from 'publint/utils';
- const CACHE_FILE = join(
- 'node_modules',
- '.cache',
- 'publint',
- '.pkglintcache.json',
- );
- interface PubLintCommandOptions {
- /**
- * Only errors are checked, no program exit is performed
- */
- check?: boolean;
- }
- /**
- * Get files that require lint
- * @param files
- */
- async function getLintFiles(files: string[] = []) {
- const lintFiles: string[] = [];
- if (files?.length > 0) {
- return files.filter((file) => basename(file) === 'package.json');
- }
- const { packages } = await getPackages();
- for (const { dir } of packages) {
- lintFiles.push(join(dir, 'package.json'));
- }
- return lintFiles;
- }
- function getCacheFile() {
- const root = findMonorepoRoot();
- return join(root, CACHE_FILE);
- }
- async function readCache(cacheFile: string) {
- try {
- await ensureFile(cacheFile);
- return await readJSON(cacheFile);
- } catch {
- return {};
- }
- }
- async function runPublint(files: string[], { check }: PubLintCommandOptions) {
- const lintFiles = await getLintFiles(files);
- const cacheFile = getCacheFile();
- const cacheData = await readCache(cacheFile);
- const cache: Record<string, { hash: string; result: Result }> = cacheData;
- const results = await Promise.all(
- lintFiles.map(async (file) => {
- try {
- const pkgJson = await readJSON(file);
- if (pkgJson.private) {
- return null;
- }
- Reflect.deleteProperty(pkgJson, 'dependencies');
- Reflect.deleteProperty(pkgJson, 'devDependencies');
- Reflect.deleteProperty(pkgJson, 'peerDependencies');
- const content = JSON.stringify(pkgJson);
- const hash = generatorContentHash(content);
- const publintResult: Result =
- cache?.[file]?.hash === hash
- ? (cache?.[file]?.result ?? [])
- : await publint({
- level: 'suggestion',
- pkgDir: dirname(file),
- strict: true,
- });
- cache[file] = {
- hash,
- result: publintResult,
- };
- return { pkgJson, pkgPath: file, publintResult };
- } catch {
- return null;
- }
- }),
- );
- await outputJSON(cacheFile, cache);
- printResult(results, check);
- }
- function printResult(
- results: Array<null | {
- pkgJson: Record<string, number | string>;
- pkgPath: string;
- publintResult: Result;
- }>,
- check?: boolean,
- ) {
- let errorCount = 0;
- let warningCount = 0;
- let suggestionsCount = 0;
- for (const result of results) {
- if (!result) {
- continue;
- }
- const { pkgJson, pkgPath, publintResult } = result;
- const messages = publintResult?.messages ?? [];
- if (messages?.length < 1) {
- continue;
- }
- consola.log('');
- consola.log(pkgPath);
- for (const message of messages) {
- switch (message.type) {
- case 'error': {
- errorCount++;
- break;
- }
- case 'suggestion': {
- suggestionsCount++;
- break;
- }
- case 'warning': {
- warningCount++;
- break;
- }
- // No default
- }
- const ruleUrl = `https://publint.dev/rules#${message.code.toLocaleLowerCase()}`;
- consola.log(
- ` ${formatMessage(message, pkgJson)}${colors.dim(` ${ruleUrl}`)}`,
- );
- }
- }
- const totalCount = warningCount + errorCount + suggestionsCount;
- if (totalCount > 0) {
- consola.error(
- colors.red(
- `${UNICODE.FAILURE} ${totalCount} problem (${errorCount} errors, ${warningCount} warnings, ${suggestionsCount} suggestions)`,
- ),
- );
- !check && process.exit(1);
- } else {
- consola.log(colors.green(`${UNICODE.SUCCESS} No problem`));
- }
- }
- function definePubLintCommand(cac: CAC) {
- cac
- .command('publint [...files]')
- .usage('Check if the monorepo package conforms to the publint standard.')
- .option('--check', 'Only errors are checked, no program exit is performed.')
- .action(runPublint);
- }
- export { definePubLintCommand };
|