@compas/cli
Version:
CLI containing utilities and simple script runner
187 lines (166 loc) • 4.56 kB
JavaScript
import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import {
dirnameForModule,
exec,
isNil,
pathJoin,
processDirectoryRecursive,
} from "@compas/stdlib";
/**
* @type {import("../../generated/common/types.js").CliCommandDefinitionInput}
*/
export const cliDefinition = {
name: "check-env",
shortDescription: "Various checks helping with a better Compas experience.",
longDescription: `This command is able to check a few things in the current project, to see if it is optimally configured.
- '.env.local' should be in the .gitignore
- '.cache' should be in the .gitignore
- Only a single Compas version should be installed
- Docker should be available to use with \`compas docker\`
`,
executor: cliExecutor,
};
/**
*
* @param {import("@compas/stdlib").Logger} logger
* @returns {Promise<import("../../cli/types.js").CliResult>}
*/
export async function cliExecutor(logger) {
const { version: compasVersion } = JSON.parse(
await readFile(
pathJoin(dirnameForModule(import.meta), "../../../package.json"),
"utf-8",
),
);
logger.info(`
Running 'compas check-env':
Versions:
- Compas: v${compasVersion}
- Node.js: ${process.version}
`);
const results = (
await Promise.all([
areOtherCompasVersionsInstalled(compasVersion),
isEnvLocalNotIgnored(),
isDotCacheNotIgnored(),
isDockerInstalled(),
])
).filter((it) => it.failed);
if (results.length) {
logger.info(`Setup checks:
${results.map((it) => it.message ?? "").join("\n")}
`);
} else {
logger.info("Static setup checks passed!\n");
}
return {
exitStatus: results.length ? "failed" : "passed",
};
}
/**
* If a `.env.local` exists, it should be ignored
*
* @returns {Promise<{ failed: boolean, message?: string }>}
*/
async function isEnvLocalNotIgnored() {
let result;
if (!existsSync("./.env.local")) {
result = false;
} else {
const { exitCode } = await exec(`git check-ignore -q .env.local`);
// 128 if no git repo exists in directory
result = exitCode !== 0 && exitCode !== 128;
}
if (!result) {
return {
failed: false,
};
}
return {
failed: true,
message:
"- '.env.local' found but not git ignored. Please add it to your '.gitignore'.",
};
}
/**
* Checks if the `.cache` folder is ignored.
*
* @returns {Promise<{ failed: boolean, message?: string }>}
*/
async function isDotCacheNotIgnored() {
const { exitCode } = await exec(`git check-ignore -q .cache`);
// 128 if no git repo exists in directory
const result = exitCode !== 0 && exitCode !== 128;
if (!result) {
return {
failed: false,
};
}
return {
failed: true,
message:
"- '.cache' is not git ignored. Please add it to your '.gitignore'. This directory is used to cache results for tools like Prettier and ESLint.",
};
}
/**
* Multiple versions of compas conflict with the use of ES Module live bindings, which
* leads to weird behaviour. So warn if that is happening.
*
* @param {string} compasVersion
* @returns {Promise<{ failed: boolean, message?: string }>}
*/
async function areOtherCompasVersionsInstalled(compasVersion) {
const foundVersions = [];
await processDirectoryRecursive(
process.cwd(),
async (file) => {
if (file.endsWith("/@compas/stdlib/package.json")) {
const { version } = JSON.parse(await readFile(file, "utf-8"));
if (
version !== compasVersion &&
isNil(foundVersions.find((it) => it.version === version))
) {
foundVersions.push({
path: file,
version,
});
}
}
},
{
skipNodeModules: false,
},
);
if (foundVersions.length > 0) {
let message = `- Multiple @compas/stdlib versions found, to to ensure a stable experience use the version that is commonly accepted by all your dependencies.`;
for (const { path, version } of foundVersions) {
message += `\n - v${version} - ${path}`;
}
return {
failed: true,
message,
};
}
return {
failed: false,
};
}
/**
* Docker is necessary for `compas docker` usage
*
* @returns {Promise<{ failed: boolean, message?: string }>}
*/
async function isDockerInstalled() {
try {
await exec("docker -v");
return {
failed: false,
};
} catch {
return {
failed: true,
message: `- Could not locate a local 'docker' executable. This is necessary for the 'compas docker' commands.`,
};
}
}