Switch to pnpm and fix package dependency isolation - #16
Merged
Merged
Conversation
Add peer deps as devDependencies in satellite packages so they can typecheck and test without hoisting. Move @tabler/icons-react from root to canopycms package, add vitest to canopycms devDeps, and clean root package.json to only contain tooling devDeps. Add canopycms/test-utils subpath export replacing fragile cross-package relative imports. Use standard Request/Response types in canopycms-next handler API to avoid NextRequest type duplication across packages. Add @codemirror/language and @lezer/highlight as direct deps to work around missing dependency declarations in @mdxeditor/editor.
Replace npm workspaces + install-strategy=nested with pnpm which provides per-workspace isolation by default. Remove package-lock.json, add pnpm-workspace.yaml and pnpm-lock.yaml. Update workspace cross-references to use workspace: protocol. Update root scripts to use pnpm -r.
added 4 commits
March 27, 2026 08:03
Move auth cache refresh logic from hardcoded dynamic imports in cli/init.ts to a plugin interface method. Each auth plugin implements createCacheRefresher() to provide its own cache refresh function, eliminating the circular dependency where the core package imported from its own plugins.
Add @types/react to canopycms-next, canopycms-auth-dev, and canopycms-cdk devDeps. Add simple-git to test-app devDeps. These were previously resolved via stale node_modules from npm; clean pnpm install correctly exposes them.
jpslav
added a commit
that referenced
this pull request
Aug 15, 2026
…hared-block docs Adopter requests #13, #15, #16 from the go-live backlog re-baseline. - entry-schema.ts: add BlockValueOf<Blocks, N> and BlockComponentRegistry<Blocks, ExtraProps> — a mapped type keyed off a block field's discriminated union that requires exactly one component per template, making a block-to-component registry exhaustive at compile time. Ship the types, not a renderBlocks() component: the adopter who requested this already arrived at the mapped-type solution independently, and a runtime helper would have to pick a key strategy, an unknown-template policy, and prop-threading shape that isn't right for everyone. Type-level tests prove exhaustiveness in both directions (missing template, unknown extra key both fail to compile). - entry-schema.ts: add defineFieldFragment(), a 3-line const-inference identity helper beside defineBlockTemplate, for discoverability of the already-working field-array-spread pattern. - README: document Block Component Registries, Reusable Field Fragments (both composition mechanisms plus a per-use-override example), and Shared / Referenced Blocks (recipe + a prominent listEntries-never-resolves-references caveat, called out in two places). - apps/example1: refactor PostView.tsx from an if-chain ending in "Unknown block" to a BlockComponentRegistry; wire one real shared/referenced block (snippet entry type + sharedCta block template) into schemas.ts and content. - docs/adopter-migration.md: add the three Unreleased entries. - Move block-registry-types.md, field-fragments-docs.md and shared-blocks-listentries-caveat.md to .claude/future-tasks/resolved/, updating index.md and inbound links. Verified: pnpm lint, lint:bundle, lint:tasks, typecheck, and the full test suite (3910 tests across 5 packages) all pass.
jpslav
added a commit
that referenced
this pull request
Aug 15, 2026
…hared-block docs Adopter requests #13, #15, #16 from the go-live backlog re-baseline. - entry-schema.ts: add BlockValueOf<Blocks, N> and BlockComponentRegistry<Blocks, ExtraProps> — a mapped type keyed off a block field's discriminated union that requires exactly one component per template, making a block-to-component registry exhaustive at compile time. Ship the types, not a renderBlocks() component: the adopter who requested this already arrived at the mapped-type solution independently, and a runtime helper would have to pick a key strategy, an unknown-template policy, and prop-threading shape that isn't right for everyone. Type-level tests prove exhaustiveness in both directions (missing template, unknown extra key both fail to compile). - entry-schema.ts: add defineFieldFragment(), a 3-line const-inference identity helper beside defineBlockTemplate, for discoverability of the already-working field-array-spread pattern. - README: document Block Component Registries, Reusable Field Fragments (both composition mechanisms plus a per-use-override example), and Shared / Referenced Blocks (recipe + a prominent listEntries-never-resolves-references caveat, called out in two places). - apps/example1: refactor PostView.tsx from an if-chain ending in "Unknown block" to a BlockComponentRegistry; wire one real shared/referenced block (snippet entry type + sharedCta block template) into schemas.ts and content. - docs/adopter-migration.md: add the three Unreleased entries. - Move block-registry-types.md, field-fragments-docs.md and shared-blocks-listentries-caveat.md to .claude/future-tasks/resolved/, updating index.md and inbound links. Verified: pnpm lint, lint:bundle, lint:tasks, typecheck, and the full test suite (3910 tests across 5 packages) all pass.
jpslav
added a commit
that referenced
this pull request
Aug 21, 2026
Found by a second independent review, reproduced before fixing.
gray-matter@4 caches parsed files PROCESS-GLOBALLY by content and hands every
caller the same `data` object instance. `readEntryData` merged the body into
that shared object in place, so any listing that touched an md entry rewrote
the cached frontmatter for the whole process.
The consequence landed squarely on this branch's headline use case. Because
`ContentStore.read()`'s md branch calls `matter()` again, a resolved reference
to an md entry picked up the poisoned frontmatter — so the SAME snippet
resolved differently depending on unrelated scoping:
listEntries({ rootPath: 'content/posts', resolveReferences: true })
-> id, slug, collection, title
listEntries({ resolveReferences: true }) // whole site
-> id, slug, collection, title, body
A search index over md shared blocks therefore contained the snippet's text or
not, decided by whether the listing happened to also list the snippet's own
collection. That is the exact silent-data class #16 exists to fix, one level
down, and every test added in the first two commits used a `format: 'json'`
target — which reparses fresh and cannot show it. Two md-target regression
tests now cover both directions, verified red against the pre-fix code.
Fixed at the single mutation site by copying before the merge; `readEntryData`
is the only writer of a gray-matter result in the package.
Also from the same review:
- Corrected a claim this branch introduced in two places (`resolveSingleReference`
and concurrency.md) that uncached resolution "reparses per occurrence and has
no such hazard". False for md/mdx: the same global cache means nested
frontmatter objects alias across occurrences, calls and requests. The cached
path is in fact the safer of the two, since it hands out a structuredClone.
Filed as graymatter-cache-shared-frontmatter.md — fixing it means changing
`read()`, which this branch promised byte-identical.
- With poisoning fixed, a resolved md/mdx reference deterministically carries
frontmatter and no body, while a *listed* md entry's data does carry it. That
inconsistency needs a product decision, not a patch: filed as
resolved-reference-md-body.md with three options weighed, and documented in
README as a third caveat so adopters are not surprised meanwhile.
- Corrected an overstated perf claim of my own: cloning is ~63x cheaper than
re-read-and-parse for a snippet-sized target but ~0.8x for a 265KB JSON one.
The comment now carries the measured numbers and the two facts that keep the
bad end narrow.
- Noted the one shape structuredClone does not preserve (`!!binary` Buffer ->
Uint8Array), verified against real parser output.
- Added the missing tree test for a block shared ACROSS collections, which is
the only thing pinning that the cache spans the whole recursive walk.
jpslav
added a commit
that referenced
this pull request
Aug 21, 2026
…resolution feat: opt-in reference resolution for listEntries and buildContentTree (#16)
This was referenced Aug 22, 2026
jpslav
added a commit
that referenced
this pull request
Sep 13, 2026
The same examples/aws-deployment drift was filed three times on 2026-09-12, twice on int-202609-a and once on int-202609-cms-image, and the base merge brought all three together. example-aws-deployment-drift-from-template.md, the P1 filing, is now canonical and carries every distinct fact from the other two, re-checked against the merged tree: five template/example pairs and their current state, the workflow and stack drift, the guards that exist and what each pins, and the three fix options with the seven placeholders. example-deploy-workflow-drifted.md and examples-aws-deployment-drift.md move to resolved/ as duplicates. Do-next #16 now points at the canonical file, its P3 row is gone, and the epic spec links the resolved copy.
jpslav
added a commit
that referenced
this pull request
Sep 13, 2026
- scaffold-synth.test.ts: the imagePlatforms comment and the GitHub App test's comment now state the invariant precisely: no other test reads cdk.out after beforeAll. Before, the App test's comment said every other assertion reads beforeAll state, but the prod-mode and workflow tests read scaffold files from disk. - example-aws-deployment-drift-from-template.md: PR #323 edited more of the example than runs-on and build args, and it added NEXT_PUBLIC_CANOPY_MODE independently of #322. Names the test that pins it, calls the cross-copy checks per-feature pins rather than textual ones (the asset-support check instantiates AssetSupport), and adds that the example's tsconfig.json extends a ../tsconfig.json that examples/aws-deployment/ does not have. - index.md: Do-next #16 and the P1 row use the same wording for the cross-copy checks. - cms-image-pr5-review-followups.md: item 6, from the base merge's code review. The file-level beforeAll in scaffold-synth.test.ts also runs, and can fail, for the type-check describe, which has a scaffold of its own.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.