@publish.os/nextjs 0.0.1-alpha.90 → 0.0.1-alpha.92
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/compiler-assets/nextjs/defaults/group/index.tsx +16 -11
- package/compiler-assets/nextjs/defaults/group/layout.tsx +6 -6
- package/compiler-assets/nextjs/defaults/group/page.tsx +36 -26
- package/compiler-assets/nextjs/defaults/project/next.config.mjs +21 -11
- package/compiler-assets/nextjs/defaults/project/tsconfig.json.template +10 -0
- package/compiler-assets/nextjs/defaults/root/css/components/code-block.css +18 -0
- package/compiler-assets/nextjs/defaults/root/index.tsx +16 -11
- package/compiler-assets/nextjs/defaults/root/layout.tsx +7 -3
- package/compiler-assets/nextjs/shims/agent-user-agents.ts +65 -0
- package/compiler-assets/nextjs/shims/document.tsx +46 -0
- package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +5 -0
- package/compiler-assets/nextjs/shims/global-layout.tsx +8 -42
- package/compiler-assets/nextjs/shims/global-not-found.tsx +69 -0
- package/compiler-assets/nextjs/shims/markdown-route.ts +2 -0
- package/compiler-assets/nextjs/shims/metadata-icons.tsx +39 -0
- package/compiler-assets/nextjs/shims/not-found-content.tsx +53 -0
- package/compiler-assets/nextjs/shims/not-found-navigation.tsx +60 -0
- package/compiler-assets/nextjs/shims/proxy.ts +254 -86
- package/compiler-assets/nextjs/shims/site-fenced-code-block.tsx +2 -6
- package/compiler-assets/nextjs/shims/site-mdx-components.tsx +28 -16
- package/compiler-assets/nextjs/shims/site-mdx.tsx +6 -1
- package/dist/{build-operation-2JAYBSKF.js → build-operation-CJDUL4OW.js} +29 -23
- package/dist/{button-c3WGnxIj.d.ts → button-D6Xp2HBm.d.ts} +4 -4
- package/dist/capture-local-source-WGGDIUU2.js +258 -0
- package/dist/chunk-22RSSUNV.js +13 -0
- package/dist/chunk-3T4HVDSC.js +426 -0
- package/dist/{chunk-WKTEO6RS.js → chunk-4BC2L2KA.js} +46 -12
- package/dist/{chunk-WDY4IK6Z.js → chunk-5J3YUNRY.js} +3 -10
- package/dist/chunk-5KAD7W53.js +248 -0
- package/dist/{chunk-DWA3ZRGW.js → chunk-6C65KV7O.js} +2 -2
- package/dist/{chunk-GGKFQ3MA.js → chunk-6D2E6UMD.js} +144 -70
- package/dist/{chunk-7OGTPSKI.js → chunk-7NY22B5U.js} +8 -3
- package/dist/chunk-DX7PXFCM.js +2519 -0
- package/dist/{chunk-726GV2NF.js → chunk-DZ3VWEV5.js} +950 -632
- package/dist/chunk-EFO6AWXN.js +19 -0
- package/dist/chunk-IL4RJOKI.js +186 -0
- package/dist/{chunk-H6XILMZX.js → chunk-NHSLTARU.js} +2588 -1493
- package/dist/{chunk-DLDFHU7Y.js → chunk-NNBJZPFM.js} +4 -2
- package/dist/{chunk-VO2I7MTM.js → chunk-SBMDONTV.js} +10 -7
- package/dist/{chunk-YLTOLYWM.js → chunk-WL75GQ5M.js} +6 -18
- package/dist/compiler.d.ts +15 -2
- package/dist/compiler.js +111 -23
- package/dist/components/blocks/agent-actions.d.ts +81 -0
- package/dist/components/blocks/agent-actions.js +563 -0
- package/dist/components/blocks/toc.d.ts +43 -0
- package/dist/components/blocks/toc.js +136 -0
- package/dist/components/ui/button.d.ts +4 -0
- package/dist/components/ui/button.js +94 -0
- package/dist/compose-44GJSMXD.js +16 -0
- package/dist/config/createNextConfig.js +19 -3
- package/dist/{dev-operation-TZDVJCN7.js → dev-operation-WNECBHZ5.js} +134 -84
- package/dist/{framework-selection-X4U6Y6QC.js → framework-selection-ZVO323PK.js} +3 -2
- package/dist/ide-support-TG74L4FF.js +16 -0
- package/dist/index.d.ts +151 -95
- package/dist/index.js +25 -0
- package/dist/jsx-dev-runtime.d.ts +6 -0
- package/dist/jsx-dev-runtime.js +100 -0
- package/dist/jsx-runtime.d.ts +7 -0
- package/dist/jsx-runtime.js +97 -0
- package/dist/kernel/components/blocks/AgentActions.d.ts +37 -8
- package/dist/kernel/components/blocks/AgentActions.js +384 -174
- package/dist/kernel/components/blocks/agent-actions.d.ts +4 -0
- package/dist/kernel/components/blocks/agent-actions.js +563 -0
- package/dist/kernel/components/blocks/animated-icon-swap.d.ts +2 -2
- package/dist/kernel/components/blocks/animated-icon-swap.js +1 -1
- package/dist/kernel/components/blocks/color-palette.d.ts +2 -2
- package/dist/kernel/components/blocks/color-palette.js +4 -4
- package/dist/kernel/components/blocks/logo-block.d.ts +2 -2
- package/dist/kernel/components/blocks/logo-block.js +1 -1
- package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +6 -5
- package/dist/kernel/components/blocks/nav-breadcrumb.js +21 -15
- package/dist/kernel/components/blocks/navbar.d.ts +5 -4
- package/dist/kernel/components/blocks/navbar.js +79 -59
- package/dist/kernel/components/blocks/navigation-progress.d.ts +2 -2
- package/dist/kernel/components/blocks/navigation-progress.js +2 -2
- package/dist/kernel/components/blocks/next-prev.d.ts +4 -4
- package/dist/kernel/components/blocks/next-prev.js +30 -7
- package/dist/kernel/components/blocks/page-list.d.ts +4 -4
- package/dist/kernel/components/blocks/page-list.js +32 -9
- package/dist/kernel/components/blocks/search.d.ts +9 -15
- package/dist/kernel/components/blocks/search.js +16 -17
- package/dist/kernel/components/blocks/sidebar-node.d.ts +3 -5
- package/dist/kernel/components/blocks/sidebar-node.js +13 -3
- package/dist/kernel/components/blocks/sidebar.d.ts +6 -7
- package/dist/kernel/components/blocks/sidebar.js +137 -28
- package/dist/kernel/components/blocks/theme-toggle.d.ts +3 -4
- package/dist/kernel/components/blocks/theme-toggle.js +5 -5
- package/dist/kernel/components/blocks/toc-client.d.ts +40 -0
- package/dist/kernel/components/blocks/toc-client.js +495 -0
- package/dist/kernel/components/blocks/toc.d.ts +20 -2
- package/dist/kernel/components/blocks/toc.js +121 -179
- package/dist/kernel/components/mdx/components/blockquote.d.ts +2 -2
- package/dist/kernel/components/mdx/components/blockquote.js +1 -1
- package/dist/kernel/components/mdx/components/callout.d.ts +3 -3
- package/dist/kernel/components/mdx/components/callout.js +1 -1
- package/dist/kernel/components/mdx/components/code-block.d.ts +2 -2
- package/dist/kernel/components/mdx/components/code-block.js +2 -2
- package/dist/kernel/components/mdx/components/copy-code-button.d.ts +2 -2
- package/dist/kernel/components/mdx/components/copy-code-button.js +5 -4
- package/dist/kernel/components/mdx/components/figure-caption.d.ts +3 -3
- package/dist/kernel/components/mdx/components/figure-caption.js +3 -2
- package/dist/kernel/components/mdx/components/figure.d.ts +8 -4
- package/dist/kernel/components/mdx/components/figure.js +23 -13
- package/dist/kernel/components/mdx/components/footnotes.d.ts +2 -2
- package/dist/kernel/components/mdx/components/footnotes.js +1 -1
- package/dist/kernel/components/mdx/components/heading-copy-link.d.ts +2 -2
- package/dist/kernel/components/mdx/components/heading-copy-link.js +1 -1
- package/dist/kernel/components/mdx/components/heading.d.ts +2 -2
- package/dist/kernel/components/mdx/components/heading.js +6 -3
- package/dist/kernel/components/mdx/components/hr.d.ts +4 -4
- package/dist/kernel/components/mdx/components/hr.js +2 -2
- package/dist/kernel/components/mdx/components/image-zoom.d.ts +3 -3
- package/dist/kernel/components/mdx/components/image-zoom.js +4 -3
- package/dist/kernel/components/mdx/components/image.d.ts +2 -2
- package/dist/kernel/components/mdx/components/image.js +22 -17
- package/dist/kernel/components/mdx/components/inline-code.d.ts +2 -2
- package/dist/kernel/components/mdx/components/inline-code.js +1 -1
- package/dist/kernel/components/mdx/components/kbd.d.ts +3 -3
- package/dist/kernel/components/mdx/components/kbd.js +2 -2
- package/dist/kernel/components/mdx/components/link.d.ts +6 -6
- package/dist/kernel/components/mdx/components/link.js +2 -2
- package/dist/kernel/components/mdx/components/paragraph.d.ts +2 -2
- package/dist/kernel/components/mdx/components/paragraph.js +1 -1
- package/dist/kernel/components/mdx/components/strikethrough.d.ts +2 -2
- package/dist/kernel/components/mdx/components/strikethrough.js +1 -1
- package/dist/kernel/components/mdx/components/table.d.ts +7 -7
- package/dist/kernel/components/mdx/components/table.js +2 -2
- package/dist/kernel/components/mdx/components/task-list.d.ts +3 -3
- package/dist/kernel/components/mdx/components/task-list.js +1 -1
- package/dist/kernel/components/mdx/components/video-player.d.ts +8 -4
- package/dist/kernel/components/mdx/components/video-player.js +6 -2
- package/dist/kernel/components/mdx/components/video.d.ts +5 -4
- package/dist/kernel/components/mdx/components/video.js +7 -5
- package/dist/kernel/components/mdx/error-boundary.d.ts +3 -4
- package/dist/kernel/components/mdx/error-boundary.js +2 -2
- package/dist/kernel/components/mdx/error-fallback.d.ts +2 -2
- package/dist/kernel/components/mdx/error-fallback.js +1 -1
- package/dist/kernel/components/mdx/mdx.d.ts +57 -16
- package/dist/kernel/components/mdx/mdx.js +232 -35
- package/dist/kernel/components/mdx/rehype-heading-ids.d.ts +1 -0
- package/dist/kernel/components/mdx/rehype-heading-ids.js +5 -3
- package/dist/kernel/components/mdx/types.d.ts +1 -1
- package/dist/kernel/components/next-image.d.ts +12 -0
- package/dist/kernel/components/next-image.js +61 -0
- package/dist/kernel/components/ui/accordion.d.ts +7 -7
- package/dist/kernel/components/ui/accordion.js +1 -1
- package/dist/kernel/components/ui/breadcrumb-trail.d.ts +3 -4
- package/dist/kernel/components/ui/breadcrumb-trail.js +2 -2
- package/dist/kernel/components/ui/breadcrumb.d.ts +14 -14
- package/dist/kernel/components/ui/breadcrumb.js +4 -4
- package/dist/kernel/components/ui/button-group.d.ts +6 -6
- package/dist/kernel/components/ui/button-group.js +3 -3
- package/dist/kernel/components/ui/button.d.ts +59 -7
- package/dist/kernel/components/ui/button.js +43 -39
- package/dist/kernel/components/ui/card.d.ts +7 -7
- package/dist/kernel/components/ui/card.js +1 -1
- package/dist/kernel/components/ui/dialog.d.ts +10 -10
- package/dist/kernel/components/ui/dialog.js +1 -1
- package/dist/kernel/components/ui/drawer.d.ts +12 -12
- package/dist/kernel/components/ui/drawer.js +1 -1
- package/dist/kernel/components/ui/input.d.ts +2 -2
- package/dist/kernel/components/ui/input.js +1 -1
- package/dist/kernel/components/ui/kbd.d.ts +4 -4
- package/dist/kernel/components/ui/kbd.js +1 -1
- package/dist/kernel/components/ui/link.d.ts +11 -5
- package/dist/kernel/components/ui/link.js +2 -2
- package/dist/kernel/components/ui/menu.d.ts +22 -22
- package/dist/kernel/components/ui/menu.js +1 -1
- package/dist/kernel/components/ui/mod-key.d.ts +2 -3
- package/dist/kernel/components/ui/mod-key.js +2 -2
- package/dist/kernel/components/ui/navigation-menu.d.ts +15 -15
- package/dist/kernel/components/ui/navigation-menu.js +1 -1
- package/dist/kernel/components/ui/popover.d.ts +11 -11
- package/dist/kernel/components/ui/popover.js +1 -1
- package/dist/kernel/components/ui/progress.d.ts +7 -7
- package/dist/kernel/components/ui/progress.js +1 -1
- package/dist/kernel/components/ui/scroll-area.d.ts +9 -10
- package/dist/kernel/components/ui/scroll-area.js +1 -1
- package/dist/kernel/components/ui/select.d.ts +16 -16
- package/dist/kernel/components/ui/select.js +1 -1
- package/dist/kernel/components/ui/separator.d.ts +3 -3
- package/dist/kernel/components/ui/separator.js +1 -1
- package/dist/kernel/components/ui/table.d.ts +7 -7
- package/dist/kernel/components/ui/table.js +1 -1
- package/dist/kernel/components/ui/tabs.d.ts +12 -12
- package/dist/kernel/components/ui/tabs.js +1 -1
- package/dist/kernel/components/ui/tag.d.ts +3 -3
- package/dist/kernel/components/ui/tag.js +1 -1
- package/dist/kernel/components/ui/text.d.ts +4 -4
- package/dist/kernel/components/ui/text.js +1 -1
- package/dist/kernel/components/ui/toast.d.ts +11 -11
- package/dist/kernel/components/ui/toast.js +1 -1
- package/dist/kernel/components/ui/tooltip.d.ts +9 -9
- package/dist/kernel/components/ui/tooltip.js +1 -1
- package/dist/kernel/defaults.registry.d.ts +4 -17
- package/dist/kernel/defaults.registry.js +4 -19
- package/dist/kernel/routes/agent/markdown.d.ts +7 -0
- package/dist/kernel/routes/agent/markdown.js +229 -54
- package/dist/kernel/routes/layouts/group.d.ts +1 -1
- package/dist/kernel/routes/layouts/group.js +5 -7
- package/dist/kernel/routes/metadata/feed.d.ts +1 -2
- package/dist/kernel/routes/metadata/feed.js +119 -88
- package/dist/kernel/routes/metadata/llms.d.ts +3 -23
- package/dist/kernel/routes/metadata/llms.js +2 -103
- package/dist/kernel/routes/metadata/og-image.js +1 -1
- package/dist/kernel/routes/metadata/robots.d.ts +5 -3
- package/dist/kernel/routes/metadata/robots.js +4 -25
- package/dist/kernel/routes/metadata/sitemap.d.ts +36 -3
- package/dist/kernel/routes/metadata/sitemap.js +277 -49
- package/dist/kernel/routes/not-found.d.ts +34 -0
- package/dist/kernel/routes/not-found.js +213 -0
- package/dist/kernel/routes/pages/group.d.ts +5 -5
- package/dist/kernel/routes/pages/group.js +11 -5
- package/dist/kernel/routes/pages/leaf.d.ts +38 -6
- package/dist/kernel/routes/pages/leaf.js +21 -12
- package/dist/kernel/routes/pages/root.d.ts +5 -5
- package/dist/kernel/routes/pages/root.js +21 -6
- package/dist/keyboard-shortcuts.d.ts +2 -2
- package/dist/keyboard-shortcuts.js +1 -1
- package/dist/manifest-types-CcVPH_jh.d.ts +83 -0
- package/dist/resolvers/manifest-resolver.d.ts +2 -2
- package/dist/resolvers/manifest-resolver.js +65 -35
- package/dist/route-metadata-og-image-BeKKFHg7.d.ts +58 -0
- package/dist/search.d.ts +2 -9
- package/dist/search.js +9 -16
- package/dist/sidebar-types-CMrMM0Nj.d.ts +105 -0
- package/dist/theme-scope.d.ts +2 -2
- package/dist/theme-scope.js +1 -1
- package/dist/types-BbqmOzLC.d.ts +168 -0
- package/dist/types-TjH1ek0X.d.ts +22 -0
- package/dist/types-sM2g9nW0.d.ts +12 -0
- package/dist/types.d.ts +117 -91
- package/dist/utils/heading-ids.js +30 -11
- package/dist/utils/toc-selection.d.ts +7 -0
- package/dist/utils/toc-selection.js +14 -0
- package/dist/validate-local-source-EN2GW4FR.js +16 -0
- package/dist/validate-publish-os-source-OEWQDB7B.js +14 -0
- package/init/seed/minimal/publish.os/assets/blog/hello-world.svg +13 -0
- package/init/seed/minimal/publish.os/assets/favicon.dark.svg +13 -0
- package/init/seed/minimal/publish.os/assets/favicon.svg +13 -0
- package/init/seed/minimal/publish.os/blog/content/hello-world.mdx +48 -0
- package/init/seed/minimal/publish.os/blog/content/series-a.mdx +11 -0
- package/init/seed/minimal/publish.os/blog/index.tsx +85 -0
- package/init/seed/minimal/publish.os/blog/layout.tsx +13 -0
- package/init/seed/minimal/publish.os/blog/page.tsx +91 -0
- package/init/seed/minimal/publish.os/components/blog/copy-link.tsx +64 -0
- package/init/seed/minimal/publish.os/components/blog/frontmatter.ts +41 -0
- package/init/seed/minimal/publish.os/docs/content/get-started.mdx +2 -3
- package/init/seed/minimal/publish.os/docs/content/groups.mdx +5 -6
- package/init/seed/minimal/publish.os/publish.os.config.json +17 -2
- package/init/seed/minimal/publish.os/theme.css +3 -3
- package/init/seed/minimal/seed.json +12 -4
- package/init/seed/scratch/publish.os/docs/content/get-started.mdx +1 -1
- package/init/seed/scratch/publish.os/publish.os.config.json +1 -1
- package/package.json +48 -23
- package/release/app/phosphor/package.json +4 -4
- package/release/app/phosphor/pnpm-lock.yaml.template +123 -184
- package/release/compiler/package.json +5 -3
- package/release/compiler/pnpm-lock.yaml.template +153 -183
- package/release/release.json +6 -6
- package/dist/button-CtzUJB8u.d.ts +0 -11
- package/dist/chunk-535TM4A5.js +0 -1455
- package/dist/chunk-B2V22WRT.js +0 -297
- package/dist/chunk-NECL5IBJ.js +0 -318
- package/dist/chunk-SVYGXXYR.js +0 -36
- package/dist/chunk-UF6ATARM.js +0 -1100
- package/dist/compose-NN23UJGC.js +0 -13
- package/dist/ide-support-ZNW7ODFY.js +0 -15
- package/dist/kernel/components/blocks/design-system-preview.d.ts +0 -61
- package/dist/kernel/components/blocks/design-system-preview.js +0 -667
- package/dist/kernel/components/blocks/link-playground.d.ts +0 -5
- package/dist/kernel/components/blocks/link-playground.js +0 -744
- package/dist/kernel/components/blocks/navigation-progress-playground.d.ts +0 -5
- package/dist/kernel/components/blocks/navigation-progress-playground.js +0 -629
- package/dist/kernel/components/ui/theme-toggle.d.ts +0 -24
- package/dist/kernel/components/ui/theme-toggle.js +0 -53
- package/dist/sidebar-types-DKpR27II.d.ts +0 -25
- package/dist/types-BzkZty3r.d.ts +0 -147
- package/dist/types-CJWRG05y.d.ts +0 -76
- package/dist/types-CUtrppxt.d.ts +0 -69
- package/dist/validate-local-source-XRL2Y7XE.js +0 -14
- package/dist/validate-publish-os-source-DAPZ4QGE.js +0 -12
- package/init/seed/minimal/publish.os/assets/favicon/favicon.dark.png +0 -0
- package/init/seed/minimal/publish.os/assets/favicon/favicon.png +0 -0
|
@@ -1,45 +1,64 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
AppError as AppError2,
|
|
3
|
+
ERROR_CODES,
|
|
4
|
+
assertPublishOsSourceBoundary
|
|
5
|
+
} from "./chunk-7NY22B5U.js";
|
|
4
6
|
import {
|
|
7
|
+
FrontmatterParseError,
|
|
8
|
+
MdxIconImportError,
|
|
5
9
|
ROOT_CONTENT_GROUP_KEY,
|
|
6
10
|
ROOT_CONTENT_GROUP_TITLE,
|
|
7
11
|
assertSourceDependency,
|
|
12
|
+
authoredDate,
|
|
8
13
|
buildFaviconsAndLogos,
|
|
9
14
|
buildMediaAssets,
|
|
10
15
|
collectBindingNames,
|
|
16
|
+
convertMdxToMarkdown,
|
|
11
17
|
groupedMdxExports,
|
|
12
18
|
joinUrlPath,
|
|
13
|
-
localCssImports,
|
|
14
19
|
mdxComponentExportName,
|
|
15
20
|
namedValueExports,
|
|
16
21
|
normalizeSlugPath,
|
|
17
22
|
normalizeUrlPath,
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
originalDefaultComponentSourcePath,
|
|
24
|
+
parseSourceFrontmatter,
|
|
25
|
+
replacedMdxSourceFiles,
|
|
26
|
+
supportedNextjsSourceImports,
|
|
27
|
+
validateMdxIconImports
|
|
28
|
+
} from "./chunk-DX7PXFCM.js";
|
|
20
29
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
isFrameworkNamespacePath
|
|
31
|
+
} from "./chunk-EFO6AWXN.js";
|
|
32
|
+
import {
|
|
33
|
+
localCssImports
|
|
34
|
+
} from "./chunk-5KAD7W53.js";
|
|
25
35
|
import {
|
|
26
36
|
isApprovedPublishOsPath,
|
|
27
37
|
isExcludedSourcePath,
|
|
28
|
-
isFrameworkNamespacePath,
|
|
29
38
|
isReservedFrameworkPath,
|
|
30
39
|
isSameOrDescendant
|
|
31
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-WL75GQ5M.js";
|
|
32
41
|
import {
|
|
33
42
|
runtimePackageVersion
|
|
34
43
|
} from "./chunk-AI2UULNX.js";
|
|
35
44
|
import {
|
|
36
|
-
AppError,
|
|
37
45
|
readInstalledFrameworkLocation
|
|
38
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-5J3YUNRY.js";
|
|
47
|
+
import {
|
|
48
|
+
AppError
|
|
49
|
+
} from "./chunk-22RSSUNV.js";
|
|
39
50
|
|
|
40
51
|
// src/lib/compiler/validation/validate-publish-os-source.ts
|
|
41
52
|
import { readdir as readdir3, readFile as readFile5, stat as stat3 } from "fs/promises";
|
|
42
|
-
import {
|
|
53
|
+
import {
|
|
54
|
+
basename as basename2,
|
|
55
|
+
dirname,
|
|
56
|
+
extname as extname4,
|
|
57
|
+
parse,
|
|
58
|
+
relative as relative4,
|
|
59
|
+
resolve as resolve7,
|
|
60
|
+
sep
|
|
61
|
+
} from "path";
|
|
43
62
|
import ts2 from "typescript";
|
|
44
63
|
|
|
45
64
|
// src/lib/compiler/source/diagnostics/diagnostic-store.ts
|
|
@@ -48,7 +67,7 @@ var diagnosticSchema = z.object({
|
|
|
48
67
|
code: z.string(),
|
|
49
68
|
level: z.union([z.literal("error"), z.literal("warning")]),
|
|
50
69
|
message: z.string(),
|
|
51
|
-
details: z.record(z.unknown()).optional()
|
|
70
|
+
details: z.record(z.string(), z.unknown()).optional()
|
|
52
71
|
}), DiagnosticStore = class {
|
|
53
72
|
diagnostics = [];
|
|
54
73
|
add(diagnostic) {
|
|
@@ -86,11 +105,22 @@ import { resolve } from "path";
|
|
|
86
105
|
|
|
87
106
|
// src/lib/compiler/source/config/format-config-issues.ts
|
|
88
107
|
function issueMessages(issue) {
|
|
89
|
-
return issue.code === "invalid_union" ? issue.
|
|
90
|
-
(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
return issue.code === "invalid_union" ? issue.errors.flatMap(
|
|
109
|
+
(issues) => issues.flatMap(
|
|
110
|
+
(nestedIssue) => issueMessages({
|
|
111
|
+
...nestedIssue,
|
|
112
|
+
path: [...issue.path, ...nestedIssue.path]
|
|
113
|
+
})
|
|
114
|
+
)
|
|
115
|
+
) : issue.code === "unrecognized_keys" ? issue.keys.map((key) => {
|
|
116
|
+
let path = [...issue.path, key].join(".");
|
|
117
|
+
return issue.path.join(".") === "media.assets" && [
|
|
118
|
+
"maxImageBytes",
|
|
119
|
+
"maxVideoBytes",
|
|
120
|
+
"maxTotalBytes",
|
|
121
|
+
"maxFileCount"
|
|
122
|
+
].includes(key) ? `${path}: Media limits are fixed by PublishOS. Remove this field.` : `${path}: Unknown field.`;
|
|
123
|
+
}) : [`${issue.path.join(".") || "(root)"}: ${issue.message}`];
|
|
94
124
|
}
|
|
95
125
|
function formatConfigIssues(error) {
|
|
96
126
|
return [...new Set(error.issues.flatMap(issueMessages))].join(`
|
|
@@ -100,68 +130,115 @@ function formatConfigIssues(error) {
|
|
|
100
130
|
// src/lib/compiler/source/config/publish-os-config-schema.ts
|
|
101
131
|
import { z as z4 } from "zod";
|
|
102
132
|
|
|
103
|
-
// src/lib/compiler/source/config/
|
|
133
|
+
// src/lib/compiler/source/config/metadata-schema.ts
|
|
104
134
|
import { z as z2 } from "zod";
|
|
105
|
-
var
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
135
|
+
var imagePathSchema = z2.string().min(1).refine((value) => {
|
|
136
|
+
if (value.startsWith("/assets/") && value.length > 8)
|
|
137
|
+
return !0;
|
|
138
|
+
try {
|
|
139
|
+
return new URL(value).protocol === "https:";
|
|
140
|
+
} catch {
|
|
141
|
+
return !1;
|
|
142
|
+
}
|
|
143
|
+
}, "Use a nonempty /assets/ path or an absolute HTTPS URL."), imageSchema = z2.union([
|
|
144
|
+
imagePathSchema,
|
|
145
|
+
z2.object({ src: imagePathSchema, alt: z2.string().optional() }).strict()
|
|
146
|
+
]), socialFields = {
|
|
109
147
|
title: z2.string().optional(),
|
|
110
148
|
description: z2.string().optional(),
|
|
111
|
-
image:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
149
|
+
image: imageSchema.optional()
|
|
150
|
+
}, pageMetadataSchema = z2.object({
|
|
151
|
+
openGraph: z2.object(socialFields).strict().optional(),
|
|
152
|
+
robots: z2.object({ index: z2.boolean().optional(), follow: z2.boolean().optional() }).strict().optional()
|
|
153
|
+
}).strict(), indexMetadataSchema = z2.object({
|
|
154
|
+
openGraph: z2.object({
|
|
155
|
+
...socialFields,
|
|
156
|
+
scope: z2.enum(["page", "subtree"]).optional()
|
|
157
|
+
}).strict().refine(
|
|
158
|
+
(value) => value.scope === void 0 || value.image !== void 0,
|
|
159
|
+
{
|
|
160
|
+
message: "Set metadata.openGraph.image when setting scope.",
|
|
161
|
+
path: ["scope"]
|
|
162
|
+
}
|
|
163
|
+
).optional()
|
|
164
|
+
}).strict();
|
|
117
165
|
|
|
118
166
|
// src/lib/compiler/source/config/publish-os-group-schema.ts
|
|
119
167
|
import { z as z3 } from "zod";
|
|
120
|
-
var
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}),
|
|
168
|
+
var navigationSortSchema = z3.object({
|
|
169
|
+
source: z3.literal("frontmatter"),
|
|
170
|
+
field: z3.string().min(1),
|
|
171
|
+
type: z3.enum(["date", "number"]),
|
|
172
|
+
direction: z3.enum(["asc", "desc"]).default("asc")
|
|
173
|
+
}).strict(), navigationEntrySchema = z3.lazy(
|
|
174
|
+
() => z3.discriminatedUnion("type", [
|
|
128
175
|
z3.object({
|
|
129
176
|
type: z3.literal("page"),
|
|
130
|
-
file: z3.string(),
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
177
|
+
file: z3.string().min(1).optional(),
|
|
178
|
+
group: z3.string().min(1).optional(),
|
|
179
|
+
label: z3.string().min(1).optional(),
|
|
180
|
+
items: z3.array(navigationEntrySchema).optional(),
|
|
181
|
+
navigation: z3.lazy(() => navigationConfigSchema).optional()
|
|
182
|
+
}).strict(),
|
|
183
|
+
z3.object({
|
|
184
|
+
type: z3.literal("folder"),
|
|
185
|
+
group: z3.string().min(1).optional(),
|
|
186
|
+
label: z3.string().min(1),
|
|
187
|
+
items: z3.array(navigationEntrySchema).optional(),
|
|
188
|
+
navigation: z3.lazy(() => navigationConfigSchema).optional()
|
|
189
|
+
}).strict(),
|
|
190
|
+
z3.object({
|
|
191
|
+
type: z3.literal("section"),
|
|
192
|
+
label: z3.string().min(1),
|
|
193
|
+
items: z3.array(navigationEntrySchema)
|
|
194
|
+
}).strict(),
|
|
134
195
|
z3.object({
|
|
135
196
|
type: z3.literal("link"),
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}),
|
|
197
|
+
label: z3.string().min(1),
|
|
198
|
+
url: z3.string().min(1)
|
|
199
|
+
}).strict(),
|
|
140
200
|
z3.object({
|
|
141
|
-
type: z3.literal("
|
|
142
|
-
id: z3.string(),
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
201
|
+
type: z3.literal("view"),
|
|
202
|
+
id: z3.string().min(1).optional(),
|
|
203
|
+
label: z3.string().min(1).optional(),
|
|
204
|
+
items: z3.array(navigationEntrySchema)
|
|
205
|
+
}).strict(),
|
|
206
|
+
z3.object({
|
|
207
|
+
type: z3.literal("switch-view"),
|
|
208
|
+
label: z3.string().min(1),
|
|
209
|
+
target: z3.union([
|
|
210
|
+
z3.string().min(1),
|
|
211
|
+
z3.object({ view: z3.string().min(1) }).strict()
|
|
212
|
+
])
|
|
213
|
+
}).strict()
|
|
214
|
+
]).superRefine((entry, context) => {
|
|
215
|
+
entry.type === "page" && entry.file && entry.group && context.addIssue({
|
|
216
|
+
code: "custom",
|
|
217
|
+
path: ["group"],
|
|
218
|
+
message: 'A page navigation entry cannot set both "file" and "group". Use "file" for a page or "group" for a group placement.'
|
|
219
|
+
});
|
|
220
|
+
})
|
|
221
|
+
), navigationConfigSchema = z3.object({
|
|
222
|
+
items: z3.array(navigationEntrySchema),
|
|
223
|
+
sort: z3.unknown().optional()
|
|
224
|
+
}).strict().transform(({ items, sort }) => {
|
|
225
|
+
if (sort === void 0) return { items };
|
|
226
|
+
let parsedSort = navigationSortSchema.safeParse(sort);
|
|
227
|
+
return parsedSort.success ? { items, sort: parsedSort.data } : {
|
|
228
|
+
items,
|
|
229
|
+
sortWarning: 'Invalid navigation sort config. Use source "frontmatter", a field, type "date" or "number", and direction "asc" or "desc". Filename order is used.'
|
|
230
|
+
};
|
|
231
|
+
}), outputScopeSchema = z3.enum(["site", "group", "self"]), outputDeclarationSchema = z3.object({
|
|
157
232
|
path: z3.string().min(1),
|
|
158
233
|
scope: outputScopeSchema.optional(),
|
|
159
234
|
description: z3.string().optional(),
|
|
160
235
|
limit: z3.number().int().positive().optional()
|
|
161
|
-
}).strict(),
|
|
236
|
+
}).strict(), sitemapOutputSchema = outputDeclarationSchema.extend({
|
|
237
|
+
path: z3.string().min(1).optional(),
|
|
238
|
+
mode: z3.enum(["index", "urls"]).optional()
|
|
239
|
+
}), outputDefaultsDeclarationSchema = z3.object({
|
|
162
240
|
scope: outputScopeSchema.optional(),
|
|
163
241
|
limit: z3.number().int().positive().optional(),
|
|
164
|
-
status: z3.array(outputStatusSchema).optional(),
|
|
165
242
|
includeDescriptions: z3.boolean().optional()
|
|
166
243
|
}).strict(), feedsOutputSchema = z3.object({
|
|
167
244
|
rss: outputDeclarationSchema.optional(),
|
|
@@ -172,10 +249,12 @@ var treeNodeSchema = z3.lazy(
|
|
|
172
249
|
atom: outputDefaultsDeclarationSchema.optional(),
|
|
173
250
|
json: outputDefaultsDeclarationSchema.optional()
|
|
174
251
|
}).strict(), outputsSchema = z3.object({
|
|
175
|
-
llms: outputDeclarationSchema.optional(),
|
|
252
|
+
llms: outputDeclarationSchema.or(z3.literal(!1)).optional(),
|
|
253
|
+
llmsFull: outputDeclarationSchema.or(z3.literal(!1)).optional(),
|
|
176
254
|
feeds: feedsOutputSchema.optional(),
|
|
177
|
-
robots: outputDeclarationSchema.optional(),
|
|
178
|
-
sitemap:
|
|
255
|
+
robots: outputDeclarationSchema.or(z3.literal(!1)).optional(),
|
|
256
|
+
sitemap: sitemapOutputSchema.or(z3.literal(!1)).optional(),
|
|
257
|
+
sitemapMarkdown: outputDeclarationSchema.or(z3.literal(!1)).optional()
|
|
179
258
|
}).strict(), outputDefaultsSchema = z3.object({
|
|
180
259
|
llms: outputDefaultsDeclarationSchema.optional(),
|
|
181
260
|
feeds: feedsOutputDefaultsSchema.optional()
|
|
@@ -237,66 +316,35 @@ var httpsOriginSchema = z3.string().transform((value) => normalizeHttpsOrigin(va
|
|
|
237
316
|
]), fontConfigSchema = z3.record(
|
|
238
317
|
z3.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
|
|
239
318
|
fontRoleSchema
|
|
240
|
-
), groupConfigDocumentSchema = z3.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
outputs: outputsSchema.optional(),
|
|
254
|
-
outputDefaults: outputDefaultsSchema.optional()
|
|
255
|
-
}).strict()
|
|
256
|
-
);
|
|
319
|
+
), groupConfigDocumentSchema = z3.object({
|
|
320
|
+
origin: httpsOriginSchema.optional(),
|
|
321
|
+
metadata: indexMetadataSchema.optional(),
|
|
322
|
+
title: z3.string().optional(),
|
|
323
|
+
slug: z3.string().optional(),
|
|
324
|
+
path: z3.string().regex(/^\//).optional(),
|
|
325
|
+
theme: groupThemeSchema.optional(),
|
|
326
|
+
fonts: fontConfigSchema.optional(),
|
|
327
|
+
meta: z3.record(z3.string(), z3.unknown()).optional(),
|
|
328
|
+
navigation: navigationConfigSchema.optional(),
|
|
329
|
+
outputs: outputsSchema.optional(),
|
|
330
|
+
outputDefaults: outputDefaultsSchema.optional()
|
|
331
|
+
}).strict();
|
|
257
332
|
|
|
258
333
|
// src/lib/compiler/source/config/publish-os-config-schema.ts
|
|
259
334
|
var siteSchema = z4.object({
|
|
260
335
|
name: z4.string().optional(),
|
|
261
336
|
description: z4.string().optional()
|
|
262
|
-
}).strict(), runtimePackageMessage = 'Set runtime to "@publish.os/nextjs@<exact-version>". Replace <exact-version> with the exact framework version for this source.', runtimeSchema = z4.string({
|
|
263
|
-
required_error: runtimePackageMessage,
|
|
264
|
-
invalid_type_error: runtimePackageMessage
|
|
265
|
-
}).refine((value) => runtimePackageVersion(value) !== void 0, {
|
|
337
|
+
}).strict(), runtimePackageMessage = 'Set runtime to "@publish.os/nextjs@<exact-version>". Replace <exact-version> with the exact framework version for this source.', runtimeSchema = z4.string({ error: runtimePackageMessage }).refine((value) => runtimePackageVersion(value) !== void 0, {
|
|
266
338
|
message: runtimePackageMessage
|
|
267
339
|
}), rootThemeSchema = z4.object({
|
|
268
340
|
defaultMode: z4.enum(["system", "light", "dark"]).optional(),
|
|
269
341
|
forcedMode: z4.enum(["light", "dark"]).optional()
|
|
270
|
-
}).strict(), remoteMediaSourceSchema = z4.object({
|
|
271
|
-
protocol: z4.literal("https"),
|
|
272
|
-
hostname: z4.string().min(1),
|
|
273
|
-
pathname: z4.string().optional()
|
|
274
|
-
}).strict(), remoteEmbedSourceSchema = z4.object({
|
|
275
|
-
provider: z4.enum([
|
|
276
|
-
"youtube",
|
|
277
|
-
"vimeo",
|
|
278
|
-
"loom",
|
|
279
|
-
"wistia",
|
|
280
|
-
"cloudflare-stream"
|
|
281
|
-
]),
|
|
282
|
-
hostname: z4.string().min(1)
|
|
283
342
|
}).strict(), mediaConfigSchema = z4.object({
|
|
284
|
-
assets: z4.object({
|
|
285
|
-
maxImageBytes: z4.number().int().positive().optional(),
|
|
286
|
-
maxVideoBytes: z4.number().int().positive().optional(),
|
|
287
|
-
maxTotalBytes: z4.number().int().positive().optional(),
|
|
288
|
-
maxFileCount: z4.number().int().positive().optional()
|
|
289
|
-
}).strict().optional(),
|
|
290
|
-
remote: z4.object({
|
|
291
|
-
images: z4.array(remoteMediaSourceSchema).optional(),
|
|
292
|
-
videos: z4.array(remoteMediaSourceSchema).optional(),
|
|
293
|
-
embeds: z4.array(remoteEmbedSourceSchema).optional()
|
|
294
|
-
}).strict().optional()
|
|
343
|
+
assets: z4.object({}).strict().optional()
|
|
295
344
|
}).strict(), agentConfigSchema = z4.object({
|
|
296
345
|
enabled: z4.boolean().optional(),
|
|
297
346
|
markdownRoutes: z4.boolean().optional(),
|
|
298
347
|
markdownAccept: z4.boolean().optional(),
|
|
299
|
-
plainTextAccept: z4.boolean().optional(),
|
|
300
348
|
agentDetection: z4.boolean().optional(),
|
|
301
349
|
discoveryHeaders: z4.boolean().optional(),
|
|
302
350
|
blockInternalMarkdownRoute: z4.boolean().optional()
|
|
@@ -310,11 +358,11 @@ var siteSchema = z4.object({
|
|
|
310
358
|
theme: rootThemeSchema.optional(),
|
|
311
359
|
fonts: fontConfigSchema.optional(),
|
|
312
360
|
media: mediaConfigSchema.optional(),
|
|
313
|
-
|
|
314
|
-
meta: z4.record(z4.unknown()).optional(),
|
|
361
|
+
metadata: indexMetadataSchema.optional(),
|
|
362
|
+
meta: z4.record(z4.string(), z4.unknown()).optional(),
|
|
315
363
|
outputs: outputsSchema.optional(),
|
|
316
364
|
outputDefaults: outputDefaultsSchema.optional(),
|
|
317
|
-
groups: z4.record(groupConfigDocumentSchema).default({})
|
|
365
|
+
groups: z4.record(z4.string(), groupConfigDocumentSchema).default({})
|
|
318
366
|
}).strict();
|
|
319
367
|
|
|
320
368
|
// src/lib/compiler/source/diagnostics/json-source-location.ts
|
|
@@ -603,9 +651,17 @@ async function discoverGroups(options) {
|
|
|
603
651
|
parent: options.parentKey
|
|
604
652
|
}
|
|
605
653
|
});
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
654
|
+
siblingKeys.set(normalized.localKey, entry.name);
|
|
655
|
+
let key = options.parentKey ? `${options.parentKey}/${normalized.localKey}` : normalized.localKey, isDiscoverable = await isDiscoverableGroupFolder(
|
|
656
|
+
folderPath,
|
|
657
|
+
options.absoluteBase
|
|
658
|
+
), children = await discoverGroups({
|
|
659
|
+
absoluteBase: options.absoluteBase,
|
|
660
|
+
folderPath,
|
|
661
|
+
parentKey: key
|
|
662
|
+
});
|
|
663
|
+
if (!isDiscoverable && children.length === 0) continue;
|
|
664
|
+
let group = {
|
|
609
665
|
key,
|
|
610
666
|
localKey: normalized.localKey,
|
|
611
667
|
localFolderName: entry.name,
|
|
@@ -614,13 +670,7 @@ async function discoverGroups(options) {
|
|
|
614
670
|
layoutBoundary: normalized.layoutBoundary,
|
|
615
671
|
hasContent: await pathIsDirectory(join(folderPath, "content"))
|
|
616
672
|
};
|
|
617
|
-
groups.push(group
|
|
618
|
-
...await discoverGroups({
|
|
619
|
-
absoluteBase: options.absoluteBase,
|
|
620
|
-
folderPath,
|
|
621
|
-
parentKey: key
|
|
622
|
-
})
|
|
623
|
-
);
|
|
673
|
+
groups.push(group, ...children);
|
|
624
674
|
}
|
|
625
675
|
return groups;
|
|
626
676
|
}
|
|
@@ -720,7 +770,20 @@ async function collectGroup(options) {
|
|
|
720
770
|
localConfigPath,
|
|
721
771
|
key,
|
|
722
772
|
absoluteBase
|
|
723
|
-
), merged = mergeGroupConfig(entry, localConfig), slug = normalizeGroupSlug(merged.slug ?? localKey, key), path = merged.path ? normalizeGroupPath(merged.path, key) : joinUrlPath(parentPath, slug)
|
|
773
|
+
), merged = mergeGroupConfig(entry, localConfig), slug = normalizeGroupSlug(merged.slug ?? localKey, key), path = merged.path ? normalizeGroupPath(merged.path, key) : joinUrlPath(parentPath, slug);
|
|
774
|
+
if (parentKey && (parentPath === "/" ? path === "/" : path === parentPath || !path.startsWith(`${parentPath}/`)))
|
|
775
|
+
throw new AppError2({
|
|
776
|
+
code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
|
|
777
|
+
message: `Nested group ${key} must use a path beneath its parent ${parentKey} (${parentPath}); received ${path}`,
|
|
778
|
+
details: {
|
|
779
|
+
group: key,
|
|
780
|
+
parentGroup: parentKey,
|
|
781
|
+
path,
|
|
782
|
+
parentPath,
|
|
783
|
+
correction: `Set the path for ${key} to a path beneath ${parentPath}, or move the group to a top-level source folder.`
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
let contentPath = join(folderPath, "content");
|
|
724
787
|
discovered.hasContent && await ensureDirectoryExists(contentPath, key, "content folder");
|
|
725
788
|
let existingPathOwner = paths.get(path);
|
|
726
789
|
if (existingPathOwner && !(path === "/" && existingPathOwner === ROOT_CONTENT_GROUP_KEY))
|
|
@@ -731,6 +794,7 @@ async function collectGroup(options) {
|
|
|
731
794
|
});
|
|
732
795
|
paths.set(path, key), groups[key] = {
|
|
733
796
|
...merged,
|
|
797
|
+
...localConfig?.navigation ? { localNavigation: localConfig.navigation } : {},
|
|
734
798
|
key,
|
|
735
799
|
slug,
|
|
736
800
|
path,
|
|
@@ -801,25 +865,55 @@ async function resolvePublishOsConfig(baseDir) {
|
|
|
801
865
|
|
|
802
866
|
// src/lib/compiler/source/content/scan-publish-os-content.ts
|
|
803
867
|
import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
804
|
-
import { extname, join as join2, relative as relative2 } from "path";
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
"
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
868
|
+
import { extname as extname2, join as join2, relative as relative2 } from "path";
|
|
869
|
+
|
|
870
|
+
// src/lib/compiler/source/content/page-defaults.ts
|
|
871
|
+
import { basename, extname } from "path";
|
|
872
|
+
import remarkMdx from "remark-mdx";
|
|
873
|
+
import remarkParse from "remark-parse";
|
|
874
|
+
import { unified } from "unified";
|
|
875
|
+
var parser = unified().use(remarkParse).use(remarkMdx);
|
|
876
|
+
function defaultTitle(path) {
|
|
877
|
+
return basename(path, extname(path)).replace(/[-_]+/g, " ").replace(
|
|
878
|
+
/(^|\s)(\p{L})/gu,
|
|
879
|
+
(_, space, letter) => space + letter.toLocaleUpperCase()
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
function staticText(node) {
|
|
883
|
+
return node.type === "text" ? node.value ?? "" : ["emphasis", "strong", "delete", "link", "linkReference"].includes(
|
|
884
|
+
node.type
|
|
885
|
+
) ? (node.children ?? []).map((child) => staticText(child)).join("") : "";
|
|
886
|
+
}
|
|
887
|
+
function firstProse(node) {
|
|
888
|
+
if (node.type === "paragraph")
|
|
889
|
+
return (node.children ?? []).map((child) => staticText(child)).join("").replace(/\s+/g, " ").trim() || void 0;
|
|
890
|
+
if (["root", "blockquote", "list", "listItem"].includes(node.type))
|
|
891
|
+
for (let child of node.children ?? []) {
|
|
892
|
+
let prose = firstProse(child);
|
|
893
|
+
if (prose) return prose;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function defaultDescription(body) {
|
|
897
|
+
let prose = firstProse(parser.parse(body));
|
|
898
|
+
if (!prose) return "";
|
|
899
|
+
let sentence = [
|
|
900
|
+
...new Intl.Segmenter(void 0, { granularity: "sentence" }).segment(
|
|
901
|
+
prose
|
|
902
|
+
)
|
|
903
|
+
][0]?.segment.trim() ?? prose, chars = [
|
|
904
|
+
...new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(
|
|
905
|
+
sentence
|
|
906
|
+
)
|
|
907
|
+
].map(({ segment }) => segment);
|
|
908
|
+
if (chars.length <= 160) return sentence;
|
|
909
|
+
let prefix = chars.slice(0, 159).join(""), boundary = /\s/u.test(chars[159] ?? "") ? -1 : prefix.search(/\s+\S*$/u);
|
|
910
|
+
return `${(boundary > 0 ? prefix.slice(0, boundary) : prefix).trimEnd()}\u2026`;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// src/lib/compiler/source/content/scan-publish-os-content.ts
|
|
817
914
|
function fileSlug(relativePath) {
|
|
818
915
|
return relativePath.replace(/\\/g, "/").replace(/\.mdx$/i, "");
|
|
819
916
|
}
|
|
820
|
-
function asString(value) {
|
|
821
|
-
return typeof value == "string" && value.length > 0 ? value : void 0;
|
|
822
|
-
}
|
|
823
917
|
function normalizeContentSlug(value, absolutePath) {
|
|
824
918
|
let result = normalizeSlugPath(value);
|
|
825
919
|
if (result.errors.length > 0)
|
|
@@ -834,283 +928,153 @@ function normalizeContentSlug(value, absolutePath) {
|
|
|
834
928
|
});
|
|
835
929
|
return result.value;
|
|
836
930
|
}
|
|
837
|
-
function asDateString(value) {
|
|
838
|
-
if (typeof value == "string" && value.length > 0) return value;
|
|
839
|
-
if (value instanceof Date && !Number.isNaN(value.getTime()))
|
|
840
|
-
return value.toISOString().slice(0, 10);
|
|
841
|
-
}
|
|
842
|
-
function isIsoDate(value) {
|
|
843
|
-
return !Number.isNaN(Date.parse(value));
|
|
844
|
-
}
|
|
845
931
|
function warning(code, message, details) {
|
|
846
932
|
return { code, level: "warning", message, details };
|
|
847
933
|
}
|
|
848
|
-
function
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
function normalizeAuthorship(authors, absolutePath) {
|
|
860
|
-
let warnings = [];
|
|
861
|
-
if (authors === void 0)
|
|
862
|
-
return { authors: [], warnings };
|
|
863
|
-
if (!Array.isArray(authors))
|
|
864
|
-
return warnings.push(
|
|
865
|
-
warning(
|
|
866
|
-
"CONTENT_AUTHORS_INVALID",
|
|
867
|
-
`Ignoring invalid authors for ${absolutePath}`,
|
|
868
|
-
{
|
|
869
|
-
path: absolutePath
|
|
870
|
-
}
|
|
871
|
-
)
|
|
872
|
-
), { authors: [], warnings };
|
|
873
|
-
let normalized = [];
|
|
874
|
-
for (let author of authors) {
|
|
875
|
-
if (typeof author == "string" && author.length > 0) {
|
|
876
|
-
normalized.push({ name: author });
|
|
877
|
-
continue;
|
|
878
|
-
}
|
|
879
|
-
if (author && typeof author == "object" && typeof author.name == "string") {
|
|
880
|
-
let value = author;
|
|
881
|
-
normalized.push({
|
|
882
|
-
id: asString(value.id),
|
|
883
|
-
name: value.name,
|
|
884
|
-
handle: asString(value.handle),
|
|
885
|
-
avatar: asString(value.avatar),
|
|
886
|
-
url: asString(value.url)
|
|
887
|
-
});
|
|
888
|
-
continue;
|
|
889
|
-
}
|
|
890
|
-
warnings.push(
|
|
891
|
-
warning(
|
|
892
|
-
"CONTENT_AUTHOR_INVALID",
|
|
893
|
-
`Ignoring invalid author entry for ${absolutePath}`,
|
|
894
|
-
{
|
|
895
|
-
path: absolutePath
|
|
896
|
-
}
|
|
897
|
-
)
|
|
898
|
-
);
|
|
899
|
-
}
|
|
900
|
-
return { authors: normalized, warnings };
|
|
901
|
-
}
|
|
902
|
-
function normalizeTags(tags, absolutePath) {
|
|
903
|
-
return tags === void 0 ? { tags: [], warnings: [] } : Array.isArray(tags) ? {
|
|
904
|
-
tags: tags.filter((tag) => typeof tag == "string"),
|
|
905
|
-
warnings: []
|
|
906
|
-
} : {
|
|
907
|
-
tags: [],
|
|
908
|
-
warnings: [
|
|
909
|
-
warning(
|
|
910
|
-
"CONTENT_TAGS_INVALID",
|
|
911
|
-
`Ignoring invalid tags for ${absolutePath}`,
|
|
912
|
-
{
|
|
913
|
-
path: absolutePath
|
|
914
|
-
}
|
|
915
|
-
)
|
|
916
|
-
]
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
function normalizeOgMetadata(value, absolutePath) {
|
|
920
|
-
if (value === void 0) return;
|
|
921
|
-
if (!value || typeof value != "object" || Array.isArray(value))
|
|
922
|
-
throw new AppError2({
|
|
923
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
924
|
-
message: `Invalid og frontmatter for ${absolutePath}; expected an object with title, description, or image`,
|
|
925
|
-
details: { path: absolutePath, field: "og" }
|
|
926
|
-
});
|
|
927
|
-
let source = value, allowedFields = /* @__PURE__ */ new Set(["title", "description", "image"]);
|
|
928
|
-
for (let field of Object.keys(source))
|
|
929
|
-
if (!allowedFields.has(field))
|
|
930
|
-
throw new AppError2({
|
|
931
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
932
|
-
message: `Invalid og frontmatter field "${field}" for ${absolutePath}`,
|
|
933
|
-
details: { path: absolutePath, field: `og.${field}` }
|
|
934
|
-
});
|
|
935
|
-
let og = {};
|
|
936
|
-
if (source.title !== void 0) {
|
|
937
|
-
if (typeof source.title != "string" || source.title.length === 0)
|
|
938
|
-
throw new AppError2({
|
|
939
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
940
|
-
message: `Invalid og.title for ${absolutePath}; expected a non-empty string`,
|
|
941
|
-
details: { path: absolutePath, field: "og.title" }
|
|
942
|
-
});
|
|
943
|
-
og.title = source.title;
|
|
944
|
-
}
|
|
945
|
-
if (source.description !== void 0) {
|
|
946
|
-
if (typeof source.description != "string" || source.description.length === 0)
|
|
947
|
-
throw new AppError2({
|
|
948
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
949
|
-
message: `Invalid og.description for ${absolutePath}; expected a non-empty string`,
|
|
950
|
-
details: { path: absolutePath, field: "og.description" }
|
|
951
|
-
});
|
|
952
|
-
og.description = source.description;
|
|
953
|
-
}
|
|
954
|
-
if (source.image !== void 0)
|
|
955
|
-
if (typeof source.image == "string" && source.image.length > 0)
|
|
956
|
-
og.image = source.image;
|
|
957
|
-
else if (source.image && typeof source.image == "object" && !Array.isArray(source.image)) {
|
|
958
|
-
let image = source.image, allowedImageFields = /* @__PURE__ */ new Set(["src", "alt"]);
|
|
959
|
-
for (let field of Object.keys(image))
|
|
960
|
-
if (!allowedImageFields.has(field))
|
|
961
|
-
throw new AppError2({
|
|
962
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
963
|
-
message: `Invalid og.image field "${field}" for ${absolutePath}`,
|
|
964
|
-
details: { path: absolutePath, field: `og.image.${field}` }
|
|
965
|
-
});
|
|
966
|
-
if (typeof image.src != "string" || image.src.length === 0)
|
|
967
|
-
throw new AppError2({
|
|
968
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
969
|
-
message: `Invalid og.image.src for ${absolutePath}; expected a non-empty string`,
|
|
970
|
-
details: { path: absolutePath, field: "og.image.src" }
|
|
971
|
-
});
|
|
972
|
-
if (image.alt !== void 0 && typeof image.alt != "string")
|
|
973
|
-
throw new AppError2({
|
|
974
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
975
|
-
message: `Invalid og.image.alt for ${absolutePath}; expected a string`,
|
|
976
|
-
details: { path: absolutePath, field: "og.image.alt" }
|
|
977
|
-
});
|
|
978
|
-
og.image = {
|
|
979
|
-
src: image.src,
|
|
980
|
-
...typeof image.alt == "string" ? { alt: image.alt } : {}
|
|
981
|
-
};
|
|
982
|
-
} else
|
|
983
|
-
throw new AppError2({
|
|
984
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
985
|
-
message: `Invalid og.image for ${absolutePath}; expected a non-empty string or object with src`,
|
|
986
|
-
details: { path: absolutePath, field: "og.image" }
|
|
987
|
-
});
|
|
988
|
-
return Object.keys(og).length > 0 ? og : void 0;
|
|
989
|
-
}
|
|
990
|
-
function customMeta(frontmatter, absolutePath) {
|
|
991
|
-
let meta = {}, warnings = [];
|
|
992
|
-
for (let [key, value] of Object.entries(frontmatter))
|
|
993
|
-
if (!RESERVED_FIELDS.has(key)) {
|
|
994
|
-
if (UNSUPPORTED_FIELDS.has(key)) {
|
|
934
|
+
async function readContentFile(group, absolutePath) {
|
|
935
|
+
let raw = await readFile3(absolutePath, "utf-8");
|
|
936
|
+
try {
|
|
937
|
+
let parsed = parseSourceFrontmatter(raw), frontmatter = parsed.data, relativePath = relative2(group.contentPath, absolutePath), fileID = normalizeContentSlug(fileSlug(relativePath), absolutePath), warnings = [];
|
|
938
|
+
if (parsed.content.includes("<")) {
|
|
939
|
+
let unknown = new Set(
|
|
940
|
+
convertMdxToMarkdown({ source: parsed.content }).diagnostics.filter(
|
|
941
|
+
(diagnostic) => diagnostic.code === "mdx-component-unknown"
|
|
942
|
+
).map((diagnostic) => diagnostic.componentName).filter((name) => !!name)
|
|
943
|
+
);
|
|
944
|
+
for (let name of unknown)
|
|
995
945
|
warnings.push(
|
|
996
946
|
warning(
|
|
997
|
-
"
|
|
998
|
-
`
|
|
999
|
-
{ path: absolutePath
|
|
947
|
+
"CONTENT_COMPONENT_NOT_CONVERTED",
|
|
948
|
+
`Custom component <${name}> renders in HTML, but its runtime output is not included in generated Markdown or search. Authored children remain available.`,
|
|
949
|
+
{ path: absolutePath }
|
|
1000
950
|
)
|
|
1001
951
|
);
|
|
1002
|
-
continue;
|
|
1003
|
-
}
|
|
1004
|
-
meta[key] = value;
|
|
1005
952
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
953
|
+
for (let field of ["title", "description"])
|
|
954
|
+
if (frontmatter[field] !== void 0 && typeof frontmatter[field] != "string")
|
|
955
|
+
throw new AppError2({
|
|
956
|
+
code: "CONTENT_FRONTMATTER_TYPE_INVALID",
|
|
957
|
+
message: `Invalid frontmatter field "${field}" in ${absolutePath}; expected a string. Remove it to use the default or provide text.`,
|
|
958
|
+
details: { path: absolutePath, field }
|
|
959
|
+
});
|
|
960
|
+
let title = frontmatter.title?.trim() || defaultTitle(relativePath), description = frontmatter.description?.trim() || defaultDescription(parsed.content);
|
|
961
|
+
for (let field of ["title", "description"])
|
|
962
|
+
frontmatter[field]?.trim() || warnings.push(
|
|
963
|
+
warning(
|
|
964
|
+
"CONTENT_FRONTMATTER_DEFAULT",
|
|
965
|
+
`Missing or blank ${field} in ${absolutePath}; add ${field} to frontmatter.`,
|
|
966
|
+
{ path: absolutePath, field }
|
|
967
|
+
)
|
|
968
|
+
);
|
|
1018
969
|
if (!fileID)
|
|
1019
970
|
throw new AppError2({
|
|
1020
971
|
code: ERROR_CODES.CONTENT_FRONTMATTER_ID_INVALID,
|
|
1021
972
|
message: `Could not derive fileID for ${absolutePath}`,
|
|
1022
973
|
details: { path: absolutePath }
|
|
1023
974
|
});
|
|
1024
|
-
if (!
|
|
975
|
+
if (frontmatter.slug !== void 0 && (typeof frontmatter.slug != "string" || !frontmatter.slug.trim() || /[\\/]/.test(frontmatter.slug)))
|
|
1025
976
|
throw new AppError2({
|
|
1026
|
-
code: ERROR_CODES.
|
|
1027
|
-
message: `
|
|
1028
|
-
details: {
|
|
977
|
+
code: ERROR_CODES.CONTENT_SLUG_INVALID,
|
|
978
|
+
message: `Invalid slug in ${absolutePath}; use one nonempty path segment, or remove slug to use the filename.`,
|
|
979
|
+
details: {
|
|
980
|
+
path: absolutePath,
|
|
981
|
+
field: "slug",
|
|
982
|
+
correction: "Remove slug or provide one nonempty path segment."
|
|
983
|
+
}
|
|
1029
984
|
});
|
|
1030
|
-
let
|
|
1031
|
-
if (slugResult.errors.length > 0)
|
|
985
|
+
let sourceSegments = fileID.split("/"), slugSource = frontmatter.slug ?? sourceSegments.at(-1) ?? "", slugResult = normalizeSlugPath(slugSource);
|
|
986
|
+
if (slugResult.errors.length > 0 || !slugResult.value || slugResult.value.includes("/"))
|
|
1032
987
|
throw new AppError2({
|
|
1033
988
|
code: ERROR_CODES.CONTENT_SLUG_INVALID,
|
|
1034
|
-
message: `Invalid
|
|
989
|
+
message: `Invalid slug "${slugSource}" in ${absolutePath}; remove it or provide one valid path segment.`,
|
|
1035
990
|
details: {
|
|
1036
991
|
errors: slugResult.errors,
|
|
1037
992
|
path: absolutePath,
|
|
1038
|
-
|
|
993
|
+
field: "slug",
|
|
994
|
+
correction: "Remove slug or provide one valid path segment."
|
|
1039
995
|
}
|
|
1040
996
|
});
|
|
1041
|
-
let slug = slugResult.value;
|
|
1042
|
-
slugResult.changed && warnings.push(
|
|
997
|
+
let slug = slugResult.value, routeSlug = [...sourceSegments.slice(0, -1), slug].join("/");
|
|
998
|
+
if (slugResult.changed && warnings.push(
|
|
1043
999
|
warning(
|
|
1044
1000
|
"CONTENT_SLUG_NORMALIZED",
|
|
1045
1001
|
`Normalized slug "${slugSource}" to "${slug}" for ${absolutePath}`,
|
|
1046
1002
|
{ path: absolutePath, from: slugSource, to: slug }
|
|
1047
1003
|
)
|
|
1048
|
-
)
|
|
1049
|
-
|
|
1050
|
-
|
|
1004
|
+
), frontmatter.status !== void 0 && typeof frontmatter.status != "string")
|
|
1005
|
+
throw new AppError2({
|
|
1006
|
+
code: "CONTENT_FRONTMATTER_TYPE_INVALID",
|
|
1007
|
+
message: `Invalid frontmatter field "status" in ${absolutePath}; expected a string.`,
|
|
1008
|
+
details: {
|
|
1009
|
+
path: absolutePath,
|
|
1010
|
+
field: "status",
|
|
1011
|
+
correction: 'Set status to "draft" or "published", or remove it.'
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
let normalizedStatus = frontmatter.status?.trim().toLowerCase(), status = "published";
|
|
1015
|
+
normalizedStatus === "published" || normalizedStatus === "draft" ? status = normalizedStatus : frontmatter.status !== void 0 && warnings.push(
|
|
1051
1016
|
warning(
|
|
1052
1017
|
"CONTENT_STATUS_INVALID",
|
|
1053
|
-
`
|
|
1054
|
-
{ path: absolutePath, status: frontmatter.status }
|
|
1055
|
-
)
|
|
1056
|
-
);
|
|
1057
|
-
let rawPublishedAt = asDateString(frontmatter.publishedAt), publishedAt = rawPublishedAt && isIsoDate(rawPublishedAt) ? rawPublishedAt : void 0;
|
|
1058
|
-
rawPublishedAt && !publishedAt && warnings.push(
|
|
1059
|
-
warning(
|
|
1060
|
-
"CONTENT_DATE_INVALID",
|
|
1061
|
-
`Invalid publishedAt for ${absolutePath}`,
|
|
1018
|
+
`Status "${String(frontmatter.status)}" is not recognized; this page will be published.`,
|
|
1062
1019
|
{
|
|
1063
1020
|
path: absolutePath,
|
|
1064
|
-
|
|
1065
|
-
|
|
1021
|
+
propertyPath: "status",
|
|
1022
|
+
correction: 'Set status to "draft" to keep the page unpublished, or "published" to publish it.'
|
|
1066
1023
|
}
|
|
1067
1024
|
)
|
|
1068
1025
|
);
|
|
1069
|
-
let
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1026
|
+
let metadataResult = pageMetadataSchema.safeParse(
|
|
1027
|
+
frontmatter.metadata === void 0 ? {} : frontmatter.metadata
|
|
1028
|
+
);
|
|
1029
|
+
if (!metadataResult.success) {
|
|
1030
|
+
let issue = metadataResult.error.issues[0], key = issue?.code === "unrecognized_keys" ? issue.keys[0] : void 0, field = [
|
|
1031
|
+
"metadata",
|
|
1032
|
+
...issue?.path ?? [],
|
|
1033
|
+
...key ? [key] : []
|
|
1034
|
+
].join(".");
|
|
1035
|
+
throw new AppError2({
|
|
1036
|
+
code: "CONTENT_FRONTMATTER_TYPE_INVALID",
|
|
1037
|
+
message: `Invalid ${field} in ${absolutePath}. ${formatConfigIssues(metadataResult.error)}. Correct or remove this field.`,
|
|
1038
|
+
details: {
|
|
1075
1039
|
path: absolutePath,
|
|
1076
|
-
field
|
|
1077
|
-
|
|
1040
|
+
field,
|
|
1041
|
+
correction: `Correct or remove ${field}.`
|
|
1078
1042
|
}
|
|
1079
|
-
)
|
|
1080
|
-
|
|
1081
|
-
let
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
let resolved = {
|
|
1046
|
+
...frontmatter,
|
|
1047
|
+
title,
|
|
1048
|
+
description,
|
|
1049
|
+
slug,
|
|
1050
|
+
status,
|
|
1051
|
+
metadata: metadataResult.data
|
|
1052
|
+
};
|
|
1053
|
+
return {
|
|
1087
1054
|
fileID,
|
|
1088
1055
|
group: group.key,
|
|
1089
|
-
slug,
|
|
1090
|
-
|
|
1091
|
-
description: asString(frontmatter.description),
|
|
1092
|
-
og,
|
|
1056
|
+
slug: routeSlug,
|
|
1057
|
+
frontmatter: resolved,
|
|
1093
1058
|
status,
|
|
1094
|
-
publishedAt,
|
|
1095
|
-
updatedAt,
|
|
1096
|
-
authors: authors.authors,
|
|
1097
|
-
tags: tags.tags,
|
|
1098
|
-
meta: meta.meta,
|
|
1099
1059
|
absolutePath,
|
|
1100
1060
|
relativePath,
|
|
1101
|
-
body: parsed.content
|
|
1061
|
+
body: parsed.content,
|
|
1102
1062
|
warnings
|
|
1103
1063
|
};
|
|
1104
1064
|
} catch (error) {
|
|
1105
1065
|
throw error instanceof AppError2 ? error : new AppError2({
|
|
1106
1066
|
code: ERROR_CODES.CONTENT_FILE_PARSE_FAILED,
|
|
1107
|
-
message: `Failed to parse
|
|
1108
|
-
details: {
|
|
1067
|
+
message: `Failed to parse page ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1068
|
+
details: {
|
|
1069
|
+
path: absolutePath,
|
|
1070
|
+
...error instanceof FrontmatterParseError ? { line: error.line, column: error.column, field: error.field } : {},
|
|
1071
|
+
error
|
|
1072
|
+
}
|
|
1109
1073
|
});
|
|
1110
1074
|
}
|
|
1111
1075
|
}
|
|
1112
1076
|
async function walkContent(group) {
|
|
1113
|
-
let files = [], stack = [group.contentPath]
|
|
1077
|
+
let files = [], stack = [group.contentPath];
|
|
1114
1078
|
for (; stack.length > 0; ) {
|
|
1115
1079
|
let current = stack.pop();
|
|
1116
1080
|
if (!current) continue;
|
|
@@ -1128,15 +1092,15 @@ async function walkContent(group) {
|
|
|
1128
1092
|
stack.push(absolute);
|
|
1129
1093
|
continue;
|
|
1130
1094
|
}
|
|
1131
|
-
|
|
1095
|
+
extname2(entry).toLowerCase() === ".mdx" && files.push(await readContentFile(group, absolute));
|
|
1132
1096
|
}
|
|
1133
1097
|
}
|
|
1134
1098
|
return files;
|
|
1135
1099
|
}
|
|
1136
|
-
function buildRoutes(groups, files) {
|
|
1100
|
+
function buildRoutes(groups, files, includeDrafts = !1) {
|
|
1137
1101
|
let routes = [];
|
|
1138
1102
|
for (let file of files) {
|
|
1139
|
-
if (file.status === "draft")
|
|
1103
|
+
if (file.status === "draft" && !includeDrafts)
|
|
1140
1104
|
continue;
|
|
1141
1105
|
let group = groups[file.group];
|
|
1142
1106
|
if (!group)
|
|
@@ -1154,50 +1118,75 @@ function buildRoutes(groups, files) {
|
|
|
1154
1118
|
async function scanContent(config) {
|
|
1155
1119
|
let files = (await Promise.all(
|
|
1156
1120
|
Object.values(config.groups).map((group) => walkContent(group))
|
|
1157
|
-
)).flat(), warnings = files.flatMap((file) => file.warnings), fileIDs = /* @__PURE__ */ new
|
|
1121
|
+
)).flat(), warnings = files.flatMap((file) => file.warnings), fileIDs = /* @__PURE__ */ new Map();
|
|
1158
1122
|
for (let file of files) {
|
|
1159
|
-
let scopedFileID = `${file.group}:${file.fileID}
|
|
1160
|
-
if (
|
|
1123
|
+
let scopedFileID = `${file.group}:${file.fileID}`, previous = fileIDs.get(scopedFileID), sourceRoot = config.groups[file.group]?.__meta.basePath ?? "";
|
|
1124
|
+
if (previous)
|
|
1161
1125
|
throw new AppError2({
|
|
1162
1126
|
code: ERROR_CODES.CONTENT_DUPLICATE_ID,
|
|
1163
|
-
message: `Duplicate
|
|
1164
|
-
details: {
|
|
1127
|
+
message: `Duplicate page identity "${file.fileID}" in group "${file.group}": ${relative2(sourceRoot, previous.absolutePath)} and ${relative2(sourceRoot, file.absolutePath)}. Rename one source file.`,
|
|
1128
|
+
details: {
|
|
1129
|
+
path: file.absolutePath,
|
|
1130
|
+
existingPath: previous.absolutePath,
|
|
1131
|
+
fileID: file.fileID,
|
|
1132
|
+
group: file.group,
|
|
1133
|
+
correction: "Rename one source file."
|
|
1134
|
+
}
|
|
1165
1135
|
});
|
|
1166
|
-
fileIDs.
|
|
1136
|
+
fileIDs.set(scopedFileID, file);
|
|
1167
1137
|
}
|
|
1168
|
-
let routes = buildRoutes(config.groups, files),
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
let
|
|
1179
|
-
if (
|
|
1138
|
+
let candidates = buildRoutes(config.groups, files, !0), routes = buildRoutes(config.groups, files), owners = /* @__PURE__ */ new Map();
|
|
1139
|
+
for (let group of Object.values(config.groups))
|
|
1140
|
+
owners.set(group.path, {
|
|
1141
|
+
source: relative2(
|
|
1142
|
+
group.__meta.basePath,
|
|
1143
|
+
group.__meta.filePath ?? group.__meta.basePath
|
|
1144
|
+
),
|
|
1145
|
+
kind: `group ${group.key}`
|
|
1146
|
+
});
|
|
1147
|
+
for (let route of candidates) {
|
|
1148
|
+
let file = fileIDs.get(`${route.group}:${route.fileID}`);
|
|
1149
|
+
if (!file) continue;
|
|
1150
|
+
let sourceRoot = config.groups[route.group]?.__meta.basePath ?? "", sourcePath = relative2(sourceRoot, file.absolutePath), paths = [[route.path, "page"]];
|
|
1151
|
+
config.agent?.enabled !== !1 && config.agent?.markdownRoutes !== !1 && paths.push([`${route.path}.md`, "Markdown"]);
|
|
1152
|
+
let normalized = normalizeSlugPath(route.path.replace(/^\//, ""));
|
|
1153
|
+
if (normalized.errors.length)
|
|
1180
1154
|
throw new AppError2({
|
|
1181
|
-
code: ERROR_CODES.
|
|
1182
|
-
message: `
|
|
1155
|
+
code: ERROR_CODES.CONTENT_SLUG_INVALID,
|
|
1156
|
+
message: `Invalid route "${route.path}" for ${file.absolutePath}. Shorten the path.`,
|
|
1183
1157
|
details: {
|
|
1158
|
+
path: file.absolutePath,
|
|
1184
1159
|
route: route.path,
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
existingGroup: groupPathOwner
|
|
1160
|
+
errors: normalized.errors,
|
|
1161
|
+
correction: "Shorten the path."
|
|
1188
1162
|
}
|
|
1189
1163
|
});
|
|
1190
|
-
|
|
1164
|
+
for (let [path, kind] of paths) {
|
|
1165
|
+
let previous = owners.get(path);
|
|
1166
|
+
if (previous)
|
|
1167
|
+
throw new AppError2({
|
|
1168
|
+
code: ERROR_CODES.CONTENT_DUPLICATE_ROUTE,
|
|
1169
|
+
message: `URL "${path}" is used by ${previous.kind} ${previous.source} and ${kind} ${sourcePath}. Change a page slug or group path.`,
|
|
1170
|
+
details: {
|
|
1171
|
+
path: file.absolutePath,
|
|
1172
|
+
existingPath: previous.source,
|
|
1173
|
+
route: path,
|
|
1174
|
+
correction: "Change a page slug or group path."
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
owners.set(path, { source: sourcePath, kind });
|
|
1178
|
+
}
|
|
1191
1179
|
}
|
|
1192
1180
|
let meta = config.meta;
|
|
1193
1181
|
return {
|
|
1194
1182
|
site: {
|
|
1195
1183
|
name: config.site?.name ?? meta?.title ?? "Untitled",
|
|
1196
1184
|
description: config.site?.description ?? meta?.description,
|
|
1197
|
-
...config.
|
|
1185
|
+
...config.metadata ? { metadata: config.metadata } : {}
|
|
1198
1186
|
},
|
|
1199
1187
|
files,
|
|
1200
1188
|
routes,
|
|
1189
|
+
candidateRoutes: candidates,
|
|
1201
1190
|
warnings
|
|
1202
1191
|
};
|
|
1203
1192
|
}
|
|
@@ -1216,13 +1205,26 @@ function appErrorToDiagnostic(error) {
|
|
|
1216
1205
|
import { readFile as readFile4 } from "fs/promises";
|
|
1217
1206
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
1218
1207
|
async function missingPageReferences(options) {
|
|
1219
|
-
let diagnostics = [], configSource = /* @__PURE__ */ new Map(),
|
|
1220
|
-
options.content.
|
|
1208
|
+
let diagnostics = [], configSource = /* @__PURE__ */ new Map(), files = new Map(
|
|
1209
|
+
options.content.files.map(
|
|
1210
|
+
(file) => [
|
|
1211
|
+
relative3(options.sourceRoot, file.absolutePath).replace(/\\/g, "/"),
|
|
1212
|
+
file
|
|
1213
|
+
]
|
|
1214
|
+
)
|
|
1215
|
+
), groupContentRoots = Object.values(options.config.groups).map(
|
|
1216
|
+
(group) => ({
|
|
1217
|
+
group,
|
|
1218
|
+
prefix: `${group.__meta.relativeContentPath.replace(/\\/g, "/")}/`
|
|
1219
|
+
})
|
|
1221
1220
|
), rootConfig = resolve4(options.sourceRoot, "publish.os.config.json");
|
|
1222
|
-
|
|
1221
|
+
function groupForSourcePath(path) {
|
|
1222
|
+
return groupContentRoots.filter(({ prefix }) => path.startsWith(prefix)).sort((a, b) => b.prefix.length - a.prefix.length)[0]?.group;
|
|
1223
|
+
}
|
|
1224
|
+
async function locationFor(groupFile, path, rootPath) {
|
|
1223
1225
|
for (let [file, sourcePath] of [
|
|
1224
1226
|
[groupFile, path],
|
|
1225
|
-
[rootConfig,
|
|
1227
|
+
[rootConfig, rootPath]
|
|
1226
1228
|
]) {
|
|
1227
1229
|
let raw = configSource.get(file);
|
|
1228
1230
|
raw === void 0 && (raw = await readFile4(file, "utf8"), configSource.set(file, raw));
|
|
@@ -1235,227 +1237,491 @@ async function missingPageReferences(options) {
|
|
|
1235
1237
|
}
|
|
1236
1238
|
}
|
|
1237
1239
|
for (let group of Object.values(options.config.groups)) {
|
|
1238
|
-
|
|
1240
|
+
let hasLocalNavigation = !!group.localNavigation, basePath = hasLocalNavigation ? ["navigation", "items"] : ["groups", group.key, "navigation", "items"], rootBasePath = hasLocalNavigation ? ["groups", group.key, ...basePath] : basePath;
|
|
1241
|
+
async function visit(nodes, path, rootPath, contentGroup = group) {
|
|
1239
1242
|
for (let [index, node] of nodes.entries()) {
|
|
1240
|
-
let nodePath = [...path, index];
|
|
1241
|
-
if (node.type === "page" &&
|
|
1242
|
-
let propertyPath =
|
|
1243
|
-
|
|
1244
|
-
"file"
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1243
|
+
let nodePath = [...path, index], rootNodePath = [...rootPath, index], targetGroupKey = node.type === "folder" || node.type === "page" ? node.group : void 0, nestedContentGroup = targetGroupKey ? options.config.groups[targetGroupKey] ?? contentGroup : contentGroup;
|
|
1244
|
+
if (node.type === "page" && node.file) {
|
|
1245
|
+
let expected = node.file.replace(/\\/g, "/"), propertyPath = `${nodePath.join(".")}.file`, location = await locationFor(
|
|
1246
|
+
group.__meta.filePath,
|
|
1247
|
+
[...nodePath, "file"],
|
|
1248
|
+
[...rootNodePath, "file"]
|
|
1249
|
+
), expectedPrefix = `${contentGroup.__meta.relativeContentPath.replace(/\\/g, "/")}/`, referencedFile = files.get(expected), targetGroupKey2 = (referencedFile ? options.config.groups[referencedFile.group] : groupForSourcePath(expected))?.key ?? referencedFile?.group, isForeign = targetGroupKey2 !== void 0 && targetGroupKey2 !== contentGroup.key;
|
|
1250
|
+
if (isForeign || !referencedFile) {
|
|
1251
|
+
let code = isForeign ? "NAVIGATION_PAGE_FOREIGN" : "NAVIGATION_PAGE_MISSING", message = isForeign ? `Referenced page "${node.file}" belongs to group "${targetGroupKey2}", not "${contentGroup.key}".` : `Referenced page "${node.file}" was not found.`;
|
|
1252
|
+
diagnostics.push({
|
|
1253
|
+
code,
|
|
1254
|
+
level: "error",
|
|
1255
|
+
message,
|
|
1256
|
+
details: {
|
|
1257
|
+
...location,
|
|
1258
|
+
propertyPath,
|
|
1259
|
+
group: contentGroup.key,
|
|
1260
|
+
target: node.file,
|
|
1261
|
+
...targetGroupKey2 ? { owner: targetGroupKey2 } : {},
|
|
1262
|
+
correction: `Use a source-root-relative MDX path inside ${expectedPrefix}, such as ${expectedPrefix}<file>.mdx.`
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1256
1266
|
}
|
|
1257
|
-
"
|
|
1267
|
+
"items" in node && node.items && await visit(
|
|
1268
|
+
node.items,
|
|
1269
|
+
[...nodePath, "items"],
|
|
1270
|
+
[...rootNodePath, "items"],
|
|
1271
|
+
nestedContentGroup
|
|
1272
|
+
), (node.type === "folder" || node.type === "page") && node.navigation && await visit(
|
|
1273
|
+
node.navigation.items,
|
|
1274
|
+
[...nodePath, "navigation", "items"],
|
|
1275
|
+
[...rootNodePath, "navigation", "items"],
|
|
1276
|
+
nestedContentGroup
|
|
1277
|
+
);
|
|
1258
1278
|
}
|
|
1259
1279
|
}
|
|
1260
|
-
|
|
1280
|
+
let navigation = group.localNavigation ?? group.navigation;
|
|
1281
|
+
await visit(navigation?.items ?? [], basePath, rootBasePath);
|
|
1261
1282
|
}
|
|
1262
1283
|
return diagnostics;
|
|
1263
1284
|
}
|
|
1264
1285
|
|
|
1265
1286
|
// src/lib/compiler/source/manifest/create-publish-os-manifest.ts
|
|
1266
1287
|
import { createHash } from "crypto";
|
|
1288
|
+
|
|
1289
|
+
// src/lib/compiler/source/manifest/sort-navigation-entries.ts
|
|
1290
|
+
function sortKey(value, type) {
|
|
1291
|
+
if (value === void 0) return { reason: "missing" };
|
|
1292
|
+
if (type === "number")
|
|
1293
|
+
return typeof value == "number" && Number.isFinite(value) ? { value } : { reason: "invalid" };
|
|
1294
|
+
let date = authoredDate(value);
|
|
1295
|
+
return date ? { value: Date.parse(date) } : { reason: "invalid" };
|
|
1296
|
+
}
|
|
1297
|
+
function filenameOrder(left, right) {
|
|
1298
|
+
return left.localeCompare(right);
|
|
1299
|
+
}
|
|
1300
|
+
function sortNavigationEntries({
|
|
1301
|
+
entries,
|
|
1302
|
+
explicitCount = 0,
|
|
1303
|
+
field,
|
|
1304
|
+
type,
|
|
1305
|
+
direction
|
|
1306
|
+
}) {
|
|
1307
|
+
if (!Number.isInteger(explicitCount) || explicitCount < 0 || explicitCount > entries.length)
|
|
1308
|
+
throw new RangeError(
|
|
1309
|
+
"explicitCount must be between zero and the entry count."
|
|
1310
|
+
);
|
|
1311
|
+
let automatic = entries.slice(explicitCount), pageEntries = automatic.flatMap(
|
|
1312
|
+
(candidate, index) => candidate.kind === "page" ? [{ candidate, index, key: sortKey(candidate.frontmatter[field], type) }] : []
|
|
1313
|
+
), warnings = pageEntries.flatMap(
|
|
1314
|
+
({ candidate, key }) => key.reason ? [
|
|
1315
|
+
{
|
|
1316
|
+
code: key.reason === "missing" ? "NAVIGATION_SORT_VALUE_MISSING" : "NAVIGATION_SORT_VALUE_INVALID",
|
|
1317
|
+
field,
|
|
1318
|
+
file: candidate.sourcePath
|
|
1319
|
+
}
|
|
1320
|
+
] : []
|
|
1321
|
+
), directionFactor = direction === "asc" ? 1 : -1;
|
|
1322
|
+
pageEntries.sort((left, right) => {
|
|
1323
|
+
let leftValue = left.key.value, rightValue = right.key.value, leftValid = leftValue !== void 0;
|
|
1324
|
+
return leftValid !== (rightValue !== void 0) ? leftValid ? -1 : 1 : leftValue !== void 0 && rightValue !== void 0 && leftValue !== rightValue ? (leftValue - rightValue) * directionFactor : filenameOrder(
|
|
1325
|
+
left.candidate.sourcePath,
|
|
1326
|
+
right.candidate.sourcePath
|
|
1327
|
+
) || left.index - right.index;
|
|
1328
|
+
});
|
|
1329
|
+
let nextPage = 0, sortedAutomatic = automatic.map(
|
|
1330
|
+
(candidate) => candidate.kind === "page" ? pageEntries[nextPage++]?.candidate : candidate
|
|
1331
|
+
);
|
|
1332
|
+
return {
|
|
1333
|
+
entries: [
|
|
1334
|
+
...entries.slice(0, explicitCount).map((candidate) => candidate.entry),
|
|
1335
|
+
...sortedAutomatic.flatMap(
|
|
1336
|
+
(candidate) => candidate ? [candidate.entry] : []
|
|
1337
|
+
)
|
|
1338
|
+
],
|
|
1339
|
+
warnings
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// src/lib/compiler/source/manifest/create-publish-os-manifest.ts
|
|
1267
1344
|
function stableStringify(value) {
|
|
1268
1345
|
return Array.isArray(value) ? `[${value.map((item) => stableStringify(item)).join(",")}]` : value && typeof value == "object" ? `{${Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(
|
|
1269
1346
|
([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`
|
|
1270
1347
|
).join(",")}}` : JSON.stringify(value);
|
|
1271
1348
|
}
|
|
1272
|
-
function
|
|
1273
|
-
|
|
1274
|
-
if (config.groups[relativeKey]) return relativeKey;
|
|
1275
|
-
if (config.groups[reference]) return reference;
|
|
1276
|
-
}
|
|
1277
|
-
function normalizeTreeNode(node, group, pagesByFileID, routesByFileID, options) {
|
|
1278
|
-
if (node.type === "page") {
|
|
1279
|
-
let page = pagesByFileID.get(node.file), route = routesByFileID.get(node.file);
|
|
1280
|
-
return !page || !route ? null : {
|
|
1281
|
-
type: "page",
|
|
1282
|
-
fileID: node.file,
|
|
1283
|
-
title: node.title ?? page.title,
|
|
1284
|
-
path: route.path,
|
|
1285
|
-
group,
|
|
1286
|
-
description: page.description,
|
|
1287
|
-
og: page.og,
|
|
1288
|
-
status: page.status,
|
|
1289
|
-
publishedAt: page.publishedAt,
|
|
1290
|
-
updatedAt: page.updatedAt,
|
|
1291
|
-
authors: page.authors,
|
|
1292
|
-
tags: page.tags,
|
|
1293
|
-
meta: page.meta
|
|
1294
|
-
};
|
|
1295
|
-
}
|
|
1296
|
-
if (node.type === "link")
|
|
1297
|
-
return {
|
|
1298
|
-
type: "link",
|
|
1299
|
-
id: node.id,
|
|
1300
|
-
title: node.title,
|
|
1301
|
-
href: node.url
|
|
1302
|
-
};
|
|
1303
|
-
let children = node.children?.map(
|
|
1304
|
-
(child) => normalizeTreeNode(child, group, pagesByFileID, routesByFileID, options)
|
|
1305
|
-
).filter((child) => child !== null) ?? [];
|
|
1306
|
-
if (node.type === "folder") {
|
|
1307
|
-
let referencedGroup = resolveGroupReference(
|
|
1308
|
-
options.config,
|
|
1309
|
-
group,
|
|
1310
|
-
node.group ?? node.id
|
|
1311
|
-
), referencedChildren = children.length > 0 || !referencedGroup ? children : options.childTreeForGroup(referencedGroup);
|
|
1312
|
-
return {
|
|
1313
|
-
type: "folder",
|
|
1314
|
-
id: node.id,
|
|
1315
|
-
title: node.title,
|
|
1316
|
-
path: referencedGroup ? options.config.groups[referencedGroup]?.path : void 0,
|
|
1317
|
-
group: referencedGroup ?? group,
|
|
1318
|
-
children: referencedChildren
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
return {
|
|
1322
|
-
type: node.type,
|
|
1323
|
-
id: node.id,
|
|
1324
|
-
title: node.title,
|
|
1325
|
-
group,
|
|
1326
|
-
children
|
|
1327
|
-
};
|
|
1349
|
+
function nodeId(group, source, type) {
|
|
1350
|
+
return createHash("sha256").update(`${group}\0${type}\0${source}`).digest("hex").slice(0, 16);
|
|
1328
1351
|
}
|
|
1329
|
-
function
|
|
1330
|
-
return
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
fileID: page.fileID,
|
|
1335
|
-
title: page.title,
|
|
1336
|
-
path: route.path,
|
|
1352
|
+
function navigationError(message, group, target, source) {
|
|
1353
|
+
return new AppError2({
|
|
1354
|
+
code: ERROR_CODES.CONFIG_GROUP_INVALID_SCHEMA,
|
|
1355
|
+
message,
|
|
1356
|
+
details: {
|
|
1337
1357
|
group,
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1358
|
+
target,
|
|
1359
|
+
...source ? { propertyPath: source } : {},
|
|
1360
|
+
correction: "Correct the navigation entry and save the group config."
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
function defaultItems(options) {
|
|
1365
|
+
let remaining = new Map(options.pages.map((page) => [page.fileID, page])), sortWarningReported = !1;
|
|
1366
|
+
function folderItems(folder) {
|
|
1367
|
+
let names = /* @__PURE__ */ new Set();
|
|
1368
|
+
for (let fileID of remaining.keys()) {
|
|
1369
|
+
if (folder && !fileID.startsWith(`${folder}/`)) continue;
|
|
1370
|
+
let name = fileID.slice(folder ? folder.length + 1 : 0);
|
|
1371
|
+
names.add(name.split("/")[0] ?? name);
|
|
1372
|
+
}
|
|
1373
|
+
let items2 = [];
|
|
1374
|
+
for (let name of [...names].sort((a, b) => a.localeCompare(b))) {
|
|
1375
|
+
let prefix = folder ? `${folder}/${name}` : name, file = remaining.get(prefix);
|
|
1376
|
+
if (file) {
|
|
1377
|
+
remaining.delete(prefix);
|
|
1378
|
+
let route = options.routes.get(file.fileID);
|
|
1379
|
+
if (!route) continue;
|
|
1380
|
+
items2.push({
|
|
1381
|
+
id: nodeId(options.group, file.fileID, "page"),
|
|
1382
|
+
type: "page",
|
|
1383
|
+
label: file.frontmatter.title,
|
|
1384
|
+
href: route.path,
|
|
1385
|
+
page: {
|
|
1386
|
+
fileID: file.fileID,
|
|
1387
|
+
group: file.group,
|
|
1388
|
+
path: file.path,
|
|
1389
|
+
frontmatter: file.frontmatter
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
let children = folderItems(prefix);
|
|
1395
|
+
children.length && items2.push({
|
|
1396
|
+
id: nodeId(options.group, prefix, "folder"),
|
|
1397
|
+
type: "folder",
|
|
1398
|
+
label: name,
|
|
1399
|
+
children
|
|
1400
|
+
});
|
|
1355
1401
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1402
|
+
return sortItems(items2);
|
|
1403
|
+
}
|
|
1404
|
+
let items = folderItems("");
|
|
1405
|
+
for (let child of options.childGroups)
|
|
1406
|
+
items.push(options.makeGroup(child, "page"));
|
|
1407
|
+
return items;
|
|
1408
|
+
function sortItems(items2) {
|
|
1409
|
+
let sort = options.sort;
|
|
1410
|
+
if (!sort)
|
|
1411
|
+
return options.sortWarning && !sortWarningReported && (sortWarningReported = !0, options.onWarning?.({
|
|
1412
|
+
code: "NAVIGATION_SORT_CONFIG_INVALID",
|
|
1413
|
+
level: "warning",
|
|
1414
|
+
message: `Group "${options.group}": ${options.sortWarning}`,
|
|
1415
|
+
details: {
|
|
1416
|
+
group: options.group,
|
|
1417
|
+
propertyPath: "navigation.sort"
|
|
1418
|
+
}
|
|
1419
|
+
})), items2;
|
|
1420
|
+
let result = sortNavigationEntries({
|
|
1421
|
+
entries: items2.map(
|
|
1422
|
+
(entry) => entry.type === "page" ? {
|
|
1423
|
+
kind: "page",
|
|
1424
|
+
entry,
|
|
1425
|
+
sourcePath: `${options.contentPath.replace(/\\/g, "/")}/${entry.page.fileID}.mdx`,
|
|
1426
|
+
frontmatter: entry.page.frontmatter
|
|
1427
|
+
} : { kind: "other", entry }
|
|
1428
|
+
),
|
|
1429
|
+
field: sort.field,
|
|
1430
|
+
type: sort.type,
|
|
1431
|
+
direction: sort.direction
|
|
1432
|
+
});
|
|
1433
|
+
for (let warning2 of result.warnings)
|
|
1434
|
+
options.onWarning?.({
|
|
1435
|
+
code: warning2.code,
|
|
1436
|
+
level: "warning",
|
|
1437
|
+
message: warning2.code === "NAVIGATION_SORT_VALUE_MISSING" ? `Navigation sort field "${warning2.field}" is missing from ${warning2.file}; the page sorts after valid values.` : `Navigation sort field "${warning2.field}" is invalid in ${warning2.file}; the page sorts after valid values.`,
|
|
1438
|
+
details: {
|
|
1439
|
+
group: options.group,
|
|
1440
|
+
field: warning2.field,
|
|
1441
|
+
file: warning2.file
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
return result.entries;
|
|
1359
1445
|
}
|
|
1360
|
-
return ids;
|
|
1361
1446
|
}
|
|
1362
|
-
function createManifest(config, content) {
|
|
1447
|
+
function createManifest(config, content, onNavigationWarning) {
|
|
1363
1448
|
let routesByGroup = /* @__PURE__ */ new Map();
|
|
1364
1449
|
for (let route of content.routes) {
|
|
1365
1450
|
let groupRoutes = routesByGroup.get(route.group);
|
|
1366
1451
|
groupRoutes || (groupRoutes = /* @__PURE__ */ new Map(), routesByGroup.set(route.group, groupRoutes)), groupRoutes.has(route.fileID) || groupRoutes.set(route.fileID, route);
|
|
1367
1452
|
}
|
|
1368
|
-
let pages = [], pagesByGroup = /* @__PURE__ */ new Map(),
|
|
1453
|
+
let pages = [], pagesByGroup = /* @__PURE__ */ new Map(), sourcePathToPage = /* @__PURE__ */ new Map();
|
|
1369
1454
|
for (let file of content.files) {
|
|
1370
1455
|
let route = routesByGroup.get(file.group)?.get(file.fileID);
|
|
1371
1456
|
if (!route) continue;
|
|
1372
1457
|
let page = {
|
|
1373
1458
|
fileID: file.fileID,
|
|
1374
1459
|
group: file.group,
|
|
1375
|
-
slug: route.slug,
|
|
1376
1460
|
path: route.path,
|
|
1377
|
-
|
|
1378
|
-
description: file.description,
|
|
1379
|
-
og: file.og,
|
|
1380
|
-
status: file.status,
|
|
1381
|
-
publishedAt: file.publishedAt,
|
|
1382
|
-
updatedAt: file.updatedAt,
|
|
1383
|
-
authors: file.authors,
|
|
1384
|
-
tags: file.tags,
|
|
1385
|
-
meta: file.meta,
|
|
1461
|
+
frontmatter: file.frontmatter,
|
|
1386
1462
|
body: file.body
|
|
1387
1463
|
};
|
|
1388
1464
|
pages.push(page);
|
|
1389
1465
|
let groupPages = pagesByGroup.get(page.group);
|
|
1390
1466
|
groupPages ? groupPages.push(page) : pagesByGroup.set(page.group, [page]);
|
|
1391
|
-
let
|
|
1392
|
-
|
|
1467
|
+
let group = config.groups[file.group];
|
|
1468
|
+
if (!group) continue;
|
|
1469
|
+
let sourcePath = `${group.__meta.relativeContentPath.replace(/\\/g, "/")}/${file.relativePath.replace(/\\/g, "/")}`;
|
|
1470
|
+
sourcePathToPage.set(sourcePath, page);
|
|
1393
1471
|
}
|
|
1394
|
-
let
|
|
1395
|
-
for (let group of
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
let
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
if (
|
|
1404
|
-
|
|
1472
|
+
let groupEntries = Object.values(config.groups), childrenByGroup = /* @__PURE__ */ new Map();
|
|
1473
|
+
for (let group of groupEntries) {
|
|
1474
|
+
if (!group.parentKey) continue;
|
|
1475
|
+
let children = childrenByGroup.get(group.parentKey) ?? [];
|
|
1476
|
+
children.push(group.key), childrenByGroup.set(group.parentKey, children);
|
|
1477
|
+
}
|
|
1478
|
+
for (let children of childrenByGroup.values())
|
|
1479
|
+
children.sort((a, b) => a.localeCompare(b));
|
|
1480
|
+
function groupNode(groupKey, presentation, label, fallback, stack, placementPath) {
|
|
1481
|
+
if (stack.has(groupKey))
|
|
1482
|
+
throw navigationError(
|
|
1483
|
+
`Recursive group placement for "${groupKey}".`,
|
|
1484
|
+
groupKey,
|
|
1485
|
+
groupKey
|
|
1486
|
+
);
|
|
1487
|
+
let group = config.groups[groupKey];
|
|
1488
|
+
if (!group)
|
|
1489
|
+
throw navigationError(
|
|
1490
|
+
`Navigation references missing group "${groupKey}".`,
|
|
1491
|
+
groupKey,
|
|
1492
|
+
groupKey
|
|
1493
|
+
);
|
|
1494
|
+
let children = buildGroupItems(
|
|
1495
|
+
groupKey,
|
|
1496
|
+
group.localNavigation ?? fallback ?? group.navigation,
|
|
1497
|
+
/* @__PURE__ */ new Set([...stack, groupKey]),
|
|
1498
|
+
fallback
|
|
1499
|
+
), route = group.path;
|
|
1500
|
+
return {
|
|
1501
|
+
id: nodeId(groupKey, placementPath, "group"),
|
|
1502
|
+
type: "group",
|
|
1503
|
+
label: label ?? group.title ?? groupKey,
|
|
1504
|
+
groupKey,
|
|
1505
|
+
route,
|
|
1506
|
+
presentation,
|
|
1507
|
+
...presentation === "page" ? { href: route } : {},
|
|
1508
|
+
children
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
function buildGroupItems(groupKey, navigation, stack = /* @__PURE__ */ new Set(), inheritedFallback) {
|
|
1405
1512
|
let group = config.groups[groupKey];
|
|
1406
1513
|
if (!group) return [];
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
group.
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1514
|
+
let groupPages = pagesByGroup.get(groupKey) ?? [], routesByFileID = routesByGroup.get(groupKey) ?? /* @__PURE__ */ new Map(), groupContentPath = group?.__meta.relativeContentPath ?? "content", placedPages = /* @__PURE__ */ new Set(), placedGroups = /* @__PURE__ */ new Set();
|
|
1515
|
+
function normalizeEntry(entry, source) {
|
|
1516
|
+
let id = nodeId(groupKey, source, entry.type);
|
|
1517
|
+
if (entry.type === "page") {
|
|
1518
|
+
if (entry.group) {
|
|
1519
|
+
let target = entry.group, targetGroup = config.groups[target];
|
|
1520
|
+
if (!targetGroup)
|
|
1521
|
+
throw navigationError(
|
|
1522
|
+
`Navigation references missing group "${target}".`,
|
|
1523
|
+
groupKey,
|
|
1524
|
+
target
|
|
1525
|
+
);
|
|
1526
|
+
if (targetGroup.parentKey !== groupKey)
|
|
1527
|
+
throw navigationError(
|
|
1528
|
+
`Group "${target}" must be a child of "${groupKey}".`,
|
|
1529
|
+
groupKey,
|
|
1530
|
+
target
|
|
1531
|
+
);
|
|
1532
|
+
if (placedGroups.has(target))
|
|
1533
|
+
throw navigationError(
|
|
1534
|
+
`Group "${target}" appears more than once in navigation.`,
|
|
1535
|
+
groupKey,
|
|
1536
|
+
target
|
|
1537
|
+
);
|
|
1538
|
+
return placedGroups.add(target), groupNode(
|
|
1539
|
+
target,
|
|
1540
|
+
"page",
|
|
1541
|
+
entry.label,
|
|
1542
|
+
entry.navigation ?? inheritedFallback,
|
|
1543
|
+
stack,
|
|
1544
|
+
source
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
let sourcePath = entry.file ?? "", page = sourcePathToPage.get(sourcePath), expectedPrefix = `${groupContentPath.replace(/\\/g, "/")}/`;
|
|
1548
|
+
if (!sourcePath.startsWith(expectedPrefix))
|
|
1549
|
+
throw navigationError(
|
|
1550
|
+
`Page reference "${entry.file ?? ""}" does not belong to "${groupKey}".`,
|
|
1551
|
+
groupKey,
|
|
1552
|
+
entry.file ?? ""
|
|
1553
|
+
);
|
|
1554
|
+
if (!page || page.group !== groupKey) return null;
|
|
1555
|
+
if (placedPages.has(page.fileID))
|
|
1556
|
+
throw navigationError(
|
|
1557
|
+
`Page "${entry.file}" appears more than once in navigation.`,
|
|
1558
|
+
groupKey,
|
|
1559
|
+
entry.file ?? ""
|
|
1560
|
+
);
|
|
1561
|
+
placedPages.add(page.fileID);
|
|
1562
|
+
let route = routesByFileID.get(page.fileID);
|
|
1563
|
+
if (!route)
|
|
1564
|
+
throw navigationError(
|
|
1565
|
+
`Page route is missing for "${entry.file}".`,
|
|
1566
|
+
groupKey,
|
|
1567
|
+
entry.file ?? ""
|
|
1568
|
+
);
|
|
1569
|
+
let children = entry.items?.map((child, index) => normalizeEntry(child, `${source}.${index}`)).filter((child) => child !== null);
|
|
1570
|
+
return {
|
|
1571
|
+
id,
|
|
1572
|
+
type: "page",
|
|
1573
|
+
label: entry.label ?? page.frontmatter.title,
|
|
1574
|
+
href: route.path,
|
|
1575
|
+
page: {
|
|
1576
|
+
fileID: page.fileID,
|
|
1577
|
+
group: page.group,
|
|
1578
|
+
path: page.path,
|
|
1579
|
+
frontmatter: page.frontmatter
|
|
1580
|
+
},
|
|
1581
|
+
...children?.length ? { children } : {}
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
if (entry.type === "folder" && entry.group) {
|
|
1585
|
+
let target = entry.group, targetGroup = config.groups[target];
|
|
1586
|
+
if (!targetGroup)
|
|
1587
|
+
throw navigationError(
|
|
1588
|
+
`Navigation references missing group "${target}".`,
|
|
1589
|
+
groupKey,
|
|
1590
|
+
target
|
|
1591
|
+
);
|
|
1592
|
+
if (targetGroup.parentKey !== groupKey)
|
|
1593
|
+
throw navigationError(
|
|
1594
|
+
`Group "${target}" must be a child of "${groupKey}".`,
|
|
1595
|
+
groupKey,
|
|
1596
|
+
target
|
|
1597
|
+
);
|
|
1598
|
+
if (placedGroups.has(target))
|
|
1599
|
+
throw navigationError(
|
|
1600
|
+
`Group "${target}" appears more than once in navigation.`,
|
|
1601
|
+
groupKey,
|
|
1602
|
+
target
|
|
1603
|
+
);
|
|
1604
|
+
return placedGroups.add(target), groupNode(
|
|
1605
|
+
target,
|
|
1606
|
+
"folder",
|
|
1607
|
+
entry.label,
|
|
1608
|
+
entry.navigation ?? inheritedFallback,
|
|
1609
|
+
stack,
|
|
1610
|
+
source
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
if (entry.type === "section" || entry.type === "folder") {
|
|
1614
|
+
let children = entry.items?.map((child, index) => normalizeEntry(child, `${source}.${index}`)).filter((child) => child !== null) ?? [];
|
|
1615
|
+
return { id, type: entry.type, label: entry.label, children };
|
|
1616
|
+
}
|
|
1617
|
+
return entry.type === "link" ? { id, type: "link", label: entry.label, href: entry.url } : entry.type === "view" ? {
|
|
1618
|
+
id,
|
|
1619
|
+
type: "view",
|
|
1620
|
+
...entry.id ? { viewId: entry.id } : {},
|
|
1621
|
+
...entry.label ? { label: entry.label } : {},
|
|
1622
|
+
items: entry.items.map((child, index) => normalizeEntry(child, `${source}.${index}`)).filter((child) => child !== null)
|
|
1623
|
+
} : {
|
|
1624
|
+
id,
|
|
1625
|
+
type: "switch-view",
|
|
1626
|
+
label: entry.label,
|
|
1627
|
+
target: entry.target
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
let items = navigation?.items.map(
|
|
1631
|
+
(entry, index) => normalizeEntry(entry, `navigation.items.${index}`)
|
|
1632
|
+
).filter((entry) => entry !== null) ?? [], automatic = defaultItems({
|
|
1633
|
+
group: groupKey,
|
|
1634
|
+
pages: groupPages.filter((page) => !placedPages.has(page.fileID)),
|
|
1635
|
+
routes: routesByFileID,
|
|
1636
|
+
childGroups: (childrenByGroup.get(groupKey) ?? []).filter(
|
|
1637
|
+
(key) => !placedGroups.has(key)
|
|
1638
|
+
),
|
|
1639
|
+
contentPath: groupContentPath,
|
|
1640
|
+
sort: navigation?.sort,
|
|
1641
|
+
sortWarning: navigation?.sortWarning,
|
|
1642
|
+
onWarning: onNavigationWarning,
|
|
1643
|
+
makeGroup: (key, presentation, label) => groupNode(
|
|
1644
|
+
key,
|
|
1645
|
+
presentation,
|
|
1646
|
+
label,
|
|
1647
|
+
inheritedFallback,
|
|
1648
|
+
stack,
|
|
1649
|
+
`default:${key}`
|
|
1650
|
+
)
|
|
1651
|
+
});
|
|
1652
|
+
return items.push(...automatic), items;
|
|
1653
|
+
}
|
|
1654
|
+
let rootKeys = groupEntries.filter((group) => !group.parentKey).map((group) => group.key).sort((a, b) => a.localeCompare(b)), navigationByRoot = /* @__PURE__ */ new Map();
|
|
1655
|
+
for (let rootGroup of rootKeys)
|
|
1656
|
+
navigationByRoot.set(rootGroup, {
|
|
1657
|
+
rootGroup,
|
|
1658
|
+
items: buildGroupItems(
|
|
1659
|
+
rootGroup,
|
|
1660
|
+
config.groups[rootGroup]?.localNavigation ?? config.groups[rootGroup]?.navigation
|
|
1425
1661
|
)
|
|
1426
|
-
|
|
1427
|
-
|
|
1662
|
+
});
|
|
1663
|
+
for (let [rootGroup, navigation] of navigationByRoot) {
|
|
1664
|
+
let inspect2 = function(nodes, path) {
|
|
1665
|
+
for (let [index, node] of nodes.entries()) {
|
|
1666
|
+
let source = `${path}.${index}`;
|
|
1667
|
+
if (node.type === "view" && node.viewId) {
|
|
1668
|
+
if (views.has(node.viewId))
|
|
1669
|
+
throw navigationError(
|
|
1670
|
+
`View ID "${node.viewId}" is duplicated.`,
|
|
1671
|
+
rootGroup,
|
|
1672
|
+
node.viewId,
|
|
1673
|
+
source
|
|
1674
|
+
);
|
|
1675
|
+
views.set(node.viewId, source);
|
|
1676
|
+
}
|
|
1677
|
+
node.type === "switch-view" && switches.push({ target: node.target, source }), "children" in node && node.children && inspect2(node.children, source), node.type === "view" && inspect2(node.items, source);
|
|
1678
|
+
}
|
|
1679
|
+
};
|
|
1680
|
+
var inspect = inspect2;
|
|
1681
|
+
let views = /* @__PURE__ */ new Map(), switches = [];
|
|
1682
|
+
inspect2(navigation.items, "navigation.items");
|
|
1683
|
+
for (let control of switches) {
|
|
1684
|
+
let viewTarget = typeof control.target == "string" ? void 0 : control.target.view, groupTarget = typeof control.target == "string" ? control.target : void 0;
|
|
1685
|
+
if (viewTarget && !views.has(viewTarget))
|
|
1686
|
+
throw navigationError(
|
|
1687
|
+
`Switch-view target "${viewTarget}" does not exist.`,
|
|
1688
|
+
rootGroup,
|
|
1689
|
+
viewTarget,
|
|
1690
|
+
control.source
|
|
1691
|
+
);
|
|
1692
|
+
if (groupTarget && !config.groups[groupTarget] && !views.has(groupTarget))
|
|
1693
|
+
throw navigationError(
|
|
1694
|
+
`Switch-view target "${groupTarget}" is neither a group nor a named view.`,
|
|
1695
|
+
rootGroup,
|
|
1696
|
+
groupTarget,
|
|
1697
|
+
control.source
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1428
1700
|
}
|
|
1429
1701
|
let groups = Object.fromEntries(
|
|
1430
|
-
|
|
1431
|
-
let
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
path: group.path,
|
|
1435
|
-
origin: group.origin ?? (group.key === ROOT_CONTENT_GROUP_KEY ? void 0 : config.origin),
|
|
1436
|
-
ancestry: group.ancestry,
|
|
1437
|
-
...group.og ? { og: group.og } : {},
|
|
1438
|
-
meta: group.meta,
|
|
1439
|
-
tree: groupTree(group.key),
|
|
1440
|
-
pages: groupPages
|
|
1702
|
+
groupEntries.map((group) => {
|
|
1703
|
+
let rootGroup = group.ancestry[0] ?? group.key, navigation = navigationByRoot.get(rootGroup) ?? {
|
|
1704
|
+
rootGroup,
|
|
1705
|
+
items: []
|
|
1441
1706
|
};
|
|
1442
|
-
return [
|
|
1707
|
+
return [
|
|
1708
|
+
group.key,
|
|
1709
|
+
{
|
|
1710
|
+
key: group.key,
|
|
1711
|
+
title: group.title ?? group.key,
|
|
1712
|
+
path: group.path,
|
|
1713
|
+
origin: group.origin ?? (group.key === ROOT_CONTENT_GROUP_KEY ? void 0 : config.origin),
|
|
1714
|
+
ancestry: group.ancestry,
|
|
1715
|
+
...group.metadata ? { metadata: group.metadata } : {},
|
|
1716
|
+
meta: group.meta,
|
|
1717
|
+
navigation,
|
|
1718
|
+
pages: pagesByGroup.get(group.key) ?? []
|
|
1719
|
+
}
|
|
1720
|
+
];
|
|
1443
1721
|
})
|
|
1444
|
-
), tree = Object.values(groups).filter((group) => !config.groups[group.key]?.parentKey).flatMap(
|
|
1445
|
-
(group) => group.key === ROOT_CONTENT_GROUP_KEY ? group.tree : [
|
|
1446
|
-
{
|
|
1447
|
-
type: "section",
|
|
1448
|
-
id: group.key,
|
|
1449
|
-
title: group.title,
|
|
1450
|
-
path: group.path,
|
|
1451
|
-
group: group.key,
|
|
1452
|
-
children: group.tree
|
|
1453
|
-
}
|
|
1454
|
-
]
|
|
1455
1722
|
), manifest = {
|
|
1456
1723
|
site: content.site,
|
|
1457
1724
|
groups,
|
|
1458
|
-
tree,
|
|
1459
1725
|
routes: content.routes,
|
|
1460
1726
|
pages,
|
|
1461
1727
|
contentHash: ""
|
|
@@ -1466,7 +1732,8 @@ function createManifest(config, content) {
|
|
|
1466
1732
|
// src/lib/compiler/source/prepare-publish-os-source.ts
|
|
1467
1733
|
async function preparePublishOsSource({
|
|
1468
1734
|
publishOsDir,
|
|
1469
|
-
sourceIdentity
|
|
1735
|
+
sourceIdentity,
|
|
1736
|
+
includeDrafts = !1
|
|
1470
1737
|
}) {
|
|
1471
1738
|
try {
|
|
1472
1739
|
let sourceRoot = resolve5(publishOsDir), boundary = await assertPublishOsSourceBoundary(
|
|
@@ -1475,35 +1742,41 @@ async function preparePublishOsSource({
|
|
|
1475
1742
|
), config = await resolvePublishOsConfig(sourceRoot), content = await scanContent(config), media = await buildMediaAssets({
|
|
1476
1743
|
config,
|
|
1477
1744
|
publishOsDir
|
|
1478
|
-
}), faviconsAndLogos =
|
|
1745
|
+
}), faviconsAndLogos = buildFaviconsAndLogos({
|
|
1746
|
+
media
|
|
1747
|
+
}), missingPages = await missingPageReferences({
|
|
1479
1748
|
sourceRoot,
|
|
1480
1749
|
config,
|
|
1481
1750
|
content
|
|
1482
|
-
})
|
|
1751
|
+
}), sourceDiagnostics = [
|
|
1752
|
+
...boundary.nestedSources.map((file) => ({
|
|
1753
|
+
code: "SOURCE_NESTED_EXCLUDED",
|
|
1754
|
+
level: "warning",
|
|
1755
|
+
message: "Nested publish.os source is excluded from this site.",
|
|
1756
|
+
details: { file }
|
|
1757
|
+
})),
|
|
1758
|
+
...content.warnings,
|
|
1759
|
+
...missingPages,
|
|
1760
|
+
...faviconsAndLogos.warnings,
|
|
1761
|
+
...media.warnings.map((warning2) => ({
|
|
1762
|
+
...warning2,
|
|
1763
|
+
level: "warning"
|
|
1764
|
+
}))
|
|
1765
|
+
];
|
|
1766
|
+
if (missingPages.some((diagnostic) => diagnostic.level === "error"))
|
|
1767
|
+
return { diagnostics: sourceDiagnostics };
|
|
1768
|
+
let navigationWarnings = [], manifest = createManifest(
|
|
1769
|
+
config,
|
|
1770
|
+
includeDrafts ? { ...content, routes: content.candidateRoutes } : content,
|
|
1771
|
+
(warning2) => navigationWarnings.push(warning2)
|
|
1772
|
+
);
|
|
1483
1773
|
return {
|
|
1484
|
-
diagnostics: [
|
|
1485
|
-
...boundary.nestedSources.map((file) => ({
|
|
1486
|
-
code: "SOURCE_NESTED_EXCLUDED",
|
|
1487
|
-
level: "warning",
|
|
1488
|
-
message: "Nested publish.os source is excluded from this site.",
|
|
1489
|
-
details: { file }
|
|
1490
|
-
})),
|
|
1491
|
-
...content.warnings,
|
|
1492
|
-
...missingPages,
|
|
1493
|
-
...media.warnings.map((warning2) => ({
|
|
1494
|
-
...warning2,
|
|
1495
|
-
level: "warning"
|
|
1496
|
-
})),
|
|
1497
|
-
...faviconsAndLogos.warnings.map((warning2) => ({
|
|
1498
|
-
...warning2,
|
|
1499
|
-
level: "warning"
|
|
1500
|
-
}))
|
|
1501
|
-
],
|
|
1774
|
+
diagnostics: [...sourceDiagnostics, ...navigationWarnings],
|
|
1502
1775
|
source: {
|
|
1503
1776
|
assets: { faviconsAndLogos, media },
|
|
1504
1777
|
config,
|
|
1505
1778
|
content,
|
|
1506
|
-
manifest
|
|
1779
|
+
manifest
|
|
1507
1780
|
}
|
|
1508
1781
|
};
|
|
1509
1782
|
} catch (error) {
|
|
@@ -1521,12 +1794,12 @@ async function preparePublishOsSource({
|
|
|
1521
1794
|
|
|
1522
1795
|
// src/lib/compiler/theme/theme-token-diagnostics.ts
|
|
1523
1796
|
import { readdirSync, readFileSync } from "fs";
|
|
1524
|
-
import { extname as
|
|
1797
|
+
import { extname as extname3, join as join3, resolve as resolve6 } from "path";
|
|
1525
1798
|
import postcss from "postcss";
|
|
1526
1799
|
function cssFiles(directory) {
|
|
1527
1800
|
return readdirSync(directory, { withFileTypes: !0 }).flatMap((entry) => {
|
|
1528
1801
|
let path = join3(directory, entry.name);
|
|
1529
|
-
return entry.isDirectory() ? cssFiles(path) :
|
|
1802
|
+
return entry.isDirectory() ? cssFiles(path) : extname3(entry.name) === ".css" ? [path] : [];
|
|
1530
1803
|
});
|
|
1531
1804
|
}
|
|
1532
1805
|
function readDesignTokens() {
|
|
@@ -1587,17 +1860,19 @@ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
1587
1860
|
"GroupContext",
|
|
1588
1861
|
"LayoutProps",
|
|
1589
1862
|
"ManifestLike",
|
|
1590
|
-
"
|
|
1591
|
-
"
|
|
1592
|
-
"
|
|
1593
|
-
"
|
|
1594
|
-
"
|
|
1863
|
+
"NavigationContainerNode",
|
|
1864
|
+
"NavigationGroupNode",
|
|
1865
|
+
"NavigationLinkNode",
|
|
1866
|
+
"NavigationNode",
|
|
1867
|
+
"NavigationPageNode",
|
|
1868
|
+
"NavigationSwitchViewNode",
|
|
1869
|
+
"NavigationViewNode",
|
|
1595
1870
|
"PageData",
|
|
1596
1871
|
"PageProps",
|
|
1872
|
+
"ResolvedNavigation",
|
|
1597
1873
|
"RootLayoutProps",
|
|
1598
1874
|
"RootPageProps",
|
|
1599
|
-
"SiteData"
|
|
1600
|
-
"TreeNode"
|
|
1875
|
+
"SiteData"
|
|
1601
1876
|
]), ROUTE_EXECUTION_EXPORTS = [
|
|
1602
1877
|
"runtime",
|
|
1603
1878
|
"dynamic",
|
|
@@ -1647,7 +1922,7 @@ function normalizeLocalFontSourcePath(value) {
|
|
|
1647
1922
|
return value.replace(/^\/+/, "").replace(/^\.\//, "");
|
|
1648
1923
|
}
|
|
1649
1924
|
function isFontFilePath(path) {
|
|
1650
|
-
return FONT_FILE_EXTENSIONS.has(
|
|
1925
|
+
return FONT_FILE_EXTENSIONS.has(extname4(path).toLowerCase());
|
|
1651
1926
|
}
|
|
1652
1927
|
function isValidSourceFontFilePath(path) {
|
|
1653
1928
|
return path.startsWith("fonts/") && isFontFilePath(path);
|
|
@@ -1692,7 +1967,7 @@ async function validateConfiguredLocalFontSources({
|
|
|
1692
1967
|
return sourcePaths;
|
|
1693
1968
|
}
|
|
1694
1969
|
function isHardRejectedPath(path) {
|
|
1695
|
-
let name =
|
|
1970
|
+
let name = basename2(path);
|
|
1696
1971
|
return isReservedFrameworkPath(path) || path.split("/").some((segment) => ["packages", "scripts"].includes(segment)) || LOCKFILES.has(name) || name === "tsconfig.json" && path !== "tsconfig.json";
|
|
1697
1972
|
}
|
|
1698
1973
|
function isAllowedBareImport(specifier) {
|
|
@@ -1704,6 +1979,10 @@ function isAllowedBareImport(specifier) {
|
|
|
1704
1979
|
}
|
|
1705
1980
|
return ALLOWED_BARE_IMPORTS.has(specifier);
|
|
1706
1981
|
}
|
|
1982
|
+
function isOriginalDefaultComponentBridge(options) {
|
|
1983
|
+
let sourcePath = originalDefaultComponentSourcePath(options.specifier);
|
|
1984
|
+
return sourcePath ? options.relativeFile.replace(/\.[^.]+$/, "") === sourcePath : !1;
|
|
1985
|
+
}
|
|
1707
1986
|
function addFileError(store, code, message, file, details = {}) {
|
|
1708
1987
|
store.error(code, message, { ...details, file });
|
|
1709
1988
|
}
|
|
@@ -1790,7 +2069,7 @@ function importDetails(options) {
|
|
|
1790
2069
|
};
|
|
1791
2070
|
}
|
|
1792
2071
|
function scriptKindForPath(path) {
|
|
1793
|
-
switch (
|
|
2072
|
+
switch (extname4(path).toLowerCase()) {
|
|
1794
2073
|
case ".tsx":
|
|
1795
2074
|
return ts2.ScriptKind.TSX;
|
|
1796
2075
|
case ".jsx":
|
|
@@ -2029,7 +2308,7 @@ function validateSourceFilePath({
|
|
|
2029
2308
|
groupFolders,
|
|
2030
2309
|
store
|
|
2031
2310
|
}) {
|
|
2032
|
-
if (
|
|
2311
|
+
if (basename2(file) === "leaf.tsx" && (file === "leaf.tsx" || groupFolders.has(dirname(file))) || file.split("/").includes("(leaf)")) {
|
|
2033
2312
|
let suggestedPath = file.replaceAll("(leaf)", "(page)").replace(/leaf\.tsx$/, "page.tsx");
|
|
2034
2313
|
return addFileError(
|
|
2035
2314
|
store,
|
|
@@ -2056,7 +2335,7 @@ function validateSourceFontFile({
|
|
|
2056
2335
|
return isValidSourceFontFilePath(file) ? (configured || addFileWarning(
|
|
2057
2336
|
store,
|
|
2058
2337
|
"FRAMEWORK_FONT_FILE_UNUSED",
|
|
2059
|
-
`Font file "${
|
|
2338
|
+
`Font file "${basename2(file)}" is not used by the current font configuration and will not be included in the site.`,
|
|
2060
2339
|
file,
|
|
2061
2340
|
{ fontSourcePath: `/${file}`, suggestion: fontConfigHint(file) }
|
|
2062
2341
|
), !0) : (addFileWarning(
|
|
@@ -2066,7 +2345,7 @@ function validateSourceFontFile({
|
|
|
2066
2345
|
file,
|
|
2067
2346
|
{
|
|
2068
2347
|
fontSourcePath: `/${file}`,
|
|
2069
|
-
suggestion: fontConfigHint(`fonts/${
|
|
2348
|
+
suggestion: fontConfigHint(`fonts/${basename2(file)}`)
|
|
2070
2349
|
}
|
|
2071
2350
|
), !0);
|
|
2072
2351
|
}
|
|
@@ -2075,7 +2354,7 @@ function validateLegacyIconReferences(source, file, store) {
|
|
|
2075
2354
|
match && addFileError(
|
|
2076
2355
|
store,
|
|
2077
2356
|
"FRAMEWORK_ICON_RESOLVER_REJECTED",
|
|
2078
|
-
'The Icon resolver is removed. Import an exact component from "@phosphor-icons/react/ssr" in
|
|
2357
|
+
'The Icon resolver is removed. Import an exact named component from "@phosphor-icons/react/ssr" in MDX or TSX instead.',
|
|
2079
2358
|
file,
|
|
2080
2359
|
sourceDetails(source, match.index, match[0] ?? "")
|
|
2081
2360
|
);
|
|
@@ -2107,7 +2386,7 @@ async function validateSourceFileContents({
|
|
|
2107
2386
|
checkedCss,
|
|
2108
2387
|
store
|
|
2109
2388
|
}) {
|
|
2110
|
-
let extension =
|
|
2389
|
+
let extension = extname4(file), sourceModule = SOURCE_EXTENSIONS.has(extension), validatesAttribution = ATTRIBUTION_VALIDATION_EXTENSIONS.has(extension), scansLegacyIcons = sourceModule || MARKDOWN_ICON_SCAN_EXTENSIONS.has(extension) && !file.startsWith("assets/");
|
|
2111
2390
|
if (!validatesAttribution && !scansLegacyIcons) return;
|
|
2112
2391
|
let absolutePath = resolve7(publishOsDir, file), source = await readFile5(absolutePath, "utf-8");
|
|
2113
2392
|
validatesAttribution && validateAttributionReferences(source, file, store), extension === ".css" && (validateThemeTokenSpelling(source, file, store), await validateCssDependencies({
|
|
@@ -2115,7 +2394,23 @@ async function validateSourceFileContents({
|
|
|
2115
2394
|
filePath: absolutePath,
|
|
2116
2395
|
css: source,
|
|
2117
2396
|
checked: checkedCss
|
|
2118
|
-
}))
|
|
2397
|
+
}));
|
|
2398
|
+
let importsIconBinding = !1;
|
|
2399
|
+
if (extension === ".mdx" && !file.startsWith("assets/"))
|
|
2400
|
+
try {
|
|
2401
|
+
importsIconBinding = (await validateMdxIconImports(source)).some(({ local }) => local === "Icon");
|
|
2402
|
+
} catch (error) {
|
|
2403
|
+
let line = error instanceof MdxIconImportError ? error.line : 1, column = error instanceof MdxIconImportError ? error.column : 1;
|
|
2404
|
+
addFileError(
|
|
2405
|
+
store,
|
|
2406
|
+
error instanceof MdxIconImportError ? error.code : "FRAMEWORK_MDX_IMPORT_INVALID",
|
|
2407
|
+
error instanceof Error ? error.message : String(error),
|
|
2408
|
+
file,
|
|
2409
|
+
{ line, column, sourceLine: source.split(`
|
|
2410
|
+
`)[line - 1] ?? "" }
|
|
2411
|
+
);
|
|
2412
|
+
}
|
|
2413
|
+
scansLegacyIcons && !importsIconBinding && validateLegacyIconReferences(source, file, store), sourceModule && (validateSourceModuleSafety(source, file, store), await validateSourceModule({
|
|
2119
2414
|
absolutePath,
|
|
2120
2415
|
groupFolders,
|
|
2121
2416
|
publishOsDir,
|
|
@@ -2140,18 +2435,32 @@ async function validateSourceBoundary(publishOsDir, config, store) {
|
|
|
2140
2435
|
"package.json"
|
|
2141
2436
|
);
|
|
2142
2437
|
for (let file of await walkAllFiles(publishOsDir))
|
|
2143
|
-
validateSourceFilePath({ file, groupFolders, store }) &&
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2438
|
+
if (validateSourceFilePath({ file, groupFolders, store }) && !validateSourceFontFile({ file, configuredFontSourcePaths, store })) {
|
|
2439
|
+
if (!isApprovedPublishOsPath(file, groupFolders))
|
|
2440
|
+
if (file.startsWith("public/")) {
|
|
2441
|
+
let assetPath = file.slice(7);
|
|
2442
|
+
addFileWarning(
|
|
2443
|
+
store,
|
|
2444
|
+
"FRAMEWORK_SOURCE_PUBLIC_UNSUPPORTED",
|
|
2445
|
+
`Files in publish.os/public are not included. Move this file to publish.os/assets/${assetPath} and reference it as /assets/${assetPath}.`,
|
|
2446
|
+
file,
|
|
2447
|
+
{ correction: `assets/${assetPath}` }
|
|
2448
|
+
);
|
|
2449
|
+
} else
|
|
2450
|
+
addFileWarning(
|
|
2451
|
+
store,
|
|
2452
|
+
"FRAMEWORK_SOURCE_PATH_IGNORED",
|
|
2453
|
+
"This file is outside the supported publish.os structure and will not be included in the site.",
|
|
2454
|
+
file
|
|
2455
|
+
);
|
|
2456
|
+
await validateSourceFileContents({
|
|
2457
|
+
publishOsDir,
|
|
2458
|
+
file,
|
|
2459
|
+
groupFolders,
|
|
2460
|
+
checkedCss,
|
|
2461
|
+
store
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2155
2464
|
}
|
|
2156
2465
|
async function validateCssDependencies({
|
|
2157
2466
|
publishOsDir,
|
|
@@ -2296,7 +2605,7 @@ async function validateImportSpecifier(options) {
|
|
|
2296
2605
|
);
|
|
2297
2606
|
return;
|
|
2298
2607
|
}
|
|
2299
|
-
isAllowedBareImport(options.specifier)
|
|
2608
|
+
!isAllowedBareImport(options.specifier) && !isOriginalDefaultComponentBridge(options) && addFileError(
|
|
2300
2609
|
options.store,
|
|
2301
2610
|
"FRAMEWORK_IMPORT_NOT_ALLOWED",
|
|
2302
2611
|
`Can't import from "${options.specifier}". Source modules can only import approved runtime packages.`,
|
|
@@ -2304,10 +2613,18 @@ async function validateImportSpecifier(options) {
|
|
|
2304
2613
|
importDetails(options)
|
|
2305
2614
|
);
|
|
2306
2615
|
}
|
|
2307
|
-
function toValidationDiagnostic(diagnostic) {
|
|
2616
|
+
function toValidationDiagnostic(diagnostic, sourceRoot) {
|
|
2617
|
+
let path = diagnostic.details?.file ?? diagnostic.details?.path ?? diagnostic.file, file = typeof path == "string" && isSameOrDescendant(sourceRoot, resolve7(sourceRoot, path)) ? relative4(sourceRoot, resolve7(sourceRoot, path)).split(sep).join("/") : void 0;
|
|
2308
2618
|
return {
|
|
2309
2619
|
...diagnostic,
|
|
2310
|
-
|
|
2620
|
+
message: sourceRoot === parse(sourceRoot).root ? diagnostic.message : diagnostic.message.replaceAll(`${sourceRoot}${sep}`, ""),
|
|
2621
|
+
file,
|
|
2622
|
+
...typeof diagnostic.details?.field == "string" && !diagnostic.details.propertyPath ? {
|
|
2623
|
+
details: {
|
|
2624
|
+
...diagnostic.details,
|
|
2625
|
+
propertyPath: diagnostic.details.field
|
|
2626
|
+
}
|
|
2627
|
+
} : {},
|
|
2311
2628
|
line: typeof diagnostic.details?.line == "number" ? diagnostic.details.line : diagnostic.line,
|
|
2312
2629
|
column: typeof diagnostic.details?.column == "number" ? diagnostic.details.column : diagnostic.column
|
|
2313
2630
|
};
|
|
@@ -2322,7 +2639,8 @@ async function validatePublishOsSource(options) {
|
|
|
2322
2639
|
try {
|
|
2323
2640
|
let prepared = options.sourcePreparation ?? await preparePublishOsSource({
|
|
2324
2641
|
publishOsDir,
|
|
2325
|
-
sourceIdentity: options.sourceIdentity
|
|
2642
|
+
sourceIdentity: options.sourceIdentity,
|
|
2643
|
+
includeDrafts: options.includeDrafts
|
|
2326
2644
|
});
|
|
2327
2645
|
for (let diagnostic of prepared.diagnostics)
|
|
2328
2646
|
store.add(diagnostic);
|
|
@@ -2330,7 +2648,7 @@ async function validatePublishOsSource(options) {
|
|
|
2330
2648
|
} catch (error) {
|
|
2331
2649
|
error instanceof AppError ? store.error(error.code, error.message, error.details) : store.error("FRAMEWORK_VALIDATE_UNKNOWN", String(error));
|
|
2332
2650
|
}
|
|
2333
|
-
let allDiagnostics = store.all().map(toValidationDiagnostic), errors = allDiagnostics.filter((d) => d.level === "error"), warnings = allDiagnostics.filter((d) => d.level === "warning"), valid = errors.length === 0;
|
|
2651
|
+
let allDiagnostics = store.all().map((diagnostic) => toValidationDiagnostic(diagnostic, publishOsDir)), errors = allDiagnostics.filter((d) => d.level === "error"), warnings = allDiagnostics.filter((d) => d.level === "warning"), valid = errors.length === 0;
|
|
2334
2652
|
return {
|
|
2335
2653
|
valid,
|
|
2336
2654
|
errors,
|