Skip to content

Commit 86f56af

Browse files
bpamiriclaude
andauthored
docs(docs): record lazy-loading decision + add defensive spec (#2283)
Closes #2249. Issue 2249 asked whether to keep or remove `"lazy": true` support in package.json. Decision: keep. The feature is small (~15 lines in PackageLoader), legitimately useful for third-party service-only packages, and zero audit debt — no first-party package currently qualifies, so we're not carrying adoption weight for an unused feature. ## What's already in place - **Guide:** `web/sites/guides/src/content/docs/v4-0-0-snapshot/digging-deeper/packages.mdx` has a dedicated `## Lazy loading` section and the `lazy` field is documented in the manifest reference (added in #2281). - **Tests:** `vendor/wheels/tests/specs/packages/PackageLoaderSpec.cfc` already covers the happy path: eager-skip, `isPackageLoaded`, and on-demand instantiation via `getPackage()`. ## What this PR adds - **CHANGELOG entry** under Unreleased / Changed documenting the retain-and-document decision, first-party audit result (no candidates), and pointer to the guide. - **Defensive spec** for the `canBeLazy` gate: a package declaring `lazy: true` alongside `mixins: "controller"` must still be loaded eagerly — if the lazy flag were honored the mixin tables would be incomplete at boot. New fixture at `tests/_assets/packages/lazyignored/`. ## Audit of first-party packages All six first-party packages declare `mixins: "controller"` and therefore cannot be lazy-loaded (lazy requires mixins=none and no middleware). No conversion work to do: | Package | mixins | Candidate? | |---|---|---| | wheels-sentry | controller | no | | wheels-hotwire | controller | no | | wheels-basecoat | controller | no | | wheels-legacy-adapter | controller | no | | wheels-i18n | controller | no | | wheels-seo-suite | controller | no | The feature remains valid and tested for any future third-party service-only package that benefits. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fe9a603 commit 86f56af

4 files changed

Lines changed: 38 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ All historical references to "CFWheels" in this changelog have been preserved fo
125125
- CI engine testing restructured: 42 jobs reduced to 8 via engine-grouped testing (#1939)
126126
- `wheels mcp wheels` MCP surface curated — 7 CLI-only commands (`mcp`, `d`, `new`, `console`, `start`, `stop`, `browser`) hidden from MCP `tools/list` via the `mcpHiddenTools()` convention (requires LuCLI 0.3.4+). All remain reachable as CLI subcommands. Tool count drops from 23 to 16 for agent consumers.
127127
- LuCLI stdio MCP (`wheels mcp wheels`) is now the canonical AI-agent surface for Wheels. `wheels mcp setup` generates `.mcp.json` and `.opencode.json` pointing at the stdio transport. No port or running dev server required. Updated templates: `cli/src/templates/McpConfig.json`, `app/snippets/McpConfig.json`, `tools/build/base/.mcp.json`, `tools/build/base/.opencode.json`.
128+
- Package lazy-loading (`"lazy": true` in `package.json`) retained and documented in the [Packages](web/sites/guides/src/content/docs/v4-0-0-snapshot/digging-deeper/packages.mdx) guide. Audit of all six first-party packages (`wheels-sentry`, `wheels-hotwire`, `wheels-basecoat`, `wheels-legacy-adapter`, `wheels-i18n`, `wheels-seo-suite`) found no candidates — all provide controller mixins, which require eager load to populate the mixin tables. The feature remains valid for third-party service-only packages. Added a defensive test that a package declaring `lazy: true` alongside mixins or middleware is still loaded eagerly (the loader's existing `canBeLazy` gate). (#2249)
128129

129130
### Deprecated
130131

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
component {
2+
public any function init() {
3+
this.version = "1.0.0";
4+
this.initialized = true;
5+
return this;
6+
}
7+
8+
public string function $lazyIgnoredHelper() {
9+
return "lazyignored-eagerly-loaded";
10+
}
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "wheels-lazyignored",
3+
"version": "1.0.0",
4+
"description": "Test fixture: lazy=true combined with mixins — lazy flag must be ignored, package must load eagerly so mixin tables are populated at boot.",
5+
"lazy": true,
6+
"provides": {
7+
"mixins": "controller"
8+
}
9+
}

vendor/wheels/tests/specs/packages/PackageLoaderSpec.cfc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,23 @@ component extends="wheels.WheelsTest" {
446446
expect(loader.getPackages()).toHaveKey("lazypkg");
447447
});
448448

449+
it("ignores lazy=true when the package also declares mixins", () => {
450+
// Packages that contribute to mixin tables must load eagerly
451+
// so the tables are complete by the time controllers/views
452+
// reference the mixed-in methods. The `canBeLazy` gate in
453+
// PackageLoader requires mixins=none AND no middleware.
454+
var loader = new wheels.PackageLoader(
455+
vendorPath = fixturesPath,
456+
componentPrefix = componentPrefix
457+
);
458+
var pkgs = loader.getPackages();
459+
460+
// lazyignored declares lazy=true + mixins=controller;
461+
// it should be eagerly loaded despite the lazy flag.
462+
expect(pkgs).toHaveKey("lazyignored");
463+
expect(pkgs.lazyignored.initialized).toBeTrue();
464+
});
465+
449466
});
450467

451468
describe("Mixin collisions", () => {

0 commit comments

Comments
 (0)