diff --git a/.gitignore b/.gitignore index 34249dd76..bea662444 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,9 @@ config.*.timestamp-*-*.* yarn.lock +# Claude +CLAUDE.md + # Syncthing .stfolder/ .stversions/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..1ce975269 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,40 @@ +# AGENTS.md + +Lando is a local development orchestration tool built around Docker, recipes, and plugin-driven automation for app environments and developer tooling. Lando Core (`@lando/core`) is the event-driven Node.js runtime and plugin base behind the `lando` CLI: it bootstraps config, tasks, engine integration, app loading, and built-in plugin behavior. + +## Maintenance +- Keep this file up to date. If you identify a serious repo-specific gotcha that is likely to trip future agents, update this file in the same change. + +## Commands +- Use `npm`. Node 20 required. +- Full verification: `npm test`. Focused unit test: `npx mocha --timeout 5000 test/.spec.js`. +- Integration tests: `npm run test:leia`. **Do not run locally** — they modify the host system and are CI-only. +- The `.lando.yml` at the project root is solely for docs work (VitePress); it has no relation to app code. + +## Architecture +- `bin/lando` — CLI entrypoint/runtime selector. +- `lib/lando.js` — package main export; owns bootstrap sequencing. +- `index.js` — **not** the library entrypoint; it's the core plugin bootstrap that registers default config and lifecycle hooks. +- Most behavior is wired through events and hook modules, not direct imports. Start with `lib/lando.js`, `app.js`, and `index.js`, then follow `hooks/`. +- Bootstrap levels: `config → tasks → engine → app`. Tooling commands may only reach `engine` if compose cache exists, so not every CLI path initializes a full app. + +## Config And Caches +- Landofile loading order (from the app root directory): `.lando.base.yml`, `.lando.dist.yml`, `.lando.recipe.yml`, `.lando.upstream.yml`, `.lando.yml`, `.lando.local.yml`, `.lando.user.yml`. +- CLI relies heavily on task cache and compose cache to decide bootstrap level and available commands. +- If a command change seems ignored, clear caches with `lando --clear` before assuming the code path is wrong. + +## Writing +- Keep commit messages, PR descriptions, and issue comments concise. +- User-facing changes only in `CHANGELOG.md`'s `## {{ UNRELEASED_VERSION }}` section; follow the existing bullet and verb style. + +## Repo-Specific Gotchas +- **Packaged binary**: adding runtime-loaded files or new top-level directories requires updating `pkg.assets` and `pkg.scripts` in `package.json`, or they'll be missing from the packaged CLI. +- **New source directories** must be added to `jsconfig.json` `include`, `package.json` `nyc.include`, and `package.json` `pkg.scripts`. +- **ESLint**: `require-jsdoc` applies to `FunctionDeclaration` only — arrow functions and expressions do not need JSDoc to pass lint. +- **`examples/**/README.md`** files are executable Leia specs — edits there affect CI test behavior. + +## TypeScript Migration +- We are incrementally migrating to TypeScript via JSDoc type definitions. Add `@param`, `@return`, `@typedef` to any functions you modify or create. +- Type definitions go in co-located `.types.js` files (e.g., `utils/foo.types.js` next to `utils/foo.js`). +- `npm run typecheck` is not gated in CI. Use `npm run typecheck:full` to include node_modules errors. +- Use proper TypeScript types in JSDoc annotations. diff --git a/examples/AGENTS.md b/examples/AGENTS.md new file mode 100644 index 000000000..4baffec39 --- /dev/null +++ b/examples/AGENTS.md @@ -0,0 +1,7 @@ +# AGENTS.md + +- Each `README.md` is an executable Leia spec, not just documentation. **Do not run locally** — they are CI-only and can mutate host state. +- Leia expects specific section headings: `Start up tests`, `Verification commands`, `Destroy tests`. `npm run test:leia` targets `Destroy tests` for cleanup. +- When changing example behavior, keep the `README.md` commands in the same directory in sync. +- When adding, removing, or renaming specs, update the corresponding jobs in `.github/workflows/pr-core-tests.yml` and the `pr-setup-{linux,windows,macos}-tests.yml` workflows. +- `examples/.lando.yml` loads `@lando/core` from `..` — examples exercise this checkout, not an installed release. diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..65dcb173f --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "noEmit": true, + "skipLibCheck": true, + "strict": false, + "moduleResolution": "node", + "ignoreDeprecations": "6.0", + "target": "es2022", + "resolveJsonModule": true + }, + "include": [ + "app.js", + "index.js", + "builders/**/*.js", + "components/**/*.js", + "experimental/**/*.js", + "hooks/**/*.js", + "inits/**/*.js", + "lib/**/*.js", + "messages/**/*.js", + "packages/**/*.js", + "plugins/**/*.js", + "renderers/**/*.js", + "sources/**/*.js", + "tasks/**/*.js", + "utils/**/*.js" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/package-lock.json b/package-lock.json index 7bef3d632..032b0ca7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@lando/core", - "version": "3.26.2", + "version": "3.26.3", "license": "MIT", "dependencies": { "@lando/argv": "^1.2.0", @@ -77,6 +77,7 @@ "@babel/eslint-parser": "^7.16.0", "@lando/leia": "^1.0.0-beta.4", "@lando/vitepress-theme-default-plus": "^1.1.5", + "@types/lodash": "^4.17.24", "@yao-pkg/pkg": "^5.16.1", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", @@ -89,6 +90,7 @@ "nyc": "^15.1.0", "sinon": "^4.3.0", "sinon-chai": "^2.14.0", + "typescript": "^6.0.2", "ua-parser-js": "^1.0.39", "vitepress": "^1.5.0" }, @@ -3688,6 +3690,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/markdown-it": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", @@ -14613,6 +14622,20 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/ua-parser-js": { "version": "1.0.40", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", diff --git a/package.json b/package.json index d25269574..2542acf0a 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,9 @@ "pkg": "pkg --config package.json --output dist/lando ---targets node20 --options 'dns-result-order=ipv4first' bin/lando", "test:unit": "nyc --reporter=html --reporter=text mocha --timeout 5000 test/**/*.spec.js", "test:leia": "leia \"examples/**/README.md\" -c 'Destroy tests' --stdin", - "test": "npm run lint && npm run test:unit" + "test": "npm run lint && npm run test:unit", + "typecheck": "tsc --project jsconfig.json 2>&1 | grep -v '^node_modules/' || true", + "typecheck:full": "tsc --project jsconfig.json || true" }, "pkg": { "outputPath": "dist", @@ -148,6 +150,7 @@ "@babel/eslint-parser": "^7.16.0", "@lando/leia": "^1.0.0-beta.4", "@lando/vitepress-theme-default-plus": "^1.1.5", + "@types/lodash": "^4.17.24", "@yao-pkg/pkg": "^5.16.1", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", @@ -160,6 +163,7 @@ "nyc": "^15.1.0", "sinon": "^4.3.0", "sinon-chai": "^2.14.0", + "typescript": "^6.0.2", "ua-parser-js": "^1.0.39", "vitepress": "^1.5.0" }