@janga/norna 0.7.9 → 0.7.11
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 +4 -2
- package/bin/norna-cli.mjs +3 -0
- package/docs/README.md +16 -12
- package/docs/commands.md +6 -0
- package/docs/configuration.md +10 -54
- package/docs/content.md +20 -21
- package/docs/design/norna-diagram-design.md +2 -0
- package/docs/design/site-examples-structure.md +35 -24
- package/docs/engine-development.md +13 -7
- package/docs/getting-started.md +56 -19
- package/docs/routes.md +8 -6
- package/docs/site-structure.md +11 -6
- package/docs/sitewide-content.md +74 -0
- package/docs/theme.md +125 -140
- package/docs/typography.md +72 -45
- package/examples/README.md +41 -0
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/site.schema.json +59 -0
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/sitewide.schema.json +138 -0
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/theme.schema.json +755 -0
- package/examples/complete-sites/dog-shelter-multi-page/.astro/content-modules.mjs +1 -0
- package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/content.d.ts +8 -0
- package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -0
- package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev-local.json +2 -2
- package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev.json +2 -2
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +53 -0
- package/examples/complete-sites/dog-shelter-multi-page/.astro/settings.json +5 -0
- package/examples/complete-sites/dog-shelter-multi-page/README.md +9 -0
- package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/_metadata.json +13 -7
- package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +18 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/config.mjs +30 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/content.md +12 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/public/favicon.svg +10 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.svg +19 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.svg +19 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/route-content.md +30 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/routes/020-adopt/route-content.md +12 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.md +6 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/theme.md +25 -0
- package/examples/complete-sites/dog-shelter-single-page/README.md +9 -0
- package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +18 -0
- package/examples/complete-sites/dog-shelter-single-page/site/config.mjs +30 -0
- package/examples/complete-sites/dog-shelter-single-page/site/content.md +38 -0
- package/examples/complete-sites/dog-shelter-single-page/site/images/what-we-do/dog-house.svg +25 -0
- package/examples/complete-sites/dog-shelter-single-page/site/images/you-can-help/heart.svg +13 -0
- package/examples/complete-sites/dog-shelter-single-page/site/public/favicon.svg +10 -0
- package/examples/complete-sites/dog-shelter-single-page/site/public/robots.txt +2 -0
- package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.md +6 -0
- package/examples/complete-sites/dog-shelter-single-page/site/theme.md +25 -0
- package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +59 -0
- package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +138 -0
- package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +755 -0
- package/examples/feature-demos/media-and-surfaces/.astro/content-modules.mjs +1 -0
- package/examples/feature-demos/media-and-surfaces/.astro/content.d.ts +183 -0
- package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -0
- package/examples/feature-demos/media-and-surfaces/.astro/dev.log +11 -0
- package/examples/feature-demos/media-and-surfaces/.astro/settings.json +5 -0
- package/examples/feature-demos/media-and-surfaces/.astro/types.d.ts +2 -0
- package/examples/feature-demos/media-and-surfaces/README.md +15 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +74 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
- package/examples/feature-demos/media-and-surfaces/site/.norna/generated-images.json +66 -0
- package/examples/feature-demos/media-and-surfaces/site/config.mjs +30 -0
- package/examples/feature-demos/media-and-surfaces/site/content.md +64 -0
- package/examples/feature-demos/media-and-surfaces/site/public/robots.txt +2 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-carousel.svg +11 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-stack.svg +9 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-surfaces.svg +8 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-one.svg +8 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-three.svg +13 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-two.svg +8 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/stack/stack-one.svg +8 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/stack/stack-two.svg +9 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/route-content.md +85 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/010-media/theme.md +3 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/route-content.md +44 -0
- package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/theme.md +8 -0
- package/examples/feature-demos/media-and-surfaces/site/sitewide-content.md +6 -0
- package/examples/feature-demos/media-and-surfaces/site/theme.md +3 -0
- package/examples/{typography-presets/.astro/collections/theme.schema.json → feature-demos/theme-presets/.astro/collections/routeTheme.schema.json} +143 -65
- package/examples/feature-demos/theme-presets/.astro/collections/site.schema.json +59 -0
- package/examples/feature-demos/theme-presets/.astro/collections/sitewide.schema.json +138 -0
- package/examples/feature-demos/theme-presets/.astro/collections/theme.schema.json +755 -0
- package/examples/feature-demos/theme-presets/.astro/content-assets.mjs +4 -0
- package/examples/feature-demos/theme-presets/.astro/content-modules.mjs +1 -0
- package/examples/feature-demos/theme-presets/.astro/content.d.ts +183 -0
- package/examples/feature-demos/theme-presets/.astro/data-store.json +1 -0
- package/examples/feature-demos/theme-presets/.astro/dev-local.json +8 -0
- package/examples/feature-demos/theme-presets/.astro/dev.json +15 -0
- package/examples/feature-demos/theme-presets/.astro/dev.log +127 -0
- package/examples/feature-demos/theme-presets/.astro/types.d.ts +2 -0
- package/examples/feature-demos/theme-presets/README.md +14 -0
- package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js +1389 -0
- package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js.map +1 -0
- package/examples/feature-demos/theme-presets/node_modules/.vite/deps/package.json +3 -0
- package/examples/feature-demos/theme-presets/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/examples/feature-demos/theme-presets/site/.norna/generated-images.json +290 -0
- package/examples/{typography-presets → feature-demos/theme-presets}/site/config.mjs +4 -16
- package/examples/feature-demos/theme-presets/site/content.md +17 -0
- package/examples/feature-demos/theme-presets/site/routes/010-portfolio/route-content.md +34 -0
- package/examples/feature-demos/theme-presets/site/routes/010-portfolio/theme.md +3 -0
- package/examples/feature-demos/theme-presets/site/routes/020-documentation/route-content.md +33 -0
- package/examples/feature-demos/theme-presets/site/routes/020-documentation/theme.md +3 -0
- package/examples/feature-demos/theme-presets/site/routes/030-project/route-content.md +34 -0
- package/examples/feature-demos/theme-presets/site/routes/030-project/theme.md +3 -0
- package/examples/feature-demos/theme-presets/site/routes/040-statement/route-content.md +34 -0
- package/examples/feature-demos/theme-presets/site/routes/040-statement/theme.md +3 -0
- package/examples/feature-demos/theme-presets/site/sitewide-content.md +14 -0
- package/examples/feature-demos/theme-presets/site/theme.md +3 -0
- package/fixtures/basic/site/config.mjs +0 -13
- package/fixtures/basic/site/sitewide-content.md +12 -0
- package/fixtures/basic/site/theme.md +2 -2
- package/fixtures/content-model-v2/site/content.md +2 -5
- package/fixtures/content-model-v2/site/theme.md +1 -3
- package/package.json +9 -4
- package/scripts/build-pages.mjs +71 -0
- package/scripts/check-config.mjs +23 -1
- package/scripts/export-theme-preset.mjs +31 -0
- package/scripts/init-site.mjs +1 -0
- package/scripts/lib/example-sites.mjs +34 -0
- package/scripts/lib/frontmatter-yaml.mjs +28 -0
- package/scripts/lib/logo-assets.mjs +35 -0
- package/scripts/lib/norna-markdown-blocks.mjs +166 -0
- package/scripts/lib/presentation.mjs +106 -47
- package/scripts/lib/project-config.mjs +68 -105
- package/scripts/lib/site-content.mjs +26 -118
- package/scripts/lib/site-paths.mjs +2 -0
- package/scripts/lib/sitewide-config.mjs +34 -0
- package/scripts/lib/theme-config.mjs +75 -8
- package/scripts/lib/theme-presets.mjs +209 -0
- package/scripts/lib/typography.mjs +20 -26
- package/scripts/show-typography.mjs +43 -149
- package/scripts/sync-content-sections.mjs +19 -17
- package/scripts/test-content-check.mjs +59 -27
- package/scripts/test-content-model-v2.mjs +154 -0
- package/scripts/test-engine-commands.mjs +4 -1
- package/scripts/test-examples.mjs +15 -0
- package/scripts/test-navigation.mjs +73 -45
- package/scripts/test-package-check.mjs +66 -24
- package/scripts/test-temporary-visibility.mjs +19 -2
- package/scripts/test-theme-presets.mjs +151 -0
- package/src/components/CardList.astro +5 -2
- package/src/components/GalleryGrid.astro +37 -16
- package/src/components/SiteBanners.astro +100 -0
- package/src/components/SiteNavigation.astro +48 -31
- package/src/components/SitePage.astro +55 -13
- package/src/components/SiteSection.astro +193 -35
- package/src/content.config.ts +80 -48
- package/src/layouts/BaseLayout.astro +64 -14
- package/src/lib/routeThemes.ts +61 -0
- package/src/lib/sectionContent.ts +235 -51
- package/src/lib/sitePublicAssets.ts +1 -0
- package/src/styles/global.css +284 -93
- package/starters/basic/.github/workflows/deploy.yml +1 -1
- package/starters/basic/README.md +7 -6
- package/starters/basic/package.json +1 -0
- package/starters/basic/site/config.mjs +0 -13
- package/starters/basic/site/content.md +1 -1
- package/starters/basic/site/sitewide-content.md +14 -0
- package/starters/basic/site/theme.md +13 -24
- package/starters/project/.github/workflows/deploy.yml +1 -1
- package/starters/project/README.md +10 -8
- package/starters/project/package.json +1 -0
- package/starters/project/site/config.mjs +0 -13
- package/starters/project/site/sitewide-content.md +14 -0
- package/starters/project/site/theme.md +3 -11
- package/examples/dog-gallery/site/.norna/generated-images.json +0 -226
- package/examples/dog-gallery/site/config.mjs +0 -67
- package/examples/dog-gallery/site/content.md +0 -128
- package/examples/dog-gallery/site/images/black-dogs/black-puppy-meadow.png +0 -0
- package/examples/dog-gallery/site/images/black-dogs/photo-of-a-black-dog.jpg +0 -0
- package/examples/dog-gallery/site/images/brown-dogs/brown-dog.jpg +0 -0
- package/examples/dog-gallery/site/images/brown-dogs/dog-accompanies-master.jpg +0 -0
- package/examples/dog-gallery/site/images/golden-dogs/golden-retriever.jpg +0 -0
- package/examples/dog-gallery/site/images/golden-dogs/toller-puppy.jpg +0 -0
- package/examples/dog-gallery/site/images/white-dogs/white-cute-dog.jpg +0 -0
- package/examples/dog-gallery/site/images/white-dogs/white-puppy-garden.png +0 -0
- package/examples/dog-gallery/site/public/favicon.svg +0 -7
- package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +0 -38
- package/examples/dog-gallery/site/theme.md +0 -112
- package/examples/typography-presets/.astro/collections/site.schema.json +0 -776
- package/examples/typography-presets/.astro/data-store.json +0 -1
- package/examples/typography-presets/.astro/dev.log +0 -24
- package/examples/typography-presets/site/.norna/generated-images.json +0 -290
- package/examples/typography-presets/site/content.md +0 -134
- package/examples/typography-presets/site/theme.md +0 -43
- /package/examples/{typography-presets/.astro/content-modules.mjs → complete-sites/dog-shelter-multi-page/.astro/content-assets.mjs} +0 -0
- /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/types.d.ts +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
- /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js +0 -0
- /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/package.json +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
- /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/site/public/robots.txt +0 -0
- /package/examples/{typography-presets → feature-demos/media-and-surfaces}/.astro/content-assets.mjs +0 -0
- /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/package.json +0 -0
- /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/settings.json +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/_metadata.json +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
- /package/examples/{typography-presets → feature-demos/theme-presets}/site/public/robots.txt +0 -0
- /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/puppy-portrait.jpg +0 -0
- /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/shepherd-portrait.jpg +0 -0
- /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/white-puppy.jpg +0 -0
- /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/black-schnauzer.jpg +0 -0
- /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/retriever-portrait.jpg +0 -0
- /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/schnauzer-portrait.jpg +0 -0
- /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/boxer-portrait.jpg +0 -0
- /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grass-puppy.jpg +0 -0
- /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grey-street-dog.jpg +0 -0
- /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/black-pedigree.jpg +0 -0
- /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/sheepdog-grass.jpg +0 -0
- /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/sheepdog-wide.jpg +0 -0
|
@@ -129,6 +129,99 @@ test('content model v2 fixture checks and builds', async () => {
|
|
|
129
129
|
}
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
+
test('route theme replaces visual theme while site identity stays at the root', async () => {
|
|
133
|
+
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
134
|
+
try {
|
|
135
|
+
await writeFile(path.join(siteDir, 'theme.md'), `---
|
|
136
|
+
typography:
|
|
137
|
+
preset: text-forward
|
|
138
|
+
---
|
|
139
|
+
`);
|
|
140
|
+
await writeFile(path.join(siteDir, 'sitewide-content.md'), `---
|
|
141
|
+
navigation:
|
|
142
|
+
brand: Root Brand
|
|
143
|
+
---
|
|
144
|
+
`);
|
|
145
|
+
await mkdir(path.join(siteDir, 'routes', '010-guide'), { recursive: true });
|
|
146
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
147
|
+
title: Root
|
|
148
|
+
description: Root page
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Home {#home}
|
|
152
|
+
|
|
153
|
+
Root content.
|
|
154
|
+
`);
|
|
155
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'route-content.md'), `---
|
|
156
|
+
title: Guide
|
|
157
|
+
description: Guide page
|
|
158
|
+
navigation:
|
|
159
|
+
label: Guide
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Guide {#guide}
|
|
163
|
+
|
|
164
|
+
Route content.
|
|
165
|
+
`);
|
|
166
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'theme.md'), `---
|
|
167
|
+
typography:
|
|
168
|
+
preset: statement
|
|
169
|
+
presentation:
|
|
170
|
+
palette: light
|
|
171
|
+
---
|
|
172
|
+
`);
|
|
173
|
+
|
|
174
|
+
await runNorna(['--site-dir', siteDir, 'build']);
|
|
175
|
+
const rootHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
176
|
+
const routeHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'guide', 'index.html'), 'utf8');
|
|
177
|
+
assert.match(rootHtml, /--color-page: #000000/);
|
|
178
|
+
assert.match(routeHtml, /--color-page: #ffffff/);
|
|
179
|
+
assert.match(routeHtml, /<a class="site-brand" href="\/">Root Brand<\/a>/);
|
|
180
|
+
} finally {
|
|
181
|
+
await rm(root, { recursive: true, force: true });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('route theme cannot define site identity', async () => {
|
|
186
|
+
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
187
|
+
try {
|
|
188
|
+
await mkdir(path.join(siteDir, 'routes', '010-guide'), { recursive: true });
|
|
189
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
190
|
+
title: Root
|
|
191
|
+
description: Root page
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Home {#home}
|
|
195
|
+
|
|
196
|
+
Root content.
|
|
197
|
+
`);
|
|
198
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'route-content.md'), `---
|
|
199
|
+
title: Guide
|
|
200
|
+
description: Guide page
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Guide {#guide}
|
|
204
|
+
|
|
205
|
+
Route content.
|
|
206
|
+
`);
|
|
207
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'theme.md'), `---
|
|
208
|
+
navigation:
|
|
209
|
+
brand: Route Brand
|
|
210
|
+
---
|
|
211
|
+
`);
|
|
212
|
+
|
|
213
|
+
await assert.rejects(
|
|
214
|
+
runNorna(['--site-dir', siteDir, 'config:check']),
|
|
215
|
+
(error) => {
|
|
216
|
+
assert.match(error.output, /may not define navigation\. Brand and logo belong in site\/sitewide-content\.md/);
|
|
217
|
+
return true;
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
} finally {
|
|
221
|
+
await rm(root, { recursive: true, force: true });
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
132
225
|
test('content:check warns for local Markdown images but not external Markdown images', async () => {
|
|
133
226
|
const { root, siteDir } = await createTempSite();
|
|
134
227
|
try {
|
|
@@ -189,6 +282,40 @@ description: Fixture
|
|
|
189
282
|
}
|
|
190
283
|
});
|
|
191
284
|
|
|
285
|
+
test('inline notes render as linked numbered margin notes', async () => {
|
|
286
|
+
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
287
|
+
try {
|
|
288
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
289
|
+
title: Inline notes
|
|
290
|
+
description: Fixture
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Intro {#intro}
|
|
294
|
+
|
|
295
|
+
The first paragraph points to an explanation.{note-ref}
|
|
296
|
+
|
|
297
|
+
{note: This explanation names \`sitewide-content.md\` and stays beside the paragraph on wide screens.}
|
|
298
|
+
|
|
299
|
+
The second paragraph has its own explanation.{note-ref}
|
|
300
|
+
|
|
301
|
+
{note: This is the second explanation.}
|
|
302
|
+
`);
|
|
303
|
+
|
|
304
|
+
await runNorna(['--site-dir', siteDir, 'build']);
|
|
305
|
+
const html = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
306
|
+
assert.match(html, /<sup class="section-note-ref"><a id="note-ref-home-intro-1" href="#note-home-intro-1" aria-label="Note 1" aria-describedby="note-home-intro-1" data-note-id="note-home-intro-1">1<\/a><\/sup>/);
|
|
307
|
+
assert.match(html, /<aside class="section-note section-note-margin" id="note-home-intro-1" aria-label="Note 1" role="note" data-note-id="note-home-intro-1" style="grid-row: 1;">/);
|
|
308
|
+
assert.match(html, /<a class="section-note-number" href="#note-ref-home-intro-1" aria-label="Note 1">\s*1\s*<\/a>/);
|
|
309
|
+
assert.match(html, /This explanation names <code>sitewide-content\.md<\/code> and stays beside the paragraph on wide screens\./);
|
|
310
|
+
assert.match(html, /<sup class="section-note-ref"><a id="note-ref-home-intro-2" href="#note-home-intro-2" aria-label="Note 2" aria-describedby="note-home-intro-2" data-note-id="note-home-intro-2">2<\/a><\/sup>/);
|
|
311
|
+
assert.match(html, /<aside class="section-note section-note-margin" id="note-home-intro-2" aria-label="Note 2" role="note" data-note-id="note-home-intro-2" style="grid-row: 2;">/);
|
|
312
|
+
assert.match(html, /This is the second explanation\./);
|
|
313
|
+
assert.doesNotMatch(html, /\{note:/);
|
|
314
|
+
} finally {
|
|
315
|
+
await rm(root, { recursive: true, force: true });
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
192
319
|
test('updated managed SVG images get updated static output', async () => {
|
|
193
320
|
const { root, siteDir } = await createTempSite();
|
|
194
321
|
try {
|
|
@@ -842,6 +969,33 @@ description: Fixture
|
|
|
842
969
|
}
|
|
843
970
|
});
|
|
844
971
|
|
|
972
|
+
test('content:check rejects the removed norna-note block', async () => {
|
|
973
|
+
const { root, siteDir } = await createTempSite();
|
|
974
|
+
try {
|
|
975
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
976
|
+
title: Removed note block
|
|
977
|
+
description: Fixture
|
|
978
|
+
---
|
|
979
|
+
|
|
980
|
+
## Intro {#intro}
|
|
981
|
+
|
|
982
|
+
\`\`\`norna-note
|
|
983
|
+
This block is no longer supported.
|
|
984
|
+
\`\`\`
|
|
985
|
+
`);
|
|
986
|
+
|
|
987
|
+
await assert.rejects(
|
|
988
|
+
() => runContentScript(siteDir, ['--check']),
|
|
989
|
+
(error) => {
|
|
990
|
+
assert.match(error.output, /Unknown Norna block "norna-note"\. Use one of: norna-image-stack, norna-image-carousel, norna-card-list\./);
|
|
991
|
+
return true;
|
|
992
|
+
},
|
|
993
|
+
);
|
|
994
|
+
} finally {
|
|
995
|
+
await rm(root, { recursive: true, force: true });
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
|
|
845
999
|
test('content:check supports tilde fenced Norna blocks', async () => {
|
|
846
1000
|
const { root, siteDir } = await createTempSite();
|
|
847
1001
|
try {
|
|
@@ -45,6 +45,7 @@ try {
|
|
|
45
45
|
assert.equal(initializedPackageJson.dependencies['@janga/norna'], packageJson.version);
|
|
46
46
|
assert.equal(initializedPackageJson.scripts['norna:engine:update'], 'norna engine:update');
|
|
47
47
|
assert.equal(initializedPackageJson.scripts['norna:engine:version'], 'norna engine:version');
|
|
48
|
+
assert.equal(initializedPackageJson.scripts['norna:theme:export'], 'norna theme:export');
|
|
48
49
|
assert.equal(initializedPackageJson.scripts['engine:update'], undefined);
|
|
49
50
|
assert.equal(initializedPackageJson.scripts['engine:version'], undefined);
|
|
50
51
|
|
|
@@ -57,8 +58,10 @@ try {
|
|
|
57
58
|
for (const [presetName, preset] of Object.entries(typographyPresets)) {
|
|
58
59
|
for (const [level, heading] of Object.entries(preset.headings)) {
|
|
59
60
|
assert.equal(heading.size, 'medium', `${presetName} ${level} should use medium size`);
|
|
61
|
+
assert.ok([400, 500, 600, 700].includes(heading.weight), `${presetName} ${level} should use a supported weight`);
|
|
60
62
|
}
|
|
61
63
|
assert.equal(preset.body.size, 'medium', `${presetName} body should use medium size`);
|
|
64
|
+
assert.ok(['narrow', 'normal', 'wide'].includes(preset.body.width), `${presetName} body should use a supported width`);
|
|
62
65
|
assert.equal(preset.caption.size, 'medium', `${presetName} caption should use medium size`);
|
|
63
66
|
}
|
|
64
67
|
for (const [rhythmName, rhythm] of Object.entries(typographyRhythms)) {
|
|
@@ -76,7 +79,7 @@ try {
|
|
|
76
79
|
assert.match(showResult.stdout, /pages:/);
|
|
77
80
|
assert.match(showResult.stdout, /\s+\/:/);
|
|
78
81
|
assert.match(showResult.stdout, /value: quiet-gallery/);
|
|
79
|
-
assert.match(showResult.stdout, /
|
|
82
|
+
assert.match(showResult.stdout, /source: "site\/theme\.md"/);
|
|
80
83
|
assert.match(showResult.stdout, /intro:/);
|
|
81
84
|
|
|
82
85
|
const initAgainResult = runCli(['init', initializedSiteRoot]);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { getExampleSites } from './lib/example-sites.mjs';
|
|
4
|
+
import { runInherit } from './lib/run-command.mjs';
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
const cliPath = path.join(root, 'bin', 'norna.mjs');
|
|
8
|
+
const examples = await getExampleSites(root);
|
|
9
|
+
|
|
10
|
+
for (const example of examples) {
|
|
11
|
+
console.log(`\nBuilding ${example.siteLabel}`);
|
|
12
|
+
await runInherit(process.execPath, [cliPath, '--site-dir', example.siteLabel, 'build'], { cwd: root });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log(`\nok - built ${examples.length} example sites`);
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import net from 'node:net';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { promisify } from 'node:util';
|
|
5
|
-
|
|
6
|
-
const execFileAsync = promisify(execFile);
|
|
7
5
|
|
|
8
6
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
7
|
const cliPath = path.join(root, 'bin', 'norna.mjs');
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const probeUrls = [url, 'http://127.0.0.1:4321/', 'http://[::1]:4321/'];
|
|
8
|
+
const navigationDemoSiteDir = path.join(root, 'examples', 'feature-demos', 'media-and-surfaces', 'site');
|
|
9
|
+
const host = '127.0.0.1';
|
|
13
10
|
const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
14
11
|
const testTargets = process.argv.slice(2);
|
|
15
12
|
const playwrightTargets = testTargets.length > 0 ? testTargets : ['tests/navigation.spec.ts'];
|
|
@@ -18,33 +15,46 @@ const sleep = (milliseconds) => new Promise((resolve) => {
|
|
|
18
15
|
setTimeout(resolve, milliseconds);
|
|
19
16
|
});
|
|
20
17
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
const getAvailablePort = () => new Promise((resolve, reject) => {
|
|
19
|
+
const server = net.createServer();
|
|
20
|
+
server.unref();
|
|
21
|
+
server.once('error', reject);
|
|
22
|
+
server.listen(0, host, () => {
|
|
23
|
+
const address = server.address();
|
|
24
|
+
const port = typeof address === 'object' && address ? address.port : null;
|
|
25
|
+
server.close((error) => {
|
|
26
|
+
if (error) {
|
|
27
|
+
reject(error);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
resolve(port);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const port = await getAvailablePort();
|
|
36
|
+
if (!port) throw new Error('Could not reserve a port for the navigation test server.');
|
|
37
|
+
const url = `http://${host}:${port}/`;
|
|
31
38
|
|
|
39
|
+
const isReachable = async () => {
|
|
32
40
|
try {
|
|
33
|
-
await
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
return true;
|
|
41
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(1_000) });
|
|
42
|
+
await response.arrayBuffer();
|
|
43
|
+
return response.ok;
|
|
38
44
|
} catch {
|
|
39
45
|
return false;
|
|
40
46
|
}
|
|
41
47
|
};
|
|
42
48
|
|
|
43
|
-
const waitForServer = async () => {
|
|
49
|
+
const waitForServer = async (serverProcess) => {
|
|
44
50
|
const startedAt = Date.now();
|
|
45
51
|
const timeoutMs = 30_000;
|
|
46
52
|
|
|
47
53
|
while (Date.now() - startedAt < timeoutMs) {
|
|
54
|
+
if (serverProcess.exitCode !== null) {
|
|
55
|
+
throw new Error(`Navigation test server exited before ${url} became reachable.`);
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
if (await isReachable()) {
|
|
49
59
|
return;
|
|
50
60
|
}
|
|
@@ -55,12 +65,6 @@ const waitForServer = async () => {
|
|
|
55
65
|
throw new Error(`Timed out waiting for ${url}`);
|
|
56
66
|
};
|
|
57
67
|
|
|
58
|
-
const runCapture = async (command, args, options = {}) => execFileAsync(command, args, {
|
|
59
|
-
cwd: root,
|
|
60
|
-
maxBuffer: 1024 * 1024 * 10,
|
|
61
|
-
...options,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
68
|
const runInherit = (command, args, options = {}) => new Promise((resolve, reject) => {
|
|
65
69
|
const child = spawn(command, args, {
|
|
66
70
|
cwd: root,
|
|
@@ -82,31 +86,57 @@ const runInherit = (command, args, options = {}) => new Promise((resolve, reject
|
|
|
82
86
|
});
|
|
83
87
|
});
|
|
84
88
|
|
|
85
|
-
const
|
|
86
|
-
await
|
|
89
|
+
const startServer = async () => {
|
|
90
|
+
await runInherit(process.execPath, [cliPath, 'site:public'], {
|
|
91
|
+
cwd: navigationDemoSiteDir,
|
|
92
|
+
});
|
|
93
|
+
await runInherit(process.execPath, [cliPath, 'images'], {
|
|
94
|
+
cwd: navigationDemoSiteDir,
|
|
95
|
+
});
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
const serverProcess = spawn(process.execPath, [
|
|
98
|
+
cliPath,
|
|
99
|
+
'astro',
|
|
100
|
+
'dev',
|
|
101
|
+
'--ignore-lock',
|
|
102
|
+
'--host',
|
|
103
|
+
host,
|
|
104
|
+
'--port',
|
|
105
|
+
String(port),
|
|
106
|
+
], {
|
|
107
|
+
cwd: navigationDemoSiteDir,
|
|
108
|
+
stdio: 'inherit',
|
|
90
109
|
env: {
|
|
91
110
|
...process.env,
|
|
92
|
-
|
|
111
|
+
ASTRO_DEV_BACKGROUND: '0',
|
|
93
112
|
},
|
|
94
113
|
});
|
|
95
|
-
|
|
114
|
+
serverProcess.once('error', (error) => {
|
|
115
|
+
throw error;
|
|
116
|
+
});
|
|
117
|
+
await waitForServer(serverProcess);
|
|
96
118
|
|
|
97
|
-
return
|
|
119
|
+
return serverProcess;
|
|
98
120
|
};
|
|
99
121
|
|
|
100
|
-
const stopServer = async () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
const stopServer = async (serverProcess) => {
|
|
123
|
+
if (!serverProcess || serverProcess.exitCode !== null) return;
|
|
124
|
+
|
|
125
|
+
serverProcess.kill('SIGTERM');
|
|
126
|
+
const stopped = await Promise.race([
|
|
127
|
+
new Promise((resolve) => serverProcess.once('exit', () => resolve(true))),
|
|
128
|
+
sleep(5_000).then(() => false),
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
if (!stopped && serverProcess.exitCode === null) {
|
|
132
|
+
serverProcess.kill('SIGKILL');
|
|
133
|
+
}
|
|
104
134
|
};
|
|
105
135
|
|
|
106
|
-
let
|
|
136
|
+
let serverProcess;
|
|
107
137
|
|
|
108
138
|
try {
|
|
109
|
-
|
|
139
|
+
serverProcess = await startServer();
|
|
110
140
|
await runInherit(npmBin, ['exec', '--', 'playwright', 'test', ...playwrightTargets], {
|
|
111
141
|
env: {
|
|
112
142
|
...process.env,
|
|
@@ -114,7 +144,5 @@ try {
|
|
|
114
144
|
},
|
|
115
145
|
});
|
|
116
146
|
} finally {
|
|
117
|
-
|
|
118
|
-
await stopServer();
|
|
119
|
-
}
|
|
147
|
+
await stopServer(serverProcess);
|
|
120
148
|
}
|
|
@@ -160,6 +160,7 @@ try {
|
|
|
160
160
|
assertFileExists(path.join(packagedStarterRoot, 'README.md')),
|
|
161
161
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'config.mjs')),
|
|
162
162
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'theme.md')),
|
|
163
|
+
assertFileExists(path.join(packagedStarterRoot, 'site', 'sitewide-content.md')),
|
|
163
164
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'content.md')),
|
|
164
165
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'images', 'work', '.gitkeep')),
|
|
165
166
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'public', 'robots.txt')),
|
|
@@ -184,11 +185,15 @@ try {
|
|
|
184
185
|
.replace("url: 'https://example.com/'", "url: 'https://example.com/site/'")
|
|
185
186
|
.replace("basePath: '/'", "basePath: '/site/'"),
|
|
186
187
|
);
|
|
187
|
-
const
|
|
188
|
-
const
|
|
188
|
+
const packageCheckSitewidePath = path.join(siteProjectRoot, 'site', 'sitewide-content.md');
|
|
189
|
+
const packageCheckSitewide = await readFile(packageCheckSitewidePath, 'utf8');
|
|
189
190
|
await writeFile(
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
packageCheckSitewidePath,
|
|
192
|
+
packageCheckSitewide.replace('brand: Example Site', 'brand: Package Check Brand\n logo:\n alt: Package Check Logo\n height: 2.6rem'),
|
|
193
|
+
);
|
|
194
|
+
await writeFile(
|
|
195
|
+
path.join(siteProjectRoot, 'site', 'public', 'logo.svg'),
|
|
196
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" fill="black"/></svg>\n',
|
|
192
197
|
);
|
|
193
198
|
await writeFile(path.join(siteProjectRoot, 'site', 'content.md'), `---
|
|
194
199
|
title: Package Check Site
|
|
@@ -201,14 +206,14 @@ sections:
|
|
|
201
206
|
|
|
202
207
|
## Intro {#intro}
|
|
203
208
|
|
|
204
|
-
This page verifies that
|
|
209
|
+
This page verifies that **packaged norna sites** can build with the current content model.
|
|
205
210
|
|
|
206
211
|
<!-- norna-image-provenance:
|
|
207
212
|
image: package-check.svg
|
|
208
213
|
source: package check fixture
|
|
209
214
|
-->
|
|
210
215
|
|
|
211
|
-
##
|
|
216
|
+
## **Warning** {#warning}
|
|
212
217
|
|
|
213
218
|
This section verifies styled section headings.
|
|
214
219
|
|
|
@@ -262,6 +267,10 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
262
267
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
263
268
|
'--section-heading-font-size-desktop: clamp(1.35rem, 1.1rem + 0.9vw, 2rem)',
|
|
264
269
|
);
|
|
270
|
+
await assertFileIncludes(
|
|
271
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
272
|
+
'--section-heading-font-weight: 400',
|
|
273
|
+
);
|
|
265
274
|
await assertFileIncludes(
|
|
266
275
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
267
276
|
'--section-heading-spacing-after: 0.55em',
|
|
@@ -288,7 +297,15 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
288
297
|
);
|
|
289
298
|
await assertFileIncludes(
|
|
290
299
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
291
|
-
'<a class="site-brand" href="/site/"
|
|
300
|
+
'<a class="site-brand" href="/site/"><img class="site-brand-logo" src="/site/logo.svg" alt="Package Check Logo">',
|
|
301
|
+
);
|
|
302
|
+
await assertFileIncludes(
|
|
303
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
304
|
+
'<div class="site-nav-submenu">',
|
|
305
|
+
);
|
|
306
|
+
await assertFileIncludes(
|
|
307
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
308
|
+
'href="/site/#intro">Intro</a>',
|
|
292
309
|
);
|
|
293
310
|
await assertFileIncludes(
|
|
294
311
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -298,6 +315,10 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
298
315
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
299
316
|
'--section-body-line-height: 1.5',
|
|
300
317
|
);
|
|
318
|
+
await assertFileIncludes(
|
|
319
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
320
|
+
'--section-body-width-desktop: min(72ch, var(--text-width))',
|
|
321
|
+
);
|
|
301
322
|
await assertFileExcludes(
|
|
302
323
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
303
324
|
'norna-image-provenance',
|
|
@@ -318,25 +339,29 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
318
339
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
319
340
|
'--section-caption-font-size-desktop: clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
320
341
|
);
|
|
342
|
+
await assertFileExcludes(
|
|
343
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
344
|
+
'class="inline-style',
|
|
345
|
+
);
|
|
321
346
|
await assertFileIncludes(
|
|
322
347
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
323
|
-
'
|
|
348
|
+
'data-section-title="Warning"',
|
|
324
349
|
);
|
|
325
350
|
await assertFileIncludes(
|
|
326
351
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
327
|
-
'
|
|
352
|
+
'href="/site/#warning">Warning</a>',
|
|
328
353
|
);
|
|
329
354
|
await assertFileIncludes(
|
|
330
355
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
331
|
-
'
|
|
356
|
+
'--section-background-color: #000000',
|
|
332
357
|
);
|
|
333
358
|
await assertFileIncludes(
|
|
334
359
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
335
|
-
'
|
|
360
|
+
'--section-background-color: #171717',
|
|
336
361
|
);
|
|
337
362
|
await assertFileIncludes(
|
|
338
363
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
339
|
-
'--section-background-color: #
|
|
364
|
+
'--section-background-color: #252525',
|
|
340
365
|
);
|
|
341
366
|
await assertFileIncludes(
|
|
342
367
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -344,7 +369,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
344
369
|
);
|
|
345
370
|
await assertFileIncludes(
|
|
346
371
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
347
|
-
'--site-top-text-color: #
|
|
372
|
+
'--site-top-text-color: #f2eee6',
|
|
348
373
|
);
|
|
349
374
|
await assertFileIncludes(
|
|
350
375
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -352,11 +377,11 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
352
377
|
);
|
|
353
378
|
await assertFileIncludes(
|
|
354
379
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
355
|
-
'--site-footer-text-color: #
|
|
380
|
+
'--site-footer-text-color: #f2eee6',
|
|
356
381
|
);
|
|
357
382
|
await assertFileIncludes(
|
|
358
383
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
359
|
-
'--section-text-color: #
|
|
384
|
+
'--section-text-color: #f2eee6',
|
|
360
385
|
);
|
|
361
386
|
await writeFile(path.join(siteProjectRoot, 'site', 'public', 'favicon.ico'), 'fake icon');
|
|
362
387
|
await runInherit(npxBin, ['norna', 'build'], { cwd: siteProjectRoot, env: npmEnv });
|
|
@@ -382,35 +407,52 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
382
407
|
);
|
|
383
408
|
await writeFile(
|
|
384
409
|
siteThemePath,
|
|
385
|
-
siteTheme.replace('
|
|
410
|
+
siteTheme.replace('\n palette: dark', '\n palette: neon'),
|
|
386
411
|
);
|
|
387
412
|
await runExpectFailure(
|
|
388
413
|
npxBin,
|
|
389
414
|
['norna', 'build'],
|
|
390
415
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
391
|
-
'presentation.
|
|
416
|
+
'presentation.palette',
|
|
392
417
|
);
|
|
393
418
|
await writeFile(
|
|
394
419
|
siteThemePath,
|
|
395
|
-
siteTheme.replace('
|
|
420
|
+
siteTheme.replace(' sectionSurfaces:\n mode: cycle', ' sectionSurfaces:\n mode: glowing'),
|
|
396
421
|
);
|
|
397
422
|
await runExpectFailure(
|
|
398
423
|
npxBin,
|
|
399
424
|
['norna', 'build'],
|
|
400
425
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
401
|
-
'presentation.
|
|
426
|
+
'presentation.sectionSurfaces.mode',
|
|
402
427
|
);
|
|
403
428
|
await writeFile(
|
|
404
429
|
siteThemePath,
|
|
405
|
-
siteTheme.replace(
|
|
430
|
+
siteTheme.replace(
|
|
431
|
+
' sectionSurfaces:\n mode: cycle',
|
|
432
|
+
' sectionSurfaces:\n mode: cycle\n sequence: [base, base]',
|
|
433
|
+
),
|
|
406
434
|
);
|
|
407
435
|
await runExpectFailure(
|
|
408
436
|
npxBin,
|
|
409
437
|
['norna', 'build'],
|
|
410
438
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
411
|
-
'presentation.
|
|
439
|
+
'presentation.sectionSurfaces',
|
|
412
440
|
);
|
|
413
441
|
await writeFile(siteThemePath, siteTheme);
|
|
442
|
+
await writeFile(
|
|
443
|
+
siteContentPath,
|
|
444
|
+
siteContent.replace(
|
|
445
|
+
'This page verifies that **packaged norna sites** can build with the current content model.',
|
|
446
|
+
'This page verifies that [packaged norna sites]{.highlight} can build with the current content model.',
|
|
447
|
+
),
|
|
448
|
+
);
|
|
449
|
+
await runExpectFailure(
|
|
450
|
+
npxBin,
|
|
451
|
+
['norna', 'build'],
|
|
452
|
+
{ cwd: siteProjectRoot, env: npmEnv },
|
|
453
|
+
'Inline color style ".highlight" is no longer supported',
|
|
454
|
+
);
|
|
455
|
+
await writeFile(siteContentPath, siteContent);
|
|
414
456
|
await writeFile(
|
|
415
457
|
siteContentPath,
|
|
416
458
|
siteContent.replace(
|
|
@@ -422,7 +464,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
422
464
|
npxBin,
|
|
423
465
|
['norna', 'build'],
|
|
424
466
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
425
|
-
'presentation
|
|
467
|
+
'presentation',
|
|
426
468
|
);
|
|
427
469
|
await writeFile(
|
|
428
470
|
siteContentPath,
|
|
@@ -435,7 +477,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
435
477
|
npxBin,
|
|
436
478
|
['norna', 'build'],
|
|
437
479
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
438
|
-
'sections.intro
|
|
480
|
+
'sections.intro: Unrecognized key: "presentation"',
|
|
439
481
|
);
|
|
440
482
|
await writeFile(
|
|
441
483
|
siteContentPath,
|
|
@@ -448,7 +490,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
448
490
|
npxBin,
|
|
449
491
|
['norna', 'build'],
|
|
450
492
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
451
|
-
'sections.intro
|
|
493
|
+
'sections.intro: Unrecognized key: "presentation"',
|
|
452
494
|
);
|
|
453
495
|
await writeFile(
|
|
454
496
|
siteThemePath,
|
|
@@ -51,8 +51,23 @@ try {
|
|
|
51
51
|
await writeFile(path.join(siteDir, 'theme.md'), `---
|
|
52
52
|
typography:
|
|
53
53
|
preset: quiet-gallery
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
presentation:
|
|
55
|
+
palette: dark
|
|
56
|
+
---
|
|
57
|
+
`);
|
|
58
|
+
await writeFile(path.join(siteDir, 'sitewide-content.md'), `---
|
|
59
|
+
banners:
|
|
60
|
+
- id: expired-banner
|
|
61
|
+
visible:
|
|
62
|
+
until: "2026-01-01"
|
|
63
|
+
title: Expired banner
|
|
64
|
+
text: Expired banner text.
|
|
65
|
+
- id: active-banner
|
|
66
|
+
visible:
|
|
67
|
+
from: "2026-01-01"
|
|
68
|
+
until: "2026-12-31"
|
|
69
|
+
title: Active banner
|
|
70
|
+
text: Active banner text.
|
|
56
71
|
---
|
|
57
72
|
`);
|
|
58
73
|
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
@@ -88,6 +103,8 @@ Always visible section text.
|
|
|
88
103
|
assert.match(html, /Active section text/);
|
|
89
104
|
assert.match(html, /Always visible section text/);
|
|
90
105
|
assert.doesNotMatch(html, /Expired section text/);
|
|
106
|
+
assert.match(html, /Active banner text/);
|
|
107
|
+
assert.doesNotMatch(html, /Expired banner text/);
|
|
91
108
|
|
|
92
109
|
console.log('Temporary visibility test passed.');
|
|
93
110
|
} finally {
|