@janga/norna 0.7.9 → 0.7.10
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 +1 -1
- package/docs/README.md +14 -11
- package/docs/configuration.md +9 -54
- package/docs/content.md +6 -7
- package/docs/design/norna-diagram-design.md +2 -0
- package/docs/getting-started.md +18 -16
- package/docs/routes.md +7 -6
- package/docs/site-structure.md +4 -2
- package/docs/sitewide-content.md +74 -0
- package/docs/theme.md +61 -128
- package/docs/typography.md +45 -33
- package/examples/001-dog-shelter-single-page/README.md +9 -0
- package/examples/001-dog-shelter-single-page/site/config.mjs +27 -0
- package/examples/001-dog-shelter-single-page/site/content.md +38 -0
- package/examples/001-dog-shelter-single-page/site/images/what-we-do/dog-house.svg +25 -0
- package/examples/001-dog-shelter-single-page/site/images/you-can-help/heart.svg +13 -0
- package/examples/001-dog-shelter-single-page/site/public/favicon.svg +10 -0
- package/examples/001-dog-shelter-single-page/site/public/robots.txt +2 -0
- package/examples/001-dog-shelter-single-page/site/sitewide-content.md +6 -0
- package/examples/001-dog-shelter-single-page/site/theme.md +25 -0
- package/examples/002-dog-shelter-multi-page/README.md +9 -0
- package/examples/002-dog-shelter-multi-page/site/config.mjs +27 -0
- package/examples/002-dog-shelter-multi-page/site/content.md +12 -0
- package/examples/002-dog-shelter-multi-page/site/public/favicon.svg +10 -0
- package/examples/002-dog-shelter-multi-page/site/public/robots.txt +2 -0
- package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.svg +19 -0
- package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.svg +19 -0
- package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/route-content.md +30 -0
- package/examples/002-dog-shelter-multi-page/site/routes/020-adopt/route-content.md +12 -0
- package/examples/002-dog-shelter-multi-page/site/sitewide-content.md +6 -0
- package/examples/002-dog-shelter-multi-page/site/theme.md +25 -0
- package/examples/dog-gallery/.astro/collections/site.schema.json +776 -0
- package/examples/dog-gallery/.astro/collections/theme.schema.json +672 -0
- package/examples/dog-gallery/.astro/content-assets.mjs +4 -0
- package/examples/dog-gallery/.astro/content-modules.mjs +1 -0
- package/examples/dog-gallery/.astro/content.d.ts +175 -0
- package/examples/dog-gallery/.astro/data-store.json +1 -0
- package/examples/dog-gallery/.astro/dev-local.json +8 -0
- package/examples/dog-gallery/.astro/dev.json +13 -0
- package/examples/dog-gallery/.astro/dev.log +95 -0
- package/examples/dog-gallery/.astro/settings.json +5 -0
- package/examples/dog-gallery/.astro/types.d.ts +2 -0
- package/examples/dog-gallery/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +1 -0
- package/examples/dog-gallery/dist/_astro/SitePage.DicGh_5N.css +1 -0
- package/examples/dog-gallery/dist/dog-care/index.html +15 -0
- package/examples/dog-gallery/dist/dog-shelter-logo.svg +14 -0
- package/examples/dog-gallery/dist/favicon.svg +7 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1536.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-960.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-3939.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-2400.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1600.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-2576.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1277.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1536.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-768.webp +0 -0
- package/examples/dog-gallery/dist/index.html +45 -0
- package/examples/dog-gallery/dist/robots.txt +2 -0
- package/examples/dog-gallery/node_modules/.vite/deps/_metadata.json +7 -7
- package/examples/dog-gallery/site/.norna/generated-images.json +8 -8
- package/examples/dog-gallery/site/config.mjs +0 -20
- package/examples/dog-gallery/site/content.md +4 -5
- package/examples/dog-gallery/site/public/logo.svg +14 -0
- package/examples/dog-gallery/site/sitewide-content.md +16 -0
- package/examples/dog-gallery/site/theme.md +13 -24
- package/examples/presentation/README.md +15 -0
- package/examples/presentation/site/.norna/generated-images.json +66 -0
- package/examples/presentation/site/config.mjs +29 -0
- package/examples/presentation/site/content.md +67 -0
- package/examples/presentation/site/public/robots.txt +2 -0
- package/examples/presentation/site/routes/010-presets/route-content.md +63 -0
- package/examples/presentation/site/routes/010-presets/theme.md +4 -0
- package/examples/presentation/site/routes/020-media/images/cards/card-carousel.svg +11 -0
- package/examples/presentation/site/routes/020-media/images/cards/card-stack.svg +9 -0
- package/examples/presentation/site/routes/020-media/images/cards/card-surfaces.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/carousel/carousel-one.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/carousel/carousel-three.svg +13 -0
- package/examples/presentation/site/routes/020-media/images/carousel/carousel-two.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/stack/stack-one.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/stack/stack-two.svg +9 -0
- package/examples/presentation/site/routes/020-media/route-content.md +77 -0
- package/examples/presentation/site/routes/020-media/theme.md +4 -0
- package/examples/presentation/site/routes/030-surfaces/route-content.md +44 -0
- package/examples/presentation/site/routes/030-surfaces/theme.md +7 -0
- package/examples/presentation/site/sitewide-content.md +6 -0
- package/examples/presentation/site/theme.md +43 -0
- package/examples/typography-presets/.astro/collections/routeTheme.schema.json +746 -0
- package/examples/typography-presets/.astro/collections/site.schema.json +0 -717
- package/examples/typography-presets/.astro/collections/sitewide.schema.json +114 -0
- package/examples/typography-presets/.astro/collections/theme.schema.json +161 -59
- package/examples/typography-presets/.astro/content.d.ts +8 -0
- package/examples/typography-presets/.astro/data-store.json +1 -1
- package/examples/typography-presets/.astro/dev-local.json +2 -2
- package/examples/typography-presets/.astro/dev.json +2 -2
- package/examples/typography-presets/.astro/dev.log +37 -16
- package/examples/typography-presets/site/.norna/generated-images.json +76 -76
- package/examples/typography-presets/site/config.mjs +0 -13
- package/examples/typography-presets/site/content.md +8 -126
- package/examples/typography-presets/site/routes/010-quiet-gallery/route-content.md +34 -0
- package/examples/typography-presets/site/routes/010-quiet-gallery/theme.md +4 -0
- package/examples/typography-presets/site/routes/020-compact-gallery/route-content.md +33 -0
- package/examples/typography-presets/site/routes/020-compact-gallery/theme.md +4 -0
- package/examples/typography-presets/site/routes/030-text-forward/route-content.md +34 -0
- package/examples/typography-presets/site/routes/030-text-forward/theme.md +4 -0
- package/examples/typography-presets/site/routes/040-statement/route-content.md +34 -0
- package/examples/typography-presets/site/routes/040-statement/theme.md +4 -0
- package/examples/typography-presets/site/sitewide-content.md +14 -0
- package/examples/typography-presets/site/theme.md +9 -9
- package/fixtures/basic/site/config.mjs +0 -13
- package/fixtures/basic/site/sitewide-content.md +12 -0
- package/fixtures/basic/site/theme.md +2 -2
- package/fixtures/content-model-v2/site/content.md +2 -5
- package/fixtures/content-model-v2/site/theme.md +1 -3
- package/package.json +3 -2
- package/scripts/check-config.mjs +22 -1
- package/scripts/lib/frontmatter-yaml.mjs +28 -0
- package/scripts/lib/logo-assets.mjs +35 -0
- package/scripts/lib/norna-markdown-blocks.mjs +166 -0
- package/scripts/lib/presentation.mjs +104 -46
- package/scripts/lib/project-config.mjs +67 -105
- package/scripts/lib/site-content.mjs +26 -118
- package/scripts/lib/site-paths.mjs +2 -0
- package/scripts/lib/sitewide-config.mjs +34 -0
- package/scripts/lib/theme-config.mjs +72 -8
- package/scripts/lib/typography.mjs +20 -26
- package/scripts/show-typography.mjs +40 -147
- package/scripts/sync-content-sections.mjs +19 -17
- package/scripts/test-content-check.mjs +59 -27
- package/scripts/test-content-model-v2.mjs +154 -0
- package/scripts/test-engine-commands.mjs +3 -1
- package/scripts/test-package-check.mjs +66 -24
- package/scripts/test-temporary-visibility.mjs +19 -2
- package/src/components/CardList.astro +5 -2
- package/src/components/GalleryGrid.astro +37 -16
- package/src/components/SiteBanners.astro +100 -0
- package/src/components/SiteNavigation.astro +48 -31
- package/src/components/SitePage.astro +55 -13
- package/src/components/SiteSection.astro +193 -35
- package/src/content.config.ts +78 -48
- package/src/layouts/BaseLayout.astro +64 -14
- package/src/lib/routeThemes.ts +61 -0
- package/src/lib/sectionContent.ts +235 -51
- package/src/lib/sitePublicAssets.ts +1 -0
- package/src/styles/global.css +282 -93
- package/starters/basic/.github/workflows/deploy.yml +1 -1
- package/starters/basic/README.md +7 -6
- package/starters/basic/site/config.mjs +0 -13
- package/starters/basic/site/content.md +1 -1
- package/starters/basic/site/sitewide-content.md +14 -0
- package/starters/basic/site/theme.md +13 -24
- package/starters/project/.github/workflows/deploy.yml +1 -1
- package/starters/project/README.md +10 -8
- package/starters/project/site/config.mjs +0 -13
- package/starters/project/site/sitewide-content.md +14 -0
- package/starters/project/site/theme.md +3 -11
- /package/examples/typography-presets/site/{images → routes/010-quiet-gallery/images}/quiet-gallery/puppy-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/010-quiet-gallery/images}/quiet-gallery/shepherd-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/010-quiet-gallery/images}/quiet-gallery/white-puppy.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/020-compact-gallery/images}/compact-gallery/black-schnauzer.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/020-compact-gallery/images}/compact-gallery/retriever-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/020-compact-gallery/images}/compact-gallery/schnauzer-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/030-text-forward/images}/text-forward/boxer-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/030-text-forward/images}/text-forward/grass-puppy.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/030-text-forward/images}/text-forward/grey-street-dog.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/040-statement/images}/statement/black-pedigree.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/040-statement/images}/statement/sheepdog-grass.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/040-statement/images}/statement/sheepdog-wide.jpg +0 -0
|
@@ -17,27 +17,32 @@ export const typographyPresets = {
|
|
|
17
17
|
h1: {
|
|
18
18
|
align: { desktop: 'left', mobile: 'left' },
|
|
19
19
|
size: 'medium',
|
|
20
|
+
weight: 400,
|
|
20
21
|
lineHeight: 1.04,
|
|
21
22
|
},
|
|
22
23
|
h2: {
|
|
23
24
|
align: { desktop: 'left', mobile: 'left' },
|
|
24
25
|
size: 'medium',
|
|
26
|
+
weight: 400,
|
|
25
27
|
lineHeight: 1.08,
|
|
26
28
|
},
|
|
27
29
|
h3: {
|
|
28
30
|
align: { desktop: 'left', mobile: 'left' },
|
|
29
31
|
size: 'medium',
|
|
32
|
+
weight: 500,
|
|
30
33
|
lineHeight: 1.35,
|
|
31
34
|
},
|
|
32
35
|
h4: {
|
|
33
36
|
align: { desktop: 'left', mobile: 'left' },
|
|
34
37
|
size: 'medium',
|
|
38
|
+
weight: 400,
|
|
35
39
|
lineHeight: 1.5,
|
|
36
40
|
},
|
|
37
41
|
},
|
|
38
42
|
body: {
|
|
39
43
|
align: { desktop: 'left', mobile: 'left' },
|
|
40
44
|
size: 'medium',
|
|
45
|
+
width: 'normal',
|
|
41
46
|
lineHeight: 1.5,
|
|
42
47
|
},
|
|
43
48
|
caption: {
|
|
@@ -51,27 +56,32 @@ export const typographyPresets = {
|
|
|
51
56
|
h1: {
|
|
52
57
|
align: { desktop: 'left', mobile: 'left' },
|
|
53
58
|
size: 'medium',
|
|
59
|
+
weight: 500,
|
|
54
60
|
lineHeight: 1.04,
|
|
55
61
|
},
|
|
56
62
|
h2: {
|
|
57
63
|
align: { desktop: 'left', mobile: 'left' },
|
|
58
64
|
size: 'medium',
|
|
65
|
+
weight: 500,
|
|
59
66
|
lineHeight: 1.08,
|
|
60
67
|
},
|
|
61
68
|
h3: {
|
|
62
69
|
align: { desktop: 'left', mobile: 'left' },
|
|
63
70
|
size: 'medium',
|
|
71
|
+
weight: 600,
|
|
64
72
|
lineHeight: 1.3,
|
|
65
73
|
},
|
|
66
74
|
h4: {
|
|
67
75
|
align: { desktop: 'left', mobile: 'left' },
|
|
68
76
|
size: 'medium',
|
|
77
|
+
weight: 500,
|
|
69
78
|
lineHeight: 1.42,
|
|
70
79
|
},
|
|
71
80
|
},
|
|
72
81
|
body: {
|
|
73
82
|
align: { desktop: 'left', mobile: 'left' },
|
|
74
83
|
size: 'medium',
|
|
84
|
+
width: 'wide',
|
|
75
85
|
lineHeight: 1.42,
|
|
76
86
|
},
|
|
77
87
|
caption: {
|
|
@@ -85,27 +95,32 @@ export const typographyPresets = {
|
|
|
85
95
|
h1: {
|
|
86
96
|
align: { desktop: 'left', mobile: 'left' },
|
|
87
97
|
size: 'medium',
|
|
98
|
+
weight: 500,
|
|
88
99
|
lineHeight: 1.06,
|
|
89
100
|
},
|
|
90
101
|
h2: {
|
|
91
102
|
align: { desktop: 'left', mobile: 'left' },
|
|
92
103
|
size: 'medium',
|
|
104
|
+
weight: 500,
|
|
93
105
|
lineHeight: 1.12,
|
|
94
106
|
},
|
|
95
107
|
h3: {
|
|
96
108
|
align: { desktop: 'left', mobile: 'left' },
|
|
97
109
|
size: 'medium',
|
|
110
|
+
weight: 500,
|
|
98
111
|
lineHeight: 1.35,
|
|
99
112
|
},
|
|
100
113
|
h4: {
|
|
101
114
|
align: { desktop: 'left', mobile: 'left' },
|
|
102
115
|
size: 'medium',
|
|
116
|
+
weight: 400,
|
|
103
117
|
lineHeight: 1.55,
|
|
104
118
|
},
|
|
105
119
|
},
|
|
106
120
|
body: {
|
|
107
121
|
align: { desktop: 'left', mobile: 'left' },
|
|
108
122
|
size: 'medium',
|
|
123
|
+
width: 'narrow',
|
|
109
124
|
lineHeight: 1.62,
|
|
110
125
|
},
|
|
111
126
|
caption: {
|
|
@@ -119,27 +134,32 @@ export const typographyPresets = {
|
|
|
119
134
|
h1: {
|
|
120
135
|
align: { desktop: 'left', mobile: 'left' },
|
|
121
136
|
size: 'medium',
|
|
137
|
+
weight: 600,
|
|
122
138
|
lineHeight: 1.02,
|
|
123
139
|
},
|
|
124
140
|
h2: {
|
|
125
141
|
align: { desktop: 'left', mobile: 'left' },
|
|
126
142
|
size: 'medium',
|
|
143
|
+
weight: 600,
|
|
127
144
|
lineHeight: 1.04,
|
|
128
145
|
},
|
|
129
146
|
h3: {
|
|
130
147
|
align: { desktop: 'left', mobile: 'left' },
|
|
131
148
|
size: 'medium',
|
|
149
|
+
weight: 600,
|
|
132
150
|
lineHeight: 1.18,
|
|
133
151
|
},
|
|
134
152
|
h4: {
|
|
135
153
|
align: { desktop: 'left', mobile: 'left' },
|
|
136
154
|
size: 'medium',
|
|
155
|
+
weight: 500,
|
|
137
156
|
lineHeight: 1.42,
|
|
138
157
|
},
|
|
139
158
|
},
|
|
140
159
|
body: {
|
|
141
160
|
align: { desktop: 'left', mobile: 'left' },
|
|
142
161
|
size: 'medium',
|
|
162
|
+
width: 'narrow',
|
|
143
163
|
lineHeight: 1.42,
|
|
144
164
|
},
|
|
145
165
|
caption: {
|
|
@@ -243,32 +263,6 @@ export const resolveTypographyConfig = (typography = defaultTypography) => {
|
|
|
243
263
|
};
|
|
244
264
|
};
|
|
245
265
|
|
|
246
|
-
export const resolveTypographyOverride = (baseResolved, typographyConfig) => {
|
|
247
|
-
if (typographyConfig?.preset || typographyConfig?.rhythm) {
|
|
248
|
-
return resolveTypographyConfig({
|
|
249
|
-
preset: typographyConfig.preset ?? baseResolved.preset,
|
|
250
|
-
rhythm: typographyConfig.rhythm ?? baseResolved.rhythm,
|
|
251
|
-
overrides: typographyConfig.overrides,
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (typographyConfig?.overrides) {
|
|
256
|
-
return {
|
|
257
|
-
preset: baseResolved.preset,
|
|
258
|
-
rhythm: baseResolved.rhythm,
|
|
259
|
-
values: mergeDeep(baseResolved.values, typographyConfig.overrides),
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return baseResolved;
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
export const resolveSectionTypography = (defaultTypographyConfig, sectionTypographyConfig) => {
|
|
267
|
-
const defaultResolved = resolveTypographyConfig(defaultTypographyConfig);
|
|
268
|
-
|
|
269
|
-
return resolveTypographyOverride(defaultResolved, sectionTypographyConfig);
|
|
270
|
-
};
|
|
271
|
-
|
|
272
266
|
const quoteString = (value) => typeof value === 'string' && !/^[a-z0-9.-]+$/i.test(value)
|
|
273
267
|
? JSON.stringify(value)
|
|
274
268
|
: value;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { findMap } from './lib/frontmatter-yaml.mjs';
|
|
3
4
|
import {
|
|
4
5
|
defaultTypography,
|
|
5
|
-
resolveTypographyOverride,
|
|
6
6
|
resolveTypographyConfig,
|
|
7
7
|
toYamlLines,
|
|
8
8
|
typographyPresets,
|
|
@@ -15,11 +15,13 @@ import {
|
|
|
15
15
|
splitSiteFile,
|
|
16
16
|
validateContentFrontmatterStructure,
|
|
17
17
|
validateFrontmatterIndentation,
|
|
18
|
+
validateRouteThemeFrontmatterStructure,
|
|
18
19
|
validateThemeFrontmatterStructure,
|
|
19
20
|
} from './lib/site-content.mjs';
|
|
20
21
|
import {
|
|
21
22
|
siteContentLabel,
|
|
22
23
|
siteContentPath,
|
|
24
|
+
siteRoutesDir,
|
|
23
25
|
siteThemeLabel,
|
|
24
26
|
siteThemePath,
|
|
25
27
|
} from './lib/site-paths.mjs';
|
|
@@ -30,30 +32,35 @@ const typographyValuePaths = [
|
|
|
30
32
|
['headings', 'h1', 'align', 'desktop'],
|
|
31
33
|
['headings', 'h1', 'align', 'mobile'],
|
|
32
34
|
['headings', 'h1', 'size'],
|
|
35
|
+
['headings', 'h1', 'weight'],
|
|
33
36
|
['headings', 'h1', 'lineHeight'],
|
|
34
37
|
['headings', 'h1', 'spacingBefore'],
|
|
35
38
|
['headings', 'h1', 'spacingAfter'],
|
|
36
39
|
['headings', 'h2', 'align', 'desktop'],
|
|
37
40
|
['headings', 'h2', 'align', 'mobile'],
|
|
38
41
|
['headings', 'h2', 'size'],
|
|
42
|
+
['headings', 'h2', 'weight'],
|
|
39
43
|
['headings', 'h2', 'lineHeight'],
|
|
40
44
|
['headings', 'h2', 'spacingBefore'],
|
|
41
45
|
['headings', 'h2', 'spacingAfter'],
|
|
42
46
|
['headings', 'h3', 'align', 'desktop'],
|
|
43
47
|
['headings', 'h3', 'align', 'mobile'],
|
|
44
48
|
['headings', 'h3', 'size'],
|
|
49
|
+
['headings', 'h3', 'weight'],
|
|
45
50
|
['headings', 'h3', 'lineHeight'],
|
|
46
51
|
['headings', 'h3', 'spacingBefore'],
|
|
47
52
|
['headings', 'h3', 'spacingAfter'],
|
|
48
53
|
['headings', 'h4', 'align', 'desktop'],
|
|
49
54
|
['headings', 'h4', 'align', 'mobile'],
|
|
50
55
|
['headings', 'h4', 'size'],
|
|
56
|
+
['headings', 'h4', 'weight'],
|
|
51
57
|
['headings', 'h4', 'lineHeight'],
|
|
52
58
|
['headings', 'h4', 'spacingBefore'],
|
|
53
59
|
['headings', 'h4', 'spacingAfter'],
|
|
54
60
|
['body', 'align', 'desktop'],
|
|
55
61
|
['body', 'align', 'mobile'],
|
|
56
62
|
['body', 'size'],
|
|
63
|
+
['body', 'width'],
|
|
57
64
|
['body', 'lineHeight'],
|
|
58
65
|
['body', 'paragraphSpacing'],
|
|
59
66
|
['caption', 'align', 'desktop'],
|
|
@@ -74,8 +81,6 @@ const rhythmValuePaths = new Set([
|
|
|
74
81
|
'body.paragraphSpacing',
|
|
75
82
|
'caption.spacingBefore',
|
|
76
83
|
]);
|
|
77
|
-
const countIndent = (line) => line.match(/^\s*/)?.[0].length ?? 0;
|
|
78
|
-
|
|
79
84
|
const isPlainObject = (value) => (
|
|
80
85
|
value !== null &&
|
|
81
86
|
typeof value === 'object' &&
|
|
@@ -156,19 +161,6 @@ const applyOverrideSources = (sources, typographyConfig, sourceLabel) => {
|
|
|
156
161
|
return sources;
|
|
157
162
|
};
|
|
158
163
|
|
|
159
|
-
const inheritedSources = (sources) => {
|
|
160
|
-
const inherited = structuredClone(sources);
|
|
161
|
-
|
|
162
|
-
for (const path of typographyValuePaths) {
|
|
163
|
-
setPath(inherited, path, {
|
|
164
|
-
...getPath(sources, path),
|
|
165
|
-
inherited: true,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return inherited;
|
|
170
|
-
};
|
|
171
|
-
|
|
172
164
|
const resolveAnnotatedTypographyConfig = (typographyConfig, sourceLabel) => {
|
|
173
165
|
const resolved = resolveTypographyConfig(typographyConfig ?? defaultTypography);
|
|
174
166
|
const sources = applyOverrideSources(
|
|
@@ -193,126 +185,14 @@ const resolveAnnotatedTypographyConfig = (typographyConfig, sourceLabel) => {
|
|
|
193
185
|
};
|
|
194
186
|
};
|
|
195
187
|
|
|
196
|
-
const resolveAnnotatedTypographyOverride = (baseAnnotated, typographyConfig, sourceLabel) => {
|
|
197
|
-
if (typographyConfig?.preset || typographyConfig?.rhythm) {
|
|
198
|
-
const resolvedConfig = {
|
|
199
|
-
preset: typographyConfig.preset ?? baseAnnotated.resolved.preset,
|
|
200
|
-
rhythm: typographyConfig.rhythm ?? baseAnnotated.resolved.rhythm,
|
|
201
|
-
overrides: typographyConfig.overrides,
|
|
202
|
-
};
|
|
203
|
-
const resolved = resolveTypographyConfig(resolvedConfig);
|
|
204
|
-
const sources = applyOverrideSources(
|
|
205
|
-
defaultSources(resolved.preset, resolved.rhythm),
|
|
206
|
-
typographyConfig,
|
|
207
|
-
sourceLabel,
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
preset: {
|
|
212
|
-
value: resolved.preset,
|
|
213
|
-
source: typographyConfig.preset ? sourceLabel : baseAnnotated.preset.source,
|
|
214
|
-
...(typographyConfig.preset ? {} : { inherited: true }),
|
|
215
|
-
},
|
|
216
|
-
rhythm: {
|
|
217
|
-
value: resolved.rhythm,
|
|
218
|
-
source: typographyConfig.rhythm ? sourceLabel : baseAnnotated.rhythm.source,
|
|
219
|
-
...(typographyConfig.rhythm ? {} : { inherited: true }),
|
|
220
|
-
},
|
|
221
|
-
resolved,
|
|
222
|
-
sources,
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (typographyConfig?.overrides) {
|
|
227
|
-
const resolved = resolveTypographyOverride(baseAnnotated.resolved, typographyConfig);
|
|
228
|
-
const sources = applyOverrideSources(
|
|
229
|
-
inheritedSources(baseAnnotated.sources),
|
|
230
|
-
typographyConfig,
|
|
231
|
-
sourceLabel,
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
return {
|
|
235
|
-
preset: {
|
|
236
|
-
value: resolved.preset,
|
|
237
|
-
source: baseAnnotated.preset.source,
|
|
238
|
-
inherited: true,
|
|
239
|
-
},
|
|
240
|
-
rhythm: {
|
|
241
|
-
value: resolved.rhythm,
|
|
242
|
-
source: baseAnnotated.rhythm.source,
|
|
243
|
-
inherited: true,
|
|
244
|
-
},
|
|
245
|
-
resolved,
|
|
246
|
-
sources,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
preset: {
|
|
252
|
-
value: baseAnnotated.resolved.preset,
|
|
253
|
-
source: baseAnnotated.preset.source,
|
|
254
|
-
inherited: true,
|
|
255
|
-
},
|
|
256
|
-
rhythm: {
|
|
257
|
-
value: baseAnnotated.resolved.rhythm,
|
|
258
|
-
source: baseAnnotated.rhythm.source,
|
|
259
|
-
inherited: true,
|
|
260
|
-
},
|
|
261
|
-
resolved: baseAnnotated.resolved,
|
|
262
|
-
sources: inheritedSources(baseAnnotated.sources),
|
|
263
|
-
};
|
|
264
|
-
};
|
|
265
|
-
|
|
266
188
|
const formatAnnotatedTypography = (annotated) => ({
|
|
267
189
|
preset: annotated.preset,
|
|
268
190
|
rhythm: annotated.rhythm,
|
|
269
191
|
resolved: annotateResolvedValues(annotated.resolved, annotated.sources),
|
|
270
192
|
});
|
|
271
193
|
|
|
272
|
-
const getSectionMetadataBlocks = (lines) => {
|
|
273
|
-
const sectionsMap = findMap(lines, 'sections');
|
|
274
|
-
if (!sectionsMap) return new Map();
|
|
275
|
-
|
|
276
|
-
const sections = new Map();
|
|
277
|
-
let current = null;
|
|
278
|
-
|
|
279
|
-
for (let index = sectionsMap.index + 1; index < lines.length; index += 1) {
|
|
280
|
-
const line = lines[index];
|
|
281
|
-
if (!line.trim()) continue;
|
|
282
|
-
|
|
283
|
-
const indent = countIndent(line);
|
|
284
|
-
if (indent <= sectionsMap.indent) break;
|
|
285
|
-
|
|
286
|
-
const sectionMatch = line.match(/^\s{2}([a-z0-9-]+):(?:\s+.*)?\s*$/);
|
|
287
|
-
if (sectionMatch) {
|
|
288
|
-
if (current) current.end = index;
|
|
289
|
-
current = { id: sectionMatch[1], start: index, end: lines.length };
|
|
290
|
-
sections.set(current.id, current);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const sectionList = Array.from(sections.values());
|
|
295
|
-
const finalSection = sectionList.at(-1);
|
|
296
|
-
if (finalSection) {
|
|
297
|
-
const afterSections = lines.findIndex((line, index) => (
|
|
298
|
-
index > finalSection.start &&
|
|
299
|
-
line.trim() &&
|
|
300
|
-
countIndent(line) <= sectionsMap.indent
|
|
301
|
-
));
|
|
302
|
-
finalSection.end = afterSections === -1 ? lines.length : afterSections;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return sections;
|
|
306
|
-
};
|
|
307
|
-
|
|
308
194
|
const readThemeTypography = async () => {
|
|
309
|
-
const themeFile = await readFile(siteThemePath, 'utf8')
|
|
310
|
-
if (error?.code === 'ENOENT') {
|
|
311
|
-
return '---\n---\n';
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
throw error;
|
|
315
|
-
});
|
|
195
|
+
const themeFile = await readFile(siteThemePath, 'utf8');
|
|
316
196
|
const { frontmatter: themeFrontmatter, frontmatterBody: themeFrontmatterBody } = splitSiteFile(themeFile, siteThemeLabel);
|
|
317
197
|
const indentationIssues = [];
|
|
318
198
|
validateFrontmatterIndentation(themeFrontmatter, (issue) => indentationIssues.push(issue));
|
|
@@ -329,8 +209,35 @@ const readThemeTypography = async () => {
|
|
|
329
209
|
return resolveAnnotatedTypographyConfig(themeTypographyConfig ?? defaultTypography, siteThemeLabel);
|
|
330
210
|
};
|
|
331
211
|
|
|
332
|
-
const
|
|
333
|
-
|
|
212
|
+
const readRouteThemeTypography = async (contentFile, themeTypography) => {
|
|
213
|
+
if (contentFile.isHome) return themeTypography;
|
|
214
|
+
|
|
215
|
+
const routeThemePath = path.join(siteRoutesDir, contentFile.routeDirectory, 'theme.md');
|
|
216
|
+
const routeThemeLabel = `${contentFile.contentLabel.replace(/\/route-content\.md$/, '')}/theme.md`;
|
|
217
|
+
const routeThemeFile = await readFile(routeThemePath, 'utf8').catch((error) => {
|
|
218
|
+
if (error?.code === 'ENOENT') return null;
|
|
219
|
+
throw error;
|
|
220
|
+
});
|
|
221
|
+
if (!routeThemeFile) return themeTypography;
|
|
222
|
+
|
|
223
|
+
const { frontmatter, frontmatterBody } = splitSiteFile(routeThemeFile, routeThemeLabel);
|
|
224
|
+
const indentationIssues = [];
|
|
225
|
+
validateFrontmatterIndentation(frontmatter, (issue) => indentationIssues.push(issue));
|
|
226
|
+
validateRouteThemeFrontmatterStructure(frontmatter, (issue) => indentationIssues.push(issue));
|
|
227
|
+
if (indentationIssues.length > 0) {
|
|
228
|
+
throw new Error([
|
|
229
|
+
`Cannot inspect typography because ${routeThemeLabel} has invalid frontmatter.`,
|
|
230
|
+
...indentationIssues.map((issue) => `- ${issue.message}`),
|
|
231
|
+
].join('\n'));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const lines = frontmatterBody.split(/\r?\n/);
|
|
235
|
+
const typographyConfig = findMap(lines, 'typography', 0, lines.length, 0)?.value;
|
|
236
|
+
return resolveAnnotatedTypographyConfig(typographyConfig ?? defaultTypography, routeThemeLabel);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const readPageTypography = async (contentFile, siteThemeTypography) => {
|
|
240
|
+
const { frontmatter, body } = await readSiteFile(contentFile.contentPath, contentFile.contentLabel);
|
|
334
241
|
const indentationIssues = [];
|
|
335
242
|
validateFrontmatterIndentation(frontmatter, (issue) => indentationIssues.push(issue));
|
|
336
243
|
validateContentFrontmatterStructure(frontmatter, (issue) => indentationIssues.push(issue));
|
|
@@ -341,27 +248,13 @@ const readPageTypography = async (contentFile, themeTypography) => {
|
|
|
341
248
|
].join('\n'));
|
|
342
249
|
}
|
|
343
250
|
|
|
344
|
-
const
|
|
345
|
-
const pagePresentation = findMap(lines, 'presentation', 0, lines.length, 0);
|
|
346
|
-
const pageTypographyConfig = pagePresentation
|
|
347
|
-
? findMap(lines, 'typography', pagePresentation.index + 1, pagePresentation.nextIndex)?.value
|
|
348
|
-
: null;
|
|
349
|
-
const pageTypography = resolveAnnotatedTypographyOverride(themeTypography, pageTypographyConfig ?? undefined, contentFile.contentLabel);
|
|
350
|
-
const sectionMetadataBlocks = getSectionMetadataBlocks(lines);
|
|
251
|
+
const pageTypography = await readRouteThemeTypography(contentFile, siteThemeTypography);
|
|
351
252
|
const sections = getBodySections(body).sections
|
|
352
253
|
.filter((section) => section.id)
|
|
353
254
|
.map((section) => {
|
|
354
|
-
const metadata = sectionMetadataBlocks.get(section.id);
|
|
355
|
-
const presentation = metadata
|
|
356
|
-
? findMap(lines, 'presentation', metadata.start + 1, metadata.end)
|
|
357
|
-
: null;
|
|
358
|
-
const typography = presentation
|
|
359
|
-
? findMap(lines, 'typography', presentation.index + 1, presentation.nextIndex)?.value
|
|
360
|
-
: null;
|
|
361
|
-
|
|
362
255
|
return {
|
|
363
256
|
id: section.id,
|
|
364
|
-
typography:
|
|
257
|
+
typography: pageTypography,
|
|
365
258
|
};
|
|
366
259
|
});
|
|
367
260
|
|
|
@@ -5,6 +5,7 @@ import { stdin as input, stdout as output } from 'node:process';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import {
|
|
8
|
+
extractInlineNoteDiagnostics,
|
|
8
9
|
extractMarkdownImageReferences,
|
|
9
10
|
extractNornaMarkdownBlockDiagnostics,
|
|
10
11
|
getNornaBlockImageReferences,
|
|
@@ -12,15 +13,13 @@ import {
|
|
|
12
13
|
import {
|
|
13
14
|
getBodySections,
|
|
14
15
|
getContentFiles,
|
|
15
|
-
getFrontmatterInlineStyleNames,
|
|
16
16
|
getFrontmatterSections,
|
|
17
17
|
getImageCandidatesByName,
|
|
18
|
-
|
|
18
|
+
getDeprecatedInlineStyleReferences,
|
|
19
19
|
readSiteFile,
|
|
20
20
|
readThemeFile,
|
|
21
21
|
toPosixPath,
|
|
22
22
|
validateContentFrontmatterStructure,
|
|
23
|
-
validateFrontmatterColorValues,
|
|
24
23
|
validateFrontmatterIndentation,
|
|
25
24
|
validateThemeFrontmatterStructure,
|
|
26
25
|
} from './lib/site-content.mjs';
|
|
@@ -395,17 +394,12 @@ try {
|
|
|
395
394
|
themeFrontmatter = (await readThemeFile(siteThemePath)).frontmatter;
|
|
396
395
|
validateFrontmatterIndentation(themeFrontmatter, addIssue);
|
|
397
396
|
validateThemeFrontmatterStructure(themeFrontmatter, addIssue);
|
|
398
|
-
validateFrontmatterColorValues(themeFrontmatter, addIssue, siteThemeLabel);
|
|
399
397
|
} catch (error) {
|
|
400
398
|
if (error?.code !== 'ENOENT') {
|
|
401
399
|
throw error;
|
|
402
400
|
}
|
|
403
401
|
}
|
|
404
402
|
|
|
405
|
-
const themeInlineStyleNames = themeFrontmatter
|
|
406
|
-
? getFrontmatterInlineStyleNames(themeFrontmatter)
|
|
407
|
-
: new Set();
|
|
408
|
-
|
|
409
403
|
const contentFiles = await getContentFiles();
|
|
410
404
|
const allImageFiles = [];
|
|
411
405
|
const contentFileContexts = [];
|
|
@@ -417,7 +411,6 @@ for (const contentFile of contentFiles) {
|
|
|
417
411
|
const bodyLineOffset = frontmatter.split(/\r?\n/).length - 1;
|
|
418
412
|
validateFrontmatterIndentation(frontmatter, addIssue);
|
|
419
413
|
validateContentFrontmatterStructure(frontmatter, addIssue);
|
|
420
|
-
validateFrontmatterColorValues(frontmatter, addIssue, contentFile.contentLabel);
|
|
421
414
|
|
|
422
415
|
const frontmatterSections = getFrontmatterSections(frontmatter);
|
|
423
416
|
const frontmatterIds = new Set(frontmatterSections.map((section) => section.id));
|
|
@@ -465,6 +458,17 @@ for (const contentFile of contentFiles) {
|
|
|
465
458
|
});
|
|
466
459
|
blockResultsBySectionId.set(section.id, blockResults);
|
|
467
460
|
|
|
461
|
+
const inlineNoteResults = extractInlineNoteDiagnostics(section.text, {
|
|
462
|
+
label: contentFile.contentLabel,
|
|
463
|
+
lineOffset: bodyLineOffset + section.line - 1,
|
|
464
|
+
});
|
|
465
|
+
for (const error of inlineNoteResults.errors) {
|
|
466
|
+
addSectionIssue(contentFile, section, {
|
|
467
|
+
severity: 'error',
|
|
468
|
+
message: error.message,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
468
472
|
for (const reference of getNornaBlockImageReferences(blockResults.blocks)) {
|
|
469
473
|
const expectedPath = getExpectedImagePath(contentFile, section.id, reference.image);
|
|
470
474
|
addExpectedReference(globalExpectedReferencesByPath, expectedPath, { contentFile, section, reference });
|
|
@@ -501,14 +505,12 @@ for (const context of contentFileContexts) {
|
|
|
501
505
|
}
|
|
502
506
|
}
|
|
503
507
|
|
|
504
|
-
for (const styleName of new Set(
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
});
|
|
511
|
-
}
|
|
508
|
+
for (const styleName of new Set(getDeprecatedInlineStyleReferences(body))) {
|
|
509
|
+
addContentIssue(contentFile, {
|
|
510
|
+
severity: 'error',
|
|
511
|
+
message: `Inline color style ".${styleName}" is no longer supported in ${contentFile.contentLabel}.`,
|
|
512
|
+
fix: 'Use standard Markdown emphasis, a blockquote, or a semantic Norna block instead of color syntax.',
|
|
513
|
+
});
|
|
512
514
|
}
|
|
513
515
|
|
|
514
516
|
for (const section of sections) {
|
|
@@ -45,8 +45,8 @@ const makePngHeader = ({ width, height }) => {
|
|
|
45
45
|
const defaultTheme = `---
|
|
46
46
|
typography:
|
|
47
47
|
preset: quiet-gallery
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
presentation:
|
|
49
|
+
palette: dark
|
|
50
50
|
---
|
|
51
51
|
`;
|
|
52
52
|
|
|
@@ -173,14 +173,6 @@ test('content:check warns when carousel images use different aspect ratios', asy
|
|
|
173
173
|
});
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
-
const inlineStyleTheme = `---
|
|
177
|
-
presentation:
|
|
178
|
-
inlineStyles:
|
|
179
|
-
highlight:
|
|
180
|
-
color: "#ffd84d"
|
|
181
|
-
---
|
|
182
|
-
`;
|
|
183
|
-
|
|
184
176
|
const inlineStyleSite = `---
|
|
185
177
|
title: Inline Style
|
|
186
178
|
---
|
|
@@ -188,10 +180,9 @@ title: Inline Style
|
|
|
188
180
|
This has [known text]{.highlight} and [unknown text]{.missing}.
|
|
189
181
|
`;
|
|
190
182
|
|
|
191
|
-
test('content:check fails when Markdown uses
|
|
183
|
+
test('content:check fails when Markdown uses removed inline color styles', async () => {
|
|
192
184
|
await withTempProject({
|
|
193
185
|
site: inlineStyleSite,
|
|
194
|
-
theme: inlineStyleTheme,
|
|
195
186
|
files: [],
|
|
196
187
|
}, async (root) => {
|
|
197
188
|
const result = runContentScript(root, ['--check']);
|
|
@@ -199,20 +190,12 @@ test('content:check fails when Markdown uses undefined inline styles', async ()
|
|
|
199
190
|
|
|
200
191
|
assert.equal(result.status, 1, output);
|
|
201
192
|
assert.match(output, /^Content check failed\./m);
|
|
202
|
-
assert.match(output, /Inline style "\.
|
|
203
|
-
assert.
|
|
193
|
+
assert.match(output, /Inline color style "\.highlight" is no longer supported in site\/content\.md\./);
|
|
194
|
+
assert.match(output, /Inline color style "\.missing" is no longer supported in site\/content\.md\./);
|
|
204
195
|
});
|
|
205
196
|
});
|
|
206
197
|
|
|
207
|
-
|
|
208
|
-
presentation:
|
|
209
|
-
inlineStyles:
|
|
210
|
-
yellow:
|
|
211
|
-
color: "#ffd844d"
|
|
212
|
-
---
|
|
213
|
-
`;
|
|
214
|
-
|
|
215
|
-
test('content:check explains invalid inline style colors', async () => {
|
|
198
|
+
test('content:check suggests semantic Markdown instead of inline color styles', async () => {
|
|
216
199
|
await withTempProject({
|
|
217
200
|
site: `---
|
|
218
201
|
title: Invalid Inline Color
|
|
@@ -220,7 +203,6 @@ title: Invalid Inline Color
|
|
|
220
203
|
## Intro {#intro}
|
|
221
204
|
This has [yellow text]{.yellow}.
|
|
222
205
|
`,
|
|
223
|
-
theme: invalidInlineStyleColorTheme,
|
|
224
206
|
files: [],
|
|
225
207
|
}, async (root) => {
|
|
226
208
|
const result = runContentScript(root, ['--check']);
|
|
@@ -228,9 +210,59 @@ This has [yellow text]{.yellow}.
|
|
|
228
210
|
|
|
229
211
|
assert.equal(result.status, 1, output);
|
|
230
212
|
assert.match(output, /^Content check failed\./m);
|
|
231
|
-
assert.match(output, /
|
|
232
|
-
|
|
233
|
-
|
|
213
|
+
assert.match(output, /Use standard Markdown emphasis, a blockquote, or a semantic Norna block instead of color syntax\./);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const inlineNoteSite = `---
|
|
218
|
+
title: Inline notes
|
|
219
|
+
description: Fixture
|
|
220
|
+
---
|
|
221
|
+
## Intro {#intro}
|
|
222
|
+
This paragraph has a note reference.{note-ref}
|
|
223
|
+
|
|
224
|
+
{note: Additional context.}
|
|
225
|
+
|
|
226
|
+
\`\`\`text
|
|
227
|
+
{note-ref}
|
|
228
|
+
{note: This is code, not a note.
|
|
229
|
+
\`\`\`
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
test('content:check accepts valid inline notes and ignores note syntax in code fences', async () => {
|
|
233
|
+
await withTempProject({ site: inlineNoteSite, files: [] }, async (root) => {
|
|
234
|
+
const result = runContentScript(root, ['--check']);
|
|
235
|
+
const output = getOutput(result);
|
|
236
|
+
|
|
237
|
+
assert.equal(result.status, 0, output);
|
|
238
|
+
assert.match(output, /Content check passed\./);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('content:check rejects unpaired and repeated inline notes', async () => {
|
|
243
|
+
await withTempProject({
|
|
244
|
+
site: `---
|
|
245
|
+
title: Invalid inline notes
|
|
246
|
+
description: Fixture
|
|
247
|
+
---
|
|
248
|
+
## Orphan {#orphan}
|
|
249
|
+
{note: This note has no reference.}
|
|
250
|
+
|
|
251
|
+
## Missing {#missing}
|
|
252
|
+
This reference has no note.{note-ref}
|
|
253
|
+
|
|
254
|
+
## Repeated {#repeated}
|
|
255
|
+
This paragraph has {note-ref} two references {note-ref}.
|
|
256
|
+
`,
|
|
257
|
+
files: [],
|
|
258
|
+
}, async (root) => {
|
|
259
|
+
const result = runContentScript(root, ['--check']);
|
|
260
|
+
const output = getOutput(result);
|
|
261
|
+
|
|
262
|
+
assert.equal(result.status, 1, output);
|
|
263
|
+
assert.match(output, /A "\{note: \.\.\.\}" requires a preceding paragraph containing "\{note-ref\}"\./);
|
|
264
|
+
assert.match(output, /contains "\{note-ref\}" but has no following "\{note: \.\.\.\}"/);
|
|
265
|
+
assert.match(output, /A paragraph may contain only one "\{note-ref\}"\./);
|
|
234
266
|
});
|
|
235
267
|
});
|
|
236
268
|
|