@janga/norna 0.7.18 → 0.7.23
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 +22 -12
- package/astro.config.mjs +25 -0
- package/bin/norna-cli.mjs +8 -2
- package/package.json +60 -13
- package/schemas/category.schema.json +20 -0
- package/schemas/config.schema.json +22 -14
- package/schemas/content-frontmatter.schema.json +25 -5
- package/schemas/manifest.json +2 -1
- package/schemas/page-theme.schema.json +58 -30
- package/schemas/sitewide-content.schema.json +20 -19
- package/schemas/theme.schema.json +542 -243
- package/scripts/add-site-node.mjs +251 -0
- package/scripts/check-config.mjs +32 -4
- package/scripts/deploy-site.mjs +5 -9
- package/scripts/dev-local.mjs +175 -112
- package/scripts/generate-images.mjs +11 -15
- package/scripts/generate-schemas.mjs +4 -1
- package/scripts/init-site.mjs +35 -0
- package/scripts/lib/astro-command.mjs +14 -9
- package/scripts/lib/content-sync-apply.mjs +34 -0
- package/scripts/lib/content-sync-plan.mjs +179 -0
- package/scripts/lib/editor-language-service.mjs +156 -177
- package/scripts/lib/heading-ids.mjs +4 -2
- package/scripts/lib/image-presentation.mjs +2 -0
- package/scripts/lib/markdown-links.mjs +182 -0
- package/scripts/lib/navigation-model.mjs +36 -19
- package/scripts/lib/norna-markdown-blocks.mjs +65 -145
- package/scripts/lib/norna-markdown-render-plugin.mjs +37 -0
- package/scripts/lib/object.mjs +28 -0
- package/scripts/lib/page-aliases.mjs +164 -0
- package/scripts/lib/page-markdown.mjs +332 -0
- package/scripts/lib/presentation-contract.mjs +266 -0
- package/scripts/lib/presentation-palette-metadata.mjs +51 -0
- package/scripts/lib/presentation.mjs +508 -79
- package/scripts/lib/project-config.mjs +78 -10
- package/scripts/lib/schema-definitions.mjs +67 -16
- package/scripts/lib/schema-editor-metadata.mjs +126 -24
- package/scripts/lib/schema-value-definitions.mjs +30 -12
- package/scripts/lib/site-content.mjs +46 -239
- package/scripts/lib/site-link-graph.mjs +326 -0
- package/scripts/lib/site-page-urls.mjs +10 -0
- package/scripts/lib/site-paths.mjs +2 -3
- package/scripts/lib/site-structure.mjs +189 -0
- package/scripts/lib/sitemap.mjs +34 -0
- package/scripts/lib/theme-presets.mjs +118 -119
- package/scripts/lib/theme-profiles.mjs +223 -0
- package/scripts/lib/typography.mjs +220 -35
- package/scripts/lib/yaml-config.mjs +50 -6
- package/scripts/lib/yaml-indentation.mjs +86 -0
- package/scripts/show-typography.mjs +2 -3
- package/scripts/sync-content-sections.mjs +158 -244
- package/scripts/sync-site-public.mjs +23 -2
- package/src/components/CodeBlockCopyScript.astro +92 -0
- package/src/components/DisplaySettings.astro +107 -0
- package/src/components/ImageCarousel.astro +38 -13
- package/src/components/NavigationPageTree.astro +103 -88
- package/src/components/PageAliasRedirect.astro +49 -0
- package/src/components/PageContentsNavigation.astro +21 -0
- package/src/components/SectionNavigationScript.astro +112 -2
- package/src/components/SiteBreadcrumbs.astro +15 -12
- package/src/components/SiteNavigation.astro +104 -41
- package/src/components/SitePage.astro +76 -40
- package/src/components/SiteSection.astro +40 -191
- package/src/components/SiteTreeNavigation.astro +10 -10
- package/src/content.config.ts +5 -2
- package/src/layouts/BaseLayout.astro +55 -9
- package/src/lib/generatedImages.ts +0 -7
- package/src/lib/readerPreferencesScript.mjs +141 -0
- package/src/lib/sectionContent.ts +119 -201
- package/src/lib/siteNavigation.ts +36 -18
- package/src/lib/sitePages.ts +92 -20
- package/src/pages/[...slug].astro +35 -8
- package/src/styles/content.css +494 -0
- package/src/styles/foundations.css +284 -0
- package/src/styles/global.css +6 -2047
- package/src/styles/media.css +523 -0
- package/src/styles/navigation.css +495 -0
- package/src/styles/page-layout.css +437 -0
- package/src/styles/responsive.css +525 -0
- package/starters/basic/README.md +3 -3
- package/docs/README.md +0 -137
- package/docs/assets/norna-dog-shelter.jpg +0 -0
- package/docs/commands.md +0 -189
- package/docs/configuration.md +0 -142
- package/docs/content.md +0 -351
- package/docs/design/command-organization.md +0 -414
- package/docs/design/navigation-and-theme-plan.md +0 -186
- package/docs/design/norna-diagram-design.md +0 -159
- package/docs/design/site-examples-structure.md +0 -119
- package/docs/engine-development.md +0 -215
- package/docs/getting-started.md +0 -206
- package/docs/how-to/embedded-site.md +0 -35
- package/docs/images-and-metadata.md +0 -188
- package/docs/local-development.md +0 -82
- package/docs/pages.md +0 -190
- package/docs/public-files.md +0 -118
- package/docs/publishing.md +0 -117
- package/docs/requirements.md +0 -54
- package/docs/site-files.md +0 -152
- package/docs/sitewide-content.md +0 -73
- package/docs/theme.md +0 -237
- package/docs/typography.md +0 -168
- package/examples/README.md +0 -49
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/site.schema.json +0 -35
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/sitewide.schema.json +0 -97
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/theme.schema.json +0 -852
- package/examples/complete-sites/dog-shelter-multi-page/.astro/content-assets.mjs +0 -4
- package/examples/complete-sites/dog-shelter-multi-page/.astro/content-modules.mjs +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/.astro/content.d.ts +0 -183
- package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev-local.json +0 -8
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.json +0 -13
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +0 -36
- package/examples/complete-sites/dog-shelter-multi-page/.astro/settings.json +0 -5
- package/examples/complete-sites/dog-shelter-multi-page/.astro/types.d.ts +0 -2
- package/examples/complete-sites/dog-shelter-multi-page/README.md +0 -24
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/_metadata.json +0 -74
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro-B3JHJyiv.js +0 -434
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro-B3JHJyiv.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2702
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/audit-C3SVr7dT.js +0 -1534
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/audit-C3SVr7dT.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/highlight-BLtvqtzO.js +0 -48
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/icons-C92i61-2.js +0 -42
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/icons-C92i61-2.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/package.json +0 -3
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -4
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/settings-v5GgKks_.js +0 -190
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/settings-v5GgKks_.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/toolbar-BFvJVp26.js +0 -458
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js +0 -1016
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/window-vo3A6TMn.js +0 -32
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/window-vo3A6TMn.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/xray-DwhzGckb.js +0 -111
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/xray-DwhzGckb.js.map +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +0 -98
- package/examples/complete-sites/dog-shelter-multi-page/site/config.yaml +0 -1
- package/examples/complete-sites/dog-shelter-multi-page/site/pages/000-home/content.md +0 -23
- package/examples/complete-sites/dog-shelter-multi-page/site/pages/000-home/images/shelter-dog.jpg +0 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/pages/010-dogs/content.md +0 -21
- package/examples/complete-sites/dog-shelter-multi-page/site/pages/010-dogs/images/rex.jpg +0 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/pages/010-dogs/images/rover.jpg +0 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/pages/020-adopt/content.md +0 -22
- package/examples/complete-sites/dog-shelter-multi-page/site/pages/020-adopt/images/adoption-dog.jpg +0 -0
- package/examples/complete-sites/dog-shelter-multi-page/site/public/favicon.svg +0 -10
- package/examples/complete-sites/dog-shelter-multi-page/site/public/logo.svg +0 -9
- package/examples/complete-sites/dog-shelter-multi-page/site/public/robots.txt +0 -2
- package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.yaml +0 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/theme.yaml +0 -1
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/site.schema.json +0 -35
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/sitewide.schema.json +0 -97
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/theme.schema.json +0 -852
- package/examples/complete-sites/dog-shelter-single-page/.astro/content-assets.mjs +0 -4
- package/examples/complete-sites/dog-shelter-single-page/.astro/content-modules.mjs +0 -1
- package/examples/complete-sites/dog-shelter-single-page/.astro/content.d.ts +0 -183
- package/examples/complete-sites/dog-shelter-single-page/.astro/data-store.json +0 -1
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev-local.json +0 -8
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.json +0 -13
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.log +0 -18
- package/examples/complete-sites/dog-shelter-single-page/.astro/settings.json +0 -5
- package/examples/complete-sites/dog-shelter-single-page/.astro/types.d.ts +0 -2
- package/examples/complete-sites/dog-shelter-single-page/README.md +0 -24
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/_metadata.json +0 -74
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro-B3JHJyiv.js +0 -434
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro-B3JHJyiv.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2702
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/audit-C3SVr7dT.js +0 -1534
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/audit-C3SVr7dT.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/highlight-BLtvqtzO.js +0 -48
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/icons-C92i61-2.js +0 -42
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/icons-C92i61-2.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/package.json +0 -3
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -4
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/settings-v5GgKks_.js +0 -190
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/settings-v5GgKks_.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/toolbar-BFvJVp26.js +0 -458
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js +0 -1016
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/window-vo3A6TMn.js +0 -32
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/window-vo3A6TMn.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/xray-DwhzGckb.js +0 -111
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/xray-DwhzGckb.js.map +0 -1
- package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +0 -50
- package/examples/complete-sites/dog-shelter-single-page/site/config.yaml +0 -1
- package/examples/complete-sites/dog-shelter-single-page/site/pages/000-home/content.md +0 -37
- package/examples/complete-sites/dog-shelter-single-page/site/pages/000-home/images/foster-dog.jpg +0 -0
- package/examples/complete-sites/dog-shelter-single-page/site/pages/000-home/images/rescue-dog.jpg +0 -0
- package/examples/complete-sites/dog-shelter-single-page/site/public/favicon.svg +0 -10
- package/examples/complete-sites/dog-shelter-single-page/site/public/logo.svg +0 -9
- package/examples/complete-sites/dog-shelter-single-page/site/public/robots.txt +0 -2
- package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.yaml +0 -4
- package/examples/complete-sites/dog-shelter-single-page/site/theme.yaml +0 -1
- package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +0 -35
- package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +0 -97
- package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +0 -844
- package/examples/feature-demos/media-and-surfaces/.astro/content-assets.mjs +0 -4
- package/examples/feature-demos/media-and-surfaces/.astro/content-modules.mjs +0 -1
- package/examples/feature-demos/media-and-surfaces/.astro/content.d.ts +0 -183
- package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +0 -1
- package/examples/feature-demos/media-and-surfaces/.astro/dev.log +0 -14
- package/examples/feature-demos/media-and-surfaces/.astro/settings.json +0 -5
- package/examples/feature-demos/media-and-surfaces/.astro/types.d.ts +0 -2
- package/examples/feature-demos/media-and-surfaces/README.md +0 -21
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +0 -80
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js +0 -434
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2702
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js +0 -1534
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/embla-carousel.js +0 -1389
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/embla-carousel.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js +0 -48
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js +0 -42
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/package.json +0 -3
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -4
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js +0 -190
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js +0 -458
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js +0 -1016
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js +0 -32
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js +0 -111
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js.map +0 -1
- package/examples/feature-demos/media-and-surfaces/site/.norna/generated-images.json +0 -66
- package/examples/feature-demos/media-and-surfaces/site/config.yaml +0 -1
- package/examples/feature-demos/media-and-surfaces/site/pages/000-home/content.md +0 -15
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/content.md +0 -71
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/card-carousel.svg +0 -11
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/card-stack.svg +0 -9
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/card-surfaces.svg +0 -8
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/carousel-one.svg +0 -8
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/carousel-three.svg +0 -13
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/carousel-two.svg +0 -8
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/stack-one.svg +0 -8
- package/examples/feature-demos/media-and-surfaces/site/pages/010-media/images/stack-two.svg +0 -9
- package/examples/feature-demos/media-and-surfaces/site/pages/020-surfaces/content.md +0 -42
- package/examples/feature-demos/media-and-surfaces/site/pages/020-surfaces/theme.yaml +0 -2
- package/examples/feature-demos/media-and-surfaces/site/public/robots.txt +0 -2
- package/examples/feature-demos/media-and-surfaces/site/sitewide-content.yaml +0 -2
- package/examples/feature-demos/media-and-surfaces/site/theme.yaml +0 -1
- package/examples/feature-demos/sitewide-content/.astro/collections/site.schema.json +0 -59
- package/examples/feature-demos/sitewide-content/.astro/collections/sitewide.schema.json +0 -138
- package/examples/feature-demos/sitewide-content/.astro/collections/theme.schema.json +0 -755
- package/examples/feature-demos/sitewide-content/.astro/content-assets.mjs +0 -1
- package/examples/feature-demos/sitewide-content/.astro/content-modules.mjs +0 -1
- package/examples/feature-demos/sitewide-content/.astro/content.d.ts +0 -183
- package/examples/feature-demos/sitewide-content/.astro/data-store.json +0 -1
- package/examples/feature-demos/sitewide-content/.astro/dev.json +0 -13
- package/examples/feature-demos/sitewide-content/.astro/dev.log +0 -18
- package/examples/feature-demos/sitewide-content/.astro/settings.json +0 -5
- package/examples/feature-demos/sitewide-content/.astro/types.d.ts +0 -2
- package/examples/feature-demos/sitewide-content/README.md +0 -26
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/_metadata.json +0 -74
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro-B3JHJyiv.js +0 -434
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro-B3JHJyiv.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2702
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/audit-C3SVr7dT.js +0 -1534
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/audit-C3SVr7dT.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/highlight-BLtvqtzO.js +0 -48
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/icons-C92i61-2.js +0 -42
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/icons-C92i61-2.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/package.json +0 -3
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -4
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/settings-v5GgKks_.js +0 -190
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/settings-v5GgKks_.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/toolbar-BFvJVp26.js +0 -458
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/ui-library-Cd35oAvo.js +0 -1016
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/window-vo3A6TMn.js +0 -32
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/window-vo3A6TMn.js.map +0 -1
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/xray-DwhzGckb.js +0 -111
- package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/xray-DwhzGckb.js.map +0 -1
- package/examples/feature-demos/sitewide-content/site/.norna/generated-images.json +0 -1
- package/examples/feature-demos/sitewide-content/site/config.yaml +0 -1
- package/examples/feature-demos/sitewide-content/site/pages/000-home/content.md +0 -17
- package/examples/feature-demos/sitewide-content/site/pages/010-logo-and-navigation/content.md +0 -17
- package/examples/feature-demos/sitewide-content/site/pages/020-notices/content.md +0 -23
- package/examples/feature-demos/sitewide-content/site/pages/030-footer/content.md +0 -18
- package/examples/feature-demos/sitewide-content/site/public/logo.svg +0 -6
- package/examples/feature-demos/sitewide-content/site/public/robots.txt +0 -2
- package/examples/feature-demos/sitewide-content/site/sitewide-content.yaml +0 -14
- package/examples/feature-demos/sitewide-content/site/theme.yaml +0 -1
- package/examples/feature-demos/theme-preset-documentation/README.md +0 -16
- package/examples/feature-demos/theme-preset-documentation/site/.norna/generated-images.json +0 -74
- package/examples/feature-demos/theme-preset-documentation/site/config.yaml +0 -1
- package/examples/feature-demos/theme-preset-documentation/site/pages/000-home/content.md +0 -30
- package/examples/feature-demos/theme-preset-documentation/site/pages/000-home/images/black-schnauzer.jpg +0 -0
- package/examples/feature-demos/theme-preset-documentation/site/pages/000-home/images/retriever-portrait.jpg +0 -0
- package/examples/feature-demos/theme-preset-documentation/site/pages/000-home/images/schnauzer-portrait.jpg +0 -0
- package/examples/feature-demos/theme-preset-documentation/site/public/robots.txt +0 -2
- package/examples/feature-demos/theme-preset-documentation/site/sitewide-content.yaml +0 -3
- package/examples/feature-demos/theme-preset-documentation/site/theme.yaml +0 -1
- package/examples/feature-demos/theme-preset-portfolio/README.md +0 -16
- package/examples/feature-demos/theme-preset-portfolio/site/.norna/generated-images.json +0 -74
- package/examples/feature-demos/theme-preset-portfolio/site/config.yaml +0 -1
- package/examples/feature-demos/theme-preset-portfolio/site/pages/000-home/content.md +0 -31
- package/examples/feature-demos/theme-preset-portfolio/site/pages/000-home/images/puppy-portrait.jpg +0 -0
- package/examples/feature-demos/theme-preset-portfolio/site/pages/000-home/images/shepherd-portrait.jpg +0 -0
- package/examples/feature-demos/theme-preset-portfolio/site/pages/000-home/images/white-puppy.jpg +0 -0
- package/examples/feature-demos/theme-preset-portfolio/site/public/robots.txt +0 -2
- package/examples/feature-demos/theme-preset-portfolio/site/sitewide-content.yaml +0 -3
- package/examples/feature-demos/theme-preset-portfolio/site/theme.yaml +0 -1
- package/examples/feature-demos/theme-preset-project/.astro/collections/routeTheme.schema.json +0 -746
- package/examples/feature-demos/theme-preset-project/.astro/collections/site.schema.json +0 -59
- package/examples/feature-demos/theme-preset-project/.astro/collections/sitewide.schema.json +0 -138
- package/examples/feature-demos/theme-preset-project/.astro/collections/theme.schema.json +0 -755
- package/examples/feature-demos/theme-preset-project/.astro/content-assets.mjs +0 -4
- package/examples/feature-demos/theme-preset-project/.astro/content-modules.mjs +0 -1
- package/examples/feature-demos/theme-preset-project/.astro/content.d.ts +0 -183
- package/examples/feature-demos/theme-preset-project/.astro/data-store.json +0 -1
- package/examples/feature-demos/theme-preset-project/.astro/dev-local.json +0 -8
- package/examples/feature-demos/theme-preset-project/.astro/dev.json +0 -15
- package/examples/feature-demos/theme-preset-project/.astro/dev.log +0 -164
- package/examples/feature-demos/theme-preset-project/.astro/settings.json +0 -5
- package/examples/feature-demos/theme-preset-project/.astro/types.d.ts +0 -2
- package/examples/feature-demos/theme-preset-project/README.md +0 -16
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/_metadata.json +0 -80
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -434
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2702
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -1534
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/embla-carousel.js +0 -1389
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/embla-carousel.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -48
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/icons-RyebKi45.js +0 -42
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/package.json +0 -3
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -4
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -190
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/toolbar-63PA619H.js +0 -458
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -1016
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/window-jPkpKVcy.js +0 -32
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -111
- package/examples/feature-demos/theme-preset-project/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -1
- package/examples/feature-demos/theme-preset-project/site/.norna/generated-images.json +0 -74
- package/examples/feature-demos/theme-preset-project/site/config.yaml +0 -1
- package/examples/feature-demos/theme-preset-project/site/pages/000-home/content.md +0 -31
- package/examples/feature-demos/theme-preset-project/site/pages/000-home/images/boxer-portrait.jpg +0 -0
- package/examples/feature-demos/theme-preset-project/site/pages/000-home/images/grass-puppy.jpg +0 -0
- package/examples/feature-demos/theme-preset-project/site/pages/000-home/images/grey-street-dog.jpg +0 -0
- package/examples/feature-demos/theme-preset-project/site/public/robots.txt +0 -2
- package/examples/feature-demos/theme-preset-project/site/sitewide-content.yaml +0 -3
- package/examples/feature-demos/theme-preset-project/site/theme.yaml +0 -1
- package/examples/feature-demos/theme-preset-statement/README.md +0 -16
- package/examples/feature-demos/theme-preset-statement/site/.norna/generated-images.json +0 -74
- package/examples/feature-demos/theme-preset-statement/site/config.yaml +0 -1
- package/examples/feature-demos/theme-preset-statement/site/pages/000-home/content.md +0 -31
- package/examples/feature-demos/theme-preset-statement/site/pages/000-home/images/black-pedigree.jpg +0 -0
- package/examples/feature-demos/theme-preset-statement/site/pages/000-home/images/sheepdog-grass.jpg +0 -0
- package/examples/feature-demos/theme-preset-statement/site/pages/000-home/images/sheepdog-wide.jpg +0 -0
- package/examples/feature-demos/theme-preset-statement/site/public/robots.txt +0 -2
- package/examples/feature-demos/theme-preset-statement/site/sitewide-content.yaml +0 -3
- package/examples/feature-demos/theme-preset-statement/site/theme.yaml +0 -1
- package/fixtures/basic/site/.norna/generated-images.json +0 -1
- package/fixtures/basic/site/config.yaml +0 -1
- package/fixtures/basic/site/pages/000-home/content.md +0 -14
- package/fixtures/basic/site/pages/000-home/images/.gitkeep +0 -1
- package/fixtures/basic/site/public/robots.txt +0 -2
- package/fixtures/basic/site/sitewide-content.yaml +0 -3
- package/fixtures/basic/site/theme.yaml +0 -1
- package/fixtures/content-model-v2/.astro/collections/site.schema.json +0 -776
- package/fixtures/content-model-v2/.astro/collections/theme.schema.json +0 -668
- package/fixtures/content-model-v2/.astro/content-assets.mjs +0 -4
- package/fixtures/content-model-v2/.astro/content-modules.mjs +0 -1
- package/fixtures/content-model-v2/.astro/content.d.ts +0 -175
- package/fixtures/content-model-v2/.astro/data-store.json +0 -1
- package/fixtures/content-model-v2/.astro/dev-local.json +0 -8
- package/fixtures/content-model-v2/.astro/dev.json +0 -13
- package/fixtures/content-model-v2/.astro/dev.log +0 -231
- package/fixtures/content-model-v2/.astro/settings.json +0 -5
- package/fixtures/content-model-v2/.astro/types.d.ts +0 -2
- package/fixtures/content-model-v2/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +0 -1
- package/fixtures/content-model-v2/dist/_astro/SitePage.BbfZub4g.css +0 -1
- package/fixtures/content-model-v2/dist/guide/index.html +0 -3
- package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-971.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-971.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-971.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-960.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-768.webp +0 -0
- package/fixtures/content-model-v2/dist/index.html +0 -8
- package/fixtures/content-model-v2/node_modules/.vite/deps/_metadata.json +0 -80
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -434
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2702
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -1534
- package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js +0 -1389
- package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -48
- package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js +0 -42
- package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/package.json +0 -3
- package/fixtures/content-model-v2/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -4
- package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -190
- package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js +0 -458
- package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -1016
- package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js +0 -32
- package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -1
- package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -111
- package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -1
- package/fixtures/content-model-v2/site/.norna/generated-images.json +0 -210
- package/fixtures/content-model-v2/site/config.yaml +0 -1
- package/fixtures/content-model-v2/site/pages/000-home/content.md +0 -79
- package/fixtures/content-model-v2/site/pages/000-home/images/adopt.svg +0 -9
- package/fixtures/content-model-v2/site/pages/000-home/images/detail.jpg +0 -0
- package/fixtures/content-model-v2/site/pages/000-home/images/duplicate.jpg +0 -0
- package/fixtures/content-model-v2/site/pages/000-home/images/foster.svg +0 -12
- package/fixtures/content-model-v2/site/pages/000-home/images/hero.jpg +0 -0
- package/fixtures/content-model-v2/site/pages/000-home/images/image.jpg +0 -0
- package/fixtures/content-model-v2/site/pages/000-home/images/slide-one.jpg +0 -0
- package/fixtures/content-model-v2/site/pages/000-home/images/slide-three.jpg +0 -0
- package/fixtures/content-model-v2/site/pages/000-home/images/slide-two.jpg +0 -0
- package/fixtures/content-model-v2/site/pages/010-guide/content.md +0 -18
- package/fixtures/content-model-v2/site/pages/010-guide/images/duplicate.jpg +0 -0
- package/fixtures/content-model-v2/site/theme.yaml +0 -4
- package/fixtures/nested-pages/.astro/collections/site.schema.json +0 -35
- package/fixtures/nested-pages/.astro/collections/sitewide.schema.json +0 -97
- package/fixtures/nested-pages/.astro/collections/theme.schema.json +0 -844
- package/fixtures/nested-pages/.astro/content-assets.mjs +0 -4
- package/fixtures/nested-pages/.astro/content-modules.mjs +0 -1
- package/fixtures/nested-pages/.astro/content.d.ts +0 -183
- package/fixtures/nested-pages/.astro/data-store.json +0 -1
- package/fixtures/nested-pages/.astro/dev-local.json +0 -8
- package/fixtures/nested-pages/.astro/dev.log +0 -1128
- package/fixtures/nested-pages/.astro/settings.json +0 -5
- package/fixtures/nested-pages/.astro/types.d.ts +0 -2
- package/fixtures/nested-pages/node_modules/.vite/deps/_metadata.json +0 -74
- package/fixtures/nested-pages/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -434
- package/fixtures/nested-pages/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_aria-query.js +0 -6390
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_axobject-query.js +0 -2702
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_html-escaper.js +0 -65
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -455
- package/fixtures/nested-pages/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -1534
- package/fixtures/nested-pages/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -48
- package/fixtures/nested-pages/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/icons-RyebKi45.js +0 -42
- package/fixtures/nested-pages/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/package.json +0 -3
- package/fixtures/nested-pages/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -4
- package/fixtures/nested-pages/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -190
- package/fixtures/nested-pages/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/toolbar-63PA619H.js +0 -458
- package/fixtures/nested-pages/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -1016
- package/fixtures/nested-pages/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/window-jPkpKVcy.js +0 -32
- package/fixtures/nested-pages/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -1
- package/fixtures/nested-pages/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -111
- package/fixtures/nested-pages/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -1
- package/fixtures/nested-pages/site/.norna/generated-images.json +0 -10
- package/fixtures/nested-pages/site/config.yaml +0 -3
- package/fixtures/nested-pages/site/pages/000-home/content.md +0 -15
- package/fixtures/nested-pages/site/pages/010-guides/content.md +0 -15
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/content.md +0 -21
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/images/diagram.svg +0 -21
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/pages/010-macos/content.md +0 -18
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/theme.yaml +0 -2
- package/fixtures/nested-pages/site/pages/010-guides/pages/020-workflows/content.md +0 -11
- package/fixtures/nested-pages/site/pages/010-guides/pages/030-release-notes/content.md +0 -9
- package/fixtures/nested-pages/site/pages/010-guides/pages/040-configuration/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/050-content/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/060-themes/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/070-images/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/080-cards/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/090-notes/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/100-validation/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/110-synchronization/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/120-building/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/130-publishing/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/140-troubleshooting/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/150-migration/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/160-accessibility/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/170-performance/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/180-security/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/190-localization/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/200-upgrading/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/pages/210-faq/content.md +0 -3
- package/fixtures/nested-pages/site/pages/010-guides/theme.yaml +0 -7
- package/fixtures/nested-pages/site/pages/020-reference/content.md +0 -10
- package/fixtures/nested-pages/site/pages/020-reference/pages/010-installation/content.md +0 -11
- package/fixtures/nested-pages/site/theme.yaml +0 -1
- package/scripts/build-pages.mjs +0 -69
- package/scripts/lib/example-sites.mjs +0 -49
- package/scripts/lib/frontmatter-yaml.mjs +0 -113
- package/scripts/lib/git-status.mjs +0 -7
- package/scripts/release.mjs +0 -153
- package/scripts/test-banner-visibility.mjs +0 -80
- package/scripts/test-ci-lockfile.mjs +0 -65
- package/scripts/test-cli-discovery.mjs +0 -124
- package/scripts/test-client-javascript.mjs +0 -225
- package/scripts/test-content-check.mjs +0 -621
- package/scripts/test-content-model-v2.mjs +0 -1693
- package/scripts/test-documentation.mjs +0 -189
- package/scripts/test-editor-language-service.mjs +0 -305
- package/scripts/test-engine-commands.mjs +0 -204
- package/scripts/test-examples.mjs +0 -53
- package/scripts/test-heading-ids.mjs +0 -68
- package/scripts/test-navigation-model.mjs +0 -63
- package/scripts/test-navigation-preview.mjs +0 -108
- package/scripts/test-navigation.mjs +0 -157
- package/scripts/test-nested-pages.mjs +0 -125
- package/scripts/test-package-check.mjs +0 -500
- package/scripts/test-page-model.mjs +0 -108
- package/scripts/test-project-config.mjs +0 -139
- package/scripts/test-schemas.mjs +0 -169
- package/scripts/test-site-public.mjs +0 -85
- package/scripts/test-theme-presets.mjs +0 -188
- package/src/components/SiteNotesScript.astro +0 -43
- package/starters/project/.github/workflows/deploy.yml +0 -64
- package/starters/project/README.md +0 -84
- package/starters/project/package-lock.json +0 -4296
- package/starters/project/package.json +0 -37
- package/starters/project/site/config.yaml +0 -1
- package/starters/project/site/pages/000-home/content.md +0 -119
- package/starters/project/site/pages/010-guide/content.md +0 -80
- package/starters/project/site/public/robots.txt +0 -2
- package/starters/project/site/sitewide-content.yaml +0 -3
- package/starters/project/site/theme.yaml +0 -1
- package/tsconfig.json +0 -5
|
@@ -1,1693 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { execFile } from 'node:child_process';
|
|
3
|
-
import { access, cp, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { promisify } from 'node:util';
|
|
7
|
-
import { test } from 'node:test';
|
|
8
|
-
|
|
9
|
-
const execFileAsync = promisify(execFile);
|
|
10
|
-
const repoRoot = path.resolve(import.meta.dirname, '..');
|
|
11
|
-
const nodeBin = process.execPath;
|
|
12
|
-
const nornaBin = path.join(repoRoot, 'bin', 'norna.mjs');
|
|
13
|
-
const contentScript = path.join(repoRoot, 'scripts', 'sync-content-sections.mjs');
|
|
14
|
-
const fixtureSiteDir = path.join(repoRoot, 'fixtures', 'content-model-v2', 'site');
|
|
15
|
-
const fixtureRoot = path.dirname(fixtureSiteDir);
|
|
16
|
-
|
|
17
|
-
const runNorna = async (args, options = {}) => {
|
|
18
|
-
try {
|
|
19
|
-
return await execFileAsync(nodeBin, [nornaBin, ...args], {
|
|
20
|
-
cwd: repoRoot,
|
|
21
|
-
maxBuffer: 1024 * 1024 * 10,
|
|
22
|
-
...options,
|
|
23
|
-
});
|
|
24
|
-
} catch (error) {
|
|
25
|
-
error.output = `${error.stdout ?? ''}${error.stderr ?? ''}`;
|
|
26
|
-
throw error;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const runContentScript = async (siteDir, args) => {
|
|
31
|
-
try {
|
|
32
|
-
return await execFileAsync(nodeBin, [contentScript, ...args], {
|
|
33
|
-
cwd: repoRoot,
|
|
34
|
-
env: {
|
|
35
|
-
...process.env,
|
|
36
|
-
NORNA_SITE_DIR: siteDir,
|
|
37
|
-
},
|
|
38
|
-
maxBuffer: 1024 * 1024 * 10,
|
|
39
|
-
});
|
|
40
|
-
} catch (error) {
|
|
41
|
-
error.output = `${error.stdout ?? ''}${error.stderr ?? ''}`;
|
|
42
|
-
throw error;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const createTempSite = async ({ underRepoCache = false } = {}) => {
|
|
47
|
-
const tempParent = underRepoCache
|
|
48
|
-
? path.join(repoRoot, 'node_modules', '.cache')
|
|
49
|
-
: os.tmpdir();
|
|
50
|
-
await mkdir(tempParent, { recursive: true });
|
|
51
|
-
const root = await mkdtemp(path.join(tempParent, 'norna-content-model-v2-'));
|
|
52
|
-
const siteDir = path.join(root, 'site');
|
|
53
|
-
await mkdir(path.join(siteDir, 'pages', '000-home'), { recursive: true });
|
|
54
|
-
await writeFile(path.join(siteDir, 'config.yaml'), 'url: https://example.com/\n');
|
|
55
|
-
await writeFile(path.join(siteDir, 'theme.yaml'), `typography:
|
|
56
|
-
profile: reading
|
|
57
|
-
`);
|
|
58
|
-
return { root, siteDir };
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const fileExists = async (filePath) => access(filePath).then(() => true, () => false);
|
|
62
|
-
|
|
63
|
-
const runGit = async (cwd, args) => execFileAsync('git', args, {
|
|
64
|
-
cwd,
|
|
65
|
-
env: {
|
|
66
|
-
...process.env,
|
|
67
|
-
GIT_AUTHOR_NAME: 'Norna Test',
|
|
68
|
-
GIT_AUTHOR_EMAIL: 'norna@example.test',
|
|
69
|
-
GIT_COMMITTER_NAME: 'Norna Test',
|
|
70
|
-
GIT_COMMITTER_EMAIL: 'norna@example.test',
|
|
71
|
-
},
|
|
72
|
-
maxBuffer: 1024 * 1024,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const initCleanGitWorktree = async (root) => {
|
|
76
|
-
await runGit(root, ['init']);
|
|
77
|
-
await runGit(root, ['config', 'user.email', 'norna@example.test']);
|
|
78
|
-
await runGit(root, ['config', 'user.name', 'Norna Test']);
|
|
79
|
-
await runGit(root, ['add', '.']);
|
|
80
|
-
await runGit(root, ['commit', '-m', 'initial']);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const createTempFixtureCopy = async () => {
|
|
84
|
-
const tempParent = path.join(repoRoot, 'node_modules', '.cache');
|
|
85
|
-
await mkdir(tempParent, { recursive: true });
|
|
86
|
-
const root = await mkdtemp(path.join(tempParent, 'norna-content-model-v2-fixture-'));
|
|
87
|
-
const fixtureCopyRoot = path.join(root, 'content-model-v2');
|
|
88
|
-
|
|
89
|
-
await cp(fixtureRoot, fixtureCopyRoot, {
|
|
90
|
-
recursive: true,
|
|
91
|
-
filter: (source) => ![
|
|
92
|
-
path.join(fixtureRoot, '.astro'),
|
|
93
|
-
path.join(fixtureRoot, 'dist'),
|
|
94
|
-
path.join(fixtureRoot, 'node_modules'),
|
|
95
|
-
path.join(fixtureSiteDir, '.norna'),
|
|
96
|
-
].some((ignoredPath) => source === ignoredPath || source.startsWith(`${ignoredPath}${path.sep}`)),
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
root,
|
|
101
|
-
siteDir: path.join(fixtureCopyRoot, 'site'),
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
test('content model v2 fixture checks and builds', async () => {
|
|
106
|
-
const { root, siteDir } = await createTempFixtureCopy();
|
|
107
|
-
try {
|
|
108
|
-
await runNorna(['--site-dir', siteDir, 'content:check']);
|
|
109
|
-
await runNorna(['--site-dir', siteDir, 'build']);
|
|
110
|
-
|
|
111
|
-
const manifest = JSON.parse(await readFile(path.join(siteDir, '.norna', 'generated-images.json'), 'utf8'));
|
|
112
|
-
assert.ok(manifest['pages/000-home/images/duplicate.jpg']);
|
|
113
|
-
assert.ok(manifest['pages/010-guide/images/duplicate.jpg']);
|
|
114
|
-
|
|
115
|
-
const homepageHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
116
|
-
assert.match(homepageHtml, /site-section-page-title/);
|
|
117
|
-
assert.match(homepageHtml, /site-section-page-title-empty/);
|
|
118
|
-
assert.match(homepageHtml, /fetchpriority="high"/);
|
|
119
|
-
} finally {
|
|
120
|
-
await rm(root, { recursive: true, force: true });
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test('the removed root page structure is rejected with a migration hint', async () => {
|
|
125
|
-
const { root, siteDir } = await createTempSite();
|
|
126
|
-
try {
|
|
127
|
-
await writeFile(path.join(siteDir, 'content.md'), `# Home
|
|
128
|
-
Home content.
|
|
129
|
-
`);
|
|
130
|
-
|
|
131
|
-
await assert.rejects(
|
|
132
|
-
runNorna(['--site-dir', siteDir, 'content:check']),
|
|
133
|
-
(error) => {
|
|
134
|
-
assert.match(error.output, /The old root-page structure is no longer supported: site\/content\.md\./);
|
|
135
|
-
assert.match(error.output, /Move the homepage content to site\/pages\/000-home\/content\.md/);
|
|
136
|
-
return true;
|
|
137
|
-
},
|
|
138
|
-
);
|
|
139
|
-
} finally {
|
|
140
|
-
await rm(root, { recursive: true, force: true });
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test('the removed routes directory is rejected with a page migration hint', async () => {
|
|
145
|
-
const { root, siteDir } = await createTempSite();
|
|
146
|
-
try {
|
|
147
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `# Home
|
|
148
|
-
Home content.
|
|
149
|
-
`);
|
|
150
|
-
await mkdir(path.join(siteDir, 'routes', '010-guide'), { recursive: true });
|
|
151
|
-
await writeFile(path.join(siteDir, 'routes', '010-guide', 'content.md'), 'Old page content.\n');
|
|
152
|
-
|
|
153
|
-
await assert.rejects(
|
|
154
|
-
runNorna(['--site-dir', siteDir, 'content:check']),
|
|
155
|
-
(error) => {
|
|
156
|
-
assert.match(error.output, /site\/routes is no longer supported\. Rename it to site\/pages and use NNN-page-id directory names\./);
|
|
157
|
-
return true;
|
|
158
|
-
},
|
|
159
|
-
);
|
|
160
|
-
} finally {
|
|
161
|
-
await rm(root, { recursive: true, force: true });
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test('the required home page cannot own child pages', async () => {
|
|
166
|
-
const { root, siteDir } = await createTempSite();
|
|
167
|
-
try {
|
|
168
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `# Home
|
|
169
|
-
|
|
170
|
-
## Welcome {#welcome}
|
|
171
|
-
|
|
172
|
-
Home content.
|
|
173
|
-
`);
|
|
174
|
-
const homeChildDir = path.join(siteDir, 'pages', '000-home', 'pages', '010-news');
|
|
175
|
-
await mkdir(homeChildDir, { recursive: true });
|
|
176
|
-
await writeFile(path.join(homeChildDir, 'content.md'), '# News\n\nNews content.\n');
|
|
177
|
-
|
|
178
|
-
await assert.rejects(
|
|
179
|
-
runNorna(['--site-dir', siteDir, 'content:check']),
|
|
180
|
-
(error) => {
|
|
181
|
-
assert.match(error.output, /site\/pages\/000-home is the homepage and cannot contain child pages\./);
|
|
182
|
-
assert.match(error.output, /Move these page directories beside 000-home under site\/pages\//);
|
|
183
|
-
assert.match(error.output, /site\/pages\/000-home\/pages\/010-news/);
|
|
184
|
-
return true;
|
|
185
|
-
},
|
|
186
|
-
);
|
|
187
|
-
} finally {
|
|
188
|
-
await rm(root, { recursive: true, force: true });
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test('page theme changes page presentation while preserving site visual identity', async () => {
|
|
193
|
-
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
194
|
-
try {
|
|
195
|
-
await writeFile(path.join(siteDir, 'theme.yaml'), `preset: documentation
|
|
196
|
-
palette: dark
|
|
197
|
-
`);
|
|
198
|
-
await mkdir(path.join(siteDir, 'pages', '010-guide'), { recursive: true });
|
|
199
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
200
|
-
page:
|
|
201
|
-
description: Root page
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
# Root
|
|
205
|
-
|
|
206
|
-
## Home {#home}
|
|
207
|
-
|
|
208
|
-
Root content.
|
|
209
|
-
`);
|
|
210
|
-
await writeFile(path.join(siteDir, 'pages', '010-guide', 'content.md'), `---
|
|
211
|
-
page:
|
|
212
|
-
description: Guide page
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
# Guide
|
|
216
|
-
Page content.
|
|
217
|
-
`);
|
|
218
|
-
await mkdir(path.join(siteDir, 'pages', '010-guide', 'pages', '010-detail'), { recursive: true });
|
|
219
|
-
await writeFile(path.join(siteDir, 'pages', '010-guide', 'pages', '010-detail', 'content.md'), `# Detail
|
|
220
|
-
|
|
221
|
-
Inherited page content.
|
|
222
|
-
`);
|
|
223
|
-
await writeFile(path.join(siteDir, 'pages', '010-guide', 'theme.yaml'), `layout:
|
|
224
|
-
contentSpacing: spacious
|
|
225
|
-
textWidth: wide
|
|
226
|
-
sections:
|
|
227
|
-
backgroundPattern: cycling
|
|
228
|
-
`);
|
|
229
|
-
await writeFile(path.join(siteDir, 'pages', '010-guide', 'pages', '010-detail', 'theme.yaml'), `layout:
|
|
230
|
-
textWidth: narrow
|
|
231
|
-
`);
|
|
232
|
-
|
|
233
|
-
await runNorna(['--site-dir', siteDir, 'build']);
|
|
234
|
-
const rootHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
235
|
-
const pageHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'guide', 'index.html'), 'utf8');
|
|
236
|
-
const detailHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'guide', 'detail', 'index.html'), 'utf8');
|
|
237
|
-
assert.match(rootHtml, /--color-page: #000000/);
|
|
238
|
-
assert.match(pageHtml, /--color-page: #000000/);
|
|
239
|
-
assert.match(rootHtml, /--font-sans: Georgia, 'Times New Roman', serif/);
|
|
240
|
-
assert.match(pageHtml, /--font-sans: Georgia, 'Times New Roman', serif/);
|
|
241
|
-
assert.match(pageHtml, /--space-section-to-section-desktop: clamp\(2\.25rem, 5vw, 4\.5rem\)/);
|
|
242
|
-
assert.match(pageHtml, /--section-body-width-desktop: min\(72ch,/);
|
|
243
|
-
assert.match(detailHtml, /--space-section-to-section-desktop: clamp\(2\.25rem, 5vw, 4\.5rem\)/);
|
|
244
|
-
assert.match(detailHtml, /--section-body-width-desktop: min\(60ch,/);
|
|
245
|
-
assert.match(pageHtml, /<a href="\/">\s*Root\s*<\/a>/);
|
|
246
|
-
} finally {
|
|
247
|
-
await rm(root, { recursive: true, force: true });
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
test('page metadata, navigation logo, and page listing have separate roles', async () => {
|
|
252
|
-
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
253
|
-
try {
|
|
254
|
-
await mkdir(path.join(siteDir, 'public'), { recursive: true });
|
|
255
|
-
await writeFile(path.join(siteDir, 'public', 'logo.svg'), '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"/>');
|
|
256
|
-
await writeFile(path.join(siteDir, 'sitewide-content.yaml'), `logo:
|
|
257
|
-
height: 2rem
|
|
258
|
-
`);
|
|
259
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `# Welcome
|
|
260
|
-
|
|
261
|
-
## Intro {#intro}
|
|
262
|
-
|
|
263
|
-
Homepage content.
|
|
264
|
-
`);
|
|
265
|
-
await mkdir(path.join(siteDir, 'pages', '010-guide'), { recursive: true });
|
|
266
|
-
await writeFile(path.join(siteDir, 'pages', '010-guide', 'content.md'), `---
|
|
267
|
-
page:
|
|
268
|
-
description: The visible guide page.
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-
# Guide
|
|
272
|
-
Guide content.
|
|
273
|
-
`);
|
|
274
|
-
await mkdir(path.join(siteDir, 'pages', '020-private'), { recursive: true });
|
|
275
|
-
await writeFile(path.join(siteDir, 'pages', '020-private', 'content.md'), `---
|
|
276
|
-
navigation:
|
|
277
|
-
listed: false
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
# Unlisted
|
|
281
|
-
Public but unlisted content.
|
|
282
|
-
`);
|
|
283
|
-
|
|
284
|
-
await runNorna(['--site-dir', siteDir, 'build']);
|
|
285
|
-
const distDir = path.join(path.dirname(siteDir), 'dist');
|
|
286
|
-
const homeHtml = await readFile(path.join(distDir, 'index.html'), 'utf8');
|
|
287
|
-
const guideHtml = await readFile(path.join(distDir, 'guide', 'index.html'), 'utf8');
|
|
288
|
-
const unlistedHtml = await readFile(path.join(distDir, 'private', 'index.html'), 'utf8');
|
|
289
|
-
|
|
290
|
-
assert.match(homeHtml, /<title>Welcome<\/title>/);
|
|
291
|
-
assert.doesNotMatch(homeHtml, /<meta name="description"/);
|
|
292
|
-
assert.match(homeHtml, /<img class="site-brand-logo"[^>]+alt="Welcome"/);
|
|
293
|
-
assert.match(homeHtml, /<a href="\/" aria-current="page">\s*Welcome\s*<\/a>/);
|
|
294
|
-
assert.match(homeHtml, /<a href="\/guide\/">\s*Guide\s*<\/a>/);
|
|
295
|
-
assert.doesNotMatch(homeHtml, />\s*Unlisted\s*<\/a>/);
|
|
296
|
-
assert.match(guideHtml, /<meta name="description" content="The visible guide page\."/);
|
|
297
|
-
assert.match(unlistedHtml, /Public but unlisted content/);
|
|
298
|
-
} finally {
|
|
299
|
-
await rm(root, { recursive: true, force: true });
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
test('footer build information uses the site language and fixed formatting', async () => {
|
|
304
|
-
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
305
|
-
try {
|
|
306
|
-
await writeFile(path.join(siteDir, 'config.yaml'), 'url: https://example.com/\nlanguage: sv-SE\n');
|
|
307
|
-
await writeFile(path.join(siteDir, 'sitewide-content.yaml'), `footer:
|
|
308
|
-
buildInfo: true
|
|
309
|
-
`);
|
|
310
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
311
|
-
page:
|
|
312
|
-
description: Footer build information.
|
|
313
|
-
---
|
|
314
|
-
|
|
315
|
-
# Footer
|
|
316
|
-
|
|
317
|
-
## Home {#home}
|
|
318
|
-
|
|
319
|
-
Content.
|
|
320
|
-
`);
|
|
321
|
-
|
|
322
|
-
await runNorna(['--site-dir', siteDir, 'build']);
|
|
323
|
-
const html = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
324
|
-
assert.match(html, /<span>Byggd [^<]+<\/span>/);
|
|
325
|
-
} finally {
|
|
326
|
-
await rm(root, { recursive: true, force: true });
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
test('page theme cannot define navigation logo settings', async () => {
|
|
331
|
-
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
332
|
-
try {
|
|
333
|
-
await mkdir(path.join(siteDir, 'pages', '010-guide'), { recursive: true });
|
|
334
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
335
|
-
page:
|
|
336
|
-
description: Root page
|
|
337
|
-
---
|
|
338
|
-
|
|
339
|
-
# Root
|
|
340
|
-
|
|
341
|
-
## Home {#home}
|
|
342
|
-
|
|
343
|
-
Root content.
|
|
344
|
-
`);
|
|
345
|
-
await writeFile(path.join(siteDir, 'pages', '010-guide', 'content.md'), `---
|
|
346
|
-
page:
|
|
347
|
-
description: Guide page
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
# Guide
|
|
351
|
-
Page content.
|
|
352
|
-
`);
|
|
353
|
-
await writeFile(path.join(siteDir, 'pages', '010-guide', 'theme.yaml'), `logo:
|
|
354
|
-
height: 3rem
|
|
355
|
-
`);
|
|
356
|
-
|
|
357
|
-
await assert.rejects(
|
|
358
|
-
runNorna(['--site-dir', siteDir, 'config:check']),
|
|
359
|
-
(error) => {
|
|
360
|
-
assert.match(error.output, /may not define navigation logo settings\. Optional logo display settings belong under "logo:" in site\/sitewide-content\.yaml/);
|
|
361
|
-
return true;
|
|
362
|
-
},
|
|
363
|
-
);
|
|
364
|
-
} finally {
|
|
365
|
-
await rm(root, { recursive: true, force: true });
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
test('navigation logo settings require one conventional logo file', async () => {
|
|
370
|
-
const { root, siteDir } = await createTempSite();
|
|
371
|
-
try {
|
|
372
|
-
await writeFile(path.join(siteDir, 'sitewide-content.yaml'), `logo:
|
|
373
|
-
height: 2rem
|
|
374
|
-
`);
|
|
375
|
-
|
|
376
|
-
await assert.rejects(
|
|
377
|
-
runNorna(['--site-dir', siteDir, 'config:check']),
|
|
378
|
-
(error) => {
|
|
379
|
-
assert.match(error.output, /Site-wide logo is configured, but no logo file was found/);
|
|
380
|
-
assert.match(error.output, /site\/public\/logo\.svg/);
|
|
381
|
-
return true;
|
|
382
|
-
},
|
|
383
|
-
);
|
|
384
|
-
} finally {
|
|
385
|
-
await rm(root, { recursive: true, force: true });
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
test('multiple conventional navigation logo files stop config validation', async () => {
|
|
390
|
-
const { root, siteDir } = await createTempSite();
|
|
391
|
-
try {
|
|
392
|
-
await mkdir(path.join(siteDir, 'public'), { recursive: true });
|
|
393
|
-
await writeFile(path.join(siteDir, 'public', 'logo.svg'), '<svg/>');
|
|
394
|
-
await writeFile(path.join(siteDir, 'public', 'logo.png'), 'png');
|
|
395
|
-
|
|
396
|
-
await assert.rejects(
|
|
397
|
-
runNorna(['--site-dir', siteDir, 'config:check']),
|
|
398
|
-
(error) => {
|
|
399
|
-
assert.match(error.output, /Found multiple logo files/);
|
|
400
|
-
assert.match(error.output, /site\/public\/logo\.svg/);
|
|
401
|
-
assert.match(error.output, /site\/public\/logo\.png/);
|
|
402
|
-
return true;
|
|
403
|
-
},
|
|
404
|
-
);
|
|
405
|
-
} finally {
|
|
406
|
-
await rm(root, { recursive: true, force: true });
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
test('content:check warns for local Markdown images but not external Markdown images', async () => {
|
|
411
|
-
const { root, siteDir } = await createTempSite();
|
|
412
|
-
try {
|
|
413
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
414
|
-
page:
|
|
415
|
-
description: Fixture
|
|
416
|
-
---
|
|
417
|
-
|
|
418
|
-
# Markdown Image Warning
|
|
419
|
-
|
|
420
|
-
## Intro {#intro}
|
|
421
|
-
|
|
422
|
-

|
|
423
|
-
|
|
424
|
-

|
|
425
|
-
|
|
426
|
-

|
|
427
|
-
`);
|
|
428
|
-
|
|
429
|
-
const { stdout } = await runContentScript(siteDir, ['--check']);
|
|
430
|
-
assert.match(stdout, /Markdown image "portrait\.jpg" references a local image that is not managed by Norna\./);
|
|
431
|
-
assert.doesNotMatch(stdout, /https:\/\/example\.com\/portrait\.jpg/);
|
|
432
|
-
assert.doesNotMatch(stdout, /\/favicon\.svg/);
|
|
433
|
-
} finally {
|
|
434
|
-
await rm(root, { recursive: true, force: true });
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
test('managed SVG images are copied as static image output', async () => {
|
|
439
|
-
const { root, siteDir } = await createTempSite();
|
|
440
|
-
try {
|
|
441
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
442
|
-
page:
|
|
443
|
-
description: Fixture
|
|
444
|
-
---
|
|
445
|
-
|
|
446
|
-
# SVG Fixture
|
|
447
|
-
|
|
448
|
-
## Intro {#intro}
|
|
449
|
-
|
|
450
|
-
\`\`\`norna-image-stack
|
|
451
|
-
- image: diagram.svg
|
|
452
|
-
alt: Diagram
|
|
453
|
-
\`\`\`
|
|
454
|
-
`);
|
|
455
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images'), { recursive: true });
|
|
456
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'diagram.svg'), '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 620"><rect width="900" height="620"/></svg>\n');
|
|
457
|
-
|
|
458
|
-
await runContentScript(siteDir, ['--check']);
|
|
459
|
-
await runNorna(['--site-dir', siteDir, 'images']);
|
|
460
|
-
|
|
461
|
-
const manifest = JSON.parse(await readFile(path.join(siteDir, '.norna', 'generated-images.json'), 'utf8'));
|
|
462
|
-
const entry = manifest['pages/000-home/images/diagram.svg'];
|
|
463
|
-
assert.equal(entry.kind, 'static');
|
|
464
|
-
assert.equal(entry.width, 900);
|
|
465
|
-
assert.equal(entry.height, 620);
|
|
466
|
-
assert.match(entry.src, /^\/images\/original\/pages\/000-home\/images\/diagram-[a-f0-9]{8}\.svg$/);
|
|
467
|
-
assert.equal(entry.variants, undefined);
|
|
468
|
-
assert.equal(await fileExists(path.join(siteDir, '.norna', 'public', entry.src.replace(/^\//, ''))), true);
|
|
469
|
-
} finally {
|
|
470
|
-
await rm(root, { recursive: true, force: true });
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
test('inline notes render as linked numbered margin notes', async () => {
|
|
475
|
-
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
476
|
-
try {
|
|
477
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
478
|
-
page:
|
|
479
|
-
description: Fixture
|
|
480
|
-
---
|
|
481
|
-
|
|
482
|
-
# Inline notes
|
|
483
|
-
|
|
484
|
-
## Intro {#intro}
|
|
485
|
-
|
|
486
|
-
The first paragraph points to an explanation.{note-ref}
|
|
487
|
-
|
|
488
|
-
{note: This explanation names \`sitewide-content.yaml\` and stays beside the
|
|
489
|
-
paragraph on wide screens.}
|
|
490
|
-
|
|
491
|
-
The second paragraph has its own explanation.{note-ref}
|
|
492
|
-
|
|
493
|
-
{note:
|
|
494
|
-
This is the second
|
|
495
|
-
explanation.
|
|
496
|
-
}
|
|
497
|
-
`);
|
|
498
|
-
|
|
499
|
-
await runNorna(['--site-dir', siteDir, 'build']);
|
|
500
|
-
const html = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
501
|
-
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>/);
|
|
502
|
-
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;">/);
|
|
503
|
-
assert.match(html, /<a class="section-note-number" href="#note-ref-home-intro-1" aria-label="Note 1">\s*1\s*<\/a>/);
|
|
504
|
-
assert.match(html, /This explanation names <code>sitewide-content\.yaml<\/code> and stays beside the paragraph on wide screens\./);
|
|
505
|
-
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>/);
|
|
506
|
-
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;">/);
|
|
507
|
-
assert.match(html, /This is the second explanation\./);
|
|
508
|
-
assert.doesNotMatch(html, /\{note:/);
|
|
509
|
-
} finally {
|
|
510
|
-
await rm(root, { recursive: true, force: true });
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
test('updated managed SVG images get updated static output', async () => {
|
|
515
|
-
const { root, siteDir } = await createTempSite();
|
|
516
|
-
try {
|
|
517
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
518
|
-
page:
|
|
519
|
-
description: Fixture
|
|
520
|
-
---
|
|
521
|
-
|
|
522
|
-
# SVG Update Fixture
|
|
523
|
-
|
|
524
|
-
## Intro {#intro}
|
|
525
|
-
|
|
526
|
-
\`\`\`norna-image-stack
|
|
527
|
-
- image: diagram.svg
|
|
528
|
-
alt: Diagram
|
|
529
|
-
\`\`\`
|
|
530
|
-
`);
|
|
531
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images'), { recursive: true });
|
|
532
|
-
const sourcePath = path.join(siteDir, 'pages', '000-home', 'images', 'diagram.svg');
|
|
533
|
-
await writeFile(sourcePath, '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 620"><rect width="900" height="620" fill="red"/></svg>\n');
|
|
534
|
-
|
|
535
|
-
await runContentScript(siteDir, ['--check']);
|
|
536
|
-
await runNorna(['--site-dir', siteDir, 'images']);
|
|
537
|
-
|
|
538
|
-
const firstManifest = JSON.parse(await readFile(path.join(siteDir, '.norna', 'generated-images.json'), 'utf8'));
|
|
539
|
-
const firstEntry = firstManifest['pages/000-home/images/diagram.svg'];
|
|
540
|
-
const firstOutputPath = path.join(siteDir, '.norna', 'public', firstEntry.src.replace(/^\//, ''));
|
|
541
|
-
|
|
542
|
-
await writeFile(sourcePath, '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 620"><rect width="900" height="620" fill="blue"/></svg>\n');
|
|
543
|
-
await runNorna(['--site-dir', siteDir, 'images']);
|
|
544
|
-
|
|
545
|
-
const secondManifest = JSON.parse(await readFile(path.join(siteDir, '.norna', 'generated-images.json'), 'utf8'));
|
|
546
|
-
const secondEntry = secondManifest['pages/000-home/images/diagram.svg'];
|
|
547
|
-
const secondOutputPath = path.join(siteDir, '.norna', 'public', secondEntry.src.replace(/^\//, ''));
|
|
548
|
-
|
|
549
|
-
assert.notEqual(secondEntry.sourceHash, firstEntry.sourceHash);
|
|
550
|
-
assert.notEqual(secondEntry.src, firstEntry.src);
|
|
551
|
-
assert.equal(await fileExists(firstOutputPath), false);
|
|
552
|
-
assert.match(await readFile(secondOutputPath, 'utf8'), /fill="blue"/);
|
|
553
|
-
} finally {
|
|
554
|
-
await rm(root, { recursive: true, force: true });
|
|
555
|
-
}
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
test('content:check warns when carousel SVG images have no intrinsic aspect ratio', async () => {
|
|
559
|
-
const { root, siteDir } = await createTempSite();
|
|
560
|
-
try {
|
|
561
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
562
|
-
page:
|
|
563
|
-
description: Fixture
|
|
564
|
-
---
|
|
565
|
-
|
|
566
|
-
# SVG Carousel Fixture
|
|
567
|
-
|
|
568
|
-
## Intro {#intro}
|
|
569
|
-
|
|
570
|
-
\`\`\`norna-image-carousel
|
|
571
|
-
- image: first.svg
|
|
572
|
-
alt: First
|
|
573
|
-
- image: second.svg
|
|
574
|
-
alt: Second
|
|
575
|
-
\`\`\`
|
|
576
|
-
`);
|
|
577
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images'), { recursive: true });
|
|
578
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'first.svg'), '<svg xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100"/></svg>\n');
|
|
579
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'second.svg'), '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect width="100" height="100"/></svg>\n');
|
|
580
|
-
|
|
581
|
-
const { stdout } = await runContentScript(siteDir, ['--check']);
|
|
582
|
-
assert.match(stdout, /Content check completed with warnings\./);
|
|
583
|
-
assert.match(stdout, /Carousel on line \d+ uses images without an intrinsic aspect ratio: first\.svg\./);
|
|
584
|
-
} finally {
|
|
585
|
-
await rm(root, { recursive: true, force: true });
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
test('norna-card-list images are managed image references', async () => {
|
|
590
|
-
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
591
|
-
try {
|
|
592
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
593
|
-
page:
|
|
594
|
-
description: Fixture
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
# Card List Fixture
|
|
598
|
-
|
|
599
|
-
## Help {#help}
|
|
600
|
-
|
|
601
|
-
\`\`\`norna-card-list
|
|
602
|
-
layout: image-left
|
|
603
|
-
flow: stack
|
|
604
|
-
size: l
|
|
605
|
-
width: narrow
|
|
606
|
-
|
|
607
|
-
- title: Adopt
|
|
608
|
-
text: Give a dog a new home.
|
|
609
|
-
image: adopt.svg
|
|
610
|
-
link: /adopt/
|
|
611
|
-
badge-text: Recommended
|
|
612
|
-
- title: Donate
|
|
613
|
-
text: Support the shelter.
|
|
614
|
-
\`\`\`
|
|
615
|
-
`);
|
|
616
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images'), { recursive: true });
|
|
617
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'adopt.svg'), '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 100"><rect width="160" height="100"/></svg>\n');
|
|
618
|
-
|
|
619
|
-
const { stdout } = await runContentScript(siteDir, ['--check']);
|
|
620
|
-
assert.match(stdout, /Content check passed\./);
|
|
621
|
-
|
|
622
|
-
await runNorna(['--site-dir', siteDir, 'images']);
|
|
623
|
-
const manifest = JSON.parse(await readFile(path.join(siteDir, '.norna', 'generated-images.json'), 'utf8'));
|
|
624
|
-
assert.equal(manifest['pages/000-home/images/adopt.svg'].kind, 'static');
|
|
625
|
-
|
|
626
|
-
await runNorna(['--site-dir', siteDir, 'build']);
|
|
627
|
-
const html = await readFile(path.join(root, 'dist', 'index.html'), 'utf8');
|
|
628
|
-
assert.match(html, /card-list-layout-image-left/);
|
|
629
|
-
assert.match(html, /card-list-flow-stack/);
|
|
630
|
-
assert.match(html, /card-list-size-l/);
|
|
631
|
-
assert.match(html, /card-list-width-narrow/);
|
|
632
|
-
assert.match(html, /card-list-item-has-image/);
|
|
633
|
-
assert.match(html, /class="card-list-badge">Recommended<\/p>/);
|
|
634
|
-
assert.match(html, /Give a dog a new home\./);
|
|
635
|
-
assert.match(html, /href="\/adopt\/"/);
|
|
636
|
-
assert.match(html, /src="\/images\/original\/pages\/000-home\/images\/adopt-[a-f0-9]{8}\.svg"/);
|
|
637
|
-
} finally {
|
|
638
|
-
await rm(root, { recursive: true, force: true });
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
test('content:check fails when a norna-card-list image file is missing', async () => {
|
|
643
|
-
const { root, siteDir } = await createTempSite();
|
|
644
|
-
try {
|
|
645
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
646
|
-
page:
|
|
647
|
-
description: Fixture
|
|
648
|
-
---
|
|
649
|
-
|
|
650
|
-
# Missing Card Image
|
|
651
|
-
|
|
652
|
-
## Help {#help}
|
|
653
|
-
|
|
654
|
-
\`\`\`norna-card-list
|
|
655
|
-
- title: Adopt
|
|
656
|
-
image: missing.svg
|
|
657
|
-
\`\`\`
|
|
658
|
-
`);
|
|
659
|
-
|
|
660
|
-
await assert.rejects(
|
|
661
|
-
() => runContentScript(siteDir, ['--check']),
|
|
662
|
-
(error) => {
|
|
663
|
-
assert.match(error.output, /Image "missing\.svg" does not exist at .*site\/pages\/000-home\/images\/missing\.svg or anywhere under any page image root\./);
|
|
664
|
-
return true;
|
|
665
|
-
},
|
|
666
|
-
);
|
|
667
|
-
} finally {
|
|
668
|
-
await rm(root, { recursive: true, force: true });
|
|
669
|
-
}
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
test('content:check fails for malformed norna-card-list blocks', async () => {
|
|
673
|
-
const { root, siteDir } = await createTempSite();
|
|
674
|
-
try {
|
|
675
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
676
|
-
page:
|
|
677
|
-
description: Fixture
|
|
678
|
-
---
|
|
679
|
-
|
|
680
|
-
# Malformed Card List
|
|
681
|
-
|
|
682
|
-
## Help {#help}
|
|
683
|
-
|
|
684
|
-
\`\`\`norna-card-list
|
|
685
|
-
- text: Missing title
|
|
686
|
-
\`\`\`
|
|
687
|
-
`);
|
|
688
|
-
|
|
689
|
-
await assert.rejects(
|
|
690
|
-
() => runContentScript(siteDir, ['--check']),
|
|
691
|
-
(error) => {
|
|
692
|
-
assert.match(error.output, /Invalid norna-card-list entry "- text: Missing title"\. Start each card with "- title: Card title"\./);
|
|
693
|
-
return true;
|
|
694
|
-
},
|
|
695
|
-
);
|
|
696
|
-
} finally {
|
|
697
|
-
await rm(root, { recursive: true, force: true });
|
|
698
|
-
}
|
|
699
|
-
});
|
|
700
|
-
|
|
701
|
-
test('content:check fails for invalid norna-card-list options', async () => {
|
|
702
|
-
const { root, siteDir } = await createTempSite();
|
|
703
|
-
try {
|
|
704
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
705
|
-
page:
|
|
706
|
-
description: Fixture
|
|
707
|
-
---
|
|
708
|
-
|
|
709
|
-
# Invalid Card List Options
|
|
710
|
-
|
|
711
|
-
## Help {#help}
|
|
712
|
-
|
|
713
|
-
\`\`\`norna-card-list
|
|
714
|
-
layout: floating
|
|
715
|
-
flow: list
|
|
716
|
-
size: huge
|
|
717
|
-
width: full
|
|
718
|
-
|
|
719
|
-
- title: Adopt
|
|
720
|
-
text: Give a dog a new home.
|
|
721
|
-
\`\`\`
|
|
722
|
-
`);
|
|
723
|
-
|
|
724
|
-
await assert.rejects(
|
|
725
|
-
() => runContentScript(siteDir, ['--check']),
|
|
726
|
-
(error) => {
|
|
727
|
-
assert.match(error.output, /Invalid norna-card-list layout "floating"\. Use one of: image-top, image-left, image-right\./);
|
|
728
|
-
return true;
|
|
729
|
-
},
|
|
730
|
-
);
|
|
731
|
-
} finally {
|
|
732
|
-
await rm(root, { recursive: true, force: true });
|
|
733
|
-
}
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
test('content:check fails for invalid norna-card-list width', async () => {
|
|
737
|
-
const { root, siteDir } = await createTempSite();
|
|
738
|
-
try {
|
|
739
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
740
|
-
page:
|
|
741
|
-
description: Fixture
|
|
742
|
-
---
|
|
743
|
-
|
|
744
|
-
# Invalid Card List Width
|
|
745
|
-
|
|
746
|
-
## Help {#help}
|
|
747
|
-
|
|
748
|
-
\`\`\`norna-card-list
|
|
749
|
-
width: full
|
|
750
|
-
|
|
751
|
-
- title: Adopt
|
|
752
|
-
text: Give a dog a new home.
|
|
753
|
-
\`\`\`
|
|
754
|
-
`);
|
|
755
|
-
|
|
756
|
-
await assert.rejects(
|
|
757
|
-
() => runContentScript(siteDir, ['--check']),
|
|
758
|
-
(error) => {
|
|
759
|
-
assert.match(error.output, /Invalid norna-card-list width "full"\. Use one of: text, narrow, normal, wide\./);
|
|
760
|
-
return true;
|
|
761
|
-
},
|
|
762
|
-
);
|
|
763
|
-
} finally {
|
|
764
|
-
await rm(root, { recursive: true, force: true });
|
|
765
|
-
}
|
|
766
|
-
});
|
|
767
|
-
|
|
768
|
-
test('content:check fails for invalid norna-card-list size', async () => {
|
|
769
|
-
const { root, siteDir } = await createTempSite();
|
|
770
|
-
try {
|
|
771
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
772
|
-
page:
|
|
773
|
-
description: Fixture
|
|
774
|
-
---
|
|
775
|
-
|
|
776
|
-
# Invalid Card List Size
|
|
777
|
-
|
|
778
|
-
## Help {#help}
|
|
779
|
-
|
|
780
|
-
\`\`\`norna-card-list
|
|
781
|
-
size: huge
|
|
782
|
-
|
|
783
|
-
- title: Adopt
|
|
784
|
-
text: Give a dog a new home.
|
|
785
|
-
\`\`\`
|
|
786
|
-
`);
|
|
787
|
-
|
|
788
|
-
await assert.rejects(
|
|
789
|
-
() => runContentScript(siteDir, ['--check']),
|
|
790
|
-
(error) => {
|
|
791
|
-
assert.match(error.output, /Invalid norna-card-list size "huge"\. Use one of: s, m, l, xl\./);
|
|
792
|
-
return true;
|
|
793
|
-
},
|
|
794
|
-
);
|
|
795
|
-
} finally {
|
|
796
|
-
await rm(root, { recursive: true, force: true });
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
test('content:sync moves Norna-managed images inside the same page image root', async () => {
|
|
801
|
-
const { root, siteDir } = await createTempSite();
|
|
802
|
-
try {
|
|
803
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
804
|
-
page:
|
|
805
|
-
description: Fixture
|
|
806
|
-
---
|
|
807
|
-
|
|
808
|
-
# Sync Fixture
|
|
809
|
-
|
|
810
|
-
## Work {#work}
|
|
811
|
-
|
|
812
|
-
\`\`\`norna-image-stack
|
|
813
|
-
- image: moved.jpg
|
|
814
|
-
alt: Moved image
|
|
815
|
-
\`\`\`
|
|
816
|
-
`);
|
|
817
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images', 'old'), { recursive: true });
|
|
818
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'old', 'moved.jpg'), 'fixture image');
|
|
819
|
-
|
|
820
|
-
await runContentScript(siteDir, ['--write', '--yes']);
|
|
821
|
-
|
|
822
|
-
const moved = await readFile(path.join(siteDir, 'pages', '000-home', 'images', 'moved.jpg'), 'utf8');
|
|
823
|
-
assert.equal(moved, 'fixture image');
|
|
824
|
-
} finally {
|
|
825
|
-
await rm(root, { recursive: true, force: true });
|
|
826
|
-
}
|
|
827
|
-
});
|
|
828
|
-
|
|
829
|
-
test('content:sync moves managed SVG images inside the same page image root', async () => {
|
|
830
|
-
const { root, siteDir } = await createTempSite();
|
|
831
|
-
try {
|
|
832
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
833
|
-
page:
|
|
834
|
-
description: Fixture
|
|
835
|
-
---
|
|
836
|
-
|
|
837
|
-
# SVG Sync Fixture
|
|
838
|
-
|
|
839
|
-
## Work {#work}
|
|
840
|
-
|
|
841
|
-
\`\`\`norna-image-stack
|
|
842
|
-
- image: moved.svg
|
|
843
|
-
alt: Moved diagram
|
|
844
|
-
\`\`\`
|
|
845
|
-
`);
|
|
846
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images', 'old'), { recursive: true });
|
|
847
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'old', 'moved.svg'), '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"></svg>\n');
|
|
848
|
-
|
|
849
|
-
await runContentScript(siteDir, ['--write', '--yes']);
|
|
850
|
-
|
|
851
|
-
const moved = await readFile(path.join(siteDir, 'pages', '000-home', 'images', 'moved.svg'), 'utf8');
|
|
852
|
-
assert.match(moved, /viewBox="0 0 10 10"/);
|
|
853
|
-
} finally {
|
|
854
|
-
await rm(root, { recursive: true, force: true });
|
|
855
|
-
}
|
|
856
|
-
});
|
|
857
|
-
|
|
858
|
-
test('content:sync moves Norna-managed images across page image roots when git is clean', async () => {
|
|
859
|
-
const { root, siteDir } = await createTempSite();
|
|
860
|
-
try {
|
|
861
|
-
const sourcePageDir = path.join(siteDir, 'pages', '010-source');
|
|
862
|
-
const targetPageDir = path.join(siteDir, 'pages', '020-target');
|
|
863
|
-
await mkdir(path.join(sourcePageDir, 'images'), { recursive: true });
|
|
864
|
-
await mkdir(targetPageDir, { recursive: true });
|
|
865
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
866
|
-
page:
|
|
867
|
-
description: Fixture
|
|
868
|
-
---
|
|
869
|
-
|
|
870
|
-
# Cross Page Sync
|
|
871
|
-
|
|
872
|
-
## Home {#home}
|
|
873
|
-
|
|
874
|
-
Text.
|
|
875
|
-
`);
|
|
876
|
-
await writeFile(path.join(sourcePageDir, 'content.md'), `---
|
|
877
|
-
page:
|
|
878
|
-
description: Fixture
|
|
879
|
-
---
|
|
880
|
-
|
|
881
|
-
# Source
|
|
882
|
-
|
|
883
|
-
## Old {#old}
|
|
884
|
-
|
|
885
|
-
Text.
|
|
886
|
-
`);
|
|
887
|
-
await writeFile(path.join(targetPageDir, 'content.md'), `---
|
|
888
|
-
page:
|
|
889
|
-
description: Fixture
|
|
890
|
-
---
|
|
891
|
-
|
|
892
|
-
# Target
|
|
893
|
-
|
|
894
|
-
## Work {#work}
|
|
895
|
-
|
|
896
|
-
\`\`\`norna-image-stack
|
|
897
|
-
- image: moved.jpg
|
|
898
|
-
\`\`\`
|
|
899
|
-
`);
|
|
900
|
-
await writeFile(path.join(sourcePageDir, 'images', 'moved.jpg'), 'page image');
|
|
901
|
-
await initCleanGitWorktree(root);
|
|
902
|
-
|
|
903
|
-
await runContentScript(siteDir, ['--write', '--yes']);
|
|
904
|
-
|
|
905
|
-
assert.equal(await fileExists(path.join(targetPageDir, 'images', 'moved.jpg')), true);
|
|
906
|
-
assert.equal(await fileExists(path.join(sourcePageDir, 'images', 'moved.jpg')), false);
|
|
907
|
-
} finally {
|
|
908
|
-
await rm(root, { recursive: true, force: true });
|
|
909
|
-
}
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
test('content:sync refuses cross-page image moves when git status is dirty', async () => {
|
|
913
|
-
const { root, siteDir } = await createTempSite();
|
|
914
|
-
try {
|
|
915
|
-
const sourcePageDir = path.join(siteDir, 'pages', '010-source');
|
|
916
|
-
const targetPageDir = path.join(siteDir, 'pages', '020-target');
|
|
917
|
-
await mkdir(path.join(sourcePageDir, 'images'), { recursive: true });
|
|
918
|
-
await mkdir(targetPageDir, { recursive: true });
|
|
919
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
920
|
-
page:
|
|
921
|
-
description: Fixture
|
|
922
|
-
---
|
|
923
|
-
|
|
924
|
-
# Dirty Cross Page Sync
|
|
925
|
-
|
|
926
|
-
## Home {#home}
|
|
927
|
-
|
|
928
|
-
Text.
|
|
929
|
-
`);
|
|
930
|
-
await writeFile(path.join(sourcePageDir, 'content.md'), `---
|
|
931
|
-
page:
|
|
932
|
-
description: Fixture
|
|
933
|
-
---
|
|
934
|
-
|
|
935
|
-
# Source
|
|
936
|
-
|
|
937
|
-
## Old {#old}
|
|
938
|
-
|
|
939
|
-
Text.
|
|
940
|
-
`);
|
|
941
|
-
await writeFile(path.join(targetPageDir, 'content.md'), `---
|
|
942
|
-
page:
|
|
943
|
-
description: Fixture
|
|
944
|
-
---
|
|
945
|
-
|
|
946
|
-
# Target
|
|
947
|
-
|
|
948
|
-
## Work {#work}
|
|
949
|
-
|
|
950
|
-
\`\`\`norna-image-stack
|
|
951
|
-
- image: moved.jpg
|
|
952
|
-
\`\`\`
|
|
953
|
-
`);
|
|
954
|
-
await writeFile(path.join(sourcePageDir, 'images', 'moved.jpg'), 'page image');
|
|
955
|
-
await initCleanGitWorktree(root);
|
|
956
|
-
await writeFile(path.join(root, 'dirty.txt'), 'dirty');
|
|
957
|
-
|
|
958
|
-
await assert.rejects(
|
|
959
|
-
() => runContentScript(siteDir, ['--write', '--yes']),
|
|
960
|
-
(error) => {
|
|
961
|
-
assert.match(error.output, /Cross-page content sync requires a clean git working tree before moving files between page image roots\./);
|
|
962
|
-
assert.match(error.output, /dirty\.txt/);
|
|
963
|
-
return true;
|
|
964
|
-
},
|
|
965
|
-
);
|
|
966
|
-
|
|
967
|
-
assert.equal(await fileExists(path.join(targetPageDir, 'images', 'moved.jpg')), false);
|
|
968
|
-
assert.equal(await fileExists(path.join(sourcePageDir, 'images', 'moved.jpg')), true);
|
|
969
|
-
} finally {
|
|
970
|
-
await rm(root, { recursive: true, force: true });
|
|
971
|
-
}
|
|
972
|
-
});
|
|
973
|
-
|
|
974
|
-
test('content:check reports cross-page image moves without requiring clean git status', async () => {
|
|
975
|
-
const { root, siteDir } = await createTempSite();
|
|
976
|
-
try {
|
|
977
|
-
const sourcePageDir = path.join(siteDir, 'pages', '010-source');
|
|
978
|
-
const targetPageDir = path.join(siteDir, 'pages', '020-target');
|
|
979
|
-
await mkdir(path.join(sourcePageDir, 'images'), { recursive: true });
|
|
980
|
-
await mkdir(targetPageDir, { recursive: true });
|
|
981
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
982
|
-
page:
|
|
983
|
-
description: Fixture
|
|
984
|
-
---
|
|
985
|
-
|
|
986
|
-
# Cross Page Check
|
|
987
|
-
|
|
988
|
-
## Home {#home}
|
|
989
|
-
|
|
990
|
-
Text.
|
|
991
|
-
`);
|
|
992
|
-
await writeFile(path.join(sourcePageDir, 'content.md'), `---
|
|
993
|
-
page:
|
|
994
|
-
description: Fixture
|
|
995
|
-
---
|
|
996
|
-
|
|
997
|
-
# Source
|
|
998
|
-
|
|
999
|
-
## Old {#old}
|
|
1000
|
-
|
|
1001
|
-
Text.
|
|
1002
|
-
`);
|
|
1003
|
-
await writeFile(path.join(targetPageDir, 'content.md'), `---
|
|
1004
|
-
page:
|
|
1005
|
-
description: Fixture
|
|
1006
|
-
---
|
|
1007
|
-
|
|
1008
|
-
# Target
|
|
1009
|
-
|
|
1010
|
-
## Work {#work}
|
|
1011
|
-
|
|
1012
|
-
\`\`\`norna-image-stack
|
|
1013
|
-
- image: moved.jpg
|
|
1014
|
-
\`\`\`
|
|
1015
|
-
`);
|
|
1016
|
-
await writeFile(path.join(sourcePageDir, 'images', 'moved.jpg'), 'page image');
|
|
1017
|
-
await writeFile(path.join(root, 'dirty.txt'), 'dirty');
|
|
1018
|
-
|
|
1019
|
-
await assert.rejects(
|
|
1020
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1021
|
-
(error) => {
|
|
1022
|
-
assert.match(error.output, /Image "moved\.jpg" is used here but is located in .*site\/pages\/010-source\/images\/moved\.jpg\./);
|
|
1023
|
-
assert.match(error.output, /Run norna content:sync to move it from .*site\/pages\/010-source to .*site\/pages\/020-target\./);
|
|
1024
|
-
assert.doesNotMatch(error.output, /requires a clean git working tree before moving files/);
|
|
1025
|
-
return true;
|
|
1026
|
-
},
|
|
1027
|
-
);
|
|
1028
|
-
} finally {
|
|
1029
|
-
await rm(root, { recursive: true, force: true });
|
|
1030
|
-
}
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
|
-
test('content:sync refuses to move a cross-page image still referenced by its current section', async () => {
|
|
1034
|
-
const { root, siteDir } = await createTempSite();
|
|
1035
|
-
try {
|
|
1036
|
-
const sourcePageDir = path.join(siteDir, 'pages', '010-source');
|
|
1037
|
-
const targetPageDir = path.join(siteDir, 'pages', '020-target');
|
|
1038
|
-
await mkdir(path.join(sourcePageDir, 'images'), { recursive: true });
|
|
1039
|
-
await mkdir(targetPageDir, { recursive: true });
|
|
1040
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1041
|
-
page:
|
|
1042
|
-
description: Fixture
|
|
1043
|
-
---
|
|
1044
|
-
|
|
1045
|
-
# Cross Page Copy
|
|
1046
|
-
|
|
1047
|
-
## Home {#home}
|
|
1048
|
-
|
|
1049
|
-
Text.
|
|
1050
|
-
`);
|
|
1051
|
-
await writeFile(path.join(sourcePageDir, 'content.md'), `---
|
|
1052
|
-
page:
|
|
1053
|
-
description: Fixture
|
|
1054
|
-
---
|
|
1055
|
-
|
|
1056
|
-
# Source
|
|
1057
|
-
|
|
1058
|
-
## Old {#old}
|
|
1059
|
-
|
|
1060
|
-
\`\`\`norna-image-stack
|
|
1061
|
-
- image: shared.jpg
|
|
1062
|
-
\`\`\`
|
|
1063
|
-
`);
|
|
1064
|
-
await writeFile(path.join(targetPageDir, 'content.md'), `---
|
|
1065
|
-
page:
|
|
1066
|
-
description: Fixture
|
|
1067
|
-
---
|
|
1068
|
-
|
|
1069
|
-
# Target
|
|
1070
|
-
|
|
1071
|
-
## Work {#work}
|
|
1072
|
-
|
|
1073
|
-
\`\`\`norna-image-stack
|
|
1074
|
-
- image: shared.jpg
|
|
1075
|
-
\`\`\`
|
|
1076
|
-
`);
|
|
1077
|
-
await writeFile(path.join(sourcePageDir, 'images', 'shared.jpg'), 'page image');
|
|
1078
|
-
await initCleanGitWorktree(root);
|
|
1079
|
-
|
|
1080
|
-
await assert.rejects(
|
|
1081
|
-
() => runContentScript(siteDir, ['--write', '--yes']),
|
|
1082
|
-
(error) => {
|
|
1083
|
-
assert.match(error.output, /Cannot relocate "shared\.jpg" from .*site\/pages\/010-source\/images\/shared\.jpg because it is still referenced from .*site\/pages\/010-source\/content\.md \[old\]\./);
|
|
1084
|
-
return true;
|
|
1085
|
-
},
|
|
1086
|
-
);
|
|
1087
|
-
|
|
1088
|
-
assert.equal(await fileExists(path.join(targetPageDir, 'images', 'shared.jpg')), false);
|
|
1089
|
-
assert.equal(await fileExists(path.join(sourcePageDir, 'images', 'shared.jpg')), true);
|
|
1090
|
-
} finally {
|
|
1091
|
-
await rm(root, { recursive: true, force: true });
|
|
1092
|
-
}
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
test('CLI content:sync refreshes generated images after moving a carousel image', async () => {
|
|
1096
|
-
const { root, siteDir } = await createTempSite();
|
|
1097
|
-
try {
|
|
1098
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1099
|
-
page:
|
|
1100
|
-
description: Fixture
|
|
1101
|
-
---
|
|
1102
|
-
|
|
1103
|
-
# Sync Fixture
|
|
1104
|
-
|
|
1105
|
-
## Work {#work}
|
|
1106
|
-
|
|
1107
|
-
\`\`\`norna-image-carousel
|
|
1108
|
-
- image: slide-one.jpg
|
|
1109
|
-
alt: Slide one
|
|
1110
|
-
- image: slide-two.jpg
|
|
1111
|
-
alt: Slide two
|
|
1112
|
-
\`\`\`
|
|
1113
|
-
`);
|
|
1114
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images', 'old'), { recursive: true });
|
|
1115
|
-
await cp(path.join(fixtureSiteDir, 'pages', '000-home', 'images', 'hero.jpg'), path.join(siteDir, 'pages', '000-home', 'images', 'old', 'slide-one.jpg'));
|
|
1116
|
-
await cp(path.join(fixtureSiteDir, 'pages', '000-home', 'images', 'detail.jpg'), path.join(siteDir, 'pages', '000-home', 'images', 'slide-two.jpg'), { force: true });
|
|
1117
|
-
|
|
1118
|
-
await runNorna(['--site-dir', siteDir, 'content:sync', '--yes']);
|
|
1119
|
-
|
|
1120
|
-
const manifest = JSON.parse(await readFile(path.join(siteDir, '.norna', 'generated-images.json'), 'utf8'));
|
|
1121
|
-
assert.equal(await fileExists(path.join(siteDir, 'pages', '000-home', 'images', 'slide-one.jpg')), true);
|
|
1122
|
-
assert.ok(manifest['pages/000-home/images/slide-one.jpg']);
|
|
1123
|
-
assert.ok(manifest['pages/000-home/images/slide-two.jpg']);
|
|
1124
|
-
} finally {
|
|
1125
|
-
await rm(root, { recursive: true, force: true });
|
|
1126
|
-
}
|
|
1127
|
-
});
|
|
1128
|
-
|
|
1129
|
-
test('automatic H2 and H3 ids render while explicit ids remain stable', async () => {
|
|
1130
|
-
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
1131
|
-
try {
|
|
1132
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1133
|
-
page:
|
|
1134
|
-
description: Fixture
|
|
1135
|
-
---
|
|
1136
|
-
|
|
1137
|
-
# Heading IDs
|
|
1138
|
-
|
|
1139
|
-
## Café and **code**
|
|
1140
|
-
|
|
1141
|
-
### More details
|
|
1142
|
-
|
|
1143
|
-
Automatic identifiers.
|
|
1144
|
-
|
|
1145
|
-
## Renamed heading {#stable-id}
|
|
1146
|
-
|
|
1147
|
-
Explicit identifier.
|
|
1148
|
-
`);
|
|
1149
|
-
|
|
1150
|
-
await runContentScript(siteDir, ['--check']);
|
|
1151
|
-
await runNorna(['--site-dir', siteDir, 'build']);
|
|
1152
|
-
|
|
1153
|
-
const html = await readFile(path.join(root, 'dist', 'index.html'), 'utf8');
|
|
1154
|
-
assert.match(html, /<h2 id="cafe-and-code">Café and <strong>code<\/strong><\/h2>/);
|
|
1155
|
-
assert.match(html, /<h3 id="more-details">More details<\/h3>/);
|
|
1156
|
-
assert.match(html, /<h2 id="stable-id">Renamed heading<\/h2>/);
|
|
1157
|
-
} finally {
|
|
1158
|
-
await rm(root, { recursive: true, force: true });
|
|
1159
|
-
}
|
|
1160
|
-
});
|
|
1161
|
-
|
|
1162
|
-
test('content:check fails for malformed Norna image blocks', async () => {
|
|
1163
|
-
const { root, siteDir } = await createTempSite();
|
|
1164
|
-
try {
|
|
1165
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1166
|
-
page:
|
|
1167
|
-
description: Fixture
|
|
1168
|
-
---
|
|
1169
|
-
|
|
1170
|
-
# Malformed Block
|
|
1171
|
-
|
|
1172
|
-
## Intro {#intro}
|
|
1173
|
-
|
|
1174
|
-
\`\`\`norna-image-stack
|
|
1175
|
-
image hero.jpg
|
|
1176
|
-
alt: Hero
|
|
1177
|
-
\`\`\`
|
|
1178
|
-
`);
|
|
1179
|
-
|
|
1180
|
-
await assert.rejects(
|
|
1181
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1182
|
-
(error) => {
|
|
1183
|
-
assert.match(error.output, /Invalid norna-image-stack entry "image hero\.jpg"\. Start each image with "- image: filename\.jpg"\./);
|
|
1184
|
-
return true;
|
|
1185
|
-
},
|
|
1186
|
-
);
|
|
1187
|
-
} finally {
|
|
1188
|
-
await rm(root, { recursive: true, force: true });
|
|
1189
|
-
}
|
|
1190
|
-
});
|
|
1191
|
-
|
|
1192
|
-
test('content:check fails for unknown Norna block names', async () => {
|
|
1193
|
-
const { root, siteDir } = await createTempSite();
|
|
1194
|
-
try {
|
|
1195
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images', 'plain'), { recursive: true });
|
|
1196
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'plain', 'image.jpg'), 'fixture image');
|
|
1197
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1198
|
-
page:
|
|
1199
|
-
description: Fixture
|
|
1200
|
-
---
|
|
1201
|
-
|
|
1202
|
-
# Unknown Block
|
|
1203
|
-
|
|
1204
|
-
## Plain {#plain}
|
|
1205
|
-
|
|
1206
|
-
\`\`\`norna-gallery-stack
|
|
1207
|
-
- image: image.jpg
|
|
1208
|
-
\`\`\`
|
|
1209
|
-
`);
|
|
1210
|
-
|
|
1211
|
-
await assert.rejects(
|
|
1212
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1213
|
-
(error) => {
|
|
1214
|
-
assert.match(error.output, /Unknown Norna block "norna-gallery-stack"\. Use one of: norna-image-stack, norna-image-carousel, norna-card-list\./);
|
|
1215
|
-
assert.match(error.output, /Use norna-image-stack for one or more stacked images\./);
|
|
1216
|
-
assert.match(error.output, /Example: ```norna-image-stack\n- image: filename\.jpg\n```/);
|
|
1217
|
-
return true;
|
|
1218
|
-
},
|
|
1219
|
-
);
|
|
1220
|
-
} finally {
|
|
1221
|
-
await rm(root, { recursive: true, force: true });
|
|
1222
|
-
}
|
|
1223
|
-
});
|
|
1224
|
-
|
|
1225
|
-
test('content:check rejects the removed norna-note block', async () => {
|
|
1226
|
-
const { root, siteDir } = await createTempSite();
|
|
1227
|
-
try {
|
|
1228
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1229
|
-
page:
|
|
1230
|
-
description: Fixture
|
|
1231
|
-
---
|
|
1232
|
-
|
|
1233
|
-
# Removed note block
|
|
1234
|
-
|
|
1235
|
-
## Intro {#intro}
|
|
1236
|
-
|
|
1237
|
-
\`\`\`norna-note
|
|
1238
|
-
This block is no longer supported.
|
|
1239
|
-
\`\`\`
|
|
1240
|
-
`);
|
|
1241
|
-
|
|
1242
|
-
await assert.rejects(
|
|
1243
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1244
|
-
(error) => {
|
|
1245
|
-
assert.match(error.output, /Unknown Norna block "norna-note"\. Use one of: norna-image-stack, norna-image-carousel, norna-card-list\./);
|
|
1246
|
-
return true;
|
|
1247
|
-
},
|
|
1248
|
-
);
|
|
1249
|
-
} finally {
|
|
1250
|
-
await rm(root, { recursive: true, force: true });
|
|
1251
|
-
}
|
|
1252
|
-
});
|
|
1253
|
-
|
|
1254
|
-
test('content:check supports tilde fenced Norna blocks', async () => {
|
|
1255
|
-
const { root, siteDir } = await createTempSite();
|
|
1256
|
-
try {
|
|
1257
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images'), { recursive: true });
|
|
1258
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'hero.jpg'), 'fixture image');
|
|
1259
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1260
|
-
page:
|
|
1261
|
-
description: Fixture
|
|
1262
|
-
---
|
|
1263
|
-
|
|
1264
|
-
# Tilde Fence
|
|
1265
|
-
|
|
1266
|
-
## Intro {#intro}
|
|
1267
|
-
|
|
1268
|
-
~~~norna-image-stack
|
|
1269
|
-
- image: hero.jpg
|
|
1270
|
-
~~~
|
|
1271
|
-
`);
|
|
1272
|
-
|
|
1273
|
-
const result = await runContentScript(siteDir, ['--check']);
|
|
1274
|
-
assert.match(result.stdout, /Content check passed\./);
|
|
1275
|
-
} finally {
|
|
1276
|
-
await rm(root, { recursive: true, force: true });
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
1279
|
-
|
|
1280
|
-
test('content:check ignores Norna blocks shown inside longer code fences', async () => {
|
|
1281
|
-
const { root, siteDir } = await createTempSite();
|
|
1282
|
-
try {
|
|
1283
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1284
|
-
page:
|
|
1285
|
-
description: Fixture
|
|
1286
|
-
---
|
|
1287
|
-
|
|
1288
|
-
# Code Example
|
|
1289
|
-
|
|
1290
|
-
## Intro {#intro}
|
|
1291
|
-
|
|
1292
|
-
\`\`\`\`md
|
|
1293
|
-
\`\`\`norna-image-stack
|
|
1294
|
-
- image: missing-example.jpg
|
|
1295
|
-
\`\`\`
|
|
1296
|
-
\`\`\`\`
|
|
1297
|
-
`);
|
|
1298
|
-
|
|
1299
|
-
const result = await runContentScript(siteDir, ['--check']);
|
|
1300
|
-
assert.match(result.stdout, /Content check passed\./);
|
|
1301
|
-
} finally {
|
|
1302
|
-
await rm(root, { recursive: true, force: true });
|
|
1303
|
-
}
|
|
1304
|
-
});
|
|
1305
|
-
|
|
1306
|
-
test('content:check fails for likely Norna block names outside fences', async () => {
|
|
1307
|
-
const { root, siteDir } = await createTempSite();
|
|
1308
|
-
try {
|
|
1309
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1310
|
-
page:
|
|
1311
|
-
description: Fixture
|
|
1312
|
-
---
|
|
1313
|
-
|
|
1314
|
-
# Missing Fence
|
|
1315
|
-
|
|
1316
|
-
## Intro {#intro}
|
|
1317
|
-
|
|
1318
|
-
norna-image-stack
|
|
1319
|
-
- image: hero.jpg
|
|
1320
|
-
`);
|
|
1321
|
-
|
|
1322
|
-
await assert.rejects(
|
|
1323
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1324
|
-
(error) => {
|
|
1325
|
-
assert.match(error.output, /Found "norna-image-stack" outside a code block\./);
|
|
1326
|
-
assert.match(error.output, /Start the block like this:\n```norna-image-stack\n- image: filename\.jpg\n```/);
|
|
1327
|
-
return true;
|
|
1328
|
-
},
|
|
1329
|
-
);
|
|
1330
|
-
} finally {
|
|
1331
|
-
await rm(root, { recursive: true, force: true });
|
|
1332
|
-
}
|
|
1333
|
-
});
|
|
1334
|
-
|
|
1335
|
-
test('content:check fails for short Norna block fences', async () => {
|
|
1336
|
-
const { root, siteDir } = await createTempSite();
|
|
1337
|
-
try {
|
|
1338
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1339
|
-
page:
|
|
1340
|
-
description: Fixture
|
|
1341
|
-
---
|
|
1342
|
-
|
|
1343
|
-
# Short Fence
|
|
1344
|
-
|
|
1345
|
-
## Intro {#intro}
|
|
1346
|
-
|
|
1347
|
-
\`\`norna-image-stack
|
|
1348
|
-
- image: hero.jpg
|
|
1349
|
-
\`\`
|
|
1350
|
-
`);
|
|
1351
|
-
|
|
1352
|
-
await assert.rejects(
|
|
1353
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1354
|
-
(error) => {
|
|
1355
|
-
assert.match(error.output, /Invalid Norna block start for "norna-image-stack"\./);
|
|
1356
|
-
assert.match(error.output, /Use three backticks or three tildes/);
|
|
1357
|
-
return true;
|
|
1358
|
-
},
|
|
1359
|
-
);
|
|
1360
|
-
} finally {
|
|
1361
|
-
await rm(root, { recursive: true, force: true });
|
|
1362
|
-
}
|
|
1363
|
-
});
|
|
1364
|
-
|
|
1365
|
-
test('content:check fails for unclosed Norna blocks', async () => {
|
|
1366
|
-
const { root, siteDir } = await createTempSite();
|
|
1367
|
-
try {
|
|
1368
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1369
|
-
page:
|
|
1370
|
-
description: Fixture
|
|
1371
|
-
---
|
|
1372
|
-
|
|
1373
|
-
# Unclosed Fence
|
|
1374
|
-
|
|
1375
|
-
## Intro {#intro}
|
|
1376
|
-
|
|
1377
|
-
\`\`\`norna-image-stack
|
|
1378
|
-
- image: hero.jpg
|
|
1379
|
-
~~~
|
|
1380
|
-
`);
|
|
1381
|
-
|
|
1382
|
-
await assert.rejects(
|
|
1383
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1384
|
-
(error) => {
|
|
1385
|
-
assert.match(error.output, /This Norna block was started on line \d+ but not closed\./);
|
|
1386
|
-
assert.match(error.output, /Add a closing ``` line after the last entry\./);
|
|
1387
|
-
return true;
|
|
1388
|
-
},
|
|
1389
|
-
);
|
|
1390
|
-
} finally {
|
|
1391
|
-
await rm(root, { recursive: true, force: true });
|
|
1392
|
-
}
|
|
1393
|
-
});
|
|
1394
|
-
|
|
1395
|
-
test('content:check continues after malformed Norna blocks', async () => {
|
|
1396
|
-
const { root, siteDir } = await createTempSite();
|
|
1397
|
-
try {
|
|
1398
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1399
|
-
page:
|
|
1400
|
-
description: Fixture
|
|
1401
|
-
---
|
|
1402
|
-
|
|
1403
|
-
# Multiple Issues
|
|
1404
|
-
|
|
1405
|
-
## Intro {#intro}
|
|
1406
|
-
|
|
1407
|
-
\`\`\`norna-image-stack
|
|
1408
|
-
image broken.jpg
|
|
1409
|
-
alt: Broken
|
|
1410
|
-
\`\`\`
|
|
1411
|
-
|
|
1412
|
-
\`\`\`norna-image-stack
|
|
1413
|
-
- image: missing-intro.jpg
|
|
1414
|
-
alt: Missing intro
|
|
1415
|
-
\`\`\`
|
|
1416
|
-
|
|
1417
|
-
## More {#more}
|
|
1418
|
-
|
|
1419
|
-
\`\`\`norna-image-stack
|
|
1420
|
-
- image: missing-more.jpg
|
|
1421
|
-
alt: Missing more
|
|
1422
|
-
\`\`\`
|
|
1423
|
-
`);
|
|
1424
|
-
|
|
1425
|
-
await assert.rejects(
|
|
1426
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1427
|
-
(error) => {
|
|
1428
|
-
assert.match(error.output, /Invalid norna-image-stack entry "image broken\.jpg"\. Start each image with "- image: filename\.jpg"\./);
|
|
1429
|
-
assert.match(error.output, /Image "missing-intro\.jpg" does not exist at .*site\/pages\/000-home\/images\/missing-intro\.jpg or anywhere under any page image root\./);
|
|
1430
|
-
assert.match(error.output, /Image "missing-more\.jpg" does not exist at .*site\/pages\/000-home\/images\/missing-more\.jpg or anywhere under any page image root\./);
|
|
1431
|
-
return true;
|
|
1432
|
-
},
|
|
1433
|
-
);
|
|
1434
|
-
} finally {
|
|
1435
|
-
await rm(root, { recursive: true, force: true });
|
|
1436
|
-
}
|
|
1437
|
-
});
|
|
1438
|
-
|
|
1439
|
-
test('content:check allows Norna images without alt text', async () => {
|
|
1440
|
-
const { root, siteDir } = await createTempSite();
|
|
1441
|
-
try {
|
|
1442
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images'), { recursive: true });
|
|
1443
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'hero.jpg'), 'fixture image');
|
|
1444
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1445
|
-
page:
|
|
1446
|
-
description: Fixture
|
|
1447
|
-
---
|
|
1448
|
-
|
|
1449
|
-
# Missing Alt
|
|
1450
|
-
|
|
1451
|
-
## Intro {#intro}
|
|
1452
|
-
|
|
1453
|
-
\`\`\`norna-image-stack
|
|
1454
|
-
- image: hero.jpg
|
|
1455
|
-
\`\`\`
|
|
1456
|
-
`);
|
|
1457
|
-
|
|
1458
|
-
const result = await runContentScript(siteDir, ['--check']);
|
|
1459
|
-
assert.match(result.stdout, /Content check passed\./);
|
|
1460
|
-
} finally {
|
|
1461
|
-
await rm(root, { recursive: true, force: true });
|
|
1462
|
-
}
|
|
1463
|
-
});
|
|
1464
|
-
|
|
1465
|
-
test('content:check reports single-image carousel and missing file together', async () => {
|
|
1466
|
-
const { root, siteDir } = await createTempSite();
|
|
1467
|
-
try {
|
|
1468
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1469
|
-
page:
|
|
1470
|
-
description: Fixture
|
|
1471
|
-
---
|
|
1472
|
-
|
|
1473
|
-
# Beginner Errors
|
|
1474
|
-
|
|
1475
|
-
## Plain {#plain}
|
|
1476
|
-
|
|
1477
|
-
\`\`\`norna-image-carousel
|
|
1478
|
-
- image: foo.jpg
|
|
1479
|
-
\`\`\`
|
|
1480
|
-
`);
|
|
1481
|
-
|
|
1482
|
-
await assert.rejects(
|
|
1483
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1484
|
-
(error) => {
|
|
1485
|
-
assert.match(error.output, /norna-image-carousel on line \d+ contains 1 image\. A carousel needs at least two images\./);
|
|
1486
|
-
assert.match(error.output, /Image "foo\.jpg" does not exist at .*site\/pages\/000-home\/images\/foo\.jpg or anywhere under any page image root\./);
|
|
1487
|
-
return true;
|
|
1488
|
-
},
|
|
1489
|
-
);
|
|
1490
|
-
} finally {
|
|
1491
|
-
await rm(root, { recursive: true, force: true });
|
|
1492
|
-
}
|
|
1493
|
-
});
|
|
1494
|
-
|
|
1495
|
-
test('content:check fails when a Norna-managed image file is missing', async () => {
|
|
1496
|
-
const { root, siteDir } = await createTempSite();
|
|
1497
|
-
try {
|
|
1498
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1499
|
-
page:
|
|
1500
|
-
description: Fixture
|
|
1501
|
-
---
|
|
1502
|
-
|
|
1503
|
-
# Missing Image
|
|
1504
|
-
|
|
1505
|
-
## Intro {#intro}
|
|
1506
|
-
|
|
1507
|
-
\`\`\`norna-image-stack
|
|
1508
|
-
- image: missing.jpg
|
|
1509
|
-
alt: Missing
|
|
1510
|
-
\`\`\`
|
|
1511
|
-
`);
|
|
1512
|
-
|
|
1513
|
-
await assert.rejects(
|
|
1514
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1515
|
-
(error) => {
|
|
1516
|
-
assert.match(error.output, /Image "missing\.jpg" does not exist at .*site\/pages\/000-home\/images\/missing\.jpg or anywhere under any page image root\./);
|
|
1517
|
-
return true;
|
|
1518
|
-
},
|
|
1519
|
-
);
|
|
1520
|
-
} finally {
|
|
1521
|
-
await rm(root, { recursive: true, force: true });
|
|
1522
|
-
}
|
|
1523
|
-
});
|
|
1524
|
-
|
|
1525
|
-
test('content:check fails when a page repeats a section id', async () => {
|
|
1526
|
-
const { root, siteDir } = await createTempSite();
|
|
1527
|
-
try {
|
|
1528
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1529
|
-
page:
|
|
1530
|
-
description: Fixture
|
|
1531
|
-
---
|
|
1532
|
-
|
|
1533
|
-
# Duplicate Section
|
|
1534
|
-
|
|
1535
|
-
## Intro {#intro}
|
|
1536
|
-
|
|
1537
|
-
First.
|
|
1538
|
-
|
|
1539
|
-
## Again {#intro}
|
|
1540
|
-
|
|
1541
|
-
Second.
|
|
1542
|
-
`);
|
|
1543
|
-
|
|
1544
|
-
await assert.rejects(
|
|
1545
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1546
|
-
(error) => {
|
|
1547
|
-
assert.match(error.output, /Two headings resolve to id "intro"\./);
|
|
1548
|
-
return true;
|
|
1549
|
-
},
|
|
1550
|
-
);
|
|
1551
|
-
} finally {
|
|
1552
|
-
await rm(root, { recursive: true, force: true });
|
|
1553
|
-
}
|
|
1554
|
-
});
|
|
1555
|
-
|
|
1556
|
-
test('content:check reports collisions between automatic H2 and H3 ids', async () => {
|
|
1557
|
-
const { root, siteDir } = await createTempSite();
|
|
1558
|
-
try {
|
|
1559
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1560
|
-
page:
|
|
1561
|
-
description: Fixture
|
|
1562
|
-
---
|
|
1563
|
-
|
|
1564
|
-
# Automatic collision
|
|
1565
|
-
|
|
1566
|
-
## Förstå
|
|
1567
|
-
|
|
1568
|
-
Section text.
|
|
1569
|
-
|
|
1570
|
-
### Forsta
|
|
1571
|
-
|
|
1572
|
-
Topic text.
|
|
1573
|
-
`);
|
|
1574
|
-
|
|
1575
|
-
await assert.rejects(
|
|
1576
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1577
|
-
(error) => {
|
|
1578
|
-
assert.match(error.output, /Two headings resolve to id "forsta"\./);
|
|
1579
|
-
assert.match(error.output, /The other heading is on line \d+\./);
|
|
1580
|
-
assert.match(error.output, /Add a unique explicit id to at least one heading/);
|
|
1581
|
-
return true;
|
|
1582
|
-
},
|
|
1583
|
-
);
|
|
1584
|
-
} finally {
|
|
1585
|
-
await rm(root, { recursive: true, force: true });
|
|
1586
|
-
}
|
|
1587
|
-
});
|
|
1588
|
-
|
|
1589
|
-
test('content:check rejects removed sections frontmatter', async () => {
|
|
1590
|
-
const { root, siteDir } = await createTempSite();
|
|
1591
|
-
try {
|
|
1592
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1593
|
-
page:
|
|
1594
|
-
description: Fixture
|
|
1595
|
-
sections:
|
|
1596
|
-
intro: {}
|
|
1597
|
-
---
|
|
1598
|
-
|
|
1599
|
-
# Removed Section Metadata
|
|
1600
|
-
|
|
1601
|
-
## Intro {#intro}
|
|
1602
|
-
|
|
1603
|
-
Text.
|
|
1604
|
-
`);
|
|
1605
|
-
|
|
1606
|
-
await assert.rejects(
|
|
1607
|
-
() => runContentScript(siteDir, ['--check']),
|
|
1608
|
-
(error) => {
|
|
1609
|
-
assert.match(error.output, /defines "sections" at the top level, but it is not a valid top-level content field\./);
|
|
1610
|
-
return true;
|
|
1611
|
-
},
|
|
1612
|
-
);
|
|
1613
|
-
} finally {
|
|
1614
|
-
await rm(root, { recursive: true, force: true });
|
|
1615
|
-
}
|
|
1616
|
-
});
|
|
1617
|
-
|
|
1618
|
-
test('content:sync refuses ambiguous image relocation', async () => {
|
|
1619
|
-
const { root, siteDir } = await createTempSite();
|
|
1620
|
-
try {
|
|
1621
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1622
|
-
page:
|
|
1623
|
-
description: Fixture
|
|
1624
|
-
---
|
|
1625
|
-
|
|
1626
|
-
# Ambiguous Move
|
|
1627
|
-
|
|
1628
|
-
## Work {#work}
|
|
1629
|
-
|
|
1630
|
-
\`\`\`norna-image-stack
|
|
1631
|
-
- image: shared.jpg
|
|
1632
|
-
alt: Shared
|
|
1633
|
-
\`\`\`
|
|
1634
|
-
`);
|
|
1635
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images', 'old-a'), { recursive: true });
|
|
1636
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images', 'old-b'), { recursive: true });
|
|
1637
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'old-a', 'shared.jpg'), 'a');
|
|
1638
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'old-b', 'shared.jpg'), 'b');
|
|
1639
|
-
|
|
1640
|
-
await assert.rejects(
|
|
1641
|
-
() => runContentScript(siteDir, ['--write', '--yes']),
|
|
1642
|
-
(error) => {
|
|
1643
|
-
assert.match(error.output, /Cannot relocate "shared\.jpg"\. Multiple files with this filename were found:/);
|
|
1644
|
-
assert.match(error.output, /site\/pages\/000-home\/images\/old-a\/shared\.jpg/);
|
|
1645
|
-
assert.match(error.output, /site\/pages\/000-home\/images\/old-b\/shared\.jpg/);
|
|
1646
|
-
return true;
|
|
1647
|
-
},
|
|
1648
|
-
);
|
|
1649
|
-
|
|
1650
|
-
assert.equal(await fileExists(path.join(siteDir, 'pages', '000-home', 'images', 'shared.jpg')), false);
|
|
1651
|
-
assert.equal(await fileExists(path.join(siteDir, 'pages', '000-home', 'images', 'old-a', 'shared.jpg')), true);
|
|
1652
|
-
assert.equal(await fileExists(path.join(siteDir, 'pages', '000-home', 'images', 'old-b', 'shared.jpg')), true);
|
|
1653
|
-
} finally {
|
|
1654
|
-
await rm(root, { recursive: true, force: true });
|
|
1655
|
-
}
|
|
1656
|
-
});
|
|
1657
|
-
|
|
1658
|
-
test('content:sync moves one page-local image once when multiple sections reference it', async () => {
|
|
1659
|
-
const { root, siteDir } = await createTempSite();
|
|
1660
|
-
try {
|
|
1661
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'content.md'), `---
|
|
1662
|
-
page:
|
|
1663
|
-
description: Fixture
|
|
1664
|
-
---
|
|
1665
|
-
|
|
1666
|
-
# Copied Image Reference
|
|
1667
|
-
|
|
1668
|
-
## Intro {#intro}
|
|
1669
|
-
|
|
1670
|
-
\`\`\`norna-image-stack
|
|
1671
|
-
- image: shared.jpg
|
|
1672
|
-
alt: Shared original
|
|
1673
|
-
\`\`\`
|
|
1674
|
-
|
|
1675
|
-
## Plain {#plain}
|
|
1676
|
-
|
|
1677
|
-
\`\`\`norna-image-stack
|
|
1678
|
-
- image: shared.jpg
|
|
1679
|
-
alt: Shared copy
|
|
1680
|
-
\`\`\`
|
|
1681
|
-
`);
|
|
1682
|
-
await mkdir(path.join(siteDir, 'pages', '000-home', 'images', 'intro'), { recursive: true });
|
|
1683
|
-
await writeFile(path.join(siteDir, 'pages', '000-home', 'images', 'intro', 'shared.jpg'), 'shared image');
|
|
1684
|
-
|
|
1685
|
-
await runContentScript(siteDir, ['--write', '--yes']);
|
|
1686
|
-
|
|
1687
|
-
assert.equal(await fileExists(path.join(siteDir, 'pages', '000-home', 'images', 'intro', 'shared.jpg')), false);
|
|
1688
|
-
assert.equal(await fileExists(path.join(siteDir, 'pages', '000-home', 'images', 'shared.jpg')), true);
|
|
1689
|
-
await runContentScript(siteDir, ['--check']);
|
|
1690
|
-
} finally {
|
|
1691
|
-
await rm(root, { recursive: true, force: true });
|
|
1692
|
-
}
|
|
1693
|
-
});
|