From 38fdcca845bd5667f3780bebb6c8cd20c10a5145 Mon Sep 17 00:00:00 2001 From: Timur Moziev Date: Wed, 4 Sep 2024 04:08:26 +0000 Subject: [PATCH] add monorepo logic to developments update dependencies update logic --- anca.json | 4 +- src/actions/nodejs.ts | 103 ++++++++++++++++++++++++++++++++---------- src/api/nodejs-npm.ts | 5 ++ src/cinnabar.ts | 4 +- src/developments.ts | 73 +++++++++++++++++++++--------- src/tui.ts | 5 ++ 6 files changed, 143 insertions(+), 51 deletions(-) diff --git a/anca.json b/anca.json index b11fd04..ac5bd27 100644 --- a/anca.json +++ b/anca.json @@ -13,8 +13,8 @@ "dataVersion": 0, "version": { "latest": "0.1.0-dev.2", - "latestNext": "0.1.0-dev.2+next.20240902_221440", - "timestamp": 1725315280 + "latestNext": "0.1.0-dev.2+next.20240904_035621", + "timestamp": 1725422181 }, "files": [ { diff --git a/src/actions/nodejs.ts b/src/actions/nodejs.ts index 6b2d2f8..7450913 100644 --- a/src/actions/nodejs.ts +++ b/src/actions/nodejs.ts @@ -1,4 +1,5 @@ /* eslint-disable sonarjs/no-duplicate-string */ +import { exec } from "child_process"; import { promptText } from "clivo"; import { isDeepStrictEqual } from "util"; @@ -83,6 +84,23 @@ const packageNameOrder = [ "pre-commit", ]; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const SCRIPTS_API: Record = { + build: "node ../esbuild.js", + dev: 'tsc-watch --onSuccess "node ./build/dev/src/index.js"', + fix: "prettier . --write --ignore-path ../.gitignore --ignore-path ../.prettierignore && eslint --fix .", + format: + "prettier . --write --ignore-path ../.gitignore --ignore-path ../.prettierignore", + lint: "eslint --fix .", + "migration:down": "knex migrate:rollback", + "migration:ls": "npx knex migrate:list", + "migration:restart": "knex migrate:rollback && knex migrate:latest", + "migration:up": "knex migrate:latest", + prepack: "npm run build", + start: "node dist/index.js", + test: "prettier . -c && eslint --max-warnings 0 . && tsc && mocha './build/dev/test'", +}; + const SCRIPTS_APP: Record = { build: "node esbuild.js", "build:bundle": "node esbuild.js full", @@ -106,6 +124,12 @@ const SCRIPTS_LIB: Record = { test: "prettier . -c && eslint --max-warnings 0 . && tsc && mocha './build/dev/test'", }; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const DEPENDENCIES_API: string[] = []; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const DEV_DEPENDENCIES_API: string[] = []; + const DEV_DEPENDENCIES_APP: string[] = [ "@cinnabar-forge/eslint-plugin", "@cinnabar-forge/meta", @@ -241,7 +265,7 @@ export async function checkNodejsPackageJson( hasPrivate(contents) && hasPublishConfig(contents) && hasWorkspaces(contents) && - hasPreCommit(contents) && + hasPreCommit(contents, development.monorepoPart ? true : false) && checkPackageJsonOrder(contents) ); } @@ -590,9 +614,10 @@ function hasWorkspaces(contents: NodejsPackageJson) { /** * * @param contents + * @param isMonorepoPart */ -function hasPreCommit(contents: NodejsPackageJson) { - return contents["pre-commit"] != null; +function hasPreCommit(contents: NodejsPackageJson, isMonorepoPart: boolean) { + return isMonorepoPart || contents["pre-commit"] != null; } /** @@ -662,7 +687,7 @@ export async function fixNodejsPackageJson( await fixPackageScripts(rebuildFile, contents, config); await fixPackageConfig(rebuildFile, contents); await updateNodejsPackageJsonDependencies(rebuildFile, development); - await updateNodejsPackageJsonDevDependencies(rebuildFile, development); + await updateNodejsPackageJsonDevDependencies(rebuildFile, development, true); await fixPackagePeerDependencies(rebuildFile, contents); await fixPackagePeerDependenciesMeta(rebuildFile, contents); await fixPackageBundleDependencies(rebuildFile, contents); @@ -685,6 +710,26 @@ export async function fixNodejsPackageJson( development.state.jsonFiles["package.json"] = rebuildFile; } +/** + * + * @param development + */ +export async function installNodejsDependencies(development: AncaDevelopment) { + // eslint-disable-next-line security/detect-child-process + exec(`cd ${development.fullPath} && npm i`, (error, stdout, stderr) => { + if (error) { + console.log( + `package.json has been updated, but can't use 'npm i', please use it in ${development.fullPath} to install dependencies manually`, + ); + return; + } + console.log(`npm install output: ${stdout}`); + if (stderr) { + console.error(`npm install errors: ${stderr}`); + } + }); +} + /** * * @param rebuildFile @@ -1062,25 +1107,26 @@ export async function updateNodejsPackageJsonDependencies( rebuildFile.dependencies = {}; - const predefinedDependencies: string[] = []; - - const allDependencies = new Set([ - ...predefinedDependencies, - ...Object.keys(contents.dependencies), - ]); + const allDependencies = Object.keys(contents.dependencies); const allDependenciesList = Array.from(allDependencies).sort(); try { - const fetchedVersions = await fetchNpmPackagesVersion(allDependenciesList); + const fetchedVersions = await fetchNpmPackagesVersion( + allDependenciesList.filter((dep: string) => !dep.includes("file:")), + ); for (const pkg of allDependenciesList) { - if (fetchedVersions[pkg] !== contents.dependencies[pkg]) { + if ( + fetchedVersions[pkg] !== contents.dependencies[pkg] && + fetchedVersions[pkg] + ) { console.log( `Updating dep '${pkg}' from ${contents.dependencies[pkg]} to ${fetchedVersions[pkg]}`, ); } - rebuildFile.dependencies[pkg] = fetchedVersions[pkg]; + rebuildFile.dependencies[pkg] = + fetchedVersions[pkg] || contents.dependencies[pkg]; } } catch (error) { console.error("Error updating dependencies:", error); @@ -1091,10 +1137,12 @@ export async function updateNodejsPackageJsonDependencies( * * @param rebuildFile * @param development + * @param addPredefined */ export async function updateNodejsPackageJsonDevDependencies( rebuildFile: NodejsPackageJson, development: AncaDevelopment, + addPredefined: boolean, ) { if (development.state == null) { return; @@ -1112,30 +1160,35 @@ export async function updateNodejsPackageJsonDevDependencies( rebuildFile.devDependencies = {}; - const predefinedDevDependencies = - development.state.config.type === "app" - ? DEV_DEPENDENCIES_APP - : DEV_DEPENDENCIES_LIB; - - const allDevDependencies = new Set([ - ...predefinedDevDependencies, - ...Object.keys(contents.devDependencies), - ]); + const allDevDependencies = addPredefined + ? new Set([ + ...(development.state.config.type === "app" + ? DEV_DEPENDENCIES_APP + : development.state.config.type === "library" + ? DEV_DEPENDENCIES_LIB + : []), + ...Object.keys(contents.devDependencies), + ]) + : Object.keys(contents.devDependencies); const allDevDependenciesList = Array.from(allDevDependencies).sort(); try { const fetchedVersions = await fetchNpmPackagesVersion( - allDevDependenciesList, + allDevDependenciesList.filter((dep: string) => !dep.includes("file:")), ); for (const pkg of allDevDependenciesList) { - if (fetchedVersions[pkg] !== contents.devDependencies[pkg]) { + if ( + fetchedVersions[pkg] !== contents.devDependencies[pkg] && + fetchedVersions[pkg] + ) { console.log( `Updating dev-dep '${pkg}' from ${contents.devDependencies[pkg]} to ${fetchedVersions[pkg]}`, ); } - rebuildFile.devDependencies[pkg] = fetchedVersions[pkg]; + rebuildFile.devDependencies[pkg] = + fetchedVersions[pkg] || contents.devDependencies[pkg]; } } catch (error) { console.error("Error updating devDependencies:", error); diff --git a/src/api/nodejs-npm.ts b/src/api/nodejs-npm.ts index 866e092..4bfbe53 100644 --- a/src/api/nodejs-npm.ts +++ b/src/api/nodejs-npm.ts @@ -7,10 +7,15 @@ import fetch from "node-fetch"; export async function fetchNpmPackagesVersion( packagesName: string[], ): Promise> { + if (packagesName.length === 0) { + return {}; + } const baseUrl = "https://npm-versions.cinnabar.ru/versions"; const query = `?packages=${packagesName.join(",")}`; const url = `${baseUrl}${query}`; + console.log("Requesting from Cinnabar Forge NPM Cache:", url); + try { const response = await fetch(url); if (!response.ok) { diff --git a/src/cinnabar.ts b/src/cinnabar.ts index 82b84f0..139aa40 100644 --- a/src/cinnabar.ts +++ b/src/cinnabar.ts @@ -1,4 +1,4 @@ // This file was generated by Cinnabar Meta. Do not edit. -export const CINNABAR_PROJECT_TIMESTAMP = 1725315280; -export const CINNABAR_PROJECT_VERSION = "0.1.0-dev.2+next.20240902_221440"; +export const CINNABAR_PROJECT_TIMESTAMP = 1725422181; +export const CINNABAR_PROJECT_VERSION = "0.1.0-dev.2+next.20240904_035621"; diff --git a/src/developments.ts b/src/developments.ts index c34e3a9..be6f1a5 100644 --- a/src/developments.ts +++ b/src/developments.ts @@ -103,7 +103,9 @@ export async function getDevelopmentStatus(development: AncaDevelopment) { ) { statuses.unshift( development.state.config.monorepo != null - ? "monorepo" + ? development.state.config.stack + ? `${development.state.config.stack} monorepo` + : "monorepo" : `${development.state.config.stack || "unsupported"} ${development.state.config.type || "project"}`, ); } @@ -158,10 +160,21 @@ export async function refreshDevelopmentState( "anca.json", )) as AncaConfig | null; - if (config != null && development.monorepoPart != null) { - config = mergician(config, development.monorepoPart); - config.monorepo = undefined; - console.log(config); + if (config != null) { + if (development.monorepoPart != null) { + config = mergician(config, development.monorepoPart); + config.monorepo = undefined; + } else if (config.monorepo != null && config.stack == null) { + const stacks = new Set(); + for (const part of config.monorepo) { + if (part.data.stack != null) { + stacks.add(part.data.stack); + } + } + if (stacks.size === 1) { + config.stack = stacks.values().next().value; + } + } } const state: AncaDevelopmentState = { @@ -191,26 +204,14 @@ export async function refreshDevelopmentState( await addCommonToDevelopmentPack(development); await addDevcontainersToDevelopmentPack(development); await addGithubActionsToDevelopmentPack(development); - if (state.config.stack === "nodejs") { - await addNodeJsToDevelopmentPack(development); - } + await addNodeJsToDevelopmentPack(development); state.meta = getDevelopmentMeta(development); await checkCommonToDevelopmentPack(development); await checkDevcontainersToDevelopmentPack(development); await checkGithubActionsToDevelopmentPack(development); - if (state.config.stack === "nodejs") { - await checkNodeJsToDevelopmentPack(development); - } - - // const folder = path.join(".", "data", "tmp", development.data.folder); - // fs.mkdirSync(folder, { recursive: true }); - // await writeFolderJsonFile( - // folder, - // development.data.name + ".json", - // development, - // ); + await checkNodeJsToDevelopmentPack(development); } /** @@ -249,6 +250,9 @@ async function addMetaToDevelopmentPack(development: AncaDevelopment) { if (development.state == null) { return; } + if (development.monorepoPart != null) { + return; + } await addFileToPack(development, "cinnabar.json"); await addFileToPack(development, "version.json"); @@ -262,6 +266,9 @@ async function addCommonToDevelopmentPack(development: AncaDevelopment) { if (development.state == null) { return; } + if (development.monorepoPart != null) { + return; + } await addFileToPack(development, ".gitignore"); await addFileToPack(development, "CONTRIBUTING.md"); @@ -277,6 +284,9 @@ async function checkCommonToDevelopmentPack(development: AncaDevelopment) { if (development.state == null) { return; } + if (development.monorepoPart != null) { + return; + } if (!(await checkGitIgnore(development))) { development.state.issues.push("gitIgnoreSetToDefault"); @@ -303,6 +313,9 @@ async function addDevcontainersToDevelopmentPack(development: AncaDevelopment) { if (development.state == null) { return; } + if (development.monorepoPart != null) { + return; + } await addJsonFileToPack(development, ".devcontainer/devcontainer.json"); await addFileToPack(development, ".devcontainer/Dockerfile"); @@ -318,6 +331,9 @@ async function checkDevcontainersToDevelopmentPack( if (development.state == null) { return; } + if (development.monorepoPart != null) { + return; + } if (!(await checkDevcontainerJson(development))) { development.state.issues.push("devcontainerJsonSetToDefault"); @@ -336,6 +352,9 @@ async function addGithubActionsToDevelopmentPack(development: AncaDevelopment) { if (development.state == null) { return; } + if (development.monorepoPart != null) { + return; + } if (development.state.config.stack !== "nodejs") { return; } @@ -354,6 +373,9 @@ async function checkGithubActionsToDevelopmentPack( if (development.state == null) { return; } + if (development.monorepoPart != null) { + return; + } if (development.state.config.stack !== "nodejs") { return; } @@ -379,12 +401,16 @@ async function addNodeJsToDevelopmentPack(development: AncaDevelopment) { if (development.state == null) { return; } + if (development.state.config.monorepo != null) { + return; + } if (development.state.config.stack !== "nodejs") { return; } await addFileToPack(development, ".prettierignore"); await addFileToPack(development, ".prettierrc"); + await addFileToPack( development, development.state.config.type === "library" @@ -393,7 +419,7 @@ async function addNodeJsToDevelopmentPack(development: AncaDevelopment) { ); await addFileToPack(development, "eslint.config.js"); await addJsonFileToPack(development, "package.json"); - if (development.state.config.type !== "library") { + if (development.state.config.type === "app") { await addFileToPack(development, "sea.build.js"); await addJsonFileToPack(development, "sea.config.json"); } @@ -408,6 +434,9 @@ async function checkNodeJsToDevelopmentPack(development: AncaDevelopment) { if (development.state == null) { return; } + if (development.state.config.monorepo != null) { + return; + } if (development.state.config.stack !== "nodejs") { return; } @@ -421,7 +450,7 @@ async function checkNodeJsToDevelopmentPack(development: AncaDevelopment) { } if ( - development.state.config.type !== "library" && + development.state.config.type === "app" && !(await checkNodejsEsbuildJs(development)) ) { development.state.issues.push("nodejsEsbuildSetToDefault"); @@ -439,7 +468,7 @@ async function checkNodeJsToDevelopmentPack(development: AncaDevelopment) { } development.state.actions.push("nodejsPackageJsonCheckUpdates"); - if (development.state.config.type !== "library") { + if (development.state.config.type === "app") { if (!(await checkNodejsSeaBuildJs(development))) { development.state.issues.push("nodejsSeaBuildJsSetToDefault"); } diff --git a/src/tui.ts b/src/tui.ts index 4e2d187..359b6f8 100644 --- a/src/tui.ts +++ b/src/tui.ts @@ -17,6 +17,7 @@ import { fixLicenseMd } from "./actions/license.js"; import { NodejsPackageJson, fixNodejsPackageJson, + installNodejsDependencies, updateNodejsPackageJsonDependencies, updateNodejsPackageJsonDevDependencies, writeNodejsPackageJson, @@ -173,10 +174,12 @@ async function showDevelopmentActions( await updateNodejsPackageJsonDevDependencies( rebuildFile, development, + false, ); fileContents.dependencies = rebuildFile.dependencies; fileContents.devDependencies = rebuildFile.devDependencies; await writeNodejsPackageJson(development); + await installNodejsDependencies(development); } await backHere(); }, @@ -186,6 +189,7 @@ async function showDevelopmentActions( action: async () => { await fixNodejsPackageJson(development, false); await writeNodejsPackageJson(development); + await installNodejsDependencies(development); await backHere(); }, label: "[package.json] Fix", @@ -194,6 +198,7 @@ async function showDevelopmentActions( action: async () => { await fixNodejsPackageJson(development, true); await writeNodejsPackageJson(development); + await installNodejsDependencies(development); await backHere(); }, label: "[package.json] Fix & add optional fields",