astro 2.4.3 → 2.4.4
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/assets/services/service.js +2 -1
- package/dist/content/runtime.d.ts +8 -6
- package/dist/content/runtime.js +14 -24
- package/dist/content/types-generator.js +32 -17
- package/dist/content/utils.d.ts +22 -4
- package/dist/content/utils.js +47 -5
- package/dist/content/vite-plugin-content-assets.js +0 -2
- package/dist/content/vite-plugin-content-imports.js +14 -13
- package/dist/content/vite-plugin-content-virtual-mod.d.ts +14 -0
- package/dist/content/vite-plugin-content-virtual-mod.js +76 -19
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/core/util.d.ts +1 -1
- package/dist/core/util.js +2 -2
- package/package.json +1 -1
- package/src/content/template/virtual-mod.mjs +15 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AstroError, AstroErrorData } from "../../core/errors/index.js";
|
|
2
|
+
import { joinPaths } from "../../core/path.js";
|
|
2
3
|
import { VALID_OPTIMIZABLE_FORMATS } from "../consts.js";
|
|
3
4
|
import { isESMImportedImage } from "../internal.js";
|
|
4
5
|
function isLocalService(service) {
|
|
@@ -87,7 +88,7 @@ const baseService = {
|
|
|
87
88
|
options.height && searchParams.append("h", options.height.toString());
|
|
88
89
|
options.quality && searchParams.append("q", options.quality.toString());
|
|
89
90
|
options.format && searchParams.append("f", options.format);
|
|
90
|
-
return "/_image?" + searchParams;
|
|
91
|
+
return joinPaths(import.meta.env.BASE_URL, "/_image?") + searchParams;
|
|
91
92
|
},
|
|
92
93
|
parseURL(url) {
|
|
93
94
|
const params = url.searchParams;
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
type
|
|
1
|
+
type LazyImport = () => Promise<any>;
|
|
2
|
+
type GlobResult = Record<string, LazyImport>;
|
|
2
3
|
type CollectionToEntryMap = Record<string, GlobResult>;
|
|
4
|
+
type GetEntryImport = (collection: string, lookupId: string) => Promise<LazyImport>;
|
|
3
5
|
export declare function createCollectionToGlobResultMap({ globResult, contentDir, }: {
|
|
4
6
|
globResult: GlobResult;
|
|
5
7
|
contentDir: string;
|
|
6
8
|
}): CollectionToEntryMap;
|
|
7
|
-
export declare function createGetCollection({ collectionToEntryMap,
|
|
9
|
+
export declare function createGetCollection({ collectionToEntryMap, getRenderEntryImport, }: {
|
|
8
10
|
collectionToEntryMap: CollectionToEntryMap;
|
|
9
|
-
|
|
11
|
+
getRenderEntryImport: GetEntryImport;
|
|
10
12
|
}): (collection: string, filter?: ((entry: any) => unknown) | undefined) => Promise<any[]>;
|
|
11
|
-
export declare function createGetEntryBySlug({
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImport, }: {
|
|
14
|
+
getEntryImport: GetEntryImport;
|
|
15
|
+
getRenderEntryImport: GetEntryImport;
|
|
14
16
|
}): (collection: string, slug: string) => Promise<{
|
|
15
17
|
id: any;
|
|
16
18
|
slug: any;
|
package/dist/content/runtime.js
CHANGED
|
@@ -20,16 +20,15 @@ function createCollectionToGlobResultMap({
|
|
|
20
20
|
if (segments.length <= 1)
|
|
21
21
|
continue;
|
|
22
22
|
const collection = segments[0];
|
|
23
|
-
const entryId = segments.slice(1).join("/");
|
|
24
23
|
collectionToGlobResultMap[collection] ??= {};
|
|
25
|
-
collectionToGlobResultMap[collection][
|
|
24
|
+
collectionToGlobResultMap[collection][key] = globResult[key];
|
|
26
25
|
}
|
|
27
26
|
return collectionToGlobResultMap;
|
|
28
27
|
}
|
|
29
28
|
const cacheEntriesByCollection = /* @__PURE__ */ new Map();
|
|
30
29
|
function createGetCollection({
|
|
31
30
|
collectionToEntryMap,
|
|
32
|
-
|
|
31
|
+
getRenderEntryImport
|
|
33
32
|
}) {
|
|
34
33
|
return async function getCollection(collection, filter) {
|
|
35
34
|
const lazyImports = Object.values(collectionToEntryMap[collection] ?? {});
|
|
@@ -50,7 +49,7 @@ function createGetCollection({
|
|
|
50
49
|
return render({
|
|
51
50
|
collection: entry.collection,
|
|
52
51
|
id: entry.id,
|
|
53
|
-
|
|
52
|
+
renderEntryImport: await getRenderEntryImport(collection, entry.slug)
|
|
54
53
|
});
|
|
55
54
|
}
|
|
56
55
|
};
|
|
@@ -66,22 +65,14 @@ function createGetCollection({
|
|
|
66
65
|
};
|
|
67
66
|
}
|
|
68
67
|
function createGetEntryBySlug({
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
getEntryImport,
|
|
69
|
+
getRenderEntryImport
|
|
71
70
|
}) {
|
|
72
71
|
return async function getEntryBySlug(collection, slug) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
for (let entry2 of entries) {
|
|
76
|
-
if (entry2.slug === slug) {
|
|
77
|
-
candidate = entry2;
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (typeof candidate === "undefined") {
|
|
72
|
+
const entryImport = await getEntryImport(collection, slug);
|
|
73
|
+
if (typeof entryImport !== "function")
|
|
82
74
|
return void 0;
|
|
83
|
-
|
|
84
|
-
const entry = candidate;
|
|
75
|
+
const entry = await entryImport();
|
|
85
76
|
return {
|
|
86
77
|
id: entry.id,
|
|
87
78
|
slug: entry.slug,
|
|
@@ -92,7 +83,7 @@ function createGetEntryBySlug({
|
|
|
92
83
|
return render({
|
|
93
84
|
collection: entry.collection,
|
|
94
85
|
id: entry.id,
|
|
95
|
-
|
|
86
|
+
renderEntryImport: await getRenderEntryImport(collection, slug)
|
|
96
87
|
});
|
|
97
88
|
}
|
|
98
89
|
};
|
|
@@ -101,17 +92,16 @@ function createGetEntryBySlug({
|
|
|
101
92
|
async function render({
|
|
102
93
|
collection,
|
|
103
94
|
id,
|
|
104
|
-
|
|
95
|
+
renderEntryImport
|
|
105
96
|
}) {
|
|
106
|
-
var _a
|
|
97
|
+
var _a;
|
|
107
98
|
const UnexpectedRenderError = new AstroError({
|
|
108
99
|
...AstroErrorData.UnknownContentCollectionError,
|
|
109
100
|
message: `Unexpected error while rendering ${String(collection)} \u2192 ${String(id)}.`
|
|
110
101
|
});
|
|
111
|
-
|
|
112
|
-
if (typeof lazyImport !== "function")
|
|
102
|
+
if (typeof renderEntryImport !== "function")
|
|
113
103
|
throw UnexpectedRenderError;
|
|
114
|
-
const baseMod = await
|
|
104
|
+
const baseMod = await renderEntryImport();
|
|
115
105
|
if (baseMod == null || typeof baseMod !== "object")
|
|
116
106
|
throw UnexpectedRenderError;
|
|
117
107
|
const { collectedStyles, collectedLinks, collectedScripts, getMod } = baseMod;
|
|
@@ -158,7 +148,7 @@ async function render({
|
|
|
158
148
|
});
|
|
159
149
|
return {
|
|
160
150
|
Content,
|
|
161
|
-
headings: ((
|
|
151
|
+
headings: ((_a = mod.getHeadings) == null ? void 0 : _a.call(mod)) ?? [],
|
|
162
152
|
remarkPluginFrontmatter: mod.frontmatter ?? {}
|
|
163
153
|
};
|
|
164
154
|
}
|
|
@@ -6,16 +6,15 @@ import { normalizePath } from "vite";
|
|
|
6
6
|
import { AstroError, AstroErrorData } from "../core/errors/index.js";
|
|
7
7
|
import { info, warn } from "../core/logger/core.js";
|
|
8
8
|
import { isRelativePath } from "../core/path.js";
|
|
9
|
-
import { CONTENT_TYPES_FILE } from "./consts.js";
|
|
9
|
+
import { CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from "./consts.js";
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
getContentEntryConfigByExtMap,
|
|
12
12
|
getContentPaths,
|
|
13
13
|
getEntryInfo,
|
|
14
14
|
getEntrySlug,
|
|
15
15
|
getEntryType,
|
|
16
16
|
loadContentConfig,
|
|
17
|
-
NoCollectionError
|
|
18
|
-
parseFrontmatter
|
|
17
|
+
NoCollectionError
|
|
19
18
|
} from "./utils.js";
|
|
20
19
|
class UnsupportedFileTypeError extends Error {
|
|
21
20
|
}
|
|
@@ -28,7 +27,8 @@ async function createContentTypesGenerator({
|
|
|
28
27
|
}) {
|
|
29
28
|
const contentTypes = {};
|
|
30
29
|
const contentPaths = getContentPaths(settings.config, fs);
|
|
31
|
-
const
|
|
30
|
+
const contentEntryConfigByExt = getContentEntryConfigByExtMap(settings);
|
|
31
|
+
const contentEntryExts = [...contentEntryConfigByExt.keys()];
|
|
32
32
|
let events = [];
|
|
33
33
|
let debounceTimeout;
|
|
34
34
|
const typeTemplateContent = await fs.promises.readFile(contentPaths.typesTemplate, "utf-8");
|
|
@@ -139,12 +139,22 @@ async function createContentTypesGenerator({
|
|
|
139
139
|
}
|
|
140
140
|
return { shouldGenerateTypes: false };
|
|
141
141
|
}
|
|
142
|
-
const { id, collection } = entryInfo;
|
|
142
|
+
const { id, collection, slug: generatedSlug } = entryInfo;
|
|
143
|
+
const contentEntryType = contentEntryConfigByExt.get(path.extname(event.entry.pathname));
|
|
144
|
+
if (!contentEntryType)
|
|
145
|
+
return { shouldGenerateTypes: false };
|
|
143
146
|
const collectionKey = JSON.stringify(collection);
|
|
144
147
|
const entryKey = JSON.stringify(id);
|
|
145
148
|
switch (event.name) {
|
|
146
149
|
case "add":
|
|
147
|
-
const addedSlug = await
|
|
150
|
+
const addedSlug = await getEntrySlug({
|
|
151
|
+
generatedSlug,
|
|
152
|
+
id,
|
|
153
|
+
collection,
|
|
154
|
+
fileUrl: event.entry,
|
|
155
|
+
contentEntryType,
|
|
156
|
+
fs
|
|
157
|
+
});
|
|
148
158
|
if (!(collectionKey in contentTypes)) {
|
|
149
159
|
addCollection(contentTypes, collectionKey);
|
|
150
160
|
}
|
|
@@ -158,7 +168,14 @@ async function createContentTypesGenerator({
|
|
|
158
168
|
}
|
|
159
169
|
return { shouldGenerateTypes: true };
|
|
160
170
|
case "change":
|
|
161
|
-
const changedSlug = await
|
|
171
|
+
const changedSlug = await getEntrySlug({
|
|
172
|
+
generatedSlug,
|
|
173
|
+
id,
|
|
174
|
+
collection,
|
|
175
|
+
fileUrl: event.entry,
|
|
176
|
+
contentEntryType,
|
|
177
|
+
fs
|
|
178
|
+
});
|
|
162
179
|
if (((_b = (_a = contentTypes[collectionKey]) == null ? void 0 : _a[entryKey]) == null ? void 0 : _b.slug) !== changedSlug) {
|
|
163
180
|
setEntry(contentTypes, collectionKey, entryKey, changedSlug);
|
|
164
181
|
return { shouldGenerateTypes: true };
|
|
@@ -224,6 +241,7 @@ async function createContentTypesGenerator({
|
|
|
224
241
|
contentConfig: observable.status === "loaded" ? observable.config : void 0,
|
|
225
242
|
contentEntryTypes: settings.contentEntryTypes
|
|
226
243
|
});
|
|
244
|
+
invalidateVirtualMod(viteServer);
|
|
227
245
|
if (observable.status === "loaded" && ["info", "warn"].includes(logLevel)) {
|
|
228
246
|
warnNonexistentCollections({
|
|
229
247
|
logging,
|
|
@@ -235,21 +253,18 @@ async function createContentTypesGenerator({
|
|
|
235
253
|
}
|
|
236
254
|
return { init, queueEvent };
|
|
237
255
|
}
|
|
256
|
+
function invalidateVirtualMod(viteServer) {
|
|
257
|
+
const virtualMod = viteServer.moduleGraph.getModuleById("\0" + VIRTUAL_MODULE_ID);
|
|
258
|
+
if (!virtualMod)
|
|
259
|
+
return;
|
|
260
|
+
viteServer.moduleGraph.invalidateModule(virtualMod);
|
|
261
|
+
}
|
|
238
262
|
function addCollection(contentMap, collectionKey) {
|
|
239
263
|
contentMap[collectionKey] = {};
|
|
240
264
|
}
|
|
241
265
|
function removeCollection(contentMap, collectionKey) {
|
|
242
266
|
delete contentMap[collectionKey];
|
|
243
267
|
}
|
|
244
|
-
async function parseSlug({
|
|
245
|
-
fs,
|
|
246
|
-
event,
|
|
247
|
-
entryInfo
|
|
248
|
-
}) {
|
|
249
|
-
const rawContents = await fs.promises.readFile(event.entry, "utf-8");
|
|
250
|
-
const { data: frontmatter } = parseFrontmatter(rawContents, fileURLToPath(event.entry));
|
|
251
|
-
return getEntrySlug({ ...entryInfo, unvalidatedSlug: frontmatter.slug });
|
|
252
|
-
}
|
|
253
268
|
function setEntry(contentTypes, collectionKey, entryKey, slug) {
|
|
254
269
|
contentTypes[collectionKey][entryKey] = { slug };
|
|
255
270
|
}
|
package/dist/content/utils.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import fsMod from 'node:fs';
|
|
|
4
4
|
import type { PluginContext } from 'rollup';
|
|
5
5
|
import { type ViteDevServer } from 'vite';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
import type { AstroConfig, AstroSettings } from '../@types/astro.js';
|
|
7
|
+
import type { AstroConfig, AstroSettings, ContentEntryType } from '../@types/astro.js';
|
|
8
8
|
export declare const collectionConfigParser: z.ZodObject<{
|
|
9
9
|
schema: z.ZodOptional<z.ZodAny>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -47,8 +47,11 @@ export type EntryInfo = {
|
|
|
47
47
|
export declare const msg: {
|
|
48
48
|
collectionConfigMissing: (collection: string) => string;
|
|
49
49
|
};
|
|
50
|
-
export declare function
|
|
51
|
-
|
|
50
|
+
export declare function parseEntrySlug({ id, collection, generatedSlug, frontmatterSlug, }: {
|
|
51
|
+
id: string;
|
|
52
|
+
collection: string;
|
|
53
|
+
generatedSlug: string;
|
|
54
|
+
frontmatterSlug?: unknown;
|
|
52
55
|
}): string;
|
|
53
56
|
export declare function getEntryData(entry: EntryInfo & {
|
|
54
57
|
unvalidatedData: Record<string, unknown>;
|
|
@@ -57,13 +60,15 @@ export declare function getEntryData(entry: EntryInfo & {
|
|
|
57
60
|
[x: string]: unknown;
|
|
58
61
|
}>;
|
|
59
62
|
export declare function getContentEntryExts(settings: Pick<AstroSettings, 'contentEntryTypes'>): string[];
|
|
63
|
+
export declare function getContentEntryConfigByExtMap(settings: Pick<AstroSettings, 'contentEntryTypes'>): Map<string, ContentEntryType>;
|
|
60
64
|
export declare class NoCollectionError extends Error {
|
|
61
65
|
}
|
|
62
66
|
export declare function getEntryInfo(params: Pick<ContentPaths, 'contentDir'> & {
|
|
63
|
-
entry: URL;
|
|
67
|
+
entry: string | URL;
|
|
64
68
|
allowFilesOutsideCollection?: true;
|
|
65
69
|
}): EntryInfo;
|
|
66
70
|
export declare function getEntryType(entryPath: string, paths: Pick<ContentPaths, 'config' | 'contentDir'>, contentFileExts: string[], experimentalAssets: boolean): 'content' | 'config' | 'ignored' | 'unsupported';
|
|
71
|
+
export declare function hasUnderscoreBelowContentDirectoryPath(fileUrl: URL, contentDir: ContentPaths['contentDir']): boolean;
|
|
67
72
|
/**
|
|
68
73
|
* Match YAML exception handling from Astro core errors
|
|
69
74
|
* @see 'astro/src/core/errors.ts'
|
|
@@ -113,4 +118,17 @@ export type ContentPaths = {
|
|
|
113
118
|
};
|
|
114
119
|
};
|
|
115
120
|
export declare function getContentPaths({ srcDir, root }: Pick<AstroConfig, 'root' | 'srcDir'>, fs?: typeof fsMod): ContentPaths;
|
|
121
|
+
/**
|
|
122
|
+
* Check for slug in content entry frontmatter and validate the type,
|
|
123
|
+
* falling back to the `generatedSlug` if none is found.
|
|
124
|
+
*/
|
|
125
|
+
export declare function getEntrySlug({ id, collection, generatedSlug, contentEntryType, fileUrl, fs, }: {
|
|
126
|
+
fs: typeof fsMod;
|
|
127
|
+
id: string;
|
|
128
|
+
collection: string;
|
|
129
|
+
generatedSlug: string;
|
|
130
|
+
fileUrl: URL;
|
|
131
|
+
contentEntryType: Pick<ContentEntryType, 'getEntryInfo'>;
|
|
132
|
+
}): Promise<string>;
|
|
133
|
+
export declare function getExtGlob(exts: string[]): string;
|
|
116
134
|
export {};
|
package/dist/content/utils.js
CHANGED
|
@@ -26,14 +26,14 @@ const contentConfigParser = z.object({
|
|
|
26
26
|
const msg = {
|
|
27
27
|
collectionConfigMissing: (collection) => `${collection} does not have a config. We suggest adding one for type safety!`
|
|
28
28
|
};
|
|
29
|
-
function
|
|
29
|
+
function parseEntrySlug({
|
|
30
30
|
id,
|
|
31
31
|
collection,
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
generatedSlug,
|
|
33
|
+
frontmatterSlug
|
|
34
34
|
}) {
|
|
35
35
|
try {
|
|
36
|
-
return z.string().default(
|
|
36
|
+
return z.string().default(generatedSlug).parse(frontmatterSlug);
|
|
37
37
|
} catch {
|
|
38
38
|
throw new AstroError({
|
|
39
39
|
...AstroErrorData.InvalidContentEntrySlugError,
|
|
@@ -91,6 +91,15 @@ async function getEntryData(entry, collectionConfig, pluginContext, settings) {
|
|
|
91
91
|
function getContentEntryExts(settings) {
|
|
92
92
|
return settings.contentEntryTypes.map((t) => t.extensions).flat();
|
|
93
93
|
}
|
|
94
|
+
function getContentEntryConfigByExtMap(settings) {
|
|
95
|
+
const map = /* @__PURE__ */ new Map();
|
|
96
|
+
for (const entryType of settings.contentEntryTypes) {
|
|
97
|
+
for (const ext of entryType.extensions) {
|
|
98
|
+
map.set(ext, entryType);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return map;
|
|
102
|
+
}
|
|
94
103
|
class NoCollectionError extends Error {
|
|
95
104
|
}
|
|
96
105
|
function getEntryInfo({
|
|
@@ -98,7 +107,10 @@ function getEntryInfo({
|
|
|
98
107
|
contentDir,
|
|
99
108
|
allowFilesOutsideCollection = false
|
|
100
109
|
}) {
|
|
101
|
-
const rawRelativePath = path.relative(
|
|
110
|
+
const rawRelativePath = path.relative(
|
|
111
|
+
fileURLToPath(contentDir),
|
|
112
|
+
typeof entry === "string" ? entry : fileURLToPath(entry)
|
|
113
|
+
);
|
|
102
114
|
const rawCollection = path.dirname(rawRelativePath).split(path.sep).shift();
|
|
103
115
|
const isOutsideCollection = rawCollection === ".." || rawCollection === ".";
|
|
104
116
|
if (!rawCollection || !allowFilesOutsideCollection && isOutsideCollection)
|
|
@@ -238,11 +250,38 @@ function search(fs, srcDir) {
|
|
|
238
250
|
}
|
|
239
251
|
return { exists: false, url: paths[0] };
|
|
240
252
|
}
|
|
253
|
+
async function getEntrySlug({
|
|
254
|
+
id,
|
|
255
|
+
collection,
|
|
256
|
+
generatedSlug,
|
|
257
|
+
contentEntryType,
|
|
258
|
+
fileUrl,
|
|
259
|
+
fs
|
|
260
|
+
}) {
|
|
261
|
+
let contents;
|
|
262
|
+
try {
|
|
263
|
+
contents = await fs.promises.readFile(fileUrl, "utf-8");
|
|
264
|
+
} catch (e) {
|
|
265
|
+
throw new AstroError(AstroErrorData.UnknownContentCollectionError, { cause: e });
|
|
266
|
+
}
|
|
267
|
+
const { slug: frontmatterSlug } = await contentEntryType.getEntryInfo({
|
|
268
|
+
fileUrl,
|
|
269
|
+
contents: await fs.promises.readFile(fileUrl, "utf-8")
|
|
270
|
+
});
|
|
271
|
+
return parseEntrySlug({ generatedSlug, frontmatterSlug, id, collection });
|
|
272
|
+
}
|
|
273
|
+
function getExtGlob(exts) {
|
|
274
|
+
return exts.length === 1 ? (
|
|
275
|
+
// Wrapping {...} breaks when there is only one extension
|
|
276
|
+
exts[0]
|
|
277
|
+
) : `{${exts.join(",")}}`;
|
|
278
|
+
}
|
|
241
279
|
export {
|
|
242
280
|
NoCollectionError,
|
|
243
281
|
collectionConfigParser,
|
|
244
282
|
contentConfigParser,
|
|
245
283
|
contentObservable,
|
|
284
|
+
getContentEntryConfigByExtMap,
|
|
246
285
|
getContentEntryExts,
|
|
247
286
|
getContentPaths,
|
|
248
287
|
getDotAstroTypeReference,
|
|
@@ -250,8 +289,11 @@ export {
|
|
|
250
289
|
getEntryInfo,
|
|
251
290
|
getEntrySlug,
|
|
252
291
|
getEntryType,
|
|
292
|
+
getExtGlob,
|
|
253
293
|
globalContentConfigObserver,
|
|
294
|
+
hasUnderscoreBelowContentDirectoryPath,
|
|
254
295
|
loadContentConfig,
|
|
255
296
|
msg,
|
|
297
|
+
parseEntrySlug,
|
|
256
298
|
parseFrontmatter
|
|
257
299
|
};
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
SCRIPTS_PLACEHOLDER,
|
|
12
12
|
STYLES_PLACEHOLDER
|
|
13
13
|
} from "./consts.js";
|
|
14
|
-
import { getContentEntryExts } from "./utils.js";
|
|
15
14
|
function isPropagatedAsset(viteId) {
|
|
16
15
|
const flags = new URLSearchParams(viteId.split("?")[1]);
|
|
17
16
|
return flags.has(PROPAGATED_ASSET_FLAG);
|
|
@@ -21,7 +20,6 @@ function astroContentAssetPropagationPlugin({
|
|
|
21
20
|
settings
|
|
22
21
|
}) {
|
|
23
22
|
let devModuleLoader;
|
|
24
|
-
const contentEntryExts = getContentEntryExts(settings);
|
|
25
23
|
return {
|
|
26
24
|
name: "astro:content-asset-propagation",
|
|
27
25
|
configureServer(server) {
|
|
@@ -6,14 +6,15 @@ import { AstroError } from "../core/errors/errors.js";
|
|
|
6
6
|
import { escapeViteEnvReferences, getFileInfo } from "../vite-plugin-utils/index.js";
|
|
7
7
|
import { CONTENT_FLAG } from "./consts.js";
|
|
8
8
|
import {
|
|
9
|
+
getContentEntryConfigByExtMap,
|
|
9
10
|
getContentEntryExts,
|
|
10
11
|
getContentPaths,
|
|
11
12
|
getEntryData,
|
|
12
13
|
getEntryInfo,
|
|
13
|
-
getEntrySlug,
|
|
14
14
|
getEntryType,
|
|
15
15
|
globalContentConfigObserver,
|
|
16
|
-
NoCollectionError
|
|
16
|
+
NoCollectionError,
|
|
17
|
+
parseEntrySlug
|
|
17
18
|
} from "./utils.js";
|
|
18
19
|
function isContentFlagImport(viteId) {
|
|
19
20
|
const flags = new URLSearchParams(viteId.split("?")[1]);
|
|
@@ -37,12 +38,7 @@ function astroContentImportPlugin({
|
|
|
37
38
|
}) {
|
|
38
39
|
const contentPaths = getContentPaths(settings.config, fs);
|
|
39
40
|
const contentEntryExts = getContentEntryExts(settings);
|
|
40
|
-
const
|
|
41
|
-
for (const entryType of settings.contentEntryTypes) {
|
|
42
|
-
for (const ext of entryType.extensions) {
|
|
43
|
-
contentEntryExtToParser.set(ext, entryType);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
41
|
+
const contentEntryConfigByExt = getContentEntryConfigByExtMap(settings);
|
|
46
42
|
const plugins = [
|
|
47
43
|
{
|
|
48
44
|
name: "astro:content-imports",
|
|
@@ -131,7 +127,7 @@ function astroContentImportPlugin({
|
|
|
131
127
|
const contentConfig = await getContentConfigFromGlobal();
|
|
132
128
|
const rawContents = await fs.promises.readFile(fileId, "utf-8");
|
|
133
129
|
const fileExt = extname(fileId);
|
|
134
|
-
if (!
|
|
130
|
+
if (!contentEntryConfigByExt.has(fileExt)) {
|
|
135
131
|
throw new AstroError({
|
|
136
132
|
...AstroErrorData.UnknownContentCollectionError,
|
|
137
133
|
message: `No parser found for content entry ${JSON.stringify(
|
|
@@ -139,13 +135,13 @@ function astroContentImportPlugin({
|
|
|
139
135
|
)}. Did you apply an integration for this file type?`
|
|
140
136
|
});
|
|
141
137
|
}
|
|
142
|
-
const
|
|
138
|
+
const contentEntryConfig = contentEntryConfigByExt.get(fileExt);
|
|
143
139
|
const {
|
|
144
140
|
rawData,
|
|
145
141
|
body,
|
|
146
|
-
slug:
|
|
142
|
+
slug: frontmatterSlug,
|
|
147
143
|
data: unvalidatedData
|
|
148
|
-
} = await
|
|
144
|
+
} = await contentEntryConfig.getEntryInfo({
|
|
149
145
|
fileUrl: pathToFileURL(fileId),
|
|
150
146
|
contents: rawContents
|
|
151
147
|
});
|
|
@@ -157,7 +153,12 @@ function astroContentImportPlugin({
|
|
|
157
153
|
throw entryInfoResult;
|
|
158
154
|
const { id, slug: generatedSlug, collection } = entryInfoResult;
|
|
159
155
|
const _internal = { filePath: fileId, rawData };
|
|
160
|
-
const slug =
|
|
156
|
+
const slug = parseEntrySlug({
|
|
157
|
+
id,
|
|
158
|
+
collection,
|
|
159
|
+
generatedSlug,
|
|
160
|
+
frontmatterSlug
|
|
161
|
+
});
|
|
161
162
|
const collectionConfig = contentConfig == null ? void 0 : contentConfig.collections[collection];
|
|
162
163
|
let data = collectionConfig ? await getEntryData(
|
|
163
164
|
{ id, collection, slug, _internal, unvalidatedData },
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import fsMod from 'node:fs';
|
|
1
3
|
import type { Plugin } from 'vite';
|
|
2
4
|
import type { AstroSettings } from '../@types/astro.js';
|
|
5
|
+
import { getContentEntryConfigByExtMap, type ContentPaths } from './utils.js';
|
|
3
6
|
interface AstroContentVirtualModPluginParams {
|
|
4
7
|
settings: AstroSettings;
|
|
5
8
|
}
|
|
6
9
|
export declare function astroContentVirtualModPlugin({ settings, }: AstroContentVirtualModPluginParams): Plugin;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a map from a collection + slug to the local file path.
|
|
12
|
+
* This is used internally to resolve entry imports when using `getEntryBySlug()`.
|
|
13
|
+
* @see `src/content/virtual-mod.mjs`
|
|
14
|
+
*/
|
|
15
|
+
export declare function getStringifiedLookupMap({ contentPaths, contentEntryConfigByExt, root, fs, }: {
|
|
16
|
+
contentEntryConfigByExt: ReturnType<typeof getContentEntryConfigByExtMap>;
|
|
17
|
+
contentPaths: Pick<ContentPaths, 'contentDir' | 'cacheDir'>;
|
|
18
|
+
root: URL;
|
|
19
|
+
fs: typeof fsMod;
|
|
20
|
+
}): Promise<string>;
|
|
7
21
|
export {};
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
+
import glob from "fast-glob";
|
|
1
2
|
import fsMod from "node:fs";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { extname } from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { rootRelativePath } from "../core/util.js";
|
|
5
6
|
import { VIRTUAL_MODULE_ID } from "./consts.js";
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getContentEntryConfigByExtMap,
|
|
9
|
+
getContentPaths,
|
|
10
|
+
getEntryInfo,
|
|
11
|
+
getEntrySlug,
|
|
12
|
+
getExtGlob,
|
|
13
|
+
hasUnderscoreBelowContentDirectoryPath,
|
|
14
|
+
NoCollectionError
|
|
15
|
+
} from "./utils.js";
|
|
7
16
|
function astroContentVirtualModPlugin({
|
|
8
17
|
settings
|
|
9
18
|
}) {
|
|
10
19
|
const contentPaths = getContentPaths(settings.config);
|
|
11
|
-
const relContentDir =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
16
|
-
)
|
|
17
|
-
);
|
|
18
|
-
const contentEntryExts = getContentEntryExts(settings);
|
|
19
|
-
const extGlob = contentEntryExts.length === 1 ? (
|
|
20
|
-
// Wrapping {...} breaks when there is only one extension
|
|
21
|
-
contentEntryExts[0]
|
|
22
|
-
) : `{${contentEntryExts.join(",")}}`;
|
|
20
|
+
const relContentDir = rootRelativePath(settings.config.root, contentPaths.contentDir);
|
|
21
|
+
const contentEntryConfigByExt = getContentEntryConfigByExtMap(settings);
|
|
22
|
+
const contentEntryExts = [...contentEntryConfigByExt.keys()];
|
|
23
|
+
const extGlob = getExtGlob(contentEntryExts);
|
|
23
24
|
const entryGlob = `${relContentDir}**/*${extGlob}`;
|
|
24
25
|
const virtualModContents = fsMod.readFileSync(contentPaths.virtualModTemplate, "utf-8").replace("@@CONTENT_DIR@@", relContentDir).replace("@@ENTRY_GLOB_PATH@@", entryGlob).replace("@@RENDER_ENTRY_GLOB_PATH@@", entryGlob);
|
|
25
26
|
const astroContentVirtualModuleId = "\0" + VIRTUAL_MODULE_ID;
|
|
@@ -31,15 +32,71 @@ function astroContentVirtualModPlugin({
|
|
|
31
32
|
return astroContentVirtualModuleId;
|
|
32
33
|
}
|
|
33
34
|
},
|
|
34
|
-
load(id) {
|
|
35
|
+
async load(id) {
|
|
36
|
+
const stringifiedLookupMap = await getStringifiedLookupMap({
|
|
37
|
+
fs: fsMod,
|
|
38
|
+
contentPaths,
|
|
39
|
+
contentEntryConfigByExt,
|
|
40
|
+
root: settings.config.root
|
|
41
|
+
});
|
|
35
42
|
if (id === astroContentVirtualModuleId) {
|
|
36
43
|
return {
|
|
37
|
-
code: virtualModContents
|
|
44
|
+
code: virtualModContents.replace(
|
|
45
|
+
"/* @@LOOKUP_MAP_ASSIGNMENT@@ */",
|
|
46
|
+
`lookupMap = ${stringifiedLookupMap};`
|
|
47
|
+
)
|
|
38
48
|
};
|
|
39
49
|
}
|
|
40
50
|
}
|
|
41
51
|
};
|
|
42
52
|
}
|
|
53
|
+
async function getStringifiedLookupMap({
|
|
54
|
+
contentPaths,
|
|
55
|
+
contentEntryConfigByExt,
|
|
56
|
+
root,
|
|
57
|
+
fs
|
|
58
|
+
}) {
|
|
59
|
+
const { contentDir } = contentPaths;
|
|
60
|
+
const globOpts = {
|
|
61
|
+
absolute: true,
|
|
62
|
+
cwd: fileURLToPath(root),
|
|
63
|
+
fs: {
|
|
64
|
+
readdir: fs.readdir.bind(fs),
|
|
65
|
+
readdirSync: fs.readdirSync.bind(fs)
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const relContentDir = rootRelativePath(root, contentDir, false);
|
|
69
|
+
const contentGlob = await glob(
|
|
70
|
+
`${relContentDir}**/*${getExtGlob([...contentEntryConfigByExt.keys()])}`,
|
|
71
|
+
globOpts
|
|
72
|
+
);
|
|
73
|
+
let filePathByLookupId = {};
|
|
74
|
+
await Promise.all(
|
|
75
|
+
contentGlob.filter((e) => !hasUnderscoreBelowContentDirectoryPath(pathToFileURL(e), contentDir)).map(async (filePath) => {
|
|
76
|
+
const info = getEntryInfo({ contentDir, entry: filePath });
|
|
77
|
+
if (info instanceof NoCollectionError)
|
|
78
|
+
return;
|
|
79
|
+
const contentEntryType = contentEntryConfigByExt.get(extname(filePath));
|
|
80
|
+
if (!contentEntryType)
|
|
81
|
+
return;
|
|
82
|
+
const { id, collection, slug: generatedSlug } = info;
|
|
83
|
+
const slug = await getEntrySlug({
|
|
84
|
+
id,
|
|
85
|
+
collection,
|
|
86
|
+
generatedSlug,
|
|
87
|
+
fs,
|
|
88
|
+
fileUrl: pathToFileURL(filePath),
|
|
89
|
+
contentEntryType
|
|
90
|
+
});
|
|
91
|
+
filePathByLookupId[collection] = {
|
|
92
|
+
...filePathByLookupId[collection],
|
|
93
|
+
[slug]: rootRelativePath(root, filePath)
|
|
94
|
+
};
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
return JSON.stringify(filePathByLookupId);
|
|
98
|
+
}
|
|
43
99
|
export {
|
|
44
|
-
astroContentVirtualModPlugin
|
|
100
|
+
astroContentVirtualModPlugin,
|
|
101
|
+
getStringifiedLookupMap
|
|
45
102
|
};
|
package/dist/core/constants.js
CHANGED
package/dist/core/dev/dev.js
CHANGED
|
@@ -53,7 +53,7 @@ async function dev(settings, options) {
|
|
|
53
53
|
isRestart: options.isRestart
|
|
54
54
|
})
|
|
55
55
|
);
|
|
56
|
-
const currentVersion = "2.4.
|
|
56
|
+
const currentVersion = "2.4.4";
|
|
57
57
|
if (currentVersion.includes("-")) {
|
|
58
58
|
warn(options.logging, null, msg.prerelease({ currentVersion }));
|
|
59
59
|
}
|
package/dist/core/messages.js
CHANGED
|
@@ -47,7 +47,7 @@ function serverStart({
|
|
|
47
47
|
base,
|
|
48
48
|
isRestart = false
|
|
49
49
|
}) {
|
|
50
|
-
const version = "2.4.
|
|
50
|
+
const version = "2.4.4";
|
|
51
51
|
const localPrefix = `${dim("\u2503")} Local `;
|
|
52
52
|
const networkPrefix = `${dim("\u2503")} Network `;
|
|
53
53
|
const emptyPrefix = " ".repeat(11);
|
|
@@ -233,7 +233,7 @@ function printHelp({
|
|
|
233
233
|
message.push(
|
|
234
234
|
linebreak(),
|
|
235
235
|
` ${bgGreen(black(` ${commandName} `))} ${green(
|
|
236
|
-
`v${"2.4.
|
|
236
|
+
`v${"2.4.4"}`
|
|
237
237
|
)} ${headline}`
|
|
238
238
|
);
|
|
239
239
|
}
|
package/dist/core/util.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare function isPage(file: URL, settings: AstroSettings): boolean;
|
|
|
37
37
|
export declare function isEndpoint(file: URL, settings: AstroSettings): boolean;
|
|
38
38
|
export declare function isModeServerWithNoAdapter(settings: AstroSettings): boolean;
|
|
39
39
|
export declare function relativeToSrcDir(config: AstroConfig, idOrUrl: URL | string): string;
|
|
40
|
-
export declare function rootRelativePath(root: URL, idOrUrl: URL | string): string;
|
|
40
|
+
export declare function rootRelativePath(root: URL, idOrUrl: URL | string, shouldPrependForwardSlash?: boolean): string;
|
|
41
41
|
export declare function emoji(char: string, fallback: string): string;
|
|
42
42
|
/**
|
|
43
43
|
* Simulate Vite's resolve and import analysis so we can import the id as an URL
|
package/dist/core/util.js
CHANGED
|
@@ -114,7 +114,7 @@ function relativeToSrcDir(config, idOrUrl) {
|
|
|
114
114
|
}
|
|
115
115
|
return id.slice(slash(fileURLToPath(config.srcDir)).length);
|
|
116
116
|
}
|
|
117
|
-
function rootRelativePath(root, idOrUrl) {
|
|
117
|
+
function rootRelativePath(root, idOrUrl, shouldPrependForwardSlash = true) {
|
|
118
118
|
let id;
|
|
119
119
|
if (typeof idOrUrl !== "string") {
|
|
120
120
|
id = unwrapId(viteID(idOrUrl));
|
|
@@ -125,7 +125,7 @@ function rootRelativePath(root, idOrUrl) {
|
|
|
125
125
|
if (id.startsWith(normalizedRoot)) {
|
|
126
126
|
id = id.slice(normalizedRoot.length);
|
|
127
127
|
}
|
|
128
|
-
return prependForwardSlash(id);
|
|
128
|
+
return shouldPrependForwardSlash ? prependForwardSlash(id) : id;
|
|
129
129
|
}
|
|
130
130
|
function emoji(char, fallback) {
|
|
131
131
|
return process.platform !== "win32" ? char : fallback;
|
package/package.json
CHANGED
|
@@ -28,6 +28,18 @@ const collectionToEntryMap = createCollectionToGlobResultMap({
|
|
|
28
28
|
contentDir,
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
let lookupMap = {};
|
|
32
|
+
/* @@LOOKUP_MAP_ASSIGNMENT@@ */
|
|
33
|
+
|
|
34
|
+
function createGlobLookup(glob) {
|
|
35
|
+
return async (collection, lookupId) => {
|
|
36
|
+
const filePath = lookupMap[collection]?.[lookupId];
|
|
37
|
+
|
|
38
|
+
if (!filePath) return undefined;
|
|
39
|
+
return glob[collection][filePath];
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
const renderEntryGlob = import.meta.glob('@@RENDER_ENTRY_GLOB_PATH@@', {
|
|
32
44
|
query: { astroPropagatedAssets: true },
|
|
33
45
|
});
|
|
@@ -38,10 +50,10 @@ const collectionToRenderEntryMap = createCollectionToGlobResultMap({
|
|
|
38
50
|
|
|
39
51
|
export const getCollection = createGetCollection({
|
|
40
52
|
collectionToEntryMap,
|
|
41
|
-
collectionToRenderEntryMap,
|
|
53
|
+
getRenderEntryImport: createGlobLookup(collectionToRenderEntryMap),
|
|
42
54
|
});
|
|
43
55
|
|
|
44
56
|
export const getEntryBySlug = createGetEntryBySlug({
|
|
45
|
-
|
|
46
|
-
collectionToRenderEntryMap,
|
|
57
|
+
getEntryImport: createGlobLookup(collectionToEntryMap),
|
|
58
|
+
getRenderEntryImport: createGlobLookup(collectionToRenderEntryMap),
|
|
47
59
|
});
|