astro 1.8.0 → 1.9.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.
@@ -8,11 +8,6 @@ declare type RawContentEvent = {
8
8
  name: ChokidarEvent;
9
9
  entry: string;
10
10
  };
11
- declare type EntryInfo = {
12
- id: string;
13
- slug: string;
14
- collection: string;
15
- };
16
11
  export declare type GenerateContentTypes = {
17
12
  init(): Promise<void>;
18
13
  queueEvent(event: RawContentEvent): void;
@@ -24,8 +19,5 @@ declare type CreateContentGeneratorParams = {
24
19
  fs: typeof fsMod;
25
20
  };
26
21
  export declare function createContentTypesGenerator({ contentConfigObserver, fs, logging, settings, }: CreateContentGeneratorParams): Promise<GenerateContentTypes>;
27
- export declare function getEntryInfo({ entry, contentDir, }: Pick<ContentPaths, 'contentDir'> & {
28
- entry: URL;
29
- }): EntryInfo | Error;
30
22
  export declare function getEntryType(entryPath: string, paths: ContentPaths): 'content' | 'config' | 'unknown' | 'generated-types';
31
23
  export {};
@@ -8,7 +8,9 @@ import { appendForwardSlash, isRelativePath } from "../core/path.js";
8
8
  import { contentFileExts, CONTENT_TYPES_FILE } from "./consts.js";
9
9
  import {
10
10
  getContentPaths,
11
- loadContentConfig
11
+ getEntryInfo,
12
+ loadContentConfig,
13
+ NoCollectionError
12
14
  } from "./utils.js";
13
15
  class UnsupportedFileTypeError extends Error {
14
16
  }
@@ -79,23 +81,26 @@ async function createContentTypesGenerator({
79
81
  }
80
82
  return { shouldGenerateTypes: true };
81
83
  }
82
- const entryInfo = getEntryInfo({
83
- entry: event.entry,
84
- contentDir: contentPaths.contentDir
85
- });
86
- if (entryInfo instanceof Error)
87
- return { shouldGenerateTypes: false };
88
84
  if (fileType === "unknown") {
89
- if (entryInfo.id.startsWith("_") && (event.name === "add" || event.name === "change")) {
85
+ const entryInfo2 = getEntryInfo({
86
+ entry: event.entry,
87
+ contentDir: contentPaths.contentDir,
88
+ allowFilesOutsideCollection: true
89
+ });
90
+ if (entryInfo2.id.startsWith("_") && (event.name === "add" || event.name === "change")) {
90
91
  return { shouldGenerateTypes: false };
91
92
  } else {
92
93
  return {
93
94
  shouldGenerateTypes: false,
94
- error: new UnsupportedFileTypeError(entryInfo.id)
95
+ error: new UnsupportedFileTypeError(entryInfo2.id)
95
96
  };
96
97
  }
97
98
  }
98
- if (entryInfo.collection === ".") {
99
+ const entryInfo = getEntryInfo({
100
+ entry: event.entry,
101
+ contentDir: contentPaths.contentDir
102
+ });
103
+ if (entryInfo instanceof NoCollectionError) {
99
104
  if (["info", "warn"].includes(logLevel)) {
100
105
  warn(
101
106
  logging,
@@ -196,23 +201,6 @@ function addEntry(contentTypes, collectionKey, entryKey, slug) {
196
201
  function removeEntry(contentTypes, collectionKey, entryKey) {
197
202
  delete contentTypes[collectionKey][entryKey];
198
203
  }
199
- function getEntryInfo({
200
- entry,
201
- contentDir
202
- }) {
203
- const rawRelativePath = path.relative(fileURLToPath(contentDir), fileURLToPath(entry));
204
- const rawCollection = path.dirname(rawRelativePath).split(path.sep).shift();
205
- if (!rawCollection)
206
- return new Error();
207
- const rawId = path.relative(rawCollection, rawRelativePath);
208
- const rawSlug = rawId.replace(path.extname(rawId), "");
209
- const res = {
210
- id: normalizePath(rawId),
211
- slug: normalizePath(rawSlug),
212
- collection: normalizePath(rawCollection)
213
- };
214
- return res;
215
- }
216
204
  function getEntryType(entryPath, paths) {
217
205
  const { dir: rawDir, ext, name, base } = path.parse(entryPath);
218
206
  const dir = appendForwardSlash(pathToFileURL(rawDir).href);
@@ -287,6 +275,5 @@ function warnNonexistentCollections({
287
275
  }
288
276
  export {
289
277
  createContentTypesGenerator,
290
- getEntryInfo,
291
278
  getEntryType
292
279
  };
@@ -120,11 +120,22 @@ declare type Entry = {
120
120
  filePath: string;
121
121
  };
122
122
  };
123
+ export declare type EntryInfo = {
124
+ id: string;
125
+ slug: string;
126
+ collection: string;
127
+ };
123
128
  export declare const msg: {
124
129
  collectionConfigMissing: (collection: string) => string;
125
130
  };
126
131
  export declare function getEntrySlug(entry: Entry, collectionConfig: CollectionConfig): Promise<string>;
127
132
  export declare function getEntryData(entry: Entry, collectionConfig: CollectionConfig): Promise<any>;
133
+ export declare class NoCollectionError extends Error {
134
+ }
135
+ export declare function getEntryInfo(params: Pick<ContentPaths, 'contentDir'> & {
136
+ entry: URL;
137
+ allowFilesOutsideCollection?: true;
138
+ }): EntryInfo;
128
139
  /**
129
140
  * Match YAML exception handling from Astro core errors
130
141
  * @see 'astro/src/core/errors.ts'
@@ -1,6 +1,8 @@
1
+ import { slug as githubSlug } from "github-slugger";
1
2
  import matter from "gray-matter";
3
+ import path from "node:path";
2
4
  import { fileURLToPath } from "node:url";
3
- import { createServer } from "vite";
5
+ import { createServer, normalizePath } from "vite";
4
6
  import { z } from "zod";
5
7
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
6
8
  import { astroContentVirtualModPlugin } from "./vite-plugin-content-virtual-mod.js";
@@ -60,10 +62,33 @@ async function getEntryData(entry, collectionConfig) {
60
62
  }
61
63
  return data;
62
64
  }
63
- const flattenPath = (path) => path.join(".");
65
+ class NoCollectionError extends Error {
66
+ }
67
+ function getEntryInfo({
68
+ entry,
69
+ contentDir,
70
+ allowFilesOutsideCollection = false
71
+ }) {
72
+ const rawRelativePath = path.relative(fileURLToPath(contentDir), fileURLToPath(entry));
73
+ const rawCollection = path.dirname(rawRelativePath).split(path.sep).shift();
74
+ const isOutsideCollection = rawCollection === ".." || rawCollection === ".";
75
+ if (!rawCollection || !allowFilesOutsideCollection && isOutsideCollection)
76
+ return new NoCollectionError();
77
+ const rawId = path.relative(rawCollection, rawRelativePath);
78
+ const rawIdWithoutFileExt = rawId.replace(new RegExp(path.extname(rawId) + "$"), "");
79
+ const rawSlugSegments = rawIdWithoutFileExt.split(path.sep);
80
+ const slug = rawSlugSegments.map((segment) => githubSlug(segment)).join("/").replace(/\/index$/, "");
81
+ const res = {
82
+ id: normalizePath(rawId),
83
+ slug,
84
+ collection: normalizePath(rawCollection)
85
+ };
86
+ return res;
87
+ }
88
+ const flattenErrorPath = (errorPath) => errorPath.join(".");
64
89
  const errorMap = (error, ctx) => {
65
90
  if (error.code === "invalid_type") {
66
- const badKeyPath = JSON.stringify(flattenPath(error.path));
91
+ const badKeyPath = JSON.stringify(flattenErrorPath(error.path));
67
92
  if (error.received === "undefined") {
68
93
  return { message: `${badKeyPath} is required.` };
69
94
  } else {
@@ -106,6 +131,7 @@ async function loadContentConfig({
106
131
  settings
107
132
  }) {
108
133
  const contentPaths = getContentPaths({ srcDir: settings.config.srcDir });
134
+ const nodeEnv = process.env.NODE_ENV;
109
135
  const tempConfigServer = await createServer({
110
136
  root: fileURLToPath(settings.config.root),
111
137
  server: { middlewareMode: true, hmr: false },
@@ -122,6 +148,7 @@ async function loadContentConfig({
122
148
  return new NotFoundError("Failed to resolve content config.");
123
149
  } finally {
124
150
  await tempConfigServer.close();
151
+ process.env.NODE_ENV = nodeEnv;
125
152
  }
126
153
  const config = contentConfigParser.safeParse(unparsedConfig);
127
154
  if (config.success) {
@@ -161,6 +188,7 @@ function getContentPaths({ srcDir }) {
161
188
  };
162
189
  }
163
190
  export {
191
+ NoCollectionError,
164
192
  NotFoundError,
165
193
  ZodParseError,
166
194
  collectionConfigParser,
@@ -168,6 +196,7 @@ export {
168
196
  contentObservable,
169
197
  getContentPaths,
170
198
  getEntryData,
199
+ getEntryInfo,
171
200
  getEntrySlug,
172
201
  loadContentConfig,
173
202
  msg,
@@ -9,7 +9,8 @@ import {
9
9
  LINKS_PLACEHOLDER,
10
10
  STYLES_PLACEHOLDER
11
11
  } from "./consts.js";
12
- function isDelayedAsset(url) {
12
+ function isDelayedAsset(viteId) {
13
+ const url = new URL(viteId, "file://");
13
14
  return url.searchParams.has(DELAYED_ASSET_FLAG) && contentFileExts.some((ext) => url.pathname.endsWith(ext));
14
15
  }
15
16
  function astroDelayedAssetPlugin({ mode }) {
@@ -23,10 +24,10 @@ function astroDelayedAssetPlugin({ mode }) {
23
24
  }
24
25
  },
25
26
  load(id) {
26
- const url = new URL(id, "file://");
27
- if (isDelayedAsset(url)) {
27
+ if (isDelayedAsset(id)) {
28
+ const basePath = id.split("?")[0];
28
29
  const code = `
29
- export { Content, getHeadings, _internal } from ${JSON.stringify(url.pathname)};
30
+ export { Content, getHeadings, _internal } from ${JSON.stringify(basePath)};
30
31
  export const collectedLinks = ${JSON.stringify(LINKS_PLACEHOLDER)};
31
32
  export const collectedStyles = ${JSON.stringify(STYLES_PLACEHOLDER)};
32
33
  `;
@@ -37,14 +38,13 @@ function astroDelayedAssetPlugin({ mode }) {
37
38
  var _a;
38
39
  if (!(options == null ? void 0 : options.ssr))
39
40
  return;
40
- const url = new URL(id, "file://");
41
- if (devModuleLoader && isDelayedAsset(url)) {
42
- const { pathname } = url;
43
- if (!((_a = devModuleLoader.getModuleById(pathname)) == null ? void 0 : _a.ssrModule)) {
44
- await devModuleLoader.import(pathname);
41
+ if (devModuleLoader && isDelayedAsset(id)) {
42
+ const basePath = id.split("?")[0];
43
+ if (!((_a = devModuleLoader.getModuleById(basePath)) == null ? void 0 : _a.ssrModule)) {
44
+ await devModuleLoader.import(basePath);
45
45
  }
46
46
  const { stylesMap, urls } = await getStylesForURL(
47
- pathToFileURL(pathname),
47
+ pathToFileURL(basePath),
48
48
  devModuleLoader,
49
49
  "development"
50
50
  );
@@ -2,18 +2,17 @@ import * as devalue from "devalue";
2
2
  import { cyan } from "kleur/colors";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { info } from "../core/logger/core.js";
5
- import { prependForwardSlash } from "../core/path.js";
6
- import { escapeViteEnvReferences } from "../vite-plugin-utils/index.js";
5
+ import { escapeViteEnvReferences, getFileInfo } from "../vite-plugin-utils/index.js";
7
6
  import { contentFileExts, CONTENT_FLAG } from "./consts.js";
8
7
  import {
9
8
  createContentTypesGenerator,
10
- getEntryInfo,
11
9
  getEntryType
12
10
  } from "./types-generator.js";
13
11
  import {
14
12
  contentObservable,
15
13
  getContentPaths,
16
14
  getEntryData,
15
+ getEntryInfo,
17
16
  getEntrySlug,
18
17
  parseFrontmatter
19
18
  } from "./utils.js";
@@ -95,8 +94,8 @@ function astroContentServerPlugin({
95
94
  {
96
95
  name: "astro-content-flag-plugin",
97
96
  async load(id) {
98
- const fileUrl = new URL(prependForwardSlash(id), "file://");
99
- if (isContentFlagImport(fileUrl)) {
97
+ const { fileId } = getFileInfo(id, settings.config);
98
+ if (isContentFlagImport(id)) {
100
99
  const observable = contentConfigObserver.get();
101
100
  let contentConfig = observable.status === "loaded" ? observable.config : void 0;
102
101
  if (observable.status === "loading") {
@@ -112,19 +111,19 @@ function astroContentServerPlugin({
112
111
  });
113
112
  });
114
113
  }
115
- const rawContents = await fs.promises.readFile(fileUrl, "utf-8");
114
+ const rawContents = await fs.promises.readFile(fileId, "utf-8");
116
115
  const {
117
116
  content: body,
118
117
  data: unparsedData,
119
118
  matter: rawData = ""
120
- } = parseFrontmatter(rawContents, fileUrl.pathname);
119
+ } = parseFrontmatter(rawContents, fileId);
121
120
  const entryInfo = getEntryInfo({
122
- entry: fileUrl,
121
+ entry: pathToFileURL(fileId),
123
122
  contentDir: contentPaths.contentDir
124
123
  });
125
124
  if (entryInfo instanceof Error)
126
125
  return;
127
- const _internal = { filePath: fileUrl.pathname, rawData };
126
+ const _internal = { filePath: fileId, rawData };
128
127
  const partialEntry = { data: unparsedData, body, _internal, ...entryInfo };
129
128
  const collectionConfig = contentConfig == null ? void 0 : contentConfig.collections[entryInfo.collection];
130
129
  const data = collectionConfig ? await getEntryData(partialEntry, collectionConfig) : unparsedData;
@@ -136,7 +135,7 @@ export const slug = ${JSON.stringify(slug)};
136
135
  export const body = ${JSON.stringify(body)};
137
136
  export const data = ${devalue.uneval(data)};
138
137
  export const _internal = {
139
- filePath: ${JSON.stringify(fileUrl.pathname)},
138
+ filePath: ${JSON.stringify(fileId)},
140
139
  rawData: ${JSON.stringify(rawData)},
141
140
  };
142
141
  `);
@@ -147,7 +146,7 @@ export const _internal = {
147
146
  viteServer.watcher.on("all", async (event, entry) => {
148
147
  if (["add", "unlink", "change"].includes(event) && getEntryType(entry, contentPaths) === "config") {
149
148
  for (const modUrl of viteServer.moduleGraph.urlToModuleMap.keys()) {
150
- if (isContentFlagImport(new URL(modUrl, "file://"))) {
149
+ if (isContentFlagImport(modUrl)) {
151
150
  const mod = await viteServer.moduleGraph.getModuleByUrl(modUrl);
152
151
  if (mod) {
153
152
  viteServer.moduleGraph.invalidateModule(mod);
@@ -158,14 +157,15 @@ export const _internal = {
158
157
  });
159
158
  },
160
159
  async transform(code, id) {
161
- if (isContentFlagImport(new URL(id, "file://"))) {
160
+ if (isContentFlagImport(id)) {
162
161
  return { code: escapeViteEnvReferences(code) };
163
162
  }
164
163
  }
165
164
  }
166
165
  ];
167
166
  }
168
- function isContentFlagImport({ searchParams, pathname }) {
167
+ function isContentFlagImport(viteId) {
168
+ const { pathname, searchParams } = new URL(viteId, "file://");
169
169
  return searchParams.has(CONTENT_FLAG) && contentFileExts.some((ext) => pathname.endsWith(ext));
170
170
  }
171
171
  export {
@@ -36,9 +36,7 @@ const _manifest = Object.assign(_deserializeManifest('${manifestReplace}'), {
36
36
  renderers: _main.renderers
37
37
  });
38
38
  const _args = ${adapter.args ? JSON.stringify(adapter.args) : "undefined"};
39
-
40
39
  export * from '${pagesVirtualModuleId}';
41
-
42
40
  ${adapter.exports ? `const _exports = adapter.createExports(_manifest, _args);
43
41
  ${adapter.exports.map((name) => {
44
42
  if (name === "default") {
@@ -135,9 +133,11 @@ function buildManifest(opts, internals, staticFiles) {
135
133
  for (const pageData of eachServerPageData(internals)) {
136
134
  const scripts = [];
137
135
  if (pageData.hoistedScript) {
136
+ const hoistedValue = pageData.hoistedScript.value;
137
+ const value = hoistedValue.endsWith(".js") ? joinBase(hoistedValue) : hoistedValue;
138
138
  scripts.unshift(
139
139
  Object.assign({}, pageData.hoistedScript, {
140
- value: joinBase(pageData.hoistedScript.value)
140
+ value
141
141
  })
142
142
  );
143
143
  }
@@ -13,7 +13,13 @@ async function createViteLoader(root, fs) {
13
13
  clearScreen: false,
14
14
  appType: "custom",
15
15
  ssr: {
16
- external: ["@astrojs/tailwind", "@astrojs/mdx", "@astrojs/react"]
16
+ external: [
17
+ "@astrojs/tailwind",
18
+ "@astrojs/mdx",
19
+ "@astrojs/react",
20
+ "@astrojs/preact",
21
+ "@astrojs/sitemap"
22
+ ]
17
23
  },
18
24
  plugins: [loadFallbackPlugin({ fs, root: pathToFileURL(root) })]
19
25
  });
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "1.8.0";
1
+ const ASTRO_VERSION = "1.9.0";
2
2
  const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
3
3
  ".markdown",
4
4
  ".mdown",
@@ -30,7 +30,7 @@ async function dev(settings, options) {
30
30
  isRestart: options.isRestart
31
31
  })
32
32
  );
33
- const currentVersion = "1.8.0";
33
+ const currentVersion = "1.9.0";
34
34
  if (currentVersion.includes("-")) {
35
35
  warn(options.logging, null, msg.prerelease({ currentVersion }));
36
36
  }
@@ -49,7 +49,7 @@ function serverStart({
49
49
  site,
50
50
  isRestart = false
51
51
  }) {
52
- const version = "1.8.0";
52
+ const version = "1.9.0";
53
53
  const rootPath = site ? site.pathname : "/";
54
54
  const localPrefix = `${dim("\u2503")} Local `;
55
55
  const networkPrefix = `${dim("\u2503")} Network `;
@@ -272,7 +272,7 @@ function printHelp({
272
272
  message.push(
273
273
  linebreak(),
274
274
  ` ${bgGreen(black(` ${commandName} `))} ${green(
275
- `v${"1.8.0"}`
275
+ `v${"1.9.0"}`
276
276
  )} ${headline}`
277
277
  );
278
278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -124,7 +124,7 @@
124
124
  "estree-walker": "^3.0.1",
125
125
  "execa": "^6.1.0",
126
126
  "fast-glob": "^3.2.11",
127
- "github-slugger": "^1.4.0",
127
+ "github-slugger": "^2.0.0",
128
128
  "gray-matter": "^4.0.3",
129
129
  "html-entities": "^2.3.3",
130
130
  "html-escaper": "^3.0.3",
@@ -209,6 +209,7 @@
209
209
  "postbuild": "astro-scripts copy \"src/**/*.astro\"",
210
210
  "benchmark": "node test/benchmark/dev.bench.js && node test/benchmark/build.bench.js",
211
211
  "test:unit": "mocha --exit --timeout 30000 ./test/units/**/*.test.js",
212
+ "test:unit:match": "mocha --exit --timeout 30000 ./test/units/**/*.test.js -g",
212
213
  "test": "pnpm run test:unit && mocha --exit --timeout 20000 --ignore **/lit-element.test.js && mocha --timeout 20000 **/lit-element.test.js",
213
214
  "test:match": "mocha --timeout 20000 -g",
214
215
  "test:e2e": "playwright test",