@publish.os/nextjs 0.0.1-alpha.91 → 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 -2
- package/compiler-assets/nextjs/defaults/group/page.tsx +7 -6
- package/compiler-assets/nextjs/defaults/project/next.config.mjs +2 -1
- 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 +9 -1
- package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +3 -0
- package/compiler-assets/nextjs/shims/proxy.ts +156 -54
- package/dist/{build-operation-5QMLXF7N.js → build-operation-CJDUL4OW.js} +5 -5
- package/dist/{chunk-GBPLER5H.js → chunk-3T4HVDSC.js} +2 -2
- package/dist/{chunk-UYGCOWRF.js → chunk-DX7PXFCM.js} +860 -50
- package/dist/{chunk-F3AGNDF4.js → chunk-DZ3VWEV5.js} +860 -575
- package/dist/{chunk-OXOFRXEI.js → chunk-NHSLTARU.js} +1284 -1164
- package/dist/{chunk-ORIUWJBQ.js → chunk-SBMDONTV.js} +3 -2
- package/dist/compiler.js +42 -23
- package/dist/components/blocks/agent-actions.d.ts +7 -7
- package/dist/components/blocks/agent-actions.js +1 -32
- package/dist/components/blocks/toc.d.ts +7 -7
- package/dist/components/ui/button.d.ts +1 -2
- package/dist/{compose-CVBJRAF6.js → compose-44GJSMXD.js} +2 -2
- package/dist/config/createNextConfig.js +2 -0
- package/dist/{dev-operation-OSHHWRWK.js → dev-operation-WNECBHZ5.js} +17 -7
- package/dist/{ide-support-CU6Z5PVR.js → ide-support-TG74L4FF.js} +3 -3
- package/dist/index.d.ts +143 -126
- package/dist/index.js +25 -0
- package/dist/kernel/components/blocks/AgentActions.d.ts +7 -7
- package/dist/kernel/components/blocks/AgentActions.js +1 -32
- package/dist/kernel/components/blocks/agent-actions.d.ts +1 -2
- package/dist/kernel/components/blocks/agent-actions.js +1 -32
- package/dist/kernel/components/blocks/animated-icon-swap.d.ts +2 -2
- package/dist/kernel/components/blocks/color-palette.d.ts +2 -2
- package/dist/kernel/components/blocks/logo-block.d.ts +2 -2
- package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +6 -5
- package/dist/kernel/components/blocks/nav-breadcrumb.js +19 -13
- package/dist/kernel/components/blocks/navbar.d.ts +5 -4
- package/dist/kernel/components/blocks/navbar.js +62 -42
- package/dist/kernel/components/blocks/navigation-progress.d.ts +2 -2
- package/dist/kernel/components/blocks/next-prev.d.ts +4 -4
- package/dist/kernel/components/blocks/next-prev.js +29 -6
- package/dist/kernel/components/blocks/page-list.d.ts +4 -4
- package/dist/kernel/components/blocks/page-list.js +30 -7
- package/dist/kernel/components/blocks/search.d.ts +9 -15
- package/dist/kernel/components/blocks/sidebar-node.d.ts +3 -5
- package/dist/kernel/components/blocks/sidebar-node.js +12 -2
- package/dist/kernel/components/blocks/sidebar.d.ts +6 -7
- package/dist/kernel/components/blocks/sidebar.js +132 -26
- package/dist/kernel/components/blocks/theme-toggle.d.ts +3 -4
- package/dist/kernel/components/blocks/toc-client.d.ts +7 -7
- package/dist/kernel/components/blocks/toc.d.ts +3 -3
- package/dist/kernel/components/mdx/components/blockquote.d.ts +2 -2
- package/dist/kernel/components/mdx/components/callout.d.ts +3 -3
- package/dist/kernel/components/mdx/components/code-block.d.ts +2 -2
- package/dist/kernel/components/mdx/components/copy-code-button.d.ts +2 -2
- package/dist/kernel/components/mdx/components/figure-caption.d.ts +2 -2
- package/dist/kernel/components/mdx/components/figure.d.ts +3 -3
- package/dist/kernel/components/mdx/components/footnotes.d.ts +2 -2
- package/dist/kernel/components/mdx/components/heading-copy-link.d.ts +2 -2
- package/dist/kernel/components/mdx/components/heading.d.ts +2 -2
- package/dist/kernel/components/mdx/components/hr.d.ts +4 -4
- package/dist/kernel/components/mdx/components/image-zoom.d.ts +3 -3
- package/dist/kernel/components/mdx/components/image-zoom.js +3 -2
- package/dist/kernel/components/mdx/components/image.d.ts +2 -2
- package/dist/kernel/components/mdx/components/image.js +1 -1
- package/dist/kernel/components/mdx/components/inline-code.d.ts +2 -2
- package/dist/kernel/components/mdx/components/kbd.d.ts +3 -3
- package/dist/kernel/components/mdx/components/link.d.ts +3 -3
- package/dist/kernel/components/mdx/components/paragraph.d.ts +2 -2
- package/dist/kernel/components/mdx/components/strikethrough.d.ts +2 -2
- package/dist/kernel/components/mdx/components/table.d.ts +7 -7
- package/dist/kernel/components/mdx/components/task-list.d.ts +3 -3
- package/dist/kernel/components/mdx/components/video-player.d.ts +2 -2
- package/dist/kernel/components/mdx/components/video.d.ts +2 -3
- package/dist/kernel/components/mdx/error-boundary.d.ts +2 -3
- package/dist/kernel/components/mdx/error-fallback.d.ts +2 -2
- package/dist/kernel/components/mdx/mdx.d.ts +42 -11
- package/dist/kernel/components/mdx/mdx.js +1 -1
- package/dist/kernel/components/next-image.d.ts +3 -3
- package/dist/kernel/components/ui/accordion.d.ts +7 -7
- package/dist/kernel/components/ui/breadcrumb-trail.d.ts +2 -3
- package/dist/kernel/components/ui/breadcrumb.d.ts +13 -13
- package/dist/kernel/components/ui/button-group.d.ts +5 -5
- package/dist/kernel/components/ui/button.d.ts +3 -3
- package/dist/kernel/components/ui/card.d.ts +7 -7
- package/dist/kernel/components/ui/dialog.d.ts +10 -10
- package/dist/kernel/components/ui/drawer.d.ts +12 -12
- package/dist/kernel/components/ui/kbd.d.ts +3 -3
- package/dist/kernel/components/ui/link.d.ts +3 -3
- package/dist/kernel/components/ui/menu.d.ts +22 -22
- package/dist/kernel/components/ui/mod-key.d.ts +2 -3
- package/dist/kernel/components/ui/navigation-menu.d.ts +15 -15
- package/dist/kernel/components/ui/popover.d.ts +11 -11
- package/dist/kernel/components/ui/progress.d.ts +7 -7
- package/dist/kernel/components/ui/scroll-area.d.ts +1 -2
- package/dist/kernel/components/ui/select.d.ts +16 -16
- package/dist/kernel/components/ui/separator.d.ts +3 -3
- package/dist/kernel/components/ui/table.d.ts +7 -7
- package/dist/kernel/components/ui/tabs.d.ts +12 -12
- package/dist/kernel/components/ui/tag.d.ts +3 -3
- package/dist/kernel/components/ui/text.d.ts +4 -4
- package/dist/kernel/components/ui/toast.d.ts +11 -11
- package/dist/kernel/components/ui/tooltip.d.ts +9 -9
- package/dist/kernel/defaults.registry.d.ts +2 -2
- package/dist/kernel/defaults.registry.js +2 -2
- package/dist/kernel/routes/agent/markdown.d.ts +4 -0
- package/dist/kernel/routes/agent/markdown.js +11 -7
- package/dist/kernel/routes/layouts/group.d.ts +1 -1
- package/dist/kernel/routes/metadata/feed.d.ts +1 -2
- package/dist/kernel/routes/metadata/feed.js +34 -29
- package/dist/kernel/routes/metadata/llms.d.ts +3 -31
- package/dist/kernel/routes/metadata/llms.js +2 -200
- 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 +15 -1
- package/dist/kernel/routes/metadata/sitemap.js +200 -112
- package/dist/kernel/routes/not-found.js +1 -1
- package/dist/kernel/routes/pages/group.d.ts +3 -3
- package/dist/kernel/routes/pages/group.js +10 -4
- package/dist/kernel/routes/pages/leaf.d.ts +9 -4
- package/dist/kernel/routes/pages/leaf.js +16 -8
- package/dist/kernel/routes/pages/root.d.ts +3 -3
- package/dist/kernel/routes/pages/root.js +20 -5
- package/dist/keyboard-shortcuts.d.ts +2 -2
- 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/search.d.ts +2 -9
- package/dist/search.js +2 -7
- package/dist/sidebar-types-CMrMM0Nj.d.ts +105 -0
- package/dist/theme-scope.d.ts +2 -2
- package/dist/types-BbqmOzLC.d.ts +168 -0
- package/dist/types-TjH1ek0X.d.ts +22 -0
- package/dist/types.d.ts +116 -91
- package/dist/{validate-local-source-SV44DBQT.js → validate-local-source-EN2GW4FR.js} +3 -3
- package/dist/{validate-publish-os-source-NZJF3TB2.js → validate-publish-os-source-OEWQDB7B.js} +2 -2
- package/init/seed/minimal/publish.os/blog/index.tsx +25 -22
- package/init/seed/minimal/publish.os/blog/page.tsx +31 -16
- package/init/seed/minimal/publish.os/components/blog/copy-link.tsx +1 -1
- package/init/seed/minimal/publish.os/components/blog/frontmatter.ts +41 -0
- package/init/seed/minimal/publish.os/publish.os.config.json +13 -2
- package/init/seed/minimal/seed.json +1 -0
- package/init/seed/scratch/publish.os/publish.os.config.json +1 -1
- package/package.json +4 -4
- package/release/app/phosphor/package.json +1 -1
- package/release/app/phosphor/pnpm-lock.yaml.template +53 -127
- package/release/compiler/pnpm-lock.yaml.template +49 -123
- package/release/release.json +4 -4
- package/dist/sidebar-types-DKpR27II.d.ts +0 -25
- package/dist/types-CJWRG05y.d.ts +0 -76
- package/dist/types-CUtrppxt.d.ts +0 -69
- package/dist/types-J2w_puPF.d.ts +0 -128
- package/dist/{button-BdCScEKi.d.ts → button-D6Xp2HBm.d.ts} +1 -1
|
@@ -4,14 +4,16 @@ import {
|
|
|
4
4
|
assertPublishOsSourceBoundary
|
|
5
5
|
} from "./chunk-7NY22B5U.js";
|
|
6
6
|
import {
|
|
7
|
+
FrontmatterParseError,
|
|
7
8
|
MdxIconImportError,
|
|
8
9
|
ROOT_CONTENT_GROUP_KEY,
|
|
9
10
|
ROOT_CONTENT_GROUP_TITLE,
|
|
10
|
-
UnsupportedFrontmatterLanguageError,
|
|
11
11
|
assertSourceDependency,
|
|
12
|
+
authoredDate,
|
|
12
13
|
buildFaviconsAndLogos,
|
|
13
14
|
buildMediaAssets,
|
|
14
15
|
collectBindingNames,
|
|
16
|
+
convertMdxToMarkdown,
|
|
15
17
|
groupedMdxExports,
|
|
16
18
|
joinUrlPath,
|
|
17
19
|
mdxComponentExportName,
|
|
@@ -23,7 +25,7 @@ import {
|
|
|
23
25
|
replacedMdxSourceFiles,
|
|
24
26
|
supportedNextjsSourceImports,
|
|
25
27
|
validateMdxIconImports
|
|
26
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-DX7PXFCM.js";
|
|
27
29
|
import {
|
|
28
30
|
isFrameworkNamespacePath
|
|
29
31
|
} from "./chunk-EFO6AWXN.js";
|
|
@@ -48,7 +50,15 @@ import {
|
|
|
48
50
|
|
|
49
51
|
// src/lib/compiler/validation/validate-publish-os-source.ts
|
|
50
52
|
import { readdir as readdir3, readFile as readFile5, stat as stat3 } from "fs/promises";
|
|
51
|
-
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";
|
|
52
62
|
import ts2 from "typescript";
|
|
53
63
|
|
|
54
64
|
// src/lib/compiler/source/diagnostics/diagnostic-store.ts
|
|
@@ -57,7 +67,7 @@ var diagnosticSchema = z.object({
|
|
|
57
67
|
code: z.string(),
|
|
58
68
|
level: z.union([z.literal("error"), z.literal("warning")]),
|
|
59
69
|
message: z.string(),
|
|
60
|
-
details: z.record(z.unknown()).optional()
|
|
70
|
+
details: z.record(z.string(), z.unknown()).optional()
|
|
61
71
|
}), DiagnosticStore = class {
|
|
62
72
|
diagnostics = [];
|
|
63
73
|
add(diagnostic) {
|
|
@@ -95,8 +105,13 @@ import { resolve } from "path";
|
|
|
95
105
|
|
|
96
106
|
// src/lib/compiler/source/config/format-config-issues.ts
|
|
97
107
|
function issueMessages(issue) {
|
|
98
|
-
return issue.code === "invalid_union" ? issue.
|
|
99
|
-
(
|
|
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
|
+
)
|
|
100
115
|
) : issue.code === "unrecognized_keys" ? issue.keys.map((key) => {
|
|
101
116
|
let path = [...issue.path, key].join(".");
|
|
102
117
|
return issue.path.join(".") === "media.assets" && [
|
|
@@ -115,60 +130,105 @@ function formatConfigIssues(error) {
|
|
|
115
130
|
// src/lib/compiler/source/config/publish-os-config-schema.ts
|
|
116
131
|
import { z as z4 } from "zod";
|
|
117
132
|
|
|
118
|
-
// src/lib/compiler/source/config/
|
|
133
|
+
// src/lib/compiler/source/config/metadata-schema.ts
|
|
119
134
|
import { z as z2 } from "zod";
|
|
120
|
-
var
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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 = {
|
|
124
147
|
title: z2.string().optional(),
|
|
125
148
|
description: z2.string().optional(),
|
|
126
|
-
image:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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();
|
|
132
165
|
|
|
133
166
|
// src/lib/compiler/source/config/publish-os-group-schema.ts
|
|
134
167
|
import { z as z3 } from "zod";
|
|
135
|
-
var
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}),
|
|
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", [
|
|
143
175
|
z3.object({
|
|
144
176
|
type: z3.literal("page"),
|
|
145
|
-
file: z3.string(),
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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(),
|
|
149
195
|
z3.object({
|
|
150
196
|
type: z3.literal("link"),
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}),
|
|
197
|
+
label: z3.string().min(1),
|
|
198
|
+
url: z3.string().min(1)
|
|
199
|
+
}).strict(),
|
|
155
200
|
z3.object({
|
|
156
|
-
type: z3.literal("
|
|
157
|
-
id: z3.string(),
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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({
|
|
172
232
|
path: z3.string().min(1),
|
|
173
233
|
scope: outputScopeSchema.optional(),
|
|
174
234
|
description: z3.string().optional(),
|
|
@@ -179,7 +239,6 @@ var treeNodeSchema = z3.lazy(
|
|
|
179
239
|
}), outputDefaultsDeclarationSchema = z3.object({
|
|
180
240
|
scope: outputScopeSchema.optional(),
|
|
181
241
|
limit: z3.number().int().positive().optional(),
|
|
182
|
-
status: z3.array(outputStatusSchema).optional(),
|
|
183
242
|
includeDescriptions: z3.boolean().optional()
|
|
184
243
|
}).strict(), feedsOutputSchema = z3.object({
|
|
185
244
|
rss: outputDeclarationSchema.optional(),
|
|
@@ -257,32 +316,25 @@ var httpsOriginSchema = z3.string().transform((value) => normalizeHttpsOrigin(va
|
|
|
257
316
|
]), fontConfigSchema = z3.record(
|
|
258
317
|
z3.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
|
|
259
318
|
fontRoleSchema
|
|
260
|
-
), groupConfigDocumentSchema = z3.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
outputs: outputsSchema.optional(),
|
|
274
|
-
outputDefaults: outputDefaultsSchema.optional()
|
|
275
|
-
}).strict()
|
|
276
|
-
);
|
|
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();
|
|
277
332
|
|
|
278
333
|
// src/lib/compiler/source/config/publish-os-config-schema.ts
|
|
279
334
|
var siteSchema = z4.object({
|
|
280
335
|
name: z4.string().optional(),
|
|
281
336
|
description: z4.string().optional()
|
|
282
|
-
}).strict(), runtimePackageMessage = 'Set runtime to "@publish.os/nextjs@<exact-version>". Replace <exact-version> with the exact framework version for this source.', runtimeSchema = z4.string({
|
|
283
|
-
required_error: runtimePackageMessage,
|
|
284
|
-
invalid_type_error: runtimePackageMessage
|
|
285
|
-
}).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, {
|
|
286
338
|
message: runtimePackageMessage
|
|
287
339
|
}), rootThemeSchema = z4.object({
|
|
288
340
|
defaultMode: z4.enum(["system", "light", "dark"]).optional(),
|
|
@@ -293,7 +345,6 @@ var siteSchema = z4.object({
|
|
|
293
345
|
enabled: z4.boolean().optional(),
|
|
294
346
|
markdownRoutes: z4.boolean().optional(),
|
|
295
347
|
markdownAccept: z4.boolean().optional(),
|
|
296
|
-
plainTextAccept: z4.boolean().optional(),
|
|
297
348
|
agentDetection: z4.boolean().optional(),
|
|
298
349
|
discoveryHeaders: z4.boolean().optional(),
|
|
299
350
|
blockInternalMarkdownRoute: z4.boolean().optional()
|
|
@@ -307,11 +358,11 @@ var siteSchema = z4.object({
|
|
|
307
358
|
theme: rootThemeSchema.optional(),
|
|
308
359
|
fonts: fontConfigSchema.optional(),
|
|
309
360
|
media: mediaConfigSchema.optional(),
|
|
310
|
-
|
|
311
|
-
meta: z4.record(z4.unknown()).optional(),
|
|
361
|
+
metadata: indexMetadataSchema.optional(),
|
|
362
|
+
meta: z4.record(z4.string(), z4.unknown()).optional(),
|
|
312
363
|
outputs: outputsSchema.optional(),
|
|
313
364
|
outputDefaults: outputDefaultsSchema.optional(),
|
|
314
|
-
groups: z4.record(groupConfigDocumentSchema).default({})
|
|
365
|
+
groups: z4.record(z4.string(), groupConfigDocumentSchema).default({})
|
|
315
366
|
}).strict();
|
|
316
367
|
|
|
317
368
|
// src/lib/compiler/source/diagnostics/json-source-location.ts
|
|
@@ -600,9 +651,17 @@ async function discoverGroups(options) {
|
|
|
600
651
|
parent: options.parentKey
|
|
601
652
|
}
|
|
602
653
|
});
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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 = {
|
|
606
665
|
key,
|
|
607
666
|
localKey: normalized.localKey,
|
|
608
667
|
localFolderName: entry.name,
|
|
@@ -611,13 +670,7 @@ async function discoverGroups(options) {
|
|
|
611
670
|
layoutBoundary: normalized.layoutBoundary,
|
|
612
671
|
hasContent: await pathIsDirectory(join(folderPath, "content"))
|
|
613
672
|
};
|
|
614
|
-
groups.push(group
|
|
615
|
-
...await discoverGroups({
|
|
616
|
-
absoluteBase: options.absoluteBase,
|
|
617
|
-
folderPath,
|
|
618
|
-
parentKey: key
|
|
619
|
-
})
|
|
620
|
-
);
|
|
673
|
+
groups.push(group, ...children);
|
|
621
674
|
}
|
|
622
675
|
return groups;
|
|
623
676
|
}
|
|
@@ -717,7 +770,20 @@ async function collectGroup(options) {
|
|
|
717
770
|
localConfigPath,
|
|
718
771
|
key,
|
|
719
772
|
absoluteBase
|
|
720
|
-
), 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");
|
|
721
787
|
discovered.hasContent && await ensureDirectoryExists(contentPath, key, "content folder");
|
|
722
788
|
let existingPathOwner = paths.get(path);
|
|
723
789
|
if (existingPathOwner && !(path === "/" && existingPathOwner === ROOT_CONTENT_GROUP_KEY))
|
|
@@ -728,6 +794,7 @@ async function collectGroup(options) {
|
|
|
728
794
|
});
|
|
729
795
|
paths.set(path, key), groups[key] = {
|
|
730
796
|
...merged,
|
|
797
|
+
...localConfig?.navigation ? { localNavigation: localConfig.navigation } : {},
|
|
731
798
|
key,
|
|
732
799
|
slug,
|
|
733
800
|
path,
|
|
@@ -798,24 +865,55 @@ async function resolvePublishOsConfig(baseDir) {
|
|
|
798
865
|
|
|
799
866
|
// src/lib/compiler/source/content/scan-publish-os-content.ts
|
|
800
867
|
import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
801
|
-
import { extname, join as join2, relative as relative2 } from "path";
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
"
|
|
811
|
-
|
|
812
|
-
|
|
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
|
|
813
914
|
function fileSlug(relativePath) {
|
|
814
915
|
return relativePath.replace(/\\/g, "/").replace(/\.mdx$/i, "");
|
|
815
916
|
}
|
|
816
|
-
function asString(value) {
|
|
817
|
-
return typeof value == "string" && value.length > 0 ? value : void 0;
|
|
818
|
-
}
|
|
819
917
|
function normalizeContentSlug(value, absolutePath) {
|
|
820
918
|
let result = normalizeSlugPath(value);
|
|
821
919
|
if (result.errors.length > 0)
|
|
@@ -830,287 +928,153 @@ function normalizeContentSlug(value, absolutePath) {
|
|
|
830
928
|
});
|
|
831
929
|
return result.value;
|
|
832
930
|
}
|
|
833
|
-
function asDateString(value) {
|
|
834
|
-
if (typeof value == "string" && value.length > 0) return value;
|
|
835
|
-
if (value instanceof Date && !Number.isNaN(value.getTime()))
|
|
836
|
-
return value.toISOString().slice(0, 10);
|
|
837
|
-
}
|
|
838
|
-
function isIsoDate(value) {
|
|
839
|
-
return !Number.isNaN(Date.parse(value));
|
|
840
|
-
}
|
|
841
931
|
function warning(code, message, details) {
|
|
842
932
|
return { code, level: "warning", message, details };
|
|
843
933
|
}
|
|
844
|
-
function
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
function normalizeAuthorship(authors, absolutePath) {
|
|
856
|
-
let warnings = [];
|
|
857
|
-
if (authors === void 0)
|
|
858
|
-
return { authors: [], warnings };
|
|
859
|
-
if (!Array.isArray(authors))
|
|
860
|
-
return warnings.push(
|
|
861
|
-
warning(
|
|
862
|
-
"CONTENT_AUTHORS_INVALID",
|
|
863
|
-
`Ignoring invalid authors for ${absolutePath}`,
|
|
864
|
-
{
|
|
865
|
-
path: absolutePath
|
|
866
|
-
}
|
|
867
|
-
)
|
|
868
|
-
), { authors: [], warnings };
|
|
869
|
-
let normalized = [];
|
|
870
|
-
for (let author of authors) {
|
|
871
|
-
if (typeof author == "string" && author.length > 0) {
|
|
872
|
-
normalized.push({ name: author });
|
|
873
|
-
continue;
|
|
874
|
-
}
|
|
875
|
-
if (author && typeof author == "object" && typeof author.name == "string") {
|
|
876
|
-
let value = author;
|
|
877
|
-
normalized.push({
|
|
878
|
-
id: asString(value.id),
|
|
879
|
-
name: value.name,
|
|
880
|
-
handle: asString(value.handle),
|
|
881
|
-
avatar: asString(value.avatar),
|
|
882
|
-
url: asString(value.url)
|
|
883
|
-
});
|
|
884
|
-
continue;
|
|
885
|
-
}
|
|
886
|
-
warnings.push(
|
|
887
|
-
warning(
|
|
888
|
-
"CONTENT_AUTHOR_INVALID",
|
|
889
|
-
`Ignoring invalid author entry for ${absolutePath}`,
|
|
890
|
-
{
|
|
891
|
-
path: absolutePath
|
|
892
|
-
}
|
|
893
|
-
)
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
|
-
return { authors: normalized, warnings };
|
|
897
|
-
}
|
|
898
|
-
function normalizeTags(tags, absolutePath) {
|
|
899
|
-
return tags === void 0 ? { tags: [], warnings: [] } : Array.isArray(tags) ? {
|
|
900
|
-
tags: tags.filter((tag) => typeof tag == "string"),
|
|
901
|
-
warnings: []
|
|
902
|
-
} : {
|
|
903
|
-
tags: [],
|
|
904
|
-
warnings: [
|
|
905
|
-
warning(
|
|
906
|
-
"CONTENT_TAGS_INVALID",
|
|
907
|
-
`Ignoring invalid tags for ${absolutePath}`,
|
|
908
|
-
{
|
|
909
|
-
path: absolutePath
|
|
910
|
-
}
|
|
911
|
-
)
|
|
912
|
-
]
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
function normalizeOgMetadata(value, absolutePath) {
|
|
916
|
-
if (value === void 0) return;
|
|
917
|
-
if (!value || typeof value != "object" || Array.isArray(value))
|
|
918
|
-
throw new AppError2({
|
|
919
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
920
|
-
message: `Invalid og frontmatter for ${absolutePath}; expected an object with title, description, or image`,
|
|
921
|
-
details: { path: absolutePath, field: "og" }
|
|
922
|
-
});
|
|
923
|
-
let source = value, allowedFields = /* @__PURE__ */ new Set(["title", "description", "image"]);
|
|
924
|
-
for (let field of Object.keys(source))
|
|
925
|
-
if (!allowedFields.has(field))
|
|
926
|
-
throw new AppError2({
|
|
927
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
928
|
-
message: `Invalid og frontmatter field "${field}" for ${absolutePath}`,
|
|
929
|
-
details: { path: absolutePath, field: `og.${field}` }
|
|
930
|
-
});
|
|
931
|
-
let og = {};
|
|
932
|
-
if (source.title !== void 0) {
|
|
933
|
-
if (typeof source.title != "string" || source.title.length === 0)
|
|
934
|
-
throw new AppError2({
|
|
935
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
936
|
-
message: `Invalid og.title for ${absolutePath}; expected a non-empty string`,
|
|
937
|
-
details: { path: absolutePath, field: "og.title" }
|
|
938
|
-
});
|
|
939
|
-
og.title = source.title;
|
|
940
|
-
}
|
|
941
|
-
if (source.description !== void 0) {
|
|
942
|
-
if (typeof source.description != "string" || source.description.length === 0)
|
|
943
|
-
throw new AppError2({
|
|
944
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
945
|
-
message: `Invalid og.description for ${absolutePath}; expected a non-empty string`,
|
|
946
|
-
details: { path: absolutePath, field: "og.description" }
|
|
947
|
-
});
|
|
948
|
-
og.description = source.description;
|
|
949
|
-
}
|
|
950
|
-
if (source.image !== void 0)
|
|
951
|
-
if (typeof source.image == "string" && source.image.length > 0)
|
|
952
|
-
og.image = source.image;
|
|
953
|
-
else if (source.image && typeof source.image == "object" && !Array.isArray(source.image)) {
|
|
954
|
-
let image = source.image, allowedImageFields = /* @__PURE__ */ new Set(["src", "alt"]);
|
|
955
|
-
for (let field of Object.keys(image))
|
|
956
|
-
if (!allowedImageFields.has(field))
|
|
957
|
-
throw new AppError2({
|
|
958
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
959
|
-
message: `Invalid og.image field "${field}" for ${absolutePath}`,
|
|
960
|
-
details: { path: absolutePath, field: `og.image.${field}` }
|
|
961
|
-
});
|
|
962
|
-
if (typeof image.src != "string" || image.src.length === 0)
|
|
963
|
-
throw new AppError2({
|
|
964
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
965
|
-
message: `Invalid og.image.src for ${absolutePath}; expected a non-empty string`,
|
|
966
|
-
details: { path: absolutePath, field: "og.image.src" }
|
|
967
|
-
});
|
|
968
|
-
if (image.alt !== void 0 && typeof image.alt != "string")
|
|
969
|
-
throw new AppError2({
|
|
970
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
971
|
-
message: `Invalid og.image.alt for ${absolutePath}; expected a string`,
|
|
972
|
-
details: { path: absolutePath, field: "og.image.alt" }
|
|
973
|
-
});
|
|
974
|
-
og.image = {
|
|
975
|
-
src: image.src,
|
|
976
|
-
...typeof image.alt == "string" ? { alt: image.alt } : {}
|
|
977
|
-
};
|
|
978
|
-
} else
|
|
979
|
-
throw new AppError2({
|
|
980
|
-
code: "CONTENT_FRONTMATTER_OG_INVALID",
|
|
981
|
-
message: `Invalid og.image for ${absolutePath}; expected a non-empty string or object with src`,
|
|
982
|
-
details: { path: absolutePath, field: "og.image" }
|
|
983
|
-
});
|
|
984
|
-
return Object.keys(og).length > 0 ? og : void 0;
|
|
985
|
-
}
|
|
986
|
-
function customMeta(frontmatter, absolutePath) {
|
|
987
|
-
let meta = {}, warnings = [];
|
|
988
|
-
for (let [key, value] of Object.entries(frontmatter))
|
|
989
|
-
if (!RESERVED_FIELDS.has(key)) {
|
|
990
|
-
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)
|
|
991
945
|
warnings.push(
|
|
992
946
|
warning(
|
|
993
|
-
"
|
|
994
|
-
`
|
|
995
|
-
{ 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 }
|
|
996
950
|
)
|
|
997
951
|
);
|
|
998
|
-
continue;
|
|
999
|
-
}
|
|
1000
|
-
meta[key] = value;
|
|
1001
952
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
+
);
|
|
1014
969
|
if (!fileID)
|
|
1015
970
|
throw new AppError2({
|
|
1016
971
|
code: ERROR_CODES.CONTENT_FRONTMATTER_ID_INVALID,
|
|
1017
972
|
message: `Could not derive fileID for ${absolutePath}`,
|
|
1018
973
|
details: { path: absolutePath }
|
|
1019
974
|
});
|
|
1020
|
-
if (!
|
|
975
|
+
if (frontmatter.slug !== void 0 && (typeof frontmatter.slug != "string" || !frontmatter.slug.trim() || /[\\/]/.test(frontmatter.slug)))
|
|
1021
976
|
throw new AppError2({
|
|
1022
|
-
code: ERROR_CODES.
|
|
1023
|
-
message: `
|
|
1024
|
-
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
|
+
}
|
|
1025
984
|
});
|
|
1026
|
-
let
|
|
1027
|
-
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("/"))
|
|
1028
987
|
throw new AppError2({
|
|
1029
988
|
code: ERROR_CODES.CONTENT_SLUG_INVALID,
|
|
1030
|
-
message: `Invalid
|
|
989
|
+
message: `Invalid slug "${slugSource}" in ${absolutePath}; remove it or provide one valid path segment.`,
|
|
1031
990
|
details: {
|
|
1032
991
|
errors: slugResult.errors,
|
|
1033
992
|
path: absolutePath,
|
|
1034
|
-
|
|
993
|
+
field: "slug",
|
|
994
|
+
correction: "Remove slug or provide one valid path segment."
|
|
1035
995
|
}
|
|
1036
996
|
});
|
|
1037
|
-
let slug = slugResult.value;
|
|
1038
|
-
slugResult.changed && warnings.push(
|
|
997
|
+
let slug = slugResult.value, routeSlug = [...sourceSegments.slice(0, -1), slug].join("/");
|
|
998
|
+
if (slugResult.changed && warnings.push(
|
|
1039
999
|
warning(
|
|
1040
1000
|
"CONTENT_SLUG_NORMALIZED",
|
|
1041
1001
|
`Normalized slug "${slugSource}" to "${slug}" for ${absolutePath}`,
|
|
1042
1002
|
{ path: absolutePath, from: slugSource, to: slug }
|
|
1043
1003
|
)
|
|
1044
|
-
)
|
|
1045
|
-
|
|
1046
|
-
|
|
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(
|
|
1047
1016
|
warning(
|
|
1048
1017
|
"CONTENT_STATUS_INVALID",
|
|
1049
|
-
`
|
|
1050
|
-
{ path: absolutePath, status: frontmatter.status }
|
|
1051
|
-
)
|
|
1052
|
-
);
|
|
1053
|
-
let rawPublishedAt = asDateString(frontmatter.publishedAt), publishedAt = rawPublishedAt && isIsoDate(rawPublishedAt) ? rawPublishedAt : void 0;
|
|
1054
|
-
rawPublishedAt && !publishedAt && warnings.push(
|
|
1055
|
-
warning(
|
|
1056
|
-
"CONTENT_DATE_INVALID",
|
|
1057
|
-
`Invalid publishedAt for ${absolutePath}`,
|
|
1018
|
+
`Status "${String(frontmatter.status)}" is not recognized; this page will be published.`,
|
|
1058
1019
|
{
|
|
1059
1020
|
path: absolutePath,
|
|
1060
|
-
|
|
1061
|
-
|
|
1021
|
+
propertyPath: "status",
|
|
1022
|
+
correction: 'Set status to "draft" to keep the page unpublished, or "published" to publish it.'
|
|
1062
1023
|
}
|
|
1063
1024
|
)
|
|
1064
1025
|
);
|
|
1065
|
-
let
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
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: {
|
|
1071
1039
|
path: absolutePath,
|
|
1072
|
-
field
|
|
1073
|
-
|
|
1040
|
+
field,
|
|
1041
|
+
correction: `Correct or remove ${field}.`
|
|
1074
1042
|
}
|
|
1075
|
-
)
|
|
1076
|
-
|
|
1077
|
-
let
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
let resolved = {
|
|
1046
|
+
...frontmatter,
|
|
1047
|
+
title,
|
|
1048
|
+
description,
|
|
1049
|
+
slug,
|
|
1050
|
+
status,
|
|
1051
|
+
metadata: metadataResult.data
|
|
1052
|
+
};
|
|
1053
|
+
return {
|
|
1083
1054
|
fileID,
|
|
1084
1055
|
group: group.key,
|
|
1085
|
-
slug,
|
|
1086
|
-
|
|
1087
|
-
description: asString(frontmatter.description),
|
|
1088
|
-
og,
|
|
1056
|
+
slug: routeSlug,
|
|
1057
|
+
frontmatter: resolved,
|
|
1089
1058
|
status,
|
|
1090
|
-
publishedAt,
|
|
1091
|
-
updatedAt,
|
|
1092
|
-
authors: authors.authors,
|
|
1093
|
-
tags: tags.tags,
|
|
1094
|
-
meta: meta.meta,
|
|
1095
1059
|
absolutePath,
|
|
1096
1060
|
relativePath,
|
|
1097
|
-
body: parsed.content
|
|
1061
|
+
body: parsed.content,
|
|
1098
1062
|
warnings
|
|
1099
1063
|
};
|
|
1100
1064
|
} catch (error) {
|
|
1101
|
-
throw error instanceof AppError2 ? error :
|
|
1102
|
-
code: ERROR_CODES.CONTENT_FRONTMATTER_LANGUAGE_UNSUPPORTED,
|
|
1103
|
-
message: `Unsupported frontmatter language "${error.language}" for ${absolutePath}. Use YAML or JSON.`,
|
|
1104
|
-
details: { path: absolutePath, language: error.language }
|
|
1105
|
-
}) : new AppError2({
|
|
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(
|
|
@@ -1481,28 +1748,35 @@ async function preparePublishOsSource({
|
|
|
1481
1748
|
sourceRoot,
|
|
1482
1749
|
config,
|
|
1483
1750
|
content
|
|
1484
|
-
})
|
|
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
|
+
);
|
|
1485
1773
|
return {
|
|
1486
|
-
diagnostics: [
|
|
1487
|
-
...boundary.nestedSources.map((file) => ({
|
|
1488
|
-
code: "SOURCE_NESTED_EXCLUDED",
|
|
1489
|
-
level: "warning",
|
|
1490
|
-
message: "Nested publish.os source is excluded from this site.",
|
|
1491
|
-
details: { file }
|
|
1492
|
-
})),
|
|
1493
|
-
...content.warnings,
|
|
1494
|
-
...missingPages,
|
|
1495
|
-
...faviconsAndLogos.warnings,
|
|
1496
|
-
...media.warnings.map((warning2) => ({
|
|
1497
|
-
...warning2,
|
|
1498
|
-
level: "warning"
|
|
1499
|
-
}))
|
|
1500
|
-
],
|
|
1774
|
+
diagnostics: [...sourceDiagnostics, ...navigationWarnings],
|
|
1501
1775
|
source: {
|
|
1502
1776
|
assets: { faviconsAndLogos, media },
|
|
1503
1777
|
config,
|
|
1504
1778
|
content,
|
|
1505
|
-
manifest
|
|
1779
|
+
manifest
|
|
1506
1780
|
}
|
|
1507
1781
|
};
|
|
1508
1782
|
} catch (error) {
|
|
@@ -1520,12 +1794,12 @@ async function preparePublishOsSource({
|
|
|
1520
1794
|
|
|
1521
1795
|
// src/lib/compiler/theme/theme-token-diagnostics.ts
|
|
1522
1796
|
import { readdirSync, readFileSync } from "fs";
|
|
1523
|
-
import { extname as
|
|
1797
|
+
import { extname as extname3, join as join3, resolve as resolve6 } from "path";
|
|
1524
1798
|
import postcss from "postcss";
|
|
1525
1799
|
function cssFiles(directory) {
|
|
1526
1800
|
return readdirSync(directory, { withFileTypes: !0 }).flatMap((entry) => {
|
|
1527
1801
|
let path = join3(directory, entry.name);
|
|
1528
|
-
return entry.isDirectory() ? cssFiles(path) :
|
|
1802
|
+
return entry.isDirectory() ? cssFiles(path) : extname3(entry.name) === ".css" ? [path] : [];
|
|
1529
1803
|
});
|
|
1530
1804
|
}
|
|
1531
1805
|
function readDesignTokens() {
|
|
@@ -1586,17 +1860,19 @@ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
1586
1860
|
"GroupContext",
|
|
1587
1861
|
"LayoutProps",
|
|
1588
1862
|
"ManifestLike",
|
|
1589
|
-
"
|
|
1590
|
-
"
|
|
1591
|
-
"
|
|
1592
|
-
"
|
|
1593
|
-
"
|
|
1863
|
+
"NavigationContainerNode",
|
|
1864
|
+
"NavigationGroupNode",
|
|
1865
|
+
"NavigationLinkNode",
|
|
1866
|
+
"NavigationNode",
|
|
1867
|
+
"NavigationPageNode",
|
|
1868
|
+
"NavigationSwitchViewNode",
|
|
1869
|
+
"NavigationViewNode",
|
|
1594
1870
|
"PageData",
|
|
1595
1871
|
"PageProps",
|
|
1872
|
+
"ResolvedNavigation",
|
|
1596
1873
|
"RootLayoutProps",
|
|
1597
1874
|
"RootPageProps",
|
|
1598
|
-
"SiteData"
|
|
1599
|
-
"TreeNode"
|
|
1875
|
+
"SiteData"
|
|
1600
1876
|
]), ROUTE_EXECUTION_EXPORTS = [
|
|
1601
1877
|
"runtime",
|
|
1602
1878
|
"dynamic",
|
|
@@ -1646,7 +1922,7 @@ function normalizeLocalFontSourcePath(value) {
|
|
|
1646
1922
|
return value.replace(/^\/+/, "").replace(/^\.\//, "");
|
|
1647
1923
|
}
|
|
1648
1924
|
function isFontFilePath(path) {
|
|
1649
|
-
return FONT_FILE_EXTENSIONS.has(
|
|
1925
|
+
return FONT_FILE_EXTENSIONS.has(extname4(path).toLowerCase());
|
|
1650
1926
|
}
|
|
1651
1927
|
function isValidSourceFontFilePath(path) {
|
|
1652
1928
|
return path.startsWith("fonts/") && isFontFilePath(path);
|
|
@@ -1691,7 +1967,7 @@ async function validateConfiguredLocalFontSources({
|
|
|
1691
1967
|
return sourcePaths;
|
|
1692
1968
|
}
|
|
1693
1969
|
function isHardRejectedPath(path) {
|
|
1694
|
-
let name =
|
|
1970
|
+
let name = basename2(path);
|
|
1695
1971
|
return isReservedFrameworkPath(path) || path.split("/").some((segment) => ["packages", "scripts"].includes(segment)) || LOCKFILES.has(name) || name === "tsconfig.json" && path !== "tsconfig.json";
|
|
1696
1972
|
}
|
|
1697
1973
|
function isAllowedBareImport(specifier) {
|
|
@@ -1793,7 +2069,7 @@ function importDetails(options) {
|
|
|
1793
2069
|
};
|
|
1794
2070
|
}
|
|
1795
2071
|
function scriptKindForPath(path) {
|
|
1796
|
-
switch (
|
|
2072
|
+
switch (extname4(path).toLowerCase()) {
|
|
1797
2073
|
case ".tsx":
|
|
1798
2074
|
return ts2.ScriptKind.TSX;
|
|
1799
2075
|
case ".jsx":
|
|
@@ -2032,7 +2308,7 @@ function validateSourceFilePath({
|
|
|
2032
2308
|
groupFolders,
|
|
2033
2309
|
store
|
|
2034
2310
|
}) {
|
|
2035
|
-
if (
|
|
2311
|
+
if (basename2(file) === "leaf.tsx" && (file === "leaf.tsx" || groupFolders.has(dirname(file))) || file.split("/").includes("(leaf)")) {
|
|
2036
2312
|
let suggestedPath = file.replaceAll("(leaf)", "(page)").replace(/leaf\.tsx$/, "page.tsx");
|
|
2037
2313
|
return addFileError(
|
|
2038
2314
|
store,
|
|
@@ -2059,7 +2335,7 @@ function validateSourceFontFile({
|
|
|
2059
2335
|
return isValidSourceFontFilePath(file) ? (configured || addFileWarning(
|
|
2060
2336
|
store,
|
|
2061
2337
|
"FRAMEWORK_FONT_FILE_UNUSED",
|
|
2062
|
-
`Font file "${
|
|
2338
|
+
`Font file "${basename2(file)}" is not used by the current font configuration and will not be included in the site.`,
|
|
2063
2339
|
file,
|
|
2064
2340
|
{ fontSourcePath: `/${file}`, suggestion: fontConfigHint(file) }
|
|
2065
2341
|
), !0) : (addFileWarning(
|
|
@@ -2069,7 +2345,7 @@ function validateSourceFontFile({
|
|
|
2069
2345
|
file,
|
|
2070
2346
|
{
|
|
2071
2347
|
fontSourcePath: `/${file}`,
|
|
2072
|
-
suggestion: fontConfigHint(`fonts/${
|
|
2348
|
+
suggestion: fontConfigHint(`fonts/${basename2(file)}`)
|
|
2073
2349
|
}
|
|
2074
2350
|
), !0);
|
|
2075
2351
|
}
|
|
@@ -2110,7 +2386,7 @@ async function validateSourceFileContents({
|
|
|
2110
2386
|
checkedCss,
|
|
2111
2387
|
store
|
|
2112
2388
|
}) {
|
|
2113
|
-
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/");
|
|
2114
2390
|
if (!validatesAttribution && !scansLegacyIcons) return;
|
|
2115
2391
|
let absolutePath = resolve7(publishOsDir, file), source = await readFile5(absolutePath, "utf-8");
|
|
2116
2392
|
validatesAttribution && validateAttributionReferences(source, file, store), extension === ".css" && (validateThemeTokenSpelling(source, file, store), await validateCssDependencies({
|
|
@@ -2337,10 +2613,18 @@ async function validateImportSpecifier(options) {
|
|
|
2337
2613
|
importDetails(options)
|
|
2338
2614
|
);
|
|
2339
2615
|
}
|
|
2340
|
-
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;
|
|
2341
2618
|
return {
|
|
2342
2619
|
...diagnostic,
|
|
2343
|
-
|
|
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
|
+
} : {},
|
|
2344
2628
|
line: typeof diagnostic.details?.line == "number" ? diagnostic.details.line : diagnostic.line,
|
|
2345
2629
|
column: typeof diagnostic.details?.column == "number" ? diagnostic.details.column : diagnostic.column
|
|
2346
2630
|
};
|
|
@@ -2355,7 +2639,8 @@ async function validatePublishOsSource(options) {
|
|
|
2355
2639
|
try {
|
|
2356
2640
|
let prepared = options.sourcePreparation ?? await preparePublishOsSource({
|
|
2357
2641
|
publishOsDir,
|
|
2358
|
-
sourceIdentity: options.sourceIdentity
|
|
2642
|
+
sourceIdentity: options.sourceIdentity,
|
|
2643
|
+
includeDrafts: options.includeDrafts
|
|
2359
2644
|
});
|
|
2360
2645
|
for (let diagnostic of prepared.diagnostics)
|
|
2361
2646
|
store.add(diagnostic);
|
|
@@ -2363,7 +2648,7 @@ async function validatePublishOsSource(options) {
|
|
|
2363
2648
|
} catch (error) {
|
|
2364
2649
|
error instanceof AppError ? store.error(error.code, error.message, error.details) : store.error("FRAMEWORK_VALIDATE_UNKNOWN", String(error));
|
|
2365
2650
|
}
|
|
2366
|
-
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;
|
|
2367
2652
|
return {
|
|
2368
2653
|
valid,
|
|
2369
2654
|
errors,
|