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.
- package/components/Code.astro +9 -0
- package/dist/@types/astro.d.ts +251 -2
- package/dist/actions/consts.d.ts +1 -1
- package/dist/actions/consts.js +1 -1
- package/dist/actions/index.js +12 -21
- package/dist/actions/runtime/virtual/server.js +9 -3
- package/dist/actions/runtime/virtual/shared.js +25 -3
- package/dist/assets/utils/resolveImports.d.ts +9 -0
- package/dist/assets/utils/resolveImports.js +22 -0
- package/dist/cli/add/index.d.ts +2 -2
- package/dist/cli/add/index.js +2 -2
- package/dist/cli/build/index.d.ts +2 -2
- package/dist/cli/build/index.js +5 -1
- package/dist/cli/check/index.d.ts +2 -2
- package/dist/cli/check/index.js +5 -2
- package/dist/cli/db/index.d.ts +4 -3
- package/dist/cli/db/index.js +10 -3
- package/dist/cli/dev/index.d.ts +2 -2
- package/dist/cli/dev/index.js +1 -0
- package/dist/cli/docs/index.d.ts +2 -2
- package/dist/cli/flags.d.ts +3 -1
- package/dist/cli/flags.js +2 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +26 -13
- package/dist/cli/info/index.d.ts +2 -2
- package/dist/cli/preferences/index.d.ts +2 -2
- package/dist/cli/preferences/index.js +1 -1
- package/dist/cli/preview/index.d.ts +2 -2
- package/dist/cli/sync/index.d.ts +2 -2
- package/dist/cli/sync/index.js +5 -2
- package/dist/cli/telemetry/index.d.ts +2 -2
- package/dist/container/index.js +3 -1
- package/dist/content/consts.d.ts +16 -2
- package/dist/content/consts.js +32 -2
- package/dist/content/content-layer.d.ts +40 -0
- package/dist/content/content-layer.js +253 -0
- package/dist/content/data-store.d.ts +114 -0
- package/dist/content/data-store.js +323 -0
- package/dist/content/loaders/file.d.ts +7 -0
- package/dist/content/loaders/file.js +72 -0
- package/dist/content/loaders/glob.d.ts +25 -0
- package/dist/content/loaders/glob.js +218 -0
- package/dist/content/loaders/index.d.ts +3 -0
- package/dist/content/loaders/index.js +7 -0
- package/dist/content/loaders/types.d.ts +36 -0
- package/dist/content/loaders/types.js +0 -0
- package/dist/content/runtime.d.ts +46 -8
- package/dist/content/runtime.js +225 -31
- package/dist/content/types-generator.js +125 -35
- package/dist/content/utils.d.ts +306 -2
- package/dist/content/utils.js +93 -7
- package/dist/content/vite-plugin-content-assets.js +9 -1
- package/dist/content/vite-plugin-content-virtual-mod.js +94 -2
- package/dist/core/app/common.js +4 -1
- package/dist/core/app/index.js +5 -3
- package/dist/core/app/types.d.ts +3 -1
- package/dist/core/build/generate.js +4 -2
- package/dist/core/build/index.js +17 -8
- package/dist/core/build/plugins/plugin-manifest.js +5 -2
- package/dist/core/build/plugins/plugin-ssr.js +35 -3
- package/dist/core/build/static-build.js +2 -0
- package/dist/core/build/types.d.ts +1 -0
- package/dist/core/config/config.d.ts +2 -5
- package/dist/core/config/config.js +0 -12
- package/dist/core/config/index.d.ts +1 -1
- package/dist/core/config/index.js +0 -2
- package/dist/core/config/schema.d.ts +34 -0
- package/dist/core/config/schema.js +6 -2
- package/dist/core/config/settings.js +5 -3
- package/dist/core/constants.js +1 -1
- package/dist/core/create-vite.js +1 -1
- package/dist/core/dev/container.js +2 -1
- package/dist/core/dev/dev.js +34 -2
- package/dist/core/dev/restart.js +25 -10
- package/dist/core/encryption.d.ts +24 -0
- package/dist/core/encryption.js +64 -0
- package/dist/core/errors/errors-data.d.ts +21 -0
- package/dist/core/errors/errors-data.js +13 -0
- package/dist/core/index.js +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/core/render-context.js +1 -0
- package/dist/core/server-islands/endpoint.js +5 -1
- package/dist/core/sync/constants.d.ts +1 -0
- package/dist/core/sync/constants.js +4 -0
- package/dist/core/sync/index.d.ts +12 -4
- package/dist/core/sync/index.js +56 -25
- package/dist/core/sync/write-files.d.ts +4 -0
- package/dist/core/sync/write-files.js +69 -0
- package/dist/core/util.js +1 -1
- package/dist/env/sync.js +6 -4
- package/dist/integrations/hooks.d.ts +7 -1
- package/dist/integrations/hooks.js +54 -0
- package/dist/preferences/index.d.ts +1 -1
- package/dist/preferences/index.js +2 -2
- package/dist/runtime/server/render/server-islands.js +9 -5
- package/dist/vite-plugin-astro-server/plugin.js +2 -0
- package/dist/vite-plugin-env/index.d.ts +3 -1
- package/dist/vite-plugin-env/index.js +11 -1
- package/dist/vite-plugin-markdown/content-entry-type.js +25 -2
- package/dist/vite-plugin-scanner/index.js +15 -5
- package/dist/vite-plugin-scanner/scan.js +1 -1
- package/package.json +15 -9
- package/templates/content/module.mjs +6 -1
- package/templates/content/types.d.ts +18 -5
- package/types/content.d.ts +34 -1
- package/dist/core/sync/setup-env-ts.d.ts +0 -8
- package/dist/core/sync/setup-env-ts.js +0 -79
package/dist/cli/db/index.js
CHANGED
|
@@ -2,10 +2,13 @@ import { resolveConfig } from "../../core/config/config.js";
|
|
|
2
2
|
import { apply as applyPolyfill } from "../../core/polyfill.js";
|
|
3
3
|
import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
|
|
4
4
|
import { getPackage } from "../install-package.js";
|
|
5
|
-
async function db({ flags }) {
|
|
5
|
+
async function db({ positionals, flags }) {
|
|
6
6
|
applyPolyfill();
|
|
7
7
|
const logger = createLoggerFromFlags(flags);
|
|
8
|
-
const getPackageOpts = {
|
|
8
|
+
const getPackageOpts = {
|
|
9
|
+
skipAsk: !!flags.yes || !!flags.y,
|
|
10
|
+
cwd: typeof flags.root == "string" ? flags.root : void 0
|
|
11
|
+
};
|
|
9
12
|
const dbPackage = await getPackage("@astrojs/db", logger, getPackageOpts, []);
|
|
10
13
|
if (!dbPackage) {
|
|
11
14
|
logger.error(
|
|
@@ -17,7 +20,11 @@ async function db({ flags }) {
|
|
|
17
20
|
const { cli } = dbPackage;
|
|
18
21
|
const inlineConfig = flagsToAstroInlineConfig(flags);
|
|
19
22
|
const { astroConfig } = await resolveConfig(inlineConfig, "build");
|
|
20
|
-
|
|
23
|
+
const yargsArgs = {
|
|
24
|
+
_: positionals,
|
|
25
|
+
...flags
|
|
26
|
+
};
|
|
27
|
+
await cli({ flags: yargsArgs, config: astroConfig });
|
|
21
28
|
}
|
|
22
29
|
export {
|
|
23
30
|
db
|
package/dist/cli/dev/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Flags } from '../flags.js';
|
|
2
2
|
interface DevOptions {
|
|
3
|
-
flags:
|
|
3
|
+
flags: Flags;
|
|
4
4
|
}
|
|
5
5
|
export declare function dev({ flags }: DevOptions): Promise<import("../../core/dev/dev.js").DevServer | undefined>;
|
|
6
6
|
export {};
|
package/dist/cli/dev/index.js
CHANGED
|
@@ -13,6 +13,7 @@ async function dev({ flags }) {
|
|
|
13
13
|
["--host", `Listen on all addresses, including LAN and public addresses.`],
|
|
14
14
|
["--host <custom-address>", `Expose on a network IP address at <custom-address>`],
|
|
15
15
|
["--open", "Automatically open the app in the browser on server start"],
|
|
16
|
+
["--force", "Clear the content layer cache, forcing a full rebuild."],
|
|
16
17
|
["--help (-h)", "See all available flags."]
|
|
17
18
|
]
|
|
18
19
|
},
|
package/dist/cli/docs/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type { Flags } from '../flags.js';
|
|
2
2
|
interface DocsOptions {
|
|
3
|
-
flags:
|
|
3
|
+
flags: Flags;
|
|
4
4
|
}
|
|
5
5
|
export declare function docs({ flags }: DocsOptions): Promise<import("execa").ExecaReturnValue<string> | undefined>;
|
|
6
6
|
export {};
|
package/dist/cli/flags.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { parseArgs } from 'node:util';
|
|
2
2
|
import type { AstroInlineConfig } from '../@types/astro.js';
|
|
3
3
|
import { Logger } from '../core/logger/core.js';
|
|
4
|
+
export type ParsedArgsResult = ReturnType<typeof parseArgs>;
|
|
5
|
+
export type Flags = ParsedArgsResult['values'];
|
|
4
6
|
export declare function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig;
|
|
5
7
|
/**
|
|
6
8
|
* The `logging` is usually created from an `AstroInlineConfig`, but some flows like `add`
|
package/dist/cli/flags.js
CHANGED
|
@@ -6,13 +6,14 @@ function flagsToAstroInlineConfig(flags) {
|
|
|
6
6
|
configFile: typeof flags.config === "string" ? flags.config : void 0,
|
|
7
7
|
mode: typeof flags.mode === "string" ? flags.mode : void 0,
|
|
8
8
|
logLevel: flags.verbose ? "debug" : flags.silent ? "silent" : void 0,
|
|
9
|
+
force: flags.force ? true : void 0,
|
|
9
10
|
// Astro user configs
|
|
10
11
|
root: typeof flags.root === "string" ? flags.root : void 0,
|
|
11
12
|
site: typeof flags.site === "string" ? flags.site : void 0,
|
|
12
13
|
base: typeof flags.base === "string" ? flags.base : void 0,
|
|
13
14
|
outDir: typeof flags.outDir === "string" ? flags.outDir : void 0,
|
|
14
15
|
server: {
|
|
15
|
-
port: typeof flags.port === "
|
|
16
|
+
port: typeof flags.port === "string" ? Number(flags.port) : void 0,
|
|
16
17
|
host: typeof flags.host === "string" || typeof flags.host === "boolean" ? flags.host : void 0,
|
|
17
18
|
open: typeof flags.open === "string" || typeof flags.open === "boolean" ? flags.open : void 0
|
|
18
19
|
}
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The primary CLI action */
|
|
2
|
-
export declare function cli(
|
|
2
|
+
export declare function cli(argv: string[]): Promise<void>;
|
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(
|
|
47
|
-
const cmd =
|
|
48
|
-
if (
|
|
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,
|
|
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 =
|
|
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] =
|
|
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 =
|
|
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(
|
|
165
|
-
const
|
|
166
|
-
|
|
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,
|
|
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);
|
package/dist/cli/info/index.d.ts
CHANGED
|
@@ -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:
|
|
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
|
|
1
|
+
import { type Flags } from '../flags.js';
|
|
2
2
|
interface PreferencesOptions {
|
|
3
|
-
flags:
|
|
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
|
|
1
|
+
import { type Flags } from '../flags.js';
|
|
2
2
|
interface PreviewOptions {
|
|
3
|
-
flags:
|
|
3
|
+
flags: Flags;
|
|
4
4
|
}
|
|
5
5
|
export declare function preview({ flags }: PreviewOptions): Promise<import("../../@types/astro.js").PreviewServer | undefined>;
|
|
6
6
|
export {};
|
package/dist/cli/sync/index.d.ts
CHANGED
package/dist/cli/sync/index.js
CHANGED
|
@@ -7,14 +7,17 @@ async function sync({ flags }) {
|
|
|
7
7
|
commandName: "astro sync",
|
|
8
8
|
usage: "[...flags]",
|
|
9
9
|
tables: {
|
|
10
|
-
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(
|
|
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
|
|
1
|
+
import { type Flags } from '../flags.js';
|
|
2
2
|
interface TelemetryOptions {
|
|
3
|
-
flags:
|
|
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>;
|
package/dist/container/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { posix } from "node:path";
|
|
|
2
2
|
import { getDefaultClientDirectives } from "../core/client-directive/index.js";
|
|
3
3
|
import { ASTRO_CONFIG_DEFAULTS } from "../core/config/schema.js";
|
|
4
4
|
import { validateConfig } from "../core/config/validate.js";
|
|
5
|
+
import { createKey } from "../core/encryption.js";
|
|
5
6
|
import { Logger } from "../core/logger/core.js";
|
|
6
7
|
import { nodeLogDestination } from "../core/logger/node.js";
|
|
7
8
|
import { removeLeadingForwardSlash } from "../core/path.js";
|
|
@@ -31,7 +32,8 @@ function createManifest(manifest, renderers, middleware) {
|
|
|
31
32
|
i18n: manifest?.i18n,
|
|
32
33
|
checkOrigin: false,
|
|
33
34
|
middleware: manifest?.middleware ?? middleware ?? defaultMiddleware,
|
|
34
|
-
experimentalEnvGetSecretEnabled: false
|
|
35
|
+
experimentalEnvGetSecretEnabled: false,
|
|
36
|
+
key: createKey()
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
39
|
class experimental_AstroContainer {
|
package/dist/content/consts.d.ts
CHANGED
|
@@ -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
|
|
11
|
-
export declare const
|
|
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";
|
package/dist/content/consts.js
CHANGED
|
@@ -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 = "
|
|
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 { DataStore } from './data-store.js';
|
|
5
|
+
import type { LoaderContext } from './loaders/types.js';
|
|
6
|
+
export interface ContentLayerOptions {
|
|
7
|
+
store: DataStore;
|
|
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
|
+
};
|