@micacms/astro 0.0.4 → 0.1.1

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 CHANGED
@@ -94,4 +94,4 @@ that wants to render with its own components.
94
94
  npm install @micacms/astro
95
95
  ```
96
96
 
97
- Astro 5 is a peer dependency. Requires Node 22.5 or newer. Licence: MIT.
97
+ Astro 6 or 7 is a peer dependency. Requires Node 22.12 or newer. Licence: MIT.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micacms/astro",
3
- "version": "0.0.4",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Reads a Mica content repository as Astro Content Collections via the Content Layer API.",
6
6
  "license": "MIT",
@@ -28,15 +28,15 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@markdoc/markdoc": "^0.5.9",
31
- "zod": "^3.25.76",
32
- "@micacms/model": "0.0.4"
31
+ "zod": "^4.3.6",
32
+ "@micacms/model": "0.1.1"
33
33
  },
34
34
  "peerDependencies": {
35
- "astro": "^5.0.0"
35
+ "astro": "^6.0.0 || ^7.0.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^22.20.1",
39
- "astro": "^5.13.0",
39
+ "astro": "^7.2.2",
40
40
  "typescript": "^5.7.3"
41
41
  },
42
42
  "scripts": {
package/dist/read.d.ts DELETED
@@ -1,44 +0,0 @@
1
- /**
2
- * Entry discovery.
3
- *
4
- * Turns a content directory into payloads, using the layout rules the model
5
- * defines. Nothing here decides where a file belongs — `@micacms/model` does —
6
- * so a directory written by the editor and a directory written by hand are read
7
- * the same way.
8
- */
9
- import { type Collection, type Singleton } from "@micacms/model";
10
- import type { ContentSource } from "./source.js";
11
- /**
12
- * Optional manual ordering for a collection, listing slugs in the order an
13
- * editor arranged them.
14
- *
15
- * A separate file rather than a field on each entry, because order is a
16
- * property of the collection: writing it into the entries would make a single
17
- * drag rewrite every one of them, and the resulting Git diff would say nothing
18
- * about what changed.
19
- */
20
- export declare const ORDER_FILE = "_order.json";
21
- export interface ContentEntry {
22
- slug: string;
23
- /** Index in the collection's order — manual when `_order.json` says so. */
24
- position: number;
25
- data: Record<string, unknown>;
26
- }
27
- /** Raised with the offending path attached, which a bare parse error lacks. */
28
- export declare class ContentReadError extends Error {
29
- readonly path: string;
30
- name: string;
31
- constructor(message: string, path: string, options?: {
32
- cause?: unknown;
33
- });
34
- }
35
- export declare function readCollection(source: ContentSource, def: Collection, name: string): Promise<ContentEntry[]>;
36
- /**
37
- * A singleton's payload, or null when the repository has no file for it yet.
38
- *
39
- * Null rather than an error: a site is normally deployed before every settings
40
- * form has been filled in, and the collection schema is a better place to
41
- * report a missing required value than the reader is.
42
- */
43
- export declare function readSingleton(source: ContentSource, def: Singleton, name: string): Promise<Record<string, unknown> | null>;
44
- //# sourceMappingURL=read.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../src/read.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAOL,KAAK,UAAU,EAEf,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,gBAAgB,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,+EAA+E;AAC/E,qBAAa,gBAAiB,SAAQ,KAAK;IAIvC,QAAQ,CAAC,IAAI,EAAE,MAAM;IAHd,IAAI,SAAsB;gBAEjC,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIhC;AAMD,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,EAAE,CAAC,CAYzB;AA4ED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAEzC"}
package/dist/read.js DELETED
@@ -1,186 +0,0 @@
1
- /**
2
- * Entry discovery.
3
- *
4
- * Turns a content directory into payloads, using the layout rules the model
5
- * defines. Nothing here decides where a file belongs — `@micacms/model` does —
6
- * so a directory written by the editor and a directory written by hand are read
7
- * the same way.
8
- */
9
- import { DOCUMENT_EXTENSION, FormatError, INDEX_FILE, entryLayout, isValidSlug, parseEntry, } from "@micacms/model";
10
- /**
11
- * Optional manual ordering for a collection, listing slugs in the order an
12
- * editor arranged them.
13
- *
14
- * A separate file rather than a field on each entry, because order is a
15
- * property of the collection: writing it into the entries would make a single
16
- * drag rewrite every one of them, and the resulting Git diff would say nothing
17
- * about what changed.
18
- */
19
- export const ORDER_FILE = "_order.json";
20
- /** Raised with the offending path attached, which a bare parse error lacks. */
21
- export class ContentReadError extends Error {
22
- path;
23
- name = "ContentReadError";
24
- constructor(message, path, options) {
25
- super(message, options);
26
- this.path = path;
27
- }
28
- }
29
- /* ------------------------------------------------------------------ *
30
- * Collections
31
- * ------------------------------------------------------------------ */
32
- export async function readCollection(source, def, name) {
33
- const slugs = await listSlugs(source, def.fields, name);
34
- const ordered = await applyOrder(source, name, slugs);
35
- const entries = [];
36
- for (const [position, slug] of ordered.entries()) {
37
- const data = await readEntry(source, def.fields, `${name}/${slug}`, entryBase(def.fields, name, slug));
38
- if (data)
39
- entries.push({ slug, position, data });
40
- }
41
- return entries;
42
- }
43
- /**
44
- * Slugs present in a collection directory.
45
- *
46
- * The layout decides what to look at: directory-shaped entries are directories,
47
- * file-shaped ones are `.json` files. Anything that does not match — a stray
48
- * README, the order file, a directory without an `index.json` — is skipped
49
- * rather than rejected, because a content repository belongs to its owner and
50
- * may hold things Mica did not put there.
51
- */
52
- async function listSlugs(source, fields, name) {
53
- const directoryLayout = entryLayout(fields) === "directory";
54
- const items = await source.list(name);
55
- const slugs = [];
56
- for (const item of items) {
57
- if (directoryLayout) {
58
- if (item.type !== "directory")
59
- continue;
60
- if (isValidSlug(item.name))
61
- slugs.push(item.name);
62
- continue;
63
- }
64
- if (item.type !== "file" || !item.name.endsWith(".json"))
65
- continue;
66
- const slug = item.name.slice(0, -".json".length);
67
- if (isValidSlug(slug))
68
- slugs.push(slug);
69
- }
70
- return slugs;
71
- }
72
- /**
73
- * Reorders slugs to match `_order.json`.
74
- *
75
- * Tolerant in both directions: a slug the file does not mention goes after the
76
- * ones it does, and a slug the file mentions but which no longer exists is
77
- * dropped. Either happens routinely when someone adds or deletes an entry in
78
- * Git without touching the order file.
79
- */
80
- async function applyOrder(source, name, slugs) {
81
- const path = `${name}/${ORDER_FILE}`;
82
- const raw = await source.read(path);
83
- if (raw === null)
84
- return slugs;
85
- let listed;
86
- try {
87
- listed = JSON.parse(raw);
88
- }
89
- catch (error) {
90
- throw new ContentReadError(`${path} is not valid JSON.`, path, { cause: error });
91
- }
92
- if (!Array.isArray(listed)) {
93
- throw new ContentReadError(`${path} must be an array of slugs.`, path);
94
- }
95
- const available = new Set(slugs);
96
- const ordered = listed.filter((slug) => typeof slug === "string" && available.delete(slug));
97
- // Whatever the order file did not mention keeps its alphabetical position.
98
- return [...ordered, ...slugs.filter((slug) => available.has(slug))];
99
- }
100
- /* ------------------------------------------------------------------ *
101
- * Singletons
102
- * ------------------------------------------------------------------ */
103
- /**
104
- * A singleton's payload, or null when the repository has no file for it yet.
105
- *
106
- * Null rather than an error: a site is normally deployed before every settings
107
- * form has been filled in, and the collection schema is a better place to
108
- * report a missing required value than the reader is.
109
- */
110
- export async function readSingleton(source, def, name) {
111
- return readEntry(source, def.fields, name, entryBase(def.fields, name));
112
- }
113
- /* ------------------------------------------------------------------ *
114
- * Shared
115
- * ------------------------------------------------------------------ */
116
- /** The directory an entry's files live in, or null for file-shaped entries. */
117
- function entryBase(fields, ...segments) {
118
- return entryLayout(fields) === "directory" ? segments.join("/") : null;
119
- }
120
- async function readEntry(source, fields, label, base) {
121
- const dataPath = base === null ? `${label}.json` : `${base}/${INDEX_FILE}`;
122
- const data = await source.read(dataPath);
123
- if (data === null)
124
- return null;
125
- const documents = base === null ? {} : await readDocuments(source, base);
126
- let parsed;
127
- try {
128
- parsed = parseEntry(fields, { data, documents });
129
- }
130
- catch (error) {
131
- // A `FormatError` names the problem but not the file, and a build that
132
- // says only "Content is not valid JSON" is close to useless.
133
- if (error instanceof FormatError) {
134
- throw new ContentReadError(`${dataPath}: ${error.message}`, dataPath, { cause: error });
135
- }
136
- throw error;
137
- }
138
- return base === null ? parsed : resolveMedia(fields, parsed, base);
139
- }
140
- /**
141
- * Rewrites media references to paths relative to the content root.
142
- *
143
- * On disk a reference is written the way a person reading the entry would
144
- * write it: `media/exterior.jpg` for a file colocated with the entry, or
145
- * `/shared/logo.svg` for one the whole site uses. Both are ambiguous once the
146
- * entry is out of its directory, so the root-relative form is what leaves here.
147
- */
148
- function resolveMedia(fields, data, base) {
149
- const resolveOne = (value) => typeof value === "string" && value !== ""
150
- ? value.startsWith("/")
151
- ? value.slice(1)
152
- : `${base}/${value}`
153
- : value;
154
- const visit = (map, record) => {
155
- const result = { ...record };
156
- for (const [key, field] of Object.entries(map)) {
157
- const value = record[key];
158
- if (field.kind === "media") {
159
- result[key] = Array.isArray(value) ? value.map(resolveOne) : resolveOne(value);
160
- }
161
- else if (field.kind === "object" && isRecord(value)) {
162
- result[key] = visit(field.fields, value);
163
- }
164
- else if (field.kind === "repeater" && Array.isArray(value)) {
165
- result[key] = value.map((row) => (isRecord(row) ? visit(field.fields, row) : row));
166
- }
167
- }
168
- return result;
169
- };
170
- return visit(fields, data);
171
- }
172
- function isRecord(value) {
173
- return value !== null && typeof value === "object" && !Array.isArray(value);
174
- }
175
- async function readDocuments(source, base) {
176
- const documents = {};
177
- for (const item of await source.list(base)) {
178
- if (item.type !== "file" || !item.name.endsWith(DOCUMENT_EXTENSION))
179
- continue;
180
- const contents = await source.read(`${base}/${item.name}`);
181
- if (contents !== null)
182
- documents[item.name] = contents;
183
- }
184
- return documents;
185
- }
186
- //# sourceMappingURL=read.js.map
package/dist/read.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../src/read.ts"],"names":[],"mappings":"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"}
package/dist/source.d.ts DELETED
@@ -1,42 +0,0 @@
1
- /**
2
- * Where content files come from.
3
- *
4
- * Two lines wide on purpose. Everything above it — entry discovery, layout,
5
- * parsing — is written against `read` and `list` and nothing else, so the same
6
- * reader serves a checked-out repository during a build, an in-memory fixture
7
- * in a test, and later a repository connector that answers from a provider API
8
- * instead of a disk.
9
- */
10
- export interface DirectoryEntry {
11
- name: string;
12
- type: "file" | "directory";
13
- }
14
- export interface ContentSource {
15
- /** A description of the origin, for error messages. */
16
- readonly origin: string;
17
- /** File contents as UTF-8 text, or null when there is no such file. */
18
- read(path: string): Promise<string | null>;
19
- /** Direct children of a directory; empty when the directory does not exist. */
20
- list(path: string): Promise<DirectoryEntry[]>;
21
- }
22
- export declare class ContentSourceError extends Error {
23
- name: string;
24
- }
25
- /**
26
- * Reads a content directory in a checked-out repository.
27
- *
28
- * A missing file is not an error: an entry with no rich text simply has no
29
- * `.mdoc` beside it, and a collection an editor has not filled in yet is an
30
- * empty directory. Refusing to build over either would be wrong. Anything else
31
- * — a permission problem, a corrupt file, a path that escapes the root — is
32
- * raised, because it means the content the build was asked for is not there.
33
- */
34
- export declare function fileSystemSource(root: string): ContentSource;
35
- /**
36
- * A source built from a flat path-to-contents map, for tests and fixtures.
37
- *
38
- * Directories are inferred from the paths rather than declared, which keeps a
39
- * fixture to the files it actually cares about.
40
- */
41
- export declare function memorySource(files: Record<string, string>, origin?: string): ContentSource;
42
- //# sourceMappingURL=source.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3C,+EAA+E;IAC/E,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;CAC/C;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAClC,IAAI,SAAwB;CACtC;AAMD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CA6C5D;AAYD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,SAAW,GAAG,aAAa,CA8B5F"}
package/dist/source.js DELETED
@@ -1,113 +0,0 @@
1
- /**
2
- * Where content files come from.
3
- *
4
- * Two lines wide on purpose. Everything above it — entry discovery, layout,
5
- * parsing — is written against `read` and `list` and nothing else, so the same
6
- * reader serves a checked-out repository during a build, an in-memory fixture
7
- * in a test, and later a repository connector that answers from a provider API
8
- * instead of a disk.
9
- */
10
- import { readdir, readFile } from "node:fs/promises";
11
- import { isAbsolute, join, resolve, sep } from "node:path";
12
- export class ContentSourceError extends Error {
13
- name = "ContentSourceError";
14
- }
15
- /* ------------------------------------------------------------------ *
16
- * Filesystem
17
- * ------------------------------------------------------------------ */
18
- /**
19
- * Reads a content directory in a checked-out repository.
20
- *
21
- * A missing file is not an error: an entry with no rich text simply has no
22
- * `.mdoc` beside it, and a collection an editor has not filled in yet is an
23
- * empty directory. Refusing to build over either would be wrong. Anything else
24
- * — a permission problem, a corrupt file, a path that escapes the root — is
25
- * raised, because it means the content the build was asked for is not there.
26
- */
27
- export function fileSystemSource(root) {
28
- const base = resolve(root);
29
- const within = (path) => {
30
- if (isAbsolute(path)) {
31
- throw new ContentSourceError(`Content path "${path}" must be relative to the content root.`);
32
- }
33
- const full = resolve(base, path);
34
- // `startsWith(base)` alone would accept a sibling called `content-old`.
35
- if (full !== base && !full.startsWith(base + sep)) {
36
- throw new ContentSourceError(`Content path "${path}" escapes the content root.`);
37
- }
38
- return full;
39
- };
40
- return {
41
- origin: base,
42
- async read(path) {
43
- try {
44
- return await readFile(within(path), "utf8");
45
- }
46
- catch (error) {
47
- if (isMissing(error))
48
- return null;
49
- throw error;
50
- }
51
- },
52
- async list(path) {
53
- let items;
54
- try {
55
- items = await readdir(within(path), { withFileTypes: true });
56
- }
57
- catch (error) {
58
- if (isMissing(error))
59
- return [];
60
- throw error;
61
- }
62
- return items
63
- .filter((item) => item.isFile() || item.isDirectory())
64
- // Sorted here rather than at each call site, so that a build does not
65
- // depend on the order a filesystem happens to report.
66
- .sort((a, b) => a.name.localeCompare(b.name))
67
- .map((item) => ({ name: item.name, type: item.isFile() ? "file" : "directory" }));
68
- },
69
- };
70
- }
71
- /** ENOENT, and ENOTDIR for a path whose parent turned out to be a file. */
72
- function isMissing(error) {
73
- const code = error?.code;
74
- return code === "ENOENT" || code === "ENOTDIR";
75
- }
76
- /* ------------------------------------------------------------------ *
77
- * In memory
78
- * ------------------------------------------------------------------ */
79
- /**
80
- * A source built from a flat path-to-contents map, for tests and fixtures.
81
- *
82
- * Directories are inferred from the paths rather than declared, which keeps a
83
- * fixture to the files it actually cares about.
84
- */
85
- export function memorySource(files, origin = "memory") {
86
- const normalise = (path) => path.replace(/^\.?\//, "").replace(/\/$/, "");
87
- return {
88
- origin,
89
- async read(path) {
90
- return files[normalise(path)] ?? null;
91
- },
92
- async list(path) {
93
- const prefix = normalise(path) === "" ? "" : `${normalise(path)}/`;
94
- const seen = new Map();
95
- for (const key of Object.keys(files)) {
96
- if (!key.startsWith(prefix))
97
- continue;
98
- const rest = key.slice(prefix.length);
99
- if (rest === "")
100
- continue;
101
- const slash = rest.indexOf("/");
102
- if (slash === -1)
103
- seen.set(rest, "file");
104
- else
105
- seen.set(rest.slice(0, slash), "directory");
106
- }
107
- return [...seen]
108
- .sort(([a], [b]) => a.localeCompare(b))
109
- .map(([name, type]) => ({ name, type }));
110
- },
111
- };
112
- }
113
- //# sourceMappingURL=source.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"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"}