@lenne.tech/cli
Version:
lenne.Tech CLI: lt
147 lines (146 loc) • 6.78 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.printMigrateResult = printMigrateResult;
exports.runMigrate = runMigrate;
/**
* Reusable init/migrate logic — used by `commands/dev/init.ts` (interactive
* + verbose, also chained from `commands/dev/install.ts`) and
* `commands/fullstack/init.ts` (silent best-effort after project creation).
*
* Returns a structured result so callers can decide what to print.
* Idempotent — safe to run multiple times.
*/
const fs_1 = require("fs");
const gluegun_1 = require("gluegun");
const path_1 = require("path");
const dev_identity_1 = require("./dev-identity");
const dev_patches_1 = require("./dev-patches");
const dev_project_1 = require("./dev-project");
const dev_state_1 = require("./dev-state");
const package_name_1 = require("./package-name");
/**
* Print a `runMigrate` result via the toolbox. Shared by `lt dev init`
* and the auto-init step of `lt dev install` so both render identically.
* Does NOT print Caddy-install hints — chaining handles that separately.
*/
function printMigrateResult(toolbox, result) {
const { print: { colors, info, success }, } = toolbox;
info('');
info(colors.bold(`Initializing "${result.identity.slug}" for lt dev`));
info(colors.dim('─'.repeat(60)));
if (result.identity.subdomains.app)
info(` App URL: https://${result.identity.subdomains.app.hostname}`);
if (result.identity.subdomains.api)
info(` API URL: https://${result.identity.subdomains.api.hostname}`);
info(` DB: mongodb://127.0.0.1/${result.dbName}`);
info('');
if (result.renamedTemplatePackage) {
success(`renamed root package.json name → "${result.renamedTemplatePackage}" (was unmodified template default)`);
}
if (result.codePatches.length > 0) {
for (const r of result.codePatches) {
if (r.patched)
success(`patched ${r.replacements}× in ${r.file}`);
else
info(colors.dim(`already patched: ${r.file}`));
}
}
else {
info(colors.dim(' patches: not needed (already env-aware)'));
}
result.claudePatches.filter((r) => r.patched).forEach((r) => success(`updated CLAUDE.md URL block: ${r.file}`));
if (result.registryUpdated) {
success(`registered in ${process.env.LT_DEV_REGISTRY_PATH || '~/.lenneTech/projects.json'}`);
}
if (result.addedGitignoreEntry)
success('added `.lt-dev/` to .gitignore');
if (result.alreadyMigrated) {
info(colors.dim(' Project was already initialized — nothing changed.'));
}
}
/**
* Run all migration steps for a resolved project.
*
* Idempotent — re-running with no changes returns `alreadyMigrated: true`.
*/
function runMigrate(input) {
const { layout } = input;
// 0. If the root package.json still carries an unmodified starter-template
// name (e.g. `lt-monorepo` from a raw `git clone`), rewrite it to the
// directory basename before deriving identity. Otherwise every cloned
// project would slug to `lt-monorepo` and collide on
// `https://lt-monorepo.localhost`. `lt fullstack init` already handles
// this — the call here is the safety net for projects that bypassed
// init (e.g. manual `git clone lenneTech/lt-monorepo my-project`).
const renamedTemplatePackage = (0, package_name_1.renameUnmodifiedTemplatePackage)({
filesystem: gluegun_1.filesystem,
projectRoot: layout.root,
});
const identity = (0, dev_identity_1.buildIdentity)(layout.root);
const dbName = (0, dev_project_1.deriveDbName)(layout.apiDir, identity.slug);
// 1. Code patches. Run `autoPatch` over EVERY existing config file (not just
// the ones a port-detector flags): the patches are idempotent, and some —
// e.g. the playwright.config `ignoreHTTPSErrors` + shard-aware
// `LT_DEV_TEST_SHARDS` timeout block — apply to configs that are already
// env-aware. So `lt dev init` makes any project fully `lt dev test --shard`
// ready in one command; an up-to-date config is a no-op (`patched: false`).
const filesToPatch = [];
if (layout.apiDir) {
const apiCfg = (0, path_1.join)(layout.apiDir, 'src', 'config.env.ts');
if ((0, fs_1.existsSync)(apiCfg))
filesToPatch.push(apiCfg);
}
if (layout.appDir) {
for (const rel of ['nuxt.config.ts', 'playwright.config.ts']) {
const f = (0, path_1.join)(layout.appDir, rel);
if ((0, fs_1.existsSync)(f))
filesToPatch.push(f);
}
}
const codePatches = filesToPatch.map((f) => (0, dev_patches_1.autoPatch)(f));
// 2. CLAUDE.md URL block (root + each subproject — only patches existing files).
const claudeCandidates = [
(0, path_1.join)(layout.root, 'CLAUDE.md'),
...(layout.apiDir ? [(0, path_1.join)(layout.apiDir, 'CLAUDE.md')] : []),
...(layout.appDir ? [(0, path_1.join)(layout.appDir, 'CLAUDE.md')] : []),
];
const claudePatches = claudeCandidates
.filter((f) => (0, fs_1.existsSync)(f))
.map((f) => (0, dev_patches_1.patchClaudeMd)(f, { dbName, identity }));
// 3. Registry — only write when something actually changed.
const reg = (0, dev_state_1.loadRegistry)();
const subdomainMap = {};
for (const [k, v] of Object.entries(identity.subdomains))
subdomainMap[k] = v.hostname;
const existing = reg.projects[identity.slug];
const next = {
dbName,
internalPorts: (existing === null || existing === void 0 ? void 0 : existing.internalPorts) || {},
lastUsedAt: existing === null || existing === void 0 ? void 0 : existing.lastUsedAt,
path: layout.root,
subdomains: subdomainMap,
};
const registryChanged = !existing ||
existing.path !== next.path ||
existing.dbName !== next.dbName ||
JSON.stringify(existing.subdomains) !== JSON.stringify(next.subdomains);
if (registryChanged) {
reg.projects[identity.slug] = next;
(0, dev_state_1.saveRegistry)(reg);
}
// 4. .gitignore
const addedGitignoreEntry = (0, dev_patches_1.addToGitignore)(layout.root, '.lt-dev/');
const codePatched = codePatches.filter((r) => r.patched).length > 0;
const claudePatched = claudePatches.filter((r) => r.patched).length > 0;
const alreadyMigrated = !codePatched && !claudePatched && !registryChanged && !addedGitignoreEntry && !renamedTemplatePackage;
return {
addedGitignoreEntry,
alreadyMigrated,
claudePatches,
codePatches,
dbName,
identity,
registryUpdated: registryChanged,
renamedTemplatePackage,
};
}