@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.
- package/README.md +33 -23
- package/dist/cli.js +320 -252
- package/package.json +9 -8
- package/template/core/AGENTS.md +0 -126
- package/template/core/CHANGELOG.md +0 -215
- package/template/core/README.md +0 -40
- package/template/core/engine/cli.mjs +0 -96
- package/template/core/engine/commands/_shared.mjs +0 -177
- package/template/core/engine/commands/deploy.mjs +0 -31
- package/template/core/engine/commands/dev.mjs +0 -49
- package/template/core/engine/commands/doctor.mjs +0 -229
- package/template/core/engine/commands/export.mjs +0 -8
- package/template/core/engine/commands/init.mjs +0 -24
- package/template/core/engine/commands/inspect.mjs +0 -35
- package/template/core/engine/commands/pdf.mjs +0 -26
- package/template/core/engine/commands/preview.mjs +0 -26
- package/template/core/engine/commands/render.mjs +0 -17
- package/template/core/engine/commands/replace.mjs +0 -41
- package/template/core/engine/commands/search.mjs +0 -33
- package/template/core/engine/commands/typecheck.mjs +0 -5
- package/template/core/engine/commands/upgrade.mjs +0 -159
- package/template/core/engine/commands/validate.mjs +0 -17
- package/template/core/engine/document-export.mjs +0 -15
- package/template/core/engine/init.mjs +0 -90
- package/template/core/engine/output/chrome-pdf.d.mts +0 -34
- package/template/core/engine/output/chrome-pdf.mjs +0 -358
- package/template/core/engine/output/deploy-sync.mjs +0 -15
- package/template/core/engine/output/fonts.mjs +0 -62
- package/template/core/engine/output/katex-assets.mjs +0 -45
- package/template/core/engine/output/page-block.mjs +0 -30
- package/template/core/engine/output/pdf-media.mjs +0 -45
- package/template/core/engine/output/public-assets.mjs +0 -19
- package/template/core/engine/output/static-server.mjs +0 -532
- package/template/core/engine/react/caption-numbering.mjs +0 -73
- package/template/core/engine/react/comment-endpoint.d.mts +0 -11
- package/template/core/engine/react/comment-endpoint.mjs +0 -102
- package/template/core/engine/react/comment-marker.mjs +0 -374
- package/template/core/engine/react/document-entry.mjs +0 -324
- package/template/core/engine/react/document-export.mjs +0 -373
- package/template/core/engine/react/http-json.mjs +0 -24
- package/template/core/engine/react/mdx-compile.mjs +0 -599
- package/template/core/engine/react/measurement-css.mjs +0 -136
- package/template/core/engine/react/object-entities.mjs +0 -119
- package/template/core/engine/react/pagination/allocator.mjs +0 -122
- package/template/core/engine/react/pagination/regions.mjs +0 -81
- package/template/core/engine/react/pagination-constants.mjs +0 -3
- package/template/core/engine/react/pagination.mjs +0 -9
- package/template/core/engine/react/pipeline/allocate.mjs +0 -251
- package/template/core/engine/react/pipeline/final-render.mjs +0 -94
- package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -302
- package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
- package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
- package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
- package/template/core/engine/react/section-css.mjs +0 -56
- package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
- package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
- package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
- package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
- package/template/core/engine/react/style-discovery.mjs +0 -142
- package/template/core/engine/runtime/config.d.mts +0 -40
- package/template/core/engine/runtime/config.mjs +0 -175
- package/template/core/engine/runtime/file-utils.mjs +0 -106
- package/template/core/engine/runtime/file-walk.mjs +0 -22
- package/template/core/engine/runtime/inspection.mjs +0 -328
- package/template/core/engine/runtime/issue-report.mjs +0 -44
- package/template/core/engine/runtime/path-utils.mjs +0 -20
- package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
- package/template/core/engine/runtime/source-text-tools.mjs +0 -832
- package/template/core/engine/runtime/source-workspace.mjs +0 -159
- package/template/core/engine/runtime/validation.mjs +0 -174
- package/template/core/index.html +0 -13
- package/template/core/openpress.config.mjs +0 -12
- package/template/core/package.json +0 -91
- package/template/core/src/main.tsx +0 -16
- package/template/core/src/openpress/app/OpenPressApp.tsx +0 -140
- package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -94
- package/template/core/src/openpress/app/index.ts +0 -2
- package/template/core/src/openpress/core/Frame.tsx +0 -78
- package/template/core/src/openpress/core/FrameContext.tsx +0 -24
- package/template/core/src/openpress/core/MdxArea.tsx +0 -34
- package/template/core/src/openpress/core/Press.tsx +0 -34
- package/template/core/src/openpress/core/cn.ts +0 -4
- package/template/core/src/openpress/core/index.tsx +0 -40
- package/template/core/src/openpress/core/primitives.tsx +0 -44
- package/template/core/src/openpress/core/types.ts +0 -191
- package/template/core/src/openpress/core/useSource.ts +0 -28
- package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
- package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
- package/template/core/src/openpress/document-model/documentTypes.ts +0 -138
- package/template/core/src/openpress/document-model/index.ts +0 -6
- package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -51
- package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
- package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
- package/template/core/src/openpress/manuscript/index.tsx +0 -238
- package/template/core/src/openpress/mdx/index.ts +0 -88
- package/template/core/src/openpress/numbering/index.ts +0 -294
- package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
- package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
- package/template/core/src/openpress/reader/index.ts +0 -10
- package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
- package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
- package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
- package/template/core/src/openpress/reader/readerScroll.ts +0 -92
- package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
- package/template/core/src/openpress/reader/readerTypes.ts +0 -4
- package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
- package/template/core/src/openpress/reader/usePanelState.ts +0 -56
- package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
- package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
- package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
- package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
- package/template/core/src/openpress/shared/Panel.tsx +0 -77
- package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
- package/template/core/src/openpress/shared/index.ts +0 -4
- package/template/core/src/openpress/shared/numberUtils.ts +0 -3
- package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
- package/template/core/src/openpress/workbench/Workbench.tsx +0 -407
- package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
- package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
- package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
- package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
- package/template/core/src/openpress/workbench/actions/index.ts +0 -5
- package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
- package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
- package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
- package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
- package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
- package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
- package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
- package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
- package/template/core/src/openpress/workbench/document/index.ts +0 -10
- package/template/core/src/openpress/workbench/index.ts +0 -2
- package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
- package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
- package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
- package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
- package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
- package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -248
- package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
- package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
- package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
- package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
- package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -76
- package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
- package/template/core/src/openpress/workbench/panels/index.ts +0 -3
- package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -523
- package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
- package/template/core/src/openpress/workbench/project/index.ts +0 -2
- package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
- package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
- package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
- package/template/core/src/openpress/workbench/shell/index.ts +0 -1
- package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
- package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
- package/template/core/src/styles/openpress/app-shell.css +0 -251
- package/template/core/src/styles/openpress/media-workspace.css +0 -230
- package/template/core/src/styles/openpress/print-route.css +0 -184
- package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
- package/template/core/src/styles/openpress/public-viewer.css +0 -688
- package/template/core/src/styles/openpress/reader-runtime.css +0 -980
- package/template/core/src/styles/openpress/responsive.css +0 -245
- package/template/core/src/styles/openpress/workbench-panels.css +0 -594
- package/template/core/src/styles/openpress/workbench.css +0 -1255
- package/template/core/src/styles/openpress.css +0 -14
- package/template/core/src/vite-env.d.ts +0 -9
- package/template/core/tsconfig.json +0 -40
- package/template/core/vite.config.ts +0 -584
- package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
- package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
- package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
- package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
- package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
- package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
- package/template/packs/academic-paper/document/components/Page.tsx +0 -60
- package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
- package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
- package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
- package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
- package/template/packs/academic-paper/document/design.md +0 -279
- package/template/packs/academic-paper/document/index.tsx +0 -123
- package/template/packs/academic-paper/document/media/README.md +0 -13
- package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
- package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
- package/template/packs/academic-paper/document/theme/README.md +0 -11
- package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
- package/template/packs/academic-paper/document/theme/base/print.css +0 -93
- package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
- package/template/packs/academic-paper/document/theme/fonts.css +0 -3
- package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
- package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
- package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
- package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
- package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
- package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
- package/template/packs/academic-paper/document/theme/tokens.css +0 -80
- package/template/packs/academic-paper/openpress.config.mjs +0 -5
- package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
- package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
- package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
- package/template/packs/claude-document/document/components/Page.tsx +0 -37
- package/template/packs/claude-document/document/design.md +0 -142
- package/template/packs/claude-document/document/index.tsx +0 -94
- package/template/packs/claude-document/document/media/README.md +0 -13
- package/template/packs/claude-document/document/openpress.config.mjs +0 -26
- package/template/packs/claude-document/document/theme/README.md +0 -15
- package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
- package/template/packs/claude-document/document/theme/base/print.css +0 -93
- package/template/packs/claude-document/document/theme/base/typography.css +0 -612
- package/template/packs/claude-document/document/theme/fonts.css +0 -4
- package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
- package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
- package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
- package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
- package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
- package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
- package/template/packs/claude-document/document/theme/tokens.css +0 -89
- package/template/packs/claude-document/openpress.config.mjs +0 -5
- package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
- package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
- package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
- package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
- package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
- package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
- package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
- package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
- package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
- package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
- package/template/packs/editorial-monograph/document/design.md +0 -279
- package/template/packs/editorial-monograph/document/index.tsx +0 -97
- package/template/packs/editorial-monograph/document/media/README.md +0 -13
- package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
- package/template/packs/editorial-monograph/document/theme/README.md +0 -11
- package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
- package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
- package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
- package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
- package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
- package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
- package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
- package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
- package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
- package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
- package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
- package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
- package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
- 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
|
-
}
|