@janga/norna 0.7.16 → 0.7.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/astro.config.mjs +35 -3
- package/docs/README.md +10 -10
- package/docs/commands.md +2 -2
- package/docs/content.md +82 -102
- package/docs/design/navigation-and-theme-plan.md +115 -0
- package/docs/design/norna-diagram-design.md +9 -9
- package/docs/design/site-examples-structure.md +9 -9
- package/docs/engine-development.md +9 -9
- package/docs/getting-started.md +20 -15
- package/docs/images-and-metadata.md +10 -9
- package/docs/pages.md +134 -0
- package/docs/public-files.md +49 -36
- package/docs/publishing.md +19 -3
- package/docs/site-files.md +29 -28
- package/docs/sitewide-content.md +20 -36
- package/docs/theme.md +20 -20
- package/docs/typography.md +10 -10
- package/examples/README.md +2 -2
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/site.schema.json +17 -41
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/sitewide.schema.json +26 -67
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/theme.schema.json +185 -88
- package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -1
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev-local.json +4 -4
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.json +5 -5
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +33 -23
- package/examples/complete-sites/dog-shelter-multi-page/README.md +1 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/_metadata.json +8 -8
- package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +20 -20
- package/examples/complete-sites/dog-shelter-multi-page/site/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/{routes → pages}/010-dogs/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/{routes → pages}/020-adopt/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.yaml +2 -4
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/site.schema.json +17 -41
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/sitewide.schema.json +26 -67
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/theme.schema.json +185 -88
- package/examples/complete-sites/dog-shelter-single-page/.astro/data-store.json +1 -1
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev-local.json +8 -0
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.json +5 -5
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.log +16 -12
- package/examples/complete-sites/dog-shelter-single-page/README.md +1 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/_metadata.json +8 -8
- package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +12 -12
- package/examples/complete-sites/dog-shelter-single-page/site/content.md +5 -2
- package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.yaml +2 -4
- package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +17 -41
- package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +26 -67
- package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +185 -88
- package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -1
- package/examples/feature-demos/media-and-surfaces/.astro/dev.log +4 -14
- package/examples/feature-demos/media-and-surfaces/README.md +2 -2
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +9 -9
- package/examples/feature-demos/media-and-surfaces/site/.norna/generated-images.json +16 -16
- package/examples/feature-demos/media-and-surfaces/site/content.md +4 -7
- package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/010-media/content.md +6 -5
- package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/020-surfaces/content.md +7 -6
- package/examples/feature-demos/media-and-surfaces/site/sitewide-content.yaml +0 -2
- package/examples/feature-demos/sitewide-content/README.md +3 -3
- package/examples/feature-demos/sitewide-content/site/content.md +8 -7
- package/examples/feature-demos/sitewide-content/site/pages/010-logo-and-navigation/content.md +17 -0
- package/examples/feature-demos/sitewide-content/site/{routes → pages}/020-notices/content.md +7 -6
- package/examples/feature-demos/sitewide-content/site/{routes → pages}/030-footer/content.md +6 -5
- package/examples/feature-demos/sitewide-content/site/sitewide-content.yaml +4 -13
- package/examples/feature-demos/theme-presets/README.md +1 -1
- package/examples/feature-demos/theme-presets/site/.norna/generated-images.json +60 -60
- package/examples/feature-demos/theme-presets/site/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/010-portfolio/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/020-documentation/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/030-project/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/040-statement/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/sitewide-content.yaml +1 -10
- package/fixtures/basic/site/content.md +5 -6
- package/fixtures/basic/site/sitewide-content.yaml +1 -8
- package/fixtures/content-model-v2/site/content.md +11 -13
- package/fixtures/content-model-v2/site/pages/010-guide/content.md +18 -0
- package/fixtures/nested-pages/site/.norna/generated-images.json +10 -0
- package/fixtures/nested-pages/site/config.yaml +1 -0
- package/fixtures/nested-pages/site/content.md +15 -0
- package/fixtures/nested-pages/site/pages/010-guides/content.md +15 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/content.md +21 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/images/diagram.svg +21 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/pages/010-macos/content.md +14 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/020-workflows/content.md +11 -0
- package/fixtures/nested-pages/site/pages/010-guides/theme.yaml +3 -0
- package/fixtures/nested-pages/site/pages/020-reference/content.md +10 -0
- package/fixtures/nested-pages/site/pages/020-reference/pages/010-installation/content.md +11 -0
- package/fixtures/nested-pages/site/theme.yaml +3 -0
- package/package.json +7 -4
- package/schemas/config.schema.json +5 -5
- package/schemas/content-frontmatter.schema.json +28 -63
- package/schemas/manifest.json +1 -0
- package/schemas/sitewide-content.schema.json +58 -132
- package/schemas/theme.schema.json +396 -86
- package/scripts/check-config.mjs +6 -5
- package/scripts/deploy-site.mjs +6 -8
- package/scripts/doctor.mjs +2 -2
- package/scripts/generate-images.mjs +11 -18
- package/scripts/generate-schemas.mjs +3 -217
- package/scripts/lib/base-path-redirect.mjs +9 -0
- package/scripts/lib/editor-language-service.mjs +126 -66
- package/scripts/lib/heading-ids.mjs +161 -0
- package/scripts/lib/navigation-model.mjs +51 -0
- package/scripts/lib/norna-markdown-blocks.mjs +3 -4
- package/scripts/lib/page-model.mjs +83 -0
- package/scripts/lib/project-config.mjs +28 -0
- package/scripts/lib/schema-definitions.mjs +41 -46
- package/scripts/lib/schema-editor-metadata.mjs +475 -0
- package/scripts/lib/schema-value-definitions.mjs +6 -6
- package/scripts/lib/site-content.mjs +124 -87
- package/scripts/lib/site-paths.mjs +2 -2
- package/scripts/lib/theme-config.mjs +9 -16
- package/scripts/lib/theme-presets.mjs +9 -1
- package/scripts/release.mjs +1 -1
- package/scripts/show-typography.mjs +26 -20
- package/scripts/sync-content-sections.mjs +106 -65
- package/scripts/{test-temporary-visibility.mjs → test-banner-visibility.mjs} +7 -24
- package/scripts/test-client-javascript.mjs +39 -42
- package/scripts/test-content-check.mjs +138 -54
- package/scripts/test-content-model-v2.mjs +445 -228
- package/scripts/test-documentation.mjs +6 -1
- package/scripts/test-editor-language-service.mjs +168 -24
- package/scripts/test-engine-commands.mjs +2 -1
- package/scripts/test-heading-ids.mjs +68 -0
- package/scripts/test-navigation-model.mjs +63 -0
- package/scripts/test-nested-pages.mjs +110 -0
- package/scripts/test-package-check.mjs +31 -40
- package/scripts/test-page-model.mjs +80 -0
- package/scripts/test-project-config.mjs +25 -1
- package/scripts/test-schemas.mjs +66 -14
- package/scripts/test-theme-presets.mjs +33 -24
- package/src/components/NavigationPageTree.astro +68 -0
- package/src/components/SectionNavigationScript.astro +110 -5
- package/src/components/SiteBreadcrumbs.astro +29 -0
- package/src/components/SiteNavigation.astro +65 -69
- package/src/components/SitePage.astro +70 -46
- package/src/components/SiteSection.astro +27 -20
- package/src/content.config.ts +9 -5
- package/src/layouts/BaseLayout.astro +11 -20
- package/src/lib/pageThemes.ts +42 -0
- package/src/lib/sectionContent.ts +109 -106
- package/src/lib/siteNavigation.ts +48 -0
- package/src/lib/sitePages.ts +85 -38
- package/src/pages/{[slug].astro → [...slug].astro} +2 -2
- package/src/styles/global.css +373 -55
- package/starters/basic/README.md +5 -7
- package/starters/basic/site/content.md +7 -4
- package/starters/basic/site/sitewide-content.yaml +1 -10
- package/starters/project/README.md +8 -8
- package/starters/project/site/content.md +5 -4
- package/starters/project/site/{routes → pages}/010-guide/content.md +5 -4
- package/starters/project/site/sitewide-content.yaml +1 -10
- package/docs/routes.md +0 -121
- package/examples/feature-demos/sitewide-content/site/routes/010-identity/content.md +0 -20
- package/fixtures/content-model-v2/site/routes/010-guide/content.md +0 -17
- package/scripts/lib/route-model.mjs +0 -17
- package/scripts/test-route-model.mjs +0 -32
- package/src/lib/routeThemes.ts +0 -41
- /package/examples/complete-sites/dog-shelter-multi-page/site/images/{intro/shelter-dog.jpg → shelter-dog.jpg} +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/010-dogs/images/dogs → pages/010-dogs/images}/rex.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/010-dogs/images/dogs → pages/010-dogs/images}/rover.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/020-adopt/images/adopt → pages/020-adopt/images}/adoption-dog.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-single-page/site/images/{you-can-help/foster-dog.jpg → foster-dog.jpg} +0 -0
- /package/examples/complete-sites/dog-shelter-single-page/site/images/{what-we-do/rescue-dog.jpg → rescue-dog.jpg} +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-carousel.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-stack.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-surfaces.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-one.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-three.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-two.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/stack → pages/010-media/images}/stack-one.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/stack → pages/010-media/images}/stack-two.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/020-surfaces/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/010-portfolio/images/portfolio → pages/010-portfolio/images}/puppy-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/010-portfolio/images/portfolio → pages/010-portfolio/images}/shepherd-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/010-portfolio/images/portfolio → pages/010-portfolio/images}/white-puppy.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/010-portfolio/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/020-documentation/images/documentation → pages/020-documentation/images}/black-schnauzer.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/020-documentation/images/documentation → pages/020-documentation/images}/retriever-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/020-documentation/images/documentation → pages/020-documentation/images}/schnauzer-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/020-documentation/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/030-project/images/project → pages/030-project/images}/boxer-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/030-project/images/project → pages/030-project/images}/grass-puppy.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/030-project/images/project → pages/030-project/images}/grey-street-dog.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/030-project/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/040-statement/images/statement → pages/040-statement/images}/black-pedigree.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/040-statement/images/statement → pages/040-statement/images}/sheepdog-grass.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/040-statement/images/statement → pages/040-statement/images}/sheepdog-wide.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/040-statement/theme.yaml +0 -0
- /package/fixtures/basic/site/images/{work/.gitkeep → .gitkeep} +0 -0
- /package/fixtures/content-model-v2/site/images/{card-section/adopt.svg → adopt.svg} +0 -0
- /package/fixtures/content-model-v2/site/images/{intro/detail.jpg → detail.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{intro/duplicate.jpg → duplicate.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{card-section/foster.svg → foster.svg} +0 -0
- /package/fixtures/content-model-v2/site/images/{intro/hero.jpg → hero.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{plain/image.jpg → image.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{timed/slide-one.jpg → slide-one.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{timed/slide-three.jpg → slide-three.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{timed/slide-two.jpg → slide-two.jpg} +0 -0
- /package/fixtures/content-model-v2/site/{routes/010-guide/images/intro → pages/010-guide/images}/duplicate.jpg +0 -0
- /package/starters/basic/site/images/{work/.gitkeep → .gitkeep} +0 -0
|
@@ -4,26 +4,30 @@ import {
|
|
|
4
4
|
siteContentLabel,
|
|
5
5
|
siteContentPath,
|
|
6
6
|
siteDir,
|
|
7
|
+
siteDirLabel,
|
|
7
8
|
siteImagesDir,
|
|
8
9
|
siteImagesLabel,
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
sitePagesDir,
|
|
11
|
+
sitePagesLabel,
|
|
12
|
+
sitewideContentLabel,
|
|
11
13
|
} from './site-paths.mjs';
|
|
12
|
-
import {
|
|
14
|
+
import { parsePageDirectoryPath } from './page-model.mjs';
|
|
15
|
+
import { getMarkdownHeadings } from './heading-ids.mjs';
|
|
16
|
+
import { schemaTopLevelKeys } from './schema-definitions.mjs';
|
|
13
17
|
|
|
14
18
|
export const rasterImageExtensions = new Set(['.jpg', '.jpeg', '.png']);
|
|
15
19
|
export const staticImageExtensions = new Set(['.svg']);
|
|
16
20
|
export const supportedImageExtensions = new Set([...rasterImageExtensions, ...staticImageExtensions]);
|
|
17
21
|
|
|
18
|
-
const h2Regex = /^##\s+.*$/gm;
|
|
19
|
-
const explicitHeadingIdRegex = /\s*\{#([a-z0-9-]+)\}\s*$/;
|
|
20
22
|
const deprecatedInlineStyleReferenceRegex = /\[[^\]\n]+\]\{\.([a-z][a-z0-9-]*)\}/g;
|
|
21
23
|
const frontmatterDelimiterRegex = /^---\s*$/;
|
|
22
|
-
const knownConfigTopLevelFrontmatterKeys = new Set(
|
|
23
|
-
const knownContentTopLevelFrontmatterKeys = new Set(
|
|
24
|
-
const knownThemeTopLevelFrontmatterKeys = new Set(
|
|
25
|
-
const
|
|
26
|
-
|
|
24
|
+
const knownConfigTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.config);
|
|
25
|
+
const knownContentTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.content);
|
|
26
|
+
const knownThemeTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.theme);
|
|
27
|
+
const knownPageThemeTopLevelFrontmatterKeys = new Set(
|
|
28
|
+
[...knownThemeTopLevelFrontmatterKeys].filter((key) => key !== 'navigation'),
|
|
29
|
+
);
|
|
30
|
+
const knownSitewideTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.sitewide);
|
|
27
31
|
const knownNestedFrontmatterKeys = new Set([
|
|
28
32
|
'align',
|
|
29
33
|
'alt',
|
|
@@ -45,11 +49,10 @@ const knownNestedFrontmatterKeys = new Set([
|
|
|
45
49
|
'heading',
|
|
46
50
|
'headings',
|
|
47
51
|
'id',
|
|
48
|
-
'include',
|
|
49
52
|
'image',
|
|
50
53
|
'imageGap',
|
|
51
54
|
'images',
|
|
52
|
-
'
|
|
55
|
+
'listed',
|
|
53
56
|
'logo',
|
|
54
57
|
'lineHeight',
|
|
55
58
|
'maxAvailableHeightPercent',
|
|
@@ -63,7 +66,6 @@ const knownNestedFrontmatterKeys = new Set([
|
|
|
63
66
|
'preset',
|
|
64
67
|
'profile',
|
|
65
68
|
'rhythm',
|
|
66
|
-
'sections',
|
|
67
69
|
'sectionGap',
|
|
68
70
|
'sectionSurfaces',
|
|
69
71
|
'size',
|
|
@@ -81,46 +83,80 @@ export const toPosixPath = (filePath) => filePath.split(path.sep).join('/');
|
|
|
81
83
|
const fileExists = async (filePath) => access(filePath).then(() => true, () => false);
|
|
82
84
|
|
|
83
85
|
export const getContentFiles = async () => {
|
|
86
|
+
const legacyRoutesDir = path.join(siteDir, 'routes');
|
|
87
|
+
const legacyRoutes = await readdir(legacyRoutesDir).catch((error) => {
|
|
88
|
+
if (error?.code === 'ENOENT') return null;
|
|
89
|
+
throw error;
|
|
90
|
+
});
|
|
91
|
+
if (legacyRoutes !== null) {
|
|
92
|
+
throw new Error(`${siteDirLabel}/routes is no longer supported. Rename it to ${sitePagesLabel} and use NNN-page-id directory names.`);
|
|
93
|
+
}
|
|
94
|
+
|
|
84
95
|
const contentFiles = [{
|
|
85
96
|
contentLabel: siteContentLabel,
|
|
86
97
|
contentPath: siteContentPath,
|
|
87
98
|
imagesDir: siteImagesDir,
|
|
88
99
|
imagesLabel: siteImagesLabel,
|
|
89
100
|
isHome: true,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
pageDirectory: null,
|
|
102
|
+
pageDirectories: [],
|
|
103
|
+
pageId: null,
|
|
104
|
+
pageIds: [],
|
|
105
|
+
pageOrder: 0,
|
|
106
|
+
pageOrders: [],
|
|
107
|
+
pagePath: '',
|
|
108
|
+
parentPagePath: null,
|
|
109
|
+
depth: 0,
|
|
93
110
|
}];
|
|
94
|
-
const
|
|
111
|
+
const collectPageContentFiles = async (pagesDir, pagesLabel, parentPageDirectory = '') => {
|
|
112
|
+
const pageEntries = await readdir(pagesDir, { withFileTypes: true }).catch((error) => {
|
|
95
113
|
if (error?.code === 'ENOENT') {
|
|
96
114
|
return [];
|
|
97
115
|
}
|
|
98
116
|
|
|
99
117
|
throw error;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
for (const entry of routeEntries) {
|
|
103
|
-
if (!entry.isDirectory()) continue;
|
|
104
|
-
|
|
105
|
-
const routeDirectory = entry.name;
|
|
106
|
-
const routeDir = path.join(siteRoutesDir, routeDirectory);
|
|
107
|
-
const routeContentPath = path.join(routeDir, 'content.md');
|
|
118
|
+
});
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
for (const entry of pageEntries.sort((left, right) => left.name.localeCompare(right.name, 'en'))) {
|
|
121
|
+
if (!entry.isDirectory()) continue;
|
|
122
|
+
|
|
123
|
+
const pageDirectory = parentPageDirectory
|
|
124
|
+
? `${parentPageDirectory}/pages/${entry.name}`
|
|
125
|
+
: entry.name;
|
|
126
|
+
const pageLabel = `${pagesLabel}/${entry.name}`;
|
|
127
|
+
const pageDir = path.join(pagesDir, entry.name);
|
|
128
|
+
const pageContentPath = path.join(pageDir, 'content.md');
|
|
129
|
+
|
|
130
|
+
if (!(await fileExists(pageContentPath))) {
|
|
131
|
+
const childPageEntries = await readdir(path.join(pageDir, 'pages'), { withFileTypes: true }).catch((error) => {
|
|
132
|
+
if (error?.code === 'ENOENT') return [];
|
|
133
|
+
throw error;
|
|
134
|
+
});
|
|
135
|
+
if (childPageEntries.some((childEntry) => childEntry.isDirectory())) {
|
|
136
|
+
throw new Error(`${pageLabel} contains nested pages but has no content.md of its own.`);
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const pageMetadata = parsePageDirectoryPath(pageDirectory, pageLabel);
|
|
141
|
+
|
|
142
|
+
contentFiles.push({
|
|
143
|
+
contentLabel: `${pageLabel}/content.md`,
|
|
144
|
+
contentPath: pageContentPath,
|
|
145
|
+
imagesDir: path.join(pageDir, 'images'),
|
|
146
|
+
imagesLabel: `${pageLabel}/images`,
|
|
147
|
+
isHome: false,
|
|
148
|
+
...pageMetadata,
|
|
149
|
+
});
|
|
110
150
|
|
|
111
|
-
|
|
151
|
+
await collectPageContentFiles(
|
|
152
|
+
path.join(pageDir, 'pages'),
|
|
153
|
+
`${pageLabel}/pages`,
|
|
154
|
+
pageDirectory,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
112
158
|
|
|
113
|
-
|
|
114
|
-
contentLabel: `${siteRoutesLabel}/${routeDirectory}/content.md`,
|
|
115
|
-
contentPath: routeContentPath,
|
|
116
|
-
imagesDir: path.join(routeDir, 'images'),
|
|
117
|
-
imagesLabel: `${siteRoutesLabel}/${routeDirectory}/images`,
|
|
118
|
-
isHome: false,
|
|
119
|
-
routeDirectory,
|
|
120
|
-
routeId,
|
|
121
|
-
routeOrder,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
159
|
+
await collectPageContentFiles(sitePagesDir, sitePagesLabel);
|
|
124
160
|
|
|
125
161
|
return contentFiles;
|
|
126
162
|
};
|
|
@@ -129,7 +165,15 @@ export const splitSiteFile = (source, label = siteContentLabel) => {
|
|
|
129
165
|
const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
130
166
|
|
|
131
167
|
if (!match) {
|
|
132
|
-
|
|
168
|
+
if (/^---(?:\r?\n|$)/.test(source)) {
|
|
169
|
+
throw new Error(`${label} starts YAML frontmatter with ---, but no closing --- delimiter was found.`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
frontmatter: '',
|
|
174
|
+
frontmatterBody: '',
|
|
175
|
+
body: source,
|
|
176
|
+
};
|
|
133
177
|
}
|
|
134
178
|
|
|
135
179
|
return {
|
|
@@ -250,10 +294,24 @@ export const validateFrontmatterStructure = (frontmatter, addIssue, {
|
|
|
250
294
|
if (knownTopLevelFrontmatterKeys.has(key)) continue;
|
|
251
295
|
|
|
252
296
|
let fix;
|
|
253
|
-
|
|
297
|
+
let message = `Frontmatter line ${lineNumber} defines "${key}" at the top level, but it is not a valid top-level ${fileKind} field.`;
|
|
298
|
+
if (fileKind === 'content' && key === 'title') {
|
|
299
|
+
fix = 'Remove "title:" and write the page title as the single Markdown H1 after frontmatter, for example "# About".';
|
|
300
|
+
} else if (fileKind === 'content' && key === 'description') {
|
|
301
|
+
fix = 'Indent "description:" under "page:". The optional meta description belongs in the page object.';
|
|
302
|
+
} else if (fileKind === 'content' && key === 'images') {
|
|
254
303
|
fix = 'Put local image references in norna-image-stack or norna-image-carousel blocks in the Markdown body.';
|
|
255
304
|
} else if (fileKind === 'content' && knownThemeTopLevelFrontmatterKeys.has(key)) {
|
|
256
305
|
fix = `Move "${key}:" to theme.yaml. Visual settings do not belong in content frontmatter.`;
|
|
306
|
+
} else if (fileKind === 'page theme' && key === 'navigation') {
|
|
307
|
+
message = `Frontmatter line ${lineNumber}: page themes may not define navigation.`;
|
|
308
|
+
fix = 'Set the site-wide navigation mode under "navigation:" in the root theme.yaml.';
|
|
309
|
+
} else if ((fileKind === 'theme' || fileKind === 'page theme') && ['logo', 'site'].includes(key)) {
|
|
310
|
+
message = `Frontmatter line ${lineNumber}: ${fileKind} may not define navigation logo settings. Optional logo display settings belong under "logo:" in ${sitewideContentLabel}.`;
|
|
311
|
+
fix = `Move only the logo display settings under "logo:" in ${sitewideContentLabel}; the homepage Markdown H1 supplies navigation text and logo alternative text.`;
|
|
312
|
+
} else if (fileKind === 'sitewide content' && (key === 'navigation' || key === 'site')) {
|
|
313
|
+
message = `Frontmatter line ${lineNumber}: "${key}:" no longer defines site identity.`;
|
|
314
|
+
fix = 'Use the homepage Markdown H1 for navigation text and logo alternative text. Move only an optional logo height to top-level "logo:".';
|
|
257
315
|
} else if (knownNestedFrontmatterKeys.has(key)) {
|
|
258
316
|
fix = `Indent "${key}:" under the object it belongs to.`;
|
|
259
317
|
} else {
|
|
@@ -262,7 +320,7 @@ export const validateFrontmatterStructure = (frontmatter, addIssue, {
|
|
|
262
320
|
|
|
263
321
|
addIssue({
|
|
264
322
|
severity: 'error',
|
|
265
|
-
message
|
|
323
|
+
message,
|
|
266
324
|
fix,
|
|
267
325
|
});
|
|
268
326
|
}
|
|
@@ -286,10 +344,10 @@ export const validateThemeYamlStructure = (frontmatter, addIssue) =>
|
|
|
286
344
|
fileKind: 'theme',
|
|
287
345
|
});
|
|
288
346
|
|
|
289
|
-
export const
|
|
347
|
+
export const validatePageThemeYamlStructure = (frontmatter, addIssue) =>
|
|
290
348
|
validateFrontmatterStructure(frontmatter, addIssue, {
|
|
291
|
-
knownTopLevelFrontmatterKeys:
|
|
292
|
-
fileKind: '
|
|
349
|
+
knownTopLevelFrontmatterKeys: knownPageThemeTopLevelFrontmatterKeys,
|
|
350
|
+
fileKind: 'page theme',
|
|
293
351
|
});
|
|
294
352
|
|
|
295
353
|
export const validateSitewideYamlStructure = (frontmatter, addIssue) =>
|
|
@@ -303,53 +361,32 @@ export const readThemeFile = async (sitePath) => {
|
|
|
303
361
|
return { frontmatter: source, frontmatterBody: source, body: '' };
|
|
304
362
|
};
|
|
305
363
|
|
|
306
|
-
export const getFrontmatterSections = (frontmatter) => {
|
|
307
|
-
const sections = [];
|
|
308
|
-
const lines = frontmatter.split(/\r?\n/);
|
|
309
|
-
let inSections = false;
|
|
310
|
-
|
|
311
|
-
for (const [index, line] of lines.entries()) {
|
|
312
|
-
if (/^sections:\s*$/.test(line)) {
|
|
313
|
-
inSections = true;
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (!inSections) continue;
|
|
318
|
-
if (/^[a-zA-Z0-9_-]+:/.test(line)) break;
|
|
319
|
-
|
|
320
|
-
const sectionMatch = line.match(/^\s{2}([a-z0-9-]+):(?:\s+.*)?\s*$/);
|
|
321
|
-
if (sectionMatch) {
|
|
322
|
-
sections.push({ id: sectionMatch[1], line: index + 1 });
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return sections;
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
export const getHeadingId = (heading) => heading.match(explicitHeadingIdRegex)?.[1];
|
|
330
|
-
|
|
331
364
|
export const getDeprecatedInlineStyleReferences = (body) => Array.from(body.matchAll(deprecatedInlineStyleReferenceRegex))
|
|
332
365
|
.map((match) => match[1]);
|
|
333
366
|
|
|
334
|
-
export const getBodySections = (body) => {
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
const start = match.index ?? 0;
|
|
342
|
-
const next = matches[index + 1];
|
|
343
|
-
const end = next?.index ?? body.length;
|
|
344
|
-
const text = body.slice(start, end).trimEnd();
|
|
345
|
-
const heading = match[0];
|
|
346
|
-
const id = getHeadingId(heading);
|
|
347
|
-
const line = body.slice(0, start).split(/\r?\n/).length;
|
|
348
|
-
|
|
349
|
-
sections.push({ id, heading, line, text });
|
|
350
|
-
}
|
|
367
|
+
export const getBodySections = async (body) => {
|
|
368
|
+
const { headings, source } = await getMarkdownHeadings(body);
|
|
369
|
+
const structuralHeadings = headings.filter((heading) => heading.depth <= 2);
|
|
370
|
+
const prelude = structuralHeadings.length > 0 ? source.slice(0, structuralHeadings[0].index) : source;
|
|
371
|
+
const sections = structuralHeadings.map((heading, index) => {
|
|
372
|
+
const next = structuralHeadings[index + 1];
|
|
373
|
+
const end = next?.index ?? source.length;
|
|
351
374
|
|
|
352
|
-
|
|
375
|
+
return {
|
|
376
|
+
...heading,
|
|
377
|
+
heading: heading.source,
|
|
378
|
+
isPageTitle: heading.depth === 1,
|
|
379
|
+
level: heading.depth,
|
|
380
|
+
text: source.slice(heading.index, end).trimEnd(),
|
|
381
|
+
};
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
headings,
|
|
386
|
+
pageHeadings: sections.filter((section) => section.isPageTitle),
|
|
387
|
+
prelude,
|
|
388
|
+
sections,
|
|
389
|
+
};
|
|
353
390
|
};
|
|
354
391
|
|
|
355
392
|
export const getImageFiles = async (directory) => {
|
|
@@ -119,7 +119,7 @@ export const siteThemePath = path.join(siteDir, 'theme.yaml');
|
|
|
119
119
|
export const siteContentPath = path.join(siteDir, 'content.md');
|
|
120
120
|
export const sitewideContentPath = path.join(siteDir, 'sitewide-content.yaml');
|
|
121
121
|
export const siteImagesDir = path.join(siteDir, 'images');
|
|
122
|
-
export const
|
|
122
|
+
export const sitePagesDir = path.join(siteDir, 'pages');
|
|
123
123
|
export const sitePublicDir = path.join(siteDir, 'public');
|
|
124
124
|
export const siteStateDir = path.join(siteDir, '.norna');
|
|
125
125
|
export const astroPublicDir = path.join(siteStateDir, 'public');
|
|
@@ -138,7 +138,7 @@ export const siteThemeLabel = getPathLabel(siteThemePath);
|
|
|
138
138
|
export const siteContentLabel = getPathLabel(siteContentPath);
|
|
139
139
|
export const sitewideContentLabel = getPathLabel(sitewideContentPath);
|
|
140
140
|
export const siteImagesLabel = getPathLabel(siteImagesDir);
|
|
141
|
-
export const
|
|
141
|
+
export const sitePagesLabel = getPathLabel(sitePagesDir);
|
|
142
142
|
export const sitePublicLabel = getPathLabel(sitePublicDir);
|
|
143
143
|
export const astroPublicLabel = getPathLabel(astroPublicDir);
|
|
144
144
|
export const generatedImagesManifestLabel = getPathLabel(generatedImagesManifestPath);
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { readFile, readdir } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
validatePageThemeYamlStructure,
|
|
5
5
|
validateThemeYamlStructure,
|
|
6
6
|
} from './site-content.mjs';
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
sitePagesDir,
|
|
9
|
+
sitePagesLabel,
|
|
10
10
|
siteThemeLabel,
|
|
11
11
|
siteThemePath,
|
|
12
|
-
sitewideContentLabel,
|
|
13
12
|
} from './site-paths.mjs';
|
|
14
13
|
import { resolveThemePresentation } from './presentation.mjs';
|
|
15
14
|
import { resolveThemeConfig } from './theme-presets.mjs';
|
|
@@ -26,16 +25,13 @@ export const readThemeConfig = async () => {
|
|
|
26
25
|
const config = parseYamlConfig(themeFile, siteThemeLabel, {
|
|
27
26
|
validateStructure: validateThemeYamlStructure,
|
|
28
27
|
});
|
|
29
|
-
if (Object.hasOwn(config, 'navigation')) {
|
|
30
|
-
throw new Error(`${siteThemeLabel} may not define navigation. The site label and logo settings belong in ${sitewideContentLabel}.`);
|
|
31
|
-
}
|
|
32
28
|
resolveThemeConfig(config, siteThemeLabel);
|
|
33
29
|
resolveThemePresentation(config, siteThemeLabel);
|
|
34
30
|
|
|
35
31
|
return config;
|
|
36
32
|
};
|
|
37
33
|
|
|
38
|
-
const
|
|
34
|
+
const getPageThemeFiles = async (directory, relativeDirectory = '') => {
|
|
39
35
|
const entries = await readdir(directory, { withFileTypes: true }).catch((error) => {
|
|
40
36
|
if (error?.code === 'ENOENT') return [];
|
|
41
37
|
throw error;
|
|
@@ -46,14 +42,14 @@ const getRouteThemeFiles = async (directory, relativeDirectory = '') => {
|
|
|
46
42
|
const relativePath = path.join(relativeDirectory, entry.name);
|
|
47
43
|
const absolutePath = path.join(directory, entry.name);
|
|
48
44
|
if (entry.isDirectory()) {
|
|
49
|
-
files.push(...await
|
|
45
|
+
files.push(...await getPageThemeFiles(absolutePath, relativePath));
|
|
50
46
|
continue;
|
|
51
47
|
}
|
|
52
48
|
|
|
53
49
|
if (entry.name === 'theme.yaml') {
|
|
54
50
|
files.push({
|
|
55
51
|
path: absolutePath,
|
|
56
|
-
label: `${
|
|
52
|
+
label: `${sitePagesLabel}/${relativePath.split(path.sep).join('/')}`,
|
|
57
53
|
});
|
|
58
54
|
}
|
|
59
55
|
}
|
|
@@ -61,18 +57,15 @@ const getRouteThemeFiles = async (directory, relativeDirectory = '') => {
|
|
|
61
57
|
return files;
|
|
62
58
|
};
|
|
63
59
|
|
|
64
|
-
export const
|
|
65
|
-
const files = await
|
|
60
|
+
export const validatePageThemeFiles = async () => {
|
|
61
|
+
const files = await getPageThemeFiles(sitePagesDir);
|
|
66
62
|
const configs = [];
|
|
67
63
|
|
|
68
64
|
for (const file of files) {
|
|
69
65
|
const source = await readFile(file.path, 'utf8');
|
|
70
66
|
const config = parseYamlConfig(source, file.label, {
|
|
71
|
-
validateStructure:
|
|
67
|
+
validateStructure: validatePageThemeYamlStructure,
|
|
72
68
|
});
|
|
73
|
-
if (Object.hasOwn(config, 'navigation')) {
|
|
74
|
-
throw new Error(`${file.label} may not define navigation. The site label and logo settings belong in ${sitewideContentLabel}.`);
|
|
75
|
-
}
|
|
76
69
|
resolveThemeConfig(config, file.label);
|
|
77
70
|
resolveThemePresentation(config, file.label);
|
|
78
71
|
|
|
@@ -2,6 +2,7 @@ import { mergeDeep } from './typography.mjs';
|
|
|
2
2
|
|
|
3
3
|
const themePresetConfigurations = Object.freeze({
|
|
4
4
|
portfolio: Object.freeze({
|
|
5
|
+
navigation: { mode: 'automatic' },
|
|
5
6
|
layout: {
|
|
6
7
|
density: 'normal',
|
|
7
8
|
pageWidth: '1240px',
|
|
@@ -24,6 +25,7 @@ const themePresetConfigurations = Object.freeze({
|
|
|
24
25
|
sectionSurfaces: ['base'],
|
|
25
26
|
}),
|
|
26
27
|
documentation: Object.freeze({
|
|
28
|
+
navigation: { mode: 'automatic' },
|
|
27
29
|
layout: {
|
|
28
30
|
density: 'compact',
|
|
29
31
|
pageWidth: '1240px',
|
|
@@ -46,6 +48,7 @@ const themePresetConfigurations = Object.freeze({
|
|
|
46
48
|
sectionSurfaces: ['base', 'soft'],
|
|
47
49
|
}),
|
|
48
50
|
project: Object.freeze({
|
|
51
|
+
navigation: { mode: 'automatic' },
|
|
49
52
|
layout: {
|
|
50
53
|
density: 'compact',
|
|
51
54
|
pageWidth: '1120px',
|
|
@@ -68,6 +71,7 @@ const themePresetConfigurations = Object.freeze({
|
|
|
68
71
|
sectionSurfaces: ['base', 'soft'],
|
|
69
72
|
}),
|
|
70
73
|
statement: Object.freeze({
|
|
74
|
+
navigation: { mode: 'automatic' },
|
|
71
75
|
layout: {
|
|
72
76
|
density: 'airy',
|
|
73
77
|
pageWidth: '1280px',
|
|
@@ -167,7 +171,7 @@ const responsiveValueLines = (label, value, indent = 2) => {
|
|
|
167
171
|
export const renderThemePresetReference = (presetName, sourceLabel = 'theme.yaml') => {
|
|
168
172
|
const preset = getThemePreset(presetName, sourceLabel);
|
|
169
173
|
const metadata = getThemePresetMetadata(presetName);
|
|
170
|
-
const { layout, images, typography, palette, sectionSurfaces } = preset;
|
|
174
|
+
const { navigation, layout, images, typography, palette, sectionSurfaces } = preset;
|
|
171
175
|
|
|
172
176
|
return [
|
|
173
177
|
`# Original values for Norna's "${presetName}" theme preset.`,
|
|
@@ -177,6 +181,10 @@ export const renderThemePresetReference = (presetName, sourceLabel = 'theme.yaml
|
|
|
177
181
|
`# Available theme presets: ${themePresetNames.join(', ')}.`,
|
|
178
182
|
`preset: ${presetName}`,
|
|
179
183
|
'',
|
|
184
|
+
'navigation:',
|
|
185
|
+
' # Alternatives: automatic, sections, top, tree.',
|
|
186
|
+
` mode: ${navigation.mode}`,
|
|
187
|
+
'',
|
|
180
188
|
'layout:',
|
|
181
189
|
' # Alternatives: compact, normal, airy.',
|
|
182
190
|
` density: ${layout.density}`,
|
package/scripts/release.mjs
CHANGED
|
@@ -129,7 +129,7 @@ if (showHelp || !releaseType) {
|
|
|
129
129
|
await run('git', ['add', ...releasePaths]);
|
|
130
130
|
await run('git', ['commit', '-m', `Release ${releaseTag}`]);
|
|
131
131
|
releaseCommitCreated = true;
|
|
132
|
-
await run('git', ['tag', releaseTag]);
|
|
132
|
+
await run('git', ['tag', '-a', releaseTag, '-m', `Release ${releaseTag}`]);
|
|
133
133
|
releaseTagCreated = true;
|
|
134
134
|
await run(npmBin, ['run', 'release:publish']);
|
|
135
135
|
await run('git', ['push', '--follow-tags']);
|
|
@@ -13,15 +13,17 @@ import {
|
|
|
13
13
|
readSiteFile,
|
|
14
14
|
validateContentFrontmatterStructure,
|
|
15
15
|
validateFrontmatterIndentation,
|
|
16
|
-
|
|
16
|
+
validatePageThemeYamlStructure,
|
|
17
17
|
} from './lib/site-content.mjs';
|
|
18
18
|
import {
|
|
19
19
|
siteContentLabel,
|
|
20
20
|
siteContentPath,
|
|
21
|
-
|
|
21
|
+
sitePagesDir,
|
|
22
|
+
sitePagesLabel,
|
|
22
23
|
siteThemeLabel,
|
|
23
24
|
siteThemePath,
|
|
24
25
|
} from './lib/site-paths.mjs';
|
|
26
|
+
import { getPageDirectoryAncestors } from './lib/page-model.mjs';
|
|
25
27
|
import { readThemeConfig } from './lib/theme-config.mjs';
|
|
26
28
|
import { resolveThemeConfig } from './lib/theme-presets.mjs';
|
|
27
29
|
import { parseYamlConfig } from './lib/yaml-config.mjs';
|
|
@@ -197,22 +199,26 @@ const readThemeTypography = async () => {
|
|
|
197
199
|
return resolveAnnotatedTypographyConfig(themeTypographyConfig ?? defaultTypography, siteThemeLabel);
|
|
198
200
|
};
|
|
199
201
|
|
|
200
|
-
const
|
|
202
|
+
const readPageThemeTypography = async (contentFile, themeTypography) => {
|
|
201
203
|
if (contentFile.isHome) return themeTypography;
|
|
202
204
|
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
205
|
+
for (const pageDirectory of getPageDirectoryAncestors(contentFile.pageDirectory).reverse()) {
|
|
206
|
+
const pageThemePath = path.join(sitePagesDir, pageDirectory, 'theme.yaml');
|
|
207
|
+
const pageThemeFile = await readFile(pageThemePath, 'utf8').catch((error) => {
|
|
208
|
+
if (error?.code === 'ENOENT') return null;
|
|
209
|
+
throw error;
|
|
210
|
+
});
|
|
211
|
+
if (!pageThemeFile) continue;
|
|
212
|
+
const pageThemeLabel = `${sitePagesLabel}/${pageDirectory}/theme.yaml`;
|
|
213
|
+
|
|
214
|
+
const pageThemeConfig = resolveThemeConfig(parseYamlConfig(pageThemeFile, pageThemeLabel, {
|
|
215
|
+
validateStructure: validatePageThemeYamlStructure,
|
|
216
|
+
}), pageThemeLabel);
|
|
217
|
+
const typographyConfig = pageThemeConfig.typography;
|
|
218
|
+
return resolveAnnotatedTypographyConfig(typographyConfig ?? defaultTypography, pageThemeLabel);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return themeTypography;
|
|
216
222
|
};
|
|
217
223
|
|
|
218
224
|
const readPageTypography = async (contentFile, siteThemeTypography) => {
|
|
@@ -227,8 +233,8 @@ const readPageTypography = async (contentFile, siteThemeTypography) => {
|
|
|
227
233
|
].join('\n'));
|
|
228
234
|
}
|
|
229
235
|
|
|
230
|
-
const pageTypography = await
|
|
231
|
-
const sections = getBodySections(body).sections
|
|
236
|
+
const pageTypography = await readPageThemeTypography(contentFile, siteThemeTypography);
|
|
237
|
+
const sections = (await getBodySections(body)).sections
|
|
232
238
|
.filter((section) => section.id)
|
|
233
239
|
.map((section) => {
|
|
234
240
|
return {
|
|
@@ -239,7 +245,7 @@ const readPageTypography = async (contentFile, siteThemeTypography) => {
|
|
|
239
245
|
|
|
240
246
|
return {
|
|
241
247
|
source: contentFile.contentLabel,
|
|
242
|
-
|
|
248
|
+
pathname: contentFile.isHome ? '/' : `/${contentFile.pagePath}/`,
|
|
243
249
|
pageTypography,
|
|
244
250
|
sections,
|
|
245
251
|
};
|
|
@@ -259,7 +265,7 @@ if (mode === 'profiles') {
|
|
|
259
265
|
typography: formatAnnotatedTypography(themeTypography),
|
|
260
266
|
},
|
|
261
267
|
pages: Object.fromEntries(pages.map((page) => [
|
|
262
|
-
page.
|
|
268
|
+
page.pathname,
|
|
263
269
|
{
|
|
264
270
|
source: page.source,
|
|
265
271
|
typography: formatAnnotatedTypography(page.pageTypography),
|