@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.
Files changed (77) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +109 -0
  3. package/astro.config.mjs +17 -0
  4. package/bin/norna.mjs +170 -0
  5. package/docs/README.md +48 -0
  6. package/docs/command-organization.md +402 -0
  7. package/docs/commands.md +152 -0
  8. package/docs/configuration.md +376 -0
  9. package/docs/content.md +384 -0
  10. package/docs/engine-development.md +164 -0
  11. package/docs/getting-started.md +96 -0
  12. package/docs/images-and-metadata.md +88 -0
  13. package/docs/local-development.md +61 -0
  14. package/docs/publishing.md +81 -0
  15. package/docs/site-examples-structure-note.md +105 -0
  16. package/docs/site-structure.md +81 -0
  17. package/fixtures/basic/site/.norna/generated-images.json +1 -0
  18. package/fixtures/basic/site/config.mjs +59 -0
  19. package/fixtures/basic/site/content.md +17 -0
  20. package/fixtures/basic/site/images/work/.gitkeep +1 -0
  21. package/fixtures/basic/site/public/robots.txt +2 -0
  22. package/fixtures/basic/site/theme.md +7 -0
  23. package/package.json +90 -0
  24. package/scripts/build-site.mjs +16 -0
  25. package/scripts/check-config.mjs +37 -0
  26. package/scripts/deploy-site.mjs +389 -0
  27. package/scripts/dev-local.mjs +313 -0
  28. package/scripts/doctor.mjs +38 -0
  29. package/scripts/engine-version.mjs +137 -0
  30. package/scripts/generate-images.mjs +369 -0
  31. package/scripts/init-site.mjs +249 -0
  32. package/scripts/lib/astro-command.mjs +34 -0
  33. package/scripts/lib/ci-lockfile.mjs +34 -0
  34. package/scripts/lib/image-dimensions.mjs +78 -0
  35. package/scripts/lib/presentation.mjs +72 -0
  36. package/scripts/lib/project-config.mjs +322 -0
  37. package/scripts/lib/run-command.mjs +21 -0
  38. package/scripts/lib/site-content.mjs +392 -0
  39. package/scripts/lib/site-paths.mjs +127 -0
  40. package/scripts/lib/typography.mjs +166 -0
  41. package/scripts/release.mjs +77 -0
  42. package/scripts/show-typography.mjs +210 -0
  43. package/scripts/sync-content-sections.mjs +610 -0
  44. package/scripts/sync-site-public.mjs +42 -0
  45. package/scripts/test-ci-lockfile.mjs +65 -0
  46. package/scripts/test-content-check.mjs +364 -0
  47. package/scripts/test-engine-commands.mjs +128 -0
  48. package/scripts/test-navigation-preview.mjs +108 -0
  49. package/scripts/test-navigation.mjs +116 -0
  50. package/scripts/test-package-check.mjs +394 -0
  51. package/scripts/test-site-public.mjs +85 -0
  52. package/scripts/test-temporary-visibility.mjs +99 -0
  53. package/scripts/update-engine.mjs +127 -0
  54. package/scripts/watch-pages-deploy.mjs +430 -0
  55. package/src/components/GalleryGrid.astro +221 -0
  56. package/src/components/SiteNavigation.astro +410 -0
  57. package/src/components/SitePage.astro +69 -0
  58. package/src/components/SiteSection.astro +174 -0
  59. package/src/content.config.ts +171 -0
  60. package/src/layouts/BaseLayout.astro +90 -0
  61. package/src/lib/generatedImages.ts +63 -0
  62. package/src/lib/sectionContent.ts +125 -0
  63. package/src/lib/sitePages.ts +80 -0
  64. package/src/lib/sitePublicAssets.ts +39 -0
  65. package/src/lib/visibility.ts +35 -0
  66. package/src/pages/[slug].astro +31 -0
  67. package/src/pages/index.astro +16 -0
  68. package/src/styles/global.css +872 -0
  69. package/starters/basic/.github/workflows/deploy.yml +65 -0
  70. package/starters/basic/README.md +55 -0
  71. package/starters/basic/package.json +35 -0
  72. package/starters/basic/site/config.mjs +60 -0
  73. package/starters/basic/site/content.md +21 -0
  74. package/starters/basic/site/images/work/.gitkeep +1 -0
  75. package/starters/basic/site/public/robots.txt +2 -0
  76. package/starters/basic/site/theme.md +53 -0
  77. 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/`.
@@ -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).