@janga/norna 0.7.16 → 0.7.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/astro.config.mjs +35 -3
- package/docs/README.md +10 -10
- package/docs/commands.md +2 -2
- package/docs/content.md +82 -102
- package/docs/design/navigation-and-theme-plan.md +115 -0
- package/docs/design/norna-diagram-design.md +9 -9
- package/docs/design/site-examples-structure.md +9 -9
- package/docs/engine-development.md +9 -9
- package/docs/getting-started.md +20 -15
- package/docs/images-and-metadata.md +10 -9
- package/docs/pages.md +134 -0
- package/docs/public-files.md +49 -36
- package/docs/publishing.md +19 -3
- package/docs/site-files.md +29 -28
- package/docs/sitewide-content.md +20 -36
- package/docs/theme.md +20 -20
- package/docs/typography.md +10 -10
- package/examples/README.md +2 -2
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/site.schema.json +17 -41
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/sitewide.schema.json +26 -67
- package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/theme.schema.json +185 -88
- package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -1
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev-local.json +4 -4
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.json +5 -5
- package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +33 -23
- package/examples/complete-sites/dog-shelter-multi-page/README.md +1 -1
- package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/_metadata.json +8 -8
- package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +20 -20
- package/examples/complete-sites/dog-shelter-multi-page/site/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/{routes → pages}/010-dogs/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/{routes → pages}/020-adopt/content.md +3 -4
- package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.yaml +2 -4
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/site.schema.json +17 -41
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/sitewide.schema.json +26 -67
- package/examples/complete-sites/dog-shelter-single-page/.astro/collections/theme.schema.json +185 -88
- package/examples/complete-sites/dog-shelter-single-page/.astro/data-store.json +1 -1
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev-local.json +8 -0
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.json +5 -5
- package/examples/complete-sites/dog-shelter-single-page/.astro/dev.log +16 -12
- package/examples/complete-sites/dog-shelter-single-page/README.md +1 -1
- package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/_metadata.json +8 -8
- package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +12 -12
- package/examples/complete-sites/dog-shelter-single-page/site/content.md +5 -2
- package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.yaml +2 -4
- package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +17 -41
- package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +26 -67
- package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +185 -88
- package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -1
- package/examples/feature-demos/media-and-surfaces/.astro/dev.log +4 -14
- package/examples/feature-demos/media-and-surfaces/README.md +2 -2
- package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +9 -9
- package/examples/feature-demos/media-and-surfaces/site/.norna/generated-images.json +16 -16
- package/examples/feature-demos/media-and-surfaces/site/content.md +4 -7
- package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/010-media/content.md +6 -5
- package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/020-surfaces/content.md +7 -6
- package/examples/feature-demos/media-and-surfaces/site/sitewide-content.yaml +0 -2
- package/examples/feature-demos/sitewide-content/README.md +3 -3
- package/examples/feature-demos/sitewide-content/site/content.md +8 -7
- package/examples/feature-demos/sitewide-content/site/pages/010-logo-and-navigation/content.md +17 -0
- package/examples/feature-demos/sitewide-content/site/{routes → pages}/020-notices/content.md +7 -6
- package/examples/feature-demos/sitewide-content/site/{routes → pages}/030-footer/content.md +6 -5
- package/examples/feature-demos/sitewide-content/site/sitewide-content.yaml +4 -13
- package/examples/feature-demos/theme-presets/README.md +1 -1
- package/examples/feature-demos/theme-presets/site/.norna/generated-images.json +60 -60
- package/examples/feature-demos/theme-presets/site/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/010-portfolio/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/020-documentation/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/030-project/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/{routes → pages}/040-statement/content.md +4 -7
- package/examples/feature-demos/theme-presets/site/sitewide-content.yaml +1 -10
- package/fixtures/basic/site/content.md +5 -6
- package/fixtures/basic/site/sitewide-content.yaml +1 -8
- package/fixtures/content-model-v2/site/content.md +11 -13
- package/fixtures/content-model-v2/site/pages/010-guide/content.md +18 -0
- package/fixtures/nested-pages/site/.norna/generated-images.json +10 -0
- package/fixtures/nested-pages/site/config.yaml +1 -0
- package/fixtures/nested-pages/site/content.md +15 -0
- package/fixtures/nested-pages/site/pages/010-guides/content.md +15 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/content.md +21 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/images/diagram.svg +21 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/010-installation/pages/010-macos/content.md +14 -0
- package/fixtures/nested-pages/site/pages/010-guides/pages/020-workflows/content.md +11 -0
- package/fixtures/nested-pages/site/pages/010-guides/theme.yaml +3 -0
- package/fixtures/nested-pages/site/pages/020-reference/content.md +10 -0
- package/fixtures/nested-pages/site/pages/020-reference/pages/010-installation/content.md +11 -0
- package/fixtures/nested-pages/site/theme.yaml +3 -0
- package/package.json +7 -4
- package/schemas/config.schema.json +5 -5
- package/schemas/content-frontmatter.schema.json +28 -63
- package/schemas/manifest.json +1 -0
- package/schemas/sitewide-content.schema.json +58 -132
- package/schemas/theme.schema.json +396 -86
- package/scripts/check-config.mjs +6 -5
- package/scripts/deploy-site.mjs +6 -8
- package/scripts/doctor.mjs +2 -2
- package/scripts/generate-images.mjs +11 -18
- package/scripts/generate-schemas.mjs +3 -217
- package/scripts/lib/base-path-redirect.mjs +9 -0
- package/scripts/lib/editor-language-service.mjs +126 -66
- package/scripts/lib/heading-ids.mjs +161 -0
- package/scripts/lib/navigation-model.mjs +51 -0
- package/scripts/lib/norna-markdown-blocks.mjs +3 -4
- package/scripts/lib/page-model.mjs +83 -0
- package/scripts/lib/project-config.mjs +28 -0
- package/scripts/lib/schema-definitions.mjs +41 -46
- package/scripts/lib/schema-editor-metadata.mjs +475 -0
- package/scripts/lib/schema-value-definitions.mjs +6 -6
- package/scripts/lib/site-content.mjs +124 -87
- package/scripts/lib/site-paths.mjs +2 -2
- package/scripts/lib/theme-config.mjs +9 -16
- package/scripts/lib/theme-presets.mjs +9 -1
- package/scripts/release.mjs +1 -1
- package/scripts/show-typography.mjs +26 -20
- package/scripts/sync-content-sections.mjs +106 -65
- package/scripts/{test-temporary-visibility.mjs → test-banner-visibility.mjs} +7 -24
- package/scripts/test-client-javascript.mjs +39 -42
- package/scripts/test-content-check.mjs +138 -54
- package/scripts/test-content-model-v2.mjs +445 -228
- package/scripts/test-documentation.mjs +6 -1
- package/scripts/test-editor-language-service.mjs +168 -24
- package/scripts/test-engine-commands.mjs +2 -1
- package/scripts/test-heading-ids.mjs +68 -0
- package/scripts/test-navigation-model.mjs +63 -0
- package/scripts/test-nested-pages.mjs +110 -0
- package/scripts/test-package-check.mjs +31 -40
- package/scripts/test-page-model.mjs +80 -0
- package/scripts/test-project-config.mjs +25 -1
- package/scripts/test-schemas.mjs +66 -14
- package/scripts/test-theme-presets.mjs +33 -24
- package/src/components/NavigationPageTree.astro +68 -0
- package/src/components/SectionNavigationScript.astro +110 -5
- package/src/components/SiteBreadcrumbs.astro +29 -0
- package/src/components/SiteNavigation.astro +65 -69
- package/src/components/SitePage.astro +70 -46
- package/src/components/SiteSection.astro +27 -20
- package/src/content.config.ts +9 -5
- package/src/layouts/BaseLayout.astro +11 -20
- package/src/lib/pageThemes.ts +42 -0
- package/src/lib/sectionContent.ts +109 -106
- package/src/lib/siteNavigation.ts +48 -0
- package/src/lib/sitePages.ts +85 -38
- package/src/pages/{[slug].astro → [...slug].astro} +2 -2
- package/src/styles/global.css +373 -55
- package/starters/basic/README.md +5 -7
- package/starters/basic/site/content.md +7 -4
- package/starters/basic/site/sitewide-content.yaml +1 -10
- package/starters/project/README.md +8 -8
- package/starters/project/site/content.md +5 -4
- package/starters/project/site/{routes → pages}/010-guide/content.md +5 -4
- package/starters/project/site/sitewide-content.yaml +1 -10
- package/docs/routes.md +0 -121
- package/examples/feature-demos/sitewide-content/site/routes/010-identity/content.md +0 -20
- package/fixtures/content-model-v2/site/routes/010-guide/content.md +0 -17
- package/scripts/lib/route-model.mjs +0 -17
- package/scripts/test-route-model.mjs +0 -32
- package/src/lib/routeThemes.ts +0 -41
- /package/examples/complete-sites/dog-shelter-multi-page/site/images/{intro/shelter-dog.jpg → shelter-dog.jpg} +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/010-dogs/images/dogs → pages/010-dogs/images}/rex.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/010-dogs/images/dogs → pages/010-dogs/images}/rover.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-multi-page/site/{routes/020-adopt/images/adopt → pages/020-adopt/images}/adoption-dog.jpg +0 -0
- /package/examples/complete-sites/dog-shelter-single-page/site/images/{you-can-help/foster-dog.jpg → foster-dog.jpg} +0 -0
- /package/examples/complete-sites/dog-shelter-single-page/site/images/{what-we-do/rescue-dog.jpg → rescue-dog.jpg} +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-carousel.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-stack.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/cards → pages/010-media/images}/card-surfaces.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-one.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-three.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/carousel → pages/010-media/images}/carousel-two.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/stack → pages/010-media/images}/stack-one.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes/010-media/images/stack → pages/010-media/images}/stack-two.svg +0 -0
- /package/examples/feature-demos/media-and-surfaces/site/{routes → pages}/020-surfaces/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/010-portfolio/images/portfolio → pages/010-portfolio/images}/puppy-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/010-portfolio/images/portfolio → pages/010-portfolio/images}/shepherd-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/010-portfolio/images/portfolio → pages/010-portfolio/images}/white-puppy.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/010-portfolio/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/020-documentation/images/documentation → pages/020-documentation/images}/black-schnauzer.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/020-documentation/images/documentation → pages/020-documentation/images}/retriever-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/020-documentation/images/documentation → pages/020-documentation/images}/schnauzer-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/020-documentation/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/030-project/images/project → pages/030-project/images}/boxer-portrait.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/030-project/images/project → pages/030-project/images}/grass-puppy.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/030-project/images/project → pages/030-project/images}/grey-street-dog.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/030-project/theme.yaml +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/040-statement/images/statement → pages/040-statement/images}/black-pedigree.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/040-statement/images/statement → pages/040-statement/images}/sheepdog-grass.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes/040-statement/images/statement → pages/040-statement/images}/sheepdog-wide.jpg +0 -0
- /package/examples/feature-demos/theme-presets/site/{routes → pages}/040-statement/theme.yaml +0 -0
- /package/fixtures/basic/site/images/{work/.gitkeep → .gitkeep} +0 -0
- /package/fixtures/content-model-v2/site/images/{card-section/adopt.svg → adopt.svg} +0 -0
- /package/fixtures/content-model-v2/site/images/{intro/detail.jpg → detail.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{intro/duplicate.jpg → duplicate.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{card-section/foster.svg → foster.svg} +0 -0
- /package/fixtures/content-model-v2/site/images/{intro/hero.jpg → hero.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{plain/image.jpg → image.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{timed/slide-one.jpg → slide-one.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{timed/slide-three.jpg → slide-three.jpg} +0 -0
- /package/fixtures/content-model-v2/site/images/{timed/slide-two.jpg → slide-two.jpg} +0 -0
- /package/fixtures/content-model-v2/site/{routes/010-guide/images/intro → pages/010-guide/images}/duplicate.jpg +0 -0
- /package/starters/basic/site/images/{work/.gitkeep → .gitkeep} +0 -0
|
@@ -69,12 +69,12 @@ const withTempProject = async ({ site, theme = defaultTheme, files, siteDirector
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
const brokenSite = `---
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
min-konst: {}
|
|
75
|
-
mitt-hem: {}
|
|
76
|
-
|
|
72
|
+
page:
|
|
73
|
+
description: Exercises grouped content diagnostics.
|
|
77
74
|
---
|
|
75
|
+
|
|
76
|
+
# Broken content fixture
|
|
77
|
+
|
|
78
78
|
## Karin Walde {#karin-walde}
|
|
79
79
|
Text.
|
|
80
80
|
|
|
@@ -112,10 +112,10 @@ test('content:check groups section issues, global issues, and unreferenced image
|
|
|
112
112
|
await withTempProject({
|
|
113
113
|
site: brokenSite,
|
|
114
114
|
files: [
|
|
115
|
-
'site/images/karin
|
|
115
|
+
'site/images/karin.jpg',
|
|
116
116
|
'site/images/karin-walde/unreferenced.jpg',
|
|
117
117
|
'site/images/min-konst/duplicate.jpg',
|
|
118
|
-
'site/images/
|
|
118
|
+
'site/images/home.jpg',
|
|
119
119
|
'site/images/mitt-hem/vav.jpeg',
|
|
120
120
|
],
|
|
121
121
|
}, async (root) => {
|
|
@@ -126,15 +126,14 @@ test('content:check groups section issues, global issues, and unreferenced image
|
|
|
126
126
|
assert.match(output, /^Content check failed\./m);
|
|
127
127
|
assert.match(output, /Content Issues\n\n\[site\/content\.md \[min-konst\]\]\n Errors:/);
|
|
128
128
|
assert.match(output, /Image "vav\.jpeg" is used here but is located in site\/images\/mitt-hem\/vav\.jpeg\./);
|
|
129
|
-
assert.match(output, /Image "missing\.jpeg" does not exist at site\/images\/
|
|
130
|
-
assert.match(output, /Unreferenced Images\nThese files are kept under page
|
|
129
|
+
assert.match(output, /Image "missing\.jpeg" does not exist at site\/images\/missing\.jpeg or anywhere under any page image root\./);
|
|
130
|
+
assert.match(output, /Unreferenced Images\nThese files are kept under page image roots but are not referenced by Norna-managed image references:/);
|
|
131
131
|
assert.match(output, /site\/images\/karin-walde\/unreferenced\.jpg/);
|
|
132
132
|
});
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
-
const carouselAspectRatioSite =
|
|
136
|
-
|
|
137
|
-
---
|
|
135
|
+
const carouselAspectRatioSite = `# Carousel Aspect Ratio
|
|
136
|
+
|
|
138
137
|
## Puppies {#puppies}
|
|
139
138
|
Text.
|
|
140
139
|
|
|
@@ -151,11 +150,11 @@ test('content:check warns when carousel images use different aspect ratios', asy
|
|
|
151
150
|
site: carouselAspectRatioSite,
|
|
152
151
|
files: [
|
|
153
152
|
{
|
|
154
|
-
path: 'site/images/
|
|
153
|
+
path: 'site/images/wide.png',
|
|
155
154
|
contents: makePngHeader({ width: 400, height: 300 }),
|
|
156
155
|
},
|
|
157
156
|
{
|
|
158
|
-
path: 'site/images/
|
|
157
|
+
path: 'site/images/wider.png',
|
|
159
158
|
contents: makePngHeader({ width: 600, height: 300 }),
|
|
160
159
|
},
|
|
161
160
|
],
|
|
@@ -165,14 +164,13 @@ test('content:check warns when carousel images use different aspect ratios', asy
|
|
|
165
164
|
|
|
166
165
|
assert.equal(result.status, 0, output);
|
|
167
166
|
assert.match(output, /^Content check completed with warnings\./m);
|
|
168
|
-
assert.match(output, /Carousel on line
|
|
167
|
+
assert.match(output, /Carousel on line 6 uses images with different aspect ratios: wide\.png \(4:3\), wider\.png \(2:1\)\./);
|
|
169
168
|
assert.match(output, /Use images with exactly matching proportions in the same carousel/);
|
|
170
169
|
});
|
|
171
170
|
});
|
|
172
171
|
|
|
173
|
-
const inlineStyleSite =
|
|
174
|
-
|
|
175
|
-
---
|
|
172
|
+
const inlineStyleSite = `# Inline Style
|
|
173
|
+
|
|
176
174
|
## Intro {#intro}
|
|
177
175
|
This has [known text]{.highlight} and [unknown text]{.missing}.
|
|
178
176
|
`;
|
|
@@ -194,9 +192,8 @@ test('content:check fails when Markdown uses removed inline color styles', async
|
|
|
194
192
|
|
|
195
193
|
test('content:check suggests semantic Markdown instead of inline color styles', async () => {
|
|
196
194
|
await withTempProject({
|
|
197
|
-
site:
|
|
198
|
-
|
|
199
|
-
---
|
|
195
|
+
site: `# Invalid Inline Color
|
|
196
|
+
|
|
200
197
|
## Intro {#intro}
|
|
201
198
|
This has [yellow text]{.yellow}.
|
|
202
199
|
`,
|
|
@@ -212,9 +209,12 @@ This has [yellow text]{.yellow}.
|
|
|
212
209
|
});
|
|
213
210
|
|
|
214
211
|
const inlineNoteSite = `---
|
|
215
|
-
|
|
216
|
-
description: Fixture
|
|
212
|
+
page:
|
|
213
|
+
description: Fixture
|
|
217
214
|
---
|
|
215
|
+
|
|
216
|
+
# Inline notes
|
|
217
|
+
|
|
218
218
|
## Intro {#intro}
|
|
219
219
|
This paragraph has a note reference.{note-ref}
|
|
220
220
|
|
|
@@ -239,9 +239,12 @@ test('content:check accepts valid inline notes and ignores note syntax in code f
|
|
|
239
239
|
test('content:check accepts wrapped and explicit multiline inline notes', async () => {
|
|
240
240
|
await withTempProject({
|
|
241
241
|
site: `---
|
|
242
|
-
|
|
243
|
-
description: Fixture
|
|
242
|
+
page:
|
|
243
|
+
description: Fixture
|
|
244
244
|
---
|
|
245
|
+
|
|
246
|
+
# Multiline inline notes
|
|
247
|
+
|
|
245
248
|
## Wrapped {#wrapped}
|
|
246
249
|
This paragraph has a formatter-wrapped note.{note-ref}
|
|
247
250
|
|
|
@@ -269,9 +272,12 @@ This paragraph has an explicit multiline note.{note-ref}
|
|
|
269
272
|
test('content:check reports an unclosed multiline inline note', async () => {
|
|
270
273
|
await withTempProject({
|
|
271
274
|
site: `---
|
|
272
|
-
|
|
273
|
-
description: Fixture
|
|
275
|
+
page:
|
|
276
|
+
description: Fixture
|
|
274
277
|
---
|
|
278
|
+
|
|
279
|
+
# Unclosed inline note
|
|
280
|
+
|
|
275
281
|
## Intro {#intro}
|
|
276
282
|
This paragraph has a note reference.{note-ref}
|
|
277
283
|
|
|
@@ -292,9 +298,12 @@ and continues to the end of the file.
|
|
|
292
298
|
test('content:check rejects nested note syntax before a multiline note is closed', async () => {
|
|
293
299
|
await withTempProject({
|
|
294
300
|
site: `---
|
|
295
|
-
|
|
296
|
-
description: Fixture
|
|
301
|
+
page:
|
|
302
|
+
description: Fixture
|
|
297
303
|
---
|
|
304
|
+
|
|
305
|
+
# Nested inline note syntax
|
|
306
|
+
|
|
298
307
|
## Nested note {#nested-note}
|
|
299
308
|
This paragraph has a note reference.{note-ref}
|
|
300
309
|
|
|
@@ -321,9 +330,12 @@ and contains {note-ref} before closing.}
|
|
|
321
330
|
test('content:check allows escaped and inline-code note syntax inside notes', async () => {
|
|
322
331
|
await withTempProject({
|
|
323
332
|
site: `---
|
|
324
|
-
|
|
325
|
-
description: Fixture
|
|
333
|
+
page:
|
|
334
|
+
description: Fixture
|
|
326
335
|
---
|
|
336
|
+
|
|
337
|
+
# Literal inline note syntax
|
|
338
|
+
|
|
327
339
|
## Intro {#intro}
|
|
328
340
|
This paragraph has a note reference.{note-ref}
|
|
329
341
|
|
|
@@ -342,9 +354,12 @@ This paragraph has a note reference.{note-ref}
|
|
|
342
354
|
test('content:check rejects unpaired and repeated inline notes', async () => {
|
|
343
355
|
await withTempProject({
|
|
344
356
|
site: `---
|
|
345
|
-
|
|
346
|
-
description: Fixture
|
|
357
|
+
page:
|
|
358
|
+
description: Fixture
|
|
347
359
|
---
|
|
360
|
+
|
|
361
|
+
# Invalid inline notes
|
|
362
|
+
|
|
348
363
|
## Orphan {#orphan}
|
|
349
364
|
{note: This note has no reference.}
|
|
350
365
|
|
|
@@ -372,9 +387,6 @@ typography:
|
|
|
372
387
|
\u00a0\u00a0overrides:
|
|
373
388
|
body:
|
|
374
389
|
paragraphSpacing: 0.3em
|
|
375
|
-
sections:
|
|
376
|
-
intro: {}
|
|
377
|
-
|
|
378
390
|
---
|
|
379
391
|
## Intro {#intro}
|
|
380
392
|
Text.
|
|
@@ -401,9 +413,6 @@ typography:
|
|
|
401
413
|
overrides:
|
|
402
414
|
body:
|
|
403
415
|
paragraphSpacing: 0.3em
|
|
404
|
-
sections:
|
|
405
|
-
intro: {}
|
|
406
|
-
|
|
407
416
|
---
|
|
408
417
|
## Intro {#intro}
|
|
409
418
|
Text.
|
|
@@ -425,14 +434,14 @@ test('content:check explains frontmatter indentation below scalar values', async
|
|
|
425
434
|
});
|
|
426
435
|
|
|
427
436
|
const topLevelImagesSite = `---
|
|
428
|
-
|
|
429
|
-
description: Example site.
|
|
430
|
-
sections:
|
|
431
|
-
intro: {}
|
|
437
|
+
page:
|
|
438
|
+
description: Example site.
|
|
432
439
|
images:
|
|
433
440
|
- image: intro.jpg
|
|
434
|
-
|
|
435
441
|
---
|
|
442
|
+
|
|
443
|
+
# Example
|
|
444
|
+
|
|
436
445
|
## Intro {#intro}
|
|
437
446
|
Text.
|
|
438
447
|
`;
|
|
@@ -447,7 +456,7 @@ test('content:check explains likely misindented frontmatter keys', async () => {
|
|
|
447
456
|
|
|
448
457
|
assert.equal(result.status, 1, output);
|
|
449
458
|
assert.match(output, /^Content check failed\./m);
|
|
450
|
-
assert.match(output, /Frontmatter line
|
|
459
|
+
assert.match(output, /Frontmatter line 4 defines "images" at the top level, but it is not a valid top-level content field\./);
|
|
451
460
|
assert.match(output, /Put local image references in norna-image-stack or norna-image-carousel blocks in the Markdown body\./);
|
|
452
461
|
});
|
|
453
462
|
});
|
|
@@ -457,8 +466,8 @@ test('content:check respects NORNA_SITE_DIR', async () => {
|
|
|
457
466
|
site: movableSite,
|
|
458
467
|
siteDirectory: 'custom-site',
|
|
459
468
|
files: [
|
|
460
|
-
'custom-site/images/
|
|
461
|
-
'custom-site/images/
|
|
469
|
+
'custom-site/images/move-me.jpg',
|
|
470
|
+
'custom-site/images/home.jpg',
|
|
462
471
|
],
|
|
463
472
|
}, async (root) => {
|
|
464
473
|
const result = runContentScript(root, ['--check'], { NORNA_SITE_DIR: 'custom-site' });
|
|
@@ -470,11 +479,12 @@ test('content:check respects NORNA_SITE_DIR', async () => {
|
|
|
470
479
|
});
|
|
471
480
|
|
|
472
481
|
const movableSite = `---
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
mitt-hem: {}
|
|
476
|
-
|
|
482
|
+
page:
|
|
483
|
+
description: Exercises content sync.
|
|
477
484
|
---
|
|
485
|
+
|
|
486
|
+
# Movable image fixture
|
|
487
|
+
|
|
478
488
|
## Min konst {#min-konst}
|
|
479
489
|
Text.
|
|
480
490
|
|
|
@@ -496,7 +506,7 @@ test('content:sync moves referenced images and keeps unreferenced images in plac
|
|
|
496
506
|
await withTempProject({
|
|
497
507
|
site: movableSite,
|
|
498
508
|
files: [
|
|
499
|
-
'site/images/
|
|
509
|
+
'site/images/home.jpg',
|
|
500
510
|
'site/images/mitt-hem/move-me.jpg',
|
|
501
511
|
'site/images/mitt-hem/unreferenced.jpg',
|
|
502
512
|
],
|
|
@@ -505,8 +515,8 @@ test('content:sync moves referenced images and keeps unreferenced images in plac
|
|
|
505
515
|
const syncOutput = getOutput(syncResult);
|
|
506
516
|
|
|
507
517
|
assert.equal(syncResult.status, 0, syncOutput);
|
|
508
|
-
assert.match(syncOutput, /Moved image "move-me\.jpg" to site\/images
|
|
509
|
-
assert.equal(await fileExists(path.join(root, 'site/images/
|
|
518
|
+
assert.match(syncOutput, /Moved image "move-me\.jpg" to site\/images\/\./);
|
|
519
|
+
assert.equal(await fileExists(path.join(root, 'site/images/move-me.jpg')), true);
|
|
510
520
|
assert.equal(await fileExists(path.join(root, 'site/images/mitt-hem/move-me.jpg')), false);
|
|
511
521
|
assert.equal(await fileExists(path.join(root, 'site/images/mitt-hem/unreferenced.jpg')), true);
|
|
512
522
|
|
|
@@ -519,6 +529,80 @@ test('content:sync moves referenced images and keeps unreferenced images in plac
|
|
|
519
529
|
});
|
|
520
530
|
});
|
|
521
531
|
|
|
532
|
+
test('content:check accepts a frontmatter-free page with one H1 title', async () => {
|
|
533
|
+
await withTempProject({
|
|
534
|
+
site: `# About
|
|
535
|
+
|
|
536
|
+
Introductory text.
|
|
537
|
+
|
|
538
|
+
\`\`\`md
|
|
539
|
+
# Example heading in code
|
|
540
|
+
\`\`\`
|
|
541
|
+
|
|
542
|
+
## Details {#details}
|
|
543
|
+
|
|
544
|
+
More text.
|
|
545
|
+
`,
|
|
546
|
+
files: [],
|
|
547
|
+
}, async (root) => {
|
|
548
|
+
const result = runContentScript(root, ['--check']);
|
|
549
|
+
assert.equal(result.status, 0, getOutput(result));
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test('content:check requires exactly one H1 before level 2 sections', async () => {
|
|
554
|
+
await withTempProject({
|
|
555
|
+
site: `## Details {#details}
|
|
556
|
+
|
|
557
|
+
Text.
|
|
558
|
+
`,
|
|
559
|
+
files: [],
|
|
560
|
+
}, async (root) => {
|
|
561
|
+
const result = runContentScript(root, ['--check']);
|
|
562
|
+
const output = getOutput(result);
|
|
563
|
+
assert.equal(result.status, 1, output);
|
|
564
|
+
assert.match(output, /The page is missing its Markdown H1 title\./);
|
|
565
|
+
assert.match(output, /a Norna page must start with its H1 title\./);
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test('content:check rejects repeated H1 page titles', async () => {
|
|
570
|
+
await withTempProject({
|
|
571
|
+
site: `# First title
|
|
572
|
+
|
|
573
|
+
Text.
|
|
574
|
+
|
|
575
|
+
# Second title
|
|
576
|
+
|
|
577
|
+
More text.
|
|
578
|
+
`,
|
|
579
|
+
files: [],
|
|
580
|
+
}, async (root) => {
|
|
581
|
+
const result = runContentScript(root, ['--check']);
|
|
582
|
+
const output = getOutput(result);
|
|
583
|
+
assert.equal(result.status, 1, output);
|
|
584
|
+
assert.match(output, /The page contains 2 Markdown H1 headings\./);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
test('content:check rejects section ids and content before the H1 page title', async () => {
|
|
589
|
+
await withTempProject({
|
|
590
|
+
site: `Text before the title.
|
|
591
|
+
|
|
592
|
+
# About {#about}
|
|
593
|
+
|
|
594
|
+
Page text.
|
|
595
|
+
`,
|
|
596
|
+
files: [],
|
|
597
|
+
}, async (root) => {
|
|
598
|
+
const result = runContentScript(root, ['--check']);
|
|
599
|
+
const output = getOutput(result);
|
|
600
|
+
assert.equal(result.status, 1, output);
|
|
601
|
+
assert.match(output, /The page contains content before its first heading\./);
|
|
602
|
+
assert.match(output, /The Markdown H1 is the page title and must not have an id\./);
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
|
|
522
606
|
let failed = 0;
|
|
523
607
|
|
|
524
608
|
for (const { name, run } of tests) {
|