startx 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/cli/package.json +2 -2
- package/apps/startx-cli/dist/index.mjs +1 -1
- package/apps/startx-cli/package.json +6 -5
- package/apps/startx-cli/src/commands/package.ts +122 -19
- package/apps/web-client/package.json +1 -1
- package/configs/eslint-config/package.json +19 -19
- package/configs/vitest-config/package.json +1 -1
- package/package.json +1 -1
- package/packages/@repo/lib/package.json +3 -3
- package/packages/@repo/lib/src/file-system-module/index.ts +1 -1
- package/packages/aix/package.json +4 -4
- package/packages/ui/package.json +19 -19
- package/pnpm-workspace.yaml +57 -0
package/apps/cli/package.json
CHANGED
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"vitest-config": "workspace:*"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@inquirer/prompts": "
|
|
30
|
+
"@inquirer/prompts": "catalog:",
|
|
31
31
|
"@repo/logger": "workspace:*",
|
|
32
32
|
"@repo/mail": "workspace:*",
|
|
33
33
|
"@repo/lib": "workspace:*",
|
|
34
34
|
"@repo/env": "workspace:*",
|
|
35
|
-
"commander": "
|
|
35
|
+
"commander": "catalog:",
|
|
36
36
|
"@repo/queue": "workspace:*",
|
|
37
37
|
"aix": "workspace:*"
|
|
38
38
|
},
|
|
@@ -223,4 +223,4 @@ export default extend(baseConfig);
|
|
|
223
223
|
`),l&&await o.writeFile(t.join(a,`vitest.config.ts`),`import vitestConfig from "vitest-config/node";
|
|
224
224
|
|
|
225
225
|
export default vitestConfig;
|
|
226
|
-
`),Z_.info(`Created package ${r} at ${t.relative(i.workspace,a)}`),Z_.info("Run `pnpm install` to link the new package.")}static async resolveEslintPreference(e){if(e.eslint===!1)return!1;let n=Iy.getDirectory(),r=await this.readRootPackage(n.workspace);return this.hasDependency(r,`eslint`)?e.eslint??!0:e.eslint===!0||await Xx.confirm({message:`ESLint is not installed in this monorepo. Install and enable it?`,default:!0})?(r.devDependencies={...r.devDependencies,eslint:await this.resolveDependencyVersion(n.workspace,`eslint`)},await this.writeJson(t.join(n.workspace,`package.json`),r),Z_.info(`Added eslint to the root devDependencies.`),e.install!==!1&&await this.installRootDependencies(n.workspace),!0):!1}static async getInstallTags(e){let t=new Set([`common`,`node`]),n=await this.readRootPackage(e.workspace);e.eslintEnabled&&t.add(`eslint`),this.hasDependency(n,`@biomejs/biome`)&&t.add(`biome`),this.hasDependency(n,`prettier`)&&t.add(`prettier`),this.hasDependency(n,`vitest`)&&t.add(`vitest`),this.hasDependency(n,`tsdown`)&&t.add(`tsdown`);for(let n of e.packages)n.packageJson?.startx?.tags?.forEach(e=>t.add(e)),n.packageJson?.startx?.iTags?.forEach(e=>t.add(e)),n.packageJson?.startx?.gTags?.forEach(e=>t.add(e)),(n.type===`apps`||n.packageJson?.startx?.mode===`standalone`)&&t.add(`runnable`);return Array.from(t)}static resolvePackageClosure(e){let t=new Map,n=[e.selectedPackage],r=r=>{let i=this.findPackage(e.packages,r);i&&!t.has(i.name)&&n.push(i)};for(e.includeEslintConfig&&r(`eslint-config`);n.length>0;){let e=n.shift();if(!t.has(e.name)){t.set(e.name,e);for(let t of[...e.packageJson?.startx?.requiredDeps??[],...e.packageJson?.startx?.requiredDevDeps??[]])r(t)}}return Array.from(t.values())}static async ensureTemplatePackage(e){let t=this.findPackage(e.packages,e.name);if(!t){Z_.warn(`Could not find template package ${e.name}; skipping.`);return}await this.installTemplatePackage({pkg:t,directory:e.directory,tags:e.tags})}static async installTemplatePackage(e){if(!e.pkg.packageJson)throw Error(`Missing package.json for ${e.pkg.name}`);let n=e.overrideRelativePath??e.pkg.relativePath,r=t.join(e.directory.workspace,n);if(await this.pathExists(t.join(r,`package.json`))&&!await Xx.confirm({message:`"${n}" already exists. Overwrite?`,default:!1})){Z_.info(`Skipping ${e.pkg.name}.`);return}let i=new Set([...e.tags,...e.pkg.packageJson.startx?.tags??[]]),a=e.pkg.packageJson.startx?.ignore??[];a.includes(`eslint-config`)&&i.delete(`eslint`),a.includes(`vitest-config`)&&i.delete(`vitest`);let{packageJson:o,isWorkspace:s}=By.handlePackageJson({app:e.pkg.packageJson,tags:Array.from(i),name:e.overrideName??e.pkg.packageJson.name??e.pkg.name});if(s)throw Error(`Cannot install workspace as a package: ${e.pkg.name}`);await Ny.writeJSONFile({dir:r,file:`package`,content:o}),await this.copyValidatedFilesFromFolder(e.pkg.path,r,i),await Ny.copyDirectory({from:t.join(e.pkg.path,`src`),to:t.join(r,`src`),exclude:i.has(`vitest`)?void 0:/\.test\.tsx?$/}),Z_.info(`Installed ${e.overrideName??e.pkg.name} at ${n}`)}static async copyValidatedFilesFromFolder(e,n,r){let i=await Ny.listFiles({dir:e}).catch(()=>[]);for(let a of i){let i=Py[a];i&&!i.tags.every(e=>r.has(e))||a!==`package.json`&&await Ny.copyFile({from:t.join(e,a),to:t.join(n,a)})}}static createPackageJson(e){let t={typecheck:`tsc --noEmit`,clean:`rimraf dist .turbo`},n={"typescript-config":`workspace:*`},r=[];return e.eslintEnabled?(t.lint=`eslint .`,t[`lint:fix`]=`eslint . --fix`,n[`eslint-config`]=`workspace:*`):r.push(`eslint-config`),e.vitestEnabled?(t.test=`vitest run`,n[`vitest-config`]=`workspace:*`):r.push(`vitest-config`),{name:e.name,version:`1.0.0`,type:`module`,scripts:t,exports:`./src/index.ts`,devDependencies:n,startx:{iTags:[`node`],requiredDevDeps:[`typescript-config`],...r.length>0?{ignore:r}:{}}}}static async checkAndInstallMissingDeps(e){let n=await this.readRootPackage(e.workspace),r=await Iy.parsePnpmWorkspace({dir:e.workspace}),i=[];for(let[t,
|
|
226
|
+
`),Z_.info(`Created package ${r} at ${t.relative(i.workspace,a)}`),Z_.info("Run `pnpm install` to link the new package.")}static async resolveEslintPreference(e){if(e.eslint===!1)return!1;let n=Iy.getDirectory(),r=await this.readRootPackage(n.workspace);return this.hasDependency(r,`eslint`)?e.eslint??!0:e.eslint===!0||await Xx.confirm({message:`ESLint is not installed in this monorepo. Install and enable it?`,default:!0})?(r.devDependencies={...r.devDependencies,eslint:await this.resolveDependencyVersion(n.workspace,`eslint`)},await this.writeJson(t.join(n.workspace,`package.json`),r),Z_.info(`Added eslint to the root devDependencies.`),e.install!==!1&&await this.installRootDependencies(n.workspace),!0):!1}static async getInstallTags(e){let t=new Set([`common`,`node`]),n=await this.readRootPackage(e.workspace);e.eslintEnabled&&t.add(`eslint`),this.hasDependency(n,`@biomejs/biome`)&&t.add(`biome`),this.hasDependency(n,`prettier`)&&t.add(`prettier`),this.hasDependency(n,`vitest`)&&t.add(`vitest`),this.hasDependency(n,`tsdown`)&&t.add(`tsdown`);for(let n of e.packages)n.packageJson?.startx?.tags?.forEach(e=>t.add(e)),n.packageJson?.startx?.iTags?.forEach(e=>t.add(e)),n.packageJson?.startx?.gTags?.forEach(e=>t.add(e)),(n.type===`apps`||n.packageJson?.startx?.mode===`standalone`)&&t.add(`runnable`);return Array.from(t)}static resolvePackageClosure(e){let t=new Map,n=[e.selectedPackage],r=r=>{let i=this.findPackage(e.packages,r);i&&!t.has(i.name)&&n.push(i)};for(e.includeEslintConfig&&r(`eslint-config`);n.length>0;){let e=n.shift();if(!t.has(e.name)){t.set(e.name,e);for(let t of[...e.packageJson?.startx?.requiredDeps??[],...e.packageJson?.startx?.requiredDevDeps??[]])r(t)}}return Array.from(t.values())}static async ensureTemplatePackage(e){let t=this.findPackage(e.packages,e.name);if(!t){Z_.warn(`Could not find template package ${e.name}; skipping.`);return}await this.installTemplatePackage({pkg:t,directory:e.directory,tags:e.tags})}static async installTemplatePackage(e){if(!e.pkg.packageJson)throw Error(`Missing package.json for ${e.pkg.name}`);let n=e.overrideRelativePath??e.pkg.relativePath,r=t.join(e.directory.workspace,n);if(await this.pathExists(t.join(r,`package.json`))&&!await Xx.confirm({message:`"${n}" already exists. Overwrite?`,default:!1})){Z_.info(`Skipping ${e.pkg.name}.`);return}let i=new Set([...e.tags,...e.pkg.packageJson.startx?.tags??[]]),a=e.pkg.packageJson.startx?.ignore??[];a.includes(`eslint-config`)&&i.delete(`eslint`),a.includes(`vitest-config`)&&i.delete(`vitest`);let{packageJson:o,isWorkspace:s}=By.handlePackageJson({app:e.pkg.packageJson,tags:Array.from(i),name:e.overrideName??e.pkg.packageJson.name??e.pkg.name});if(s)throw Error(`Cannot install workspace as a package: ${e.pkg.name}`);await this.syncDepsWithCatalog({workspace:e.directory.workspace,templateDir:e.directory.template,packageJson:o}),await Ny.writeJSONFile({dir:r,file:`package`,content:o}),await this.copyValidatedFilesFromFolder(e.pkg.path,r,i),await Ny.copyDirectory({from:t.join(e.pkg.path,`src`),to:t.join(r,`src`),exclude:i.has(`vitest`)?void 0:/\.test\.tsx?$/}),Z_.info(`Installed ${e.overrideName??e.pkg.name} at ${n}`)}static async copyValidatedFilesFromFolder(e,n,r){let i=await Ny.listFiles({dir:e}).catch(()=>[]);for(let a of i){let i=Py[a];i&&!i.tags.every(e=>r.has(e))||a!==`package.json`&&await Ny.copyFile({from:t.join(e,a),to:t.join(n,a)})}}static createPackageJson(e){let t={typecheck:`tsc --noEmit`,clean:`rimraf dist .turbo`},n={"typescript-config":`workspace:*`},r=[];return e.eslintEnabled?(t.lint=`eslint .`,t[`lint:fix`]=`eslint . --fix`,n[`eslint-config`]=`workspace:*`):r.push(`eslint-config`),e.vitestEnabled?(t.test=`vitest run`,n[`vitest-config`]=`workspace:*`):r.push(`vitest-config`),{name:e.name,version:`1.0.0`,type:`module`,scripts:t,exports:`./src/index.ts`,devDependencies:n,startx:{iTags:[`node`],requiredDevDeps:[`typescript-config`],...r.length>0?{ignore:r}:{}}}}static async checkAndInstallMissingDeps(e){let n=await this.readRootPackage(e.workspace),r=await Iy.parsePnpmWorkspace({dir:e.workspace}),i=[],a=[];for(let[t,o]of Object.entries(Ly))if(o.tags.every(t=>e.tags.includes(t))&&!o.tags.includes(`root`))if(o.version.startsWith(`workspace:`))await this.workspacePackageExists(e.workspace,t)||a.push(t);else{if(this.hasDependency(n,t))continue;let e=r?.catalog?.[t]?`catalog:`:o.version;i.push({name:t,version:e,isDev:o.isDevDependency??!0})}if(a.length>0){Z_.warn(`The following workspace packages are missing from this monorepo:`);for(let e of a)Z_.warn(` - ${e} → run: startx package add ${e}`)}if(i.length!==0){Z_.warn(`The following npm dependencies are required but not installed:`);for(let e of i)Z_.warn(` - ${e.name}`);if(!await Xx.confirm({message:`Add them to the workspace root package.json?`,default:!0})){Z_.warn(`Skipping. Some features may not work correctly without these dependencies.`);return}for(let e of i)e.isDev?n.devDependencies[e.name]=e.version:n.dependencies[e.name]=e.version;await this.writeJson(t.join(e.workspace,`package.json`),n),Z_.info(`Added missing dependencies to root package.json.`),e.install!==!1&&await this.installRootDependencies(e.workspace)}}static async workspacePackageExists(e,n){let r=n.startsWith(`@`)?t.join(...n.split(`/`)):n,i=[t.join(e,`configs`,r,`package.json`),t.join(e,`packages`,r,`package.json`),t.join(e,`apps`,r,`package.json`)];for(let e of i)if(await this.pathExists(e))return!0;return!1}static getDestinationPath(e,n){let r=t.dirname(e),i=n.includes(`/`)?n.split(`/`).pop():n;return t.join(r,i)}static getDefaultPackagePath(e){if(e.startsWith(`@`)){let[n,r]=e.split(`/`);return t.join(`packages`,n,r)}return t.join(`packages`,e)}static findPackage(e,t){return e.find(e=>e.name===t||e.packageJson?.name===t)}static hasDependency(e,t){return!!(e.dependencies?.[t]||e.devDependencies?.[t]||e.peerDependencies?.[t])}static async readRootPackage(e){let n=await o.readFile(t.join(e,`package.json`),`utf-8`);return JSON.parse(n)}static async resolveDependencyVersion(e,t){return(await Iy.parsePnpmWorkspace({dir:e}))?.catalog?.[t]?`catalog:`:t===`eslint`?`^9.0.0`:`latest`}static async installRootDependencies(e){let t=(await this.readRootPackage(e)).packageManager?.split(`@`)[0]||`pnpm`,r=t===`yarn`?`yarn`:t,i=[`install`];Z_.info(`Running ${r} ${i.join(` `)} to install ESLint...`),await new Promise((t,a)=>{let o=n(r,i,{cwd:e,stdio:`inherit`,shell:process.platform===`win32`});o.on(`error`,a),o.on(`close`,e=>{if(e===0){t();return}a(Error(`${r} ${i.join(` `)} exited with code ${e}`))})}).catch(t=>{Z_.warn(`Could not install dependencies automatically: ${t instanceof Error?t.message:t}`),Z_.warn(`Run "${r} ${i.join(` `)}" manually in ${e}.`)})}static async syncDepsWithCatalog(e){let n=t.join(e.workspace,`pnpm-workspace.yaml`),r;try{r=await o.readFile(n,`utf-8`)}catch{return}let i=jy.parseDocument(r),a=i.getIn([`catalog`]);if(!a)return;let s=await this.loadTemplateCatalog(e.templateDir),c=e.packageJson.dependencies,l=e.packageJson.devDependencies,u={},d=e=>{if(e){for(let[t,n]of Object.entries(e))if(!n.startsWith(`workspace:`))if(n===`catalog:`){if(!a[t]){let e=s[t];e&&(u[t]=e)}}else e[t]=`catalog:`,a[t]||(u[t]=n)}};if(d(c),d(l),Object.keys(u).length!==0){for(let[e,t]of Object.entries(u))i.setIn([`catalog`,e],t);await o.writeFile(n,i.toString()),Z_.info(`Added to pnpm-workspace.yaml catalog:`);for(let[e,t]of Object.entries(u))Z_.info(` + ${e}: ${t}`)}}static async loadTemplateCatalog(e){try{let n=await o.readFile(t.join(e,`pnpm-workspace.yaml`),`utf-8`);return jy.parseDocument(n).getIn([`catalog`])??{}}catch{return{}}}static async writeJson(e,t){await o.writeFile(e,`${JSON.stringify(t,null,2)}\n`)}static async pathExists(e){try{return await o.access(e),!0}catch{return!1}}},eS=`1.1.3`;const tS=new dv;tS.name(`startx`).description(`StartX CLI - Your all in one monorepo startup tool.`).version(eS),tS.command(`ping`).action(()=>{Z_.info(`pong`)}),tS.addCommand(Zx.command),tS.addCommand($x.command),tS.parse(process.argv);export{};
|
|
@@ -20,20 +20,21 @@
|
|
|
20
20
|
"author": "",
|
|
21
21
|
"license": "ISC",
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"cross-env": "
|
|
23
|
+
"cross-env": "catalog:",
|
|
24
24
|
"eslint-config": "workspace:*",
|
|
25
25
|
"tsdown-config": "workspace:*",
|
|
26
26
|
"typescript-config": "workspace:*",
|
|
27
27
|
"vitest-config": "workspace:*"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@inquirer/prompts": "
|
|
30
|
+
"@inquirer/prompts": "catalog:",
|
|
31
31
|
"@repo/lib": "workspace:*",
|
|
32
32
|
"@repo/logger": "workspace:*",
|
|
33
33
|
"@repo/env": "workspace:*",
|
|
34
|
-
"chokidar": "
|
|
35
|
-
"commander": "
|
|
36
|
-
"type-fest": "
|
|
34
|
+
"chokidar": "catalog:",
|
|
35
|
+
"commander": "catalog:",
|
|
36
|
+
"type-fest": "catalog:",
|
|
37
|
+
"yaml": "catalog:"
|
|
37
38
|
},
|
|
38
39
|
"startx": {
|
|
39
40
|
"mode": "silent",
|
|
@@ -4,6 +4,7 @@ import { spawn } from "child_process";
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import fs from "fs/promises";
|
|
6
6
|
import path from "path";
|
|
7
|
+
import * as YAML from "yaml";
|
|
7
8
|
import z from "zod";
|
|
8
9
|
|
|
9
10
|
import { DepCheck } from "../configs/deps";
|
|
@@ -261,11 +262,7 @@ export class PackageCommand {
|
|
|
261
262
|
return true;
|
|
262
263
|
}
|
|
263
264
|
|
|
264
|
-
private static async getInstallTags(props: {
|
|
265
|
-
workspace: string;
|
|
266
|
-
packages: PackageItem[];
|
|
267
|
-
eslintEnabled: boolean;
|
|
268
|
-
}) {
|
|
265
|
+
private static async getInstallTags(props: { workspace: string; packages: PackageItem[]; eslintEnabled: boolean }) {
|
|
269
266
|
const tags = new Set<TAGS>(["common", "node"]);
|
|
270
267
|
const rootPackage = await this.readRootPackage(props.workspace);
|
|
271
268
|
|
|
@@ -377,6 +374,12 @@ export class PackageCommand {
|
|
|
377
374
|
throw new Error(`Cannot install workspace as a package: ${props.pkg.name}`);
|
|
378
375
|
}
|
|
379
376
|
|
|
377
|
+
await this.syncDepsWithCatalog({
|
|
378
|
+
workspace: props.directory.workspace,
|
|
379
|
+
templateDir: props.directory.template,
|
|
380
|
+
packageJson: packageJson as Record<string, unknown>,
|
|
381
|
+
});
|
|
382
|
+
|
|
380
383
|
await fsTool.writeJSONFile({ dir: destination, file: "package", content: packageJson });
|
|
381
384
|
await this.copyValidatedFilesFromFolder(props.pkg.path, destination, tags);
|
|
382
385
|
await fsTool.copyDirectory({
|
|
@@ -442,27 +445,39 @@ export class PackageCommand {
|
|
|
442
445
|
};
|
|
443
446
|
}
|
|
444
447
|
|
|
445
|
-
private static async checkAndInstallMissingDeps(props: {
|
|
446
|
-
workspace: string;
|
|
447
|
-
tags: TAGS[];
|
|
448
|
-
install?: boolean;
|
|
449
|
-
}) {
|
|
448
|
+
private static async checkAndInstallMissingDeps(props: { workspace: string; tags: TAGS[]; install?: boolean }) {
|
|
450
449
|
const rootPackage = await this.readRootPackage(props.workspace);
|
|
451
450
|
const pnpmWorkspace = await CliUtils.parsePnpmWorkspace({ dir: props.workspace });
|
|
452
451
|
|
|
453
|
-
const
|
|
452
|
+
const missingNpm: Array<{ name: string; version: string; isDev: boolean }> = [];
|
|
453
|
+
const missingWorkspace: string[] = [];
|
|
454
|
+
|
|
454
455
|
for (const [dep, config] of Object.entries(DepCheck)) {
|
|
455
|
-
if (!config.tags.every(tag => props.tags.includes(tag
|
|
456
|
+
if (!config.tags.every(tag => props.tags.includes(tag))) continue;
|
|
456
457
|
if (config.tags.includes("root")) continue;
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
458
|
+
|
|
459
|
+
if (config.version.startsWith("workspace:")) {
|
|
460
|
+
// Workspace packages live in configs/, packages/, apps/ — not in root package.json
|
|
461
|
+
const exists = await this.workspacePackageExists(props.workspace, dep);
|
|
462
|
+
if (!exists) missingWorkspace.push(dep);
|
|
463
|
+
} else {
|
|
464
|
+
if (this.hasDependency(rootPackage, dep)) continue;
|
|
465
|
+
const version = pnpmWorkspace?.catalog?.[dep] ? "catalog:" : config.version;
|
|
466
|
+
missingNpm.push({ name: dep, version, isDev: config.isDevDependency ?? true });
|
|
467
|
+
}
|
|
460
468
|
}
|
|
461
469
|
|
|
462
|
-
if (
|
|
470
|
+
if (missingWorkspace.length > 0) {
|
|
471
|
+
logger.warn("The following workspace packages are missing from this monorepo:");
|
|
472
|
+
for (const name of missingWorkspace) {
|
|
473
|
+
logger.warn(` - ${name} → run: startx package add ${name}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
463
476
|
|
|
464
|
-
|
|
465
|
-
|
|
477
|
+
if (missingNpm.length === 0) return;
|
|
478
|
+
|
|
479
|
+
logger.warn("The following npm dependencies are required but not installed:");
|
|
480
|
+
for (const dep of missingNpm) {
|
|
466
481
|
logger.warn(` - ${dep.name}`);
|
|
467
482
|
}
|
|
468
483
|
|
|
@@ -475,7 +490,7 @@ export class PackageCommand {
|
|
|
475
490
|
return;
|
|
476
491
|
}
|
|
477
492
|
|
|
478
|
-
for (const dep of
|
|
493
|
+
for (const dep of missingNpm) {
|
|
479
494
|
if (dep.isDev) {
|
|
480
495
|
(rootPackage.devDependencies as Record<string, string>)[dep.name] = dep.version;
|
|
481
496
|
} else {
|
|
@@ -491,6 +506,24 @@ export class PackageCommand {
|
|
|
491
506
|
}
|
|
492
507
|
}
|
|
493
508
|
|
|
509
|
+
private static async workspacePackageExists(workspace: string, packageName: string): Promise<boolean> {
|
|
510
|
+
// Resolve scoped names: @repo/lib → packages/@repo/lib
|
|
511
|
+
const subPath = packageName.startsWith("@")
|
|
512
|
+
? path.join(...packageName.split("/"))
|
|
513
|
+
: packageName;
|
|
514
|
+
|
|
515
|
+
const candidates = [
|
|
516
|
+
path.join(workspace, "configs", subPath, "package.json"),
|
|
517
|
+
path.join(workspace, "packages", subPath, "package.json"),
|
|
518
|
+
path.join(workspace, "apps", subPath, "package.json"),
|
|
519
|
+
];
|
|
520
|
+
|
|
521
|
+
for (const candidate of candidates) {
|
|
522
|
+
if (await this.pathExists(candidate)) return true;
|
|
523
|
+
}
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
|
|
494
527
|
private static getDestinationPath(templateRelativePath: string, newName: string): string {
|
|
495
528
|
const parentDir = path.dirname(templateRelativePath);
|
|
496
529
|
const leafName = newName.includes("/") ? newName.split("/").pop()! : newName;
|
|
@@ -559,6 +592,76 @@ export class PackageCommand {
|
|
|
559
592
|
});
|
|
560
593
|
}
|
|
561
594
|
|
|
595
|
+
private static async syncDepsWithCatalog(props: {
|
|
596
|
+
workspace: string;
|
|
597
|
+
templateDir: string;
|
|
598
|
+
packageJson: Record<string, unknown>;
|
|
599
|
+
}): Promise<void> {
|
|
600
|
+
const workspacePath = path.join(props.workspace, "pnpm-workspace.yaml");
|
|
601
|
+
let content: string;
|
|
602
|
+
try {
|
|
603
|
+
content = await fs.readFile(workspacePath, "utf-8");
|
|
604
|
+
} catch {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const doc = YAML.parseDocument(content);
|
|
609
|
+
const catalog = doc.getIn(["catalog"]) as Record<string, string> | undefined;
|
|
610
|
+
if (!catalog) return;
|
|
611
|
+
|
|
612
|
+
// Load template's catalog to resolve "catalog:" entries to real versions
|
|
613
|
+
const templateCatalog = await this.loadTemplateCatalog(props.templateDir);
|
|
614
|
+
|
|
615
|
+
const deps = props.packageJson.dependencies as Record<string, string> | undefined;
|
|
616
|
+
const devDeps = props.packageJson.devDependencies as Record<string, string> | undefined;
|
|
617
|
+
const newEntries: Record<string, string> = {};
|
|
618
|
+
|
|
619
|
+
const processMap = (depMap: Record<string, string> | undefined) => {
|
|
620
|
+
if (!depMap) return;
|
|
621
|
+
for (const [name, version] of Object.entries(depMap)) {
|
|
622
|
+
if (version.startsWith("workspace:")) continue;
|
|
623
|
+
|
|
624
|
+
if (version === "catalog:") {
|
|
625
|
+
// Valid only if the user's catalog already has this entry.
|
|
626
|
+
// If not, resolve the real version from the template's catalog and add it.
|
|
627
|
+
if (!catalog[name]) {
|
|
628
|
+
const templateVersion = templateCatalog[name];
|
|
629
|
+
if (templateVersion) newEntries[name] = templateVersion;
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
// Hardcoded version — normalize to catalog:
|
|
633
|
+
depMap[name] = "catalog:";
|
|
634
|
+
if (!catalog[name]) newEntries[name] = version;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
processMap(deps);
|
|
640
|
+
processMap(devDeps);
|
|
641
|
+
|
|
642
|
+
if (Object.keys(newEntries).length === 0) return;
|
|
643
|
+
|
|
644
|
+
for (const [name, version] of Object.entries(newEntries)) {
|
|
645
|
+
doc.setIn(["catalog", name], version);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
await fs.writeFile(workspacePath, doc.toString());
|
|
649
|
+
logger.info("Added to pnpm-workspace.yaml catalog:");
|
|
650
|
+
for (const [name, version] of Object.entries(newEntries)) {
|
|
651
|
+
logger.info(` + ${name}: ${version}`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
private static async loadTemplateCatalog(templateDir: string): Promise<Record<string, string>> {
|
|
656
|
+
try {
|
|
657
|
+
const raw = await fs.readFile(path.join(templateDir, "pnpm-workspace.yaml"), "utf-8");
|
|
658
|
+
const doc = YAML.parseDocument(raw);
|
|
659
|
+
return (doc.getIn(["catalog"]) as Record<string, string>) ?? {};
|
|
660
|
+
} catch {
|
|
661
|
+
return {};
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
562
665
|
private static async writeJson(file: string, content: object) {
|
|
563
666
|
await fs.writeFile(file, `${JSON.stringify(content, null, 2)}\n`);
|
|
564
667
|
}
|
|
@@ -20,29 +20,29 @@
|
|
|
20
20
|
"test": "vitest run"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@eslint/compat": "
|
|
24
|
-
"@eslint/js": "
|
|
25
|
-
"@stylistic/eslint-plugin": "
|
|
26
|
-
"eslint-config-prettier": "
|
|
27
|
-
"eslint-import-resolver-typescript": "
|
|
28
|
-
"eslint-plugin-import-x": "
|
|
29
|
-
"eslint-plugin-jsx-a11y": "
|
|
30
|
-
"eslint-plugin-lodash": "
|
|
31
|
-
"eslint-plugin-react": "
|
|
32
|
-
"eslint-plugin-react-hooks": "
|
|
33
|
-
"eslint-plugin-unicorn": "
|
|
34
|
-
"eslint-plugin-unused-imports": "
|
|
35
|
-
"globals": "
|
|
23
|
+
"@eslint/compat": "catalog:",
|
|
24
|
+
"@eslint/js": "catalog:",
|
|
25
|
+
"@stylistic/eslint-plugin": "catalog:",
|
|
26
|
+
"eslint-config-prettier": "catalog:",
|
|
27
|
+
"eslint-import-resolver-typescript": "catalog:",
|
|
28
|
+
"eslint-plugin-import-x": "catalog:",
|
|
29
|
+
"eslint-plugin-jsx-a11y": "catalog:",
|
|
30
|
+
"eslint-plugin-lodash": "catalog:",
|
|
31
|
+
"eslint-plugin-react": "catalog:",
|
|
32
|
+
"eslint-plugin-react-hooks": "catalog:",
|
|
33
|
+
"eslint-plugin-unicorn": "catalog:",
|
|
34
|
+
"eslint-plugin-unused-imports": "catalog:",
|
|
35
|
+
"globals": "catalog:"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"
|
|
39
|
-
"@types/eslint-
|
|
40
|
-
"@
|
|
41
|
-
"@typescript-eslint/
|
|
42
|
-
"@typescript-eslint/
|
|
43
|
-
"@typescript-eslint/utils": "^8.0.0",
|
|
38
|
+
"@types/eslint-config-prettier": "catalog:",
|
|
39
|
+
"@types/eslint-plugin-jsx-a11y": "catalog:",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "catalog:",
|
|
41
|
+
"@typescript-eslint/rule-tester": "catalog:",
|
|
42
|
+
"@typescript-eslint/utils": "catalog:",
|
|
44
43
|
"eslint": "catalog:",
|
|
45
44
|
"typescript-config": "workspace:*",
|
|
45
|
+
"typescript-eslint": "catalog:",
|
|
46
46
|
"vitest-config": "workspace:*"
|
|
47
47
|
},
|
|
48
48
|
"startx": {
|
package/package.json
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"./*": "./src/*/index.ts"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/jsonwebtoken": "
|
|
22
|
-
"@types/nodemailer": "
|
|
21
|
+
"@types/jsonwebtoken": "catalog:",
|
|
22
|
+
"@types/nodemailer": "catalog:",
|
|
23
23
|
"eslint-config": "workspace:*",
|
|
24
24
|
"typescript-config": "workspace:*",
|
|
25
25
|
"vine": "link:@types/vinejs/vine",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"firebase-admin": "catalog:",
|
|
39
39
|
"jsonwebtoken": "catalog:",
|
|
40
40
|
"nodemailer": "catalog:",
|
|
41
|
-
"yaml": "
|
|
41
|
+
"yaml": "catalog:"
|
|
42
42
|
},
|
|
43
43
|
"startx": {
|
|
44
44
|
"iTags": [
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"vitest-config": "workspace:*"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@aws-sdk/client-bedrock": "
|
|
25
|
-
"@aws-sdk/client-bedrock-runtime": "
|
|
24
|
+
"@aws-sdk/client-bedrock": "catalog:",
|
|
25
|
+
"@aws-sdk/client-bedrock-runtime": "catalog:",
|
|
26
26
|
"@repo/env": "workspace:*",
|
|
27
27
|
"@repo/lib": "workspace:*",
|
|
28
28
|
"@repo/logger": "workspace:*",
|
|
29
|
-
"@toon-format/toon": "
|
|
29
|
+
"@toon-format/toon": "catalog:",
|
|
30
30
|
"js-tiktoken": "catalog:",
|
|
31
|
-
"jsonrepair": "
|
|
31
|
+
"jsonrepair": "catalog:",
|
|
32
32
|
"openai": "catalog:",
|
|
33
33
|
"pg": "catalog:",
|
|
34
34
|
"quickjs-emscripten": "catalog:",
|
package/packages/ui/package.json
CHANGED
|
@@ -19,34 +19,34 @@
|
|
|
19
19
|
"@tailwindcss/vite": "catalog:",
|
|
20
20
|
"@types/react": "catalog:",
|
|
21
21
|
"@types/react-dom": "catalog:",
|
|
22
|
-
"autoprefixer": "
|
|
22
|
+
"autoprefixer": "catalog:",
|
|
23
23
|
"eslint-config": "workspace:*",
|
|
24
24
|
"tailwindcss": "catalog:",
|
|
25
25
|
"typescript-config": "workspace:*",
|
|
26
26
|
"vitest-config": "workspace:*"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@fontsource-variable/eb-garamond": "
|
|
30
|
-
"@fontsource-variable/inter": "
|
|
31
|
-
"@hookform/resolvers": "
|
|
32
|
-
"@phosphor-icons/react": "
|
|
33
|
-
"@tailwindcss/postcss": "
|
|
34
|
-
"@tailwindcss/typography": "
|
|
29
|
+
"@fontsource-variable/eb-garamond": "catalog:",
|
|
30
|
+
"@fontsource-variable/inter": "catalog:",
|
|
31
|
+
"@hookform/resolvers": "catalog:",
|
|
32
|
+
"@phosphor-icons/react": "catalog:",
|
|
33
|
+
"@tailwindcss/postcss": "catalog:",
|
|
34
|
+
"@tailwindcss/typography": "catalog:",
|
|
35
35
|
"@tanstack/react-query": "catalog:",
|
|
36
36
|
"@tanstack/react-query-devtools": "catalog:",
|
|
37
|
-
"class-variance-authority": "
|
|
38
|
-
"clsx": "
|
|
39
|
-
"cmdk": "
|
|
40
|
-
"embla-carousel-react": "
|
|
41
|
-
"input-otp": "
|
|
37
|
+
"class-variance-authority": "catalog:",
|
|
38
|
+
"clsx": "catalog:",
|
|
39
|
+
"cmdk": "catalog:",
|
|
40
|
+
"embla-carousel-react": "catalog:",
|
|
41
|
+
"input-otp": "catalog:",
|
|
42
42
|
"lucide-react": "catalog:",
|
|
43
|
-
"next-themes": "
|
|
44
|
-
"radix-ui": "
|
|
45
|
-
"react-hook-form": "
|
|
46
|
-
"react-icons": "
|
|
47
|
-
"sonner": "
|
|
48
|
-
"tailwind-merge": "
|
|
49
|
-
"tw-animate-css": "
|
|
43
|
+
"next-themes": "catalog:",
|
|
44
|
+
"radix-ui": "catalog:",
|
|
45
|
+
"react-hook-form": "catalog:",
|
|
46
|
+
"react-icons": "catalog:",
|
|
47
|
+
"sonner": "catalog:",
|
|
48
|
+
"tailwind-merge": "catalog:",
|
|
49
|
+
"tw-animate-css": "catalog:"
|
|
50
50
|
},
|
|
51
51
|
"exports": {
|
|
52
52
|
"./globals.css": "./src/styles/globals.css",
|
package/pnpm-workspace.yaml
CHANGED
|
@@ -14,6 +14,28 @@ catalog:
|
|
|
14
14
|
rimraf: "^6.1.2"
|
|
15
15
|
turbo: "^2.9.14"
|
|
16
16
|
tsx: "^4.21.0"
|
|
17
|
+
cross-env: "^10.1.0"
|
|
18
|
+
|
|
19
|
+
# eslint plugins
|
|
20
|
+
"@eslint/compat": "^2.0.2"
|
|
21
|
+
"@eslint/js": "^9.0.0"
|
|
22
|
+
"@stylistic/eslint-plugin": "^2.0.0"
|
|
23
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0"
|
|
24
|
+
"@typescript-eslint/rule-tester": "^8.54.0"
|
|
25
|
+
"@typescript-eslint/utils": "^8.0.0"
|
|
26
|
+
"@types/eslint-config-prettier": "^6.11.3"
|
|
27
|
+
"@types/eslint-plugin-jsx-a11y": "^6.10.1"
|
|
28
|
+
eslint-config-prettier: "^9.1.0"
|
|
29
|
+
eslint-import-resolver-typescript: "^3.6.1"
|
|
30
|
+
eslint-plugin-import-x: "^4.0.0"
|
|
31
|
+
eslint-plugin-jsx-a11y: "^6.9.0"
|
|
32
|
+
eslint-plugin-lodash: "^8.0.0"
|
|
33
|
+
eslint-plugin-react: "^7.35.0"
|
|
34
|
+
eslint-plugin-react-hooks: "^5.0.0"
|
|
35
|
+
eslint-plugin-unicorn: "^55.0.0"
|
|
36
|
+
eslint-plugin-unused-imports: "^4.0.0"
|
|
37
|
+
globals: "^15.9.0"
|
|
38
|
+
typescript-eslint: "^8.54.0"
|
|
17
39
|
|
|
18
40
|
# utils
|
|
19
41
|
"@biomejs/biome": "^2.3.13"
|
|
@@ -24,12 +46,22 @@ catalog:
|
|
|
24
46
|
date-fns: "^4.1.0"
|
|
25
47
|
nanoid: "^5.1.6"
|
|
26
48
|
xlsx: "^0.18.5"
|
|
49
|
+
|
|
50
|
+
# cli
|
|
51
|
+
"@inquirer/prompts": "^8.3.0"
|
|
52
|
+
commander: "^14.0.0"
|
|
53
|
+
chokidar: "^4.0.3"
|
|
54
|
+
type-fest: "^5.4.4"
|
|
55
|
+
|
|
27
56
|
# testing
|
|
28
57
|
vitest: "^4.0.18"
|
|
29
58
|
"@vitest/coverage-v8": "^4.0.18"
|
|
59
|
+
jsdom: "^29.0.1"
|
|
30
60
|
|
|
31
61
|
# types
|
|
32
62
|
"@types/node": "^25.1.0"
|
|
63
|
+
"@types/jsonwebtoken": "^9.0.7"
|
|
64
|
+
"@types/nodemailer": "^6.4.16"
|
|
33
65
|
|
|
34
66
|
# frontend
|
|
35
67
|
isbot: "^5.1.36"
|
|
@@ -42,6 +74,7 @@ catalog:
|
|
|
42
74
|
lucide-react: "^0.563.0"
|
|
43
75
|
tailwindcss: "^4.2.2"
|
|
44
76
|
"@tailwindcss/vite": "^4.2.2"
|
|
77
|
+
zustand: "^5.0.13"
|
|
45
78
|
|
|
46
79
|
# email
|
|
47
80
|
react-email: "^5.2.11"
|
|
@@ -53,6 +86,25 @@ catalog:
|
|
|
53
86
|
# ui
|
|
54
87
|
"@tanstack/react-query": "^5.100.9"
|
|
55
88
|
"@tanstack/react-query-devtools": "^5.100.9"
|
|
89
|
+
"@fontsource-variable/eb-garamond": "^5.2.7"
|
|
90
|
+
"@fontsource-variable/inter": "^5.2.8"
|
|
91
|
+
"@hookform/resolvers": "^3.9.1"
|
|
92
|
+
"@phosphor-icons/react": "^2.1.10"
|
|
93
|
+
"@tailwindcss/postcss": "^4"
|
|
94
|
+
"@tailwindcss/typography": "^0.5.19"
|
|
95
|
+
autoprefixer: "^10"
|
|
96
|
+
class-variance-authority: "^0.7.1"
|
|
97
|
+
clsx: "^2.1.1"
|
|
98
|
+
cmdk: "^1.1.1"
|
|
99
|
+
embla-carousel-react: "^8.6.0"
|
|
100
|
+
input-otp: "^1.4.2"
|
|
101
|
+
next-themes: "^0.4.6"
|
|
102
|
+
radix-ui: "^1.4.3"
|
|
103
|
+
react-hook-form: "^7.75.0"
|
|
104
|
+
react-icons: "^5.5.0"
|
|
105
|
+
sonner: "^2.0.7"
|
|
106
|
+
tailwind-merge: "^2.3.0"
|
|
107
|
+
tw-animate-css: "^1.4.0"
|
|
56
108
|
|
|
57
109
|
# frontend types
|
|
58
110
|
"@types/react": "^19.2.4"
|
|
@@ -93,12 +145,17 @@ catalog:
|
|
|
93
145
|
winston: "^3.19.0"
|
|
94
146
|
winston-daily-rotate-file: "^5.0.0"
|
|
95
147
|
jsonwebtoken: "^9.0.3"
|
|
148
|
+
yaml: "^2.8.2"
|
|
96
149
|
|
|
97
150
|
# aix
|
|
98
151
|
js-tiktoken: "^1.0.21"
|
|
99
152
|
quickjs-emscripten: "^0.32.0"
|
|
100
153
|
quicktype-core: "^23.2.6"
|
|
101
154
|
openai: "^6.34.0"
|
|
155
|
+
"@aws-sdk/client-bedrock": "^3.1047.0"
|
|
156
|
+
"@aws-sdk/client-bedrock-runtime": "^3.1047.0"
|
|
157
|
+
"@toon-format/toon": "^2.1.0"
|
|
158
|
+
jsonrepair: "^3.14.0"
|
|
102
159
|
|
|
103
160
|
# bullmq
|
|
104
161
|
bullmq: "^5.76.4"
|