Skip to content

Commit

Permalink
feat(compas): add support for npx compas@latest init
Browse files Browse the repository at this point in the history
This self inits according to the docs at https://compasjs.com/docs/getting-started.html#installation
  • Loading branch information
dirkdev98 committed Aug 22, 2023
1 parent 2b2f2bf commit 5137c8a
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 9 deletions.
49 changes: 40 additions & 9 deletions packages/compas/src/cli/bin.js
@@ -1,17 +1,48 @@
#!/usr/bin/env node

import { existsSync } from "node:fs";
import { isNil } from "@compas/stdlib";
import { configLoadEnvironment } from "../config.js";
import { ciMode } from "../main/ci/index.js";
import { developmentMode } from "../main/development/index.js";
import { productionMode } from "../main/production/index.js";

const env = await configLoadEnvironment("", !isNil(process.env.NODE_ENV));
// Just execute some temporary command matching
const args = process.argv.slice(2);
const debug = args.includes("--debug");

if (env.isCI) {
await ciMode(env);
} else if (!env.isDevelopment) {
await productionMode(env);
if (debug) {
args.splice(args.indexOf("--debug"), 1);
}

if (args.length === 0) {
if (!existsSync("./package.json")) {
// eslint-disable-next-line no-console
console.log(`Please run 'npx compas@latest init' to install Compas.`);
} else {
// TODO: check if we are in a project or someone forgot to run 'compas init'.

// TODO: debug

const env = await configLoadEnvironment("", !isNil(process.env.NODE_ENV));

if (env.isCI) {
const { ciMode } = await import("../main/ci/index.js");
await ciMode(env);
} else if (!env.isDevelopment) {
const { productionMode } = await import("../main/production/index.js");
await productionMode(env);
} else {
const { developmentMode } = await import("../main/development/index.js");
await developmentMode(env);
}
}
} else if (args.length === 1) {
if (args[0] === "init") {
const { initCompas } = await import("../main/init/compas.js");
await initCompas();
}
} else {
await developmentMode(env);
// eslint-disable-next-line no-console
console.log(`Unsupported command. Available commands:
- compas
- compas init`);
}
36 changes: 36 additions & 0 deletions packages/compas/src/config.js
Expand Up @@ -83,6 +83,42 @@ APP_NAME=${dirname}
return env;
}

/**
* Load .env if exists, but other than that prepare for development mode.
*
* @returns {Promise<ConfigEnvironment>}
*/
export async function configLoadReadOnlyEnvironment() {
// Load .env.local first, since existing values in `process.env` are not overwritten.
dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });
dotenv.config();

refreshEnvironmentCache();

environment.COMPAS_LOG_PRINTER = "pretty";

const packageJson = JSON.parse(
await readFile(
pathJoin(dirnameForModule(import.meta), "../package.json"),
"utf-8",
),
);

const env = {
isCI: environment.CI === "true",
isDevelopment: isNil(process.env.NODE_ENV) || !isProduction(),
appName: environment.APP_NAME ?? process.cwd().split(path.sep).pop(),
compasVersion: packageJson.version
? `Compas ${packageJson.version}`
: "Compas v0.0.0",
nodeVersion: process.version,
};

loggerDetermineDefaultDestination();

return env;
}

/**
* Try to load the config recursively from disk.
*
Expand Down
143 changes: 143 additions & 0 deletions packages/compas/src/main/init/compas.js
@@ -0,0 +1,143 @@
import { existsSync } from "node:fs";
import { readFile, writeFile } from "node:fs/promises";
import { exec, newLogger, spawn } from "@compas/stdlib";
import { configLoadReadOnlyEnvironment } from "../../config.js";
import { logger, loggerEnable } from "../../output/log.js";
import { packageManagerDetermineInstallCommand } from "../../package-manager.js";

export async function initCompas() {
const env = await configLoadReadOnlyEnvironment();

loggerEnable(
newLogger({
ctx: {
type: env.appName,
},
}),
);
if (env.isCI) {
logger.info({
message: "Compas init is not supported in CI.",
});
return;
}

if (!env.isDevelopment) {
logger.info({
message:
"Compas init is not supported when NODE_ENV is explicitly set, but is not 'development'.",
});
return;
}

if (existsSync("package.json")) {
await initCompasInExistingProject(env);
} else {
await initCompasInNewProject(env);
}
}

async function initCompasInExistingProject(env) {
const packageJson = JSON.parse(await readFile("package.json", "utf-8"));

let alreadyInstalled = false;
const compasVersion = env.compasVersion.split("v").pop();

if (packageJson.dependencies?.compas) {
alreadyInstalled = packageJson.dependencies.compas === compasVersion;
packageJson.dependencies.compas = compasVersion;
} else if (packageJson.devDependencies?.compas) {
alreadyInstalled = packageJson.devDependencies.compas === compasVersion;
packageJson.devDependencies.compas = compasVersion;
} else {
packageJson.dependencies ??= {};
packageJson.dependencies.compas = compasVersion;
}

if (!alreadyInstalled) {
logger.info(
`Patching package.json with ${env.compasVersion} and installing dependencies...`,
);
await writeFile(
"package.json",
`${JSON.stringify(packageJson, null, 2)}\n`,
);
const packageManagerCommand = packageManagerDetermineInstallCommand();
await spawn(packageManagerCommand[0], packageManagerCommand.slice(1));

logger.info(`
Ready to roll! Run 'npx compas' to start the Compas development environment.
Tip: See https://compasjs.com/docs/getting-started.html#development-setup on how to run 'compas' without the 'npx' prefix.
`);
} else {
logger.info("Already up-to-date!");
}
}

async function initCompasInNewProject(env) {
const compasVersion = env.compasVersion.split("v").pop();
const packageJson = {
name: env.appName,
private: true,
version: "0.0.1",
type: "module",
scripts: {},
keywords: [],
dependencies: {
compas: compasVersion,
},
};

await writeFile("package.json", `${JSON.stringify(packageJson, null, 2)}\n`);

logger.info("Created a package.json. Installing with npm...");

const packageManagerCommand = packageManagerDetermineInstallCommand();
await spawn(packageManagerCommand[0], packageManagerCommand.slice(1));

if (!existsSync(".gitignore")) {
await writeFile(
".gitignore",
`# Compas
.cache
.env.local
generated
# OS
.DS_Store
# IDE
.idea
.vscode
# Dependencies
node_modules
# Log files
*-debug.log
*-error.log
# Tests
coverage
`,
);
}

if ((await exec("git --version")).exitCode === 0 && !existsSync(".git")) {
logger.info("Initializing a Git repository.");

await exec("git init");
await exec("git checkout -b main");
await exec("git add -A");
await exec(`git commit -m "Initialized project with ${env.compasVersion}"`);
}

logger.info(`
Ready to roll! Run 'npx compas' to start the Compas development environment.
You can switch to a different supported package manager like Yarn or pnpm by removing the created package-lock.json and running the equivalent of 'npm install' with your favorite package manager.
Tip: See https://compasjs.com/docs/getting-started.html#development-setup on how to run 'compas' without the 'npx' prefix.
`);
}
19 changes: 19 additions & 0 deletions packages/compas/src/package-manager.js
@@ -0,0 +1,19 @@
import { existsSync } from "node:fs";

/**
* Determine package manager command to use for installing dependencies.
*
* @returns {string[]}
*/
export function packageManagerDetermineInstallCommand() {
if (existsSync("package-lock.json")) {
return ["npm", "install"];
} else if (existsSync("yarn.lock")) {
return ["yarn"];
} else if (existsSync("pnpm-lock.yaml")) {
return ["pnpm", "install"];
}

// Default to NPM
return ["npm", "install"];
}

0 comments on commit 5137c8a

Please sign in to comment.