@janga/norna 0.7.6 → 0.7.8
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 +8 -7
- package/astro.config.mjs +76 -0
- package/bin/norna-cli.mjs +2 -2
- package/docs/README.md +3 -3
- package/docs/commands.md +7 -6
- package/docs/configuration.md +3 -2
- package/docs/content.md +202 -91
- package/docs/design/site-examples-structure.md +1 -1
- package/docs/engine-development.md +3 -3
- package/docs/getting-started.md +50 -8
- package/docs/images-and-metadata.md +117 -20
- package/docs/routes.md +20 -12
- package/docs/site-structure.md +9 -5
- package/docs/theme.md +12 -8
- package/docs/typography.md +9 -10
- package/examples/dog-gallery/site/content.md +38 -77
- package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +0 -10
- package/fixtures/basic/site/content.md +2 -4
- package/fixtures/content-model-v2/.astro/collections/site.schema.json +776 -0
- package/fixtures/content-model-v2/.astro/collections/theme.schema.json +668 -0
- package/fixtures/content-model-v2/.astro/content-assets.mjs +4 -0
- package/fixtures/content-model-v2/.astro/content-modules.mjs +1 -0
- package/fixtures/content-model-v2/.astro/content.d.ts +175 -0
- package/fixtures/content-model-v2/.astro/data-store.json +1 -0
- package/fixtures/content-model-v2/.astro/dev-local.json +8 -0
- package/fixtures/content-model-v2/.astro/dev.json +13 -0
- package/fixtures/content-model-v2/.astro/dev.log +231 -0
- package/fixtures/content-model-v2/.astro/settings.json +5 -0
- package/fixtures/content-model-v2/.astro/types.d.ts +2 -0
- package/fixtures/content-model-v2/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +1 -0
- package/fixtures/content-model-v2/dist/_astro/SitePage.BbfZub4g.css +1 -0
- package/fixtures/content-model-v2/dist/guide/index.html +3 -0
- 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 +8 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/_metadata.json +80 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js +1389 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/package.json +3 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
- package/fixtures/content-model-v2/site/.norna/generated-images.json +210 -0
- package/fixtures/content-model-v2/site/config.mjs +11 -0
- package/fixtures/content-model-v2/site/content.md +84 -0
- package/fixtures/content-model-v2/site/images/card-section/adopt.svg +9 -0
- package/fixtures/content-model-v2/site/images/card-section/foster.svg +12 -0
- package/fixtures/content-model-v2/site/images/intro/detail.jpg +0 -0
- package/fixtures/content-model-v2/site/images/intro/duplicate.jpg +0 -0
- package/fixtures/content-model-v2/site/images/intro/hero.jpg +0 -0
- package/fixtures/content-model-v2/site/images/plain/image.jpg +0 -0
- package/fixtures/content-model-v2/site/images/timed/slide-one.jpg +0 -0
- package/fixtures/content-model-v2/site/images/timed/slide-three.jpg +0 -0
- package/fixtures/content-model-v2/site/images/timed/slide-two.jpg +0 -0
- package/fixtures/content-model-v2/site/routes/010-guide/images/intro/duplicate.jpg +0 -0
- package/fixtures/content-model-v2/site/routes/010-guide/route-content.md +17 -0
- package/fixtures/content-model-v2/site/theme.md +9 -0
- package/package.json +3 -2
- package/scripts/deploy-site.mjs +11 -4
- package/scripts/dev-local.mjs +6 -0
- package/scripts/generate-images.mjs +132 -56
- package/scripts/lib/image-dimensions.mjs +26 -0
- package/scripts/lib/norna-markdown-blocks.mjs +643 -0
- package/scripts/lib/site-content.mjs +31 -29
- package/scripts/show-typography.mjs +17 -9
- package/scripts/sync-content-sections.mjs +414 -412
- package/scripts/test-content-check.mjs +59 -45
- package/scripts/test-content-model-v2.mjs +1230 -0
- package/scripts/test-engine-commands.mjs +1 -1
- package/scripts/test-package-check.mjs +36 -8
- package/scripts/test-temporary-visibility.mjs +3 -6
- package/src/components/CardList.astro +77 -0
- package/src/components/GalleryGrid.astro +22 -12
- package/src/components/SiteNavigation.astro +5 -4
- package/src/components/SitePage.astro +9 -2
- package/src/components/SiteSection.astro +35 -13
- package/src/content.config.ts +4 -17
- package/src/lib/generatedImages.ts +50 -10
- package/src/lib/sectionContent.ts +130 -35
- package/src/styles/global.css +187 -0
- package/starters/basic/README.md +4 -2
- package/starters/basic/site/content.md +9 -11
- package/starters/project/README.md +7 -3
- package/starters/project/site/content.md +0 -9
- package/starters/project/site/routes/010-guide/route-content.md +0 -5
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
} from './site-paths.mjs';
|
|
13
13
|
import { parseRouteDirectory } from './route-model.mjs';
|
|
14
14
|
|
|
15
|
-
export const
|
|
15
|
+
export const rasterImageExtensions = new Set(['.jpg', '.jpeg', '.png']);
|
|
16
|
+
export const staticImageExtensions = new Set(['.svg']);
|
|
17
|
+
export const supportedImageExtensions = new Set([...rasterImageExtensions, ...staticImageExtensions]);
|
|
16
18
|
|
|
17
19
|
const h2Regex = /^##\s+.*$/gm;
|
|
18
20
|
const explicitHeadingIdRegex = /\s*\{#([a-z0-9-]+)\}\s*$/;
|
|
@@ -248,7 +250,7 @@ export const validateFrontmatterStructure = (frontmatter, addIssue, {
|
|
|
248
250
|
if (knownTopLevelFrontmatterKeys.has(key)) continue;
|
|
249
251
|
|
|
250
252
|
const fix = knownNestedFrontmatterKeys.has(key)
|
|
251
|
-
? `Indent "${key}:" under the
|
|
253
|
+
? `Indent "${key}:" under the object it belongs to, or move image content into Norna Markdown blocks.`
|
|
252
254
|
: `Move "${key}:" under the correct parent key, or remove it if it is not part of the ${fileKind} schema.`;
|
|
253
255
|
|
|
254
256
|
addIssue({
|
|
@@ -277,7 +279,6 @@ export const getFrontmatterSections = (frontmatter) => {
|
|
|
277
279
|
const sections = [];
|
|
278
280
|
const lines = frontmatter.split(/\r?\n/);
|
|
279
281
|
let inSections = false;
|
|
280
|
-
let currentSection = null;
|
|
281
282
|
|
|
282
283
|
for (const [index, line] of lines.entries()) {
|
|
283
284
|
if (/^sections:\s*$/.test(line)) {
|
|
@@ -288,30 +289,9 @@ export const getFrontmatterSections = (frontmatter) => {
|
|
|
288
289
|
if (!inSections) continue;
|
|
289
290
|
if (/^[a-zA-Z0-9_-]+:/.test(line)) break;
|
|
290
291
|
|
|
291
|
-
const sectionMatch = line.match(/^\s{2}
|
|
292
|
+
const sectionMatch = line.match(/^\s{2}([a-z0-9-]+):(?:\s+.*)?\s*$/);
|
|
292
293
|
if (sectionMatch) {
|
|
293
|
-
|
|
294
|
-
sections.push(currentSection);
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const carouselMatch = line.match(/^\s{6}-\s+carousel:\s*$/);
|
|
299
|
-
if (carouselMatch && currentSection) {
|
|
300
|
-
currentSection.carousels.push({ images: [], imageReferences: [], line: index + 1 });
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const imageMatch = line.match(/^\s{6,}-\s+image:\s*["']?([^"'\n]+)["']?\s*$/);
|
|
305
|
-
if (imageMatch && currentSection) {
|
|
306
|
-
const image = imageMatch[1].trim();
|
|
307
|
-
const isCarouselImage = /^\s{10}-\s+image:/.test(line);
|
|
308
|
-
const carousel = isCarouselImage ? currentSection.carousels.at(-1) : null;
|
|
309
|
-
currentSection.images.push(image);
|
|
310
|
-
currentSection.imageReferences.push({ image, line: index + 1 });
|
|
311
|
-
if (carousel) {
|
|
312
|
-
carousel.images.push(image);
|
|
313
|
-
carousel.imageReferences.push({ image, line: index + 1 });
|
|
314
|
-
}
|
|
294
|
+
sections.push({ id: sectionMatch[1], line: index + 1 });
|
|
315
295
|
}
|
|
316
296
|
}
|
|
317
297
|
|
|
@@ -367,8 +347,9 @@ export const getBodySections = (body) => {
|
|
|
367
347
|
const text = body.slice(start, end).trimEnd();
|
|
368
348
|
const heading = match[0];
|
|
369
349
|
const id = getHeadingId(heading);
|
|
350
|
+
const line = body.slice(0, start).split(/\r?\n/).length;
|
|
370
351
|
|
|
371
|
-
sections.push({ id, heading, text });
|
|
352
|
+
sections.push({ id, heading, line, text });
|
|
372
353
|
}
|
|
373
354
|
|
|
374
355
|
return { prelude, sections };
|
|
@@ -397,7 +378,7 @@ export const getImageFiles = async (directory) => {
|
|
|
397
378
|
return files;
|
|
398
379
|
};
|
|
399
380
|
|
|
400
|
-
export const getImageIndex = async (contentDir, fail) => {
|
|
381
|
+
export const getImageIndex = async (contentDir, fail, contentLabel = siteImagesLabel) => {
|
|
401
382
|
const imageFiles = await getImageFiles(contentDir);
|
|
402
383
|
const imagesByName = new Map();
|
|
403
384
|
|
|
@@ -406,7 +387,7 @@ export const getImageIndex = async (contentDir, fail) => {
|
|
|
406
387
|
const existingPath = imagesByName.get(imageName);
|
|
407
388
|
|
|
408
389
|
if (existingPath) {
|
|
409
|
-
fail(`Duplicate image filename "${imageName}" found at ${
|
|
390
|
+
fail(`Duplicate image filename "${imageName}" found at ${contentLabel}/${toPosixPath(path.relative(contentDir, existingPath))} and ${contentLabel}/${toPosixPath(path.relative(contentDir, imagePath))}. The filename is ambiguous within this page image root.`);
|
|
410
391
|
continue;
|
|
411
392
|
}
|
|
412
393
|
|
|
@@ -415,3 +396,24 @@ export const getImageIndex = async (contentDir, fail) => {
|
|
|
415
396
|
|
|
416
397
|
return imagesByName;
|
|
417
398
|
};
|
|
399
|
+
|
|
400
|
+
export const getImageCandidatesByName = async (contentDir) => {
|
|
401
|
+
const imageFiles = await getImageFiles(contentDir);
|
|
402
|
+
const imagesByName = new Map();
|
|
403
|
+
|
|
404
|
+
for (const imagePath of imageFiles) {
|
|
405
|
+
const imageName = path.basename(imagePath);
|
|
406
|
+
|
|
407
|
+
if (!imagesByName.has(imageName)) {
|
|
408
|
+
imagesByName.set(imageName, []);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
imagesByName.get(imageName).push(imagePath);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
for (const candidates of imagesByName.values()) {
|
|
415
|
+
candidates.sort((left, right) => left.localeCompare(right, 'sv'));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return imagesByName;
|
|
419
|
+
};
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
typographyRhythms,
|
|
10
10
|
} from './lib/typography.mjs';
|
|
11
11
|
import {
|
|
12
|
+
getBodySections,
|
|
12
13
|
getContentFiles,
|
|
13
14
|
readSiteFile,
|
|
14
15
|
splitSiteFile,
|
|
@@ -268,11 +269,11 @@ const formatAnnotatedTypography = (annotated) => ({
|
|
|
268
269
|
resolved: annotateResolvedValues(annotated.resolved, annotated.sources),
|
|
269
270
|
});
|
|
270
271
|
|
|
271
|
-
const
|
|
272
|
+
const getSectionMetadataBlocks = (lines) => {
|
|
272
273
|
const sectionsMap = findMap(lines, 'sections');
|
|
273
|
-
if (!sectionsMap) return
|
|
274
|
+
if (!sectionsMap) return new Map();
|
|
274
275
|
|
|
275
|
-
const sections =
|
|
276
|
+
const sections = new Map();
|
|
276
277
|
let current = null;
|
|
277
278
|
|
|
278
279
|
for (let index = sectionsMap.index + 1; index < lines.length; index += 1) {
|
|
@@ -282,15 +283,16 @@ const getSectionBlocks = (lines) => {
|
|
|
282
283
|
const indent = countIndent(line);
|
|
283
284
|
if (indent <= sectionsMap.indent) break;
|
|
284
285
|
|
|
285
|
-
const sectionMatch = line.match(/^\s{2}
|
|
286
|
+
const sectionMatch = line.match(/^\s{2}([a-z0-9-]+):(?:\s+.*)?\s*$/);
|
|
286
287
|
if (sectionMatch) {
|
|
287
288
|
if (current) current.end = index;
|
|
288
289
|
current = { id: sectionMatch[1], start: index, end: lines.length };
|
|
289
|
-
sections.
|
|
290
|
+
sections.set(current.id, current);
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
293
|
|
|
293
|
-
const
|
|
294
|
+
const sectionList = Array.from(sections.values());
|
|
295
|
+
const finalSection = sectionList.at(-1);
|
|
294
296
|
if (finalSection) {
|
|
295
297
|
const afterSections = lines.findIndex((line, index) => (
|
|
296
298
|
index > finalSection.start &&
|
|
@@ -328,7 +330,7 @@ const readThemeTypography = async () => {
|
|
|
328
330
|
};
|
|
329
331
|
|
|
330
332
|
const readPageTypography = async (contentFile, themeTypography) => {
|
|
331
|
-
const { frontmatter, frontmatterBody } = await readSiteFile(contentFile.contentPath, contentFile.contentLabel);
|
|
333
|
+
const { frontmatter, frontmatterBody, body } = await readSiteFile(contentFile.contentPath, contentFile.contentLabel);
|
|
332
334
|
const indentationIssues = [];
|
|
333
335
|
validateFrontmatterIndentation(frontmatter, (issue) => indentationIssues.push(issue));
|
|
334
336
|
validateContentFrontmatterStructure(frontmatter, (issue) => indentationIssues.push(issue));
|
|
@@ -345,8 +347,14 @@ const readPageTypography = async (contentFile, themeTypography) => {
|
|
|
345
347
|
? findMap(lines, 'typography', pagePresentation.index + 1, pagePresentation.nextIndex)?.value
|
|
346
348
|
: null;
|
|
347
349
|
const pageTypography = resolveAnnotatedTypographyOverride(themeTypography, pageTypographyConfig ?? undefined, contentFile.contentLabel);
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
+
const sectionMetadataBlocks = getSectionMetadataBlocks(lines);
|
|
351
|
+
const sections = getBodySections(body).sections
|
|
352
|
+
.filter((section) => section.id)
|
|
353
|
+
.map((section) => {
|
|
354
|
+
const metadata = sectionMetadataBlocks.get(section.id);
|
|
355
|
+
const presentation = metadata
|
|
356
|
+
? findMap(lines, 'presentation', metadata.start + 1, metadata.end)
|
|
357
|
+
: null;
|
|
350
358
|
const typography = presentation
|
|
351
359
|
? findMap(lines, 'typography', presentation.index + 1, presentation.nextIndex)?.value
|
|
352
360
|
: null;
|