@janga/norna 0.7.0
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/LICENSE +674 -0
- package/README.md +109 -0
- package/astro.config.mjs +17 -0
- package/bin/norna.mjs +170 -0
- package/docs/README.md +48 -0
- package/docs/command-organization.md +402 -0
- package/docs/commands.md +152 -0
- package/docs/configuration.md +376 -0
- package/docs/content.md +384 -0
- package/docs/engine-development.md +164 -0
- package/docs/getting-started.md +96 -0
- package/docs/images-and-metadata.md +88 -0
- package/docs/local-development.md +61 -0
- package/docs/publishing.md +81 -0
- package/docs/site-examples-structure-note.md +105 -0
- package/docs/site-structure.md +81 -0
- package/fixtures/basic/site/.norna/generated-images.json +1 -0
- package/fixtures/basic/site/config.mjs +59 -0
- package/fixtures/basic/site/content.md +17 -0
- package/fixtures/basic/site/images/work/.gitkeep +1 -0
- package/fixtures/basic/site/public/robots.txt +2 -0
- package/fixtures/basic/site/theme.md +7 -0
- package/package.json +90 -0
- package/scripts/build-site.mjs +16 -0
- package/scripts/check-config.mjs +37 -0
- package/scripts/deploy-site.mjs +389 -0
- package/scripts/dev-local.mjs +313 -0
- package/scripts/doctor.mjs +38 -0
- package/scripts/engine-version.mjs +137 -0
- package/scripts/generate-images.mjs +369 -0
- package/scripts/init-site.mjs +249 -0
- package/scripts/lib/astro-command.mjs +34 -0
- package/scripts/lib/ci-lockfile.mjs +34 -0
- package/scripts/lib/image-dimensions.mjs +78 -0
- package/scripts/lib/presentation.mjs +72 -0
- package/scripts/lib/project-config.mjs +322 -0
- package/scripts/lib/run-command.mjs +21 -0
- package/scripts/lib/site-content.mjs +392 -0
- package/scripts/lib/site-paths.mjs +127 -0
- package/scripts/lib/typography.mjs +166 -0
- package/scripts/release.mjs +77 -0
- package/scripts/show-typography.mjs +210 -0
- package/scripts/sync-content-sections.mjs +610 -0
- package/scripts/sync-site-public.mjs +42 -0
- package/scripts/test-ci-lockfile.mjs +65 -0
- package/scripts/test-content-check.mjs +364 -0
- package/scripts/test-engine-commands.mjs +128 -0
- package/scripts/test-navigation-preview.mjs +108 -0
- package/scripts/test-navigation.mjs +116 -0
- package/scripts/test-package-check.mjs +394 -0
- package/scripts/test-site-public.mjs +85 -0
- package/scripts/test-temporary-visibility.mjs +99 -0
- package/scripts/update-engine.mjs +127 -0
- package/scripts/watch-pages-deploy.mjs +430 -0
- package/src/components/GalleryGrid.astro +221 -0
- package/src/components/SiteNavigation.astro +410 -0
- package/src/components/SitePage.astro +69 -0
- package/src/components/SiteSection.astro +174 -0
- package/src/content.config.ts +171 -0
- package/src/layouts/BaseLayout.astro +90 -0
- package/src/lib/generatedImages.ts +63 -0
- package/src/lib/sectionContent.ts +125 -0
- package/src/lib/sitePages.ts +80 -0
- package/src/lib/sitePublicAssets.ts +39 -0
- package/src/lib/visibility.ts +35 -0
- package/src/pages/[slug].astro +31 -0
- package/src/pages/index.astro +16 -0
- package/src/styles/global.css +872 -0
- package/starters/basic/.github/workflows/deploy.yml +65 -0
- package/starters/basic/README.md +55 -0
- package/starters/basic/package.json +35 -0
- package/starters/basic/site/config.mjs +60 -0
- package/starters/basic/site/content.md +21 -0
- package/starters/basic/site/images/work/.gitkeep +1 -0
- package/starters/basic/site/public/robots.txt +2 -0
- package/starters/basic/site/theme.md +53 -0
- package/tsconfig.json +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# norna
|
|
2
|
+
|
|
3
|
+
`norna` is a reusable command-line toolchain for small static gallery
|
|
4
|
+
sites. It provides the CLI, Astro renderer, validation scripts, image pipeline,
|
|
5
|
+
starter project, fixtures, and deploy helpers used by site repositories such as
|
|
6
|
+
`www.walde.se`.
|
|
7
|
+
|
|
8
|
+
Use this repository when you are:
|
|
9
|
+
|
|
10
|
+
- creating a new site repository from the starter,
|
|
11
|
+
- maintaining the reusable `@janga/norna` engine,
|
|
12
|
+
- looking up the generic CLI, content, configuration, image, build, or deploy
|
|
13
|
+
behavior.
|
|
14
|
+
|
|
15
|
+
Site-specific content, domain files, source images, and publication decisions
|
|
16
|
+
belong in each site repository. For example, Karin Walde's site lives in
|
|
17
|
+
`www.walde.se` and depends on a pinned version of this package.
|
|
18
|
+
|
|
19
|
+
## Mental Model
|
|
20
|
+
|
|
21
|
+
A `norna` site is file-driven:
|
|
22
|
+
|
|
23
|
+
1. A site repository depends on this package.
|
|
24
|
+
2. The site keeps technical settings, including page width, gallery width, and
|
|
25
|
+
the global font family, in `site/config.mjs`.
|
|
26
|
+
3. The site can keep site-wide visual theme defaults in `site/theme.md`.
|
|
27
|
+
4. The site keeps homepage content, section order, gallery rows, alt text, and
|
|
28
|
+
captions in `site/content.md`.
|
|
29
|
+
5. Optional route pages live under
|
|
30
|
+
`site/routes/<route-folder>/route-content.md`.
|
|
31
|
+
6. Source images live under `site/images/<section-id>/` for the homepage, or
|
|
32
|
+
under `site/routes/<route-folder>/images/<section-id>/` for a route.
|
|
33
|
+
7. Static public files live under `site/public/`.
|
|
34
|
+
8. `norna` validates the files, generates WebP variants, builds static
|
|
35
|
+
Astro pages, and can help publish the committed branch through GitHub Pages.
|
|
36
|
+
|
|
37
|
+
The default site directory is `site/`. Commands can also use another directory
|
|
38
|
+
with `NORNA_SITE_DIR` or `norna --site-dir <path>`.
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
For engine development in this repository:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
npm install
|
|
46
|
+
npm run dev:local
|
|
47
|
+
npm run test:fixture:build
|
|
48
|
+
npm run package:check
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For a new site repository:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
cd ../
|
|
55
|
+
npx @janga/norna@latest init my-gallery
|
|
56
|
+
cd my-gallery
|
|
57
|
+
npm install
|
|
58
|
+
npm run norna:dev
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Run `init` before `npm install`. A new site directory is not a Node project
|
|
62
|
+
until the starter has created its `package.json`; running `npm install` in an
|
|
63
|
+
empty directory can make npm use a parent project instead. Keep real site
|
|
64
|
+
repositories next to this engine repository, not inside it.
|
|
65
|
+
|
|
66
|
+
The starter pins `@janga/norna` to an exact npm version. Commit the
|
|
67
|
+
generated `package-lock.json` in the site repository so local builds and GitHub
|
|
68
|
+
Actions use the same engine version.
|
|
69
|
+
|
|
70
|
+
## Common Tasks
|
|
71
|
+
|
|
72
|
+
- Create a site: [Getting Started](docs/getting-started.md)
|
|
73
|
+
- Understand required site files: [Site Structure](docs/site-structure.md)
|
|
74
|
+
- Edit sections and galleries: [Content](docs/content.md)
|
|
75
|
+
- Configure a site: [Configuration](docs/configuration.md)
|
|
76
|
+
- Configure site-wide theme: [Content](docs/content.md#site-theme)
|
|
77
|
+
- Set page width: [`layout.pageWidth`](docs/configuration.md#layoutpagewidth)
|
|
78
|
+
- Set side gutters: [`layout.gutter`](docs/configuration.md#layoutgutter)
|
|
79
|
+
- Set gallery width: [`gallery.width`](docs/configuration.md#gallerywidth)
|
|
80
|
+
- Keep images within viewport height:
|
|
81
|
+
[`gallery.maxAvailableHeightPercent`](docs/configuration.md#gallerymaxavailableheightpercent)
|
|
82
|
+
- Set the site font: [`typography.fontFamily`](docs/configuration.md#typographyfontfamily)
|
|
83
|
+
- Choose typography presets and overrides: [Content](docs/content.md#typography-presets)
|
|
84
|
+
- Look up CLI and npm scripts: [Commands](docs/commands.md)
|
|
85
|
+
- Understand generated images: [Images And Metadata](docs/images-and-metadata.md)
|
|
86
|
+
- Run local preview: [Local Development](docs/local-development.md)
|
|
87
|
+
- Publish a site: [Publishing](docs/publishing.md)
|
|
88
|
+
- Work on the engine: [Engine Development](docs/engine-development.md)
|
|
89
|
+
|
|
90
|
+
## Requirements
|
|
91
|
+
|
|
92
|
+
- Node.js `>=22.12.0`.
|
|
93
|
+
- ImageMagick, either `magick` or the older `identify` and `convert` commands,
|
|
94
|
+
when generating images locally.
|
|
95
|
+
- GitHub CLI (`gh`) when using deploy checks or deploy monitoring.
|
|
96
|
+
- Playwright Chromium when running navigation diagnostics.
|
|
97
|
+
|
|
98
|
+
GitHub Pages workflows created from the starter install the image tools during
|
|
99
|
+
deployment.
|
|
100
|
+
|
|
101
|
+
## Documentation
|
|
102
|
+
|
|
103
|
+
Start with [docs/README.md](docs/README.md) for the documentation map and
|
|
104
|
+
recommended reading order.
|
|
105
|
+
|
|
106
|
+
Planning and future work are tracked in [BACKLOG.md](BACKLOG.md).
|
|
107
|
+
|
|
108
|
+
`AGENTS.md` contains operating rules for coding agents. Human-facing product
|
|
109
|
+
and workflow documentation should live in this README and `docs/`.
|
package/astro.config.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { defineConfig } from 'astro/config';
|
|
4
|
+
import {
|
|
5
|
+
astroCacheDir,
|
|
6
|
+
astroDistDir,
|
|
7
|
+
astroPublicDir,
|
|
8
|
+
engineRoot,
|
|
9
|
+
} from './scripts/lib/site-paths.mjs';
|
|
10
|
+
|
|
11
|
+
// https://astro.build/config
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
cacheDir: astroCacheDir,
|
|
14
|
+
outDir: astroDistDir,
|
|
15
|
+
publicDir: astroPublicDir,
|
|
16
|
+
srcDir: path.join(engineRoot, 'src'),
|
|
17
|
+
});
|
package/bin/norna.mjs
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
process.env.NORNA_INVOCATION_ROOT ??= process.cwd();
|
|
6
|
+
|
|
7
|
+
const usage = `
|
|
8
|
+
Usage: norna <command> [options]
|
|
9
|
+
|
|
10
|
+
Commands:
|
|
11
|
+
dev:local Start local Astro dev server
|
|
12
|
+
dev:lan Start dev server for this local network
|
|
13
|
+
dev:restart Restart local Astro dev server
|
|
14
|
+
dev:status Show local dev server status
|
|
15
|
+
dev:logs Show local dev server logs
|
|
16
|
+
dev:stop Stop local dev server
|
|
17
|
+
config:check Validate site/config.mjs
|
|
18
|
+
content:check Validate site/content.md and gallery references
|
|
19
|
+
content:sync Rewrite section order and move misplaced images
|
|
20
|
+
typography:presets Show built-in typography preset values
|
|
21
|
+
typography:show Show resolved typography for the selected site
|
|
22
|
+
site:public Sync site/public/ to public/
|
|
23
|
+
images Generate optimized image variants
|
|
24
|
+
engine:update Update @janga/norna in a site repository
|
|
25
|
+
engine:version Show installed engine and Astro versions
|
|
26
|
+
init Create a new site project from the starter
|
|
27
|
+
build Build the selected site
|
|
28
|
+
build:local Build and restart local dev server
|
|
29
|
+
deploy Build and deploy committed branch
|
|
30
|
+
deploy:commit Build, commit allowed changes, push, and check Pages
|
|
31
|
+
deploy:watch Watch GitHub Pages workflow
|
|
32
|
+
preview Preview dist/
|
|
33
|
+
astro Run Astro with norna config
|
|
34
|
+
doctor Print resolved paths
|
|
35
|
+
|
|
36
|
+
Global options:
|
|
37
|
+
--site-dir <path> Use a specific site source directory
|
|
38
|
+
-h, --help Show this help
|
|
39
|
+
`.trim();
|
|
40
|
+
|
|
41
|
+
const parseArgs = (rawArgs) => {
|
|
42
|
+
const commandArgs = [];
|
|
43
|
+
let siteDir = null;
|
|
44
|
+
|
|
45
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
46
|
+
const arg = rawArgs[index];
|
|
47
|
+
|
|
48
|
+
if (arg === '--site-dir') {
|
|
49
|
+
const value = rawArgs[index + 1];
|
|
50
|
+
if (!value || value.startsWith('-')) {
|
|
51
|
+
throw new Error('--site-dir requires a path.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
siteDir = value;
|
|
55
|
+
index += 1;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (arg.startsWith('--site-dir=')) {
|
|
60
|
+
siteDir = arg.slice('--site-dir='.length);
|
|
61
|
+
if (!siteDir) {
|
|
62
|
+
throw new Error('--site-dir requires a path.');
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
commandArgs.push(arg);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { commandArgs, siteDir };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let parsedArgs;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
parsedArgs = parseArgs(args);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { commandArgs, siteDir } = parsedArgs;
|
|
83
|
+
|
|
84
|
+
if (siteDir) {
|
|
85
|
+
process.env.NORNA_SITE_DIR = siteDir;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const [command = 'help', ...rest] = commandArgs;
|
|
89
|
+
|
|
90
|
+
if (command === '-h' || command === '--help' || command === 'help') {
|
|
91
|
+
console.log(usage);
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const [
|
|
96
|
+
{ runAstroInherit },
|
|
97
|
+
{ engineRoot, siteProjectRoot },
|
|
98
|
+
{ runInherit },
|
|
99
|
+
] = await Promise.all([
|
|
100
|
+
import('../scripts/lib/astro-command.mjs'),
|
|
101
|
+
import('../scripts/lib/site-paths.mjs'),
|
|
102
|
+
import('../scripts/lib/run-command.mjs'),
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const runScript = (relativePath, scriptArgs = []) => runInherit(
|
|
106
|
+
process.execPath,
|
|
107
|
+
[path.join(engineRoot, relativePath), ...scriptArgs],
|
|
108
|
+
{ cwd: siteProjectRoot },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const runBuild = () => runScript('scripts/build-site.mjs');
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
if (command === 'dev' || command === 'dev:local') {
|
|
115
|
+
await runScript('scripts/dev-local.mjs', rest);
|
|
116
|
+
} else if (command === 'dev:lan') {
|
|
117
|
+
await runScript('scripts/dev-local.mjs', ['lan', ...rest]);
|
|
118
|
+
} else if (command === 'dev:restart') {
|
|
119
|
+
await runScript('scripts/dev-local.mjs', ['restart', ...rest]);
|
|
120
|
+
} else if (command === 'dev:status') {
|
|
121
|
+
await runScript('scripts/dev-local.mjs', ['status', ...rest]);
|
|
122
|
+
} else if (command === 'dev:logs') {
|
|
123
|
+
await runScript('scripts/dev-local.mjs', ['logs', ...rest]);
|
|
124
|
+
} else if (command === 'dev:stop') {
|
|
125
|
+
await runScript('scripts/dev-local.mjs', ['stop', ...rest]);
|
|
126
|
+
} else if (command === 'config:check') {
|
|
127
|
+
await runScript('scripts/check-config.mjs', rest);
|
|
128
|
+
} else if (command === 'content:check') {
|
|
129
|
+
await runScript('scripts/sync-content-sections.mjs', ['--check', ...rest]);
|
|
130
|
+
await runAstroInherit(['sync']);
|
|
131
|
+
} else if (command === 'content:sync') {
|
|
132
|
+
await runScript('scripts/sync-content-sections.mjs', ['--write', ...rest]);
|
|
133
|
+
} else if (command === 'typography:presets') {
|
|
134
|
+
await runScript('scripts/show-typography.mjs', ['presets', ...rest]);
|
|
135
|
+
} else if (command === 'typography:show') {
|
|
136
|
+
await runScript('scripts/show-typography.mjs', ['show', ...rest]);
|
|
137
|
+
} else if (command === 'site:public') {
|
|
138
|
+
await runScript('scripts/sync-site-public.mjs', rest);
|
|
139
|
+
} else if (command === 'images') {
|
|
140
|
+
await runScript('scripts/generate-images.mjs', rest);
|
|
141
|
+
} else if (command === 'engine:update') {
|
|
142
|
+
await runScript('scripts/update-engine.mjs', rest);
|
|
143
|
+
} else if (command === 'engine:version') {
|
|
144
|
+
await runScript('scripts/engine-version.mjs', rest);
|
|
145
|
+
} else if (command === 'init') {
|
|
146
|
+
await runScript('scripts/init-site.mjs', rest);
|
|
147
|
+
} else if (command === 'build') {
|
|
148
|
+
await runBuild();
|
|
149
|
+
} else if (command === 'build:local') {
|
|
150
|
+
await runBuild();
|
|
151
|
+
await runScript('scripts/dev-local.mjs', ['restart']);
|
|
152
|
+
} else if (command === 'deploy') {
|
|
153
|
+
await runScript('scripts/deploy-site.mjs', rest);
|
|
154
|
+
} else if (command === 'deploy:commit') {
|
|
155
|
+
await runScript('scripts/deploy-site.mjs', ['commit', ...rest]);
|
|
156
|
+
} else if (command === 'deploy:watch') {
|
|
157
|
+
await runScript('scripts/watch-pages-deploy.mjs', rest);
|
|
158
|
+
} else if (command === 'doctor') {
|
|
159
|
+
await runScript('scripts/doctor.mjs', rest);
|
|
160
|
+
} else if (command === 'astro') {
|
|
161
|
+
await runAstroInherit(rest);
|
|
162
|
+
} else if (command === 'preview') {
|
|
163
|
+
await runAstroInherit(['preview', ...rest]);
|
|
164
|
+
} else {
|
|
165
|
+
throw new Error(`Unknown command: ${command}\n${usage}`);
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# norna Documentation
|
|
2
|
+
|
|
3
|
+
This directory contains the reusable product and engine documentation for
|
|
4
|
+
`norna`. The root README is the entry point; these files hold the details.
|
|
5
|
+
|
|
6
|
+
## Recommended Reading
|
|
7
|
+
|
|
8
|
+
For a site maintainer:
|
|
9
|
+
|
|
10
|
+
1. [Getting Started](getting-started.md)
|
|
11
|
+
2. [Site Structure](site-structure.md)
|
|
12
|
+
3. [Configuration](configuration.md)
|
|
13
|
+
4. [Content](content.md)
|
|
14
|
+
5. [Images And Metadata](images-and-metadata.md)
|
|
15
|
+
6. [Local Development](local-development.md)
|
|
16
|
+
7. [Publishing](publishing.md)
|
|
17
|
+
|
|
18
|
+
For a developer integrating or updating the engine:
|
|
19
|
+
|
|
20
|
+
1. [Commands](commands.md)
|
|
21
|
+
2. [Command Organization](command-organization.md)
|
|
22
|
+
3. [Site Examples Structure Note](site-examples-structure-note.md)
|
|
23
|
+
4. [Configuration](configuration.md)
|
|
24
|
+
5. [Content](content.md)
|
|
25
|
+
6. [Engine Development](engine-development.md)
|
|
26
|
+
|
|
27
|
+
For a quick reference:
|
|
28
|
+
|
|
29
|
+
- [Configuration](configuration.md) describes every supported
|
|
30
|
+
`site/config.mjs` field, including `layout.pageWidth`, `layout.gutter`,
|
|
31
|
+
gallery viewport limits, `typography.fontFamily`, validation rule, and
|
|
32
|
+
default.
|
|
33
|
+
- [Content](content.md) describes page and route frontmatter, section
|
|
34
|
+
frontmatter, typography presets, site themes, inline styles, temporary
|
|
35
|
+
sections, Markdown section matching, and content validation.
|
|
36
|
+
- [Commands](commands.md) lists the public CLI surface and starter npm scripts.
|
|
37
|
+
- [Command Organization](command-organization.md) defines command namespaces
|
|
38
|
+
for pure gallery projects, mixed projects, and engine development.
|
|
39
|
+
- [Site Structure](site-structure.md) separates versioned source files from
|
|
40
|
+
generated build output.
|
|
41
|
+
- [Site Examples Structure Note](site-examples-structure-note.md) defines the
|
|
42
|
+
intended vocabulary for starters, examples, docs sites, reference docs, and
|
|
43
|
+
fixtures before any repository reorganization.
|
|
44
|
+
|
|
45
|
+
Site repositories should document only their site-specific choices and link here
|
|
46
|
+
for generic `norna` behavior.
|
|
47
|
+
|
|
48
|
+
Planning and future work are tracked in [../BACKLOG.md](../BACKLOG.md).
|