@content-collections/core 0.7.0 → 0.7.2
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/dist/index.d.ts +2 -2
- package/dist/index.js +686 -491
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,244 +1,109 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
// src/utils.ts
|
|
5
|
-
import camelcase from "camelcase";
|
|
6
|
-
import pluralize from "pluralize";
|
|
7
|
-
function generateTypeName(name) {
|
|
8
|
-
const singularName = pluralize.singular(name);
|
|
9
|
-
return camelcase(singularName, { pascalCase: true });
|
|
10
|
-
}
|
|
11
|
-
function isDefined(value) {
|
|
12
|
-
return value !== void 0 && value !== null;
|
|
13
|
-
}
|
|
14
|
-
function orderByPath(a, b) {
|
|
15
|
-
return a.path.localeCompare(b.path);
|
|
16
|
-
}
|
|
17
|
-
function removeChildPaths(paths) {
|
|
18
|
-
return Array.from(
|
|
19
|
-
new Set(
|
|
20
|
-
paths.filter((path8) => {
|
|
21
|
-
return !paths.some((otherPath) => {
|
|
22
|
-
if (path8 === otherPath) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
return path8.startsWith(otherPath);
|
|
26
|
-
});
|
|
27
|
-
})
|
|
28
|
-
)
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// src/config.ts
|
|
33
|
-
var InvalidReturnTypeSymbol = Symbol(`InvalidReturnType`);
|
|
34
|
-
function defineCollection(collection) {
|
|
35
|
-
let typeName = collection.typeName;
|
|
36
|
-
if (!typeName) {
|
|
37
|
-
typeName = generateTypeName(collection.name);
|
|
38
|
-
}
|
|
39
|
-
let parser = collection.parser;
|
|
40
|
-
if (!parser) {
|
|
41
|
-
parser = "frontmatter";
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
...collection,
|
|
45
|
-
typeName,
|
|
46
|
-
parser,
|
|
47
|
-
schema: collection.schema(z)
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function defineConfig(config) {
|
|
51
|
-
return config;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// src/configurationReader.ts
|
|
55
|
-
import fs from "fs/promises";
|
|
56
|
-
import path from "path";
|
|
57
|
-
import { existsSync } from "fs";
|
|
58
|
-
import { createHash } from "crypto";
|
|
1
|
+
// src/builder.ts
|
|
2
|
+
import path8 from "node:path";
|
|
59
3
|
|
|
60
|
-
// src/
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
tsconfig = loadTsConfig();
|
|
68
|
-
}
|
|
69
|
-
return tsconfig?.data?.compilerOptions?.paths || {};
|
|
70
|
-
}
|
|
71
|
-
function createExternalsPlugin(configPath) {
|
|
72
|
-
const resolvedPaths = tsconfigResolvePaths(configPath);
|
|
73
|
-
const resolvePatterns = tsconfigPathsToRegExp(resolvedPaths);
|
|
74
|
-
return {
|
|
75
|
-
name: "external-packages",
|
|
76
|
-
setup: (build3) => {
|
|
77
|
-
const filter = /^[^.\/]|^\.[^.\/]|^\.\.[^\/]/;
|
|
78
|
-
build3.onResolve({ filter }, ({ path: path8, kind }) => {
|
|
79
|
-
if (match(path8, resolvePatterns)) {
|
|
80
|
-
if (kind === "dynamic-import") {
|
|
81
|
-
return { path: path8, external: true };
|
|
82
|
-
}
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
return { path: path8, external: true };
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
};
|
|
4
|
+
// src/cache.ts
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
8
|
+
import path, { join } from "node:path";
|
|
9
|
+
function createKey(config, input) {
|
|
10
|
+
return createHash("sha256").update(config).update(JSON.stringify(input)).digest("hex");
|
|
89
11
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return { path: join(__dirname, "index.ts"), external: true };
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
async function compile(configurationPath, outfile) {
|
|
99
|
-
const plugins = [createExternalsPlugin(configurationPath)];
|
|
100
|
-
if (process.env.NODE_ENV === "test") {
|
|
101
|
-
plugins.push(importPathPlugin);
|
|
12
|
+
async function createCacheDirectory(directory) {
|
|
13
|
+
const cacheDirectory = path.join(directory, ".content-collections", "cache");
|
|
14
|
+
if (!existsSync(cacheDirectory)) {
|
|
15
|
+
await mkdir(cacheDirectory, { recursive: true });
|
|
102
16
|
}
|
|
103
|
-
|
|
104
|
-
entryPoints: [configurationPath],
|
|
105
|
-
packages: "external",
|
|
106
|
-
bundle: true,
|
|
107
|
-
platform: "node",
|
|
108
|
-
format: "esm",
|
|
109
|
-
plugins,
|
|
110
|
-
outfile,
|
|
111
|
-
metafile: true
|
|
112
|
-
});
|
|
113
|
-
return Object.keys(result.metafile.inputs);
|
|
17
|
+
return cacheDirectory;
|
|
114
18
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
var ConfigurationError = class extends Error {
|
|
118
|
-
type;
|
|
119
|
-
constructor(type, message) {
|
|
120
|
-
super(message);
|
|
121
|
-
this.type = type;
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
var defaultConfigName = "content-collection-config.mjs";
|
|
125
|
-
function resolveCacheDir(config, options) {
|
|
126
|
-
if (options.cacheDir) {
|
|
127
|
-
return options.cacheDir;
|
|
128
|
-
}
|
|
129
|
-
return path.join(path.dirname(config), ".content-collections", "cache");
|
|
19
|
+
function fileName(input) {
|
|
20
|
+
return input.replace(/[^a-z0-9]/gi, "_").toLowerCase();
|
|
130
21
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
configName: defaultConfigName
|
|
134
|
-
}) => {
|
|
135
|
-
if (!existsSync(configurationPath)) {
|
|
136
|
-
throw new ConfigurationError(
|
|
137
|
-
"Read",
|
|
138
|
-
`configuration file ${configurationPath} does not exist`
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
const cacheDir = resolveCacheDir(configurationPath, options);
|
|
142
|
-
await fs.mkdir(cacheDir, { recursive: true });
|
|
143
|
-
const outfile = path.join(cacheDir, options.configName);
|
|
22
|
+
async function readMapping(mappingPath) {
|
|
23
|
+
if (existsSync(mappingPath)) {
|
|
144
24
|
try {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const checksum = hash.digest("hex");
|
|
150
|
-
return {
|
|
151
|
-
...module.default,
|
|
152
|
-
path: configurationPath,
|
|
153
|
-
inputPaths: configurationPaths.map((p) => path.resolve(p)),
|
|
154
|
-
generateTypes: true,
|
|
155
|
-
checksum
|
|
156
|
-
};
|
|
157
|
-
} catch (error) {
|
|
158
|
-
throw new ConfigurationError(
|
|
159
|
-
"Compile",
|
|
160
|
-
`configuration file ${configurationPath} is invalid: ${error}`
|
|
25
|
+
return JSON.parse(await readFile(mappingPath, "utf-8"));
|
|
26
|
+
} catch (e) {
|
|
27
|
+
console.error(
|
|
28
|
+
"Failed to parse the cache mapping. We will recreate the cache."
|
|
161
29
|
);
|
|
162
30
|
}
|
|
163
|
-
}
|
|
31
|
+
}
|
|
32
|
+
return {};
|
|
164
33
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
34
|
+
async function createCacheManager(baseDirectory, configChecksum) {
|
|
35
|
+
const cacheDirectory = await createCacheDirectory(baseDirectory);
|
|
36
|
+
const mappingPath = join(cacheDirectory, "mapping.json");
|
|
37
|
+
const mapping = await readMapping(mappingPath);
|
|
38
|
+
async function flush() {
|
|
39
|
+
await writeFile(mappingPath, JSON.stringify(mapping));
|
|
40
|
+
}
|
|
41
|
+
function cache(collection, file) {
|
|
42
|
+
const directory = join(
|
|
43
|
+
cacheDirectory,
|
|
44
|
+
fileName(collection),
|
|
45
|
+
fileName(file)
|
|
46
|
+
);
|
|
47
|
+
let collectionMapping = mapping[collection];
|
|
48
|
+
if (!collectionMapping) {
|
|
49
|
+
collectionMapping = {};
|
|
50
|
+
mapping[collection] = collectionMapping;
|
|
180
51
|
}
|
|
181
|
-
|
|
182
|
-
|
|
52
|
+
let fileMapping = collectionMapping[file];
|
|
53
|
+
if (!fileMapping) {
|
|
54
|
+
fileMapping = [];
|
|
55
|
+
collectionMapping[file] = fileMapping;
|
|
183
56
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
57
|
+
let newFileMapping = [];
|
|
58
|
+
const cacheFn = async (input, fn) => {
|
|
59
|
+
const key = createKey(configChecksum, input);
|
|
60
|
+
newFileMapping.push(key);
|
|
61
|
+
const filePath = join(directory, `${key}.cache`);
|
|
62
|
+
if (fileMapping?.includes(key) || newFileMapping.includes(key)) {
|
|
63
|
+
if (existsSync(filePath)) {
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(await readFile(filePath, "utf-8"));
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error(
|
|
68
|
+
"Failed to parse the cache file. We will recompute the value."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
197
72
|
}
|
|
198
|
-
await
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (isEventWithError(event)) {
|
|
222
|
-
emitter.emit("_error", {
|
|
223
|
-
...event,
|
|
224
|
-
_event: key
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
emitter.emit("_all", {
|
|
228
|
-
...event,
|
|
229
|
-
_event: key
|
|
230
|
-
});
|
|
73
|
+
const output = await fn(input);
|
|
74
|
+
if (!existsSync(directory)) {
|
|
75
|
+
await mkdir(directory, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
await writeFile(filePath, JSON.stringify(output));
|
|
78
|
+
return output;
|
|
79
|
+
};
|
|
80
|
+
const tidyUp = async () => {
|
|
81
|
+
const filesToDelete = fileMapping?.filter((key) => !newFileMapping.includes(key)) || [];
|
|
82
|
+
for (const key of filesToDelete) {
|
|
83
|
+
const filePath = join(directory, `${key}.cache`);
|
|
84
|
+
if (existsSync(filePath)) {
|
|
85
|
+
await unlink(filePath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (collectionMapping) {
|
|
89
|
+
collectionMapping[file] = newFileMapping;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
cacheFn,
|
|
94
|
+
tidyUp
|
|
95
|
+
};
|
|
231
96
|
}
|
|
232
97
|
return {
|
|
233
|
-
|
|
234
|
-
|
|
98
|
+
cache,
|
|
99
|
+
flush
|
|
235
100
|
};
|
|
236
101
|
}
|
|
237
102
|
|
|
238
103
|
// src/collector.ts
|
|
104
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
105
|
+
import path2 from "path";
|
|
239
106
|
import { glob } from "tinyglobby";
|
|
240
|
-
import { readFile } from "fs/promises";
|
|
241
|
-
import path3 from "path";
|
|
242
107
|
|
|
243
108
|
// src/parser.ts
|
|
244
109
|
import matter from "gray-matter";
|
|
@@ -260,20 +125,48 @@ function frontmatterParser(fileContent) {
|
|
|
260
125
|
content: content.trim()
|
|
261
126
|
};
|
|
262
127
|
}
|
|
263
|
-
var parsers = {
|
|
264
|
-
frontmatter: {
|
|
265
|
-
hasContent: true,
|
|
266
|
-
parse: frontmatterParser
|
|
267
|
-
},
|
|
268
|
-
json: {
|
|
269
|
-
hasContent: false,
|
|
270
|
-
parse: JSON.parse
|
|
271
|
-
},
|
|
272
|
-
yaml: {
|
|
273
|
-
hasContent: false,
|
|
274
|
-
parse: parseYaml
|
|
275
|
-
}
|
|
276
|
-
};
|
|
128
|
+
var parsers = {
|
|
129
|
+
frontmatter: {
|
|
130
|
+
hasContent: true,
|
|
131
|
+
parse: frontmatterParser
|
|
132
|
+
},
|
|
133
|
+
json: {
|
|
134
|
+
hasContent: false,
|
|
135
|
+
parse: JSON.parse
|
|
136
|
+
},
|
|
137
|
+
yaml: {
|
|
138
|
+
hasContent: false,
|
|
139
|
+
parse: parseYaml
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/utils.ts
|
|
144
|
+
import camelcase from "camelcase";
|
|
145
|
+
import pluralize from "pluralize";
|
|
146
|
+
function generateTypeName(name) {
|
|
147
|
+
const singularName = pluralize.singular(name);
|
|
148
|
+
return camelcase(singularName, { pascalCase: true });
|
|
149
|
+
}
|
|
150
|
+
function isDefined(value) {
|
|
151
|
+
return value !== void 0 && value !== null;
|
|
152
|
+
}
|
|
153
|
+
function orderByPath(a, b) {
|
|
154
|
+
return a.path.localeCompare(b.path);
|
|
155
|
+
}
|
|
156
|
+
function removeChildPaths(paths) {
|
|
157
|
+
return Array.from(
|
|
158
|
+
new Set(
|
|
159
|
+
paths.filter((path9) => {
|
|
160
|
+
return !paths.some((otherPath) => {
|
|
161
|
+
if (path9 === otherPath) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return path9.startsWith(otherPath);
|
|
165
|
+
});
|
|
166
|
+
})
|
|
167
|
+
)
|
|
168
|
+
);
|
|
169
|
+
}
|
|
277
170
|
|
|
278
171
|
// src/collector.ts
|
|
279
172
|
var CollectError = class extends Error {
|
|
@@ -286,7 +179,7 @@ var CollectError = class extends Error {
|
|
|
286
179
|
function createCollector(emitter, baseDirectory = ".") {
|
|
287
180
|
async function read(filePath) {
|
|
288
181
|
try {
|
|
289
|
-
return await
|
|
182
|
+
return await readFile2(filePath, "utf-8");
|
|
290
183
|
} catch (error) {
|
|
291
184
|
emitter.emit("collector:read-error", {
|
|
292
185
|
filePath,
|
|
@@ -296,7 +189,11 @@ function createCollector(emitter, baseDirectory = ".") {
|
|
|
296
189
|
}
|
|
297
190
|
}
|
|
298
191
|
async function collectFile(collection, filePath) {
|
|
299
|
-
const absolutePath =
|
|
192
|
+
const absolutePath = path2.join(
|
|
193
|
+
baseDirectory,
|
|
194
|
+
collection.directory,
|
|
195
|
+
filePath
|
|
196
|
+
);
|
|
300
197
|
const file = await read(absolutePath);
|
|
301
198
|
if (!file) {
|
|
302
199
|
return null;
|
|
@@ -309,7 +206,7 @@ function createCollector(emitter, baseDirectory = ".") {
|
|
|
309
206
|
};
|
|
310
207
|
} catch (error) {
|
|
311
208
|
emitter.emit("collector:parse-error", {
|
|
312
|
-
filePath:
|
|
209
|
+
filePath: path2.join(collection.directory, filePath),
|
|
313
210
|
error: new CollectError("Parse", String(error))
|
|
314
211
|
});
|
|
315
212
|
return null;
|
|
@@ -326,7 +223,7 @@ function createCollector(emitter, baseDirectory = ".") {
|
|
|
326
223
|
return void 0;
|
|
327
224
|
}
|
|
328
225
|
async function resolveCollection(collection) {
|
|
329
|
-
const collectionDirectory =
|
|
226
|
+
const collectionDirectory = path2.join(baseDirectory, collection.directory);
|
|
330
227
|
const include = Array.isArray(collection.include) ? collection.include : [collection.include];
|
|
331
228
|
const filePaths = await glob(include, {
|
|
332
229
|
cwd: collectionDirectory,
|
|
@@ -355,135 +252,24 @@ function createCollector(emitter, baseDirectory = ".") {
|
|
|
355
252
|
};
|
|
356
253
|
}
|
|
357
254
|
|
|
358
|
-
// src/writer.ts
|
|
359
|
-
import fs2 from "fs/promises";
|
|
360
|
-
import path4 from "path";
|
|
361
|
-
import pluralize2 from "pluralize";
|
|
362
|
-
|
|
363
|
-
// src/serializer.ts
|
|
364
|
-
import z2 from "zod";
|
|
365
|
-
import serializeJs from "serialize-javascript";
|
|
366
|
-
var literalSchema = z2.union([
|
|
367
|
-
// json
|
|
368
|
-
z2.string(),
|
|
369
|
-
z2.number(),
|
|
370
|
-
z2.boolean(),
|
|
371
|
-
z2.null(),
|
|
372
|
-
// serializable-javascript
|
|
373
|
-
z2.undefined(),
|
|
374
|
-
z2.date(),
|
|
375
|
-
z2.map(z2.unknown(), z2.unknown()),
|
|
376
|
-
z2.set(z2.unknown()),
|
|
377
|
-
z2.bigint()
|
|
378
|
-
]);
|
|
379
|
-
var schema = z2.lazy(
|
|
380
|
-
() => z2.union([literalSchema, z2.array(schema), z2.record(schema)])
|
|
381
|
-
);
|
|
382
|
-
var extension = "js";
|
|
383
|
-
var serializableSchema = z2.record(schema);
|
|
384
|
-
function serialize(value) {
|
|
385
|
-
const serializedValue = serializeJs(value, {
|
|
386
|
-
space: 2,
|
|
387
|
-
unsafe: true,
|
|
388
|
-
ignoreFunction: true
|
|
389
|
-
});
|
|
390
|
-
return `export default ${serializedValue};`;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// src/writer.ts
|
|
394
|
-
function createArrayConstName(name) {
|
|
395
|
-
let suffix = name.charAt(0).toUpperCase() + name.slice(1);
|
|
396
|
-
return "all" + pluralize2(suffix);
|
|
397
|
-
}
|
|
398
|
-
async function createDataFile(directory, collection) {
|
|
399
|
-
const dataPath = path4.join(
|
|
400
|
-
directory,
|
|
401
|
-
`${createArrayConstName(collection.name)}.${extension}`
|
|
402
|
-
);
|
|
403
|
-
await fs2.writeFile(
|
|
404
|
-
dataPath,
|
|
405
|
-
serialize(collection.documents.map((doc) => doc.document))
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
function createDataFiles(directory, collections) {
|
|
409
|
-
return Promise.all(
|
|
410
|
-
collections.map((collection) => createDataFile(directory, collection))
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
async function createJavaScriptFile(directory, configuration) {
|
|
414
|
-
const collections = configuration.collections.map(
|
|
415
|
-
({ name }) => createArrayConstName(name)
|
|
416
|
-
);
|
|
417
|
-
let content = `// generated by content-collections at ${/* @__PURE__ */ new Date()}
|
|
418
|
-
|
|
419
|
-
`;
|
|
420
|
-
for (const name of collections) {
|
|
421
|
-
content += `import ${name} from "./${name}.${extension}";
|
|
422
|
-
`;
|
|
423
|
-
}
|
|
424
|
-
content += "\n";
|
|
425
|
-
content += "export { " + collections.join(", ") + " };\n";
|
|
426
|
-
await fs2.writeFile(path4.join(directory, "index.js"), content, "utf-8");
|
|
427
|
-
}
|
|
428
|
-
function createImportPath(directory, target) {
|
|
429
|
-
let importPath = path4.posix.join(
|
|
430
|
-
...path4.relative(directory, target).split(path4.sep)
|
|
431
|
-
);
|
|
432
|
-
if (!importPath.startsWith(".")) {
|
|
433
|
-
importPath = "./" + importPath;
|
|
434
|
-
}
|
|
435
|
-
return importPath;
|
|
436
|
-
}
|
|
437
|
-
async function createTypeDefinitionFile(directory, configuration) {
|
|
438
|
-
if (!configuration.generateTypes) {
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
const importPath = createImportPath(directory, configuration.path);
|
|
442
|
-
let content = `import configuration from "${importPath}";
|
|
443
|
-
import { GetTypeByName } from "@content-collections/core";
|
|
444
|
-
`;
|
|
445
|
-
const collections = configuration.collections;
|
|
446
|
-
for (const collection of collections) {
|
|
447
|
-
content += `
|
|
448
|
-
`;
|
|
449
|
-
content += `export type ${collection.typeName} = GetTypeByName<typeof configuration, "${collection.name}">;
|
|
450
|
-
`;
|
|
451
|
-
content += `export declare const ${createArrayConstName(
|
|
452
|
-
collection.name
|
|
453
|
-
)}: Array<${collection.typeName}>;
|
|
454
|
-
`;
|
|
455
|
-
}
|
|
456
|
-
content += "\n";
|
|
457
|
-
content += "export {};\n";
|
|
458
|
-
await fs2.writeFile(path4.join(directory, "index.d.ts"), content, "utf-8");
|
|
459
|
-
}
|
|
460
|
-
async function createWriter(directory) {
|
|
461
|
-
await fs2.mkdir(directory, { recursive: true });
|
|
462
|
-
return {
|
|
463
|
-
createJavaScriptFile: (configuration) => createJavaScriptFile(directory, configuration),
|
|
464
|
-
createTypeDefinitionFile: (configuration) => createTypeDefinitionFile(directory, configuration),
|
|
465
|
-
createDataFiles: (collections) => createDataFiles(directory, collections)
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
|
|
469
255
|
// src/synchronizer.ts
|
|
256
|
+
import path3 from "node:path";
|
|
470
257
|
import picomatch from "picomatch";
|
|
471
|
-
import path5 from "path";
|
|
472
258
|
function createSynchronizer(readCollectionFile, collections, baseDirectory = ".") {
|
|
473
259
|
function findCollections(filePath) {
|
|
474
|
-
const resolvedFilePath =
|
|
260
|
+
const resolvedFilePath = path3.resolve(filePath);
|
|
475
261
|
return collections.filter((collection) => {
|
|
476
262
|
return resolvedFilePath.startsWith(
|
|
477
|
-
|
|
263
|
+
path3.resolve(baseDirectory, collection.directory)
|
|
478
264
|
);
|
|
479
265
|
});
|
|
480
266
|
}
|
|
481
267
|
function createRelativePath(collectionPath, filePath) {
|
|
482
|
-
const resolvedCollectionPath =
|
|
483
|
-
const resolvedFilePath =
|
|
268
|
+
const resolvedCollectionPath = path3.resolve(baseDirectory, collectionPath);
|
|
269
|
+
const resolvedFilePath = path3.resolve(filePath);
|
|
484
270
|
let relativePath = resolvedFilePath.slice(resolvedCollectionPath.length);
|
|
485
|
-
if (relativePath.startsWith(
|
|
486
|
-
relativePath = relativePath.slice(
|
|
271
|
+
if (relativePath.startsWith(path3.sep)) {
|
|
272
|
+
relativePath = relativePath.slice(path3.sep.length);
|
|
487
273
|
}
|
|
488
274
|
return relativePath;
|
|
489
275
|
}
|
|
@@ -547,110 +333,43 @@ function createSynchronizer(readCollectionFile, collections, baseDirectory = "."
|
|
|
547
333
|
};
|
|
548
334
|
}
|
|
549
335
|
|
|
550
|
-
// src/
|
|
551
|
-
import
|
|
552
|
-
import {
|
|
553
|
-
import
|
|
554
|
-
import {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
async function flush() {
|
|
585
|
-
await writeFile(mappingPath, JSON.stringify(mapping));
|
|
586
|
-
}
|
|
587
|
-
function cache(collection, file) {
|
|
588
|
-
const directory = join2(
|
|
589
|
-
cacheDirectory,
|
|
590
|
-
fileName(collection),
|
|
591
|
-
fileName(file)
|
|
592
|
-
);
|
|
593
|
-
let collectionMapping = mapping[collection];
|
|
594
|
-
if (!collectionMapping) {
|
|
595
|
-
collectionMapping = {};
|
|
596
|
-
mapping[collection] = collectionMapping;
|
|
597
|
-
}
|
|
598
|
-
let fileMapping = collectionMapping[file];
|
|
599
|
-
if (!fileMapping) {
|
|
600
|
-
fileMapping = [];
|
|
601
|
-
collectionMapping[file] = fileMapping;
|
|
602
|
-
}
|
|
603
|
-
let newFileMapping = [];
|
|
604
|
-
const cacheFn = async (input, fn) => {
|
|
605
|
-
const key = createKey(configChecksum, input);
|
|
606
|
-
newFileMapping.push(key);
|
|
607
|
-
const filePath = join2(directory, `${key}.cache`);
|
|
608
|
-
if (fileMapping?.includes(key) || newFileMapping.includes(key)) {
|
|
609
|
-
if (existsSync2(filePath)) {
|
|
610
|
-
try {
|
|
611
|
-
return JSON.parse(await readFile2(filePath, "utf-8"));
|
|
612
|
-
} catch (e) {
|
|
613
|
-
console.error(
|
|
614
|
-
"Failed to parse the cache file. We will recompute the value."
|
|
615
|
-
);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
const output = await fn(input);
|
|
620
|
-
if (!existsSync2(directory)) {
|
|
621
|
-
await mkdir(directory, { recursive: true });
|
|
622
|
-
}
|
|
623
|
-
await writeFile(filePath, JSON.stringify(output));
|
|
624
|
-
return output;
|
|
625
|
-
};
|
|
626
|
-
const tidyUp = async () => {
|
|
627
|
-
const filesToDelete = fileMapping?.filter((key) => !newFileMapping.includes(key)) || [];
|
|
628
|
-
for (const key of filesToDelete) {
|
|
629
|
-
const filePath = join2(directory, `${key}.cache`);
|
|
630
|
-
if (existsSync2(filePath)) {
|
|
631
|
-
await unlink(filePath);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
if (collectionMapping) {
|
|
635
|
-
collectionMapping[file] = newFileMapping;
|
|
636
|
-
}
|
|
637
|
-
};
|
|
638
|
-
return {
|
|
639
|
-
cacheFn,
|
|
640
|
-
tidyUp
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
return {
|
|
644
|
-
cache,
|
|
645
|
-
flush
|
|
646
|
-
};
|
|
336
|
+
// src/transformer.ts
|
|
337
|
+
import os from "node:os";
|
|
338
|
+
import { basename, dirname, extname } from "node:path";
|
|
339
|
+
import pLimit from "p-limit";
|
|
340
|
+
import { z as z2 } from "zod";
|
|
341
|
+
|
|
342
|
+
// src/serializer.ts
|
|
343
|
+
import serializeJs from "serialize-javascript";
|
|
344
|
+
import z from "zod";
|
|
345
|
+
var literalSchema = z.union([
|
|
346
|
+
// json
|
|
347
|
+
z.string(),
|
|
348
|
+
z.number(),
|
|
349
|
+
z.boolean(),
|
|
350
|
+
z.null(),
|
|
351
|
+
// serializable-javascript
|
|
352
|
+
z.undefined(),
|
|
353
|
+
z.date(),
|
|
354
|
+
z.map(z.unknown(), z.unknown()),
|
|
355
|
+
z.set(z.unknown()),
|
|
356
|
+
z.bigint()
|
|
357
|
+
]);
|
|
358
|
+
var schema = z.lazy(
|
|
359
|
+
() => z.union([literalSchema, z.array(schema), z.record(schema)])
|
|
360
|
+
);
|
|
361
|
+
var extension = "js";
|
|
362
|
+
var serializableSchema = z.record(schema);
|
|
363
|
+
function serialize(value) {
|
|
364
|
+
const serializedValue = serializeJs(value, {
|
|
365
|
+
space: 2,
|
|
366
|
+
unsafe: true,
|
|
367
|
+
ignoreFunction: true
|
|
368
|
+
});
|
|
369
|
+
return `export default ${serializedValue};`;
|
|
647
370
|
}
|
|
648
371
|
|
|
649
372
|
// src/transformer.ts
|
|
650
|
-
import { basename, dirname as dirname3, extname } from "path";
|
|
651
|
-
import { z as z3 } from "zod";
|
|
652
|
-
import os from "os";
|
|
653
|
-
import pLimit from "p-limit";
|
|
654
373
|
var TransformError = class extends Error {
|
|
655
374
|
type;
|
|
656
375
|
constructor(type, message) {
|
|
@@ -658,8 +377,8 @@ var TransformError = class extends Error {
|
|
|
658
377
|
this.type = type;
|
|
659
378
|
}
|
|
660
379
|
};
|
|
661
|
-
function createPath(
|
|
662
|
-
let p =
|
|
380
|
+
function createPath(path9, ext) {
|
|
381
|
+
let p = path9.slice(0, -ext.length);
|
|
663
382
|
if (p.endsWith("/index")) {
|
|
664
383
|
p = p.slice(0, -6);
|
|
665
384
|
}
|
|
@@ -669,15 +388,15 @@ function createTransformer(emitter, cacheManager) {
|
|
|
669
388
|
function createSchema(parserName, schema2) {
|
|
670
389
|
const parser = parsers[parserName];
|
|
671
390
|
if (!parser.hasContent) {
|
|
672
|
-
return
|
|
391
|
+
return z2.object(schema2);
|
|
673
392
|
}
|
|
674
|
-
return
|
|
675
|
-
content:
|
|
393
|
+
return z2.object({
|
|
394
|
+
content: z2.string(),
|
|
676
395
|
...schema2
|
|
677
396
|
});
|
|
678
397
|
}
|
|
679
398
|
async function parseFile(collection, file) {
|
|
680
|
-
const { data, path:
|
|
399
|
+
const { data, path: path9 } = file;
|
|
681
400
|
const schema2 = createSchema(collection.parser, collection.schema);
|
|
682
401
|
let parsedData = await schema2.safeParseAsync(data);
|
|
683
402
|
if (!parsedData.success) {
|
|
@@ -688,7 +407,7 @@ function createTransformer(emitter, cacheManager) {
|
|
|
688
407
|
});
|
|
689
408
|
return null;
|
|
690
409
|
}
|
|
691
|
-
const ext = extname(
|
|
410
|
+
const ext = extname(path9);
|
|
692
411
|
let extension2 = ext;
|
|
693
412
|
if (extension2.startsWith(".")) {
|
|
694
413
|
extension2 = extension2.slice(1);
|
|
@@ -696,11 +415,11 @@ function createTransformer(emitter, cacheManager) {
|
|
|
696
415
|
const document = {
|
|
697
416
|
...parsedData.data,
|
|
698
417
|
_meta: {
|
|
699
|
-
filePath:
|
|
700
|
-
fileName: basename(
|
|
701
|
-
directory:
|
|
418
|
+
filePath: path9,
|
|
419
|
+
fileName: basename(path9),
|
|
420
|
+
directory: dirname(path9),
|
|
702
421
|
extension: extension2,
|
|
703
|
-
path: createPath(
|
|
422
|
+
path: createPath(path9, ext)
|
|
704
423
|
}
|
|
705
424
|
};
|
|
706
425
|
return {
|
|
@@ -740,9 +459,9 @@ function createTransformer(emitter, cacheManager) {
|
|
|
740
459
|
}
|
|
741
460
|
async function transformDocument(collections, collection, transform, doc) {
|
|
742
461
|
const cache = cacheManager.cache(collection.name, doc.document._meta.path);
|
|
743
|
-
const
|
|
462
|
+
const context2 = createContext(collections, collection, cache);
|
|
744
463
|
try {
|
|
745
|
-
const document = await transform(doc.document,
|
|
464
|
+
const document = await transform(doc.document, context2);
|
|
746
465
|
await cache.tidyUp();
|
|
747
466
|
return {
|
|
748
467
|
...doc,
|
|
@@ -791,16 +510,95 @@ function createTransformer(emitter, cacheManager) {
|
|
|
791
510
|
}
|
|
792
511
|
return docs;
|
|
793
512
|
}
|
|
794
|
-
return async (untransformedCollections) => {
|
|
795
|
-
const promises = untransformedCollections.map(
|
|
796
|
-
(collection) => parseCollection(collection)
|
|
797
|
-
);
|
|
798
|
-
const collections = await Promise.all(promises);
|
|
799
|
-
for (const collection of collections) {
|
|
800
|
-
const documents = await transformCollection(collections, collection);
|
|
801
|
-
collection.documents = await validateDocuments(collection, documents);
|
|
802
|
-
}
|
|
803
|
-
return collections;
|
|
513
|
+
return async (untransformedCollections) => {
|
|
514
|
+
const promises = untransformedCollections.map(
|
|
515
|
+
(collection) => parseCollection(collection)
|
|
516
|
+
);
|
|
517
|
+
const collections = await Promise.all(promises);
|
|
518
|
+
for (const collection of collections) {
|
|
519
|
+
const documents = await transformCollection(collections, collection);
|
|
520
|
+
collection.documents = await validateDocuments(collection, documents);
|
|
521
|
+
}
|
|
522
|
+
return collections;
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/writer.ts
|
|
527
|
+
import fs from "node:fs/promises";
|
|
528
|
+
import path4 from "node:path";
|
|
529
|
+
import pluralize2 from "pluralize";
|
|
530
|
+
function createArrayConstName(name) {
|
|
531
|
+
let suffix = name.charAt(0).toUpperCase() + name.slice(1);
|
|
532
|
+
return "all" + pluralize2(suffix);
|
|
533
|
+
}
|
|
534
|
+
async function createDataFile(directory, collection) {
|
|
535
|
+
const dataPath = path4.join(
|
|
536
|
+
directory,
|
|
537
|
+
`${createArrayConstName(collection.name)}.${extension}`
|
|
538
|
+
);
|
|
539
|
+
await fs.writeFile(
|
|
540
|
+
dataPath,
|
|
541
|
+
serialize(collection.documents.map((doc) => doc.document))
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
function createDataFiles(directory, collections) {
|
|
545
|
+
return Promise.all(
|
|
546
|
+
collections.map((collection) => createDataFile(directory, collection))
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
async function createJavaScriptFile(directory, configuration) {
|
|
550
|
+
const collections = configuration.collections.map(
|
|
551
|
+
({ name }) => createArrayConstName(name)
|
|
552
|
+
);
|
|
553
|
+
let content = `// generated by content-collections at ${/* @__PURE__ */ new Date()}
|
|
554
|
+
|
|
555
|
+
`;
|
|
556
|
+
for (const name of collections) {
|
|
557
|
+
content += `import ${name} from "./${name}.${extension}";
|
|
558
|
+
`;
|
|
559
|
+
}
|
|
560
|
+
content += "\n";
|
|
561
|
+
content += "export { " + collections.join(", ") + " };\n";
|
|
562
|
+
await fs.writeFile(path4.join(directory, "index.js"), content, "utf-8");
|
|
563
|
+
}
|
|
564
|
+
function createImportPath(directory, target) {
|
|
565
|
+
let importPath = path4.posix.join(
|
|
566
|
+
...path4.relative(directory, target).split(path4.sep)
|
|
567
|
+
);
|
|
568
|
+
if (!importPath.startsWith(".")) {
|
|
569
|
+
importPath = "./" + importPath;
|
|
570
|
+
}
|
|
571
|
+
return importPath;
|
|
572
|
+
}
|
|
573
|
+
async function createTypeDefinitionFile(directory, configuration) {
|
|
574
|
+
if (!configuration.generateTypes) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const importPath = createImportPath(directory, configuration.path);
|
|
578
|
+
let content = `import configuration from "${importPath}";
|
|
579
|
+
import { GetTypeByName } from "@content-collections/core";
|
|
580
|
+
`;
|
|
581
|
+
const collections = configuration.collections;
|
|
582
|
+
for (const collection of collections) {
|
|
583
|
+
content += `
|
|
584
|
+
`;
|
|
585
|
+
content += `export type ${collection.typeName} = GetTypeByName<typeof configuration, "${collection.name}">;
|
|
586
|
+
`;
|
|
587
|
+
content += `export declare const ${createArrayConstName(
|
|
588
|
+
collection.name
|
|
589
|
+
)}: Array<${collection.typeName}>;
|
|
590
|
+
`;
|
|
591
|
+
}
|
|
592
|
+
content += "\n";
|
|
593
|
+
content += "export {};\n";
|
|
594
|
+
await fs.writeFile(path4.join(directory, "index.d.ts"), content, "utf-8");
|
|
595
|
+
}
|
|
596
|
+
async function createWriter(directory) {
|
|
597
|
+
await fs.mkdir(directory, { recursive: true });
|
|
598
|
+
return {
|
|
599
|
+
createJavaScriptFile: (configuration) => createJavaScriptFile(directory, configuration),
|
|
600
|
+
createTypeDefinitionFile: (configuration) => createTypeDefinitionFile(directory, configuration),
|
|
601
|
+
createDataFiles: (collections) => createDataFiles(directory, collections)
|
|
804
602
|
};
|
|
805
603
|
}
|
|
806
604
|
|
|
@@ -833,7 +631,7 @@ async function createBuildContext({
|
|
|
833
631
|
configuration
|
|
834
632
|
};
|
|
835
633
|
}
|
|
836
|
-
async function
|
|
634
|
+
async function build({
|
|
837
635
|
emitter,
|
|
838
636
|
transform,
|
|
839
637
|
resolved,
|
|
@@ -872,12 +670,386 @@ async function build2({
|
|
|
872
670
|
});
|
|
873
671
|
}
|
|
874
672
|
|
|
673
|
+
// src/configurationReader.ts
|
|
674
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
675
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
676
|
+
import fs3 from "node:fs/promises";
|
|
677
|
+
import path6 from "node:path";
|
|
678
|
+
|
|
679
|
+
// ../../node_modules/.pnpm/bundle-require@5.0.0_esbuild@0.21.4/node_modules/bundle-require/dist/index.js
|
|
680
|
+
import {
|
|
681
|
+
build as build2,
|
|
682
|
+
context
|
|
683
|
+
} from "esbuild";
|
|
684
|
+
|
|
685
|
+
// ../../node_modules/.pnpm/load-tsconfig@0.2.5/node_modules/load-tsconfig/dist/index.js
|
|
686
|
+
import path5 from "path";
|
|
687
|
+
import fs2 from "fs";
|
|
688
|
+
import { createRequire } from "module";
|
|
689
|
+
var singleComment = Symbol("singleComment");
|
|
690
|
+
var multiComment = Symbol("multiComment");
|
|
691
|
+
var stripWithoutWhitespace = () => "";
|
|
692
|
+
var stripWithWhitespace = (string, start, end) => string.slice(start, end).replace(/\S/g, " ");
|
|
693
|
+
var isEscaped = (jsonString, quotePosition) => {
|
|
694
|
+
let index = quotePosition - 1;
|
|
695
|
+
let backslashCount = 0;
|
|
696
|
+
while (jsonString[index] === "\\") {
|
|
697
|
+
index -= 1;
|
|
698
|
+
backslashCount += 1;
|
|
699
|
+
}
|
|
700
|
+
return Boolean(backslashCount % 2);
|
|
701
|
+
};
|
|
702
|
+
function stripJsonComments(jsonString, { whitespace = true, trailingCommas = false } = {}) {
|
|
703
|
+
if (typeof jsonString !== "string") {
|
|
704
|
+
throw new TypeError(`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``);
|
|
705
|
+
}
|
|
706
|
+
const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;
|
|
707
|
+
let isInsideString = false;
|
|
708
|
+
let isInsideComment = false;
|
|
709
|
+
let offset = 0;
|
|
710
|
+
let buffer = "";
|
|
711
|
+
let result = "";
|
|
712
|
+
let commaIndex = -1;
|
|
713
|
+
for (let index = 0; index < jsonString.length; index++) {
|
|
714
|
+
const currentCharacter = jsonString[index];
|
|
715
|
+
const nextCharacter = jsonString[index + 1];
|
|
716
|
+
if (!isInsideComment && currentCharacter === '"') {
|
|
717
|
+
const escaped = isEscaped(jsonString, index);
|
|
718
|
+
if (!escaped) {
|
|
719
|
+
isInsideString = !isInsideString;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (isInsideString) {
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (!isInsideComment && currentCharacter + nextCharacter === "//") {
|
|
726
|
+
buffer += jsonString.slice(offset, index);
|
|
727
|
+
offset = index;
|
|
728
|
+
isInsideComment = singleComment;
|
|
729
|
+
index++;
|
|
730
|
+
} else if (isInsideComment === singleComment && currentCharacter + nextCharacter === "\r\n") {
|
|
731
|
+
index++;
|
|
732
|
+
isInsideComment = false;
|
|
733
|
+
buffer += strip(jsonString, offset, index);
|
|
734
|
+
offset = index;
|
|
735
|
+
continue;
|
|
736
|
+
} else if (isInsideComment === singleComment && currentCharacter === "\n") {
|
|
737
|
+
isInsideComment = false;
|
|
738
|
+
buffer += strip(jsonString, offset, index);
|
|
739
|
+
offset = index;
|
|
740
|
+
} else if (!isInsideComment && currentCharacter + nextCharacter === "/*") {
|
|
741
|
+
buffer += jsonString.slice(offset, index);
|
|
742
|
+
offset = index;
|
|
743
|
+
isInsideComment = multiComment;
|
|
744
|
+
index++;
|
|
745
|
+
continue;
|
|
746
|
+
} else if (isInsideComment === multiComment && currentCharacter + nextCharacter === "*/") {
|
|
747
|
+
index++;
|
|
748
|
+
isInsideComment = false;
|
|
749
|
+
buffer += strip(jsonString, offset, index + 1);
|
|
750
|
+
offset = index + 1;
|
|
751
|
+
continue;
|
|
752
|
+
} else if (trailingCommas && !isInsideComment) {
|
|
753
|
+
if (commaIndex !== -1) {
|
|
754
|
+
if (currentCharacter === "}" || currentCharacter === "]") {
|
|
755
|
+
buffer += jsonString.slice(offset, index);
|
|
756
|
+
result += strip(buffer, 0, 1) + buffer.slice(1);
|
|
757
|
+
buffer = "";
|
|
758
|
+
offset = index;
|
|
759
|
+
commaIndex = -1;
|
|
760
|
+
} else if (currentCharacter !== " " && currentCharacter !== " " && currentCharacter !== "\r" && currentCharacter !== "\n") {
|
|
761
|
+
buffer += jsonString.slice(offset, index);
|
|
762
|
+
offset = index;
|
|
763
|
+
commaIndex = -1;
|
|
764
|
+
}
|
|
765
|
+
} else if (currentCharacter === ",") {
|
|
766
|
+
result += buffer + jsonString.slice(offset, index);
|
|
767
|
+
buffer = "";
|
|
768
|
+
offset = index;
|
|
769
|
+
commaIndex = index;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return result + buffer + (isInsideComment ? strip(jsonString.slice(offset)) : jsonString.slice(offset));
|
|
774
|
+
}
|
|
775
|
+
function jsoncParse(data) {
|
|
776
|
+
try {
|
|
777
|
+
return new Function("return " + stripJsonComments(data).trim())();
|
|
778
|
+
} catch (_) {
|
|
779
|
+
return {};
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
var req = true ? createRequire(import.meta.url) : __require;
|
|
783
|
+
var findUp = (name, startDir, stopDir = path5.parse(startDir).root) => {
|
|
784
|
+
let dir = startDir;
|
|
785
|
+
while (dir !== stopDir) {
|
|
786
|
+
const file = path5.join(dir, name);
|
|
787
|
+
if (fs2.existsSync(file))
|
|
788
|
+
return file;
|
|
789
|
+
if (!file.endsWith(".json")) {
|
|
790
|
+
const fileWithExt = file + ".json";
|
|
791
|
+
if (fs2.existsSync(fileWithExt))
|
|
792
|
+
return fileWithExt;
|
|
793
|
+
}
|
|
794
|
+
dir = path5.dirname(dir);
|
|
795
|
+
}
|
|
796
|
+
return null;
|
|
797
|
+
};
|
|
798
|
+
var resolveTsConfigFromFile = (cwd, filename) => {
|
|
799
|
+
if (path5.isAbsolute(filename))
|
|
800
|
+
return fs2.existsSync(filename) ? filename : null;
|
|
801
|
+
return findUp(filename, cwd);
|
|
802
|
+
};
|
|
803
|
+
var resolveTsConfigFromExtends = (cwd, name) => {
|
|
804
|
+
if (path5.isAbsolute(name))
|
|
805
|
+
return fs2.existsSync(name) ? name : null;
|
|
806
|
+
if (name.startsWith("."))
|
|
807
|
+
return findUp(name, cwd);
|
|
808
|
+
const id = req.resolve(name, { paths: [cwd] });
|
|
809
|
+
return id;
|
|
810
|
+
};
|
|
811
|
+
var loadTsConfigInternal = (dir = process.cwd(), name = "tsconfig.json", isExtends = false) => {
|
|
812
|
+
var _a, _b;
|
|
813
|
+
dir = path5.resolve(dir);
|
|
814
|
+
const id = isExtends ? resolveTsConfigFromExtends(dir, name) : resolveTsConfigFromFile(dir, name);
|
|
815
|
+
if (!id)
|
|
816
|
+
return null;
|
|
817
|
+
const data = jsoncParse(fs2.readFileSync(id, "utf-8"));
|
|
818
|
+
const configDir = path5.dirname(id);
|
|
819
|
+
if ((_a = data.compilerOptions) == null ? void 0 : _a.baseUrl) {
|
|
820
|
+
data.compilerOptions.baseUrl = path5.join(
|
|
821
|
+
configDir,
|
|
822
|
+
data.compilerOptions.baseUrl
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
let extendsFiles = [];
|
|
826
|
+
if (data.extends) {
|
|
827
|
+
const extendsList = Array.isArray(data.extends) ? data.extends : [data.extends];
|
|
828
|
+
const extendsData = {};
|
|
829
|
+
for (const name2 of extendsList) {
|
|
830
|
+
const parentConfig = loadTsConfigInternal(configDir, name2, true);
|
|
831
|
+
if (parentConfig) {
|
|
832
|
+
Object.assign(extendsData, {
|
|
833
|
+
...parentConfig == null ? void 0 : parentConfig.data,
|
|
834
|
+
compilerOptions: {
|
|
835
|
+
...extendsData.compilerOptions,
|
|
836
|
+
...(_b = parentConfig == null ? void 0 : parentConfig.data) == null ? void 0 : _b.compilerOptions
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
extendsFiles.push(...parentConfig.files);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
Object.assign(data, {
|
|
843
|
+
...extendsData,
|
|
844
|
+
...data,
|
|
845
|
+
compilerOptions: {
|
|
846
|
+
...extendsData.compilerOptions,
|
|
847
|
+
...data.compilerOptions
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
delete data.extends;
|
|
852
|
+
return { path: id, data, files: [...extendsFiles, id] };
|
|
853
|
+
};
|
|
854
|
+
var loadTsConfig = (dir, name) => loadTsConfigInternal(dir, name);
|
|
855
|
+
|
|
856
|
+
// ../../node_modules/.pnpm/bundle-require@5.0.0_esbuild@0.21.4/node_modules/bundle-require/dist/index.js
|
|
857
|
+
var tsconfigPathsToRegExp = (paths) => {
|
|
858
|
+
return Object.keys(paths || {}).map((key) => {
|
|
859
|
+
return new RegExp(`^${key.replace(/\*/, ".*")}$`);
|
|
860
|
+
});
|
|
861
|
+
};
|
|
862
|
+
var match = (id, patterns) => {
|
|
863
|
+
if (!patterns)
|
|
864
|
+
return false;
|
|
865
|
+
return patterns.some((p) => {
|
|
866
|
+
if (p instanceof RegExp) {
|
|
867
|
+
return p.test(id);
|
|
868
|
+
}
|
|
869
|
+
return id === p || id.startsWith(p + "/");
|
|
870
|
+
});
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
// src/esbuild.ts
|
|
874
|
+
import { build as build3 } from "esbuild";
|
|
875
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
876
|
+
function tsconfigResolvePaths(configPath) {
|
|
877
|
+
let tsconfig = loadTsConfig(dirname2(configPath));
|
|
878
|
+
if (!tsconfig) {
|
|
879
|
+
tsconfig = loadTsConfig();
|
|
880
|
+
}
|
|
881
|
+
return tsconfig?.data?.compilerOptions?.paths || {};
|
|
882
|
+
}
|
|
883
|
+
function createExternalsPlugin(configPath) {
|
|
884
|
+
const resolvedPaths = tsconfigResolvePaths(configPath);
|
|
885
|
+
const resolvePatterns = tsconfigPathsToRegExp(resolvedPaths);
|
|
886
|
+
return {
|
|
887
|
+
name: "external-packages",
|
|
888
|
+
setup: (build4) => {
|
|
889
|
+
const filter = /^[^.\/]|^\.[^.\/]|^\.\.[^\/]/;
|
|
890
|
+
build4.onResolve({ filter }, ({ path: path9, kind }) => {
|
|
891
|
+
if (match(path9, resolvePatterns)) {
|
|
892
|
+
if (kind === "dynamic-import") {
|
|
893
|
+
return { path: path9, external: true };
|
|
894
|
+
}
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
return { path: path9, external: true };
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
var importPathPlugin = {
|
|
903
|
+
name: "import-path",
|
|
904
|
+
setup(build4) {
|
|
905
|
+
build4.onResolve({ filter: /^\@content-collections\/core$/ }, () => {
|
|
906
|
+
return { path: join2(__dirname, "index.ts"), external: true };
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
async function compile(configurationPath, outfile) {
|
|
911
|
+
const plugins = [createExternalsPlugin(configurationPath)];
|
|
912
|
+
if (process.env.NODE_ENV === "test") {
|
|
913
|
+
plugins.push(importPathPlugin);
|
|
914
|
+
}
|
|
915
|
+
const result = await build3({
|
|
916
|
+
entryPoints: [configurationPath],
|
|
917
|
+
packages: "external",
|
|
918
|
+
bundle: true,
|
|
919
|
+
platform: "node",
|
|
920
|
+
format: "esm",
|
|
921
|
+
plugins,
|
|
922
|
+
outfile,
|
|
923
|
+
metafile: true
|
|
924
|
+
});
|
|
925
|
+
return Object.keys(result.metafile.inputs);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/configurationReader.ts
|
|
929
|
+
var ConfigurationError = class extends Error {
|
|
930
|
+
type;
|
|
931
|
+
constructor(type, message) {
|
|
932
|
+
super(message);
|
|
933
|
+
this.type = type;
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
var defaultConfigName = "content-collection-config.mjs";
|
|
937
|
+
function resolveCacheDir(config, options) {
|
|
938
|
+
if (options.cacheDir) {
|
|
939
|
+
return options.cacheDir;
|
|
940
|
+
}
|
|
941
|
+
return path6.join(path6.dirname(config), ".content-collections", "cache");
|
|
942
|
+
}
|
|
943
|
+
function createConfigurationReader() {
|
|
944
|
+
return async (configurationPath, options = {
|
|
945
|
+
configName: defaultConfigName
|
|
946
|
+
}) => {
|
|
947
|
+
if (!existsSync2(configurationPath)) {
|
|
948
|
+
throw new ConfigurationError(
|
|
949
|
+
"Read",
|
|
950
|
+
`configuration file ${configurationPath} does not exist`
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
const cacheDir = resolveCacheDir(configurationPath, options);
|
|
954
|
+
await fs3.mkdir(cacheDir, { recursive: true });
|
|
955
|
+
const outfile = path6.join(cacheDir, options.configName);
|
|
956
|
+
try {
|
|
957
|
+
const configurationPaths = await compile(configurationPath, outfile);
|
|
958
|
+
const module = await import(`file://${path6.resolve(outfile)}?x=${Date.now()}`);
|
|
959
|
+
const hash = createHash2("sha256");
|
|
960
|
+
hash.update(await fs3.readFile(outfile, "utf-8"));
|
|
961
|
+
const checksum = hash.digest("hex");
|
|
962
|
+
return {
|
|
963
|
+
...module.default,
|
|
964
|
+
path: configurationPath,
|
|
965
|
+
inputPaths: configurationPaths.map((p) => path6.resolve(p)),
|
|
966
|
+
generateTypes: true,
|
|
967
|
+
checksum
|
|
968
|
+
};
|
|
969
|
+
} catch (error) {
|
|
970
|
+
throw new ConfigurationError(
|
|
971
|
+
"Compile",
|
|
972
|
+
`configuration file ${configurationPath} is invalid: ${error}`
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/events.ts
|
|
979
|
+
import { EventEmitter } from "node:events";
|
|
980
|
+
function isEventWithError(event) {
|
|
981
|
+
return typeof event === "object" && event !== null && "error" in event;
|
|
982
|
+
}
|
|
983
|
+
function createEmitter() {
|
|
984
|
+
const emitter = new EventEmitter();
|
|
985
|
+
function on(key, listener) {
|
|
986
|
+
emitter.on(key, listener);
|
|
987
|
+
}
|
|
988
|
+
function emit(key, event) {
|
|
989
|
+
emitter.emit(key, event);
|
|
990
|
+
if (isEventWithError(event)) {
|
|
991
|
+
emitter.emit("_error", {
|
|
992
|
+
...event,
|
|
993
|
+
_event: key
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
emitter.emit("_all", {
|
|
997
|
+
...event,
|
|
998
|
+
_event: key
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
on,
|
|
1003
|
+
emit
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/watcher.ts
|
|
1008
|
+
import * as watcher from "@parcel/watcher";
|
|
1009
|
+
import path7, { dirname as dirname3, resolve } from "node:path";
|
|
1010
|
+
async function createWatcher(emitter, baseDirectory, configuration, sync) {
|
|
1011
|
+
const onChange = async (error, events) => {
|
|
1012
|
+
if (error) {
|
|
1013
|
+
emitter.emit("watcher:subscribe-error", {
|
|
1014
|
+
paths,
|
|
1015
|
+
error
|
|
1016
|
+
});
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
for (const event of events) {
|
|
1020
|
+
await sync(event.type, event.path);
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
const paths = removeChildPaths([
|
|
1024
|
+
...configuration.collections.map((collection) => path7.join(baseDirectory, collection.directory)).map((p) => resolve(p)),
|
|
1025
|
+
...configuration.inputPaths.map((p) => dirname3(p))
|
|
1026
|
+
]);
|
|
1027
|
+
const subscriptions = (await Promise.all(paths.map((path9) => watcher.subscribe(path9, onChange)))).filter(isDefined);
|
|
1028
|
+
emitter.emit("watcher:subscribed", {
|
|
1029
|
+
paths
|
|
1030
|
+
});
|
|
1031
|
+
return {
|
|
1032
|
+
unsubscribe: async () => {
|
|
1033
|
+
if (!subscriptions || subscriptions.length === 0) {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
await Promise.all(
|
|
1037
|
+
subscriptions.map((subscription) => subscription.unsubscribe())
|
|
1038
|
+
);
|
|
1039
|
+
emitter.emit("watcher:unsubscribed", {
|
|
1040
|
+
paths
|
|
1041
|
+
});
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
|
|
875
1047
|
// src/builder.ts
|
|
876
1048
|
function resolveOutputDir(baseDirectory, options) {
|
|
877
1049
|
if (options.outputDir) {
|
|
878
1050
|
return options.outputDir;
|
|
879
1051
|
}
|
|
880
|
-
return
|
|
1052
|
+
return path8.join(baseDirectory, ".content-collections", "generated");
|
|
881
1053
|
}
|
|
882
1054
|
var ConfigurationReloadError = class extends Error {
|
|
883
1055
|
constructor(message) {
|
|
@@ -888,7 +1060,7 @@ async function createBuilder(configurationPath, options = {
|
|
|
888
1060
|
configName: defaultConfigName
|
|
889
1061
|
}, emitter = createEmitter()) {
|
|
890
1062
|
const readConfiguration = createConfigurationReader();
|
|
891
|
-
const baseDirectory =
|
|
1063
|
+
const baseDirectory = path8.dirname(configurationPath);
|
|
892
1064
|
const outputDirectory = resolveOutputDir(baseDirectory, options);
|
|
893
1065
|
emitter.emit("builder:created", {
|
|
894
1066
|
createdAt: Date.now(),
|
|
@@ -897,7 +1069,7 @@ async function createBuilder(configurationPath, options = {
|
|
|
897
1069
|
});
|
|
898
1070
|
let configuration = await readConfiguration(configurationPath, options);
|
|
899
1071
|
let watcher2 = null;
|
|
900
|
-
let
|
|
1072
|
+
let context2 = await createBuildContext({
|
|
901
1073
|
emitter,
|
|
902
1074
|
baseDirectory,
|
|
903
1075
|
outputDirectory,
|
|
@@ -910,7 +1082,7 @@ async function createBuilder(configurationPath, options = {
|
|
|
910
1082
|
filePath,
|
|
911
1083
|
modification
|
|
912
1084
|
});
|
|
913
|
-
await
|
|
1085
|
+
await build(context2);
|
|
914
1086
|
return true;
|
|
915
1087
|
}
|
|
916
1088
|
} else {
|
|
@@ -919,7 +1091,7 @@ async function createBuilder(configurationPath, options = {
|
|
|
919
1091
|
filePath,
|
|
920
1092
|
modification
|
|
921
1093
|
});
|
|
922
|
-
await
|
|
1094
|
+
await build(context2);
|
|
923
1095
|
return true;
|
|
924
1096
|
}
|
|
925
1097
|
}
|
|
@@ -940,7 +1112,7 @@ async function createBuilder(configurationPath, options = {
|
|
|
940
1112
|
if (watcher2) {
|
|
941
1113
|
await watcher2.unsubscribe();
|
|
942
1114
|
}
|
|
943
|
-
|
|
1115
|
+
context2 = await createBuildContext({
|
|
944
1116
|
emitter,
|
|
945
1117
|
baseDirectory,
|
|
946
1118
|
outputDirectory,
|
|
@@ -957,7 +1129,7 @@ async function createBuilder(configurationPath, options = {
|
|
|
957
1129
|
return true;
|
|
958
1130
|
}
|
|
959
1131
|
async function onFileChange(modification, filePath) {
|
|
960
|
-
const { synchronizer } =
|
|
1132
|
+
const { synchronizer } = context2;
|
|
961
1133
|
if (modification === "delete") {
|
|
962
1134
|
return synchronizer.deleted(filePath);
|
|
963
1135
|
} else {
|
|
@@ -975,12 +1147,35 @@ async function createBuilder(configurationPath, options = {
|
|
|
975
1147
|
};
|
|
976
1148
|
}
|
|
977
1149
|
return {
|
|
978
|
-
build: () =>
|
|
1150
|
+
build: () => build(context2),
|
|
979
1151
|
sync,
|
|
980
1152
|
watch,
|
|
981
1153
|
on: emitter.on
|
|
982
1154
|
};
|
|
983
1155
|
}
|
|
1156
|
+
|
|
1157
|
+
// src/config.ts
|
|
1158
|
+
import { z as z3 } from "zod";
|
|
1159
|
+
var InvalidReturnTypeSymbol = Symbol(`InvalidReturnType`);
|
|
1160
|
+
function defineCollection(collection) {
|
|
1161
|
+
let typeName = collection.typeName;
|
|
1162
|
+
if (!typeName) {
|
|
1163
|
+
typeName = generateTypeName(collection.name);
|
|
1164
|
+
}
|
|
1165
|
+
let parser = collection.parser;
|
|
1166
|
+
if (!parser) {
|
|
1167
|
+
parser = "frontmatter";
|
|
1168
|
+
}
|
|
1169
|
+
return {
|
|
1170
|
+
...collection,
|
|
1171
|
+
typeName,
|
|
1172
|
+
parser,
|
|
1173
|
+
schema: collection.schema(z3)
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
function defineConfig(config) {
|
|
1177
|
+
return config;
|
|
1178
|
+
}
|
|
984
1179
|
export {
|
|
985
1180
|
CollectError,
|
|
986
1181
|
ConfigurationError,
|