astro 4.13.4 → 4.14.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.
Files changed (98) hide show
  1. package/components/Code.astro +9 -0
  2. package/dist/@types/astro.d.ts +249 -1
  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/assets/endpoint/node.js +1 -1
  7. package/dist/assets/utils/resolveImports.d.ts +9 -0
  8. package/dist/assets/utils/resolveImports.js +22 -0
  9. package/dist/cli/add/index.d.ts +2 -2
  10. package/dist/cli/add/index.js +2 -2
  11. package/dist/cli/build/index.d.ts +2 -2
  12. package/dist/cli/build/index.js +5 -1
  13. package/dist/cli/check/index.d.ts +2 -2
  14. package/dist/cli/check/index.js +5 -2
  15. package/dist/cli/db/index.d.ts +4 -3
  16. package/dist/cli/db/index.js +10 -3
  17. package/dist/cli/dev/index.d.ts +2 -2
  18. package/dist/cli/dev/index.js +1 -0
  19. package/dist/cli/docs/index.d.ts +2 -2
  20. package/dist/cli/flags.d.ts +3 -1
  21. package/dist/cli/flags.js +2 -1
  22. package/dist/cli/index.d.ts +1 -1
  23. package/dist/cli/index.js +26 -13
  24. package/dist/cli/info/index.d.ts +2 -2
  25. package/dist/cli/preferences/index.d.ts +2 -2
  26. package/dist/cli/preferences/index.js +1 -1
  27. package/dist/cli/preview/index.d.ts +2 -2
  28. package/dist/cli/sync/index.d.ts +2 -2
  29. package/dist/cli/sync/index.js +5 -2
  30. package/dist/cli/telemetry/index.d.ts +2 -2
  31. package/dist/content/consts.d.ts +16 -2
  32. package/dist/content/consts.js +32 -2
  33. package/dist/content/content-layer.d.ts +40 -0
  34. package/dist/content/content-layer.js +253 -0
  35. package/dist/content/data-store.d.ts +54 -0
  36. package/dist/content/data-store.js +72 -0
  37. package/dist/content/loaders/file.d.ts +7 -0
  38. package/dist/content/loaders/file.js +72 -0
  39. package/dist/content/loaders/glob.d.ts +25 -0
  40. package/dist/content/loaders/glob.js +218 -0
  41. package/dist/content/loaders/index.d.ts +3 -0
  42. package/dist/content/loaders/index.js +7 -0
  43. package/dist/content/loaders/types.d.ts +36 -0
  44. package/dist/content/loaders/types.js +0 -0
  45. package/dist/content/mutable-data-store.d.ts +77 -0
  46. package/dist/content/mutable-data-store.js +269 -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 +123 -35
  50. package/dist/content/utils.d.ts +307 -2
  51. package/dist/content/utils.js +101 -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/build/index.js +14 -7
  55. package/dist/core/build/plugins/plugin-ssr.js +32 -4
  56. package/dist/core/config/config.d.ts +2 -5
  57. package/dist/core/config/config.js +0 -12
  58. package/dist/core/config/index.d.ts +1 -1
  59. package/dist/core/config/index.js +0 -2
  60. package/dist/core/config/schema.d.ts +34 -0
  61. package/dist/core/config/schema.js +6 -2
  62. package/dist/core/config/settings.js +5 -3
  63. package/dist/core/constants.js +1 -1
  64. package/dist/core/create-vite.js +1 -1
  65. package/dist/core/dev/container.js +2 -1
  66. package/dist/core/dev/dev.js +33 -3
  67. package/dist/core/dev/restart.js +25 -10
  68. package/dist/core/errors/errors-data.d.ts +21 -0
  69. package/dist/core/errors/errors-data.js +13 -0
  70. package/dist/core/index.js +1 -1
  71. package/dist/core/logger/vite.js +1 -1
  72. package/dist/core/messages.js +2 -2
  73. package/dist/core/preview/static-preview-server.js +1 -1
  74. package/dist/core/routing/manifest/create.js +1 -1
  75. package/dist/core/sync/constants.d.ts +1 -0
  76. package/dist/core/sync/constants.js +4 -0
  77. package/dist/core/sync/index.d.ts +12 -4
  78. package/dist/core/sync/index.js +54 -24
  79. package/dist/core/sync/write-files.d.ts +4 -0
  80. package/dist/core/sync/write-files.js +69 -0
  81. package/dist/core/util.js +1 -1
  82. package/dist/env/sync.js +6 -4
  83. package/dist/integrations/hooks.d.ts +7 -1
  84. package/dist/integrations/hooks.js +54 -0
  85. package/dist/preferences/index.d.ts +1 -1
  86. package/dist/preferences/index.js +2 -2
  87. package/dist/runtime/server/render/server-islands.js +6 -4
  88. package/dist/vite-plugin-astro-server/response.js +1 -1
  89. package/dist/vite-plugin-env/index.d.ts +3 -1
  90. package/dist/vite-plugin-env/index.js +11 -1
  91. package/dist/vite-plugin-markdown/content-entry-type.js +25 -2
  92. package/dist/vite-plugin-scanner/index.js +15 -5
  93. package/package.json +10 -5
  94. package/templates/content/module.mjs +6 -1
  95. package/templates/content/types.d.ts +18 -5
  96. package/types/content.d.ts +34 -1
  97. package/dist/core/sync/setup-env-ts.d.ts +0 -8
  98. package/dist/core/sync/setup-env-ts.js +0 -79
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
+ import { parseArgs } from "node:util";
1
2
  import * as colors from "kleur/colors";
2
- import yargs from "yargs-parser";
3
3
  import { ASTRO_VERSION } from "../core/constants.js";
4
4
  async function printAstroHelp() {
5
5
  const { printHelp } = await import("../core/messages.js");
@@ -43,9 +43,9 @@ function printVersion() {
43
43
  console.log();
44
44
  console.log(` ${colors.bgGreen(colors.black(` astro `))} ${colors.green(`v${ASTRO_VERSION}`)}`);
45
45
  }
46
- function resolveCommand(flags) {
47
- const cmd = flags._[2];
48
- if (flags.version) return "version";
46
+ function resolveCommand(args) {
47
+ const cmd = args.positionals[2];
48
+ if (args.values.version) return "version";
49
49
  const supportedCommands = /* @__PURE__ */ new Set([
50
50
  "add",
51
51
  "sync",
@@ -68,7 +68,8 @@ function resolveCommand(flags) {
68
68
  }
69
69
  return "help";
70
70
  }
71
- async function runCommand(cmd, flags) {
71
+ async function runCommand(cmd, args) {
72
+ const flags = args.values;
72
73
  switch (cmd) {
73
74
  case "help":
74
75
  await printAstroHelp();
@@ -88,7 +89,7 @@ async function runCommand(cmd, flags) {
88
89
  }
89
90
  case "telemetry": {
90
91
  const { update } = await import("./telemetry/index.js");
91
- const subcommand = flags._[3]?.toString();
92
+ const subcommand = args.positionals[3];
92
93
  await update(subcommand, { flags });
93
94
  return;
94
95
  }
@@ -99,7 +100,7 @@ async function runCommand(cmd, flags) {
99
100
  }
100
101
  case "preferences": {
101
102
  const { preferences } = await import("./preferences/index.js");
102
- const [subcommand, key, value] = flags._.slice(3).map((v) => v.toString());
103
+ const [subcommand, key, value] = args.positionals.slice(3);
103
104
  const exitCode = await preferences(subcommand, key, value, { flags });
104
105
  return process.exit(exitCode);
105
106
  }
@@ -113,7 +114,7 @@ async function runCommand(cmd, flags) {
113
114
  switch (cmd) {
114
115
  case "add": {
115
116
  const { add } = await import("./add/index.js");
116
- const packages = flags._.slice(3);
117
+ const packages = args.positionals.slice(3);
117
118
  await add(packages, { flags });
118
119
  return;
119
120
  }
@@ -123,7 +124,7 @@ async function runCommand(cmd, flags) {
123
124
  case "link":
124
125
  case "init": {
125
126
  const { db } = await import("./db/index.js");
126
- await db({ flags });
127
+ await db({ positionals: args.positionals, flags });
127
128
  return;
128
129
  }
129
130
  case "dev": {
@@ -161,11 +162,23 @@ async function runCommand(cmd, flags) {
161
162
  }
162
163
  throw new Error(`Error running ${cmd} -- no command found.`);
163
164
  }
164
- async function cli(args) {
165
- const flags = yargs(args, { boolean: ["global"], alias: { g: "global" } });
166
- const cmd = resolveCommand(flags);
165
+ async function cli(argv) {
166
+ const args = parseArgs({
167
+ args: argv,
168
+ allowPositionals: true,
169
+ strict: false,
170
+ options: {
171
+ global: { type: "boolean", short: "g" },
172
+ host: { type: "string" },
173
+ // Can be boolean too, which is covered by `strict: false`
174
+ open: { type: "string" }
175
+ // Can be boolean too, which is covered by `strict: false`
176
+ // TODO: Add more flags here
177
+ }
178
+ });
179
+ const cmd = resolveCommand(args);
167
180
  try {
168
- await runCommand(cmd, flags);
181
+ await runCommand(cmd, args);
169
182
  } catch (err) {
170
183
  const { throwAndExit } = await import("./throw-and-exit.js");
171
184
  await throwAndExit(cmd, err);
@@ -1,7 +1,7 @@
1
- import type yargs from 'yargs-parser';
2
1
  import type { AstroConfig, AstroUserConfig } from '../../@types/astro.js';
2
+ import { type Flags } from '../flags.js';
3
3
  interface InfoOptions {
4
- flags: yargs.Arguments;
4
+ flags: Flags;
5
5
  }
6
6
  export declare function getInfoOutput({ userConfig, print, }: {
7
7
  userConfig: AstroUserConfig | AstroConfig;
@@ -1,6 +1,6 @@
1
- import type yargs from 'yargs-parser';
1
+ import { type Flags } from '../flags.js';
2
2
  interface PreferencesOptions {
3
- flags: yargs.Arguments;
3
+ flags: Flags;
4
4
  }
5
5
  declare const PREFERENCES_SUBCOMMANDS: readonly ["get", "set", "enable", "disable", "delete", "reset", "list"];
6
6
  export type Subcommand = (typeof PREFERENCES_SUBCOMMANDS)[number];
@@ -55,7 +55,7 @@ async function preferences(subcommand, key, value, { flags }) {
55
55
  const settings = await createSettings(astroConfig, fileURLToPath(astroConfig.root));
56
56
  const opts = {
57
57
  location: flags.global ? "global" : void 0,
58
- json: flags.json
58
+ json: !!flags.json
59
59
  };
60
60
  if (subcommand === "list") {
61
61
  return listPreferences(settings, opts);
@@ -1,6 +1,6 @@
1
- import type yargs from 'yargs-parser';
1
+ import { type Flags } from '../flags.js';
2
2
  interface PreviewOptions {
3
- flags: yargs.Arguments;
3
+ flags: Flags;
4
4
  }
5
5
  export declare function preview({ flags }: PreviewOptions): Promise<import("../../@types/astro.js").PreviewServer | undefined>;
6
6
  export {};
@@ -1,6 +1,6 @@
1
- import type yargs from 'yargs-parser';
1
+ import { type Flags } from '../flags.js';
2
2
  interface SyncOptions {
3
- flags: yargs.Arguments;
3
+ flags: Flags;
4
4
  }
5
5
  export declare function sync({ flags }: SyncOptions): Promise<0 | 1>;
6
6
  export {};
@@ -7,14 +7,17 @@ async function sync({ flags }) {
7
7
  commandName: "astro sync",
8
8
  usage: "[...flags]",
9
9
  tables: {
10
- Flags: [["--help (-h)", "See all available flags."]]
10
+ Flags: [
11
+ ["--force", "Clear the content layer cache, forcing a full rebuild."],
12
+ ["--help (-h)", "See all available flags."]
13
+ ]
11
14
  },
12
15
  description: `Generates TypeScript types for all Astro modules.`
13
16
  });
14
17
  return 0;
15
18
  }
16
19
  try {
17
- await _sync({ inlineConfig: flagsToAstroInlineConfig(flags), telemetry: true });
20
+ await _sync(flagsToAstroInlineConfig(flags), { telemetry: true });
18
21
  return 0;
19
22
  } catch (_) {
20
23
  return 1;
@@ -1,6 +1,6 @@
1
- import type yargs from 'yargs-parser';
1
+ import { type Flags } from '../flags.js';
2
2
  interface TelemetryOptions {
3
- flags: yargs.Arguments;
3
+ flags: Flags;
4
4
  }
5
5
  export declare function notify(): Promise<void>;
6
6
  export declare function update(subcommand: string, { flags }: TelemetryOptions): Promise<void>;
@@ -2,10 +2,24 @@ export declare const PROPAGATED_ASSET_FLAG = "astroPropagatedAssets";
2
2
  export declare const CONTENT_RENDER_FLAG = "astroRenderContent";
3
3
  export declare const CONTENT_FLAG = "astroContentCollectionEntry";
4
4
  export declare const DATA_FLAG = "astroDataCollectionEntry";
5
+ export declare const CONTENT_IMAGE_FLAG = "astroContentImageFlag";
6
+ export declare const CONTENT_MODULE_FLAG = "astroContentModuleFlag";
5
7
  export declare const VIRTUAL_MODULE_ID = "astro:content";
6
8
  export declare const RESOLVED_VIRTUAL_MODULE_ID: string;
9
+ export declare const DATA_STORE_VIRTUAL_ID = "astro:data-layer-content";
10
+ export declare const RESOLVED_DATA_STORE_VIRTUAL_ID: string;
11
+ export declare const MODULES_MJS_ID = "astro:content-module-imports";
12
+ export declare const MODULES_MJS_VIRTUAL_ID: string;
13
+ export declare const DEFERRED_MODULE = "astro:content-layer-deferred-module";
14
+ export declare const ASSET_IMPORTS_VIRTUAL_ID = "astro:asset-imports";
15
+ export declare const ASSET_IMPORTS_RESOLVED_STUB_ID: string;
7
16
  export declare const LINKS_PLACEHOLDER = "@@ASTRO-LINKS@@";
8
17
  export declare const STYLES_PLACEHOLDER = "@@ASTRO-STYLES@@";
9
18
  export declare const SCRIPTS_PLACEHOLDER = "@@ASTRO-SCRIPTS@@";
10
- export declare const CONTENT_FLAGS: readonly ["astroContentCollectionEntry", "astroRenderContent", "astroDataCollectionEntry", "astroPropagatedAssets"];
11
- export declare const CONTENT_TYPES_FILE = "types.d.ts";
19
+ export declare const IMAGE_IMPORT_PREFIX = "__ASTRO_IMAGE_";
20
+ export declare const CONTENT_FLAGS: readonly ["astroContentCollectionEntry", "astroRenderContent", "astroDataCollectionEntry", "astroPropagatedAssets", "astroContentImageFlag", "astroContentModuleFlag"];
21
+ export declare const CONTENT_TYPES_FILE = "astro/content.d.ts";
22
+ export declare const DATA_STORE_FILE = "data-store.json";
23
+ export declare const ASSET_IMPORTS_FILE = "assets.mjs";
24
+ export declare const MODULES_IMPORTS_FILE = "modules.mjs";
25
+ export declare const CONTENT_LAYER_TYPE = "content_layer";
@@ -2,26 +2,56 @@ const PROPAGATED_ASSET_FLAG = "astroPropagatedAssets";
2
2
  const CONTENT_RENDER_FLAG = "astroRenderContent";
3
3
  const CONTENT_FLAG = "astroContentCollectionEntry";
4
4
  const DATA_FLAG = "astroDataCollectionEntry";
5
+ const CONTENT_IMAGE_FLAG = "astroContentImageFlag";
6
+ const CONTENT_MODULE_FLAG = "astroContentModuleFlag";
5
7
  const VIRTUAL_MODULE_ID = "astro:content";
6
8
  const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
9
+ const DATA_STORE_VIRTUAL_ID = "astro:data-layer-content";
10
+ const RESOLVED_DATA_STORE_VIRTUAL_ID = "\0" + DATA_STORE_VIRTUAL_ID;
11
+ const MODULES_MJS_ID = "astro:content-module-imports";
12
+ const MODULES_MJS_VIRTUAL_ID = "\0" + MODULES_MJS_ID;
13
+ const DEFERRED_MODULE = "astro:content-layer-deferred-module";
14
+ const ASSET_IMPORTS_VIRTUAL_ID = "astro:asset-imports";
15
+ const ASSET_IMPORTS_RESOLVED_STUB_ID = "\0" + ASSET_IMPORTS_VIRTUAL_ID;
7
16
  const LINKS_PLACEHOLDER = "@@ASTRO-LINKS@@";
8
17
  const STYLES_PLACEHOLDER = "@@ASTRO-STYLES@@";
9
18
  const SCRIPTS_PLACEHOLDER = "@@ASTRO-SCRIPTS@@";
19
+ const IMAGE_IMPORT_PREFIX = "__ASTRO_IMAGE_";
10
20
  const CONTENT_FLAGS = [
11
21
  CONTENT_FLAG,
12
22
  CONTENT_RENDER_FLAG,
13
23
  DATA_FLAG,
14
- PROPAGATED_ASSET_FLAG
24
+ PROPAGATED_ASSET_FLAG,
25
+ CONTENT_IMAGE_FLAG,
26
+ CONTENT_MODULE_FLAG
15
27
  ];
16
- const CONTENT_TYPES_FILE = "types.d.ts";
28
+ const CONTENT_TYPES_FILE = "astro/content.d.ts";
29
+ const DATA_STORE_FILE = "data-store.json";
30
+ const ASSET_IMPORTS_FILE = "assets.mjs";
31
+ const MODULES_IMPORTS_FILE = "modules.mjs";
32
+ const CONTENT_LAYER_TYPE = "content_layer";
17
33
  export {
34
+ ASSET_IMPORTS_FILE,
35
+ ASSET_IMPORTS_RESOLVED_STUB_ID,
36
+ ASSET_IMPORTS_VIRTUAL_ID,
18
37
  CONTENT_FLAG,
19
38
  CONTENT_FLAGS,
39
+ CONTENT_IMAGE_FLAG,
40
+ CONTENT_LAYER_TYPE,
41
+ CONTENT_MODULE_FLAG,
20
42
  CONTENT_RENDER_FLAG,
21
43
  CONTENT_TYPES_FILE,
22
44
  DATA_FLAG,
45
+ DATA_STORE_FILE,
46
+ DATA_STORE_VIRTUAL_ID,
47
+ DEFERRED_MODULE,
48
+ IMAGE_IMPORT_PREFIX,
23
49
  LINKS_PLACEHOLDER,
50
+ MODULES_IMPORTS_FILE,
51
+ MODULES_MJS_ID,
52
+ MODULES_MJS_VIRTUAL_ID,
24
53
  PROPAGATED_ASSET_FLAG,
54
+ RESOLVED_DATA_STORE_VIRTUAL_ID,
25
55
  RESOLVED_VIRTUAL_MODULE_ID,
26
56
  SCRIPTS_PLACEHOLDER,
27
57
  STYLES_PLACEHOLDER,
@@ -0,0 +1,40 @@
1
+ import type { FSWatcher } from 'vite';
2
+ import type { AstroSettings } from '../@types/astro.js';
3
+ import type { Logger } from '../core/logger/core.js';
4
+ import type { LoaderContext } from './loaders/types.js';
5
+ import type { MutableDataStore } from './mutable-data-store.js';
6
+ export interface ContentLayerOptions {
7
+ store: MutableDataStore;
8
+ settings: AstroSettings;
9
+ logger: Logger;
10
+ watcher?: FSWatcher;
11
+ }
12
+ export declare class ContentLayer {
13
+ #private;
14
+ constructor({ settings, logger, store, watcher }: ContentLayerOptions);
15
+ /**
16
+ * Whether the content layer is currently loading content
17
+ */
18
+ get loading(): boolean;
19
+ /**
20
+ * Watch for changes to the content config and trigger a sync when it changes.
21
+ */
22
+ watchContentConfig(): void;
23
+ unwatchContentConfig(): void;
24
+ /**
25
+ * Run the `load()` method of each collection's loader, which will load the data and save it in the data store.
26
+ * The loader itself is responsible for deciding whether this will clear and reload the full collection, or
27
+ * perform an incremental update. After the data is loaded, the data store is written to disk.
28
+ */
29
+ sync(): Promise<void>;
30
+ regenerateCollectionFileManifest(): Promise<void>;
31
+ }
32
+ export declare function simpleLoader<TData extends {
33
+ id: string;
34
+ }>(handler: () => Array<TData> | Promise<Array<TData>>, context: LoaderContext): Promise<void>;
35
+ export declare const globalContentLayer: {
36
+ initialized: () => boolean;
37
+ init: (options: ContentLayerOptions) => ContentLayer;
38
+ get: () => ContentLayer;
39
+ dispose: () => void;
40
+ };
@@ -0,0 +1,253 @@
1
+ import { promises as fs, existsSync } from "node:fs";
2
+ import { isAbsolute } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import xxhash from "xxhash-wasm";
5
+ import { AstroUserError } from "../core/errors/errors.js";
6
+ import {
7
+ ASSET_IMPORTS_FILE,
8
+ CONTENT_LAYER_TYPE,
9
+ DATA_STORE_FILE,
10
+ MODULES_IMPORTS_FILE
11
+ } from "./consts.js";
12
+ import { getEntryDataAndImages, globalContentConfigObserver, posixRelative } from "./utils.js";
13
+ class ContentLayer {
14
+ #logger;
15
+ #store;
16
+ #settings;
17
+ #watcher;
18
+ #lastConfigDigest;
19
+ #unsubscribe;
20
+ #generateDigest;
21
+ #loading = false;
22
+ constructor({ settings, logger, store, watcher }) {
23
+ watcher?.setMaxListeners(50);
24
+ this.#logger = logger;
25
+ this.#store = store;
26
+ this.#settings = settings;
27
+ this.#watcher = watcher;
28
+ }
29
+ /**
30
+ * Whether the content layer is currently loading content
31
+ */
32
+ get loading() {
33
+ return this.#loading;
34
+ }
35
+ /**
36
+ * Watch for changes to the content config and trigger a sync when it changes.
37
+ */
38
+ watchContentConfig() {
39
+ this.#unsubscribe?.();
40
+ this.#unsubscribe = globalContentConfigObserver.subscribe(async (ctx) => {
41
+ if (!this.#loading && ctx.status === "loaded" && ctx.config.digest !== this.#lastConfigDigest) {
42
+ this.sync();
43
+ }
44
+ });
45
+ }
46
+ unwatchContentConfig() {
47
+ this.#unsubscribe?.();
48
+ }
49
+ /**
50
+ * Run the `load()` method of each collection's loader, which will load the data and save it in the data store.
51
+ * The loader itself is responsible for deciding whether this will clear and reload the full collection, or
52
+ * perform an incremental update. After the data is loaded, the data store is written to disk.
53
+ */
54
+ async sync() {
55
+ if (this.#loading) {
56
+ return;
57
+ }
58
+ this.#loading = true;
59
+ try {
60
+ await this.#doSync();
61
+ } finally {
62
+ this.#loading = false;
63
+ }
64
+ }
65
+ async #getGenerateDigest() {
66
+ if (this.#generateDigest) {
67
+ return this.#generateDigest;
68
+ }
69
+ const { h64ToString } = await xxhash();
70
+ this.#generateDigest = (data) => {
71
+ const dataString = typeof data === "string" ? data : JSON.stringify(data);
72
+ return h64ToString(dataString);
73
+ };
74
+ return this.#generateDigest;
75
+ }
76
+ async #getLoaderContext({
77
+ collectionName,
78
+ loaderName = "content",
79
+ parseData
80
+ }) {
81
+ return {
82
+ collection: collectionName,
83
+ store: this.#store.scopedStore(collectionName),
84
+ meta: this.#store.metaStore(collectionName),
85
+ logger: this.#logger.forkIntegrationLogger(loaderName),
86
+ settings: this.#settings,
87
+ parseData,
88
+ generateDigest: await this.#getGenerateDigest(),
89
+ watcher: this.#watcher
90
+ };
91
+ }
92
+ async #doSync() {
93
+ const contentConfig = globalContentConfigObserver.get();
94
+ const logger = this.#logger.forkIntegrationLogger("content");
95
+ if (contentConfig?.status !== "loaded") {
96
+ logger.debug("Content config not loaded, skipping sync");
97
+ return;
98
+ }
99
+ if (!this.#settings.config.experimental.contentLayer) {
100
+ const contentLayerCollections = Object.entries(contentConfig.config.collections).filter(
101
+ ([_, collection]) => collection.type === CONTENT_LAYER_TYPE
102
+ );
103
+ if (contentLayerCollections.length > 0) {
104
+ throw new AstroUserError(
105
+ `The following collections have a loader defined, but the content layer is not enabled: ${contentLayerCollections.map(([title]) => title).join(", ")}.`,
106
+ "To enable the Content Layer API, set `experimental: { contentLayer: true }` in your Astro config file."
107
+ );
108
+ }
109
+ return;
110
+ }
111
+ logger.info("Syncing content");
112
+ const { digest: currentConfigDigest } = contentConfig.config;
113
+ this.#lastConfigDigest = currentConfigDigest;
114
+ const previousConfigDigest = await this.#store.metaStore().get("config-digest");
115
+ if (currentConfigDigest && previousConfigDigest !== currentConfigDigest) {
116
+ logger.info("Content config changed, clearing cache");
117
+ this.#store.clearAll();
118
+ await this.#store.metaStore().set("config-digest", currentConfigDigest);
119
+ }
120
+ await Promise.all(
121
+ Object.entries(contentConfig.config.collections).map(async ([name, collection]) => {
122
+ if (collection.type !== CONTENT_LAYER_TYPE) {
123
+ return;
124
+ }
125
+ let { schema } = collection;
126
+ if (!schema && typeof collection.loader === "object") {
127
+ schema = collection.loader.schema;
128
+ if (typeof schema === "function") {
129
+ schema = await schema();
130
+ }
131
+ }
132
+ const collectionWithResolvedSchema = { ...collection, schema };
133
+ const parseData = async ({ id, data, filePath = "" }) => {
134
+ const { imageImports, data: parsedData } = await getEntryDataAndImages(
135
+ {
136
+ id,
137
+ collection: name,
138
+ unvalidatedData: data,
139
+ _internal: {
140
+ rawData: void 0,
141
+ filePath
142
+ }
143
+ },
144
+ collectionWithResolvedSchema,
145
+ false
146
+ );
147
+ if (imageImports?.length) {
148
+ this.#store.addAssetImports(
149
+ imageImports,
150
+ // This path may already be relative, if we're re-parsing an existing entry
151
+ isAbsolute(filePath) ? posixRelative(fileURLToPath(this.#settings.config.root), filePath) : filePath
152
+ );
153
+ }
154
+ return parsedData;
155
+ };
156
+ const context = await this.#getLoaderContext({
157
+ collectionName: name,
158
+ parseData,
159
+ loaderName: collection.loader.name
160
+ });
161
+ if (typeof collection.loader === "function") {
162
+ return simpleLoader(collection.loader, context);
163
+ }
164
+ if (!collection.loader.load) {
165
+ throw new Error(`Collection loader for ${name} does not have a load method`);
166
+ }
167
+ return collection.loader.load(context);
168
+ })
169
+ );
170
+ if (!existsSync(this.#settings.config.cacheDir)) {
171
+ await fs.mkdir(this.#settings.config.cacheDir, { recursive: true });
172
+ }
173
+ const cacheFile = new URL(DATA_STORE_FILE, this.#settings.config.cacheDir);
174
+ await this.#store.writeToDisk(cacheFile);
175
+ if (!existsSync(this.#settings.dotAstroDir)) {
176
+ await fs.mkdir(this.#settings.dotAstroDir, { recursive: true });
177
+ }
178
+ const assetImportsFile = new URL(ASSET_IMPORTS_FILE, this.#settings.dotAstroDir);
179
+ await this.#store.writeAssetImports(assetImportsFile);
180
+ const modulesImportsFile = new URL(MODULES_IMPORTS_FILE, this.#settings.dotAstroDir);
181
+ await this.#store.writeModuleImports(modulesImportsFile);
182
+ logger.info("Synced content");
183
+ if (this.#settings.config.experimental.contentIntellisense) {
184
+ await this.regenerateCollectionFileManifest();
185
+ }
186
+ }
187
+ async regenerateCollectionFileManifest() {
188
+ const collectionsManifest = new URL("collections/collections.json", this.#settings.dotAstroDir);
189
+ this.#logger.debug("content", "Regenerating collection file manifest");
190
+ if (existsSync(collectionsManifest)) {
191
+ try {
192
+ const collections = await fs.readFile(collectionsManifest, "utf-8");
193
+ const collectionsJson = JSON.parse(collections);
194
+ collectionsJson.entries ??= {};
195
+ for (const { hasSchema, name } of collectionsJson.collections) {
196
+ if (!hasSchema) {
197
+ continue;
198
+ }
199
+ const entries = this.#store.values(name);
200
+ if (!entries?.[0]?.filePath) {
201
+ continue;
202
+ }
203
+ for (const { filePath } of entries) {
204
+ if (!filePath) {
205
+ continue;
206
+ }
207
+ const key = new URL(filePath, this.#settings.config.root).href.toLowerCase();
208
+ collectionsJson.entries[key] = name;
209
+ }
210
+ }
211
+ await fs.writeFile(collectionsManifest, JSON.stringify(collectionsJson, null, 2));
212
+ } catch {
213
+ this.#logger.error("content", "Failed to regenerate collection file manifest");
214
+ }
215
+ }
216
+ this.#logger.debug("content", "Regenerated collection file manifest");
217
+ }
218
+ }
219
+ async function simpleLoader(handler, context) {
220
+ const data = await handler();
221
+ context.store.clear();
222
+ for (const raw of data) {
223
+ const item = await context.parseData({ id: raw.id, data: raw });
224
+ context.store.set({ id: raw.id, data: item });
225
+ }
226
+ }
227
+ function contentLayerSingleton() {
228
+ let instance = null;
229
+ return {
230
+ initialized: () => Boolean(instance),
231
+ init: (options) => {
232
+ instance?.unwatchContentConfig();
233
+ instance = new ContentLayer(options);
234
+ return instance;
235
+ },
236
+ get: () => {
237
+ if (!instance) {
238
+ throw new Error("Content layer not initialized");
239
+ }
240
+ return instance;
241
+ },
242
+ dispose: () => {
243
+ instance?.unwatchContentConfig();
244
+ instance = null;
245
+ }
246
+ };
247
+ }
248
+ const globalContentLayer = contentLayerSingleton();
249
+ export {
250
+ ContentLayer,
251
+ globalContentLayer,
252
+ simpleLoader
253
+ };
@@ -0,0 +1,54 @@
1
+ import type { MarkdownHeading } from '@astrojs/markdown-remark';
2
+ export interface RenderedContent {
3
+ /** Rendered HTML string. If present then `render(entry)` will return a component that renders this HTML. */
4
+ html: string;
5
+ metadata?: {
6
+ /** Any images that are present in this entry. Relative to the {@link DataEntry} filePath. */
7
+ imagePaths?: Array<string>;
8
+ /** Any headings that are present in this file. */
9
+ headings?: MarkdownHeading[];
10
+ /** Raw frontmatter, parsed parsed from the file. This may include data from remark plugins. */
11
+ frontmatter?: Record<string, any>;
12
+ /** Any other metadata that is present in this file. */
13
+ [key: string]: unknown;
14
+ };
15
+ }
16
+ export interface DataEntry<TData extends Record<string, unknown> = Record<string, unknown>> {
17
+ /** The ID of the entry. Unique per collection. */
18
+ id: string;
19
+ /** The parsed entry data */
20
+ data: TData;
21
+ /** The file path of the content, if applicable. Relative to the site root. */
22
+ filePath?: string;
23
+ /** The raw body of the content, if applicable. */
24
+ body?: string;
25
+ /** An optional content digest, to check if the content has changed. */
26
+ digest?: number | string;
27
+ /** The rendered content of the entry, if applicable. */
28
+ rendered?: RenderedContent;
29
+ /**
30
+ * If an entry is a deferred, its rendering phase is delegated to a virtual module during the runtime phase when calling `renderEntry`.
31
+ */
32
+ deferredRender?: boolean;
33
+ }
34
+ /**
35
+ * A read-only data store for content collections. This is used to retrieve data from the content layer at runtime.
36
+ * To add or modify data, use {@link MutableDataStore} instead.
37
+ */
38
+ export declare class DataStore {
39
+ protected _collections: Map<string, Map<string, any>>;
40
+ constructor();
41
+ get<T = DataEntry>(collectionName: string, key: string): T | undefined;
42
+ entries<T = DataEntry>(collectionName: string): Array<[id: string, T]>;
43
+ values<T = DataEntry>(collectionName: string): Array<T>;
44
+ keys(collectionName: string): Array<string>;
45
+ has(collectionName: string, key: string): boolean;
46
+ hasCollection(collectionName: string): boolean;
47
+ collections(): Map<string, Map<string, any>>;
48
+ /**
49
+ * Attempts to load a DataStore from the virtual module.
50
+ * This only works in Vite.
51
+ */
52
+ static fromModule(): Promise<DataStore>;
53
+ static fromMap(data: Map<string, Map<string, any>>): Promise<DataStore>;
54
+ }
@@ -0,0 +1,72 @@
1
+ import * as devalue from "devalue";
2
+ class DataStore {
3
+ _collections = /* @__PURE__ */ new Map();
4
+ constructor() {
5
+ this._collections = /* @__PURE__ */ new Map();
6
+ }
7
+ get(collectionName, key) {
8
+ return this._collections.get(collectionName)?.get(String(key));
9
+ }
10
+ entries(collectionName) {
11
+ const collection = this._collections.get(collectionName) ?? /* @__PURE__ */ new Map();
12
+ return [...collection.entries()];
13
+ }
14
+ values(collectionName) {
15
+ const collection = this._collections.get(collectionName) ?? /* @__PURE__ */ new Map();
16
+ return [...collection.values()];
17
+ }
18
+ keys(collectionName) {
19
+ const collection = this._collections.get(collectionName) ?? /* @__PURE__ */ new Map();
20
+ return [...collection.keys()];
21
+ }
22
+ has(collectionName, key) {
23
+ const collection = this._collections.get(collectionName);
24
+ if (collection) {
25
+ return collection.has(String(key));
26
+ }
27
+ return false;
28
+ }
29
+ hasCollection(collectionName) {
30
+ return this._collections.has(collectionName);
31
+ }
32
+ collections() {
33
+ return this._collections;
34
+ }
35
+ /**
36
+ * Attempts to load a DataStore from the virtual module.
37
+ * This only works in Vite.
38
+ */
39
+ static async fromModule() {
40
+ try {
41
+ const data = await import("astro:data-layer-content");
42
+ const map = devalue.unflatten(data.default);
43
+ return DataStore.fromMap(map);
44
+ } catch {
45
+ }
46
+ return new DataStore();
47
+ }
48
+ static async fromMap(data) {
49
+ const store = new DataStore();
50
+ store._collections = data;
51
+ return store;
52
+ }
53
+ }
54
+ function dataStoreSingleton() {
55
+ let instance = void 0;
56
+ return {
57
+ get: async () => {
58
+ if (!instance) {
59
+ instance = DataStore.fromModule();
60
+ }
61
+ return instance;
62
+ },
63
+ set: (store) => {
64
+ instance = store;
65
+ }
66
+ };
67
+ }
68
+ const globalDataStore = dataStoreSingleton();
69
+ export {
70
+ DataStore,
71
+ globalDataStore
72
+ };