GrowiSlides imports growi-marpit.ts only for CSS extraction (marpit.render('')) and a string constant — no Marp rendering is performed
growi-marpit.ts instantiates new Marp(...) at module scope, pulling in @marp-team/marp-core (~524KB) and @marp-team/marpit (~372KB) unconditionally
All consumers of Slides.tsx are already behind SSR-disabled dynamic boundaries (next/dynamic, useLazyLoader), making React.lazy safe to use within the package
Research Log
GrowiSlides dependency on growi-marpit.ts
Context: Investigating why GrowiSlides requires Marp at all
Selected Approach: Pre-extract CSS as TypeScript string constants in consts/marpit-base-css.ts
Rationale: The CSS is deterministic; generating it at build time eliminates runtime overhead entirely. TypeScript constants integrate seamlessly with existing import patterns.
Trade-offs: Requires regeneration on Marp version upgrade (mitigated by pre:build:src script)
Follow-up: Verify CSS output matches runtime generation; commit generated file for dev mode
Decision: React.lazy vs next/dynamic for MarpSlides
Context: Need dynamic import boundary for MarpSlides within the shared @growi/presentation package
Alternatives Considered:
next/dynamic — Next.js-specific, couples package to framework
React.lazy + Suspense — standard React, works with any bundler
Selected Approach: React.lazy + Suspense
Rationale: Although the package already uses next/head, React.lazy is the standard React pattern for code-splitting and avoids further Next.js coupling. All consumer paths already disable SSR, so React.lazy's SSR limitation is irrelevant.
Trade-offs: Requires <Suspense> wrapper; fallback UI is visible during chunk load
Follow-up: Verify chunk splitting in Next.js production build
Decision: Shared constants module for MARP_CONTAINER_CLASS_NAME
Context: MARP_CONTAINER_CLASS_NAME is defined in growi-marpit.ts but needed by GrowiSlides without Marp dependency
Selected Approach: Move to consts/index.ts (existing shared constants module)
Rationale: The constant has no dependency on Marp; co-locating it with Marp code creates an unnecessary transitive import
Risks & Mitigations
CSS drift on Marp upgrade: Pre-extracted CSS may become stale → pre:build:src script auto-regenerates before every build
Suspense flash on Marp load: Brief loading indicator when MarpSlides loads → Masked by parent next/dynamic loading spinner in most paths
Build script compatibility: .mjs script must work in CI and local dev → Use standard Node.js ESM with no external tooling dependencies