@kdrgny/justjson-astro 1.6.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/README.md +102 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +280 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @kdrgny/justjson-astro
|
|
4
|
+
|
|
5
|
+
**Astro content loader for [JustJSON](https://github.com/kdrgny-dev/justjson).**
|
|
6
|
+
|
|
7
|
+
Your `content/` folder becomes typed Astro content collections — no glue code.
|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @kdrgny/justjson-astro
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Use
|
|
20
|
+
|
|
21
|
+
One line in `src/content.config.ts`:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { justjsonCollections } from '@kdrgny/justjson-astro'
|
|
25
|
+
|
|
26
|
+
export const collections = await justjsonCollections()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Every collection and singleton in your JustJSON schema becomes an Astro
|
|
30
|
+
collection, with a Zod schema derived from your field definitions:
|
|
31
|
+
|
|
32
|
+
```astro
|
|
33
|
+
---
|
|
34
|
+
import { getCollection, getEntry } from 'astro:content'
|
|
35
|
+
|
|
36
|
+
const posts = await getCollection('posts')
|
|
37
|
+
const settings = await getEntry('settings', 'settings')
|
|
38
|
+
---
|
|
39
|
+
<h1>{settings?.data.title}</h1>
|
|
40
|
+
{posts.map((post) => <a href={`/posts/${post.id}`}>{post.data.title}</a>)}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`post.data` is fully typed. A field marked required in the editor is required in
|
|
44
|
+
the type; everything else is optional:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
{ title: string; slug: string; date?: string; body?: string; _status?: 'draft' | 'published' }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## What you get
|
|
51
|
+
|
|
52
|
+
| | |
|
|
53
|
+
|---|---|
|
|
54
|
+
| **Typed content** | Field types and required flags come straight from `_schema.json`. |
|
|
55
|
+
| **Drafts skipped** | Entries marked draft in the editor never reach your build. |
|
|
56
|
+
| **Live updates** | Save in the JustJSON editor and the dev server updates — no restart. |
|
|
57
|
+
| **Singletons** | A singleton becomes a one-entry collection; read it with `getEntry`. |
|
|
58
|
+
| **Safe when empty** | No schema yet? The build still succeeds with no collections. |
|
|
59
|
+
|
|
60
|
+
Entry `id` is the filename without `.json` — ready for `[slug].astro` routes.
|
|
61
|
+
|
|
62
|
+
## Options
|
|
63
|
+
|
|
64
|
+
`justjsonCollections()` takes an optional config:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
export const collections = await justjsonCollections({
|
|
68
|
+
drafts: true, // include draft entries (default: false)
|
|
69
|
+
contentDir: 'data', // content folder, relative to the project root
|
|
70
|
+
root: process.cwd(), // project root
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
For a single collection, use the loader directly:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { defineCollection } from 'astro:content'
|
|
78
|
+
import { justjson } from '@kdrgny/justjson-astro'
|
|
79
|
+
|
|
80
|
+
export const collections = {
|
|
81
|
+
posts: defineCollection({ loader: justjson({ collection: 'posts' }) }),
|
|
82
|
+
settings: defineCollection({ loader: justjson({ singleton: 'settings' }) }),
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Field type mapping
|
|
87
|
+
|
|
88
|
+
| JustJSON | Astro / Zod |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `text` · `richtext` · `date` · `image` · `url` · `email` · `color` | `string` |
|
|
91
|
+
| `number` | `number` |
|
|
92
|
+
| `boolean` | `boolean` |
|
|
93
|
+
| `select` | `enum` of your options (or `string` if none) |
|
|
94
|
+
| `relation` · `list` | `string[]` |
|
|
95
|
+
| `group` | nested object |
|
|
96
|
+
|
|
97
|
+
Format checks (is this a real URL?) are left to `justjson validate` on purpose,
|
|
98
|
+
so a typo in one entry never breaks your build in a surprising place.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
[MIT](https://github.com/kdrgny-dev/justjson/blob/main/LICENSE) © Kadir Günay
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Loader } from 'astro/loaders';
|
|
2
|
+
import { defineCollection } from 'astro/content/config';
|
|
3
|
+
import { Field } from '@justjson/core';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
interface JustJsonLoaderOptions {
|
|
7
|
+
/** Yüklenecek koleksiyonun şemadaki adı. `singleton` ile birlikte kullanılmaz. */
|
|
8
|
+
collection?: string;
|
|
9
|
+
/** Yüklenecek tekil kaydın şemadaki adı. Tek entry'li bir koleksiyon olarak gelir. */
|
|
10
|
+
singleton?: string;
|
|
11
|
+
/** Proje kökü. Verilmezse Astro'nun `config.root`'u kullanılır. */
|
|
12
|
+
root?: string | URL;
|
|
13
|
+
/** İçerik klasörü, köke göreli (varsayılan: "content"). */
|
|
14
|
+
contentDir?: string;
|
|
15
|
+
/** true ise `_status: 'draft'` kayıtlar da yüklenir. */
|
|
16
|
+
drafts?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Astro içerik koleksiyonlarını JustJSON'un `content/` klasöründen besler.
|
|
20
|
+
* Şemadaki alanlardan zod şeması üretir, böylece `entry.data` tam tipli gelir.
|
|
21
|
+
*/
|
|
22
|
+
declare function justjson(options: JustJsonLoaderOptions): Loader;
|
|
23
|
+
|
|
24
|
+
interface JustJsonCollectionsOptions {
|
|
25
|
+
/** Proje kökü (varsayılan: process.cwd() — `astro dev`/`build` buradan çalışır). */
|
|
26
|
+
root?: string | URL;
|
|
27
|
+
/** İçerik klasörü, köke göreli (varsayılan: "content"). */
|
|
28
|
+
contentDir?: string;
|
|
29
|
+
/** true ise taslak kayıtlar da yüklenir. */
|
|
30
|
+
drafts?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** Astro'nun `collections` export'unun beklediği şekil. */
|
|
33
|
+
type CollectionDefinition = ReturnType<typeof defineCollection>;
|
|
34
|
+
/**
|
|
35
|
+
* `content/_schema.json`'daki her koleksiyon ve tekil kayıt için bir Astro
|
|
36
|
+
* koleksiyonu üretir — content.config.ts'te tek satır yeter.
|
|
37
|
+
*
|
|
38
|
+
* Şema yoksa boş nesne döner: JustJSON'u henüz çalıştırmamış bir projede
|
|
39
|
+
* `astro build` kırılmaz.
|
|
40
|
+
*/
|
|
41
|
+
declare function justjsonCollections(options?: JustJsonCollectionsOptions): Promise<Record<string, CollectionDefinition>>;
|
|
42
|
+
|
|
43
|
+
/** JustJSON alanlarını Astro'nun kullanacağı bir zod nesnesine çevirir. */
|
|
44
|
+
declare function fieldsToZod(fields: Field[]): z.ZodObject<z.ZodRawShape>;
|
|
45
|
+
|
|
46
|
+
export { type CollectionDefinition, type JustJsonCollectionsOptions, type JustJsonLoaderOptions, fieldsToZod, justjson, justjsonCollections };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// src/loader.ts
|
|
2
|
+
import { readFile, readdir } from "fs/promises";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
// ../core/dist/index.js
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
var JustJsonError = class extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = new.target.name;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var SchemaError = class extends JustJsonError {
|
|
14
|
+
};
|
|
15
|
+
var fieldTypes = [
|
|
16
|
+
"text",
|
|
17
|
+
"richtext",
|
|
18
|
+
"number",
|
|
19
|
+
"boolean",
|
|
20
|
+
"date",
|
|
21
|
+
"select",
|
|
22
|
+
"relation",
|
|
23
|
+
"image",
|
|
24
|
+
"url",
|
|
25
|
+
"email",
|
|
26
|
+
"list",
|
|
27
|
+
"color",
|
|
28
|
+
"group"
|
|
29
|
+
];
|
|
30
|
+
var zField = z.lazy(
|
|
31
|
+
() => z.object({
|
|
32
|
+
key: z.string().min(1),
|
|
33
|
+
label: z.string().optional(),
|
|
34
|
+
type: z.enum(fieldTypes),
|
|
35
|
+
required: z.boolean().optional(),
|
|
36
|
+
options: z.array(z.string()).optional(),
|
|
37
|
+
to: z.string().optional(),
|
|
38
|
+
fields: z.array(zField).optional()
|
|
39
|
+
}).superRefine((field, ctx) => {
|
|
40
|
+
if (field.type === "select" && (!field.options || field.options.length === 0)) {
|
|
41
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "a select field requires options" });
|
|
42
|
+
}
|
|
43
|
+
if (field.type === "relation" && !field.to) {
|
|
44
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'a relation field requires "to"' });
|
|
45
|
+
}
|
|
46
|
+
if (field.type === "group" && (!field.fields || field.fields.length === 0)) {
|
|
47
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "a group field requires fields" });
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
var zCollection = z.object({
|
|
52
|
+
name: z.string().min(1),
|
|
53
|
+
label: z.string().optional(),
|
|
54
|
+
path: z.string().min(1),
|
|
55
|
+
fields: z.array(zField)
|
|
56
|
+
});
|
|
57
|
+
var zSingleton = z.object({
|
|
58
|
+
name: z.string().min(1),
|
|
59
|
+
label: z.string().optional(),
|
|
60
|
+
path: z.string().min(1),
|
|
61
|
+
fields: z.array(zField)
|
|
62
|
+
});
|
|
63
|
+
var zSchema = z.object({
|
|
64
|
+
version: z.literal(1),
|
|
65
|
+
collections: z.array(zCollection),
|
|
66
|
+
singletons: z.array(zSingleton)
|
|
67
|
+
}).superRefine((schema, ctx) => {
|
|
68
|
+
const names = /* @__PURE__ */ new Set();
|
|
69
|
+
const paths = /* @__PURE__ */ new Set();
|
|
70
|
+
const collectionNames = new Set(schema.collections.map((c) => c.name));
|
|
71
|
+
const containers = [...schema.collections, ...schema.singletons];
|
|
72
|
+
for (const c of containers) {
|
|
73
|
+
if (names.has(c.name)) {
|
|
74
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `duplicate name: ${c.name}` });
|
|
75
|
+
}
|
|
76
|
+
names.add(c.name);
|
|
77
|
+
if (c.path.includes("..") || c.path.startsWith("/") || c.path.includes("\\")) {
|
|
78
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `unsafe path: ${c.path}` });
|
|
79
|
+
}
|
|
80
|
+
if (paths.has(c.path)) {
|
|
81
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `duplicate path: ${c.path}` });
|
|
82
|
+
}
|
|
83
|
+
paths.add(c.path);
|
|
84
|
+
const keys = /* @__PURE__ */ new Set();
|
|
85
|
+
for (const f of c.fields) {
|
|
86
|
+
if (keys.has(f.key)) {
|
|
87
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `duplicate field key: ${f.key}` });
|
|
88
|
+
}
|
|
89
|
+
keys.add(f.key);
|
|
90
|
+
if (f.type === "relation" && f.to && !collectionNames.has(f.to)) {
|
|
91
|
+
ctx.addIssue({
|
|
92
|
+
code: z.ZodIssueCode.custom,
|
|
93
|
+
message: `relation target does not exist: ${f.to}`
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
function parseSchema(input) {
|
|
100
|
+
const result = zSchema.safeParse(input);
|
|
101
|
+
if (result.success) return result.data;
|
|
102
|
+
const lines = result.error.issues.map(
|
|
103
|
+
(i) => i.path.length ? `${i.path.join(".")}: ${i.message}` : i.message
|
|
104
|
+
);
|
|
105
|
+
throw new SchemaError(lines.join("\n"));
|
|
106
|
+
}
|
|
107
|
+
var STATUS_KEY = "_status";
|
|
108
|
+
function entryStatus(data) {
|
|
109
|
+
return data[STATUS_KEY] === "draft" ? "draft" : "published";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/zod-schema.ts
|
|
113
|
+
import { z as z2 } from "zod";
|
|
114
|
+
function base(field) {
|
|
115
|
+
switch (field.type) {
|
|
116
|
+
case "number":
|
|
117
|
+
return z2.number();
|
|
118
|
+
case "boolean":
|
|
119
|
+
return z2.boolean();
|
|
120
|
+
case "relation":
|
|
121
|
+
case "list":
|
|
122
|
+
return z2.array(z2.string());
|
|
123
|
+
case "group":
|
|
124
|
+
return fieldsToZod(field.fields ?? []);
|
|
125
|
+
case "select": {
|
|
126
|
+
const options = field.options ?? [];
|
|
127
|
+
return options.length > 0 ? z2.enum(options) : z2.string();
|
|
128
|
+
}
|
|
129
|
+
default:
|
|
130
|
+
return z2.string();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function fieldsToZod(fields) {
|
|
134
|
+
const shape = {
|
|
135
|
+
[STATUS_KEY]: z2.enum(["draft", "published"]).optional()
|
|
136
|
+
};
|
|
137
|
+
for (const field of fields) {
|
|
138
|
+
const type = base(field);
|
|
139
|
+
shape[field.key] = field.required ? type : type.optional();
|
|
140
|
+
}
|
|
141
|
+
return z2.object(shape);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/loader.ts
|
|
145
|
+
function toPath(value) {
|
|
146
|
+
return (typeof value === "string" ? value : fileURLToPath(value)).replace(/\/$/, "");
|
|
147
|
+
}
|
|
148
|
+
async function readSchema(root, contentDir) {
|
|
149
|
+
let raw;
|
|
150
|
+
try {
|
|
151
|
+
raw = await readFile(`${root}/${contentDir}/_schema.json`, "utf8");
|
|
152
|
+
} catch {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`justjson: no schema found at ${contentDir}/_schema.json \u2014 run \`npx @kdrgny/justjson\` in this folder first.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return parseSchema(JSON.parse(raw));
|
|
158
|
+
}
|
|
159
|
+
function justjson(options) {
|
|
160
|
+
const contentDir = options.contentDir ?? "content";
|
|
161
|
+
const name = options.collection ?? options.singleton;
|
|
162
|
+
if (!name) throw new Error("justjson: pass either `collection` or `singleton`.");
|
|
163
|
+
return {
|
|
164
|
+
name: "@kdrgny/justjson-astro",
|
|
165
|
+
schema: async () => {
|
|
166
|
+
const root = options.root ? toPath(options.root) : process.cwd();
|
|
167
|
+
try {
|
|
168
|
+
const schema = await readSchema(root, contentDir);
|
|
169
|
+
const container = options.singleton ? schema.singletons.find((s) => s.name === name) : schema.collections.find((c) => c.name === name);
|
|
170
|
+
return fieldsToZod(container?.fields ?? []);
|
|
171
|
+
} catch {
|
|
172
|
+
return fieldsToZod([]);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
load: async (ctx) => {
|
|
176
|
+
const root = toPath(options.root ?? ctx.config.root);
|
|
177
|
+
const schema = await readSchema(root, contentDir);
|
|
178
|
+
ctx.store.clear();
|
|
179
|
+
const sync = async (id, absolute, relative) => {
|
|
180
|
+
let raw;
|
|
181
|
+
try {
|
|
182
|
+
raw = await readFile(absolute, "utf8");
|
|
183
|
+
} catch {
|
|
184
|
+
ctx.store.delete(id);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const data = JSON.parse(raw);
|
|
188
|
+
if (!options.drafts && entryStatus(data) === "draft") {
|
|
189
|
+
ctx.store.delete(id);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
ctx.store.set({
|
|
193
|
+
id,
|
|
194
|
+
data: await ctx.parseData({ id, data, filePath: relative }),
|
|
195
|
+
digest: ctx.generateDigest(data),
|
|
196
|
+
filePath: relative
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
if (options.singleton) {
|
|
200
|
+
const single = schema.singletons.find((s) => s.name === name);
|
|
201
|
+
if (!single) throw new Error(`justjson: singleton "${name}" is not in the schema.`);
|
|
202
|
+
const absolute = `${root}/${contentDir}/${single.path}`;
|
|
203
|
+
await sync(name, absolute, `${contentDir}/${single.path}`);
|
|
204
|
+
watch(ctx, absolute, (changed, event) => {
|
|
205
|
+
if (changed !== absolute) return;
|
|
206
|
+
if (event === "unlink") return void ctx.store.delete(name);
|
|
207
|
+
return sync(name, absolute, `${contentDir}/${single.path}`);
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const collection = schema.collections.find((c) => c.name === name);
|
|
212
|
+
if (!collection) throw new Error(`justjson: collection "${name}" is not in the schema.`);
|
|
213
|
+
const dir = `${root}/${contentDir}/${collection.path}`;
|
|
214
|
+
const idFor = (absolute) => absolute.slice(dir.length + 1, -".json".length);
|
|
215
|
+
const relativeFor = (absolute) => `${contentDir}/${collection.path}/${absolute.slice(dir.length + 1)}`;
|
|
216
|
+
let files;
|
|
217
|
+
try {
|
|
218
|
+
files = (await readdir(dir)).filter((f) => f.endsWith(".json"));
|
|
219
|
+
} catch {
|
|
220
|
+
ctx.logger.warn(
|
|
221
|
+
`justjson: ${contentDir}/${collection.path}/ does not exist yet \u2014 collection is empty.`
|
|
222
|
+
);
|
|
223
|
+
files = [];
|
|
224
|
+
}
|
|
225
|
+
for (const file of files.sort()) {
|
|
226
|
+
await sync(file.slice(0, -".json".length), `${dir}/${file}`, relativeFor(`${dir}/${file}`));
|
|
227
|
+
}
|
|
228
|
+
watch(ctx, dir, (changed, event) => {
|
|
229
|
+
if (!changed.startsWith(`${dir}/`) || !changed.endsWith(".json")) return;
|
|
230
|
+
if (event === "unlink") return void ctx.store.delete(idFor(changed));
|
|
231
|
+
return sync(idFor(changed), changed, relativeFor(changed));
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function watch(ctx, target, onEvent) {
|
|
237
|
+
if (!ctx.watcher) return;
|
|
238
|
+
ctx.watcher.add(target);
|
|
239
|
+
for (const event of ["change", "add", "unlink"]) {
|
|
240
|
+
ctx.watcher.on(event, (path) => onEvent(path, event));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/collections.ts
|
|
245
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
246
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
247
|
+
import { defineCollection } from "astro/content/config";
|
|
248
|
+
async function justjsonCollections(options = {}) {
|
|
249
|
+
const contentDir = options.contentDir ?? "content";
|
|
250
|
+
const rootValue = options.root ?? process.cwd();
|
|
251
|
+
const root = (typeof rootValue === "string" ? rootValue : fileURLToPath2(rootValue)).replace(
|
|
252
|
+
/\/$/,
|
|
253
|
+
""
|
|
254
|
+
);
|
|
255
|
+
let raw;
|
|
256
|
+
try {
|
|
257
|
+
raw = await readFile2(`${root}/${contentDir}/_schema.json`, "utf8");
|
|
258
|
+
} catch {
|
|
259
|
+
return {};
|
|
260
|
+
}
|
|
261
|
+
const schema = parseSchema(JSON.parse(raw));
|
|
262
|
+
const shared = { root, contentDir, drafts: options.drafts };
|
|
263
|
+
const out = {};
|
|
264
|
+
for (const collection of schema.collections) {
|
|
265
|
+
out[collection.name] = defineCollection({
|
|
266
|
+
loader: justjson({ ...shared, collection: collection.name })
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
for (const singleton of schema.singletons) {
|
|
270
|
+
out[singleton.name] = defineCollection({
|
|
271
|
+
loader: justjson({ ...shared, singleton: singleton.name })
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return out;
|
|
275
|
+
}
|
|
276
|
+
export {
|
|
277
|
+
fieldsToZod,
|
|
278
|
+
justjson,
|
|
279
|
+
justjsonCollections
|
|
280
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kdrgny/justjson-astro",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"description": "Astro content loader for JustJSON — turn your content/ folder into typed Astro content collections.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"author": "Kadir Günay",
|
|
11
|
+
"homepage": "https://github.com/kdrgny-dev/justjson",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/kdrgny-dev/justjson.git",
|
|
15
|
+
"directory": "packages/astro"
|
|
16
|
+
},
|
|
17
|
+
"keywords": ["astro", "astro-loader", "astro-integration", "cms", "json", "content", "justjson"],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"files": ["dist"],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"typecheck": "tsc --noEmit"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"astro": "^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"zod": "^3.24.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@justjson/core": "workspace:*",
|
|
41
|
+
"@types/node": "^22.10.2",
|
|
42
|
+
"astro": "^5.0.0",
|
|
43
|
+
"tsup": "^8.3.5",
|
|
44
|
+
"typescript": "^5.7.2",
|
|
45
|
+
"vitest": "^2.1.8"
|
|
46
|
+
}
|
|
47
|
+
}
|