Przeglądaj źródła

Merge branch 'master' into support/migrate-to-y-websocket

Yuki Takei 2 tygodni temu
rodzic
commit
2fb358bf92

+ 0 - 58
.kiro/specs/optimize-presentation/requirements.md

@@ -1,58 +0,0 @@
-# Requirements Document
-
-## Introduction
-
-The GROWI presentation feature (`@growi/presentation` package) statically imports `@marp-team/marp-core` (~524KB) and `@marp-team/marpit` (~372KB) whenever any slide component loads, even when Marp rendering is not needed. This is because both `MarpSlides` and `GrowiSlides` components statically import `growi-marpit.ts`, which instantiates Marp objects at module scope.
-
-The goal is to decouple heavy Marp dependencies so they are only loaded when a page explicitly uses `marp: true` in its frontmatter, reducing the async chunk size for the common non-Marp slide rendering path and improving overall bundle efficiency.
-
-## Requirements
-
-### Requirement 1: Decouple GrowiSlides from Marp Runtime Dependencies
-
-**Objective:** As a developer, I want GrowiSlides to render without loading `@marp-team/marp-core` or `@marp-team/marpit`, so that non-Marp slide pages do not incur unnecessary module loading overhead.
-
-#### Acceptance Criteria
-1. The `@growi/presentation` build output for GrowiSlides shall not contain import references to `@marp-team/marp-core` or `@marp-team/marpit`.
-2. When a slide page without `marp: true` is rendered, the Presentation module shall render GrowiSlides without loading `@marp-team/marp-core` or `@marp-team/marpit` modules.
-3. The Presentation module shall provide the Marp base CSS (previously generated by `marpit.render('')`) as pre-extracted static string constants, so that GrowiSlides can apply Marp container styling without a runtime Marp dependency.
-4. The `MARP_CONTAINER_CLASS_NAME` constant shall be defined in a shared constants module, not in `growi-marpit.ts`, to avoid transitive Marp imports.
-
-### Requirement 2: Dynamic Loading of MarpSlides
-
-**Objective:** As a developer, I want MarpSlides to be loaded dynamically (on demand), so that the Marp rendering engine is only fetched when a page actually uses Marp.
-
-#### Acceptance Criteria
-1. When a slide page with `marp: true` is rendered, the Presentation module shall dynamically load MarpSlides and render Marp content correctly.
-2. While MarpSlides is loading, the Presentation module shall display a loading indicator (Suspense fallback).
-3. When a slide page without `marp: true` is rendered, the Presentation module shall not trigger the dynamic import of MarpSlides.
-
-### Requirement 3: Build-Time CSS Extraction
-
-**Objective:** As a developer, I want the Marp base CSS to be extracted at build time via an automated script, so that the pre-extracted CSS stays synchronized with the installed `@marp-team/marp-core` version.
-
-#### Acceptance Criteria
-1. The `@growi/presentation` package shall include a build-time script that generates Marp base CSS constants by invoking `slideMarpit.render('')` and `presentationMarpit.render('')`.
-2. When `pnpm run build` is executed for `@growi/presentation`, the build pipeline shall regenerate the CSS constants before compiling source files.
-3. The generated CSS constants file shall be committed to the repository so that `dev` mode works without running the extraction script first.
-
-### Requirement 4: Functional Equivalence
-
-**Objective:** As a user, I want the presentation feature to behave identically after optimization, so that existing Marp and non-Marp presentations continue to work without regression.
-
-#### Acceptance Criteria
-1. When a page with `marp: true` frontmatter is viewed inline, the Presentation module shall render Marp slides with correct styling.
-2. When a page with `slide: true` frontmatter (without `marp: true`) is viewed inline, the Presentation module shall render GrowiSlides with correct styling.
-3. When the presentation modal is opened for a Marp page, the Presentation module shall render Marp slides in the modal with correct fullscreen behavior.
-4. When the presentation modal is opened for a non-Marp slide page, the Presentation module shall render GrowiSlides in the modal with correct fullscreen behavior.
-5. When a non-slide page is viewed, the Presentation module shall not load any slide rendering components (existing lazy-loading behavior preserved).
-
-### Requirement 5: Build Verification
-
-**Objective:** As a developer, I want to verify that the optimization achieves its goal, so that Marp module separation can be confirmed in CI and during development.
-
-#### Acceptance Criteria
-1. The `@growi/presentation` package shall build successfully with `pnpm run build`.
-2. The `@growi/app` package shall build successfully with `turbo run build --filter @growi/app`.
-3. The built `GrowiSlides.js` output shall contain no references to `@marp-team/marp-core` or `@marp-team/marpit`.
-4. The built `Slides.js` output shall contain a dynamic `import()` expression for `MarpSlides`.

+ 0 - 49
.kiro/specs/optimize-presentation/tasks.md

@@ -1,49 +0,0 @@
-# Implementation Plan
-
-- [x] 1. Set up shared constants and build-time CSS extraction infrastructure
-- [x] 1.1 Move the Marp container class name constant to the shared constants module and update growi-marpit to import from there
-  - Add the `MARP_CONTAINER_CLASS_NAME` string constant to the existing shared constants module in the presentation package
-  - Update growi-marpit to import the constant from the shared module instead of defining it locally
-  - Re-export the constant from growi-marpit for backward compatibility with MarpSlides
-  - _Requirements: 1.4_
-
-- [x] 1.2 Create the build-time CSS extraction script
-  - Write a Node.js ESM script that instantiates Marp with the same configuration as growi-marpit (container classes, inlineSVG, emoji/html/math disabled)
-  - The script renders empty strings through both slide and presentation Marp instances to extract their CSS output
-  - Write the CSS strings as exported TypeScript constants to the constants directory
-  - Include a file header comment indicating the file is auto-generated and how to regenerate it
-  - Validate that extracted CSS is non-empty before writing
-  - _Requirements: 3.1_
-
-- [x] 1.3 Wire the extraction script into the build pipeline and generate the initial CSS file
-  - Add a `pre:build:src` script entry in the presentation package's package.json that runs the extraction script before the main Vite build
-  - Execute the script once to generate the initial pre-extracted CSS constants file
-  - Commit the generated file so that dev mode works without running extraction first
-  - _Requirements: 3.2, 3.3_
-
-- [x] 2. (P) Decouple GrowiSlides from Marp runtime dependencies
-  - Replace the growi-marpit import in GrowiSlides with imports from the shared constants module and the pre-extracted CSS constants
-  - Replace the runtime `marpit.render('')` call with a lookup of the pre-extracted CSS constant based on the presentation mode flag
-  - After this change, GrowiSlides must have no import path leading to `@marp-team/marp-core` or `@marp-team/marpit`
-  - Depends on task 1 (shared constants and CSS file must exist)
-  - _Requirements: 1.1, 1.2, 1.3_
-
-- [x] 3. (P) Add dynamic import for MarpSlides in the Slides routing component
-  - Replace the static import of MarpSlides with a React.lazy dynamic import that resolves the named export
-  - Wrap the MarpSlides rendering branch in a Suspense boundary with a simple loading fallback
-  - Keep GrowiSlides as a static import (the common, lightweight path)
-  - The dynamic import ensures MarpSlides and its transitive Marp dependencies are only loaded when `hasMarpFlag` is true
-  - Depends on task 1 (shared constants must exist); parallel-safe with task 2 (different file)
-  - _Requirements: 2.1, 2.2, 2.3_
-
-- [x] 4. Build verification and functional validation
-- [x] 4.1 Build the presentation package and verify module separation in the output
-  - Run the presentation package build and confirm it succeeds
-  - Inspect the built GrowiSlides output file to confirm it contains no references to `@marp-team/marp-core` or `@marp-team/marpit`
-  - Inspect the built Slides output file to confirm it contains a dynamic `import()` expression for MarpSlides
-  - _Requirements: 5.1, 5.3, 5.4_
-
-- [x] 4.2 Build the main GROWI application and verify successful compilation
-  - Run the full app build to confirm no regressions from the presentation package changes
-  - Verify that both Marp and non-Marp slide rendering paths are intact by checking the build completes without type errors
-  - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 5.2_

+ 1 - 41
.kiro/specs/optimize-presentation/design.md → .kiro/specs/presentation/design.md

@@ -1,4 +1,4 @@
-# Design Document: optimize-presentation
+# Design Document: presentation
 
 ## Overview
 
@@ -118,23 +118,6 @@ flowchart TD
   F --> G[vite build compiles all sources]
 ```
 
-## Requirements Traceability
-
-| Requirement | Summary | Components | Interfaces | Flows |
-|-------------|---------|------------|------------|-------|
-| 1.1 | No marp-core references in GrowiSlides build output | GrowiSlides, marpit-base-css | — | — |
-| 1.2 | Non-Marp slides render without loading Marp | Slides, GrowiSlides | — | Slide Rendering Decision |
-| 1.3 | Pre-extracted CSS constants for container styling | marpit-base-css | MarpitBaseCss | CSS Extraction |
-| 1.4 | MARP_CONTAINER_CLASS_NAME in shared consts | consts/index.ts | — | — |
-| 2.1 | Dynamic load MarpSlides for marp:true pages | Slides | — | Slide Rendering Decision |
-| 2.2 | Loading indicator during MarpSlides load | Slides | — | Slide Rendering Decision |
-| 2.3 | No MarpSlides import triggered for non-Marp | Slides | — | Slide Rendering Decision |
-| 3.1 | Build-time CSS extraction script | extract-marpit-css.mjs | ExtractScript | CSS Extraction |
-| 3.2 | Extraction runs before source compilation | package.json pre:build:src | — | CSS Extraction |
-| 3.3 | Generated file committed for dev mode | marpit-base-css.ts | — | — |
-| 4.1–4.5 | Functional equivalence across all render paths | All components | — | Both flows |
-| 5.1–5.4 | Build verification of module separation | Build outputs | — | — |
-
 ## Components and Interfaces
 
 | Component | Domain | Intent | Req Coverage | Key Dependencies | Contracts |
@@ -299,26 +282,3 @@ export const presentationMarpit: Marp;
 - Validation: Script exits with error if CSS extraction produces empty output
 - Risks: Marp options must stay synchronized with `growi-marpit.ts`
 
-## Testing Strategy
-
-### Unit Tests
-- Verify `GrowiSlides` renders correctly with pre-extracted CSS constants (no Marp imports in test)
-- Verify `Slides` renders `GrowiSlides` when `hasMarpFlag` is false/undefined
-- Verify `Slides` renders `MarpSlides` (via Suspense) when `hasMarpFlag` is true
-
-### Build Verification Tests
-- `GrowiSlides.js` build output contains no references to `@marp-team/marp-core` or `@marp-team/marpit`
-- `Slides.js` build output contains dynamic `import()` for MarpSlides
-- `@growi/presentation` builds without errors
-- `@growi/app` builds without errors
-
-### Integration Tests
-- Marp slide page (`marp: true`) renders correctly in inline view
-- Non-Marp slide page (`slide: true`) renders correctly in inline view
-- Presentation modal works for both Marp and non-Marp content
-
-## Performance & Scalability
-
-**Target**: Eliminate ~896KB of Marp-related JavaScript from the async chunk loaded for non-Marp slide rendering.
-
-**Measurement**: Compare the chunk contents before and after optimization using the existing `ChunkModuleStatsPlugin` or manual inspection of build output. The `initial` module count (primary KPI from build-optimization skill) is not directly affected since slides are already in async chunks, but the async chunk size is reduced.

+ 26 - 0
.kiro/specs/presentation/requirements.md

@@ -0,0 +1,26 @@
+# Presentation Feature — Requirements Overview
+
+## Introduction
+
+The GROWI presentation feature (`@growi/presentation` package) provides slide rendering for wiki pages using frontmatter flags. It supports two rendering modes:
+
+- **GrowiSlides** (`slide: true`): Lightweight slide rendering using ReactMarkdown with Marp container styling applied via pre-extracted CSS constants. Does not load Marp runtime dependencies.
+- **MarpSlides** (`marp: true`): Full Marp-powered slide rendering using `@marp-team/marp-core`, loaded dynamically only when needed.
+
+## Key Requirements
+
+### 1. Module Separation
+
+GrowiSlides renders without loading `@marp-team/marp-core` or `@marp-team/marpit`. Marp dependencies are isolated behind a dynamic import boundary (`React.lazy`) and only loaded for pages with `marp: true`.
+
+### 2. Build-Time CSS Extraction
+
+Marp base CSS is pre-extracted at build time via `extract-marpit-css.mjs`. The generated constants file (`consts/marpit-base-css.ts`) is committed to the repository so that dev mode works without running the extraction script first. The extraction runs automatically before source compilation via the `pre:build:src` script.
+
+### 3. Functional Equivalence
+
+Both Marp and non-Marp slide pages render correctly in inline view and presentation modal. No behavioral differences from the user's perspective.
+
+### 4. Build Integrity
+
+Both `@growi/presentation` and `@growi/app` build successfully. The GrowiSlides build output contains no Marp module references, and the Slides build output contains a dynamic `import()` for MarpSlides.

+ 0 - 0
.kiro/specs/optimize-presentation/research.md → .kiro/specs/presentation/research.md


+ 3 - 2
.kiro/specs/optimize-presentation/spec.json → .kiro/specs/presentation/spec.json

@@ -1,9 +1,10 @@
 {
-  "feature_name": "optimize-presentation",
+  "feature_name": "presentation",
   "created_at": "2026-03-05T12:00:00Z",
-  "updated_at": "2026-03-05T13:30:00Z",
+  "updated_at": "2026-03-23T00:00:00Z",
   "language": "en",
   "phase": "implementation-complete",
+  "cleanup_completed": true,
   "approvals": {
     "requirements": {
       "generated": true,

+ 2 - 2
package.json

@@ -79,12 +79,12 @@
     "stylelint-config-recommended-scss": "^14.0.0",
     "ts-deepmerge": "^6.2.0",
     "ts-node": "^10.9.2",
-    "ts-patch": "^3.2.0",
+    "ts-patch": "^3.3.0",
     "tsconfig-paths": "^4.2.0",
     "tspc": "^1.1.2",
     "turbo": "^2.1.3",
     "typescript": "^5.9.3",
-    "typescript-transform-paths": "^3.4.7",
+    "typescript-transform-paths": "^3.5.6",
     "vite": "^5.4.21",
     "vite-plugin-dts": "^3.9.1",
     "vite-tsconfig-paths": "^5.0.1",

+ 23 - 12
pnpm-lock.yaml

@@ -133,8 +133,8 @@ importers:
         specifier: ^10.9.2
         version: 10.9.2(@swc/core@1.10.7(@swc/helpers@0.5.18))(@types/node@20.19.17)(typescript@5.9.3)
       ts-patch:
-        specifier: ^3.2.0
-        version: 3.2.0
+        specifier: ^3.3.0
+        version: 3.3.0
       tsconfig-paths:
         specifier: ^4.2.0
         version: 4.2.0
@@ -148,8 +148,8 @@ importers:
         specifier: ^5.9.3
         version: 5.9.3
       typescript-transform-paths:
-        specifier: ^3.4.7
-        version: 3.4.7(typescript@5.9.3)
+        specifier: ^3.5.6
+        version: 3.5.6(typescript@5.9.3)
       vite:
         specifier: ^5.4.21
         version: 5.4.21(@types/node@20.19.17)(sass@1.77.6)(terser@5.46.0)
@@ -8868,6 +8868,10 @@ packages:
     resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
     engines: {node: '>=6'}
 
+  global-prefix@4.0.0:
+    resolution: {integrity: sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==}
+    engines: {node: '>=16'}
+
   globals@11.12.0:
     resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
     engines: {node: '>=4'}
@@ -9838,6 +9842,7 @@ packages:
     resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==}
     engines: {node: '>=8.17.0'}
     hasBin: true
+    bundledDependencies: []
 
   jsonfile@3.0.1:
     resolution: {integrity: sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==}
@@ -13560,8 +13565,8 @@ packages:
       '@swc/wasm':
         optional: true
 
-  ts-patch@3.2.0:
-    resolution: {integrity: sha512-fUGMkjGIlD4BFibDM+6pLYLXRguzCUY6fhP1KQzSnFJfAtTDT7DKyX0yHn3CJqfBv4mia/o3ZRte31UVf9Dl1A==}
+  ts-patch@3.3.0:
+    resolution: {integrity: sha512-zAOzDnd5qsfEnjd9IGy1IRuvA7ygyyxxdxesbhMdutt8AHFjD8Vw8hU2rMF89HX1BKRWFYqKHrO8Q6lw0NeUZg==}
     hasBin: true
 
   tsconfck@2.1.2:
@@ -13802,8 +13807,8 @@ packages:
       typeorm-aurora-data-api-driver:
         optional: true
 
-  typescript-transform-paths@3.4.7:
-    resolution: {integrity: sha512-1Us1kdkdfKd2unbkBAOV2HHRmbRBYpSuk9nJ7cLD2hP4QmfToiM/VpxNlhJc1eezVwVqSKSBjNSzZsK/fWR/9A==}
+  typescript-transform-paths@3.5.6:
+    resolution: {integrity: sha512-3eQTG6Ogt+pgPEh45uX2s9OwcfAVjWnyNO+osjYcOqYaWDVMIFUkqW8e0O1cOaVwdMqQFQf6alDT+76xmeS2Ag==}
     peerDependencies:
       typescript: '>=3.6.5'
 
@@ -23722,6 +23727,12 @@ snapshots:
       kind-of: 6.0.3
       which: 1.3.1
 
+  global-prefix@4.0.0:
+    dependencies:
+      ini: 4.1.3
+      kind-of: 6.0.3
+      which: 4.0.0
+
   globals@11.12.0: {}
 
   globals@13.24.0:
@@ -29411,10 +29422,10 @@ snapshots:
     optionalDependencies:
       '@swc/core': 1.10.7(@swc/helpers@0.5.18)
 
-  ts-patch@3.2.0:
+  ts-patch@3.3.0:
     dependencies:
       chalk: 4.1.2
-      global-prefix: 3.0.0
+      global-prefix: 4.0.0
       minimist: 1.2.8
       resolve: 1.22.8
       semver: 7.7.4
@@ -29626,9 +29637,9 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  typescript-transform-paths@3.4.7(typescript@5.9.3):
+  typescript-transform-paths@3.5.6(typescript@5.9.3):
     dependencies:
-      minimatch: 3.1.2
+      minimatch: 9.0.5
       typescript: 5.9.3
 
   typescript@5.4.2: {}