@janga/norna 0.7.16 → 0.7.18
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 +18 -9
- package/astro.config.mjs +35 -3
- package/bin/norna-cli.mjs +1 -1
- package/docs/README.md +16 -14
- package/docs/commands.md +2 -2
- package/docs/configuration.md +25 -2
- package/docs/content.md +92 -103
- package/docs/design/navigation-and-theme-plan.md +186 -0
- package/docs/design/norna-diagram-design.md +9 -9
- package/docs/design/site-examples-structure.md +15 -11
- package/docs/engine-development.md +19 -22
- package/docs/getting-started.md +21 -16
- package/docs/images-and-metadata.md +12 -9
- package/docs/pages.md +190 -0
- package/docs/public-files.md +49 -36
- package/docs/publishing.md +19 -3
- package/docs/site-files.md +46 -38
- package/docs/sitewide-content.md +20 -36
- package/docs/theme.md +109 -143
- package/docs/typography.md +19 -31
- package/examples/README.md +10 -4
- 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 → pages/000-home/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 → pages/000-home/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 +180 -91
- 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 → pages/000-home/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 +9 -7
- package/examples/feature-demos/media-and-surfaces/site/pages/020-surfaces/theme.yaml +2 -0
- 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/pages/000-home/content.md +17 -0
- 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-preset-documentation/README.md +16 -0
- package/examples/feature-demos/theme-preset-documentation/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/{theme-presets/site/routes/020-documentation → theme-preset-documentation/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-documentation/site/sitewide-content.yaml +3 -0
- package/examples/feature-demos/theme-preset-portfolio/README.md +16 -0
- package/examples/feature-demos/theme-preset-portfolio/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/theme-preset-portfolio/site/config.yaml +1 -0
- package/examples/feature-demos/{theme-presets/site/routes/010-portfolio → theme-preset-portfolio/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-portfolio/site/public/robots.txt +2 -0
- package/examples/feature-demos/theme-preset-portfolio/site/sitewide-content.yaml +3 -0
- package/examples/feature-demos/theme-preset-project/README.md +16 -0
- package/examples/feature-demos/theme-preset-project/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/theme-preset-project/site/config.yaml +1 -0
- package/examples/feature-demos/{theme-presets/site/routes/030-project → theme-preset-project/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-project/site/public/robots.txt +2 -0
- package/examples/feature-demos/theme-preset-project/site/sitewide-content.yaml +3 -0
- package/examples/feature-demos/theme-preset-statement/README.md +16 -0
- package/examples/feature-demos/theme-preset-statement/site/.norna/generated-images.json +74 -0
- package/examples/feature-demos/theme-preset-statement/site/config.yaml +1 -0
- package/examples/feature-demos/{theme-presets/site/routes/040-statement → theme-preset-statement/site/pages/000-home}/content.md +4 -7
- package/examples/feature-demos/theme-preset-statement/site/public/robots.txt +2 -0
- package/examples/feature-demos/theme-preset-statement/site/sitewide-content.yaml +3 -0
- package/fixtures/basic/site/{content.md → pages/000-home/content.md} +5 -6
- package/fixtures/basic/site/sitewide-content.yaml +1 -8
- package/fixtures/basic/site/theme.yaml +1 -8
- package/fixtures/content-model-v2/site/{content.md → pages/000-home/content.md} +11 -13
- package/fixtures/content-model-v2/site/pages/010-guide/content.md +18 -0
- package/fixtures/nested-pages/.astro/collections/site.schema.json +35 -0
- package/fixtures/nested-pages/.astro/collections/sitewide.schema.json +97 -0
- package/fixtures/nested-pages/.astro/collections/theme.schema.json +844 -0
- package/fixtures/nested-pages/.astro/content-assets.mjs +4 -0
- package/fixtures/nested-pages/.astro/content-modules.mjs +1 -0
- package/fixtures/nested-pages/.astro/content.d.ts +183 -0
- package/fixtures/nested-pages/.astro/data-store.json +1 -0
- package/fixtures/nested-pages/.astro/dev-local.json +8 -0
- package/fixtures/nested-pages/.astro/dev.log +1128 -0
- package/fixtures/nested-pages/.astro/settings.json +5 -0
- package/fixtures/nested-pages/.astro/types.d.ts +2 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/_metadata.json +74 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/package.json +3 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
- package/fixtures/nested-pages/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
- package/fixtures/nested-pages/site/.norna/generated-images.json +10 -0
- package/fixtures/nested-pages/site/config.yaml +3 -0
- package/fixtures/nested-pages/site/pages/000-home/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 +18 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/theme.yaml +2 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/020-workflows/content.md +11 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/030-release-notes/content.md +9 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/040-configuration/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/050-content/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/060-themes/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/070-images/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/080-cards/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/090-notes/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/100-validation/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/110-synchronization/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/120-building/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/130-publishing/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/140-troubleshooting/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/150-migration/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/160-accessibility/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/170-performance/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/180-security/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/190-localization/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/200-upgrading/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/210-faq/content.md +3 -0
- package/fixtures/nested-pages/site/pages/010-guides/theme.yaml +7 -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 +1 -0
- package/package.json +8 -4
- package/schemas/config.schema.json +46 -5
- package/schemas/content-frontmatter.schema.json +28 -63
- package/schemas/manifest.json +4 -2
- package/schemas/page-theme.schema.json +192 -0
- package/schemas/sitewide-content.schema.json +58 -132
- package/schemas/theme.schema.json +434 -152
- package/scripts/check-config.mjs +8 -6
- package/scripts/deploy-site.mjs +6 -8
- package/scripts/doctor.mjs +2 -2
- package/scripts/generate-images.mjs +15 -29
- package/scripts/generate-schemas.mjs +7 -218
- package/scripts/lib/base-path-redirect.mjs +9 -0
- package/scripts/lib/editor-language-service.mjs +125 -79
- package/scripts/lib/example-sites.mjs +15 -3
- 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 +102 -0
- package/scripts/lib/presentation.mjs +25 -17
- package/scripts/lib/project-config.mjs +46 -8
- package/scripts/lib/schema-definitions.mjs +78 -55
- package/scripts/lib/schema-editor-metadata.mjs +519 -0
- package/scripts/lib/schema-value-definitions.mjs +20 -6
- package/scripts/lib/site-content.mjs +159 -97
- package/scripts/lib/site-conventions.mjs +1 -0
- package/scripts/lib/site-paths.mjs +11 -5
- package/scripts/lib/theme-config.mjs +12 -18
- package/scripts/lib/theme-presets.mjs +55 -14
- package/scripts/release.mjs +1 -1
- package/scripts/show-typography.mjs +4 -30
- package/scripts/sync-content-sections.mjs +107 -68
- package/scripts/{test-temporary-visibility.mjs → test-banner-visibility.mjs} +8 -25
- package/scripts/test-client-javascript.mjs +50 -46
- package/scripts/test-content-check.mjs +153 -69
- package/scripts/test-content-model-v2.mjs +566 -285
- package/scripts/test-documentation.mjs +10 -3
- package/scripts/test-editor-language-service.mjs +172 -25
- package/scripts/test-engine-commands.mjs +6 -5
- package/scripts/test-examples.mjs +30 -1
- package/scripts/test-heading-ids.mjs +68 -0
- package/scripts/test-navigation-model.mjs +63 -0
- package/scripts/test-navigation.mjs +11 -2
- package/scripts/test-nested-pages.mjs +125 -0
- package/scripts/test-package-check.mjs +55 -95
- package/scripts/test-page-model.mjs +108 -0
- package/scripts/test-project-config.mjs +22 -3
- package/scripts/test-schemas.mjs +82 -15
- package/scripts/test-theme-presets.mjs +57 -42
- package/src/components/NavigationPageTree.astro +157 -0
- package/src/components/PageContentsLinks.astro +40 -0
- package/src/components/SectionNavigationScript.astro +117 -5
- package/src/components/SiteBreadcrumbs.astro +32 -0
- package/src/components/SiteNavigation.astro +97 -68
- package/src/components/SitePage.astro +93 -55
- package/src/components/SiteSection.astro +29 -27
- package/src/components/SiteTreeNavigation.astro +35 -0
- package/src/components/TreeNavigationScript.astro +83 -0
- package/src/content.config.ts +8 -10
- package/src/layouts/BaseLayout.astro +21 -20
- package/src/lib/pageThemes.ts +44 -0
- package/src/lib/sectionContent.ts +106 -107
- package/src/lib/siteNavigation.ts +49 -0
- package/src/lib/sitePages.ts +96 -38
- package/src/pages/{[slug].astro → [...slug].astro} +2 -2
- package/src/pages/index.astro +2 -1
- package/src/styles/global.css +743 -57
- package/starters/basic/README.md +9 -11
- package/starters/basic/site/{content.md → pages/000-home/content.md} +7 -4
- package/starters/basic/site/sitewide-content.yaml +1 -10
- package/starters/basic/site/theme.yaml +1 -105
- package/starters/project/README.md +14 -14
- package/starters/project/site/{content.md → pages/000-home/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/starters/project/site/theme.yaml +1 -31
- package/docs/routes.md +0 -121
- package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/theme.yaml +0 -3
- package/examples/feature-demos/sitewide-content/site/content.md +0 -16
- package/examples/feature-demos/sitewide-content/site/routes/010-identity/content.md +0 -20
- package/examples/feature-demos/theme-presets/README.md +0 -18
- package/examples/feature-demos/theme-presets/site/.norna/generated-images.json +0 -290
- package/examples/feature-demos/theme-presets/site/content.md +0 -17
- package/examples/feature-demos/theme-presets/site/sitewide-content.yaml +0 -12
- package/examples/feature-demos/theme-presets/site/theme.yaml +0 -1
- 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 → pages/000-home/images}/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 → pages/000-home/images}/foster-dog.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-single-page/site/{images/what-we-do → pages/000-home/images}/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/{theme-presets → theme-preset-documentation}/site/config.yaml +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation/images/documentation → theme-preset-documentation/site/pages/000-home/images}/black-schnauzer.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation/images/documentation → theme-preset-documentation/site/pages/000-home/images}/retriever-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation/images/documentation → theme-preset-documentation/site/pages/000-home/images}/schnauzer-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-documentation}/site/public/robots.txt +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/020-documentation → theme-preset-documentation/site}/theme.yaml +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio/images/portfolio → theme-preset-portfolio/site/pages/000-home/images}/puppy-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio/images/portfolio → theme-preset-portfolio/site/pages/000-home/images}/shepherd-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio/images/portfolio → theme-preset-portfolio/site/pages/000-home/images}/white-puppy.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/010-portfolio → theme-preset-portfolio/site}/theme.yaml +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/routeTheme.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/site.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/sitewide.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/collections/theme.schema.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/content-assets.mjs +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/content-modules.mjs +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/content.d.ts +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/data-store.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/dev-local.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/dev.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/dev.log +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/settings.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/.astro/types.d.ts +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/_metadata.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/embla-carousel.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/embla-carousel.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/package.json +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
- /package/examples/feature-demos/{theme-presets → theme-preset-project}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project/images/project → theme-preset-project/site/pages/000-home/images}/boxer-portrait.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project/images/project → theme-preset-project/site/pages/000-home/images}/grass-puppy.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project/images/project → theme-preset-project/site/pages/000-home/images}/grey-street-dog.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/030-project → theme-preset-project/site}/theme.yaml +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement/images/statement → theme-preset-statement/site/pages/000-home/images}/black-pedigree.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement/images/statement → theme-preset-statement/site/pages/000-home/images}/sheepdog-grass.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement/images/statement → theme-preset-statement/site/pages/000-home/images}/sheepdog-wide.jpg +0 -0
- /package/examples/feature-demos/{theme-presets/site/routes/040-statement → theme-preset-statement/site}/theme.yaml +0 -0
- /package/fixtures/basic/site/{images/work → pages/000-home/images}/.gitkeep +0 -0
- /package/fixtures/content-model-v2/site/{images/card-section → pages/000-home/images}/adopt.svg +0 -0
- /package/fixtures/content-model-v2/site/{images/intro → pages/000-home/images}/detail.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/intro → pages/000-home/images}/duplicate.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/card-section → pages/000-home/images}/foster.svg +0 -0
- /package/fixtures/content-model-v2/site/{images/intro → pages/000-home/images}/hero.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/plain → pages/000-home/images}/image.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/timed → pages/000-home/images}/slide-one.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/timed → pages/000-home/images}/slide-three.jpg +0 -0
- /package/fixtures/content-model-v2/site/{images/timed → pages/000-home/images}/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 → pages/000-home/images}/.gitkeep +0 -0
|
@@ -2,28 +2,31 @@ import { access, readdir, readFile } from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import {
|
|
4
4
|
siteContentLabel,
|
|
5
|
-
siteContentPath,
|
|
6
5
|
siteDir,
|
|
7
|
-
|
|
6
|
+
siteDirLabel,
|
|
7
|
+
homePageDirectory,
|
|
8
8
|
siteImagesLabel,
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
sitePagesDir,
|
|
10
|
+
sitePagesLabel,
|
|
11
|
+
sitewideContentLabel,
|
|
11
12
|
} from './site-paths.mjs';
|
|
12
|
-
import {
|
|
13
|
+
import { parsePageDirectoryPath } from './page-model.mjs';
|
|
14
|
+
import { getMarkdownHeadings } from './heading-ids.mjs';
|
|
15
|
+
import { schemaTopLevelKeys } from './schema-definitions.mjs';
|
|
13
16
|
|
|
14
17
|
export const rasterImageExtensions = new Set(['.jpg', '.jpeg', '.png']);
|
|
15
18
|
export const staticImageExtensions = new Set(['.svg']);
|
|
16
19
|
export const supportedImageExtensions = new Set([...rasterImageExtensions, ...staticImageExtensions]);
|
|
17
20
|
|
|
18
|
-
const h2Regex = /^##\s+.*$/gm;
|
|
19
|
-
const explicitHeadingIdRegex = /\s*\{#([a-z0-9-]+)\}\s*$/;
|
|
20
21
|
const deprecatedInlineStyleReferenceRegex = /\[[^\]\n]+\]\{\.([a-z][a-z0-9-]*)\}/g;
|
|
21
22
|
const frontmatterDelimiterRegex = /^---\s*$/;
|
|
22
|
-
const knownConfigTopLevelFrontmatterKeys = new Set(
|
|
23
|
-
const knownContentTopLevelFrontmatterKeys = new Set(
|
|
24
|
-
const knownThemeTopLevelFrontmatterKeys = new Set(
|
|
25
|
-
const
|
|
26
|
-
|
|
23
|
+
const knownConfigTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.config);
|
|
24
|
+
const knownContentTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.content);
|
|
25
|
+
const knownThemeTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.theme);
|
|
26
|
+
const knownPageThemeTopLevelFrontmatterKeys = new Set(
|
|
27
|
+
schemaTopLevelKeys.pageTheme,
|
|
28
|
+
);
|
|
29
|
+
const knownSitewideTopLevelFrontmatterKeys = new Set(schemaTopLevelKeys.sitewide);
|
|
27
30
|
const knownNestedFrontmatterKeys = new Set([
|
|
28
31
|
'align',
|
|
29
32
|
'alt',
|
|
@@ -31,7 +34,7 @@ const knownNestedFrontmatterKeys = new Set([
|
|
|
31
34
|
'blockGap',
|
|
32
35
|
'caption',
|
|
33
36
|
'carousel',
|
|
34
|
-
'
|
|
37
|
+
'contentSpacing',
|
|
35
38
|
'desktop',
|
|
36
39
|
'fontFamily',
|
|
37
40
|
'from',
|
|
@@ -45,11 +48,10 @@ const knownNestedFrontmatterKeys = new Set([
|
|
|
45
48
|
'heading',
|
|
46
49
|
'headings',
|
|
47
50
|
'id',
|
|
48
|
-
'include',
|
|
49
51
|
'image',
|
|
50
52
|
'imageGap',
|
|
51
53
|
'images',
|
|
52
|
-
'
|
|
54
|
+
'listed',
|
|
53
55
|
'logo',
|
|
54
56
|
'lineHeight',
|
|
55
57
|
'maxAvailableHeightPercent',
|
|
@@ -63,13 +65,15 @@ const knownNestedFrontmatterKeys = new Set([
|
|
|
63
65
|
'preset',
|
|
64
66
|
'profile',
|
|
65
67
|
'rhythm',
|
|
66
|
-
'sections',
|
|
67
68
|
'sectionGap',
|
|
68
|
-
'
|
|
69
|
+
'backgroundPattern',
|
|
70
|
+
'sections',
|
|
71
|
+
'shape',
|
|
69
72
|
'size',
|
|
70
73
|
'spacingAfter',
|
|
71
74
|
'spacingBefore',
|
|
72
75
|
'theme',
|
|
76
|
+
'textWidth',
|
|
73
77
|
'typography',
|
|
74
78
|
'until',
|
|
75
79
|
'visible',
|
|
@@ -81,45 +85,99 @@ export const toPosixPath = (filePath) => filePath.split(path.sep).join('/');
|
|
|
81
85
|
const fileExists = async (filePath) => access(filePath).then(() => true, () => false);
|
|
82
86
|
|
|
83
87
|
export const getContentFiles = async () => {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (error?.code === 'ENOENT') {
|
|
96
|
-
return [];
|
|
97
|
-
}
|
|
88
|
+
const legacyContentPath = path.join(siteDir, 'content.md');
|
|
89
|
+
const legacyImagesPath = path.join(siteDir, 'images');
|
|
90
|
+
const legacyPaths = [];
|
|
91
|
+
if (await fileExists(legacyContentPath)) legacyPaths.push(`${siteDirLabel}/content.md`);
|
|
92
|
+
if (await fileExists(legacyImagesPath)) legacyPaths.push(`${siteDirLabel}/images`);
|
|
93
|
+
if (legacyPaths.length > 0) {
|
|
94
|
+
throw new Error([
|
|
95
|
+
`The old root-page structure is no longer supported: ${legacyPaths.join(', ')}.`,
|
|
96
|
+
`Move the homepage content to ${sitePagesLabel}/${homePageDirectory}/content.md and its images to ${sitePagesLabel}/${homePageDirectory}/images/.`,
|
|
97
|
+
].join('\n'));
|
|
98
|
+
}
|
|
98
99
|
|
|
100
|
+
const legacyRoutesDir = path.join(siteDir, 'routes');
|
|
101
|
+
const legacyRoutes = await readdir(legacyRoutesDir).catch((error) => {
|
|
102
|
+
if (error?.code === 'ENOENT') return null;
|
|
99
103
|
throw error;
|
|
100
104
|
});
|
|
105
|
+
if (legacyRoutes !== null) {
|
|
106
|
+
throw new Error(`${siteDirLabel}/routes is no longer supported. Rename it to ${sitePagesLabel} and use NNN-page-id directory names.`);
|
|
107
|
+
}
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
const contentFiles = [];
|
|
110
|
+
const collectPageContentFiles = async (pagesDir, pagesLabel, parentPageDirectory = '') => {
|
|
111
|
+
const pageEntries = await readdir(pagesDir, { withFileTypes: true }).catch((error) => {
|
|
112
|
+
if (error?.code === 'ENOENT') {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
104
115
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const routeContentPath = path.join(routeDir, 'content.md');
|
|
116
|
+
throw error;
|
|
117
|
+
});
|
|
108
118
|
|
|
109
|
-
|
|
119
|
+
for (const entry of pageEntries.sort((left, right) => left.name.localeCompare(right.name, 'en'))) {
|
|
120
|
+
if (!entry.isDirectory()) continue;
|
|
121
|
+
|
|
122
|
+
const pageDirectory = parentPageDirectory
|
|
123
|
+
? `${parentPageDirectory}/pages/${entry.name}`
|
|
124
|
+
: entry.name;
|
|
125
|
+
const pageLabel = `${pagesLabel}/${entry.name}`;
|
|
126
|
+
const pageDir = path.join(pagesDir, entry.name);
|
|
127
|
+
const pageContentPath = path.join(pageDir, 'content.md');
|
|
128
|
+
|
|
129
|
+
if (!(await fileExists(pageContentPath))) {
|
|
130
|
+
const childPageEntries = await readdir(path.join(pageDir, 'pages'), { withFileTypes: true }).catch((error) => {
|
|
131
|
+
if (error?.code === 'ENOENT') return [];
|
|
132
|
+
throw error;
|
|
133
|
+
});
|
|
134
|
+
if (childPageEntries.some((childEntry) => childEntry.isDirectory())) {
|
|
135
|
+
throw new Error(`${pageLabel} contains nested pages but has no content.md of its own.`);
|
|
136
|
+
}
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const pageMetadata = parsePageDirectoryPath(pageDirectory, pageLabel);
|
|
140
|
+
const isHome = pageDirectory === homePageDirectory;
|
|
141
|
+
|
|
142
|
+
contentFiles.push({
|
|
143
|
+
contentLabel: `${pageLabel}/content.md`,
|
|
144
|
+
contentPath: pageContentPath,
|
|
145
|
+
imagesDir: path.join(pageDir, 'images'),
|
|
146
|
+
imagesLabel: `${pageLabel}/images`,
|
|
147
|
+
isHome,
|
|
148
|
+
...pageMetadata,
|
|
149
|
+
pagePath: isHome ? '' : pageMetadata.pagePath,
|
|
150
|
+
parentPagePath: isHome ? null : pageMetadata.parentPagePath,
|
|
151
|
+
});
|
|
110
152
|
|
|
111
|
-
|
|
153
|
+
if (isHome) {
|
|
154
|
+
const homePagesDir = path.join(pageDir, 'pages');
|
|
155
|
+
const homeChildEntries = await readdir(homePagesDir, { withFileTypes: true }).catch((error) => {
|
|
156
|
+
if (error?.code === 'ENOENT') return [];
|
|
157
|
+
throw error;
|
|
158
|
+
});
|
|
159
|
+
const homeChildDirectories = homeChildEntries.filter((childEntry) => childEntry.isDirectory());
|
|
160
|
+
if (homeChildDirectories.length > 0) {
|
|
161
|
+
throw new Error([
|
|
162
|
+
`${pageLabel} is the homepage and cannot contain child pages.`,
|
|
163
|
+
`Move these page directories beside ${homePageDirectory} under ${sitePagesLabel}/, or below another non-home page:`,
|
|
164
|
+
...homeChildDirectories.map(({ name }) => `- ${pageLabel}/pages/${name}`),
|
|
165
|
+
].join('\n'));
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
await collectPageContentFiles(
|
|
171
|
+
path.join(pageDir, 'pages'),
|
|
172
|
+
`${pageLabel}/pages`,
|
|
173
|
+
pageDirectory,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
112
177
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
imagesDir: path.join(routeDir, 'images'),
|
|
117
|
-
imagesLabel: `${siteRoutesLabel}/${routeDirectory}/images`,
|
|
118
|
-
isHome: false,
|
|
119
|
-
routeDirectory,
|
|
120
|
-
routeId,
|
|
121
|
-
routeOrder,
|
|
122
|
-
});
|
|
178
|
+
await collectPageContentFiles(sitePagesDir, sitePagesLabel);
|
|
179
|
+
if (!contentFiles.some(({ isHome }) => isHome)) {
|
|
180
|
+
throw new Error(`Homepage content is missing. Create ${siteContentLabel}.`);
|
|
123
181
|
}
|
|
124
182
|
|
|
125
183
|
return contentFiles;
|
|
@@ -129,7 +187,15 @@ export const splitSiteFile = (source, label = siteContentLabel) => {
|
|
|
129
187
|
const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
130
188
|
|
|
131
189
|
if (!match) {
|
|
132
|
-
|
|
190
|
+
if (/^---(?:\r?\n|$)/.test(source)) {
|
|
191
|
+
throw new Error(`${label} starts YAML frontmatter with ---, but no closing --- delimiter was found.`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
frontmatter: '',
|
|
196
|
+
frontmatterBody: '',
|
|
197
|
+
body: source,
|
|
198
|
+
};
|
|
133
199
|
}
|
|
134
200
|
|
|
135
201
|
return {
|
|
@@ -250,10 +316,27 @@ export const validateFrontmatterStructure = (frontmatter, addIssue, {
|
|
|
250
316
|
if (knownTopLevelFrontmatterKeys.has(key)) continue;
|
|
251
317
|
|
|
252
318
|
let fix;
|
|
253
|
-
|
|
319
|
+
let message = `Frontmatter line ${lineNumber} defines "${key}" at the top level, but it is not a valid top-level ${fileKind} field.`;
|
|
320
|
+
if (fileKind === 'content' && key === 'title') {
|
|
321
|
+
fix = 'Remove "title:" and write the page title as the single Markdown H1 after frontmatter, for example "# About".';
|
|
322
|
+
} else if (fileKind === 'content' && key === 'description') {
|
|
323
|
+
fix = 'Indent "description:" under "page:". The optional meta description belongs in the page object.';
|
|
324
|
+
} else if (fileKind === 'content' && key === 'images') {
|
|
254
325
|
fix = 'Put local image references in norna-image-stack or norna-image-carousel blocks in the Markdown body.';
|
|
255
326
|
} else if (fileKind === 'content' && knownThemeTopLevelFrontmatterKeys.has(key)) {
|
|
256
327
|
fix = `Move "${key}:" to theme.yaml. Visual settings do not belong in content frontmatter.`;
|
|
328
|
+
} else if ((fileKind === 'theme' || fileKind === 'page theme') && key === 'navigation') {
|
|
329
|
+
message = `Frontmatter line ${lineNumber}: navigation is technical, site-wide configuration and does not belong in ${fileKind}. Set it in config.yaml.`;
|
|
330
|
+
fix = 'Set the navigation mode under "navigation:" in config.yaml.';
|
|
331
|
+
} else if (fileKind === 'page theme' && ['preset', 'shape', 'palette', 'typography'].includes(key)) {
|
|
332
|
+
message = `Frontmatter line ${lineNumber}: page themes may not define site-wide visual identity through "${key}".`;
|
|
333
|
+
fix = `Move "${key}:" to the root theme.yaml. Page themes may set only layout.textWidth, layout.contentSpacing, images, and sections.backgroundPattern.`;
|
|
334
|
+
} else if ((fileKind === 'theme' || fileKind === 'page theme') && ['logo', 'site'].includes(key)) {
|
|
335
|
+
message = `Frontmatter line ${lineNumber}: ${fileKind} may not define navigation logo settings. Optional logo display settings belong under "logo:" in ${sitewideContentLabel}.`;
|
|
336
|
+
fix = `Move only the logo display settings under "logo:" in ${sitewideContentLabel}; the homepage Markdown H1 supplies navigation text and logo alternative text.`;
|
|
337
|
+
} else if (fileKind === 'sitewide content' && (key === 'navigation' || key === 'site')) {
|
|
338
|
+
message = `Frontmatter line ${lineNumber}: "${key}:" no longer defines site identity.`;
|
|
339
|
+
fix = 'Use the homepage Markdown H1 for navigation text and logo alternative text. Move only an optional logo height to top-level "logo:".';
|
|
257
340
|
} else if (knownNestedFrontmatterKeys.has(key)) {
|
|
258
341
|
fix = `Indent "${key}:" under the object it belongs to.`;
|
|
259
342
|
} else {
|
|
@@ -262,7 +345,7 @@ export const validateFrontmatterStructure = (frontmatter, addIssue, {
|
|
|
262
345
|
|
|
263
346
|
addIssue({
|
|
264
347
|
severity: 'error',
|
|
265
|
-
message
|
|
348
|
+
message,
|
|
266
349
|
fix,
|
|
267
350
|
});
|
|
268
351
|
}
|
|
@@ -286,10 +369,10 @@ export const validateThemeYamlStructure = (frontmatter, addIssue) =>
|
|
|
286
369
|
fileKind: 'theme',
|
|
287
370
|
});
|
|
288
371
|
|
|
289
|
-
export const
|
|
372
|
+
export const validatePageThemeYamlStructure = (frontmatter, addIssue) =>
|
|
290
373
|
validateFrontmatterStructure(frontmatter, addIssue, {
|
|
291
|
-
knownTopLevelFrontmatterKeys:
|
|
292
|
-
fileKind: '
|
|
374
|
+
knownTopLevelFrontmatterKeys: knownPageThemeTopLevelFrontmatterKeys,
|
|
375
|
+
fileKind: 'page theme',
|
|
293
376
|
});
|
|
294
377
|
|
|
295
378
|
export const validateSitewideYamlStructure = (frontmatter, addIssue) =>
|
|
@@ -303,53 +386,32 @@ export const readThemeFile = async (sitePath) => {
|
|
|
303
386
|
return { frontmatter: source, frontmatterBody: source, body: '' };
|
|
304
387
|
};
|
|
305
388
|
|
|
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
389
|
export const getDeprecatedInlineStyleReferences = (body) => Array.from(body.matchAll(deprecatedInlineStyleReferenceRegex))
|
|
332
390
|
.map((match) => match[1]);
|
|
333
391
|
|
|
334
|
-
export const getBodySections = (body) => {
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
392
|
+
export const getBodySections = async (body) => {
|
|
393
|
+
const { headings, source } = await getMarkdownHeadings(body);
|
|
394
|
+
const structuralHeadings = headings.filter((heading) => heading.depth <= 2);
|
|
395
|
+
const prelude = structuralHeadings.length > 0 ? source.slice(0, structuralHeadings[0].index) : source;
|
|
396
|
+
const sections = structuralHeadings.map((heading, index) => {
|
|
397
|
+
const next = structuralHeadings[index + 1];
|
|
398
|
+
const end = next?.index ?? source.length;
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
...heading,
|
|
402
|
+
heading: heading.source,
|
|
403
|
+
isPageTitle: heading.depth === 1,
|
|
404
|
+
level: heading.depth,
|
|
405
|
+
text: source.slice(heading.index, end).trimEnd(),
|
|
406
|
+
};
|
|
407
|
+
});
|
|
351
408
|
|
|
352
|
-
return {
|
|
409
|
+
return {
|
|
410
|
+
headings,
|
|
411
|
+
pageHeadings: sections.filter((section) => section.isPageTitle),
|
|
412
|
+
prelude,
|
|
413
|
+
sections,
|
|
414
|
+
};
|
|
353
415
|
};
|
|
354
416
|
|
|
355
417
|
export const getImageFiles = async (directory) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const homePageDirectory = '000-home';
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { homePageDirectory } from './site-conventions.mjs';
|
|
4
5
|
|
|
5
6
|
const siteDirectoryEnvName = 'NORNA_SITE_DIR';
|
|
6
7
|
const invocationRootEnvName = 'NORNA_INVOCATION_ROOT';
|
|
7
8
|
const defaultSiteDirectory = 'site';
|
|
9
|
+
export { homePageDirectory };
|
|
8
10
|
|
|
9
11
|
const currentFile = fileURLToPath(import.meta.url);
|
|
10
12
|
const currentDirectory = path.dirname(currentFile);
|
|
@@ -23,7 +25,10 @@ export const invocationRoot = configuredInvocationRoot
|
|
|
23
25
|
const hasSiteFilesInDirectory = (siteDir) => {
|
|
24
26
|
return (
|
|
25
27
|
existsSync(path.join(siteDir, 'config.yaml'))
|
|
26
|
-
&&
|
|
28
|
+
&& (
|
|
29
|
+
existsSync(path.join(siteDir, 'pages', homePageDirectory, 'content.md'))
|
|
30
|
+
|| existsSync(path.join(siteDir, 'content.md'))
|
|
31
|
+
)
|
|
27
32
|
);
|
|
28
33
|
};
|
|
29
34
|
|
|
@@ -116,10 +121,11 @@ const getPathLabel = (filePath) => {
|
|
|
116
121
|
export const siteDir = resolvedSitePaths.siteDir;
|
|
117
122
|
export const siteConfigPath = path.join(siteDir, 'config.yaml');
|
|
118
123
|
export const siteThemePath = path.join(siteDir, 'theme.yaml');
|
|
119
|
-
export const siteContentPath = path.join(siteDir, 'content.md');
|
|
120
124
|
export const sitewideContentPath = path.join(siteDir, 'sitewide-content.yaml');
|
|
121
|
-
export const
|
|
122
|
-
export const
|
|
125
|
+
export const sitePagesDir = path.join(siteDir, 'pages');
|
|
126
|
+
export const siteHomePageDir = path.join(sitePagesDir, homePageDirectory);
|
|
127
|
+
export const siteContentPath = path.join(siteHomePageDir, 'content.md');
|
|
128
|
+
export const siteImagesDir = path.join(siteHomePageDir, 'images');
|
|
123
129
|
export const sitePublicDir = path.join(siteDir, 'public');
|
|
124
130
|
export const siteStateDir = path.join(siteDir, '.norna');
|
|
125
131
|
export const astroPublicDir = path.join(siteStateDir, 'public');
|
|
@@ -138,7 +144,7 @@ export const siteThemeLabel = getPathLabel(siteThemePath);
|
|
|
138
144
|
export const siteContentLabel = getPathLabel(siteContentPath);
|
|
139
145
|
export const sitewideContentLabel = getPathLabel(sitewideContentPath);
|
|
140
146
|
export const siteImagesLabel = getPathLabel(siteImagesDir);
|
|
141
|
-
export const
|
|
147
|
+
export const sitePagesLabel = getPathLabel(sitePagesDir);
|
|
142
148
|
export const sitePublicLabel = getPathLabel(sitePublicDir);
|
|
143
149
|
export const astroPublicLabel = getPathLabel(astroPublicDir);
|
|
144
150
|
export const generatedImagesManifestLabel = getPathLabel(generatedImagesManifestPath);
|
|
@@ -1,18 +1,18 @@
|
|
|
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';
|
|
15
|
+
import { pageThemeSchema, themeVisualSchema } from './schema-definitions.mjs';
|
|
16
16
|
import { parseYamlConfig } from './yaml-config.mjs';
|
|
17
17
|
|
|
18
18
|
export const readThemeConfig = async () => {
|
|
@@ -24,18 +24,16 @@ export const readThemeConfig = async () => {
|
|
|
24
24
|
throw error;
|
|
25
25
|
});
|
|
26
26
|
const config = parseYamlConfig(themeFile, siteThemeLabel, {
|
|
27
|
+
schema: themeVisualSchema,
|
|
27
28
|
validateStructure: validateThemeYamlStructure,
|
|
28
29
|
});
|
|
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
30
|
resolveThemeConfig(config, siteThemeLabel);
|
|
33
31
|
resolveThemePresentation(config, siteThemeLabel);
|
|
34
32
|
|
|
35
33
|
return config;
|
|
36
34
|
};
|
|
37
35
|
|
|
38
|
-
const
|
|
36
|
+
const getPageThemeFiles = async (directory, relativeDirectory = '') => {
|
|
39
37
|
const entries = await readdir(directory, { withFileTypes: true }).catch((error) => {
|
|
40
38
|
if (error?.code === 'ENOENT') return [];
|
|
41
39
|
throw error;
|
|
@@ -46,14 +44,14 @@ const getRouteThemeFiles = async (directory, relativeDirectory = '') => {
|
|
|
46
44
|
const relativePath = path.join(relativeDirectory, entry.name);
|
|
47
45
|
const absolutePath = path.join(directory, entry.name);
|
|
48
46
|
if (entry.isDirectory()) {
|
|
49
|
-
files.push(...await
|
|
47
|
+
files.push(...await getPageThemeFiles(absolutePath, relativePath));
|
|
50
48
|
continue;
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
if (entry.name === 'theme.yaml') {
|
|
54
52
|
files.push({
|
|
55
53
|
path: absolutePath,
|
|
56
|
-
label: `${
|
|
54
|
+
label: `${sitePagesLabel}/${relativePath.split(path.sep).join('/')}`,
|
|
57
55
|
});
|
|
58
56
|
}
|
|
59
57
|
}
|
|
@@ -61,20 +59,16 @@ const getRouteThemeFiles = async (directory, relativeDirectory = '') => {
|
|
|
61
59
|
return files;
|
|
62
60
|
};
|
|
63
61
|
|
|
64
|
-
export const
|
|
65
|
-
const files = await
|
|
62
|
+
export const validatePageThemeFiles = async () => {
|
|
63
|
+
const files = await getPageThemeFiles(sitePagesDir);
|
|
66
64
|
const configs = [];
|
|
67
65
|
|
|
68
66
|
for (const file of files) {
|
|
69
67
|
const source = await readFile(file.path, 'utf8');
|
|
70
68
|
const config = parseYamlConfig(source, file.label, {
|
|
71
|
-
|
|
69
|
+
schema: pageThemeSchema,
|
|
70
|
+
validateStructure: validatePageThemeYamlStructure,
|
|
72
71
|
});
|
|
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
|
-
resolveThemeConfig(config, file.label);
|
|
77
|
-
resolveThemePresentation(config, file.label);
|
|
78
72
|
|
|
79
73
|
configs.push({ ...file, config });
|
|
80
74
|
}
|
|
@@ -2,9 +2,12 @@ import { mergeDeep } from './typography.mjs';
|
|
|
2
2
|
|
|
3
3
|
const themePresetConfigurations = Object.freeze({
|
|
4
4
|
portfolio: Object.freeze({
|
|
5
|
+
shape: 'square',
|
|
5
6
|
layout: {
|
|
6
|
-
|
|
7
|
+
contentSpacing: 'normal',
|
|
8
|
+
textWidth: 'wide',
|
|
7
9
|
pageWidth: '1240px',
|
|
10
|
+
localNavigationGap: 'clamp(1.5rem, 3vw, 3rem)',
|
|
8
11
|
gutter: {
|
|
9
12
|
desktop: 'clamp(1.25rem, 4vw, 3rem)',
|
|
10
13
|
mobile: '1rem',
|
|
@@ -21,12 +24,15 @@ const themePresetConfigurations = Object.freeze({
|
|
|
21
24
|
rhythm: 'normal',
|
|
22
25
|
},
|
|
23
26
|
palette: 'dark',
|
|
24
|
-
|
|
27
|
+
sections: { backgroundPattern: 'uniform' },
|
|
25
28
|
}),
|
|
26
29
|
documentation: Object.freeze({
|
|
30
|
+
shape: 'soft',
|
|
27
31
|
layout: {
|
|
28
|
-
|
|
32
|
+
contentSpacing: 'compact',
|
|
33
|
+
textWidth: 'narrow',
|
|
29
34
|
pageWidth: '1240px',
|
|
35
|
+
localNavigationGap: 'clamp(1.5rem, 3vw, 3rem)',
|
|
30
36
|
gutter: {
|
|
31
37
|
desktop: 'clamp(1.25rem, 4vw, 3rem)',
|
|
32
38
|
mobile: '1rem',
|
|
@@ -43,12 +49,15 @@ const themePresetConfigurations = Object.freeze({
|
|
|
43
49
|
rhythm: 'compact',
|
|
44
50
|
},
|
|
45
51
|
palette: 'paper',
|
|
46
|
-
|
|
52
|
+
sections: { backgroundPattern: 'alternating' },
|
|
47
53
|
}),
|
|
48
54
|
project: Object.freeze({
|
|
55
|
+
shape: 'soft',
|
|
49
56
|
layout: {
|
|
50
|
-
|
|
57
|
+
contentSpacing: 'compact',
|
|
58
|
+
textWidth: 'normal',
|
|
51
59
|
pageWidth: '1120px',
|
|
60
|
+
localNavigationGap: 'clamp(1.5rem, 3vw, 3rem)',
|
|
52
61
|
gutter: {
|
|
53
62
|
desktop: 'clamp(1.25rem, 4vw, 3rem)',
|
|
54
63
|
mobile: '1rem',
|
|
@@ -65,12 +74,15 @@ const themePresetConfigurations = Object.freeze({
|
|
|
65
74
|
rhythm: 'compact',
|
|
66
75
|
},
|
|
67
76
|
palette: 'light',
|
|
68
|
-
|
|
77
|
+
sections: { backgroundPattern: 'alternating' },
|
|
69
78
|
}),
|
|
70
79
|
statement: Object.freeze({
|
|
80
|
+
shape: 'square',
|
|
71
81
|
layout: {
|
|
72
|
-
|
|
82
|
+
contentSpacing: 'spacious',
|
|
83
|
+
textWidth: 'normal',
|
|
73
84
|
pageWidth: '1280px',
|
|
85
|
+
localNavigationGap: 'clamp(1.5rem, 3vw, 3rem)',
|
|
74
86
|
gutter: {
|
|
75
87
|
desktop: 'clamp(1.5rem, 5vw, 4rem)',
|
|
76
88
|
mobile: '1rem',
|
|
@@ -87,7 +99,7 @@ const themePresetConfigurations = Object.freeze({
|
|
|
87
99
|
rhythm: 'airy',
|
|
88
100
|
},
|
|
89
101
|
palette: 'paper',
|
|
90
|
-
|
|
102
|
+
sections: { backgroundPattern: 'cycling' },
|
|
91
103
|
}),
|
|
92
104
|
});
|
|
93
105
|
|
|
@@ -154,6 +166,30 @@ export const resolveThemeConfig = (theme = {}, sourceLabel = 'theme.yaml') => {
|
|
|
154
166
|
};
|
|
155
167
|
};
|
|
156
168
|
|
|
169
|
+
const mergePageThemePart = (base, override, keys) => Object.fromEntries(keys
|
|
170
|
+
.filter((key) => override?.[key] !== undefined || base?.[key] !== undefined)
|
|
171
|
+
.map((key) => [key, override?.[key] ?? base?.[key]]));
|
|
172
|
+
|
|
173
|
+
export const mergePageThemeConfig = (base = {}, override = {}) => ({
|
|
174
|
+
...base,
|
|
175
|
+
layout: {
|
|
176
|
+
...(base.layout ?? {}),
|
|
177
|
+
...mergePageThemePart(base.layout, override.layout, ['contentSpacing', 'textWidth']),
|
|
178
|
+
},
|
|
179
|
+
images: {
|
|
180
|
+
...(base.images ?? {}),
|
|
181
|
+
...mergePageThemePart(base.images, override.images, [
|
|
182
|
+
'width',
|
|
183
|
+
'maxAvailableWidthPercent',
|
|
184
|
+
'maxAvailableHeightPercent',
|
|
185
|
+
]),
|
|
186
|
+
},
|
|
187
|
+
sections: {
|
|
188
|
+
...(base.sections ?? {}),
|
|
189
|
+
...mergePageThemePart(base.sections, override.sections, ['backgroundPattern']),
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
157
193
|
const quote = (value) => JSON.stringify(value);
|
|
158
194
|
const responsiveValueLines = (label, value, indent = 2) => {
|
|
159
195
|
const prefix = ' '.repeat(indent);
|
|
@@ -167,7 +203,7 @@ const responsiveValueLines = (label, value, indent = 2) => {
|
|
|
167
203
|
export const renderThemePresetReference = (presetName, sourceLabel = 'theme.yaml') => {
|
|
168
204
|
const preset = getThemePreset(presetName, sourceLabel);
|
|
169
205
|
const metadata = getThemePresetMetadata(presetName);
|
|
170
|
-
const { layout, images, typography, palette,
|
|
206
|
+
const { shape, layout, images, typography, palette, sections } = preset;
|
|
171
207
|
|
|
172
208
|
return [
|
|
173
209
|
`# Original values for Norna's "${presetName}" theme preset.`,
|
|
@@ -177,9 +213,14 @@ export const renderThemePresetReference = (presetName, sourceLabel = 'theme.yaml
|
|
|
177
213
|
`# Available theme presets: ${themePresetNames.join(', ')}.`,
|
|
178
214
|
`preset: ${presetName}`,
|
|
179
215
|
'',
|
|
216
|
+
'# Alternatives: square, soft.',
|
|
217
|
+
`shape: ${shape}`,
|
|
218
|
+
'',
|
|
180
219
|
'layout:',
|
|
181
|
-
' # Alternatives: compact, normal,
|
|
182
|
-
`
|
|
220
|
+
' # Alternatives: compact, normal, spacious.',
|
|
221
|
+
` contentSpacing: ${layout.contentSpacing}`,
|
|
222
|
+
' # Alternatives: narrow, normal, wide.',
|
|
223
|
+
` textWidth: ${layout.textWidth}`,
|
|
183
224
|
' # Any positive CSS length, including px, rem, em, %, or clamp(...).',
|
|
184
225
|
` pageWidth: ${layout.pageWidth}`,
|
|
185
226
|
' gutter:',
|
|
@@ -211,9 +252,9 @@ export const renderThemePresetReference = (presetName, sourceLabel = 'theme.yaml
|
|
|
211
252
|
'',
|
|
212
253
|
'# Alternatives: dark, light, paper.',
|
|
213
254
|
`palette: ${palette}`,
|
|
214
|
-
'
|
|
215
|
-
'#
|
|
216
|
-
`
|
|
255
|
+
'sections:',
|
|
256
|
+
' # Alternatives: uniform, alternating, cycling.',
|
|
257
|
+
` backgroundPattern: ${sections.backgroundPattern}`,
|
|
217
258
|
'',
|
|
218
259
|
].join('\n');
|
|
219
260
|
};
|
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']);
|