astro 4.13.3 → 4.14.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.
Files changed (107) hide show
  1. package/components/Code.astro +9 -0
  2. package/dist/@types/astro.d.ts +251 -2
  3. package/dist/actions/consts.d.ts +1 -1
  4. package/dist/actions/consts.js +1 -1
  5. package/dist/actions/index.js +12 -21
  6. package/dist/actions/runtime/virtual/server.js +9 -3
  7. package/dist/actions/runtime/virtual/shared.js +25 -3
  8. package/dist/assets/utils/resolveImports.d.ts +9 -0
  9. package/dist/assets/utils/resolveImports.js +22 -0
  10. package/dist/cli/add/index.d.ts +2 -2
  11. package/dist/cli/add/index.js +2 -2
  12. package/dist/cli/build/index.d.ts +2 -2
  13. package/dist/cli/build/index.js +5 -1
  14. package/dist/cli/check/index.d.ts +2 -2
  15. package/dist/cli/check/index.js +5 -2
  16. package/dist/cli/db/index.d.ts +4 -3
  17. package/dist/cli/db/index.js +10 -3
  18. package/dist/cli/dev/index.d.ts +2 -2
  19. package/dist/cli/dev/index.js +1 -0
  20. package/dist/cli/docs/index.d.ts +2 -2
  21. package/dist/cli/flags.d.ts +3 -1
  22. package/dist/cli/flags.js +2 -1
  23. package/dist/cli/index.d.ts +1 -1
  24. package/dist/cli/index.js +26 -13
  25. package/dist/cli/info/index.d.ts +2 -2
  26. package/dist/cli/preferences/index.d.ts +2 -2
  27. package/dist/cli/preferences/index.js +1 -1
  28. package/dist/cli/preview/index.d.ts +2 -2
  29. package/dist/cli/sync/index.d.ts +2 -2
  30. package/dist/cli/sync/index.js +5 -2
  31. package/dist/cli/telemetry/index.d.ts +2 -2
  32. package/dist/container/index.js +3 -1
  33. package/dist/content/consts.d.ts +16 -2
  34. package/dist/content/consts.js +32 -2
  35. package/dist/content/content-layer.d.ts +40 -0
  36. package/dist/content/content-layer.js +253 -0
  37. package/dist/content/data-store.d.ts +114 -0
  38. package/dist/content/data-store.js +323 -0
  39. package/dist/content/loaders/file.d.ts +7 -0
  40. package/dist/content/loaders/file.js +72 -0
  41. package/dist/content/loaders/glob.d.ts +25 -0
  42. package/dist/content/loaders/glob.js +218 -0
  43. package/dist/content/loaders/index.d.ts +3 -0
  44. package/dist/content/loaders/index.js +7 -0
  45. package/dist/content/loaders/types.d.ts +36 -0
  46. package/dist/content/loaders/types.js +0 -0
  47. package/dist/content/runtime.d.ts +46 -8
  48. package/dist/content/runtime.js +225 -31
  49. package/dist/content/types-generator.js +125 -35
  50. package/dist/content/utils.d.ts +306 -2
  51. package/dist/content/utils.js +93 -7
  52. package/dist/content/vite-plugin-content-assets.js +9 -1
  53. package/dist/content/vite-plugin-content-virtual-mod.js +94 -2
  54. package/dist/core/app/common.js +4 -1
  55. package/dist/core/app/index.js +5 -3
  56. package/dist/core/app/types.d.ts +3 -1
  57. package/dist/core/build/generate.js +4 -2
  58. package/dist/core/build/index.js +17 -8
  59. package/dist/core/build/plugins/plugin-manifest.js +5 -2
  60. package/dist/core/build/plugins/plugin-ssr.js +35 -3
  61. package/dist/core/build/static-build.js +2 -0
  62. package/dist/core/build/types.d.ts +1 -0
  63. package/dist/core/config/config.d.ts +2 -5
  64. package/dist/core/config/config.js +0 -12
  65. package/dist/core/config/index.d.ts +1 -1
  66. package/dist/core/config/index.js +0 -2
  67. package/dist/core/config/schema.d.ts +34 -0
  68. package/dist/core/config/schema.js +6 -2
  69. package/dist/core/config/settings.js +5 -3
  70. package/dist/core/constants.js +1 -1
  71. package/dist/core/create-vite.js +1 -1
  72. package/dist/core/dev/container.js +2 -1
  73. package/dist/core/dev/dev.js +34 -2
  74. package/dist/core/dev/restart.js +25 -10
  75. package/dist/core/encryption.d.ts +24 -0
  76. package/dist/core/encryption.js +64 -0
  77. package/dist/core/errors/errors-data.d.ts +21 -0
  78. package/dist/core/errors/errors-data.js +13 -0
  79. package/dist/core/index.js +1 -1
  80. package/dist/core/messages.js +2 -2
  81. package/dist/core/render-context.js +1 -0
  82. package/dist/core/server-islands/endpoint.js +5 -1
  83. package/dist/core/sync/constants.d.ts +1 -0
  84. package/dist/core/sync/constants.js +4 -0
  85. package/dist/core/sync/index.d.ts +12 -4
  86. package/dist/core/sync/index.js +56 -25
  87. package/dist/core/sync/write-files.d.ts +4 -0
  88. package/dist/core/sync/write-files.js +69 -0
  89. package/dist/core/util.js +1 -1
  90. package/dist/env/sync.js +6 -4
  91. package/dist/integrations/hooks.d.ts +7 -1
  92. package/dist/integrations/hooks.js +54 -0
  93. package/dist/preferences/index.d.ts +1 -1
  94. package/dist/preferences/index.js +2 -2
  95. package/dist/runtime/server/render/server-islands.js +9 -5
  96. package/dist/vite-plugin-astro-server/plugin.js +2 -0
  97. package/dist/vite-plugin-env/index.d.ts +3 -1
  98. package/dist/vite-plugin-env/index.js +11 -1
  99. package/dist/vite-plugin-markdown/content-entry-type.js +25 -2
  100. package/dist/vite-plugin-scanner/index.js +15 -5
  101. package/dist/vite-plugin-scanner/scan.js +1 -1
  102. package/package.json +15 -9
  103. package/templates/content/module.mjs +6 -1
  104. package/templates/content/types.d.ts +18 -5
  105. package/types/content.d.ts +34 -1
  106. package/dist/core/sync/setup-env-ts.d.ts +0 -8
  107. package/dist/core/sync/setup-env-ts.js +0 -79
@@ -0,0 +1,218 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { fileURLToPath, pathToFileURL } from "node:url";
3
+ import fastGlob from "fast-glob";
4
+ import { bold, green } from "kleur/colors";
5
+ import micromatch from "micromatch";
6
+ import pLimit from "p-limit";
7
+ import { getContentEntryIdAndSlug, getEntryConfigByExtMap, posixRelative } from "../utils.js";
8
+ function generateIdDefault({ entry, base, data }) {
9
+ if (data.slug) {
10
+ return data.slug;
11
+ }
12
+ const entryURL = new URL(entry, base);
13
+ const { slug } = getContentEntryIdAndSlug({
14
+ entry: entryURL,
15
+ contentDir: base,
16
+ collection: ""
17
+ });
18
+ return slug;
19
+ }
20
+ function glob(globOptions) {
21
+ if (globOptions.pattern.startsWith("../")) {
22
+ throw new Error(
23
+ "Glob patterns cannot start with `../`. Set the `base` option to a parent directory instead."
24
+ );
25
+ }
26
+ if (globOptions.pattern.startsWith("/")) {
27
+ throw new Error(
28
+ "Glob patterns cannot start with `/`. Set the `base` option to a parent directory or use a relative path instead."
29
+ );
30
+ }
31
+ const generateId = globOptions?.generateId ?? generateIdDefault;
32
+ const fileToIdMap = /* @__PURE__ */ new Map();
33
+ return {
34
+ name: "glob-loader",
35
+ load: async ({ settings, logger, watcher, parseData, store, generateDigest }) => {
36
+ const renderFunctionByContentType = /* @__PURE__ */ new WeakMap();
37
+ const untouchedEntries = new Set(store.keys());
38
+ async function syncData(entry, base, entryType) {
39
+ if (!entryType) {
40
+ logger.warn(`No entry type found for ${entry}`);
41
+ return;
42
+ }
43
+ const fileUrl = new URL(entry, base);
44
+ const contents = await fs.readFile(fileUrl, "utf-8").catch((err) => {
45
+ logger.error(`Error reading ${entry}: ${err.message}`);
46
+ return;
47
+ });
48
+ if (!contents) {
49
+ logger.warn(`No contents found for ${entry}`);
50
+ return;
51
+ }
52
+ const { body, data } = await entryType.getEntryInfo({
53
+ contents,
54
+ fileUrl
55
+ });
56
+ const id = generateId({ entry, base, data });
57
+ untouchedEntries.delete(id);
58
+ const existingEntry = store.get(id);
59
+ const digest = generateDigest(contents);
60
+ if (existingEntry && existingEntry.digest === digest && existingEntry.filePath) {
61
+ if (existingEntry.deferredRender) {
62
+ store.addModuleImport(existingEntry.filePath);
63
+ }
64
+ if (existingEntry.rendered?.metadata?.imagePaths?.length) {
65
+ store.addAssetImports(
66
+ existingEntry.rendered.metadata.imagePaths,
67
+ existingEntry.filePath
68
+ );
69
+ }
70
+ await parseData(existingEntry);
71
+ return;
72
+ }
73
+ const filePath = fileURLToPath(fileUrl);
74
+ const relativePath = posixRelative(fileURLToPath(settings.config.root), filePath);
75
+ const parsedData = await parseData({
76
+ id,
77
+ data,
78
+ filePath
79
+ });
80
+ if (entryType.getRenderFunction) {
81
+ let render = renderFunctionByContentType.get(entryType);
82
+ if (!render) {
83
+ render = await entryType.getRenderFunction(settings);
84
+ renderFunctionByContentType.set(entryType, render);
85
+ }
86
+ let rendered = void 0;
87
+ try {
88
+ rendered = await render?.({
89
+ id,
90
+ data: parsedData,
91
+ body,
92
+ filePath,
93
+ digest
94
+ });
95
+ } catch (error) {
96
+ logger.error(`Error rendering ${entry}: ${error.message}`);
97
+ }
98
+ store.set({
99
+ id,
100
+ data: parsedData,
101
+ body,
102
+ filePath: relativePath,
103
+ digest,
104
+ rendered
105
+ });
106
+ if (rendered?.metadata?.imagePaths?.length) {
107
+ store.addAssetImports(rendered.metadata.imagePaths, relativePath);
108
+ }
109
+ } else if ("contentModuleTypes" in entryType) {
110
+ store.set({
111
+ id,
112
+ data: parsedData,
113
+ body,
114
+ filePath: relativePath,
115
+ digest,
116
+ deferredRender: true
117
+ });
118
+ } else {
119
+ store.set({ id, data: parsedData, body, filePath: relativePath, digest });
120
+ }
121
+ fileToIdMap.set(filePath, id);
122
+ }
123
+ const entryConfigByExt = getEntryConfigByExtMap([
124
+ ...settings.contentEntryTypes,
125
+ ...settings.dataEntryTypes
126
+ ]);
127
+ const baseDir = globOptions.base ? new URL(globOptions.base, settings.config.root) : settings.config.root;
128
+ if (!baseDir.pathname.endsWith("/")) {
129
+ baseDir.pathname = `${baseDir.pathname}/`;
130
+ }
131
+ const files = await fastGlob(globOptions.pattern, {
132
+ cwd: fileURLToPath(baseDir)
133
+ });
134
+ function configForFile(file) {
135
+ const ext = file.split(".").at(-1);
136
+ if (!ext) {
137
+ logger.warn(`No extension found for ${file}`);
138
+ return;
139
+ }
140
+ return entryConfigByExt.get(`.${ext}`);
141
+ }
142
+ const limit = pLimit(10);
143
+ const skippedFiles = [];
144
+ const contentDir = new URL("content/", settings.config.srcDir);
145
+ function isInContentDir(file) {
146
+ const fileUrl = new URL(file, baseDir);
147
+ return fileUrl.href.startsWith(contentDir.href);
148
+ }
149
+ const configFiles = new Set(
150
+ ["config.js", "config.ts", "config.mjs"].map((file) => new URL(file, contentDir).href)
151
+ );
152
+ function isConfigFile(file) {
153
+ const fileUrl = new URL(file, baseDir);
154
+ return configFiles.has(fileUrl.href);
155
+ }
156
+ await Promise.all(
157
+ files.map((entry) => {
158
+ if (isConfigFile(entry)) {
159
+ return;
160
+ }
161
+ if (isInContentDir(entry)) {
162
+ skippedFiles.push(entry);
163
+ return;
164
+ }
165
+ return limit(async () => {
166
+ const entryType = configForFile(entry);
167
+ await syncData(entry, baseDir, entryType);
168
+ });
169
+ })
170
+ );
171
+ const skipCount = skippedFiles.length;
172
+ if (skipCount > 0) {
173
+ logger.warn(`The glob() loader cannot be used for files in ${bold("src/content")}.`);
174
+ if (skipCount > 10) {
175
+ logger.warn(
176
+ `Skipped ${green(skippedFiles.length)} files that matched ${green(globOptions.pattern)}.`
177
+ );
178
+ } else {
179
+ logger.warn(`Skipped the following files that matched ${green(globOptions.pattern)}:`);
180
+ skippedFiles.forEach((file) => logger.warn(`\u2022 ${green(file)}`));
181
+ }
182
+ }
183
+ untouchedEntries.forEach((id) => store.delete(id));
184
+ if (!watcher) {
185
+ return;
186
+ }
187
+ const matcher = micromatch.makeRe(globOptions.pattern);
188
+ const matchesGlob = (entry) => !entry.startsWith("../") && matcher.test(entry);
189
+ const basePath = fileURLToPath(baseDir);
190
+ async function onChange(changedPath) {
191
+ const entry = posixRelative(basePath, changedPath);
192
+ if (!matchesGlob(entry)) {
193
+ return;
194
+ }
195
+ const entryType = configForFile(changedPath);
196
+ const baseUrl = pathToFileURL(basePath);
197
+ await syncData(entry, baseUrl, entryType);
198
+ logger.info(`Reloaded data from ${green(entry)}`);
199
+ }
200
+ watcher.on("change", onChange);
201
+ watcher.on("add", onChange);
202
+ watcher.on("unlink", async (deletedPath) => {
203
+ const entry = posixRelative(basePath, deletedPath);
204
+ if (!matchesGlob(entry)) {
205
+ return;
206
+ }
207
+ const id = fileToIdMap.get(deletedPath);
208
+ if (id) {
209
+ store.delete(id);
210
+ fileToIdMap.delete(deletedPath);
211
+ }
212
+ });
213
+ }
214
+ };
215
+ }
216
+ export {
217
+ glob
218
+ };
@@ -0,0 +1,3 @@
1
+ export { file } from './file.js';
2
+ export { glob } from './glob.js';
3
+ export * from './types.js';
@@ -0,0 +1,7 @@
1
+ import { file } from "./file.js";
2
+ import { glob } from "./glob.js";
3
+ export * from "./types.js";
4
+ export {
5
+ file,
6
+ glob
7
+ };
@@ -0,0 +1,36 @@
1
+ import type { FSWatcher } from 'vite';
2
+ import type { ZodSchema } from 'zod';
3
+ import type { AstroIntegrationLogger, AstroSettings } from '../../@types/astro.js';
4
+ import type { MetaStore, ScopedDataStore } from '../data-store.js';
5
+ export interface ParseDataOptions<TData extends Record<string, unknown>> {
6
+ /** The ID of the entry. Unique per collection */
7
+ id: string;
8
+ /** The raw, unvalidated data of the entry */
9
+ data: TData;
10
+ /** An optional file path, where the entry represents a local file. */
11
+ filePath?: string;
12
+ }
13
+ export interface LoaderContext {
14
+ /** The unique name of the collection */
15
+ collection: string;
16
+ /** A database abstraction to store the actual data */
17
+ store: ScopedDataStore;
18
+ /** A simple KV store, designed for things like sync tokens */
19
+ meta: MetaStore;
20
+ logger: AstroIntegrationLogger;
21
+ settings: AstroSettings;
22
+ /** Validates and parses the data according to the collection schema */
23
+ parseData<TData extends Record<string, unknown>>(props: ParseDataOptions<TData>): Promise<TData>;
24
+ /** Generates a non-cryptographic content digest. This can be used to check if the data has changed */
25
+ generateDigest(data: Record<string, unknown> | string): string;
26
+ /** When running in dev, this is a filesystem watcher that can be used to trigger updates */
27
+ watcher?: FSWatcher;
28
+ }
29
+ export interface Loader {
30
+ /** Unique name of the loader, e.g. the npm package name */
31
+ name: string;
32
+ /** Do the actual loading of the data */
33
+ load: (context: LoaderContext) => Promise<void>;
34
+ /** Optionally, define the schema of the data. Will be overridden by user-defined schema */
35
+ schema?: ZodSchema | Promise<ZodSchema> | (() => ZodSchema | Promise<ZodSchema>);
36
+ }
File without changes
@@ -1,5 +1,7 @@
1
1
  import type { MarkdownHeading } from '@astrojs/markdown-remark';
2
+ import { z } from 'zod';
2
3
  import { type AstroComponentFactory } from '../runtime/server/index.js';
4
+ import { type DataEntry } from './data-store.js';
3
5
  import type { ContentLookupMap } from './utils.js';
4
6
  type LazyImport = () => Promise<any>;
5
7
  type GlobResult = Record<string, LazyImport>;
@@ -16,9 +18,10 @@ export declare function createGetCollection({ contentCollectionToEntryMap, dataC
16
18
  getRenderEntryImport: GetEntryImport;
17
19
  cacheEntriesByCollection: Map<string, any[]>;
18
20
  }): (collection: string, filter?: (entry: any) => unknown) => Promise<any[]>;
19
- export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImport, }: {
21
+ export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImport, collectionNames, }: {
20
22
  getEntryImport: GetEntryImport;
21
23
  getRenderEntryImport: GetEntryImport;
24
+ collectionNames: Set<string>;
22
25
  }): (collection: string, slug: string) => Promise<{
23
26
  id: any;
24
27
  slug: any;
@@ -27,13 +30,14 @@ export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImp
27
30
  data: any;
28
31
  render(): Promise<RenderResult>;
29
32
  } | undefined>;
30
- export declare function createGetDataEntryById({ getEntryImport }: {
33
+ export declare function createGetDataEntryById({ getEntryImport, collectionNames, }: {
31
34
  getEntryImport: GetEntryImport;
35
+ collectionNames: Set<string>;
32
36
  }): (collection: string, id: string) => Promise<{
33
37
  id: any;
34
38
  collection: any;
35
39
  data: any;
36
- }>;
40
+ } | undefined>;
37
41
  type ContentEntryResult = {
38
42
  id: string;
39
43
  slug: string;
@@ -54,9 +58,10 @@ type EntryLookupObject = {
54
58
  collection: string;
55
59
  slug: string;
56
60
  };
57
- export declare function createGetEntry({ getEntryImport, getRenderEntryImport, }: {
61
+ export declare function createGetEntry({ getEntryImport, getRenderEntryImport, collectionNames, }: {
58
62
  getEntryImport: GetEntryImport;
59
63
  getRenderEntryImport: GetEntryImport;
64
+ collectionNames: Set<string>;
60
65
  }): (collectionOrLookupObject: string | EntryLookupObject, _lookupId?: string) => Promise<ContentEntryResult | DataEntryResult | undefined>;
61
66
  export declare function createGetEntries(getEntry: ReturnType<typeof createGetEntry>): (entries: {
62
67
  collection: string;
@@ -70,15 +75,48 @@ type RenderResult = {
70
75
  headings: MarkdownHeading[];
71
76
  remarkPluginFrontmatter: Record<string, any>;
72
77
  };
78
+ export declare function renderEntry(entry: DataEntry | {
79
+ render: () => Promise<{
80
+ Content: AstroComponentFactory;
81
+ }>;
82
+ }): Promise<{
83
+ Content: AstroComponentFactory;
84
+ } | {
85
+ Content: any;
86
+ headings: any;
87
+ remarkPluginFrontmatter: any;
88
+ }>;
73
89
  export declare function createReference({ lookupMap }: {
74
90
  lookupMap: ContentLookupMap;
75
- }): (collection: string) => import("zod").ZodEffects<import("zod").ZodString, {
91
+ }): (collection: string) => z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodObject<{
92
+ id: z.ZodString;
93
+ collection: z.ZodString;
94
+ }, "strip", z.ZodTypeAny, {
95
+ id: string;
96
+ collection: string;
97
+ }, {
98
+ id: string;
99
+ collection: string;
100
+ }>, z.ZodObject<{
101
+ slug: z.ZodString;
102
+ collection: z.ZodString;
103
+ }, "strip", z.ZodTypeAny, {
104
+ collection: string;
76
105
  slug: string;
106
+ }, {
107
+ collection: string;
108
+ slug: string;
109
+ }>]>, {
110
+ id: string;
77
111
  collection: string;
78
- id?: undefined;
79
112
  } | {
113
+ slug: string;
114
+ collection: string;
115
+ } | undefined, string | {
80
116
  id: string;
81
117
  collection: string;
82
- slug?: undefined;
83
- } | undefined, string>;
118
+ } | {
119
+ collection: string;
120
+ slug: string;
121
+ }>;
84
122
  export {};