@content-collections/core 0.6.4 → 0.7.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 +1 -1
- package/dist/index.d.ts +71 -14
- package/dist/index.js +847 -489
- package/package.json +11 -9
package/dist/index.js
CHANGED
|
@@ -1,135 +1,109 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import
|
|
1
|
+
// src/builder.ts
|
|
2
|
+
import path8 from "node:path";
|
|
3
3
|
|
|
4
|
-
// src/
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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);
|
|
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");
|
|
16
11
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
let typeName = collection.typeName;
|
|
22
|
-
if (!typeName) {
|
|
23
|
-
typeName = generateTypeName(collection.name);
|
|
24
|
-
}
|
|
25
|
-
let parser = collection.parser;
|
|
26
|
-
if (!parser) {
|
|
27
|
-
parser = "frontmatter";
|
|
12
|
+
async function createCacheDirectory(directory) {
|
|
13
|
+
const cacheDirectory = path.join(directory, ".content-collections", "cache");
|
|
14
|
+
if (!existsSync(cacheDirectory)) {
|
|
15
|
+
await mkdir(cacheDirectory, { recursive: true });
|
|
28
16
|
}
|
|
29
|
-
return
|
|
30
|
-
...collection,
|
|
31
|
-
typeName,
|
|
32
|
-
parser,
|
|
33
|
-
schema: collection.schema(z)
|
|
34
|
-
};
|
|
17
|
+
return cacheDirectory;
|
|
35
18
|
}
|
|
36
|
-
function
|
|
37
|
-
return
|
|
19
|
+
function fileName(input) {
|
|
20
|
+
return input.replace(/[^a-z0-9]/gi, "_").toLowerCase();
|
|
38
21
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
constructor(type, message) {
|
|
49
|
-
super(message);
|
|
50
|
-
this.type = type;
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
var importPathPlugin = {
|
|
54
|
-
name: "import-path",
|
|
55
|
-
setup(build2) {
|
|
56
|
-
build2.onResolve({ filter: /^\@content-collections\/core$/ }, () => {
|
|
57
|
-
return { path: path.join(__dirname, "index.ts"), external: true };
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
var defaultConfigName = "content-collection-config.mjs";
|
|
62
|
-
function resolveCacheDir(config, options) {
|
|
63
|
-
if (options.cacheDir) {
|
|
64
|
-
return options.cacheDir;
|
|
22
|
+
async function readMapping(mappingPath) {
|
|
23
|
+
if (existsSync(mappingPath)) {
|
|
24
|
+
try {
|
|
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."
|
|
29
|
+
);
|
|
30
|
+
}
|
|
65
31
|
}
|
|
66
|
-
return
|
|
32
|
+
return {};
|
|
67
33
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return { path: path7, external };
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
async function compile(configurationPath, outfile) {
|
|
79
|
-
const plugins = [
|
|
80
|
-
externalPackagesPlugin(configurationPath)
|
|
81
|
-
];
|
|
82
|
-
if (process.env.NODE_ENV === "test") {
|
|
83
|
-
plugins.push(importPathPlugin);
|
|
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));
|
|
84
40
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
function createConfigurationReader() {
|
|
96
|
-
return async (configurationPath, options = {
|
|
97
|
-
configName: defaultConfigName
|
|
98
|
-
}) => {
|
|
99
|
-
if (!existsSync(configurationPath)) {
|
|
100
|
-
throw new ConfigurationError(
|
|
101
|
-
"Read",
|
|
102
|
-
`configuration file ${configurationPath} does not exist`
|
|
103
|
-
);
|
|
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;
|
|
104
51
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
await compile(configurationPath, outfile);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
throw new ConfigurationError(
|
|
112
|
-
"Compile",
|
|
113
|
-
`configuration file ${configurationPath} is invalid: ${error}`
|
|
114
|
-
);
|
|
52
|
+
let fileMapping = collectionMapping[file];
|
|
53
|
+
if (!fileMapping) {
|
|
54
|
+
fileMapping = [];
|
|
55
|
+
collectionMapping[file] = fileMapping;
|
|
115
56
|
}
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
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
|
+
}
|
|
72
|
+
}
|
|
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
|
+
};
|
|
120
92
|
return {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
generateTypes: true,
|
|
124
|
-
checksum
|
|
93
|
+
cacheFn,
|
|
94
|
+
tidyUp
|
|
125
95
|
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
cache,
|
|
99
|
+
flush
|
|
126
100
|
};
|
|
127
101
|
}
|
|
128
102
|
|
|
129
103
|
// src/collector.ts
|
|
130
|
-
import
|
|
131
|
-
import { readFile } from "fs/promises";
|
|
104
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
132
105
|
import path2 from "path";
|
|
106
|
+
import { glob } from "tinyglobby";
|
|
133
107
|
|
|
134
108
|
// src/parser.ts
|
|
135
109
|
import matter from "gray-matter";
|
|
@@ -166,6 +140,34 @@ var parsers = {
|
|
|
166
140
|
}
|
|
167
141
|
};
|
|
168
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
|
+
}
|
|
170
|
+
|
|
169
171
|
// src/collector.ts
|
|
170
172
|
var CollectError = class extends Error {
|
|
171
173
|
type;
|
|
@@ -177,7 +179,7 @@ var CollectError = class extends Error {
|
|
|
177
179
|
function createCollector(emitter, baseDirectory = ".") {
|
|
178
180
|
async function read(filePath) {
|
|
179
181
|
try {
|
|
180
|
-
return await
|
|
182
|
+
return await readFile2(filePath, "utf-8");
|
|
181
183
|
} catch (error) {
|
|
182
184
|
emitter.emit("collector:read-error", {
|
|
183
185
|
filePath,
|
|
@@ -187,7 +189,11 @@ function createCollector(emitter, baseDirectory = ".") {
|
|
|
187
189
|
}
|
|
188
190
|
}
|
|
189
191
|
async function collectFile(collection, filePath) {
|
|
190
|
-
const absolutePath = path2.join(
|
|
192
|
+
const absolutePath = path2.join(
|
|
193
|
+
baseDirectory,
|
|
194
|
+
collection.directory,
|
|
195
|
+
filePath
|
|
196
|
+
);
|
|
191
197
|
const file = await read(absolutePath);
|
|
192
198
|
if (!file) {
|
|
193
199
|
return null;
|
|
@@ -218,7 +224,8 @@ function createCollector(emitter, baseDirectory = ".") {
|
|
|
218
224
|
}
|
|
219
225
|
async function resolveCollection(collection) {
|
|
220
226
|
const collectionDirectory = path2.join(baseDirectory, collection.directory);
|
|
221
|
-
const
|
|
227
|
+
const include = Array.isArray(collection.include) ? collection.include : [collection.include];
|
|
228
|
+
const filePaths = await glob(include, {
|
|
222
229
|
cwd: collectionDirectory,
|
|
223
230
|
onlyFiles: true,
|
|
224
231
|
absolute: false,
|
|
@@ -245,32 +252,114 @@ function createCollector(emitter, baseDirectory = ".") {
|
|
|
245
252
|
};
|
|
246
253
|
}
|
|
247
254
|
|
|
248
|
-
// src/
|
|
249
|
-
import
|
|
250
|
-
import
|
|
251
|
-
|
|
255
|
+
// src/synchronizer.ts
|
|
256
|
+
import path3 from "node:path";
|
|
257
|
+
import picomatch from "picomatch";
|
|
258
|
+
function createSynchronizer(readCollectionFile, collections, baseDirectory = ".") {
|
|
259
|
+
function findCollections(filePath) {
|
|
260
|
+
const resolvedFilePath = path3.resolve(filePath);
|
|
261
|
+
return collections.filter((collection) => {
|
|
262
|
+
return resolvedFilePath.startsWith(
|
|
263
|
+
path3.resolve(baseDirectory, collection.directory)
|
|
264
|
+
);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function createRelativePath(collectionPath, filePath) {
|
|
268
|
+
const resolvedCollectionPath = path3.resolve(baseDirectory, collectionPath);
|
|
269
|
+
const resolvedFilePath = path3.resolve(filePath);
|
|
270
|
+
let relativePath = resolvedFilePath.slice(resolvedCollectionPath.length);
|
|
271
|
+
if (relativePath.startsWith(path3.sep)) {
|
|
272
|
+
relativePath = relativePath.slice(path3.sep.length);
|
|
273
|
+
}
|
|
274
|
+
return relativePath;
|
|
275
|
+
}
|
|
276
|
+
function resolve2(filePath) {
|
|
277
|
+
const collections2 = findCollections(filePath);
|
|
278
|
+
return collections2.map((collection) => {
|
|
279
|
+
const relativePath = createRelativePath(collection.directory, filePath);
|
|
280
|
+
return {
|
|
281
|
+
collection,
|
|
282
|
+
relativePath
|
|
283
|
+
};
|
|
284
|
+
}).filter(({ collection, relativePath }) => {
|
|
285
|
+
return picomatch.isMatch(relativePath, collection.include, {
|
|
286
|
+
ignore: collection.exclude
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
function deleted(filePath) {
|
|
291
|
+
const resolvedCollections = resolve2(filePath);
|
|
292
|
+
if (resolvedCollections.length === 0) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
let changed2 = false;
|
|
296
|
+
for (const { collection, relativePath } of resolvedCollections) {
|
|
297
|
+
const index = collection.files.findIndex(
|
|
298
|
+
(file) => file.path === relativePath
|
|
299
|
+
);
|
|
300
|
+
const deleted2 = collection.files.splice(index, 1);
|
|
301
|
+
if (deleted2.length > 0) {
|
|
302
|
+
changed2 = true;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return changed2;
|
|
306
|
+
}
|
|
307
|
+
async function changed(filePath) {
|
|
308
|
+
const resolvedCollections = resolve2(filePath);
|
|
309
|
+
if (resolvedCollections.length === 0) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
let changed2 = false;
|
|
313
|
+
for (const { collection, relativePath } of resolvedCollections) {
|
|
314
|
+
const index = collection.files.findIndex(
|
|
315
|
+
(file2) => file2.path === relativePath
|
|
316
|
+
);
|
|
317
|
+
const file = await readCollectionFile(collection, relativePath);
|
|
318
|
+
if (file) {
|
|
319
|
+
changed2 = true;
|
|
320
|
+
if (index === -1) {
|
|
321
|
+
collection.files.push(file);
|
|
322
|
+
collection.files.sort(orderByPath);
|
|
323
|
+
} else {
|
|
324
|
+
collection.files[index] = file;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return changed2;
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
deleted,
|
|
332
|
+
changed
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
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";
|
|
252
341
|
|
|
253
342
|
// src/serializer.ts
|
|
254
|
-
import z2 from "zod";
|
|
255
343
|
import serializeJs from "serialize-javascript";
|
|
256
|
-
|
|
344
|
+
import z from "zod";
|
|
345
|
+
var literalSchema = z.union([
|
|
257
346
|
// json
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
347
|
+
z.string(),
|
|
348
|
+
z.number(),
|
|
349
|
+
z.boolean(),
|
|
350
|
+
z.null(),
|
|
262
351
|
// serializable-javascript
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
352
|
+
z.undefined(),
|
|
353
|
+
z.date(),
|
|
354
|
+
z.map(z.unknown(), z.unknown()),
|
|
355
|
+
z.set(z.unknown()),
|
|
356
|
+
z.bigint()
|
|
268
357
|
]);
|
|
269
|
-
var schema =
|
|
270
|
-
() =>
|
|
358
|
+
var schema = z.lazy(
|
|
359
|
+
() => z.union([literalSchema, z.array(schema), z.record(schema)])
|
|
271
360
|
);
|
|
272
361
|
var extension = "js";
|
|
273
|
-
var serializableSchema =
|
|
362
|
+
var serializableSchema = z.record(schema);
|
|
274
363
|
function serialize(value) {
|
|
275
364
|
const serializedValue = serializeJs(value, {
|
|
276
365
|
space: 2,
|
|
@@ -280,85 +369,7 @@ function serialize(value) {
|
|
|
280
369
|
return `export default ${serializedValue};`;
|
|
281
370
|
}
|
|
282
371
|
|
|
283
|
-
// src/writer.ts
|
|
284
|
-
function createArrayConstName(name) {
|
|
285
|
-
let suffix = name.charAt(0).toUpperCase() + name.slice(1);
|
|
286
|
-
return "all" + pluralize2(suffix);
|
|
287
|
-
}
|
|
288
|
-
async function createDataFile(directory, collection) {
|
|
289
|
-
const dataPath = path3.join(
|
|
290
|
-
directory,
|
|
291
|
-
`${createArrayConstName(collection.name)}.${extension}`
|
|
292
|
-
);
|
|
293
|
-
await fs2.writeFile(
|
|
294
|
-
dataPath,
|
|
295
|
-
serialize(collection.documents.map((doc) => doc.document))
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
function createDataFiles(directory, collections) {
|
|
299
|
-
return Promise.all(
|
|
300
|
-
collections.map((collection) => createDataFile(directory, collection))
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
async function createJavaScriptFile(directory, configuration) {
|
|
304
|
-
const collections = configuration.collections.map(
|
|
305
|
-
({ name }) => createArrayConstName(name)
|
|
306
|
-
);
|
|
307
|
-
let content = `// generated by content-collections at ${/* @__PURE__ */ new Date()}
|
|
308
|
-
|
|
309
|
-
`;
|
|
310
|
-
for (const name of collections) {
|
|
311
|
-
content += `import ${name} from "./${name}.${extension}";
|
|
312
|
-
`;
|
|
313
|
-
}
|
|
314
|
-
content += "\n";
|
|
315
|
-
content += "export { " + collections.join(", ") + " };\n";
|
|
316
|
-
await fs2.writeFile(path3.join(directory, "index.js"), content, "utf-8");
|
|
317
|
-
}
|
|
318
|
-
function createImportPath(directory, target) {
|
|
319
|
-
let importPath = path3.posix.join(
|
|
320
|
-
...path3.relative(directory, target).split(path3.sep)
|
|
321
|
-
);
|
|
322
|
-
if (!importPath.startsWith(".")) {
|
|
323
|
-
importPath = "./" + importPath;
|
|
324
|
-
}
|
|
325
|
-
return importPath;
|
|
326
|
-
}
|
|
327
|
-
async function createTypeDefinitionFile(directory, configuration) {
|
|
328
|
-
if (!configuration.generateTypes) {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
const importPath = createImportPath(directory, configuration.path);
|
|
332
|
-
let content = `import configuration from "${importPath}";
|
|
333
|
-
import { GetTypeByName } from "@content-collections/core";
|
|
334
|
-
`;
|
|
335
|
-
const collections = configuration.collections;
|
|
336
|
-
for (const collection of collections) {
|
|
337
|
-
content += `
|
|
338
|
-
`;
|
|
339
|
-
content += `export type ${collection.typeName} = GetTypeByName<typeof configuration, "${collection.name}">;
|
|
340
|
-
`;
|
|
341
|
-
content += `export declare const ${createArrayConstName(
|
|
342
|
-
collection.name
|
|
343
|
-
)}: Array<${collection.typeName}>;
|
|
344
|
-
`;
|
|
345
|
-
}
|
|
346
|
-
content += "\n";
|
|
347
|
-
content += "export {};\n";
|
|
348
|
-
await fs2.writeFile(path3.join(directory, "index.d.ts"), content, "utf-8");
|
|
349
|
-
}
|
|
350
|
-
async function createWriter(directory) {
|
|
351
|
-
await fs2.mkdir(directory, { recursive: true });
|
|
352
|
-
return {
|
|
353
|
-
createJavaScriptFile: (configuration) => createJavaScriptFile(directory, configuration),
|
|
354
|
-
createTypeDefinitionFile: (configuration) => createTypeDefinitionFile(directory, configuration),
|
|
355
|
-
createDataFiles: (collections) => createDataFiles(directory, collections)
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
372
|
// src/transformer.ts
|
|
360
|
-
import { basename, dirname, extname } from "path";
|
|
361
|
-
import { z as z3 } from "zod";
|
|
362
373
|
var TransformError = class extends Error {
|
|
363
374
|
type;
|
|
364
375
|
constructor(type, message) {
|
|
@@ -366,8 +377,8 @@ var TransformError = class extends Error {
|
|
|
366
377
|
this.type = type;
|
|
367
378
|
}
|
|
368
379
|
};
|
|
369
|
-
function createPath(
|
|
370
|
-
let p =
|
|
380
|
+
function createPath(path9, ext) {
|
|
381
|
+
let p = path9.slice(0, -ext.length);
|
|
371
382
|
if (p.endsWith("/index")) {
|
|
372
383
|
p = p.slice(0, -6);
|
|
373
384
|
}
|
|
@@ -377,15 +388,15 @@ function createTransformer(emitter, cacheManager) {
|
|
|
377
388
|
function createSchema(parserName, schema2) {
|
|
378
389
|
const parser = parsers[parserName];
|
|
379
390
|
if (!parser.hasContent) {
|
|
380
|
-
return
|
|
391
|
+
return z2.object(schema2);
|
|
381
392
|
}
|
|
382
|
-
return
|
|
383
|
-
content:
|
|
393
|
+
return z2.object({
|
|
394
|
+
content: z2.string(),
|
|
384
395
|
...schema2
|
|
385
396
|
});
|
|
386
397
|
}
|
|
387
398
|
async function parseFile(collection, file) {
|
|
388
|
-
const { data, path:
|
|
399
|
+
const { data, path: path9 } = file;
|
|
389
400
|
const schema2 = createSchema(collection.parser, collection.schema);
|
|
390
401
|
let parsedData = await schema2.safeParseAsync(data);
|
|
391
402
|
if (!parsedData.success) {
|
|
@@ -396,7 +407,7 @@ function createTransformer(emitter, cacheManager) {
|
|
|
396
407
|
});
|
|
397
408
|
return null;
|
|
398
409
|
}
|
|
399
|
-
const ext = extname(
|
|
410
|
+
const ext = extname(path9);
|
|
400
411
|
let extension2 = ext;
|
|
401
412
|
if (extension2.startsWith(".")) {
|
|
402
413
|
extension2 = extension2.slice(1);
|
|
@@ -404,11 +415,11 @@ function createTransformer(emitter, cacheManager) {
|
|
|
404
415
|
const document = {
|
|
405
416
|
...parsedData.data,
|
|
406
417
|
_meta: {
|
|
407
|
-
filePath:
|
|
408
|
-
fileName: basename(
|
|
409
|
-
directory: dirname(
|
|
418
|
+
filePath: path9,
|
|
419
|
+
fileName: basename(path9),
|
|
420
|
+
directory: dirname(path9),
|
|
410
421
|
extension: extension2,
|
|
411
|
-
path: createPath(
|
|
422
|
+
path: createPath(path9, ext)
|
|
412
423
|
}
|
|
413
424
|
};
|
|
414
425
|
return {
|
|
@@ -438,43 +449,48 @@ function createTransformer(emitter, cacheManager) {
|
|
|
438
449
|
},
|
|
439
450
|
collection: {
|
|
440
451
|
name: collection.name,
|
|
441
|
-
directory: collection.directory
|
|
452
|
+
directory: collection.directory,
|
|
453
|
+
documents: async () => {
|
|
454
|
+
return collection.documents.map((doc) => doc.document);
|
|
455
|
+
}
|
|
442
456
|
},
|
|
443
457
|
cache: cache.cacheFn
|
|
444
458
|
};
|
|
445
459
|
}
|
|
446
|
-
async function
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
});
|
|
468
|
-
} else {
|
|
469
|
-
emitter.emit("transformer:error", {
|
|
470
|
-
collection,
|
|
471
|
-
error: new TransformError("Transform", String(error))
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
}
|
|
460
|
+
async function transformDocument(collections, collection, transform, doc) {
|
|
461
|
+
const cache = cacheManager.cache(collection.name, doc.document._meta.path);
|
|
462
|
+
const context2 = createContext(collections, collection, cache);
|
|
463
|
+
try {
|
|
464
|
+
const document = await transform(doc.document, context2);
|
|
465
|
+
await cache.tidyUp();
|
|
466
|
+
return {
|
|
467
|
+
...doc,
|
|
468
|
+
document
|
|
469
|
+
};
|
|
470
|
+
} catch (error) {
|
|
471
|
+
if (error instanceof TransformError) {
|
|
472
|
+
emitter.emit("transformer:error", {
|
|
473
|
+
collection,
|
|
474
|
+
error
|
|
475
|
+
});
|
|
476
|
+
} else {
|
|
477
|
+
emitter.emit("transformer:error", {
|
|
478
|
+
collection,
|
|
479
|
+
error: new TransformError("Transform", String(error))
|
|
480
|
+
});
|
|
475
481
|
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function transformCollection(collections, collection) {
|
|
485
|
+
const transform = collection.transform;
|
|
486
|
+
if (transform) {
|
|
487
|
+
const limit = pLimit(os.cpus().length);
|
|
488
|
+
const docs = collection.documents.map(
|
|
489
|
+
(doc) => limit(() => transformDocument(collections, collection, transform, doc))
|
|
490
|
+
);
|
|
491
|
+
const transformed = await Promise.all(docs);
|
|
476
492
|
await cacheManager.flush();
|
|
477
|
-
return
|
|
493
|
+
return transformed.filter(isDefined);
|
|
478
494
|
}
|
|
479
495
|
return collection.documents;
|
|
480
496
|
}
|
|
@@ -507,127 +523,460 @@ function createTransformer(emitter, cacheManager) {
|
|
|
507
523
|
};
|
|
508
524
|
}
|
|
509
525
|
|
|
510
|
-
// src/
|
|
511
|
-
import
|
|
512
|
-
import path4 from "path";
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
+
`;
|
|
521
559
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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;
|
|
530
570
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
collection,
|
|
537
|
-
relativePath
|
|
538
|
-
};
|
|
539
|
-
}).filter(({ collection, relativePath }) => {
|
|
540
|
-
return micromatch.isMatch(relativePath, collection.include, {
|
|
541
|
-
ignore: collection.exclude
|
|
542
|
-
});
|
|
543
|
-
});
|
|
571
|
+
return importPath;
|
|
572
|
+
}
|
|
573
|
+
async function createTypeDefinitionFile(directory, configuration) {
|
|
574
|
+
if (!configuration.generateTypes) {
|
|
575
|
+
return;
|
|
544
576
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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)
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/build.ts
|
|
606
|
+
async function createBuildContext({
|
|
607
|
+
emitter,
|
|
608
|
+
outputDirectory,
|
|
609
|
+
baseDirectory,
|
|
610
|
+
configuration
|
|
611
|
+
}) {
|
|
612
|
+
const collector = createCollector(emitter, baseDirectory);
|
|
613
|
+
const [writer, resolved, cacheManager] = await Promise.all([
|
|
614
|
+
createWriter(outputDirectory),
|
|
615
|
+
collector.collect(configuration.collections),
|
|
616
|
+
createCacheManager(baseDirectory, configuration.checksum)
|
|
617
|
+
]);
|
|
618
|
+
const synchronizer = createSynchronizer(
|
|
619
|
+
collector.collectFile,
|
|
620
|
+
resolved,
|
|
621
|
+
baseDirectory
|
|
622
|
+
);
|
|
623
|
+
const transform = createTransformer(emitter, cacheManager);
|
|
624
|
+
return {
|
|
625
|
+
resolved,
|
|
626
|
+
writer,
|
|
627
|
+
synchronizer,
|
|
628
|
+
transform,
|
|
629
|
+
emitter,
|
|
630
|
+
cacheManager,
|
|
631
|
+
configuration
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
async function build({
|
|
635
|
+
emitter,
|
|
636
|
+
transform,
|
|
637
|
+
resolved,
|
|
638
|
+
writer,
|
|
639
|
+
configuration
|
|
640
|
+
}) {
|
|
641
|
+
const startedAt = Date.now();
|
|
642
|
+
emitter.emit("builder:start", {
|
|
643
|
+
startedAt
|
|
644
|
+
});
|
|
645
|
+
const collections = await transform(resolved);
|
|
646
|
+
await Promise.all([
|
|
647
|
+
writer.createDataFiles(collections),
|
|
648
|
+
writer.createTypeDefinitionFile(configuration),
|
|
649
|
+
writer.createJavaScriptFile(configuration)
|
|
650
|
+
]);
|
|
651
|
+
const pendingOnSuccess = collections.filter((collection) => Boolean(collection.onSuccess)).map(
|
|
652
|
+
(collection) => collection.onSuccess?.(collection.documents.map((doc) => doc.document))
|
|
653
|
+
);
|
|
654
|
+
await Promise.all(pendingOnSuccess.filter(isDefined));
|
|
655
|
+
const stats = collections.reduce(
|
|
656
|
+
(acc, collection) => {
|
|
657
|
+
acc.collections++;
|
|
658
|
+
acc.documents += collection.documents.length;
|
|
659
|
+
return acc;
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
collections: 0,
|
|
663
|
+
documents: 0
|
|
549
664
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
665
|
+
);
|
|
666
|
+
emitter.emit("builder:end", {
|
|
667
|
+
startedAt,
|
|
668
|
+
endedAt: Date.now(),
|
|
669
|
+
stats
|
|
670
|
+
});
|
|
671
|
+
}
|
|
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;
|
|
558
720
|
}
|
|
559
721
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
async function changed(filePath) {
|
|
563
|
-
const resolvedCollections = resolve(filePath);
|
|
564
|
-
if (resolvedCollections.length === 0) {
|
|
565
|
-
return false;
|
|
722
|
+
if (isInsideString) {
|
|
723
|
+
continue;
|
|
566
724
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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;
|
|
580
764
|
}
|
|
765
|
+
} else if (currentCharacter === ",") {
|
|
766
|
+
result += buffer + jsonString.slice(offset, index);
|
|
767
|
+
buffer = "";
|
|
768
|
+
offset = index;
|
|
769
|
+
commaIndex = index;
|
|
581
770
|
}
|
|
582
771
|
}
|
|
583
|
-
return changed2;
|
|
584
772
|
}
|
|
585
|
-
return
|
|
586
|
-
deleted,
|
|
587
|
-
changed
|
|
588
|
-
};
|
|
773
|
+
return result + buffer + (isInsideComment ? strip(jsonString.slice(offset)) : jsonString.slice(offset));
|
|
589
774
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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;
|
|
601
793
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
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
|
+
}
|
|
608
838
|
});
|
|
609
|
-
|
|
839
|
+
extendsFiles.push(...parentConfig.files);
|
|
610
840
|
}
|
|
611
841
|
}
|
|
612
|
-
|
|
613
|
-
|
|
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);
|
|
614
868
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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);
|
|
619
886
|
return {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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}`
|
|
623
973
|
);
|
|
624
|
-
return;
|
|
625
974
|
}
|
|
626
975
|
};
|
|
627
976
|
}
|
|
628
977
|
|
|
629
978
|
// src/events.ts
|
|
630
|
-
import { EventEmitter } from "events";
|
|
979
|
+
import { EventEmitter } from "node:events";
|
|
631
980
|
function isEventWithError(event) {
|
|
632
981
|
return typeof event === "object" && event !== null && "error" in event;
|
|
633
982
|
}
|
|
@@ -655,102 +1004,43 @@ function createEmitter() {
|
|
|
655
1004
|
};
|
|
656
1005
|
}
|
|
657
1006
|
|
|
658
|
-
// src/
|
|
659
|
-
import
|
|
660
|
-
import {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
await mkdir(cacheDirectory, { recursive: true });
|
|
670
|
-
}
|
|
671
|
-
return cacheDirectory;
|
|
672
|
-
}
|
|
673
|
-
function fileName(input) {
|
|
674
|
-
return input.replace(/[^a-z0-9]/gi, "_").toLowerCase();
|
|
675
|
-
}
|
|
676
|
-
async function readMapping(mappingPath) {
|
|
677
|
-
if (existsSync2(mappingPath)) {
|
|
678
|
-
try {
|
|
679
|
-
return JSON.parse(await readFile2(mappingPath, "utf-8"));
|
|
680
|
-
} catch (e) {
|
|
681
|
-
console.error(
|
|
682
|
-
"Failed to parse the cache mapping. We will recreate the cache."
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
return {};
|
|
687
|
-
}
|
|
688
|
-
async function createCacheManager(baseDirectory, configChecksum) {
|
|
689
|
-
const cacheDirectory = await createCacheDirectory(baseDirectory);
|
|
690
|
-
const mappingPath = join(cacheDirectory, "mapping.json");
|
|
691
|
-
const mapping = await readMapping(mappingPath);
|
|
692
|
-
async function flush() {
|
|
693
|
-
await writeFile(mappingPath, JSON.stringify(mapping));
|
|
694
|
-
}
|
|
695
|
-
function cache(collection, file) {
|
|
696
|
-
const directory = join(
|
|
697
|
-
cacheDirectory,
|
|
698
|
-
fileName(collection),
|
|
699
|
-
fileName(file)
|
|
700
|
-
);
|
|
701
|
-
let collectionMapping = mapping[collection];
|
|
702
|
-
if (!collectionMapping) {
|
|
703
|
-
collectionMapping = {};
|
|
704
|
-
mapping[collection] = collectionMapping;
|
|
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;
|
|
705
1018
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
fileMapping = [];
|
|
709
|
-
collectionMapping[file] = fileMapping;
|
|
1019
|
+
for (const event of events) {
|
|
1020
|
+
await sync(event.type, event.path);
|
|
710
1021
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
} catch (e) {
|
|
721
|
-
console.error(
|
|
722
|
-
"Failed to parse the cache file. We will recompute the value."
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
const output = await fn(input);
|
|
728
|
-
if (!existsSync2(directory)) {
|
|
729
|
-
await mkdir(directory, { recursive: true });
|
|
730
|
-
}
|
|
731
|
-
await writeFile(filePath, JSON.stringify(output));
|
|
732
|
-
return output;
|
|
733
|
-
};
|
|
734
|
-
const tidyUp = async () => {
|
|
735
|
-
const filesToDelete = fileMapping?.filter((key) => !newFileMapping.includes(key)) || [];
|
|
736
|
-
for (const key of filesToDelete) {
|
|
737
|
-
const filePath = join(directory, `${key}.cache`);
|
|
738
|
-
if (existsSync2(filePath)) {
|
|
739
|
-
await unlink(filePath);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
if (collectionMapping) {
|
|
743
|
-
collectionMapping[file] = newFileMapping;
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
return {
|
|
747
|
-
cacheFn,
|
|
748
|
-
tidyUp
|
|
749
|
-
};
|
|
750
|
-
}
|
|
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
|
+
});
|
|
751
1031
|
return {
|
|
752
|
-
|
|
753
|
-
|
|
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
|
+
}
|
|
754
1044
|
};
|
|
755
1045
|
}
|
|
756
1046
|
|
|
@@ -759,69 +1049,137 @@ function resolveOutputDir(baseDirectory, options) {
|
|
|
759
1049
|
if (options.outputDir) {
|
|
760
1050
|
return options.outputDir;
|
|
761
1051
|
}
|
|
762
|
-
return
|
|
1052
|
+
return path8.join(baseDirectory, ".content-collections", "generated");
|
|
763
1053
|
}
|
|
1054
|
+
var ConfigurationReloadError = class extends Error {
|
|
1055
|
+
constructor(message) {
|
|
1056
|
+
super(message);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
764
1059
|
async function createBuilder(configurationPath, options = {
|
|
765
1060
|
configName: defaultConfigName
|
|
766
|
-
}) {
|
|
767
|
-
const emitter = createEmitter();
|
|
1061
|
+
}, emitter = createEmitter()) {
|
|
768
1062
|
const readConfiguration = createConfigurationReader();
|
|
769
|
-
const
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
);
|
|
784
|
-
const cacheManager = await createCacheManager(baseDirectory, configuration.checksum);
|
|
785
|
-
const transform = createTransformer(emitter, cacheManager);
|
|
1063
|
+
const baseDirectory = path8.dirname(configurationPath);
|
|
1064
|
+
const outputDirectory = resolveOutputDir(baseDirectory, options);
|
|
1065
|
+
emitter.emit("builder:created", {
|
|
1066
|
+
createdAt: Date.now(),
|
|
1067
|
+
configurationPath,
|
|
1068
|
+
outputDirectory
|
|
1069
|
+
});
|
|
1070
|
+
let configuration = await readConfiguration(configurationPath, options);
|
|
1071
|
+
let watcher2 = null;
|
|
1072
|
+
let context2 = await createBuildContext({
|
|
1073
|
+
emitter,
|
|
1074
|
+
baseDirectory,
|
|
1075
|
+
outputDirectory,
|
|
1076
|
+
configuration
|
|
1077
|
+
});
|
|
786
1078
|
async function sync(modification, filePath) {
|
|
787
|
-
if (
|
|
788
|
-
|
|
1079
|
+
if (configuration.inputPaths.includes(filePath)) {
|
|
1080
|
+
if (await onConfigurationChange()) {
|
|
1081
|
+
emitter.emit("watcher:config-changed", {
|
|
1082
|
+
filePath,
|
|
1083
|
+
modification
|
|
1084
|
+
});
|
|
1085
|
+
await build(context2);
|
|
1086
|
+
return true;
|
|
1087
|
+
}
|
|
1088
|
+
} else {
|
|
1089
|
+
if (await onFileChange(modification, filePath)) {
|
|
1090
|
+
emitter.emit("watcher:file-changed", {
|
|
1091
|
+
filePath,
|
|
1092
|
+
modification
|
|
1093
|
+
});
|
|
1094
|
+
await build(context2);
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
789
1097
|
}
|
|
790
|
-
return
|
|
1098
|
+
return false;
|
|
791
1099
|
}
|
|
792
|
-
async function
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1100
|
+
async function onConfigurationChange() {
|
|
1101
|
+
try {
|
|
1102
|
+
configuration = await readConfiguration(configurationPath, options);
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
emitter.emit("watcher:config-reload-error", {
|
|
1105
|
+
error: new ConfigurationReloadError(
|
|
1106
|
+
`Failed to reload configuration: ${error}`
|
|
1107
|
+
),
|
|
1108
|
+
configurationPath
|
|
1109
|
+
});
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
if (watcher2) {
|
|
1113
|
+
await watcher2.unsubscribe();
|
|
1114
|
+
}
|
|
1115
|
+
context2 = await createBuildContext({
|
|
1116
|
+
emitter,
|
|
1117
|
+
baseDirectory,
|
|
1118
|
+
outputDirectory,
|
|
1119
|
+
configuration
|
|
806
1120
|
});
|
|
1121
|
+
if (watcher2) {
|
|
1122
|
+
watcher2 = await createWatcher(
|
|
1123
|
+
emitter,
|
|
1124
|
+
baseDirectory,
|
|
1125
|
+
configuration,
|
|
1126
|
+
sync
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
1129
|
+
return true;
|
|
1130
|
+
}
|
|
1131
|
+
async function onFileChange(modification, filePath) {
|
|
1132
|
+
const { synchronizer } = context2;
|
|
1133
|
+
if (modification === "delete") {
|
|
1134
|
+
return synchronizer.deleted(filePath);
|
|
1135
|
+
} else {
|
|
1136
|
+
return synchronizer.changed(filePath);
|
|
1137
|
+
}
|
|
807
1138
|
}
|
|
808
1139
|
async function watch() {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1140
|
+
watcher2 = await createWatcher(emitter, baseDirectory, configuration, sync);
|
|
1141
|
+
return {
|
|
1142
|
+
unsubscribe: async () => {
|
|
1143
|
+
if (watcher2) {
|
|
1144
|
+
await watcher2.unsubscribe();
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
814
1148
|
}
|
|
815
1149
|
return {
|
|
1150
|
+
build: () => build(context2),
|
|
816
1151
|
sync,
|
|
817
|
-
build: build2,
|
|
818
1152
|
watch,
|
|
819
1153
|
on: emitter.on
|
|
820
1154
|
};
|
|
821
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
|
+
}
|
|
822
1179
|
export {
|
|
823
1180
|
CollectError,
|
|
824
1181
|
ConfigurationError,
|
|
1182
|
+
ConfigurationReloadError,
|
|
825
1183
|
TransformError,
|
|
826
1184
|
createBuilder,
|
|
827
1185
|
defineCollection,
|