UNPKG

@lenne.tech/cli

Version:

lenne.Tech CLI: lt

545 lines (380 loc) 17.9 kB
# Vendor-Mode Workflow — Step-by-Step Practical guide for converting a lenne.tech fullstack project from **npm mode** to **vendor mode**, updating it, and optionally rolling back. **In short:** In vendor mode the framework code (`@lenne.tech/nest-server`, `@lenne.tech/nuxt-extensions`) is copied directly into the project (`src/core/` and `app/core/`) instead of being installed via npm. > **Complete reference**: All commands and background information can be found in the [LT-ECOSYSTEM-GUIDE](./LT-ECOSYSTEM-GUIDE.md). --- ## Table of Contents - [Prerequisites](#prerequisites) - [Vendor Modification Policy](#vendor-modification-policy) - [Part 1: Convert npm vendor](#part-1-convert-npm--vendor) - [Part 2: Update in vendor mode](#part-2-update-in-vendor-mode) - [Part 3: Roll back vendor npm](#part-3-roll-back-vendor--npm) - [Troubleshooting](#troubleshooting) --- ## Prerequisites Before you start, make sure: | Check | Command | |-------|---------| | lt CLI installed | `lt --version` | | Claude Code with lt-dev plugin | `/lt-dev:plugin:check` | | Project is a fullstack monorepo | Directories `projects/api/` and `projects/app/` exist | | Working tree is clean | `git status` shows no uncommitted changes | | You are on a feature branch | `git checkout -b feature/vendor-mode` | --- ## Vendor Modification Policy **Read this before you convert.** It shapes how you work in vendor mode. Vendoring copies the framework source into your project tree: - Backend: `projects/api/src/core/` (from `@lenne.tech/nest-server`) - Frontend: `projects/app/app/core/` (from `@lenne.tech/nuxt-extensions`) **This is a comprehension aid, not a fork.** The copy exists so Claude Code (and humans) can read framework internals directly it is **not** an invitation to embed project-specific behavior in the framework tree. ### When may I edit `core/`? Only when the change is **generally useful to every consumer** of the framework: | Valid reason to edit `core/` | Belongs in project code instead | |--------------------------------|------------------------------------| | Bugfix that every consumer hits | Customer-specific business rules | | Broad framework enhancement | Project tenant IDs, enums, branding | | Security vulnerability fix | Proprietary integration adapters | | TypeScript / build compatibility | Project-specific authorization logic | **Project-specific behavior belongs outside `core/`:** - Backend: extend/inherit from core classes, use `ICoreModuleOverrides` on `CoreModule.forRoot()` - Frontend: use `app/composables/`, `app/components/`, `app/middleware/`, or plugin overrides ### Every generic change MUST flow back upstream If you fixed or improved something in `core/` that every consumer could benefit from, you MUST submit it as a pull request to the corresponding upstream repository: | Layer | Command | Upstream | |-------|---------|----------| | Backend | `/lt-dev:backend:contribute-nest-server-core` | https://github.com/lenneTech/nest-server | | Frontend | `/lt-dev:frontend:contribute-nuxt-extensions-core` | https://github.com/lenneTech/nuxt-extensions | The contributor command analyzes your local changes, filters cosmetic noise, categorizes commits as upstream-candidate vs. project-specific, cherry-picks candidates onto a branch in a fresh upstream clone, and drafts a PR body for your review. It **never auto-pushes** you explicitly open the PR via normal GitHub flow. **Why this matters:** Without upstream flow-back, useful fixes rot in one project's vendor tree and conflict on every sync. Once merged upstream, the next `/lt-dev:*:update-*-core` sync picks the change up as upstream-delivered and the local patch disappears clean state for everyone. ### Where the policy is documented The same policy is enforced / surfaced in multiple places so you encounter it at every relevant touchpoint: - `projects/api/src/core/VENDOR.md` and `projects/app/app/core/VENDOR.md` (inside every vendor project, auto-generated by the lt CLI) - Skills `nest-server-core-vendoring` and `nuxt-extensions-core-vendoring` (lt-dev plugin) - Reviewer agents `backend-reviewer` and `frontend-reviewer` (flag non-compliant changes in code reviews) - The contributor commands themselves When in doubt, **ask before editing `core/`**. --- ## Part 1: Convert npm → vendor ### Step 1: Check status **Command:** ```bash lt status ``` **What happens:** Shows the current framework mode for backend and frontend. You expect to see `npm (@lenne.tech/nest-server dependency)` and `npm (@lenne.tech/nuxt-extensions dependency)`. --- ### Step 2: Dry-run — show plan **Command (from monorepo root):** ```bash lt fullstack convert-mode --to vendor --dry-run ``` **What happens:** The CLI scans `projects/api/` and `projects/app/`, detects the current modes, and shows **what would happen** without making any changes. Verify the output: - Both subprojects as `npm vendor` - Upstream versions are auto-detected from the respective `package.json` files --- ### Step 3: Run the conversion **Command:** ```bash lt fullstack convert-mode --to vendor --noConfirm ``` **What happens:** 1. **Backend**: clones `@lenne.tech/nest-server` into `/tmp/`, copies `src/core/` + `src/index.ts` + `src/core.module.ts` + `src/test/` + `src/templates/` + `src/types/` + `LICENSE` into `projects/api/src/core/`, applies the flatten-fix (4 edge-case files), rewrites all consumer imports from `@lenne.tech/nest-server` to relative paths, merges upstream dependencies dynamically into `package.json`, converts `express` value-imports to type-imports (vendor compatibility), creates `src/core/VENDOR.md`, prepends a vendor-notice block to `CLAUDE.md` 2. **Frontend**: clones `@lenne.tech/nuxt-extensions` into `/tmp/`, copies `src/module.ts` + `src/runtime/` into `projects/app/app/core/`, replaces `'@lenne.tech/nuxt-extensions'` in `nuxt.config.ts` with `'./app/core/module'`, rewrites the 4 explicit consumer imports, removes the npm dependency, creates `app/core/VENDOR.md` The temp directories in `/tmp/` are cleaned up automatically. --- ### Step 4: Reinstall dependencies **Command (from monorepo root):** ```bash pnpm install ``` **What happens:** pnpm installs the newly merged dependencies (that transitively came from the framework) and removes `@lenne.tech/nest-server` and `@lenne.tech/nuxt-extensions` from `node_modules/`. --- ### Step 5: Validate backend **Commands:** ```bash cd projects/api pnpm exec tsc --noEmit pnpm run lint pnpm test cd .. ``` **What happens:** - `tsc --noEmit`: TypeScript check over the entire backend code including the vendored `src/core/`. Expectation: no errors. - `pnpm run lint`: oxlint over src/ + tests/. Expectation: 0 errors. - `pnpm test`: vitest e2e suite. Expectation: all tests green (initial run may take ~10 min due to TypeScript transform of the vendored core). --- ### Step 6: Validate frontend **Commands:** ```bash cd projects/app pnpm run lint pnpm run build cd .. ``` **What happens:** - `lint`: oxlint over app/. Expectation: 0 errors. - `build`: Nuxt build runs prepare build nitro output. Expectation: `✨ Build complete!` --- ### Step 7: Check status again **Command:** ```bash lt status ``` **Expected output:** ``` Monorepo Subprojects: Backend: projects/api vendor (src/core/, VENDOR.md) Frontend: projects/app vendor (app/core/, VENDOR.md) ``` --- ### Step 8: Commit changes **Commands:** ```bash git add -A git commit -m "chore: convert fullstack to vendor mode - Backend: @lenne.tech/nest-server vendored into src/core/ - Frontend: @lenne.tech/nuxt-extensions vendored into app/core/ - Both VENDOR.md files track baseline + sync history" ``` **What happens:** The commit typically contains ~500 new files (vendored core) and modified consumer imports. --- ## Part 2: Update in vendor mode After the conversion you still need to keep your project in sync with upstream changes. In vendor mode this happens **curated** via Claude Code agents. ### Workflow A: Comprehensive update (recommended) **Command (in Claude Code):** ``` /lt-dev:fullstack:update-all ``` **What happens:** 1. **Phase 1**: Detects the modes of both subprojects (vendor in this case) 2. **Phase 2**: Generates `UPDATE_PLAN.md` with version gaps and waits for your approval 3. **Phase 3**: Backend sync via `nest-server-core-updater` agent (clone upstream, diff, human review, apply, reapply flatten-fix) 4. **Phase 4**: Frontend sync via `nuxt-extensions-core-updater` agent (clone upstream, diff, human review, apply) 5. **Phase 5**: Package maintenance via `npm-package-maintainer` (FULL MODE) 6. **Phase 6**: `CLAUDE.md` sync from upstream starters 7. **Phase 7**: Cross-validation (build, lint, tests for both subprojects) 8. **Phase 8**: Final report --- ### Workflow B: Update backend only **Command:** ``` /lt-dev:backend:update-nest-server-core ``` **What happens:** Same as Phase 3 of Workflow A syncs `src/core/` with upstream changes. --- ### Workflow C: Update frontend only **Command:** ``` /lt-dev:frontend:update-nuxt-extensions-core ``` **What happens:** Same as Phase 4 of Workflow A syncs `app/core/` with upstream changes. --- ### Workflow D: Sync to a specific version **Command:** ``` /lt-dev:backend:update-nest-server-core --target 11.25.0 ``` **What happens:** Instead of syncing to HEAD, a specific upstream version is pulled. Useful for stable major/minor releases. --- ### Freshness check **Command (available in both subprojects):** ```bash cd projects/api # or projects/app pnpm run check:vendor-freshness ``` **What happens:** Reads the baseline version from `VENDOR.md` and compares it with the current version on npm. Non-blocking warning if a newer version exists. Automatically executed by `pnpm run check`. --- ### After the update: validation **Command (from monorepo root):** ```bash pnpm run check ``` **What happens:** Runs audit + format:check + lint + tests + build + server-start per subproject. Must pass green before you commit the update. --- ### Upstream contribution (optional) If you have made local patches in the vendored core that are **generally useful** (bugfix, new feature, type correction), you can prepare them as upstream PRs: **Backend patches:** ``` /lt-dev:backend:contribute-nest-server-core ``` **Frontend patches:** ``` /lt-dev:frontend:contribute-nuxt-extensions-core ``` **What happens:** The agent scans `git log` since the VENDOR.md baseline, filters out cosmetic commits, categorizes substantial commits as `upstream-candidate` or `project-specific`, cherry-picks the candidates onto a fresh upstream branch, generates a PR body draft, and shows you a summary. **The push is done manually by you after review.** --- ## Part 3: Roll back vendor → npm In case vendor mode doesn't work for your project or you want to go back to the npm dependency. ### Step 1: Check local patches **Command:** ```bash cat projects/api/src/core/VENDOR.md | grep -A 20 "## Local changes" cat projects/app/app/core/VENDOR.md | grep -A 20 "## Local changes" ``` **What happens:** Shows the "Local changes" table from both `VENDOR.md` files. **If substantial patches are listed there, they will be lost during the rollback!** --- ### Step 2: Contribute patches upstream (if any) **Recommendation**: Before rolling back, contribute the local patches: ``` /lt-dev:backend:contribute-nest-server-core /lt-dev:frontend:contribute-nuxt-extensions-core ``` **What happens:** See "Upstream contribution" above. After merging the upstream PRs the rollback can happen without data loss. --- ### Step 3: Dry-run — show plan **Command (from monorepo root):** ```bash lt fullstack convert-mode --to npm --dry-run ``` **What happens:** Shows `vendor npm` for both subprojects. The versions to install are read from the `VENDOR.md` baselines. --- ### Step 4: Run the rollback **Command:** ```bash lt fullstack convert-mode --to npm --noConfirm ``` **What happens:** 1. **Backend**: - Reads the baseline version from `src/core/VENDOR.md` - Warns about local patches in the "Local changes" table - Rewrites all consumer imports from relative paths back to `@lenne.tech/nest-server` - Deletes `src/core/` - Restores `@lenne.tech/nest-server` in `package.json` (with baseline version) - Restores `migrate:*` scripts to `node_modules/.bin/` - Removes vendor artifacts: `bin/migrate.js`, `migrations-utils/ts-compiler.js`, `migration-guides/` - Removes the vendor marker from `CLAUDE.md` 2. **Frontend**: - Reads the baseline version from `app/core/VENDOR.md` - Rewrites the 4 explicit consumer imports back to `@lenne.tech/nuxt-extensions` - Deletes `app/core/` - Restores `@lenne.tech/nuxt-extensions` in `package.json` - Rewrites `nuxt.config.ts`: `'./app/core/module'` `'@lenne.tech/nuxt-extensions'` - Removes the `check:vendor-freshness` script - Removes the vendor marker from `CLAUDE.md` --- ### Step 5: Reinstall dependencies **Command:** ```bash pnpm install ``` **What happens:** pnpm installs `@lenne.tech/nest-server` and `@lenne.tech/nuxt-extensions` freshly from the npm registry. --- ### Step 6: Validate **Commands:** ```bash cd projects/api && pnpm exec tsc --noEmit && pnpm run lint && pnpm test && cd .. cd projects/app && pnpm run lint && pnpm run build && cd .. ``` **What happens:** Makes sure everything still works after the rollback. `tsc` in the backend verifies that the `@lenne.tech/nest-server` types from `node_modules/` are now found. The frontend build verifies that Nuxt loads the module as an npm dependency. --- ### Step 7: Check status again **Command:** ```bash lt status ``` **Expected output:** ``` Monorepo Subprojects: Backend: projects/api npm (@lenne.tech/nest-server dependency) Frontend: projects/app npm (@lenne.tech/nuxt-extensions dependency) ``` --- ### Step 8: Commit changes **Commands:** ```bash git add -A git commit -m "chore: revert fullstack to npm mode - Backend: back to @lenne.tech/nest-server X.Y.Z npm dependency - Frontend: back to @lenne.tech/nuxt-extensions A.B.C npm dependency - Vendored cores (src/core/, app/core/) removed" ``` --- ## Troubleshooting ### Problem: `tsc` fails with `new Error('msg', { cause })` error **Cause:** TypeScript target is too old (ES2020 or lower). **Fix:** In `projects/api/tsconfig.json` set the target to `"es2022"`: ```json { "compilerOptions": { "target": "es2022" } } ``` --- ### Problem: Vitest error `'express' does not provide an export named 'Response'` **Cause:** In vendor mode the TypeScript source of the core is evaluated directly by vitest. Value-imports of TypeScript type-only exports break. **Fix:** Should have been fixed automatically by the CLI. If not, update all affected files: ```typescript // Before import { Request, Response } from 'express'; // After import type { Request, Response } from 'express'; ``` --- ### Problem: Conversion fails with "Destination path already exists" **Cause:** The project already has project-specific `bin/` or `migration-guides/` directories that collide with upstream contents. **Fix:** Back up the contents, delete the directories, run the conversion again, copy the contents back. ```bash cp projects/api/bin/migrate.js /tmp/migrate-backup.js cp -r projects/api/migration-guides /tmp/migration-guides-backup rm -rf projects/api/bin projects/api/migration-guides lt fullstack convert-mode --to vendor --noConfirm # After conversion: mv /tmp/migration-guides-backup/MY-FILE.md projects/api/migration-guides/ ``` --- ### Problem: Some consumer imports missing from the rewrite after conversion **Symptom:** `lt fullstack convert-mode` shows a warning like `X file(s) still contain '@lenne.tech/nest-server' imports`. **Fix:** Manually check the reported files and rewrite the imports to relative paths. The warning shows the exact paths. --- ### Problem: Tests fail under parallel load (flakiness) **Cause:** TypeScript source loading in vendor mode is slower than pre-compiled `dist/` this exposes existing timing-sensitive test races. **Fix options:** - Make individual flaky tests robust (retry pattern, polling instead of `setTimeout`) - As a workaround, `retry: 3` is already active in `vitest-e2e.config.ts` - Last resort: `poolOptions.forks.singleFork: true` (makes tests sequential ~4× slower) --- ### Problem: Upstream sync finds conflicts **Symptom:** `/lt-dev:backend:update-nest-server-core` shows conflicts between upstream changes and local patches. **Fix:** The agent pauses and presents the conflicts. You can: - `approve all` take all upstream picks (local patches overwritten) - `approve clean` only conflict-free picks - `reject <file>` skip a specific file - `show <file>` render the hunk - `done` proceed with current selection --- ## Quick Reference | Action | Command | |--------|---------| | Check status | `lt status` | | Dry-run npm→vendor | `lt fullstack convert-mode --to vendor --dry-run` | | npm→vendor | `lt fullstack convert-mode --to vendor --noConfirm` | | Vendor update | `/lt-dev:fullstack:update-all` | | Backend-only update | `/lt-dev:backend:update-nest-server-core` | | Frontend-only update | `/lt-dev:frontend:update-nuxt-extensions-core` | | Freshness check | `pnpm run check:vendor-freshness` | | Full check | `pnpm run check` | | Upstream PR (backend) | `/lt-dev:backend:contribute-nest-server-core` | | Upstream PR (frontend) | `/lt-dev:frontend:contribute-nuxt-extensions-core` | | Dry-run vendor→npm | `lt fullstack convert-mode --to npm --dry-run` | | vendor→npm | `lt fullstack convert-mode --to npm --noConfirm` | --- > **Further reading**: Architecture, concepts, and reference for all CLI and plugin functions in the [LT-ECOSYSTEM-GUIDE](./LT-ECOSYSTEM-GUIDE.md).