@open-press/cli 0.8.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +33 -23
  2. package/dist/cli.js +320 -252
  3. package/package.json +9 -8
  4. package/template/core/AGENTS.md +0 -126
  5. package/template/core/CHANGELOG.md +0 -215
  6. package/template/core/README.md +0 -40
  7. package/template/core/engine/cli.mjs +0 -96
  8. package/template/core/engine/commands/_shared.mjs +0 -177
  9. package/template/core/engine/commands/deploy.mjs +0 -31
  10. package/template/core/engine/commands/dev.mjs +0 -49
  11. package/template/core/engine/commands/doctor.mjs +0 -229
  12. package/template/core/engine/commands/export.mjs +0 -8
  13. package/template/core/engine/commands/init.mjs +0 -24
  14. package/template/core/engine/commands/inspect.mjs +0 -35
  15. package/template/core/engine/commands/pdf.mjs +0 -26
  16. package/template/core/engine/commands/preview.mjs +0 -26
  17. package/template/core/engine/commands/render.mjs +0 -17
  18. package/template/core/engine/commands/replace.mjs +0 -41
  19. package/template/core/engine/commands/search.mjs +0 -33
  20. package/template/core/engine/commands/typecheck.mjs +0 -5
  21. package/template/core/engine/commands/upgrade.mjs +0 -159
  22. package/template/core/engine/commands/validate.mjs +0 -17
  23. package/template/core/engine/document-export.mjs +0 -15
  24. package/template/core/engine/init.mjs +0 -90
  25. package/template/core/engine/output/chrome-pdf.d.mts +0 -34
  26. package/template/core/engine/output/chrome-pdf.mjs +0 -358
  27. package/template/core/engine/output/deploy-sync.mjs +0 -15
  28. package/template/core/engine/output/fonts.mjs +0 -62
  29. package/template/core/engine/output/katex-assets.mjs +0 -45
  30. package/template/core/engine/output/page-block.mjs +0 -30
  31. package/template/core/engine/output/pdf-media.mjs +0 -45
  32. package/template/core/engine/output/public-assets.mjs +0 -19
  33. package/template/core/engine/output/static-server.mjs +0 -532
  34. package/template/core/engine/react/caption-numbering.mjs +0 -73
  35. package/template/core/engine/react/comment-endpoint.d.mts +0 -11
  36. package/template/core/engine/react/comment-endpoint.mjs +0 -102
  37. package/template/core/engine/react/comment-marker.mjs +0 -374
  38. package/template/core/engine/react/document-entry.mjs +0 -324
  39. package/template/core/engine/react/document-export.mjs +0 -373
  40. package/template/core/engine/react/http-json.mjs +0 -24
  41. package/template/core/engine/react/mdx-compile.mjs +0 -599
  42. package/template/core/engine/react/measurement-css.mjs +0 -136
  43. package/template/core/engine/react/object-entities.mjs +0 -119
  44. package/template/core/engine/react/pagination/allocator.mjs +0 -122
  45. package/template/core/engine/react/pagination/regions.mjs +0 -81
  46. package/template/core/engine/react/pagination-constants.mjs +0 -3
  47. package/template/core/engine/react/pagination.mjs +0 -9
  48. package/template/core/engine/react/pipeline/allocate.mjs +0 -251
  49. package/template/core/engine/react/pipeline/final-render.mjs +0 -94
  50. package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -302
  51. package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
  52. package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
  53. package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
  54. package/template/core/engine/react/section-css.mjs +0 -56
  55. package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
  56. package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
  57. package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
  58. package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
  59. package/template/core/engine/react/style-discovery.mjs +0 -142
  60. package/template/core/engine/runtime/config.d.mts +0 -40
  61. package/template/core/engine/runtime/config.mjs +0 -175
  62. package/template/core/engine/runtime/file-utils.mjs +0 -106
  63. package/template/core/engine/runtime/file-walk.mjs +0 -22
  64. package/template/core/engine/runtime/inspection.mjs +0 -328
  65. package/template/core/engine/runtime/issue-report.mjs +0 -44
  66. package/template/core/engine/runtime/path-utils.mjs +0 -20
  67. package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
  68. package/template/core/engine/runtime/source-text-tools.mjs +0 -832
  69. package/template/core/engine/runtime/source-workspace.mjs +0 -159
  70. package/template/core/engine/runtime/validation.mjs +0 -174
  71. package/template/core/index.html +0 -13
  72. package/template/core/openpress.config.mjs +0 -12
  73. package/template/core/package.json +0 -91
  74. package/template/core/src/main.tsx +0 -16
  75. package/template/core/src/openpress/app/OpenPressApp.tsx +0 -140
  76. package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -94
  77. package/template/core/src/openpress/app/index.ts +0 -2
  78. package/template/core/src/openpress/core/Frame.tsx +0 -78
  79. package/template/core/src/openpress/core/FrameContext.tsx +0 -24
  80. package/template/core/src/openpress/core/MdxArea.tsx +0 -34
  81. package/template/core/src/openpress/core/Press.tsx +0 -34
  82. package/template/core/src/openpress/core/cn.ts +0 -4
  83. package/template/core/src/openpress/core/index.tsx +0 -40
  84. package/template/core/src/openpress/core/primitives.tsx +0 -44
  85. package/template/core/src/openpress/core/types.ts +0 -191
  86. package/template/core/src/openpress/core/useSource.ts +0 -28
  87. package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
  88. package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
  89. package/template/core/src/openpress/document-model/documentTypes.ts +0 -138
  90. package/template/core/src/openpress/document-model/index.ts +0 -6
  91. package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -51
  92. package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
  93. package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
  94. package/template/core/src/openpress/manuscript/index.tsx +0 -238
  95. package/template/core/src/openpress/mdx/index.ts +0 -88
  96. package/template/core/src/openpress/numbering/index.ts +0 -294
  97. package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
  98. package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
  99. package/template/core/src/openpress/reader/index.ts +0 -10
  100. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
  101. package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
  102. package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
  103. package/template/core/src/openpress/reader/readerScroll.ts +0 -92
  104. package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
  105. package/template/core/src/openpress/reader/readerTypes.ts +0 -4
  106. package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
  107. package/template/core/src/openpress/reader/usePanelState.ts +0 -56
  108. package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
  109. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
  110. package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
  111. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
  112. package/template/core/src/openpress/shared/Panel.tsx +0 -77
  113. package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
  114. package/template/core/src/openpress/shared/index.ts +0 -4
  115. package/template/core/src/openpress/shared/numberUtils.ts +0 -3
  116. package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
  117. package/template/core/src/openpress/workbench/Workbench.tsx +0 -407
  118. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
  119. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
  120. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
  121. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
  122. package/template/core/src/openpress/workbench/actions/index.ts +0 -5
  123. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
  124. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
  125. package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
  126. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
  127. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
  128. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
  129. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
  130. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
  131. package/template/core/src/openpress/workbench/document/index.ts +0 -10
  132. package/template/core/src/openpress/workbench/index.ts +0 -2
  133. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
  134. package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
  135. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
  136. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
  137. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
  138. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -248
  139. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
  140. package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
  141. package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
  142. package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
  143. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -76
  144. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
  145. package/template/core/src/openpress/workbench/panels/index.ts +0 -3
  146. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -523
  147. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
  148. package/template/core/src/openpress/workbench/project/index.ts +0 -2
  149. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
  150. package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
  151. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
  152. package/template/core/src/openpress/workbench/shell/index.ts +0 -1
  153. package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
  154. package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
  155. package/template/core/src/styles/openpress/app-shell.css +0 -251
  156. package/template/core/src/styles/openpress/media-workspace.css +0 -230
  157. package/template/core/src/styles/openpress/print-route.css +0 -184
  158. package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
  159. package/template/core/src/styles/openpress/public-viewer.css +0 -688
  160. package/template/core/src/styles/openpress/reader-runtime.css +0 -980
  161. package/template/core/src/styles/openpress/responsive.css +0 -245
  162. package/template/core/src/styles/openpress/workbench-panels.css +0 -594
  163. package/template/core/src/styles/openpress/workbench.css +0 -1255
  164. package/template/core/src/styles/openpress.css +0 -14
  165. package/template/core/src/vite-env.d.ts +0 -9
  166. package/template/core/tsconfig.json +0 -40
  167. package/template/core/vite.config.ts +0 -584
  168. package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
  169. package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
  170. package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
  171. package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
  172. package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
  173. package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
  174. package/template/packs/academic-paper/document/components/Page.tsx +0 -60
  175. package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
  176. package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
  177. package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
  178. package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
  179. package/template/packs/academic-paper/document/design.md +0 -279
  180. package/template/packs/academic-paper/document/index.tsx +0 -123
  181. package/template/packs/academic-paper/document/media/README.md +0 -13
  182. package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
  183. package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
  184. package/template/packs/academic-paper/document/theme/README.md +0 -11
  185. package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
  186. package/template/packs/academic-paper/document/theme/base/print.css +0 -93
  187. package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
  188. package/template/packs/academic-paper/document/theme/fonts.css +0 -3
  189. package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
  190. package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
  191. package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
  192. package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
  193. package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
  194. package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
  195. package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
  196. package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
  197. package/template/packs/academic-paper/document/theme/tokens.css +0 -80
  198. package/template/packs/academic-paper/openpress.config.mjs +0 -5
  199. package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
  200. package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
  201. package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
  202. package/template/packs/claude-document/document/components/Page.tsx +0 -37
  203. package/template/packs/claude-document/document/design.md +0 -142
  204. package/template/packs/claude-document/document/index.tsx +0 -94
  205. package/template/packs/claude-document/document/media/README.md +0 -13
  206. package/template/packs/claude-document/document/openpress.config.mjs +0 -26
  207. package/template/packs/claude-document/document/theme/README.md +0 -15
  208. package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
  209. package/template/packs/claude-document/document/theme/base/print.css +0 -93
  210. package/template/packs/claude-document/document/theme/base/typography.css +0 -612
  211. package/template/packs/claude-document/document/theme/fonts.css +0 -4
  212. package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
  213. package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
  214. package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
  215. package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
  216. package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
  217. package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
  218. package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
  219. package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
  220. package/template/packs/claude-document/document/theme/tokens.css +0 -89
  221. package/template/packs/claude-document/openpress.config.mjs +0 -5
  222. package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
  223. package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
  224. package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
  225. package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
  226. package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
  227. package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
  228. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
  229. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
  230. package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
  231. package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
  232. package/template/packs/editorial-monograph/document/design.md +0 -279
  233. package/template/packs/editorial-monograph/document/index.tsx +0 -97
  234. package/template/packs/editorial-monograph/document/media/README.md +0 -13
  235. package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
  236. package/template/packs/editorial-monograph/document/theme/README.md +0 -11
  237. package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
  238. package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
  239. package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
  240. package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
  241. package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
  242. package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
  243. package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
  244. package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
  245. package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
  246. package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
  247. package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
  248. package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
  249. package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
  250. package/template/packs/editorial-monograph/openpress.config.mjs +0 -5
@@ -1,324 +0,0 @@
1
- // Layer 1 — Document entry loader.
2
- //
3
- // Loads `document/index.tsx`, validates it exports a Press component as
4
- // default, reads optional `config` and `sources` named exports, and sets
5
- // up the vite SSR server with `@open-press/core` aliases (including the
6
- // subpaths `/mdx` and `/manuscript`).
7
-
8
- import fs from "node:fs/promises";
9
- import { createRequire } from "node:module";
10
- import path from "node:path";
11
- import { fileURLToPath } from "node:url";
12
- import react from "@vitejs/plugin-react";
13
- import ts from "typescript";
14
- import { createServer as createViteServer } from "vite";
15
- import { normalizeConfig } from "../runtime/config.mjs";
16
-
17
- const ENGINE_REACT_DIR = path.dirname(fileURLToPath(import.meta.url));
18
- const FRAMEWORK_ROOT = path.resolve(ENGINE_REACT_DIR, "..", "..");
19
- export const CORE_ENTRY = path.join(FRAMEWORK_ROOT, "src", "openpress", "core", "index.tsx");
20
- export const MDX_ENTRY = path.join(FRAMEWORK_ROOT, "src", "openpress", "mdx", "index.ts");
21
- export const MANUSCRIPT_ENTRY = path.join(FRAMEWORK_ROOT, "src", "openpress", "manuscript", "index.tsx");
22
- export const NUMBERING_ENTRY = path.join(FRAMEWORK_ROOT, "src", "openpress", "numbering", "index.ts");
23
- const REACT_PACKAGE_ROOT = path.join(FRAMEWORK_ROOT, "node_modules", "react");
24
- const require = createRequire(import.meta.url);
25
- const REACT_EXPORT_NAMES = Object.keys(require("react")).filter((name) => /^[A-Za-z_$][\w$]*$/.test(name));
26
-
27
- export async function loadReactDocumentEntry(root = ".", { server: externalServer } = {}) {
28
- const workspaceRoot = path.resolve(root);
29
- const entryPath = path.join(workspaceRoot, "document", "index.tsx");
30
- if (!(await fileExists(entryPath))) return null;
31
-
32
- const source = await fs.readFile(entryPath, "utf8");
33
- assertNoObviousTopLevelSideEffects(source, entryPath);
34
-
35
- // If caller provides a server, reuse it so module identity is shared
36
- // across the pipeline (PressContext, React, etc.). Otherwise open a
37
- // temporary server for one-shot config reads.
38
- const ownServer = externalServer ?? (await createReactSsrServer(workspaceRoot));
39
- try {
40
- const mod = await ownServer.ssrLoadModule(entryPath);
41
-
42
- // Press default export is required for export/render but not for
43
- // config-only commands (search, replace, validate). Validate if present;
44
- // export pipeline throws separately if it's missing when actually needed.
45
- const Press = typeof mod.default === "function" ? mod.default : null;
46
-
47
- const config = normalizeReactDocumentConfig(workspaceRoot, entryPath, mod.config);
48
- const sources = mod.sources ?? {};
49
- if (sources && (typeof sources !== "object" || Array.isArray(sources))) {
50
- throw new Error(
51
- `OpenPress document entry ${entryPath} \`sources\` export must be an object literal (or omitted).`,
52
- );
53
- }
54
-
55
- return {
56
- entryPath,
57
- config,
58
- Press,
59
- sources,
60
- };
61
- } finally {
62
- if (!externalServer) await ownServer.close();
63
- }
64
- }
65
-
66
- export async function createReactSsrServer(workspaceRoot = ".") {
67
- const resolvedWorkspaceRoot = path.resolve(workspaceRoot);
68
- return createViteServer({
69
- configFile: false,
70
- root: FRAMEWORK_ROOT,
71
- cacheDir: path.join(resolvedWorkspaceRoot, ".openpress", "vite-ssr"),
72
- appType: "custom",
73
- logLevel: "silent",
74
- plugins: [reactRuntimePlugin(), react()],
75
- resolve: {
76
- alias: [
77
- // ORDER MATTERS: subpath aliases must precede the base alias so that
78
- // `@open-press/core/mdx` doesn't resolve to `@open-press/core` + `/mdx`.
79
- { find: "@open-press/core/mdx", replacement: MDX_ENTRY },
80
- { find: "@open-press/core/manuscript", replacement: MANUSCRIPT_ENTRY },
81
- { find: "@open-press/core/numbering", replacement: NUMBERING_ENTRY },
82
- { find: "@open-press/core", replacement: CORE_ENTRY },
83
- { find: "@/components", replacement: path.join(resolvedWorkspaceRoot, "document", "components") },
84
- ],
85
- },
86
- optimizeDeps: {
87
- include: [
88
- "@mdx-js/react",
89
- "react",
90
- "react-dom",
91
- "react-dom/server",
92
- "react/jsx-dev-runtime",
93
- "react/jsx-runtime",
94
- ],
95
- },
96
- server: {
97
- middlewareMode: true,
98
- fs: {
99
- allow: [FRAMEWORK_ROOT, resolvedWorkspaceRoot],
100
- },
101
- },
102
- });
103
- }
104
-
105
- function assertNoObviousTopLevelSideEffects(source, entryPath) {
106
- const sourceFile = ts.createSourceFile(entryPath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
107
- for (const statement of sourceFile.statements) {
108
- if (ts.isImportDeclaration(statement)) {
109
- assertPureImport(statement, entryPath);
110
- continue;
111
- }
112
-
113
- if (ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement)) continue;
114
- if (ts.isExportDeclaration(statement) && statement.isTypeOnly) continue;
115
- if (ts.isFunctionDeclaration(statement)) continue;
116
- if (ts.isVariableStatement(statement)) {
117
- assertTopLevelVariableStatement(statement, entryPath);
118
- continue;
119
- }
120
- if (ts.isExportAssignment(statement)) {
121
- assertPureDefaultExport(statement.expression, entryPath);
122
- continue;
123
- }
124
-
125
- throw new Error(`OpenPress document entry has unsupported top-level code in ${entryPath}: ${statementKindName(statement)}`);
126
- }
127
- }
128
-
129
- function assertPureImport(statement, entryPath) {
130
- if (!statement.importClause) {
131
- throw new Error(`OpenPress document entry has an unsupported side-effect import in ${entryPath}`);
132
- }
133
- const moduleName = stringLiteralText(statement.moduleSpecifier);
134
- if (!statement.importClause.isTypeOnly && isFileSystemModule(moduleName)) {
135
- throw new Error(`OpenPress document entry imports filesystem APIs at top level in ${entryPath}`);
136
- }
137
- }
138
-
139
- function assertTopLevelVariableStatement(statement, entryPath) {
140
- if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0) {
141
- throw new Error(`OpenPress document entry only allows top-level const declarations in ${entryPath}`);
142
- }
143
-
144
- const exported = hasModifier(statement, ts.SyntaxKind.ExportKeyword);
145
- for (const declaration of statement.declarationList.declarations) {
146
- if (!ts.isIdentifier(declaration.name)) {
147
- throw new Error(`OpenPress document entry only allows identifier const declarations at top level in ${entryPath}`);
148
- }
149
- const name = declaration.name.text;
150
- if (exported && name !== "config" && name !== "sources") {
151
- throw new Error(`OpenPress document entry only allows exported const config and sources in ${entryPath}`);
152
- }
153
- if (!declaration.initializer) {
154
- throw new Error(`OpenPress document entry const "${name}" must have an initializer in ${entryPath}`);
155
- }
156
- if (name === "config") {
157
- assertPureExpression(declaration.initializer, entryPath, { allowMdxSourceCall: false });
158
- } else if (name === "sources") {
159
- assertPureSourcesInitializer(declaration.initializer, entryPath);
160
- } else {
161
- assertPureStaticInitializer(declaration.initializer, entryPath);
162
- }
163
- }
164
- }
165
-
166
- function assertPureSourcesInitializer(node, entryPath) {
167
- const expression = skipExpressionWrappers(node);
168
- if (!ts.isObjectLiteralExpression(expression)) {
169
- throw new Error(`OpenPress document entry exported sources must be an object literal in ${entryPath}`);
170
- }
171
- assertPureExpression(expression, entryPath, { allowMdxSourceCall: true });
172
- }
173
-
174
- function assertPureStaticInitializer(node, entryPath) {
175
- const expression = skipExpressionWrappers(node);
176
- if (ts.isFunctionExpression(expression) || ts.isArrowFunction(expression) || ts.isClassExpression(expression)) return;
177
- assertPureExpression(expression, entryPath, { allowMdxSourceCall: false });
178
- }
179
-
180
- function assertPureDefaultExport(node, entryPath) {
181
- const expression = skipExpressionWrappers(node);
182
- if (ts.isFunctionExpression(expression) || ts.isArrowFunction(expression) || ts.isIdentifier(expression)) return;
183
- throw new Error(`OpenPress document entry default export must be a function component in ${entryPath}`);
184
- }
185
-
186
- function assertPureExpression(node, entryPath, { allowMdxSourceCall }) {
187
- visitExpression(node, (child) => {
188
- if (ts.isAwaitExpression(child)) {
189
- throw new Error(`OpenPress document entry has an unsupported top-level side effect: await in ${entryPath}`);
190
- }
191
- if (ts.isCallExpression(child)) {
192
- const callee = skipExpressionWrappers(child.expression);
193
- if (allowMdxSourceCall && ts.isIdentifier(callee) && callee.text === "mdxSource") return;
194
- if (ts.isIdentifier(callee) && callee.text === "fetch") {
195
- throw new Error(`OpenPress document entry has an unsupported top-level side effect: fetch(...) in ${entryPath}`);
196
- }
197
- if (ts.isPropertyAccessExpression(callee) && isIdentifierText(callee.expression, "console")) {
198
- throw new Error(`OpenPress document entry has an unsupported top-level side effect: console.${callee.name.text}(...) in ${entryPath}`);
199
- }
200
- if (ts.isPropertyAccessExpression(callee) && isIdentifierText(callee.expression, "fs")) {
201
- throw new Error(`OpenPress document entry has an unsupported top-level side effect: fs.${callee.name.text}(...) in ${entryPath}`);
202
- }
203
- throw new Error(`OpenPress document entry cannot execute top-level function calls outside sources.mdxSource(...) in ${entryPath}`);
204
- }
205
- if (ts.isPropertyAccessExpression(child) && isProcessEnvAccess(child)) {
206
- throw new Error(`OpenPress document entry cannot read process.env at top level in ${entryPath}`);
207
- }
208
- });
209
- }
210
-
211
- function visitExpression(node, visitor) {
212
- visitor(node);
213
- if (node !== undefined && node !== null && isFunctionLikeExpression(node)) return;
214
- ts.forEachChild(node, (child) => visitExpression(child, visitor));
215
- }
216
-
217
- function isFunctionLikeExpression(node) {
218
- return ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isClassExpression(node);
219
- }
220
-
221
- function skipExpressionWrappers(node) {
222
- let current = node;
223
- while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isTypeAssertionExpression(current) || ts.isNonNullExpression(current)) {
224
- current = current.expression;
225
- }
226
- return current;
227
- }
228
-
229
- function isProcessEnvAccess(node) {
230
- return node.name.text === "env" && isIdentifierText(skipExpressionWrappers(node.expression), "process");
231
- }
232
-
233
- function isIdentifierText(node, text) {
234
- return ts.isIdentifier(node) && node.text === text;
235
- }
236
-
237
- function hasModifier(node, kind) {
238
- return ts.canHaveModifiers(node) && (ts.getModifiers(node) ?? []).some((modifier) => modifier.kind === kind);
239
- }
240
-
241
- function statementKindName(node) {
242
- return ts.SyntaxKind[node.kind] ?? String(node.kind);
243
- }
244
-
245
- function stringLiteralText(node) {
246
- return ts.isStringLiteral(node) ? node.text : "";
247
- }
248
-
249
- function isFileSystemModule(moduleName) {
250
- return moduleName === "fs" || moduleName === "node:fs" || moduleName === "fs/promises" || moduleName === "node:fs/promises";
251
- }
252
-
253
- function normalizeReactDocumentConfig(workspaceRoot, entryPath, config) {
254
- if (config != null && (typeof config !== "object" || Array.isArray(config))) {
255
- throw new Error("OpenPress React document entry `config` export must be an object when provided.");
256
- }
257
- const rawConfig = config ?? {};
258
- const paths = rawConfig.paths ?? {};
259
- return normalizeConfig(workspaceRoot, {
260
- ...rawConfig,
261
- documentDir: rawConfig.documentDir ?? paths.documentDir ?? "document",
262
- sourceDir: rawConfig.sourceDir ?? paths.chaptersDir ?? paths.sourceDir ?? "chapters",
263
- componentsDir: rawConfig.componentsDir ?? paths.componentsDir ?? "components",
264
- mediaDir: rawConfig.mediaDir ?? paths.mediaDir ?? "media",
265
- themeDir: rawConfig.themeDir ?? paths.themeDir ?? "theme",
266
- designDoc: rawConfig.designDoc ?? paths.designDoc ?? "design.md",
267
- }, entryPath);
268
- }
269
-
270
- async function fileExists(filePath) {
271
- try {
272
- const stat = await fs.stat(filePath);
273
- return stat.isFile();
274
- } catch (error) {
275
- if (error?.code === "ENOENT") return false;
276
- throw error;
277
- }
278
- }
279
-
280
- function reactRuntimePlugin() {
281
- const modules = {
282
- react: "\0openpress-react",
283
- "react/jsx-runtime": "\0openpress-react-jsx-runtime",
284
- "react/jsx-dev-runtime": "\0openpress-react-jsx-dev-runtime",
285
- };
286
- return {
287
- name: "openpress-react-runtime",
288
- enforce: "pre",
289
- resolveId(id) {
290
- return modules[id] ?? null;
291
- },
292
- load(id) {
293
- if (id === modules.react) return reactModuleShim();
294
- if (id === modules["react/jsx-runtime"]) {
295
- return runtimeModuleShim(path.join(REACT_PACKAGE_ROOT, "jsx-runtime.js"), ["Fragment", "jsx", "jsxs"]);
296
- }
297
- if (id === modules["react/jsx-dev-runtime"]) {
298
- return runtimeModuleShim(path.join(REACT_PACKAGE_ROOT, "jsx-dev-runtime.js"), ["Fragment", "jsxDEV"]);
299
- }
300
- return null;
301
- },
302
- };
303
- }
304
-
305
- function runtimeModuleShim(modulePath, names) {
306
- const exports = names.map((name) => `export const ${name} = runtime.${name};`).join("\n");
307
- return `import { createRequire } from "node:module";
308
- const require = createRequire(${JSON.stringify(import.meta.url)});
309
- const runtime = require(${JSON.stringify(modulePath)});
310
- ${exports}
311
- export default runtime;
312
- `;
313
- }
314
-
315
- function reactModuleShim() {
316
- const reactPath = require.resolve("react");
317
- const exports = REACT_EXPORT_NAMES.map((name) => `export const ${name} = React.${name};`).join("\n");
318
- return `import { createRequire } from "node:module";
319
- const require = createRequire(${JSON.stringify(import.meta.url)});
320
- const React = require(${JSON.stringify(reactPath)});
321
- ${exports}
322
- export default React;
323
- `;
324
- }
@@ -1,373 +0,0 @@
1
- // Layer 6 orchestrator.
2
- //
3
- // Wires Layer 1 (entry load) -> source resolution -> Layer 2/3/4 iteration
4
- // -> Layer 5 final render -> document.json + asset sync.
5
-
6
- import fs from "node:fs/promises";
7
- import path from "node:path";
8
- import { pathToFileURL } from "node:url";
9
- import { documentRelativePath, pageToBlock } from "../output/page-block.mjs";
10
- import { syncPublicAssets } from "../output/public-assets.mjs";
11
- import { createCaptionNumberingState, numberCaptionsInHtml } from "./caption-numbering.mjs";
12
- import { buildSectionScopedCss } from "./section-css.mjs";
13
- import { CORE_ENTRY, createReactSsrServer, loadReactDocumentEntry } from "./document-entry.mjs";
14
- import { buildReactMeasurementCss } from "./measurement-css.mjs";
15
- import { buildObjectEntities } from "./object-entities.mjs";
16
- import { allocateChains } from "./pipeline/allocate.mjs";
17
- import { measureFrames } from "./pipeline/frame-measurement.mjs";
18
- import { renderFinalPress } from "./pipeline/final-render.mjs";
19
- import { expandPressTree } from "./pipeline/press-tree.mjs";
20
- import { resolveAllSources } from "./sources/mdx-resolver.mjs";
21
- import { discoverSectionStyles } from "./style-discovery.mjs";
22
-
23
- const MAX_ITERATIONS = 20;
24
-
25
- export async function exportReactDocument(root = ".", { syncAssets = true } = {}) {
26
- const workspaceRoot = path.resolve(root);
27
- // Quick existence check without opening an SSR server.
28
- const fastCheck = await loadReactDocumentEntry(workspaceRoot);
29
- if (!fastCheck) return null;
30
-
31
- const server = await createReactSsrServer(workspaceRoot);
32
- try {
33
- // Reload the entry through THIS server so the module identity matches
34
- // what the rest of the pipeline (PressContext, hooks) sees.
35
- const entry = await loadReactDocumentEntry(workspaceRoot, { server });
36
- if (!entry) return null;
37
- if (!entry.Press) {
38
- throw new Error(
39
- `OpenPress document entry ${entry.entryPath} must default-export a Press component (function) to export. ` +
40
- `Legacy named exports (cover/toc/backCover) are not supported in v0.6 — see the Press Tree spec.`,
41
- );
42
- }
43
- // Resolve PressContext + Frame markers from the engine's loaded core module.
44
- // Use the absolute file path so the user's `import "@open-press/core"`
45
- // (resolved via vite alias) and our load hit the same module cache entry.
46
- const coreModule = await server.ssrLoadModule(CORE_ENTRY);
47
- const PressContext = coreModule.PressContext;
48
- if (!PressContext) {
49
- throw new Error("Engine could not resolve PressContext from @open-press/core.");
50
- }
51
-
52
- // Discover workspace for component scope and chapter-scoped style files.
53
- const workspace = await discoverSectionStyles(workspaceRoot, entry.config);
54
- const coreAuthorComponents = {};
55
- for (const name of ["MediaFigure", "ImageFigure"]) {
56
- if (typeof coreModule[name] === "function") coreAuthorComponents[name] = coreModule[name];
57
- }
58
- const globalComponents = {
59
- ...coreAuthorComponents,
60
- ...(await loadComponentModules(server, workspace.globalComponents ?? [])),
61
- };
62
-
63
- // Resolve sources.
64
- const documentRoot = entry.config.paths.documentRoot;
65
- const { resolved: sources, renderData: renderRegistry } = await resolveAllSources({
66
- sources: entry.sources,
67
- documentRoot,
68
- globalComponents,
69
- });
70
-
71
- // Build measurement CSS.
72
- const css = await buildReactMeasurementCss(workspaceRoot, entry.config, workspace);
73
-
74
- // Iterative allocation loop.
75
- let hints = null;
76
- let allocation = null;
77
- let lastFrames = null;
78
- let warnings = [];
79
- for (let iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
80
- const { html, frames } = expandPressTree({
81
- Press: entry.Press,
82
- PressContext,
83
- sources,
84
- hints,
85
- });
86
- lastFrames = frames;
87
- validateAllChainsKnown(frames, sources);
88
- const measurement = await measureFrames({
89
- pressHtml: html,
90
- sources,
91
- renderRegistry,
92
- css,
93
- baseHref: pathToFileURL(`${documentRoot}${path.sep}`).href,
94
- mediaDir: path.join(documentRoot, "media"),
95
- captionNumbering: entry.config.captionNumbering,
96
- });
97
- const alloc = allocateChains({
98
- frames,
99
- mdxAreas: measurement.mdxAreas,
100
- blockHeights: measurement.blockHeights,
101
- sources,
102
- });
103
- if (process.env.OPENPRESS_DEBUG_ALLOC) {
104
- const sample = measurement.mdxAreas
105
- .slice(0, 5)
106
- .map((a) => `${a.frameKey}#${a.indexInFrame} cap=${a.capacity.toFixed(0)} raw=${(a.rawHeight ?? 0).toFixed(0)}`);
107
- const blocks = measurement.blockHeights
108
- .slice(0, 8)
109
- .map((b) => `${b.id} h=${b.height.toFixed(0)}`);
110
- process.stderr.write(`[allocator iter ${iteration}]\n`);
111
- process.stderr.write(` mdxAreas[0..4]: ${sample.join(" | ")}\n`);
112
- process.stderr.write(` blocks[0..7]: ${blocks.join(" | ")}\n`);
113
- process.stderr.write(` hints: ${JSON.stringify(alloc.hints.totalPagesPerChain)}\n`);
114
- if (alloc.warnings.length > 0) {
115
- process.stderr.write(` warnings: ${JSON.stringify(alloc.warnings)}\n`);
116
- }
117
- }
118
- if (hintsEqual(hints, alloc.hints)) {
119
- allocation = alloc.allocation;
120
- warnings = alloc.warnings;
121
- break;
122
- }
123
- hints = alloc.hints;
124
- }
125
- if (allocation == null) {
126
- throw new Error(
127
- `Allocation did not converge after ${MAX_ITERATIONS} iterations. ` +
128
- `This usually means a chain keeps growing without fitting; check MdxArea capacities and block heights.`,
129
- );
130
- }
131
-
132
- const toc = buildTocContext({ sources, frames: lastFrames ?? [], allocation });
133
-
134
- // Final render.
135
- const final = await renderFinalPress({
136
- Press: entry.Press,
137
- PressContext,
138
- sources,
139
- hints,
140
- toc,
141
- allocation,
142
- renderRegistry,
143
- });
144
-
145
- // Write chapter-scoped CSS (under section-scoped rules but filename
146
- // unchanged for now).
147
- const chapterCss = await buildSectionScopedCss(workspace);
148
- const styles = [];
149
- await fs.mkdir(entry.config.paths.publicDir, { recursive: true });
150
- if (chapterCss.trim()) {
151
- await fs.writeFile(path.join(entry.config.paths.publicDir, "chapter-scoped.css"), chapterCss, "utf8");
152
- styles.push({
153
- kind: "chapter-scoped-css",
154
- href: "/openpress/chapter-scoped.css",
155
- path: "chapter-scoped.css",
156
- });
157
- }
158
-
159
- // Build document.json. The reader filters blocks by kind === "htmlPage",
160
- // so wrap each frame through `pageToBlock` to inherit that contract.
161
- const blockMap = {};
162
- const captionState = createCaptionNumberingState();
163
- const blocks = final.frames.map((frame, index) => {
164
- const source = {
165
- file: "index.tsx",
166
- path: "document/index.tsx",
167
- kind: frame.role ?? "manuscript.content",
168
- slug: frame.frameKey,
169
- sectionIndex: index + 1,
170
- };
171
- const html = numberCaptionsInHtml(frame.html, entry.config.captionNumbering, captionState);
172
- for (const id of collectFrameBlockIds(frame.blockIds, html)) {
173
- blockMap[id] = { id, pageIndex: index, pageNumber: index + 1, frameKey: frame.frameKey };
174
- }
175
- const block = pageToBlock(index, html, source, entry.config, {
176
- idPrefix: "openpress-page",
177
- anchorPrefix: "page",
178
- titleFallback: "Page",
179
- });
180
- return {
181
- ...block,
182
- frameKey: frame.frameKey,
183
- role: frame.role ?? null,
184
- chrome: frame.chrome ?? true,
185
- blockIds: frame.blockIds,
186
- };
187
- });
188
-
189
- // Enrich blockMap with source records from resolved chains so comment
190
- // tooling can resolve block IDs back to MDX positions.
191
- const sourceBlockIndex = buildSourceBlockIndex(sources);
192
- for (const id of Object.keys(blockMap)) {
193
- const sourceRecord = sourceBlockIndex.get(id);
194
- if (sourceRecord) {
195
- blockMap[id] = {
196
- ...blockMap[id],
197
- kind: sourceRecord.kind,
198
- name: sourceRecord.name,
199
- path: sourceRecord.path,
200
- source: sourceRecord.source,
201
- chainId: sourceRecord.chainId,
202
- sectionSlug: sourceRecord.sectionSlug,
203
- };
204
- }
205
- }
206
-
207
- const objectEntities = buildObjectEntities({
208
- frames: final.frames.map((frame, index) => ({ ...frame, pageIndex: index })),
209
- blocks,
210
- blockMap,
211
- });
212
-
213
- const readerDocument = {
214
- meta: {
215
- title: trimmedString(entry.config.title) ?? "Untitled Document",
216
- subtitle: trimmedString(entry.config.subtitle) ?? "",
217
- organization: trimmedString(entry.config.organization) ?? "",
218
- workspaceLabel: trimmedString(entry.config.workspaceLabel) ?? "",
219
- version: "openpress-press-tree-v1",
220
- },
221
- source: {
222
- type: "openpress-press-tree-mdx",
223
- contentDir: documentRelativePath(entry.config, entry.config.sourceDir),
224
- editable: true,
225
- editMode: "source-mdx",
226
- styles,
227
- blockMap,
228
- objectEntities,
229
- frames: final.frames.map((frame, index) => ({
230
- frameKey: frame.frameKey,
231
- role: frame.role ?? null,
232
- pageIndex: index,
233
- mdxAreas: frame.mdxAreas.map((area) => ({
234
- chainId: area.chainId,
235
- indexInFrame: area.indexInFrame,
236
- blockIds: area.blockIds,
237
- })),
238
- })),
239
- chains: Object.keys(sources).flatMap((id) => Object.keys(sources[id].chains)),
240
- warnings,
241
- },
242
- blocks,
243
- };
244
-
245
- const documentPath = path.join(entry.config.paths.publicDir, "document.json");
246
- await fs.writeFile(documentPath, JSON.stringify(readerDocument, null, 2), "utf8");
247
-
248
- if (syncAssets) {
249
- await syncPublicAssets(workspaceRoot, entry.config.paths.publicDir, entry.config);
250
- }
251
-
252
- return { documentPath, pageCount: blocks.length, document: readerDocument };
253
- } finally {
254
- await server.close();
255
- }
256
- }
257
-
258
- async function loadComponentModules(server, components) {
259
- const out = {};
260
- for (const component of components) {
261
- const mod = await server.ssrLoadModule(component.absolutePath);
262
- if (typeof mod.default !== "function") {
263
- throw new Error(
264
- `OpenPress component module ${component.documentPath} must default-export a React component.`,
265
- );
266
- }
267
- out[component.name] = mod.default;
268
- }
269
- return out;
270
- }
271
-
272
- function validateAllChainsKnown(frames, sources) {
273
- const known = new Set();
274
- for (const source of Object.values(sources)) {
275
- for (const chainId of Object.keys(source.chains)) known.add(chainId);
276
- }
277
- for (const frame of frames) {
278
- for (const area of frame.mdxAreas) {
279
- if (!known.has(area.chainId)) {
280
- const list = [...known].sort().slice(0, 10).join(", ");
281
- throw new Error(
282
- `Unknown chainId "${area.chainId}" referenced by frame "${frame.frameKey}". ` +
283
- `Known chains: ${list || "(none)"}${known.size > 10 ? ", ..." : ""}.`,
284
- );
285
- }
286
- }
287
- }
288
- }
289
-
290
- function hintsEqual(a, b) {
291
- if (a === b) return true;
292
- if (!a || !b) return false;
293
- const aMap = a.totalPagesPerChain ?? {};
294
- const bMap = b.totalPagesPerChain ?? {};
295
- const keys = new Set([...Object.keys(aMap), ...Object.keys(bMap)]);
296
- for (const key of keys) {
297
- if (aMap[key] !== bMap[key]) return false;
298
- }
299
- return true;
300
- }
301
-
302
- function buildSourceBlockIndex(sources) {
303
- const index = new Map();
304
- for (const source of Object.values(sources)) {
305
- for (const [chainId, blocks] of Object.entries(source.chains)) {
306
- for (const block of blocks) {
307
- index.set(block.id, { ...block, chainId });
308
- }
309
- }
310
- }
311
- return index;
312
- }
313
-
314
- function collectFrameBlockIds(allocatedIds, html) {
315
- const ids = new Set(allocatedIds ?? []);
316
- const pattern = /\sdata-openpress-block-id="([^"]+)"/g;
317
- let match;
318
- while ((match = pattern.exec(String(html ?? "")))) {
319
- if (match[1]) ids.add(match[1]);
320
- }
321
- return ids;
322
- }
323
-
324
- function buildTocContext({ sources, frames, allocation }) {
325
- const toc = {};
326
- for (const source of Object.values(sources)) {
327
- for (const [tocChainId, tocBlocks] of Object.entries(source.chains).filter(([chainId]) => chainId.startsWith(`toc:${source.id}`))) {
328
- if (tocBlocks.length === 0) continue;
329
- toc[tocChainId] = tocBlocks.map((block) => ({
330
- id: `${source.id}:${block.sectionSlug}`,
331
- blockId: block.id,
332
- sourceId: source.id,
333
- sectionSlug: block.sectionSlug,
334
- title: block.title,
335
- href: block.href,
336
- level: block.level,
337
- label: block.label,
338
- pageNumber: firstAllocatedPageNumberForBlock(frames, allocation, block.targetBlockId)
339
- ?? firstAllocatedPageNumber(frames, allocation, `${source.id}:${block.sectionSlug}`),
340
- }));
341
- }
342
- }
343
- return toc;
344
- }
345
-
346
- function firstAllocatedPageNumberForBlock(frames, allocation, blockId) {
347
- if (!blockId) return undefined;
348
- for (let index = 0; index < frames.length; index += 1) {
349
- const frameAllocation = allocation?.[frames[index].frameKey] ?? {};
350
- for (const areaArr of Object.values(frameAllocation)) {
351
- if (areaArr?.some((area) => Array.isArray(area) && area.includes(blockId))) return index + 1;
352
- }
353
- }
354
- return undefined;
355
- }
356
-
357
- function firstAllocatedPageNumber(frames, allocation, chainId) {
358
- for (let index = 0; index < frames.length; index += 1) {
359
- const frame = frames[index];
360
- const allocated = allocation?.[frame.frameKey]?.[chainId];
361
- if (allocated?.some((area) => Array.isArray(area) && area.length > 0)) return index + 1;
362
- }
363
- for (let index = 0; index < frames.length; index += 1) {
364
- if (frames[index].mdxAreas.some((area) => area.chainId === chainId)) return index + 1;
365
- }
366
- return undefined;
367
- }
368
-
369
- function trimmedString(value) {
370
- if (typeof value !== "string") return null;
371
- const trimmed = value.trim();
372
- return trimmed ? trimmed : null;
373
- }