@nifrajs/content 4.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/fs.ts +21 -15
package/package.json
CHANGED
package/src/fs.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* gives you a typed, validated API over them. Build/server-time (Bun/Node/Deno); for the edge, bake the
|
|
4
4
|
* entries at build (the manifest approach `@nifrajs/web/fs` uses for routes) - tracked as a follow-up.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import type { Stats } from "node:fs"
|
|
7
|
+
import { readdir, readFile, stat as statPath } from "node:fs/promises"
|
|
7
8
|
import { resolve, sep } from "node:path"
|
|
8
9
|
import { type Entry, type InferSchema, parseEntry, type StandardSchemaV1 } from "./index.ts"
|
|
9
10
|
|
|
@@ -21,7 +22,8 @@ export interface Collection<Frontmatter> {
|
|
|
21
22
|
/** Every entry in the directory (unordered - sort/filter in your loader). Throws if any file's
|
|
22
23
|
* frontmatter is invalid. */
|
|
23
24
|
all(): Promise<Array<Entry<Frontmatter>>>
|
|
24
|
-
/** One entry by slug (filename without extension), or `null` if not found.
|
|
25
|
+
/** One entry by slug (filename without extension), or `null` if not found. Throws, as `all()`
|
|
26
|
+
* does, if the file's frontmatter is invalid. */
|
|
25
27
|
get(slug: string): Promise<Entry<Frontmatter> | null>
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -87,7 +89,7 @@ export function defineCollection<S extends StandardSchemaV1>(
|
|
|
87
89
|
const cached = fileCache.get(path)
|
|
88
90
|
if (cached?.stamp === stamp) return cached.entry
|
|
89
91
|
const entry = await parseEntry({
|
|
90
|
-
raw:
|
|
92
|
+
raw: await readFile(path, "utf8"),
|
|
91
93
|
slug,
|
|
92
94
|
schema: config.schema,
|
|
93
95
|
})
|
|
@@ -95,19 +97,21 @@ export function defineCollection<S extends StandardSchemaV1>(
|
|
|
95
97
|
return entry
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
.map((file) => {
|
|
100
|
+
// Async throughout: a collection read on a long-lived server must not block its event loop.
|
|
101
|
+
const listFiles = async (): Promise<FileRecord[]> => {
|
|
102
|
+
const files = (await readdir(root)).filter(matches).sort()
|
|
103
|
+
return Promise.all(
|
|
104
|
+
files.map(async (file) => {
|
|
103
105
|
const path = resolve(root, file)
|
|
104
|
-
const stat =
|
|
106
|
+
const stat = await statPath(path)
|
|
105
107
|
return { file, path, stamp: `${stat.mtimeMs}:${stat.size}` }
|
|
106
|
-
})
|
|
108
|
+
}),
|
|
109
|
+
)
|
|
110
|
+
}
|
|
107
111
|
|
|
108
112
|
return {
|
|
109
113
|
async all() {
|
|
110
|
-
const files = listFiles()
|
|
114
|
+
const files = await listFiles()
|
|
111
115
|
const fingerprint = files.map((file) => `${file.file}\0${file.stamp}`).join("\0")
|
|
112
116
|
if (collectionCache?.fingerprint === fingerprint) return [...collectionCache.entries]
|
|
113
117
|
|
|
@@ -126,13 +130,15 @@ export function defineCollection<S extends StandardSchemaV1>(
|
|
|
126
130
|
for (const ext of extensions) {
|
|
127
131
|
const file = resolve(root, `${slug}${ext}`)
|
|
128
132
|
if (!insideRoot(root, file)) return null
|
|
133
|
+
let stat: Stats
|
|
129
134
|
try {
|
|
130
|
-
|
|
131
|
-
if (!stat.isFile()) continue
|
|
132
|
-
return await readEntry(file, slug, `${stat.mtimeMs}:${stat.size}`)
|
|
135
|
+
stat = await statPath(file)
|
|
133
136
|
} catch {
|
|
134
|
-
// Not this extension
|
|
137
|
+
continue // Not this extension; try the next configured one.
|
|
135
138
|
}
|
|
139
|
+
if (!stat.isFile()) continue
|
|
140
|
+
// Outside the try: a file that exists but fails to parse is an error, not a missing entry.
|
|
141
|
+
return await readEntry(file, slug, `${stat.mtimeMs}:${stat.size}`)
|
|
136
142
|
}
|
|
137
143
|
return null
|
|
138
144
|
},
|