From 1efaef6be0265c68eac706623778e8ad23b33247 Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Wed, 24 May 2023 16:52:22 -0400 Subject: [PATCH 01/26] Markdoc - Shiki (#7187) * chore: remove unused util * chore: changeset * deps: shiki * wip: first stab at shiki markdoc config * feat: get shiki working! * refactor: return HTML string directly from transform * chore: move shiki to markdoc dev dep * refactor: use async cache with clear docs on why * test: transform units with Shiki config options * refactor: switch to `extends` model * refactor: nodes/ -> extensions/ * feat: raise friendly error for Promise extensions * docs: README * chore: lint * chore: dead file * chore: lowercase for fuzzy find please * fix: bad ctx spread * chore: clean up cache, add shiki imp error * chore: add shiki to optional peer deps * chore: hoist those consts * docs: more explicit "install shiki now please" Co-authored-by: Sarah Rainsberger * oops bad find and replace * chore: update changeset * nit: period haunts me --------- Co-authored-by: Sarah Rainsberger --- .changeset/eleven-tables-speak.md | 17 +++ packages/astro/src/runtime/server/index.ts | 9 +- packages/integrations/markdoc/README.md | 35 +++++ .../markdoc/components/TreeNode.ts | 15 +- packages/integrations/markdoc/package.json | 11 +- packages/integrations/markdoc/src/config.ts | 15 +- .../markdoc/src/extensions/shiki.ts | 138 ++++++++++++++++++ .../src/{nodes/heading.ts => heading-ids.ts} | 32 ++-- packages/integrations/markdoc/src/index.ts | 6 +- .../integrations/markdoc/src/nodes/index.ts | 4 - packages/integrations/markdoc/src/runtime.ts | 44 ++++-- .../markdoc/test/syntax-highlighting.test.js | 89 +++++++++++ pnpm-lock.yaml | 3 + 13 files changed, 383 insertions(+), 35 deletions(-) create mode 100644 .changeset/eleven-tables-speak.md create mode 100644 packages/integrations/markdoc/src/extensions/shiki.ts rename packages/integrations/markdoc/src/{nodes/heading.ts => heading-ids.ts} (64%) delete mode 100644 packages/integrations/markdoc/src/nodes/index.ts create mode 100644 packages/integrations/markdoc/test/syntax-highlighting.test.js diff --git a/.changeset/eleven-tables-speak.md b/.changeset/eleven-tables-speak.md new file mode 100644 index 000000000..6ff1474c7 --- /dev/null +++ b/.changeset/eleven-tables-speak.md @@ -0,0 +1,17 @@ +--- +'@astrojs/markdoc': patch +--- + +Add support for syntax highlighting with Shiki. Install `shiki` in your project with `npm i shiki`, and apply to your Markdoc config using the `extends` option: + +```js +// markdoc.config.mjs +import { defineMarkdocConfig, shiki } from '@astrojs/markdoc/config'; +export default defineMarkdocConfig({ + extends: [ + await shiki({ /** Shiki config options */ }), + ], +}) +``` + +Learn more in the [`@astrojs/markdoc` README.](https://docs.astro.build/en/guides/integrations-guide/markdoc/#syntax-highlighting) diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index 1f1e1e97b..021e55a56 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -1,7 +1,14 @@ export { createComponent } from './astro-component.js'; export { createAstro } from './astro-global.js'; export { renderEndpoint } from './endpoint.js'; -export { escapeHTML, HTMLBytes, HTMLString, markHTMLString, unescapeHTML } from './escape.js'; +export { + escapeHTML, + HTMLBytes, + HTMLString, + markHTMLString, + unescapeHTML, + isHTMLString, +} from './escape.js'; export { renderJSX } from './jsx.js'; export { addAttribute, diff --git a/packages/integrations/markdoc/README.md b/packages/integrations/markdoc/README.md index e3cec5499..815f0420b 100644 --- a/packages/integrations/markdoc/README.md +++ b/packages/integrations/markdoc/README.md @@ -203,6 +203,41 @@ export default defineMarkdocConfig({ }) ``` +### Syntax highlighting + +`@astrojs/markdoc` provides a [Shiki](https://github.com/shikijs/shiki) extension to highlight your code blocks. + +To use this extension, you must separately install `shiki` as a dependency: + +```bash +npm i shiki +``` + +Then, apply the `shiki()` extension to your Markdoc config using the `extends` property. You can optionally pass a shiki configuration object: + +```js +// markdoc.config.mjs +import { defineMarkdocConfig, shiki } from '@astrojs/markdoc/config'; + +export default defineMarkdocConfig({ + extends: [ + await shiki({ + // Choose from Shiki's built-in themes (or add your own) + // Default: 'github-dark' + // https://github.com/shikijs/shiki/blob/main/docs/themes.md + theme: 'dracula', + // Enable word wrap to prevent horizontal scrolling + // Default: false + wrap: true, + // Pass custom languages + // Note: Shiki has countless langs built-in, including `.astro`! + // https://github.com/shikijs/shiki/blob/main/docs/languages.md + langs: [], + }) + ], +}) +``` + ### Access frontmatter and content collection information from your templates You can access content collection information from your Markdoc templates using the `$entry` variable. This includes the entry `slug`, `collection` name, and frontmatter `data` parsed by your content collection schema (if any). This example renders the `title` frontmatter property as a heading: diff --git a/packages/integrations/markdoc/components/TreeNode.ts b/packages/integrations/markdoc/components/TreeNode.ts index a60597a0d..d12180a18 100644 --- a/packages/integrations/markdoc/components/TreeNode.ts +++ b/packages/integrations/markdoc/components/TreeNode.ts @@ -2,12 +2,18 @@ import type { AstroInstance } from 'astro'; import { Fragment } from 'astro/jsx-runtime'; import type { RenderableTreeNode } from '@markdoc/markdoc'; import Markdoc from '@markdoc/markdoc'; -import { createComponent, renderComponent, render } from 'astro/runtime/server/index.js'; +import { + createComponent, + renderComponent, + render, + HTMLString, + isHTMLString, +} from 'astro/runtime/server/index.js'; export type TreeNode = | { type: 'text'; - content: string; + content: string | HTMLString; } | { type: 'component'; @@ -25,6 +31,7 @@ export type TreeNode = export const ComponentNode = createComponent({ factory(result: any, { treeNode }: { treeNode: TreeNode }) { if (treeNode.type === 'text') return render`${treeNode.content}`; + const slots = { default: () => render`${treeNode.children.map((child) => @@ -46,7 +53,9 @@ export const ComponentNode = createComponent({ }); export function createTreeNode(node: RenderableTreeNode | RenderableTreeNode[]): TreeNode { - if (typeof node === 'string' || typeof node === 'number') { + if (isHTMLString(node)) { + return { type: 'text', content: node as HTMLString }; + } else if (typeof node === 'string' || typeof node === 'number') { return { type: 'text', content: String(node) }; } else if (Array.isArray(node)) { return { diff --git a/packages/integrations/markdoc/package.json b/packages/integrations/markdoc/package.json index f031c8f6c..2086073ad 100644 --- a/packages/integrations/markdoc/package.json +++ b/packages/integrations/markdoc/package.json @@ -1,6 +1,6 @@ { "name": "@astrojs/markdoc", - "description": "Add support for Markdoc pages in your Astro site", + "description": "Add support for Markdoc in your Astro site", "version": "0.2.3", "type": "module", "types": "./dist/index.d.ts", @@ -47,7 +47,13 @@ "zod": "^3.17.3" }, "peerDependencies": { - "astro": "workspace:^2.5.5" + "astro": "workspace:^2.5.5", + "shiki": "^0.14.1" + }, + "peerDependenciesMeta": { + "shiki": { + "optional": true + } }, "devDependencies": { "@astrojs/markdown-remark": "^2.2.1", @@ -61,6 +67,7 @@ "linkedom": "^0.14.12", "mocha": "^9.2.2", "rollup": "^3.20.1", + "shiki": "^0.14.1", "vite": "^4.3.1" }, "engines": { diff --git a/packages/integrations/markdoc/src/config.ts b/packages/integrations/markdoc/src/config.ts index f8943ba1a..a8f202424 100644 --- a/packages/integrations/markdoc/src/config.ts +++ b/packages/integrations/markdoc/src/config.ts @@ -1,10 +1,19 @@ import type { ConfigType as MarkdocConfig } from '@markdoc/markdoc'; import _Markdoc from '@markdoc/markdoc'; -import { nodes as astroNodes } from './nodes/index.js'; +import { heading } from './heading-ids.js'; + +export type AstroMarkdocConfig = Record> = + MarkdocConfig & { + ctx?: C; + extends?: ResolvedAstroMarkdocConfig[]; + }; + +export type ResolvedAstroMarkdocConfig = Omit; export const Markdoc = _Markdoc; -export const nodes = { ...Markdoc.nodes, ...astroNodes }; +export const nodes = { ...Markdoc.nodes, heading }; +export { shiki } from './extensions/shiki.js'; -export function defineMarkdocConfig(config: MarkdocConfig): MarkdocConfig { +export function defineMarkdocConfig(config: AstroMarkdocConfig): AstroMarkdocConfig { return config; } diff --git a/packages/integrations/markdoc/src/extensions/shiki.ts b/packages/integrations/markdoc/src/extensions/shiki.ts new file mode 100644 index 000000000..96d91d541 --- /dev/null +++ b/packages/integrations/markdoc/src/extensions/shiki.ts @@ -0,0 +1,138 @@ +// @ts-expect-error Cannot find module 'astro/runtime/server/index.js' or its corresponding type declarations. +import { unescapeHTML } from 'astro/runtime/server/index.js'; +import type { ShikiConfig } from 'astro'; +import type * as shikiTypes from 'shiki'; +import type { AstroMarkdocConfig } from '../config.js'; +import Markdoc from '@markdoc/markdoc'; +import { MarkdocError } from '../utils.js'; + +// Map of old theme names to new names to preserve compatibility when we upgrade shiki +const compatThemes: Record = { + 'material-darker': 'material-theme-darker', + 'material-default': 'material-theme', + 'material-lighter': 'material-theme-lighter', + 'material-ocean': 'material-theme-ocean', + 'material-palenight': 'material-theme-palenight', +}; + +const normalizeTheme = (theme: string | shikiTypes.IShikiTheme) => { + if (typeof theme === 'string') { + return compatThemes[theme] || theme; + } else if (compatThemes[theme.name]) { + return { ...theme, name: compatThemes[theme.name] }; + } else { + return theme; + } +}; + +const ASTRO_COLOR_REPLACEMENTS = { + '#000001': 'var(--astro-code-color-text)', + '#000002': 'var(--astro-code-color-background)', + '#000004': 'var(--astro-code-token-constant)', + '#000005': 'var(--astro-code-token-string)', + '#000006': 'var(--astro-code-token-comment)', + '#000007': 'var(--astro-code-token-keyword)', + '#000008': 'var(--astro-code-token-parameter)', + '#000009': 'var(--astro-code-token-function)', + '#000010': 'var(--astro-code-token-string-expression)', + '#000011': 'var(--astro-code-token-punctuation)', + '#000012': 'var(--astro-code-token-link)', +}; + +const PRE_SELECTOR = /
([\+|\-])/g;
+const INLINE_STYLE_SELECTOR = /style="(.*?)"/;
+
+/**
+ * Note: cache only needed for dev server reloads, internal test suites, and manual calls to `Markdoc.transform` by the user.
+ * Otherwise, `shiki()` is only called once per build, NOT once per page, so a cache isn't needed!
+ */
+const highlighterCache = new Map();
+
+export async function shiki({
+	langs = [],
+	theme = 'github-dark',
+	wrap = false,
+}: ShikiConfig = {}): Promise {
+	let getHighlighter: (options: shikiTypes.HighlighterOptions) => Promise;
+	try {
+		getHighlighter = (await import('shiki')).getHighlighter;
+	} catch {
+		throw new MarkdocError({
+			message: 'Shiki is not installed. Run `npm install shiki` to use the `shiki` extension.',
+		});
+	}
+	theme = normalizeTheme(theme);
+
+	const cacheID: string = typeof theme === 'string' ? theme : theme.name;
+	if (!highlighterCache.has(cacheID)) {
+		highlighterCache.set(
+			cacheID,
+			await getHighlighter({ theme }).then((hl) => {
+				hl.setColorReplacements(ASTRO_COLOR_REPLACEMENTS);
+				return hl;
+			})
+		);
+	}
+	const highlighter = highlighterCache.get(cacheID)!;
+
+	for (const lang of langs) {
+		await highlighter.loadLanguage(lang);
+	}
+	return {
+		nodes: {
+			fence: {
+				attributes: Markdoc.nodes.fence.attributes!,
+				transform({ attributes }) {
+					let lang: string;
+
+					if (typeof attributes.language === 'string') {
+						const langExists = highlighter
+							.getLoadedLanguages()
+							.includes(attributes.language as any);
+						if (langExists) {
+							lang = attributes.language;
+						} else {
+							// eslint-disable-next-line no-console
+							console.warn(
+								`[Shiki highlighter] The language "${attributes.language}" doesn't exist, falling back to plaintext.`
+							);
+							lang = 'plaintext';
+						}
+					} else {
+						lang = 'plaintext';
+					}
+
+					let html = highlighter.codeToHtml(attributes.content, { lang });
+
+					// Q: Could these regexes match on a user's inputted code blocks?
+					// A: Nope! All rendered HTML is properly escaped.
+					// Ex. If a user typed `$2'
+						);
+					}
+
+					if (wrap === false) {
+						html = html.replace(INLINE_STYLE_SELECTOR, 'style="$1; overflow-x: auto;"');
+					} else if (wrap === true) {
+						html = html.replace(
+							INLINE_STYLE_SELECTOR,
+							'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"'
+						);
+					}
+
+					// Use `unescapeHTML` to return `HTMLString` for Astro renderer to inline as HTML
+					return unescapeHTML(html);
+				},
+			},
+		},
+	};
+}
diff --git a/packages/integrations/markdoc/src/nodes/heading.ts b/packages/integrations/markdoc/src/heading-ids.ts
similarity index 64%
rename from packages/integrations/markdoc/src/nodes/heading.ts
rename to packages/integrations/markdoc/src/heading-ids.ts
index 0210e9b90..57b84d059 100644
--- a/packages/integrations/markdoc/src/nodes/heading.ts
+++ b/packages/integrations/markdoc/src/heading-ids.ts
@@ -1,13 +1,8 @@
 import Markdoc, { type ConfigType, type RenderableTreeNode, type Schema } from '@markdoc/markdoc';
 import Slugger from 'github-slugger';
-import { getTextContent } from '../runtime.js';
-
-type ConfigTypeWithCtx = ConfigType & {
-	// TODO: decide on `ctx` as a convention for config merging
-	ctx: {
-		headingSlugger: Slugger;
-	};
-};
+import { getTextContent } from './runtime.js';
+import type { AstroMarkdocConfig } from './config.js';
+import { MarkdocError } from './utils.js';
 
 function getSlug(
 	attributes: Record,
@@ -24,16 +19,31 @@ function getSlug(
 	return slug;
 }
 
+type HeadingIdConfig = AstroMarkdocConfig<{
+	headingSlugger: Slugger;
+}>;
+
+/*
+	Expose standalone node for users to import in their config.
+	Allows users to apply a custom `render: AstroComponent`
+	and spread our default heading attributes.
+*/
 export const heading: Schema = {
 	children: ['inline'],
 	attributes: {
 		id: { type: String },
 		level: { type: Number, required: true, default: 1 },
 	},
-	transform(node, config: ConfigTypeWithCtx) {
+	transform(node, config: HeadingIdConfig) {
 		const { level, ...attributes } = node.transformAttributes(config);
 		const children = node.transformChildren(config);
 
+		if (!config.ctx?.headingSlugger) {
+			throw new MarkdocError({
+				message:
+					'Unexpected problem adding heading IDs to Markdoc file. Did you modify the `ctx.headingSlugger` property in your Markdoc config?',
+			});
+		}
 		const slug = getSlug(attributes, children, config.ctx.headingSlugger);
 
 		const render = config.nodes?.heading?.render ?? `h${level}`;
@@ -49,9 +59,9 @@ export const heading: Schema = {
 	},
 };
 
-export function setupHeadingConfig(): ConfigTypeWithCtx {
+// Called internally to ensure `ctx` is generated per-file, instead of per-build.
+export function setupHeadingConfig(): HeadingIdConfig {
 	const headingSlugger = new Slugger();
-
 	return {
 		ctx: {
 			headingSlugger,
diff --git a/packages/integrations/markdoc/src/index.ts b/packages/integrations/markdoc/src/index.ts
index 627f08c77..64ae4cbc0 100644
--- a/packages/integrations/markdoc/src/index.ts
+++ b/packages/integrations/markdoc/src/index.ts
@@ -52,7 +52,11 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration
 					async getRenderModule({ entry, viteId }) {
 						const ast = Markdoc.parse(entry.body);
 						const pluginContext = this;
-						const markdocConfig = setupConfig(userMarkdocConfig, entry);
+						const markdocConfig = setupConfig(
+							userMarkdocConfig,
+							entry,
+							markdocConfigResult?.fileUrl.pathname
+						);
 
 						const validationErrors = Markdoc.validate(ast, markdocConfig).filter((e) => {
 							return (
diff --git a/packages/integrations/markdoc/src/nodes/index.ts b/packages/integrations/markdoc/src/nodes/index.ts
deleted file mode 100644
index 4cd7e3667..000000000
--- a/packages/integrations/markdoc/src/nodes/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { heading } from './heading.js';
-export { setupHeadingConfig } from './heading.js';
-
-export const nodes = { heading };
diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts
index 3164cda13..4c5614b56 100644
--- a/packages/integrations/markdoc/src/runtime.ts
+++ b/packages/integrations/markdoc/src/runtime.ts
@@ -1,32 +1,56 @@
 import type { MarkdownHeading } from '@astrojs/markdown-remark';
-import Markdoc, {
-	type ConfigType as MarkdocConfig,
-	type RenderableTreeNode,
-} from '@markdoc/markdoc';
+import Markdoc, { type RenderableTreeNode } from '@markdoc/markdoc';
 import type { ContentEntryModule } from 'astro';
-import { setupHeadingConfig } from './nodes/index.js';
+import { setupHeadingConfig } from './heading-ids.js';
+import type { AstroMarkdocConfig } from './config.js';
+import { MarkdocError } from './utils.js';
 
 /** Used to call `Markdoc.transform()` and `Markdoc.Ast` in runtime modules */
 export { default as Markdoc } from '@markdoc/markdoc';
 
 /**
  * Merge user config with default config and set up context (ex. heading ID slugger)
- * Called on each file's individual transform
+ * Called on each file's individual transform.
+ * TODO: virtual module to merge configs per-build instead of per-file?
  */
-export function setupConfig(userConfig: MarkdocConfig, entry: ContentEntryModule): MarkdocConfig {
-	const defaultConfig: MarkdocConfig = {
-		// `setupXConfig()` could become a "plugin" convention as well?
+export function setupConfig(
+	userConfig: AstroMarkdocConfig,
+	entry: ContentEntryModule,
+	markdocConfigPath?: string
+): Omit {
+	let defaultConfig: AstroMarkdocConfig = {
 		...setupHeadingConfig(),
 		variables: { entry },
 	};
+
+	if (userConfig.extends) {
+		for (const extension of userConfig.extends) {
+			if (extension instanceof Promise) {
+				throw new MarkdocError({
+					message: 'An extension passed to `extends` in your markdoc config returns a Promise.',
+					hint: 'Call `await` for async extensions. Example: `extends: [await myExtension()]`',
+					location: {
+						file: markdocConfigPath,
+					},
+				});
+			}
+
+			defaultConfig = mergeConfig(defaultConfig, extension);
+		}
+	}
+
 	return mergeConfig(defaultConfig, userConfig);
 }
 
 /** Merge function from `@markdoc/markdoc` internals */
-function mergeConfig(configA: MarkdocConfig, configB: MarkdocConfig): MarkdocConfig {
+function mergeConfig(configA: AstroMarkdocConfig, configB: AstroMarkdocConfig): AstroMarkdocConfig {
 	return {
 		...configA,
 		...configB,
+		ctx: {
+			...configA.ctx,
+			...configB.ctx,
+		},
 		tags: {
 			...configA.tags,
 			...configB.tags,
diff --git a/packages/integrations/markdoc/test/syntax-highlighting.test.js b/packages/integrations/markdoc/test/syntax-highlighting.test.js
new file mode 100644
index 000000000..ef1845eb9
--- /dev/null
+++ b/packages/integrations/markdoc/test/syntax-highlighting.test.js
@@ -0,0 +1,89 @@
+import { parseHTML } from 'linkedom';
+import { expect } from 'chai';
+import Markdoc from '@markdoc/markdoc';
+import { shiki } from '../dist/config.js';
+import { setupConfig } from '../dist/runtime.js';
+import { isHTMLString } from 'astro/runtime/server/index.js';
+
+const entry = `
+\`\`\`ts
+const highlighting = true;
+\`\`\`
+
+\`\`\`css
+.highlighting {
+	color: red;
+}
+\`\`\`
+`;
+
+describe('Markdoc - syntax highlighting', () => {
+	it('transforms with defaults', async () => {
+		const ast = Markdoc.parse(entry);
+		const content = Markdoc.transform(ast, await getConfigExtendingShiki());
+
+		expect(content.children).to.have.lengthOf(2);
+		for (const codeBlock of content.children) {
+			expect(isHTMLString(codeBlock)).to.be.true;
+
+			const pre = parsePreTag(codeBlock);
+			expect(pre.classList).to.include('astro-code');
+			expect(pre.classList).to.include('github-dark');
+		}
+	});
+	it('transforms with `theme` property', async () => {
+		const ast = Markdoc.parse(entry);
+		const content = Markdoc.transform(
+			ast,
+			await getConfigExtendingShiki({
+				theme: 'dracula',
+			})
+		);
+		expect(content.children).to.have.lengthOf(2);
+		for (const codeBlock of content.children) {
+			expect(isHTMLString(codeBlock)).to.be.true;
+
+			const pre = parsePreTag(codeBlock);
+			expect(pre.classList).to.include('astro-code');
+			expect(pre.classList).to.include('dracula');
+		}
+	});
+	it('transforms with `wrap` property', async () => {
+		const ast = Markdoc.parse(entry);
+		const content = Markdoc.transform(
+			ast,
+			await getConfigExtendingShiki({
+				wrap: true,
+			})
+		);
+		expect(content.children).to.have.lengthOf(2);
+		for (const codeBlock of content.children) {
+			expect(isHTMLString(codeBlock)).to.be.true;
+
+			const pre = parsePreTag(codeBlock);
+			expect(pre.getAttribute('style')).to.include('white-space: pre-wrap');
+			expect(pre.getAttribute('style')).to.include('word-wrap: break-word');
+		}
+	});
+});
+
+/**
+ * @param {import('astro').ShikiConfig} config
+ * @returns {import('../src/config.js').AstroMarkdocConfig}
+ */
+async function getConfigExtendingShiki(config) {
+	return setupConfig({
+		extends: [await shiki(config)],
+	});
+}
+
+/**
+ * @param {string} html
+ * @returns {HTMLPreElement}
+ */
+function parsePreTag(html) {
+	const { document } = parseHTML(html);
+	const pre = document.querySelector('pre');
+	expect(pre).to.exist;
+	return pre;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f91304cf4..f5f47aa8c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4025,6 +4025,9 @@ importers:
       rollup:
         specifier: ^3.20.1
         version: 3.20.1
+      shiki:
+        specifier: ^0.14.1
+        version: 0.14.1
       vite:
         specifier: ^4.3.1
         version: 4.3.1(@types/node@18.16.3)(sass@1.52.2)

From dc31b8a722136eff90a600380a6419a37808d614 Mon Sep 17 00:00:00 2001
From: bholmesdev 
Date: Wed, 24 May 2023 20:54:43 +0000
Subject: [PATCH 02/26] [ci] format

---
 packages/astro/src/runtime/server/index.ts            | 2 +-
 packages/integrations/markdoc/src/extensions/shiki.ts | 4 ++--
 packages/integrations/markdoc/src/heading-ids.ts      | 4 ++--
 packages/integrations/markdoc/src/runtime.ts          | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts
index 021e55a56..b7542d39b 100644
--- a/packages/astro/src/runtime/server/index.ts
+++ b/packages/astro/src/runtime/server/index.ts
@@ -5,9 +5,9 @@ export {
 	escapeHTML,
 	HTMLBytes,
 	HTMLString,
+	isHTMLString,
 	markHTMLString,
 	unescapeHTML,
-	isHTMLString,
 } from './escape.js';
 export { renderJSX } from './jsx.js';
 export {
diff --git a/packages/integrations/markdoc/src/extensions/shiki.ts b/packages/integrations/markdoc/src/extensions/shiki.ts
index 96d91d541..c61a8ad71 100644
--- a/packages/integrations/markdoc/src/extensions/shiki.ts
+++ b/packages/integrations/markdoc/src/extensions/shiki.ts
@@ -1,9 +1,9 @@
 // @ts-expect-error Cannot find module 'astro/runtime/server/index.js' or its corresponding type declarations.
-import { unescapeHTML } from 'astro/runtime/server/index.js';
+import Markdoc from '@markdoc/markdoc';
 import type { ShikiConfig } from 'astro';
+import { unescapeHTML } from 'astro/runtime/server/index.js';
 import type * as shikiTypes from 'shiki';
 import type { AstroMarkdocConfig } from '../config.js';
-import Markdoc from '@markdoc/markdoc';
 import { MarkdocError } from '../utils.js';
 
 // Map of old theme names to new names to preserve compatibility when we upgrade shiki
diff --git a/packages/integrations/markdoc/src/heading-ids.ts b/packages/integrations/markdoc/src/heading-ids.ts
index 57b84d059..5c2f197f2 100644
--- a/packages/integrations/markdoc/src/heading-ids.ts
+++ b/packages/integrations/markdoc/src/heading-ids.ts
@@ -1,7 +1,7 @@
-import Markdoc, { type ConfigType, type RenderableTreeNode, type Schema } from '@markdoc/markdoc';
+import Markdoc, { type RenderableTreeNode, type Schema } from '@markdoc/markdoc';
 import Slugger from 'github-slugger';
-import { getTextContent } from './runtime.js';
 import type { AstroMarkdocConfig } from './config.js';
+import { getTextContent } from './runtime.js';
 import { MarkdocError } from './utils.js';
 
 function getSlug(
diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts
index 4c5614b56..5a59d5fef 100644
--- a/packages/integrations/markdoc/src/runtime.ts
+++ b/packages/integrations/markdoc/src/runtime.ts
@@ -1,8 +1,8 @@
 import type { MarkdownHeading } from '@astrojs/markdown-remark';
 import Markdoc, { type RenderableTreeNode } from '@markdoc/markdoc';
 import type { ContentEntryModule } from 'astro';
-import { setupHeadingConfig } from './heading-ids.js';
 import type { AstroMarkdocConfig } from './config.js';
+import { setupHeadingConfig } from './heading-ids.js';
 import { MarkdocError } from './utils.js';
 
 /** Used to call `Markdoc.transform()` and `Markdoc.Ast` in runtime modules */

From 59d8c50b8426cd6825abc07405041779b7999022 Mon Sep 17 00:00:00 2001
From: Akash Rajpurohit 
Date: Thu, 25 May 2023 13:54:59 +0530
Subject: [PATCH 03/26] fix: :adhesive_bandage: update partytown options ts
 type (#7175)

---
 .changeset/curvy-hotels-study.md             |  5 +++++
 packages/integrations/partytown/src/index.ts | 10 ++++------
 2 files changed, 9 insertions(+), 6 deletions(-)
 create mode 100644 .changeset/curvy-hotels-study.md

diff --git a/.changeset/curvy-hotels-study.md b/.changeset/curvy-hotels-study.md
new file mode 100644
index 000000000..7ac2e8033
--- /dev/null
+++ b/.changeset/curvy-hotels-study.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/partytown': patch
+---
+
+fix typescript type for partytown options
diff --git a/packages/integrations/partytown/src/index.ts b/packages/integrations/partytown/src/index.ts
index 95841a06c..97badee1b 100644
--- a/packages/integrations/partytown/src/index.ts
+++ b/packages/integrations/partytown/src/index.ts
@@ -9,17 +9,15 @@ import { fileURLToPath } from 'url';
 import sirv from './sirv.js';
 const resolve = createRequire(import.meta.url).resolve;
 
-type PartytownOptions =
-	| {
-			config?: PartytownConfig;
-	  }
-	| undefined;
+type PartytownOptions = {
+	config?: PartytownConfig;
+};
 
 function appendForwardSlash(str: string) {
 	return str.endsWith('/') ? str : str + '/';
 }
 
-export default function createPlugin(options: PartytownOptions): AstroIntegration {
+export default function createPlugin(options?: PartytownOptions): AstroIntegration {
 	let config: AstroConfig;
 	let partytownSnippetHtml: string;
 	const partytownEntrypoint = resolve('@builder.io/partytown/package.json');

From 6ca3b5a9e8b9aa19a9436043f8ead41e7938c32e Mon Sep 17 00:00:00 2001
From: Alexander Niebuhr 
Date: Thu, 25 May 2023 11:15:26 +0200
Subject: [PATCH 04/26] Export ChangeFreqEnum type for sitemap integration
 (#7028)

---
 .changeset/clever-garlics-doubt.md         | 5 +++++
 packages/integrations/sitemap/src/index.ts | 1 +
 2 files changed, 6 insertions(+)
 create mode 100644 .changeset/clever-garlics-doubt.md

diff --git a/.changeset/clever-garlics-doubt.md b/.changeset/clever-garlics-doubt.md
new file mode 100644
index 000000000..a09ca541e
--- /dev/null
+++ b/.changeset/clever-garlics-doubt.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/sitemap': patch
+---
+
+exported enum type to support typescript > 5.0
diff --git a/packages/integrations/sitemap/src/index.ts b/packages/integrations/sitemap/src/index.ts
index dc1cb89d6..46ff694fd 100644
--- a/packages/integrations/sitemap/src/index.ts
+++ b/packages/integrations/sitemap/src/index.ts
@@ -12,6 +12,7 @@ import { generateSitemap } from './generate-sitemap.js';
 import { Logger } from './utils/logger.js';
 import { validateOptions } from './validate-options.js';
 
+export { EnumChangefreq as ChangeFreqEnum } from 'sitemap';
 export type ChangeFreq = `${EnumChangefreq}`;
 export type SitemapItem = Pick<
 	SitemapItemLoose,

From 8727b5bd8570db7658d015e10290a325ca1e3c65 Mon Sep 17 00:00:00 2001
From: Emanuele Stoppa 
Date: Thu, 25 May 2023 10:29:11 +0100
Subject: [PATCH 05/26] fix: formatting comment (#7206)

---
 packages/integrations/markdoc/src/extensions/shiki.ts | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/integrations/markdoc/src/extensions/shiki.ts b/packages/integrations/markdoc/src/extensions/shiki.ts
index c61a8ad71..d03a60139 100644
--- a/packages/integrations/markdoc/src/extensions/shiki.ts
+++ b/packages/integrations/markdoc/src/extensions/shiki.ts
@@ -1,7 +1,9 @@
+// leave space, so organize imports doesn't mess up comments
 // @ts-expect-error Cannot find module 'astro/runtime/server/index.js' or its corresponding type declarations.
+import { unescapeHTML } from 'astro/runtime/server/index.js';
+
 import Markdoc from '@markdoc/markdoc';
 import type { ShikiConfig } from 'astro';
-import { unescapeHTML } from 'astro/runtime/server/index.js';
 import type * as shikiTypes from 'shiki';
 import type { AstroMarkdocConfig } from '../config.js';
 import { MarkdocError } from '../utils.js';

From 20a97922aad2d7f687284c8f1bdbea0f30ef36ed Mon Sep 17 00:00:00 2001
From: Bjorn Lu 
Date: Thu, 25 May 2023 19:42:57 +0800
Subject: [PATCH 06/26] Document partytown script block by extensions (#7205)

---
 packages/integrations/partytown/README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/integrations/partytown/README.md b/packages/integrations/partytown/README.md
index 4ad0621d6..81b3457b6 100644
--- a/packages/integrations/partytown/README.md
+++ b/packages/integrations/partytown/README.md
@@ -132,6 +132,8 @@ export default defineConfig ({
 
 ## Troubleshooting
 
+- If you're getting a `Failed to fetch` error, make sure you're not using any browser extensions that are blocking the script.
+
 For help, check out the `#support` channel on [Discord](https://astro.build/chat). Our friendly Support Squad members are here to help!
 
 You can also check our [Astro Integration Documentation][astro-integration] for more on integrations.

From ea16570b1e0929678170c10b06c011dc668d7013 Mon Sep 17 00:00:00 2001
From: Bjorn Lu 
Date: Thu, 25 May 2023 19:43:29 +0800
Subject: [PATCH 07/26] Add MDX optimize option (#7151)

Co-authored-by: Sarah Rainsberger 
---
 .changeset/dirty-singers-enjoy.md             |   5 +
 packages/integrations/mdx/README.md           |  66 +++++++++++
 packages/integrations/mdx/package.json        |   1 +
 packages/integrations/mdx/src/index.ts        |   4 +
 packages/integrations/mdx/src/plugins.ts      |   8 ++
 .../mdx/src/rehype-optimize-static.ts         | 105 ++++++++++++++++++
 .../fixtures/mdx-optimize/astro.config.mjs    |   9 ++
 .../test/fixtures/mdx-optimize/package.json   |   8 ++
 .../src/components/Blockquote.astro           |   3 +
 .../mdx-optimize/src/components/Strong.astro  |   3 +
 .../mdx-optimize/src/pages/_imported.mdx      |   3 +
 .../mdx-optimize/src/pages/import.astro       |  15 +++
 .../fixtures/mdx-optimize/src/pages/index.mdx |  15 +++
 .../mdx/test/mdx-optimize.test.js             |  47 ++++++++
 pnpm-lock.yaml                                |  12 ++
 15 files changed, 304 insertions(+)
 create mode 100644 .changeset/dirty-singers-enjoy.md
 create mode 100644 packages/integrations/mdx/src/rehype-optimize-static.ts
 create mode 100644 packages/integrations/mdx/test/fixtures/mdx-optimize/astro.config.mjs
 create mode 100644 packages/integrations/mdx/test/fixtures/mdx-optimize/package.json
 create mode 100644 packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Blockquote.astro
 create mode 100644 packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Strong.astro
 create mode 100644 packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/_imported.mdx
 create mode 100644 packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/import.astro
 create mode 100644 packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/index.mdx
 create mode 100644 packages/integrations/mdx/test/mdx-optimize.test.js

diff --git a/.changeset/dirty-singers-enjoy.md b/.changeset/dirty-singers-enjoy.md
new file mode 100644
index 000000000..b02f166fe
--- /dev/null
+++ b/.changeset/dirty-singers-enjoy.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/mdx': patch
+---
+
+Add `optimize` option for faster builds and rendering
diff --git a/packages/integrations/mdx/README.md b/packages/integrations/mdx/README.md
index 53c3c9be9..e24a25efc 100644
--- a/packages/integrations/mdx/README.md
+++ b/packages/integrations/mdx/README.md
@@ -83,6 +83,7 @@ You can configure how your MDX is rendered with the following options:
 - [Options inherited from Markdown config](#options-inherited-from-markdown-config)
 - [`extendMarkdownConfig`](#extendmarkdownconfig)
 - [`recmaPlugins`](#recmaplugins)
+- [`optimize`](#optimize)
 
 ### Options inherited from Markdown config
 
@@ -183,6 +184,71 @@ These are plugins that modify the output [estree](https://github.com/estree/estr
 
 We suggest [using AST Explorer](https://astexplorer.net/) to play with estree outputs, and trying [`estree-util-visit`](https://unifiedjs.com/explore/package/estree-util-visit/) for searching across JavaScript nodes.
 
+### `optimize`
+
+- **Type:** `boolean | { customComponentNames?: string[] }`
+
+This is an optional configuration setting to optimize the MDX output for faster builds and rendering via an internal rehype plugin. This may be useful if you have many MDX files and notice slow builds. However, this option may generate some unescaped HTML, so make sure your site's interactive parts still work correctly after enabling it.
+
+This is disabled by default. To enable MDX optimization, add the following to your MDX integration configuration:
+
+__`astro.config.mjs`__
+
+```js
+import { defineConfig } from 'astro/config';
+import mdx from '@astrojs/mdx';
+
+export default defineConfig({
+  integrations: [
+    mdx({
+      optimize: true,
+    })
+  ]
+});
+```
+
+#### `customComponentNames`
+
+- **Type:** `string[]`
+
+An optional property of `optimize` to prevent the MDX optimizer from handling any [custom components passed to imported MDX content via the components prop](https://docs.astro.build/en/guides/markdown-content/#custom-components-with-imported-mdx).
+
+You will need to exclude these components from optimization as the optimizer eagerly converts content into a static string, which will break custom components that needs to be dynamically rendered. 
+
+For example, the intended MDX output of the following is `...` in place of every `"

...

"`: + +```astro +--- +import { Content, components } from '../content.mdx'; +import Heading from '../Heading.astro'; +--- + + +``` + +To configure optimization for this using the `customComponentNames` property, specify an array of HTML element names that should be treated as custom components: + +__`astro.config.mjs`__ + +```js +import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; + +export default defineConfig({ + integrations: [ + mdx({ + optimize: { + // Prevent the optimizer from handling `h1` elements + // These will be treated as custom components + customComponentNames: ['h1'], + }, + }) + ] +}); +``` + +Note that if your MDX file [configures custom components using `export const components = { ... }`](https://docs.astro.build/en/guides/markdown-content/#assigning-custom-components-to-html-elements), then you do not need to manually configure this option. The optimizer will automatically detect them. + ## Examples * The [Astro MDX starter template](https://github.com/withastro/astro/tree/latest/examples/with-mdx) shows how to use MDX files in your Astro project. diff --git a/packages/integrations/mdx/package.json b/packages/integrations/mdx/package.json index 63d520cb1..4b14dad2c 100644 --- a/packages/integrations/mdx/package.json +++ b/packages/integrations/mdx/package.json @@ -42,6 +42,7 @@ "estree-util-visit": "^1.2.0", "github-slugger": "^1.4.0", "gray-matter": "^4.0.3", + "hast-util-to-html": "^8.0.4", "kleur": "^4.1.4", "rehype-raw": "^6.1.1", "remark-frontmatter": "^4.0.1", diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index 0d1ff9d13..e11cd1ac5 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -11,6 +11,7 @@ import { SourceMapGenerator } from 'source-map'; import { VFile } from 'vfile'; import type { Plugin as VitePlugin } from 'vite'; import { getRehypePlugins, getRemarkPlugins, recmaInjectImportMetaEnvPlugin } from './plugins.js'; +import type { OptimizeOptions } from './rehype-optimize-static.js'; import { getFileInfo, ignoreStringPlugins, parseFrontmatter } from './utils.js'; export type MdxOptions = Omit & { @@ -21,6 +22,7 @@ export type MdxOptions = Omit & { @@ -194,6 +196,7 @@ function markdownConfigToMdxOptions(markdownConfig: typeof markdownConfigDefault remarkPlugins: ignoreStringPlugins(markdownConfig.remarkPlugins), rehypePlugins: ignoreStringPlugins(markdownConfig.rehypePlugins), remarkRehype: (markdownConfig.remarkRehype as any) ?? {}, + optimize: false, }; } @@ -214,6 +217,7 @@ function applyDefaultOptions({ remarkPlugins: options.remarkPlugins ?? defaults.remarkPlugins, rehypePlugins: options.rehypePlugins ?? defaults.rehypePlugins, shikiConfig: options.shikiConfig ?? defaults.shikiConfig, + optimize: options.optimize ?? defaults.optimize, }; } diff --git a/packages/integrations/mdx/src/plugins.ts b/packages/integrations/mdx/src/plugins.ts index af9950451..94c3c10ba 100644 --- a/packages/integrations/mdx/src/plugins.ts +++ b/packages/integrations/mdx/src/plugins.ts @@ -15,6 +15,7 @@ import type { VFile } from 'vfile'; import type { MdxOptions } from './index.js'; import { rehypeInjectHeadingsExport } from './rehype-collect-headings.js'; import rehypeMetaString from './rehype-meta-string.js'; +import { rehypeOptimizeStatic } from './rehype-optimize-static.js'; import { remarkImageToComponent } from './remark-images-to-component.js'; import remarkPrism from './remark-prism.js'; import remarkShiki from './remark-shiki.js'; @@ -144,6 +145,13 @@ export function getRehypePlugins(mdxOptions: MdxOptions): PluggableList { // computed from `astro.data.frontmatter` in VFile data rehypeApplyFrontmatterExport, ]; + + if (mdxOptions.optimize) { + // Convert user `optimize` option to compatible `rehypeOptimizeStatic` option + const options = mdxOptions.optimize === true ? undefined : mdxOptions.optimize; + rehypePlugins.push([rehypeOptimizeStatic, options]); + } + return rehypePlugins; } diff --git a/packages/integrations/mdx/src/rehype-optimize-static.ts b/packages/integrations/mdx/src/rehype-optimize-static.ts new file mode 100644 index 000000000..c476f7c83 --- /dev/null +++ b/packages/integrations/mdx/src/rehype-optimize-static.ts @@ -0,0 +1,105 @@ +import { visit } from 'estree-util-visit'; +import { toHtml } from 'hast-util-to-html'; + +// accessing untyped hast and mdx types +type Node = any; + +export interface OptimizeOptions { + customComponentNames?: string[]; +} + +const exportConstComponentsRe = /export\s+const\s+components\s*=/; + +/** + * For MDX only, collapse static subtrees of the hast into `set:html`. Subtrees + * do not include any MDX elements. + * + * This optimization reduces the JS output as more content are represented as a + * string instead, which also reduces the AST size that Rollup holds in memory. + */ +export function rehypeOptimizeStatic(options?: OptimizeOptions) { + return (tree: any) => { + // A set of non-static components to avoid collapsing when walking the tree + // as they need to be preserved as JSX to be rendered dynamically. + const customComponentNames = new Set(options?.customComponentNames); + + // Find `export const components = { ... }` and get it's object's keys to be + // populated into `customComponentNames`. This configuration is used to render + // some HTML elements as custom components, and we also want to avoid collapsing them. + for (const child of tree.children) { + if (child.type === 'mdxjsEsm' && exportConstComponentsRe.test(child.value)) { + // Try to loosely get the object property nodes + const objectPropertyNodes = child.data.estree.body[0]?.declarations?.[0]?.init?.properties; + if (objectPropertyNodes) { + for (const objectPropertyNode of objectPropertyNodes) { + const componentName = objectPropertyNode.key?.name ?? objectPropertyNode.key?.value; + if (componentName) { + customComponentNames.add(componentName); + } + } + } + } + } + + // All possible elements that could be the root of a subtree + const allPossibleElements = new Set(); + // The current collapsible element stack while traversing the tree + const elementStack: Node[] = []; + + visit(tree, { + enter(node) { + // @ts-expect-error read tagName naively + const isCustomComponent = node.tagName && customComponentNames.has(node.tagName); + // For nodes that can't be optimized, eliminate all elements in the + // `elementStack` from the `allPossibleElements` set. + if (node.type.startsWith('mdx') || isCustomComponent) { + for (const el of elementStack) { + allPossibleElements.delete(el); + } + // Micro-optimization: While this destroys the meaning of an element + // stack for this node, things will still work but we won't repeatedly + // run the above for other nodes anymore. If this is confusing, you can + // comment out the code below when reading. + elementStack.length = 0; + } + // For possible subtree root nodes, record them in `elementStack` and + // `allPossibleElements` to be used in the "leave" hook below. + if (node.type === 'element' || node.type === 'mdxJsxFlowElement') { + elementStack.push(node); + allPossibleElements.add(node); + } + }, + leave(node, _, __, parents) { + // Do the reverse of the if condition above, popping the `elementStack`, + // and consolidating `allPossibleElements` as a subtree root. + if (node.type === 'element' || node.type === 'mdxJsxFlowElement') { + elementStack.pop(); + // Many possible elements could be part of a subtree, in order to find + // the root, we check the parent of the element we're popping. If the + // parent exists in `allPossibleElements`, then we're definitely not + // the root, so remove ourselves. This will work retroactively as we + // climb back up the tree. + const parent = parents[parents.length - 1]; + if (allPossibleElements.has(parent)) { + allPossibleElements.delete(node); + } + } + }, + }); + + // For all possible subtree roots, collapse them into `set:html` and + // strip of their children + for (const el of allPossibleElements) { + if (el.type === 'mdxJsxFlowElement') { + el.attributes.push({ + type: 'mdxJsxAttribute', + name: 'set:html', + value: toHtml(el.children), + }); + } else { + el.properties['set:html'] = toHtml(el.children); + } + el.children = []; + } + }; +} diff --git a/packages/integrations/mdx/test/fixtures/mdx-optimize/astro.config.mjs b/packages/integrations/mdx/test/fixtures/mdx-optimize/astro.config.mjs new file mode 100644 index 000000000..b92b48617 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-optimize/astro.config.mjs @@ -0,0 +1,9 @@ +import mdx from '@astrojs/mdx'; + +export default { + integrations: [mdx({ + optimize: { + customComponentNames: ['strong'] + } + })] +} diff --git a/packages/integrations/mdx/test/fixtures/mdx-optimize/package.json b/packages/integrations/mdx/test/fixtures/mdx-optimize/package.json new file mode 100644 index 000000000..69120477e --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-optimize/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/mdx-optimize", + "private": true, + "dependencies": { + "@astrojs/mdx": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Blockquote.astro b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Blockquote.astro new file mode 100644 index 000000000..aa55e82b1 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Blockquote.astro @@ -0,0 +1,3 @@ +
+ +
diff --git a/packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Strong.astro b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Strong.astro new file mode 100644 index 000000000..3c0b39ffc --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/components/Strong.astro @@ -0,0 +1,3 @@ + + + diff --git a/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/_imported.mdx b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/_imported.mdx new file mode 100644 index 000000000..efe520341 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/_imported.mdx @@ -0,0 +1,3 @@ +I once heard a very **inspirational** quote: + +> I like pancakes diff --git a/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/import.astro b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/import.astro new file mode 100644 index 000000000..b522a3028 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/import.astro @@ -0,0 +1,15 @@ +--- +import { Content, components } from './index.mdx' +import Strong from '../components/Strong.astro' +--- + + + + + Import MDX component + + +

Astro page

+ + + diff --git a/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/index.mdx b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/index.mdx new file mode 100644 index 000000000..a3545bd8c --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/mdx-optimize/src/pages/index.mdx @@ -0,0 +1,15 @@ +import Blockquote from '../components/Blockquote.astro' + +export const components = { + blockquote: Blockquote +} + +# MDX page + +I once heard a very inspirational quote: + +> I like pancakes + +```js +const pancakes = 'yummy' +``` diff --git a/packages/integrations/mdx/test/mdx-optimize.test.js b/packages/integrations/mdx/test/mdx-optimize.test.js new file mode 100644 index 000000000..2e67a7064 --- /dev/null +++ b/packages/integrations/mdx/test/mdx-optimize.test.js @@ -0,0 +1,47 @@ +import { expect } from 'chai'; +import { parseHTML } from 'linkedom'; +import { loadFixture } from '../../../astro/test/test-utils.js'; + +const FIXTURE_ROOT = new URL('./fixtures/mdx-optimize/', import.meta.url); + +describe('MDX optimize', () => { + let fixture; + before(async () => { + fixture = await loadFixture({ + root: FIXTURE_ROOT, + }); + await fixture.build(); + }); + + it('renders an MDX page fine', async () => { + const html = await fixture.readFile('/index.html'); + const { document } = parseHTML(html); + + expect(document.querySelector('h1').textContent).include('MDX page'); + expect(document.querySelector('p').textContent).include( + 'I once heard a very inspirational quote:' + ); + + const blockquote = document.querySelector('blockquote.custom-blockquote'); + expect(blockquote).to.not.be.null; + expect(blockquote.textContent).to.include('I like pancakes'); + + const code = document.querySelector('pre.astro-code'); + expect(code).to.not.be.null; + expect(code.textContent).to.include(`const pancakes = 'yummy'`); + }); + + it('renders an Astro page that imports MDX fine', async () => { + const html = await fixture.readFile('/import/index.html'); + const { document } = parseHTML(html); + + expect(document.querySelector('h1').textContent).include('Astro page'); + expect(document.querySelector('p').textContent).include( + 'I once heard a very inspirational quote:' + ); + + const blockquote = document.querySelector('blockquote.custom-blockquote'); + expect(blockquote).to.not.be.null; + expect(blockquote.textContent).to.include('I like pancakes'); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5f47aa8c..9d6401b12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4143,6 +4143,9 @@ importers: gray-matter: specifier: ^4.0.3 version: 4.0.3 + hast-util-to-html: + specifier: ^8.0.4 + version: 8.0.4 kleur: specifier: ^4.1.4 version: 4.1.5 @@ -4316,6 +4319,15 @@ importers: specifier: ^18.1.0 version: 18.2.0(react@18.2.0) + packages/integrations/mdx/test/fixtures/mdx-optimize: + dependencies: + '@astrojs/mdx': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/mdx/test/fixtures/mdx-page: dependencies: '@astrojs/mdx': From f5a8cffac22c9e33fad6f47f7d166b55c86ad87b Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Thu, 25 May 2023 14:59:25 +0200 Subject: [PATCH 08/26] =?UTF-8?q?fix(vercel):=20Fix=20typo=20in=20README?= =?UTF-8?q?=20=F0=9F=A4=A6=E2=80=8D=E2=99=80=EF=B8=8F=20(#7208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/plenty-geese-fold.md | 5 +++++ packages/integrations/vercel/README.md | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/plenty-geese-fold.md diff --git a/.changeset/plenty-geese-fold.md b/.changeset/plenty-geese-fold.md new file mode 100644 index 000000000..aa7783e92 --- /dev/null +++ b/.changeset/plenty-geese-fold.md @@ -0,0 +1,5 @@ +--- +'@astrojs/vercel': patch +--- + +Fix `imagesConfig` being wrongly spelt as `imageConfig` in the README diff --git a/packages/integrations/vercel/README.md b/packages/integrations/vercel/README.md index 0af5632b5..6b0848d44 100644 --- a/packages/integrations/vercel/README.md +++ b/packages/integrations/vercel/README.md @@ -108,7 +108,7 @@ export default defineConfig({ }); ``` -### imageConfig +### imagesConfig **Type:** `VercelImageConfig`
**Available for:** Edge, Serverless, Static @@ -124,7 +124,7 @@ import vercel from '@astrojs/vercel/static'; export default defineConfig({ output: 'server', adapter: vercel({ - imageConfig: { + imagesConfig: { sizes: [320, 640, 1280] } }) From 8b041bf57c76830c4070330270521e05d8e58474 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 25 May 2023 14:28:40 +0100 Subject: [PATCH 09/26] refactor: emit pages as physical entry points (#7193) --- .changeset/dry-taxis-suffer.md | 6 + packages/astro/src/@types/astro.ts | 2 +- packages/astro/src/core/app/index.ts | 8 +- packages/astro/src/core/app/types.ts | 3 +- packages/astro/src/core/build/generate.ts | 32 ++-- packages/astro/src/core/build/graph.ts | 6 +- packages/astro/src/core/build/internal.ts | 23 ++- .../astro/src/core/build/plugins/README.md | 144 ++++++++++++++++++ .../core/build/plugins/plugin-middleware.ts | 20 +-- .../src/core/build/plugins/plugin-pages.ts | 83 ++++++---- .../src/core/build/plugins/plugin-ssr.ts | 71 ++++++--- packages/astro/src/core/build/static-build.ts | 46 +++++- packages/astro/src/core/build/types.ts | 4 +- 13 files changed, 354 insertions(+), 94 deletions(-) create mode 100644 .changeset/dry-taxis-suffer.md create mode 100644 packages/astro/src/core/build/plugins/README.md diff --git a/.changeset/dry-taxis-suffer.md b/.changeset/dry-taxis-suffer.md new file mode 100644 index 000000000..b0cb68b24 --- /dev/null +++ b/.changeset/dry-taxis-suffer.md @@ -0,0 +1,6 @@ +--- +'astro': patch +--- + +Refactor how pages are emitted during the internal bundling. Now each +page is emitted as a separate entry point. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index c7b1c4f59..c494cf127 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1691,7 +1691,7 @@ export interface APIContext = Record { * context.locals.greeting = "Hello!"; - * next(); + * return next(); * }); * ``` * Inside a `.astro` file: diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts index 1e2dd1d24..90e17f438 100644 --- a/packages/astro/src/core/app/index.ts +++ b/packages/astro/src/core/app/index.ts @@ -32,8 +32,6 @@ export { deserializeManifest } from './common.js'; const clientLocalsSymbol = Symbol.for('astro.locals'); -export const pagesVirtualModuleId = '@astrojs-pages-virtual-entry'; -export const resolvedPagesVirtualModuleId = '\0' + pagesVirtualModuleId; const responseSentSymbol = Symbol.for('astro.responseSent'); export interface MatchOptions { @@ -139,7 +137,8 @@ export class App { defaultStatus = 404; } - let mod = await this.#manifest.pageMap.get(routeData.component)!(); + let page = await this.#manifest.pageMap.get(routeData.component)!(); + let mod = await page.page(); if (routeData.type === 'page') { let response = await this.#renderPage(request, routeData, mod, defaultStatus); @@ -148,7 +147,8 @@ export class App { if (response.status === 500 || response.status === 404) { const errorPageData = matchRoute('/' + response.status, this.#manifestData); if (errorPageData && errorPageData.route !== routeData.route) { - mod = await this.#manifest.pageMap.get(errorPageData.component)!(); + page = await this.#manifest.pageMap.get(errorPageData.component)!(); + mod = await page.page(); try { let errorResponse = await this.#renderPage( request, diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 3747e96e3..0fa2c034b 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -8,6 +8,7 @@ import type { SSRLoadedRenderer, SSRResult, } from '../../@types/astro'; +import type { SinglePageBuiltModule } from '../build/types'; export type ComponentPath = string; @@ -31,7 +32,7 @@ export interface RouteInfo { export type SerializedRouteInfo = Omit & { routeData: SerializedRouteData; }; -type ImportComponentInstance = () => Promise; +type ImportComponentInstance = () => Promise; export interface SSRManifest { adapterName: string; diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 5a9f075c4..3c24aa4bc 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -20,7 +20,11 @@ import { generateImage as generateImageInternal, getStaticImageList, } from '../../assets/generate.js'; -import { hasPrerenderedPages, type BuildInternals } from '../../core/build/internal.js'; +import { + hasPrerenderedPages, + type BuildInternals, + eachPageDataFromEntryPoint, +} from '../../core/build/internal.js'; import { prependForwardSlash, removeLeadingForwardSlash, @@ -47,11 +51,12 @@ import { getOutDirWithinCwd, getOutFile, getOutFolder } from './common.js'; import { cssOrder, eachPageData, getPageDataByComponent, mergeInlineCss } from './internal.js'; import type { PageBuildData, - SingleFileBuiltModule, + SinglePageBuiltModule, StaticBuildOptions, StylesheetAsset, } from './types'; import { getTimeStat } from './util.js'; +import { ASTRO_PAGE_MODULE_ID } from './plugins/plugin-pages'; function shouldSkipDraft(pageModule: ComponentInstance, settings: AstroSettings): boolean { return ( @@ -99,18 +104,23 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn const verb = ssr ? 'prerendering' : 'generating'; info(opts.logging, null, `\n${bgGreen(black(` ${verb} static routes `))}`); - const ssrEntryURL = new URL('./' + serverEntry + `?time=${Date.now()}`, outFolder); - const ssrEntry = await import(ssrEntryURL.toString()); const builtPaths = new Set(); if (ssr) { - for (const pageData of eachPageData(internals)) { - if (pageData.route.prerender) - await generatePage(opts, internals, pageData, ssrEntry, builtPaths); + for (const [pageData, filePath] of eachPageDataFromEntryPoint(internals)) { + if (pageData.route.prerender) { + const ssrEntryURLPage = new URL('./' + filePath + `?time=${Date.now()}`, outFolder); + const ssrEntryPage = await import(ssrEntryURLPage.toString()); + + await generatePage(opts, internals, pageData, ssrEntryPage, builtPaths); + } } } else { - for (const pageData of eachPageData(internals)) { - await generatePage(opts, internals, pageData, ssrEntry, builtPaths); + for (const [pageData, filePath] of eachPageDataFromEntryPoint(internals)) { + const ssrEntryURLPage = new URL('./' + filePath + `?time=${Date.now()}`, outFolder); + const ssrEntryPage = await import(ssrEntryURLPage.toString()); + + await generatePage(opts, internals, pageData, ssrEntryPage, builtPaths); } } @@ -153,7 +163,7 @@ async function generatePage( opts: StaticBuildOptions, internals: BuildInternals, pageData: PageBuildData, - ssrEntry: SingleFileBuiltModule, + ssrEntry: SinglePageBuiltModule, builtPaths: Set ) { let timeStart = performance.now(); @@ -169,7 +179,7 @@ async function generatePage( .map(({ sheet }) => sheet) .reduce(mergeInlineCss, []); - const pageModulePromise = ssrEntry.pageMap?.get(pageData.component); + const pageModulePromise = ssrEntry.page; const middleware = ssrEntry.middleware; if (!pageModulePromise) { diff --git a/packages/astro/src/core/build/graph.ts b/packages/astro/src/core/build/graph.ts index 68d264b10..3ce325309 100644 --- a/packages/astro/src/core/build/graph.ts +++ b/packages/astro/src/core/build/graph.ts @@ -1,6 +1,6 @@ import type { GetModuleInfo, ModuleInfo } from 'rollup'; -import { resolvedPagesVirtualModuleId } from '../app/index.js'; +import { ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js'; // This walks up the dependency graph and yields out each ModuleInfo object. export function* walkParentInfos( @@ -43,8 +43,8 @@ export function* walkParentInfos( // it is imported by the top-level virtual module. export function moduleIsTopLevelPage(info: ModuleInfo): boolean { return ( - info.importers[0] === resolvedPagesVirtualModuleId || - info.dynamicImporters[0] == resolvedPagesVirtualModuleId + info.importers[0]?.includes(ASTRO_PAGE_RESOLVED_MODULE_ID) || + info.dynamicImporters[0]?.includes(ASTRO_PAGE_RESOLVED_MODULE_ID) ); } diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index 1d69849c9..f6025238a 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -1,10 +1,10 @@ import type { Rollup } from 'vite'; import type { PageBuildData, StylesheetAsset, ViteID } from './types'; - import type { SSRResult } from '../../@types/astro'; import type { PageOptions } from '../../vite-plugin-astro/types'; import { prependForwardSlash, removeFileExtension } from '../path.js'; import { viteID } from '../util.js'; +import { ASTRO_PAGE_EXTENSION_POST_PATTERN, ASTRO_PAGE_MODULE_ID } from './plugins/plugin-pages.js'; export interface BuildInternals { /** @@ -97,7 +97,6 @@ export function createBuildInternals(): BuildInternals { hoistedScriptIdToPagesMap, entrySpecifierToBundleMap: new Map(), pageToBundleMap: new Map(), - pagesByComponent: new Map(), pageOptionsByPage: new Map(), pagesByViteID: new Map(), @@ -215,6 +214,26 @@ export function* eachPageData(internals: BuildInternals) { yield* internals.pagesByComponent.values(); } +export function* eachPageDataFromEntryPoint( + internals: BuildInternals +): Generator<[PageBuildData, string]> { + for (const [entryPoint, filePath] of internals.entrySpecifierToBundleMap) { + if (entryPoint.includes(ASTRO_PAGE_MODULE_ID)) { + const [, pageName] = entryPoint.split(':'); + const pageData = internals.pagesByComponent.get( + `${pageName.replace(ASTRO_PAGE_EXTENSION_POST_PATTERN, '.')}` + ); + if (!pageData) { + throw new Error( + "Build failed. Astro couldn't find the emitted page from " + pageName + ' pattern' + ); + } + + yield [pageData, filePath]; + } + } +} + export function hasPrerenderedPages(internals: BuildInternals) { for (const pageData of eachPageData(internals)) { if (pageData.route.prerender) { diff --git a/packages/astro/src/core/build/plugins/README.md b/packages/astro/src/core/build/plugins/README.md new file mode 100644 index 000000000..32ac8c448 --- /dev/null +++ b/packages/astro/src/core/build/plugins/README.md @@ -0,0 +1,144 @@ +# Plugin directory (WIP) + +This file serves as developer documentation to explain how the internal plugins work + + +## `plugin-middleware` + +This plugin is responsible to retrieve the `src/middleware.{ts.js}` file and emit an entry point during the SSR build. + +The final file is emitted only if the user has the middleware file. The final name of the file is `middleware.mjs`. + +This is **not** a virtual module. The plugin will try to resolve the physical file. + +## `plugin-renderers` + +This plugin is responsible to collect all the renderers inside an Astro application and emit them in a single file. + +The emitted file is called `renderers.mjs`. + +The emitted file has content similar to: + +```js +const renderers = [Object.assign({"name":"astro:jsx","serverEntrypoint":"astro/jsx/server.js","jsxImportSource":"astro"}, { ssr: server_default }),]; + +export { renderers }; +``` + +## `plugin-pages` + +This plugin is responsible to collect all pages inside an Astro application, and emit a single entry point file for each page. + +This plugin **will emit code** only when building a static site. + +In order to achieve that, the plugin emits these pages as **virtual modules**. Doing so allows us to bypass: +- rollup resolution of the files +- possible plugins that get triggered when the name of the module has an extension e.g. `.astro` + +The plugin does the following operations: +- loop through all the pages and collects their paths; +- with each path, we create a new [string](#plugin-pages-mapping-resolution) that will serve and virtual module for that particular page +- when resolving the page, we check if the `id` of the module starts with `@astro-page` +- once the module is resolved, we emit [the code of the module](#plugin-pages-code-generation) + + +### `plugin pages` mapping resolution + +The mapping is as follows: + +``` +src/pages/index.astro => @astro-page:src/pages/index@_@astro +``` + +1. We add a fixed prefix, which is used as virtual module naming convention; +2. We replace the dot that belongs extension with an arbitrary string. + +This kind of patterns will then allow us to retrieve the path physical path of the +file back from that string. This is important for the [code generation](#plugin-pages-code-generation) + + + +### `plugin pages` code generation + +When generating the code of the page, we will import and export the following modules: +- the `renderers.mjs` +- the `middleware.mjs` +- the page, via dynamic import + +The emitted code of each entry point will look like this: + +```js +export { renderers } from '../renderers.mjs'; +import { _ as _middleware } from '../middleware.mjs'; +import '../chunks/astro.540fbe4e.mjs'; + +const page = () => import('../chunks/pages/index.astro.8aad0438.mjs'); +const middleware = _middleware; + +export { middleware, page }; +``` + +If we have a `pages/` folder that looks like this: +``` +├── blog +│ ├── first.astro +│ └── post.astro +├── first.astro +├── index.astro +├── issue.md +└── second.astro +``` + +The emitted entry points will be stored inside a `pages/` folder, and they +will look like this: +``` +├── _astro +│ ├── first.132e69e0.css +│ ├── first.49cbf029.css +│ ├── post.a3e86c58.css +│ └── second.d178d0b2.css +├── chunks +│ ├── astro.540fbe4e.mjs +│ └── pages +│ ├── first.astro.493fa853.mjs +│ ├── index.astro.8aad0438.mjs +│ ├── issue.md.535b7d3b.mjs +│ ├── post.astro.26e892d9.mjs +│ └── second.astro.76540694.mjs +├── middleware.mjs +├── pages +│ ├── blog +│ │ ├── first.astro.mjs +│ │ └── post.astro.mjs +│ ├── first.astro.mjs +│ ├── index.astro.mjs +│ ├── issue.md.mjs +│ └── second.astro.mjs +└── renderers.mjs +``` + +Of course, all these files will be deleted by Astro at the end build. + +## `plugin-ssr` (WIP) + +This plugin is responsible to create a single `entry.mjs` file that will be used +in SSR. + +This plugin **will emit code** only when building an **SSR** site. + +The plugin will collect all the [virtual pages](#plugin-pages) and create +a JavaScript `Map`. These map will look like this: + +```js +const _page$0 = () => import("../chunks/.mjs") +const _page$1 = () => import("../chunks/.mjs") + +const pageMap = new Map([ + ["src/pages/index.astro", _page$0], + ["src/pages/about.astro", _page$1], +]) +``` + +It will also import the [`renderers`](#plugin-renderers) virtual module +and the [`middleware`](#plugin-middleware) virtual module. + diff --git a/packages/astro/src/core/build/plugins/plugin-middleware.ts b/packages/astro/src/core/build/plugins/plugin-middleware.ts index 507c4ae71..dd9872da3 100644 --- a/packages/astro/src/core/build/plugins/plugin-middleware.ts +++ b/packages/astro/src/core/build/plugins/plugin-middleware.ts @@ -6,9 +6,7 @@ import type { AstroBuildPlugin } from '../plugin'; import type { StaticBuildOptions } from '../types'; export const MIDDLEWARE_MODULE_ID = '@astro-middleware'; -export const RESOLVED_MIDDLEWARE_MODULE_ID = '\0@astro-middleware'; -let inputs: Set = new Set(); export function vitePluginMiddleware( opts: StaticBuildOptions, _internals: BuildInternals @@ -21,26 +19,14 @@ export function vitePluginMiddleware( } }, - resolveId(id) { + async resolveId(id) { if (id === MIDDLEWARE_MODULE_ID && opts.settings.config.experimental.middleware) { - return RESOLVED_MIDDLEWARE_MODULE_ID; - } - }, - - async load(id) { - if (id === RESOLVED_MIDDLEWARE_MODULE_ID && opts.settings.config.experimental.middleware) { - const imports: string[] = []; - const exports: string[] = []; - let middlewareId = await this.resolve( + const middlewareId = await this.resolve( `${opts.settings.config.srcDir.pathname}/${MIDDLEWARE_PATH_SEGMENT_NAME}` ); if (middlewareId) { - imports.push(`import { onRequest } from "${middlewareId.id}"`); - exports.push(`export { onRequest }`); + return middlewareId.id; } - const result = [imports.join('\n'), exports.join('\n')]; - - return result.join('\n'); } }, }; diff --git a/packages/astro/src/core/build/plugins/plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts index 051c85583..3ea0a61c8 100644 --- a/packages/astro/src/core/build/plugins/plugin-pages.ts +++ b/packages/astro/src/core/build/plugins/plugin-pages.ts @@ -1,11 +1,33 @@ import type { Plugin as VitePlugin } from 'vite'; -import { pagesVirtualModuleId, resolvedPagesVirtualModuleId } from '../../app/index.js'; import { addRollupInput } from '../add-rollup-input.js'; -import { eachPageData, type BuildInternals } from '../internal.js'; +import { type BuildInternals } from '../internal.js'; import type { AstroBuildPlugin } from '../plugin'; import type { StaticBuildOptions } from '../types'; import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js'; import { RENDERERS_MODULE_ID } from './plugin-renderers.js'; +import { extname } from 'node:path'; + +export const ASTRO_PAGE_MODULE_ID = '@astro-page:'; +export const ASTRO_PAGE_RESOLVED_MODULE_ID = '\0@astro-page:'; + +// This is an arbitrary string that we are going to replace the dot of the extension +export const ASTRO_PAGE_EXTENSION_POST_PATTERN = '@_@'; + +/** + * 1. We add a fixed prefix, which is used as virtual module naming convention; + * 2. We replace the dot that belongs extension with an arbitrary string. + * + * @param path + */ +export function getVirtualModulePageNameFromPath(path: string) { + // we mask the extension, so this virtual file + // so rollup won't trigger other plugins in the process + const extension = extname(path); + return `${ASTRO_PAGE_MODULE_ID}${path.replace( + extension, + extension.replace('.', ASTRO_PAGE_EXTENSION_POST_PATTERN) + )}`; +} function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): VitePlugin { return { @@ -13,42 +35,49 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V options(options) { if (opts.settings.config.output === 'static') { - return addRollupInput(options, [pagesVirtualModuleId]); + const inputs: Set = new Set(); + + for (const path of Object.keys(opts.allPages)) { + inputs.add(getVirtualModulePageNameFromPath(path)); + } + + return addRollupInput(options, Array.from(inputs)); } }, resolveId(id) { - if (id === pagesVirtualModuleId) { - return resolvedPagesVirtualModuleId; + if (id.startsWith(ASTRO_PAGE_MODULE_ID)) { + return '\0' + id; } }, async load(id) { - if (id === resolvedPagesVirtualModuleId) { - let importMap = ''; + if (id.startsWith(ASTRO_PAGE_RESOLVED_MODULE_ID)) { const imports: string[] = []; const exports: string[] = []; - const content: string[] = []; - let i = 0; - imports.push(`import { renderers } from "${RENDERERS_MODULE_ID}";`); - exports.push(`export { renderers };`); - for (const pageData of eachPageData(internals)) { - const variable = `_page${i}`; - imports.push( - `const ${variable} = () => import(${JSON.stringify(pageData.moduleSpecifier)});` - ); - importMap += `[${JSON.stringify(pageData.component)}, ${variable}],`; - i++; + // we remove the module name prefix from id, this will result into a string that will start with "src/..." + const pageName = id.slice(ASTRO_PAGE_RESOLVED_MODULE_ID.length); + // We replaced the `.` of the extension with ASTRO_PAGE_EXTENSION_POST_PATTERN, let's replace it back + const pageData = internals.pagesByComponent.get( + `${pageName.replace(ASTRO_PAGE_EXTENSION_POST_PATTERN, '.')}` + ); + if (pageData) { + const resolvedPage = await this.resolve(pageData.moduleSpecifier); + if (resolvedPage) { + imports.push(`const page = () => import(${JSON.stringify(pageData.moduleSpecifier)});`); + exports.push(`export { page }`); + + imports.push(`import { renderers } from "${RENDERERS_MODULE_ID}";`); + exports.push(`export { renderers };`); + + if (opts.settings.config.experimental.middleware) { + imports.push(`import * as _middleware from "${MIDDLEWARE_MODULE_ID}";`); + exports.push(`export const middleware = _middleware;`); + } + + return `${imports.join('\n')}${exports.join('\n')}`; + } } - - if (opts.settings.config.experimental.middleware) { - imports.push(`import * as _middleware from "${MIDDLEWARE_MODULE_ID}";`); - exports.push(`export const middleware = _middleware;`); - } - - content.push(`export const pageMap = new Map([${importMap}]);`); - - return `${imports.join('\n')}${content.join('\n')}${exports.join('\n')}`; } }, }; diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts index a6d02b792..b40faf20d 100644 --- a/packages/astro/src/core/build/plugins/plugin-ssr.ts +++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts @@ -1,62 +1,90 @@ import type { Plugin as VitePlugin } from 'vite'; -import type { AstroAdapter, AstroConfig } from '../../../@types/astro'; +import type { AstroAdapter } from '../../../@types/astro'; import type { SerializedRouteInfo, SerializedSSRManifest } from '../../app/types'; import type { StaticBuildOptions } from '../types'; - +import type { AstroBuildPlugin } from '../plugin'; import glob from 'fast-glob'; import { fileURLToPath } from 'url'; import { runHookBuildSsr } from '../../../integrations/index.js'; import { isHybridOutput } from '../../../prerender/utils.js'; import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../../vite-plugin-scripts/index.js'; -import { pagesVirtualModuleId } from '../../app/index.js'; import { joinPaths, prependForwardSlash } from '../../path.js'; import { serializeRouteData } from '../../routing/index.js'; import { addRollupInput } from '../add-rollup-input.js'; import { getOutFile, getOutFolder } from '../common.js'; import { cssOrder, mergeInlineCss, type BuildInternals } from '../internal.js'; -import type { AstroBuildPlugin } from '../plugin'; +import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js'; import { RENDERERS_MODULE_ID } from './plugin-renderers.js'; +import { getVirtualModulePageNameFromPath } from './plugin-pages.js'; -export const virtualModuleId = '@astrojs-ssr-virtual-entry'; -const resolvedVirtualModuleId = '\0' + virtualModuleId; +export const SSR_VIRTUAL_MODULE_ID = '@astrojs-ssr-virtual-entry'; +const RESOLVED_SSR_VIRTUAL_MODULE_ID = '\0' + SSR_VIRTUAL_MODULE_ID; const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@'; const replaceExp = new RegExp(`['"](${manifestReplace})['"]`, 'g'); function vitePluginSSR( internals: BuildInternals, adapter: AstroAdapter, - config: AstroConfig + options: StaticBuildOptions ): VitePlugin { return { name: '@astrojs/vite-plugin-astro-ssr', enforce: 'post', options(opts) { - return addRollupInput(opts, [virtualModuleId]); + return addRollupInput(opts, [SSR_VIRTUAL_MODULE_ID]); }, resolveId(id) { - if (id === virtualModuleId) { - return resolvedVirtualModuleId; + if (id === SSR_VIRTUAL_MODULE_ID) { + return RESOLVED_SSR_VIRTUAL_MODULE_ID; } }, - load(id) { - if (id === resolvedVirtualModuleId) { - let middleware = ''; + async load(id) { + if (id === RESOLVED_SSR_VIRTUAL_MODULE_ID) { + const { + settings: { config }, + allPages, + } = options; + const imports: string[] = []; + const contents: string[] = []; + const exports: string[] = []; + let middleware; if (config.experimental?.middleware === true) { - middleware = 'middleware: _main.middleware'; + imports.push(`import * as _middleware from "${MIDDLEWARE_MODULE_ID}"`); + middleware = 'middleware: _middleware'; } - return `import * as adapter from '${adapter.serverEntrypoint}'; + let i = 0; + const pageMap: string[] = []; + + for (const path of Object.keys(allPages)) { + const virtualModuleName = getVirtualModulePageNameFromPath(path); + let module = await this.resolve(virtualModuleName); + if (module) { + const variable = `_page${i}`; + // we need to use the non-resolved ID in order to resolve correctly the virtual module + imports.push(`const ${variable} = () => import("${virtualModuleName}");`); + + const pageData = internals.pagesByComponent.get(path); + if (pageData) { + pageMap.push(`[${JSON.stringify(pageData.component)}, ${variable}]`); + } + i++; + } + } + + contents.push(`const pageMap = new Map([${pageMap.join(',')}]);`); + exports.push(`export { pageMap }`); + const content = `import * as adapter from '${adapter.serverEntrypoint}'; import { renderers } from '${RENDERERS_MODULE_ID}'; -import * as _main from '${pagesVirtualModuleId}'; import { deserializeManifest as _deserializeManifest } from 'astro/app'; import { _privateSetManifestDontUseThis } from 'astro:ssr-manifest'; const _manifest = Object.assign(_deserializeManifest('${manifestReplace}'), { - pageMap: _main.pageMap, - renderers: _main.renderers, + pageMap, + renderers, ${middleware} }); _privateSetManifestDontUseThis(_manifest); const _args = ${adapter.args ? JSON.stringify(adapter.args) : 'undefined'}; -export * from '${pagesVirtualModuleId}'; + ${ adapter.exports ? `const _exports = adapter.createExports(_manifest, _args); @@ -77,6 +105,7 @@ const _start = 'start'; if(_start in adapter) { adapter[_start](_manifest, _args); }`; + return `${imports.join('\n')}${contents.join('\n')}${content}${exports.join('\n')}`; } return void 0; }, @@ -92,7 +121,7 @@ if(_start in adapter) { if (chunk.type === 'asset') { continue; } - if (chunk.modules[resolvedVirtualModuleId]) { + if (chunk.modules[RESOLVED_SSR_VIRTUAL_MODULE_ID]) { internals.ssrEntryChunk = chunk; delete bundle[chunkName]; } @@ -250,7 +279,7 @@ export function pluginSSR( hooks: { 'build:before': () => { let vitePlugin = ssr - ? vitePluginSSR(internals, options.settings.adapter!, options.settings.config) + ? vitePluginSSR(internals, options.settings.adapter!, options) : undefined; return { diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index ef6541b30..54e99e0f4 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -17,7 +17,6 @@ import { isModeServerWithNoAdapter } from '../../core/util.js'; import { runHookBuildSetup } from '../../integrations/index.js'; import { isHybridOutput } from '../../prerender/utils.js'; import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; -import { resolvedPagesVirtualModuleId } from '../app/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; import { info } from '../logger/core.js'; import { getOutDirWithinCwd } from './common.js'; @@ -25,10 +24,14 @@ import { generatePages } from './generate.js'; import { trackPageData } from './internal.js'; import { createPluginContainer, type AstroBuildPluginContainer } from './plugin.js'; import { registerAllPlugins } from './plugins/index.js'; -import { RESOLVED_MIDDLEWARE_MODULE_ID } from './plugins/plugin-middleware.js'; import { RESOLVED_RENDERERS_MODULE_ID } from './plugins/plugin-renderers.js'; import type { PageBuildData, StaticBuildOptions } from './types'; import { getTimeStat } from './util.js'; +import { + ASTRO_PAGE_EXTENSION_POST_PATTERN, + ASTRO_PAGE_RESOLVED_MODULE_ID, +} from './plugins/plugin-pages.js'; +import { SSR_VIRTUAL_MODULE_ID } from './plugins/plugin-ssr.js'; export async function viteBuild(opts: StaticBuildOptions) { const { allPages, settings } = opts; @@ -172,10 +175,17 @@ async function ssrBuild( assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`, ...viteConfig.build?.rollupOptions?.output, entryFileNames(chunkInfo) { - if (chunkInfo.facadeModuleId === resolvedPagesVirtualModuleId) { - return opts.buildConfig.serverEntry; - } else if (chunkInfo.facadeModuleId === RESOLVED_MIDDLEWARE_MODULE_ID) { + if (chunkInfo.facadeModuleId?.startsWith(ASTRO_PAGE_RESOLVED_MODULE_ID)) { + return makeAstroPageEntryPointFileName(chunkInfo.facadeModuleId); + } else if ( + // checks if the path of the module we have middleware, e.g. middleware.js / middleware/index.js + chunkInfo.facadeModuleId?.includes('middleware') && + // checks if the file actually export the `onRequest` function + chunkInfo.exports.includes('onRequest') + ) { return 'middleware.mjs'; + } else if (chunkInfo.facadeModuleId === SSR_VIRTUAL_MODULE_ID) { + return opts.settings.config.build.serverEntry; } else if (chunkInfo.facadeModuleId === RESOLVED_RENDERERS_MODULE_ID) { return 'renderers.mjs'; } else { @@ -408,3 +418,29 @@ async function ssrMoveAssets(opts: StaticBuildOptions) { removeEmptyDirs(serverAssets); } } + +/** + * This function takes as input the virtual module name of an astro page and transform + * to generate an `.mjs` file: + * + * Input: `@astro-page:src/pages/index@_@astro` + * + * Output: `pages/index.astro.mjs` + * + * 1. We remove the module id prefix, `@astro-page:` + * 2. We remove `src/` + * 3. We replace square brackets with underscore, for example `[slug]` + * 4. At last, we replace the extension pattern with a simple dot + * 5. We append the `.mjs` string, so the file will always be a JS file + * + * @param facadeModuleId + */ +function makeAstroPageEntryPointFileName(facadeModuleId: string) { + return `${facadeModuleId + .replace(ASTRO_PAGE_RESOLVED_MODULE_ID, '') + .replace('src/', '') + .replaceAll('[', '_') + .replaceAll(']', '_') + // this must be last + .replace(ASTRO_PAGE_EXTENSION_POST_PATTERN, '.')}.mjs`; +} diff --git a/packages/astro/src/core/build/types.ts b/packages/astro/src/core/build/types.ts index c0f38de45..772235697 100644 --- a/packages/astro/src/core/build/types.ts +++ b/packages/astro/src/core/build/types.ts @@ -49,8 +49,8 @@ export interface StaticBuildOptions { type ImportComponentInstance = () => Promise; -export interface SingleFileBuiltModule { - pageMap: Map; +export interface SinglePageBuiltModule { + page: ImportComponentInstance; middleware: AstroMiddlewareInstance; renderers: SSRLoadedRenderer[]; } From 223e0131fcd3cfc83575ab9860eb2648b7240b35 Mon Sep 17 00:00:00 2001 From: ematipico Date: Thu, 25 May 2023 13:30:42 +0000 Subject: [PATCH 10/26] [ci] format --- packages/astro/src/core/app/types.ts | 1 - packages/astro/src/core/build/generate.ts | 5 ++--- packages/astro/src/core/build/internal.ts | 2 +- .../astro/src/core/build/plugins/plugin-pages.ts | 2 +- packages/astro/src/core/build/plugins/plugin-ssr.ts | 12 ++++++------ packages/astro/src/core/build/static-build.ts | 6 +++--- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 0fa2c034b..0682acb6a 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -1,7 +1,6 @@ import type { MarkdownRenderingOptions } from '@astrojs/markdown-remark'; import type { AstroMiddlewareInstance, - ComponentInstance, RouteData, SerializedRouteData, SSRComponentMetadata, diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 3c24aa4bc..a12313987 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -21,9 +21,9 @@ import { getStaticImageList, } from '../../assets/generate.js'; import { + eachPageDataFromEntryPoint, hasPrerenderedPages, type BuildInternals, - eachPageDataFromEntryPoint, } from '../../core/build/internal.js'; import { prependForwardSlash, @@ -48,7 +48,7 @@ import { createRequest } from '../request.js'; import { matchRoute } from '../routing/match.js'; import { getOutputFilename } from '../util.js'; import { getOutDirWithinCwd, getOutFile, getOutFolder } from './common.js'; -import { cssOrder, eachPageData, getPageDataByComponent, mergeInlineCss } from './internal.js'; +import { cssOrder, getPageDataByComponent, mergeInlineCss } from './internal.js'; import type { PageBuildData, SinglePageBuiltModule, @@ -56,7 +56,6 @@ import type { StylesheetAsset, } from './types'; import { getTimeStat } from './util.js'; -import { ASTRO_PAGE_MODULE_ID } from './plugins/plugin-pages'; function shouldSkipDraft(pageModule: ComponentInstance, settings: AstroSettings): boolean { return ( diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index f6025238a..18d28ef74 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -1,10 +1,10 @@ import type { Rollup } from 'vite'; -import type { PageBuildData, StylesheetAsset, ViteID } from './types'; import type { SSRResult } from '../../@types/astro'; import type { PageOptions } from '../../vite-plugin-astro/types'; import { prependForwardSlash, removeFileExtension } from '../path.js'; import { viteID } from '../util.js'; import { ASTRO_PAGE_EXTENSION_POST_PATTERN, ASTRO_PAGE_MODULE_ID } from './plugins/plugin-pages.js'; +import type { PageBuildData, StylesheetAsset, ViteID } from './types'; export interface BuildInternals { /** diff --git a/packages/astro/src/core/build/plugins/plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts index 3ea0a61c8..4428d8ac6 100644 --- a/packages/astro/src/core/build/plugins/plugin-pages.ts +++ b/packages/astro/src/core/build/plugins/plugin-pages.ts @@ -1,3 +1,4 @@ +import { extname } from 'node:path'; import type { Plugin as VitePlugin } from 'vite'; import { addRollupInput } from '../add-rollup-input.js'; import { type BuildInternals } from '../internal.js'; @@ -5,7 +6,6 @@ import type { AstroBuildPlugin } from '../plugin'; import type { StaticBuildOptions } from '../types'; import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js'; import { RENDERERS_MODULE_ID } from './plugin-renderers.js'; -import { extname } from 'node:path'; export const ASTRO_PAGE_MODULE_ID = '@astro-page:'; export const ASTRO_PAGE_RESOLVED_MODULE_ID = '\0@astro-page:'; diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts index b40faf20d..accdd01ad 100644 --- a/packages/astro/src/core/build/plugins/plugin-ssr.ts +++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts @@ -1,21 +1,21 @@ -import type { Plugin as VitePlugin } from 'vite'; -import type { AstroAdapter } from '../../../@types/astro'; -import type { SerializedRouteInfo, SerializedSSRManifest } from '../../app/types'; -import type { StaticBuildOptions } from '../types'; -import type { AstroBuildPlugin } from '../plugin'; import glob from 'fast-glob'; import { fileURLToPath } from 'url'; +import type { Plugin as VitePlugin } from 'vite'; +import type { AstroAdapter } from '../../../@types/astro'; import { runHookBuildSsr } from '../../../integrations/index.js'; import { isHybridOutput } from '../../../prerender/utils.js'; import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../../vite-plugin-scripts/index.js'; +import type { SerializedRouteInfo, SerializedSSRManifest } from '../../app/types'; import { joinPaths, prependForwardSlash } from '../../path.js'; import { serializeRouteData } from '../../routing/index.js'; import { addRollupInput } from '../add-rollup-input.js'; import { getOutFile, getOutFolder } from '../common.js'; import { cssOrder, mergeInlineCss, type BuildInternals } from '../internal.js'; +import type { AstroBuildPlugin } from '../plugin'; +import type { StaticBuildOptions } from '../types'; import { MIDDLEWARE_MODULE_ID } from './plugin-middleware.js'; -import { RENDERERS_MODULE_ID } from './plugin-renderers.js'; import { getVirtualModulePageNameFromPath } from './plugin-pages.js'; +import { RENDERERS_MODULE_ID } from './plugin-renderers.js'; export const SSR_VIRTUAL_MODULE_ID = '@astrojs-ssr-virtual-entry'; const RESOLVED_SSR_VIRTUAL_MODULE_ID = '\0' + SSR_VIRTUAL_MODULE_ID; diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 54e99e0f4..cceaa0361 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -24,14 +24,14 @@ import { generatePages } from './generate.js'; import { trackPageData } from './internal.js'; import { createPluginContainer, type AstroBuildPluginContainer } from './plugin.js'; import { registerAllPlugins } from './plugins/index.js'; -import { RESOLVED_RENDERERS_MODULE_ID } from './plugins/plugin-renderers.js'; -import type { PageBuildData, StaticBuildOptions } from './types'; -import { getTimeStat } from './util.js'; import { ASTRO_PAGE_EXTENSION_POST_PATTERN, ASTRO_PAGE_RESOLVED_MODULE_ID, } from './plugins/plugin-pages.js'; +import { RESOLVED_RENDERERS_MODULE_ID } from './plugins/plugin-renderers.js'; import { SSR_VIRTUAL_MODULE_ID } from './plugins/plugin-ssr.js'; +import type { PageBuildData, StaticBuildOptions } from './types'; +import { getTimeStat } from './util.js'; export async function viteBuild(opts: StaticBuildOptions) { const { allPages, settings } = opts; From 16b836411980f18c58ca15712d92cec1b3c95670 Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Thu, 25 May 2023 11:35:07 -0400 Subject: [PATCH 11/26] Markdoc - improve syntax highlighting support (#7209) * feat: prism and shiki support, with better exports! * chore: update tests * chore: fix lock * chore: add prism test * chore: remove `async` from prism * docs: update syntax highlight readme * chore: changeset * edit: remove `await` from prism docs * chore: update old changest with new shiki instructions * fix: add trailing newline on ts-expect-error * refactor: resolve promises internally * docs: remove `await` from shiki examples --- .changeset/eleven-tables-speak.md | 8 +- .changeset/popular-berries-travel.md | 17 +++ packages/integrations/markdoc/README.md | 31 +++-- packages/integrations/markdoc/package.json | 15 +-- packages/integrations/markdoc/src/config.ts | 1 - .../markdoc/src/extensions/prism.ts | 24 ++++ .../markdoc/src/extensions/shiki.ts | 14 +-- packages/integrations/markdoc/src/index.ts | 26 ++-- packages/integrations/markdoc/src/runtime.ts | 30 +++-- .../markdoc/test/syntax-highlighting.test.js | 113 +++++++++++------- pnpm-lock.yaml | 35 +++--- 11 files changed, 200 insertions(+), 114 deletions(-) create mode 100644 .changeset/popular-berries-travel.md create mode 100644 packages/integrations/markdoc/src/extensions/prism.ts diff --git a/.changeset/eleven-tables-speak.md b/.changeset/eleven-tables-speak.md index 6ff1474c7..3eac90a36 100644 --- a/.changeset/eleven-tables-speak.md +++ b/.changeset/eleven-tables-speak.md @@ -2,14 +2,16 @@ '@astrojs/markdoc': patch --- -Add support for syntax highlighting with Shiki. Install `shiki` in your project with `npm i shiki`, and apply to your Markdoc config using the `extends` option: +Add support for syntax highlighting with Shiki. Apply to your Markdoc config using the `extends` property: ```js // markdoc.config.mjs -import { defineMarkdocConfig, shiki } from '@astrojs/markdoc/config'; +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import shiki from '@astrojs/markdoc/shiki'; + export default defineMarkdocConfig({ extends: [ - await shiki({ /** Shiki config options */ }), + shiki({ /** Shiki config options */ }), ], }) ``` diff --git a/.changeset/popular-berries-travel.md b/.changeset/popular-berries-travel.md new file mode 100644 index 000000000..a3755d267 --- /dev/null +++ b/.changeset/popular-berries-travel.md @@ -0,0 +1,17 @@ +--- +'@astrojs/markdoc': patch +--- + +Add a built-in extension for syntax highlighting with Prism. Apply to your Markdoc config using the `extends` property: + +```js +// markdoc.config.mjs +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import prism from '@astrojs/markdoc/prism'; + +export default defineMarkdocConfig({ + extends: [prism()], +}) +``` + +Learn more in the [`@astrojs/markdoc` README.](https://docs.astro.build/en/guides/integrations-guide/markdoc/#syntax-highlighting) diff --git a/packages/integrations/markdoc/README.md b/packages/integrations/markdoc/README.md index 815f0420b..dd2f2d4de 100644 --- a/packages/integrations/markdoc/README.md +++ b/packages/integrations/markdoc/README.md @@ -205,23 +205,20 @@ export default defineMarkdocConfig({ ### Syntax highlighting -`@astrojs/markdoc` provides a [Shiki](https://github.com/shikijs/shiki) extension to highlight your code blocks. +`@astrojs/markdoc` provides [Shiki](https://github.com/shikijs/shiki) and [Prism](https://github.com/PrismJS) extensions to highlight your code blocks. -To use this extension, you must separately install `shiki` as a dependency: +#### Shiki -```bash -npm i shiki -``` - -Then, apply the `shiki()` extension to your Markdoc config using the `extends` property. You can optionally pass a shiki configuration object: +Apply the `shiki()` extension to your Markdoc config using the `extends` property. You can optionally pass a shiki configuration object: ```js // markdoc.config.mjs -import { defineMarkdocConfig, shiki } from '@astrojs/markdoc/config'; +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import shiki from '@astrojs/markdoc/shiki'; export default defineMarkdocConfig({ extends: [ - await shiki({ + shiki({ // Choose from Shiki's built-in themes (or add your own) // Default: 'github-dark' // https://github.com/shikijs/shiki/blob/main/docs/themes.md @@ -238,6 +235,22 @@ export default defineMarkdocConfig({ }) ``` +#### Prism + +Apply the `prism()` extension to your Markdoc config using the `extends` property. + +```js +// markdoc.config.mjs +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; +import prism from '@astrojs/markdoc/prism'; + +export default defineMarkdocConfig({ + extends: [prism()], +}) +``` + +📚 To learn about configuring Prism stylesheets, [see our syntax highlighting guide.](https://docs.astro.build/en/guides/markdown-content/#prism-configuration) + ### Access frontmatter and content collection information from your templates You can access content collection information from your Markdoc templates using the `$entry` variable. This includes the entry `slug`, `collection` name, and frontmatter `data` parsed by your content collection schema (if any). This example renders the `title` frontmatter property as a heading: diff --git a/packages/integrations/markdoc/package.json b/packages/integrations/markdoc/package.json index 2086073ad..62ed853d7 100644 --- a/packages/integrations/markdoc/package.json +++ b/packages/integrations/markdoc/package.json @@ -19,6 +19,8 @@ "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://docs.astro.build/en/guides/integrations-guide/markdoc/", "exports": { + "./prism": "./dist/extensions/prism.js", + "./shiki": "./dist/extensions/shiki.js", ".": "./dist/index.js", "./components": "./components/index.ts", "./runtime": "./dist/runtime.js", @@ -39,7 +41,9 @@ "test:match": "mocha --timeout 20000 -g" }, "dependencies": { - "@markdoc/markdoc": "^0.2.2", + "shiki": "^0.14.1", + "@astrojs/prism": "^2.1.2", + "@markdoc/markdoc": "^0.3.0", "esbuild": "^0.17.12", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", @@ -47,13 +51,7 @@ "zod": "^3.17.3" }, "peerDependencies": { - "astro": "workspace:^2.5.5", - "shiki": "^0.14.1" - }, - "peerDependenciesMeta": { - "shiki": { - "optional": true - } + "astro": "workspace:^2.5.5" }, "devDependencies": { "@astrojs/markdown-remark": "^2.2.1", @@ -67,7 +65,6 @@ "linkedom": "^0.14.12", "mocha": "^9.2.2", "rollup": "^3.20.1", - "shiki": "^0.14.1", "vite": "^4.3.1" }, "engines": { diff --git a/packages/integrations/markdoc/src/config.ts b/packages/integrations/markdoc/src/config.ts index a8f202424..23ff744f7 100644 --- a/packages/integrations/markdoc/src/config.ts +++ b/packages/integrations/markdoc/src/config.ts @@ -12,7 +12,6 @@ export type ResolvedAstroMarkdocConfig = Omit; export const Markdoc = _Markdoc; export const nodes = { ...Markdoc.nodes, heading }; -export { shiki } from './extensions/shiki.js'; export function defineMarkdocConfig(config: AstroMarkdocConfig): AstroMarkdocConfig { return config; diff --git a/packages/integrations/markdoc/src/extensions/prism.ts b/packages/integrations/markdoc/src/extensions/prism.ts new file mode 100644 index 000000000..e28112c9a --- /dev/null +++ b/packages/integrations/markdoc/src/extensions/prism.ts @@ -0,0 +1,24 @@ +// leave space, so organize imports doesn't mess up comments +// @ts-expect-error Cannot find module 'astro/runtime/server/index.js' or its corresponding type declarations. +import { unescapeHTML } from 'astro/runtime/server/index.js'; + +import { runHighlighterWithAstro } from '@astrojs/prism/dist/highlighter'; +import { Markdoc, type AstroMarkdocConfig } from '../config.js'; + +export default function prism(): AstroMarkdocConfig { + return { + nodes: { + fence: { + attributes: Markdoc.nodes.fence.attributes!, + transform({ attributes: { language, content } }) { + const { html, classLanguage } = runHighlighterWithAstro(language, content); + + // Use `unescapeHTML` to return `HTMLString` for Astro renderer to inline as HTML + return unescapeHTML( + `
${html}
` + ); + }, + }, + }, + }; +} diff --git a/packages/integrations/markdoc/src/extensions/shiki.ts b/packages/integrations/markdoc/src/extensions/shiki.ts index d03a60139..491deed56 100644 --- a/packages/integrations/markdoc/src/extensions/shiki.ts +++ b/packages/integrations/markdoc/src/extensions/shiki.ts @@ -2,11 +2,11 @@ // @ts-expect-error Cannot find module 'astro/runtime/server/index.js' or its corresponding type declarations. import { unescapeHTML } from 'astro/runtime/server/index.js'; -import Markdoc from '@markdoc/markdoc'; import type { ShikiConfig } from 'astro'; import type * as shikiTypes from 'shiki'; import type { AstroMarkdocConfig } from '../config.js'; -import { MarkdocError } from '../utils.js'; +import Markdoc from '@markdoc/markdoc'; +import { getHighlighter } from 'shiki'; // Map of old theme names to new names to preserve compatibility when we upgrade shiki const compatThemes: Record = { @@ -51,19 +51,11 @@ const INLINE_STYLE_SELECTOR = /style="(.*?)"/; */ const highlighterCache = new Map(); -export async function shiki({ +export default async function shiki({ langs = [], theme = 'github-dark', wrap = false, }: ShikiConfig = {}): Promise { - let getHighlighter: (options: shikiTypes.HighlighterOptions) => Promise; - try { - getHighlighter = (await import('shiki')).getHighlighter; - } catch { - throw new MarkdocError({ - message: 'Shiki is not installed. Run `npm install shiki` to use the `shiki` extension.', - }); - } theme = normalizeTheme(theme); const cacheID: string = typeof theme === 'string' ? theme : theme.name; diff --git a/packages/integrations/markdoc/src/index.ts b/packages/integrations/markdoc/src/index.ts index 64ae4cbc0..0486a44b5 100644 --- a/packages/integrations/markdoc/src/index.ts +++ b/packages/integrations/markdoc/src/index.ts @@ -32,7 +32,19 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration name: '@astrojs/markdoc', hooks: { 'astro:config:setup': async (params) => { - const { config: astroConfig, addContentEntryType } = params as SetupHookParams; + const { + config: astroConfig, + updateConfig, + addContentEntryType, + } = params as SetupHookParams; + + updateConfig({ + vite: { + ssr: { + external: ['@astrojs/markdoc/prism', '@astrojs/markdoc/shiki'], + }, + }, + }); markdocConfigResult = await loadMarkdocConfig(astroConfig); const userMarkdocConfig = markdocConfigResult?.config ?? {}; @@ -52,11 +64,7 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration async getRenderModule({ entry, viteId }) { const ast = Markdoc.parse(entry.body); const pluginContext = this; - const markdocConfig = setupConfig( - userMarkdocConfig, - entry, - markdocConfigResult?.fileUrl.pathname - ); + const markdocConfig = await setupConfig(userMarkdocConfig, entry); const validationErrors = Markdoc.validate(ast, markdocConfig).filter((e) => { return ( @@ -94,7 +102,7 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration const res = `import { jsx as h } from 'astro/jsx-runtime'; import { Renderer } from '@astrojs/markdoc/components'; - import { collectHeadings, setupConfig, Markdoc } from '@astrojs/markdoc/runtime'; + import { collectHeadings, setupConfig, setupConfigSync, Markdoc } from '@astrojs/markdoc/runtime'; import * as entry from ${JSON.stringify(viteId + '?astroContentCollectionEntry')}; ${ markdocConfigResult @@ -118,13 +126,13 @@ export function getHeadings() { '' } const headingConfig = userConfig.nodes?.heading; - const config = setupConfig(headingConfig ? { nodes: { heading: headingConfig } } : {}, entry); + const config = setupConfigSync(headingConfig ? { nodes: { heading: headingConfig } } : {}, entry); const ast = Markdoc.Ast.fromJSON(stringifiedAst); const content = Markdoc.transform(ast, config); return collectHeadings(Array.isArray(content) ? content : content.children); } export async function Content (props) { - const config = setupConfig({ + const config = await setupConfig({ ...userConfig, variables: { ...userConfig.variables, ...props }, }, entry); diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts index 5a59d5fef..939e6d602 100644 --- a/packages/integrations/markdoc/src/runtime.ts +++ b/packages/integrations/markdoc/src/runtime.ts @@ -13,26 +13,19 @@ export { default as Markdoc } from '@markdoc/markdoc'; * Called on each file's individual transform. * TODO: virtual module to merge configs per-build instead of per-file? */ -export function setupConfig( +export async function setupConfig( userConfig: AstroMarkdocConfig, - entry: ContentEntryModule, - markdocConfigPath?: string -): Omit { + entry: ContentEntryModule +): Promise> { let defaultConfig: AstroMarkdocConfig = { ...setupHeadingConfig(), variables: { entry }, }; if (userConfig.extends) { - for (const extension of userConfig.extends) { + for (let extension of userConfig.extends) { if (extension instanceof Promise) { - throw new MarkdocError({ - message: 'An extension passed to `extends` in your markdoc config returns a Promise.', - hint: 'Call `await` for async extensions. Example: `extends: [await myExtension()]`', - location: { - file: markdocConfigPath, - }, - }); + extension = await extension; } defaultConfig = mergeConfig(defaultConfig, extension); @@ -42,6 +35,19 @@ export function setupConfig( return mergeConfig(defaultConfig, userConfig); } +/** Used for synchronous `getHeadings()` function */ +export function setupConfigSync( + userConfig: AstroMarkdocConfig, + entry: ContentEntryModule +): Omit { + let defaultConfig: AstroMarkdocConfig = { + ...setupHeadingConfig(), + variables: { entry }, + }; + + return mergeConfig(defaultConfig, userConfig); +} + /** Merge function from `@markdoc/markdoc` internals */ function mergeConfig(configA: AstroMarkdocConfig, configB: AstroMarkdocConfig): AstroMarkdocConfig { return { diff --git a/packages/integrations/markdoc/test/syntax-highlighting.test.js b/packages/integrations/markdoc/test/syntax-highlighting.test.js index ef1845eb9..1530e0c82 100644 --- a/packages/integrations/markdoc/test/syntax-highlighting.test.js +++ b/packages/integrations/markdoc/test/syntax-highlighting.test.js @@ -1,7 +1,8 @@ import { parseHTML } from 'linkedom'; import { expect } from 'chai'; import Markdoc from '@markdoc/markdoc'; -import { shiki } from '../dist/config.js'; +import shiki from '../dist/extensions/shiki.js'; +import prism from '../dist/extensions/prism.js'; import { setupConfig } from '../dist/runtime.js'; import { isHTMLString } from 'astro/runtime/server/index.js'; @@ -18,52 +19,76 @@ const highlighting = true; `; describe('Markdoc - syntax highlighting', () => { - it('transforms with defaults', async () => { - const ast = Markdoc.parse(entry); - const content = Markdoc.transform(ast, await getConfigExtendingShiki()); + describe('shiki', () => { + it('transforms with defaults', async () => { + const ast = Markdoc.parse(entry); + const content = Markdoc.transform(ast, await getConfigExtendingShiki()); - expect(content.children).to.have.lengthOf(2); - for (const codeBlock of content.children) { - expect(isHTMLString(codeBlock)).to.be.true; + expect(content.children).to.have.lengthOf(2); + for (const codeBlock of content.children) { + expect(isHTMLString(codeBlock)).to.be.true; - const pre = parsePreTag(codeBlock); - expect(pre.classList).to.include('astro-code'); - expect(pre.classList).to.include('github-dark'); - } + const pre = parsePreTag(codeBlock); + expect(pre.classList).to.include('astro-code'); + expect(pre.classList).to.include('github-dark'); + } + }); + it('transforms with `theme` property', async () => { + const ast = Markdoc.parse(entry); + const content = Markdoc.transform( + ast, + await getConfigExtendingShiki({ + theme: 'dracula', + }) + ); + expect(content.children).to.have.lengthOf(2); + for (const codeBlock of content.children) { + expect(isHTMLString(codeBlock)).to.be.true; + + const pre = parsePreTag(codeBlock); + expect(pre.classList).to.include('astro-code'); + expect(pre.classList).to.include('dracula'); + } + }); + it('transforms with `wrap` property', async () => { + const ast = Markdoc.parse(entry); + const content = Markdoc.transform( + ast, + await getConfigExtendingShiki({ + wrap: true, + }) + ); + expect(content.children).to.have.lengthOf(2); + for (const codeBlock of content.children) { + expect(isHTMLString(codeBlock)).to.be.true; + + const pre = parsePreTag(codeBlock); + expect(pre.getAttribute('style')).to.include('white-space: pre-wrap'); + expect(pre.getAttribute('style')).to.include('word-wrap: break-word'); + } + }); }); - it('transforms with `theme` property', async () => { - const ast = Markdoc.parse(entry); - const content = Markdoc.transform( - ast, - await getConfigExtendingShiki({ - theme: 'dracula', - }) - ); - expect(content.children).to.have.lengthOf(2); - for (const codeBlock of content.children) { - expect(isHTMLString(codeBlock)).to.be.true; - const pre = parsePreTag(codeBlock); - expect(pre.classList).to.include('astro-code'); - expect(pre.classList).to.include('dracula'); - } - }); - it('transforms with `wrap` property', async () => { - const ast = Markdoc.parse(entry); - const content = Markdoc.transform( - ast, - await getConfigExtendingShiki({ - wrap: true, - }) - ); - expect(content.children).to.have.lengthOf(2); - for (const codeBlock of content.children) { - expect(isHTMLString(codeBlock)).to.be.true; + describe('prism', () => { + it('transforms', async () => { + const ast = Markdoc.parse(entry); + const config = await setupConfig({ + extends: [prism()], + }); + const content = Markdoc.transform(ast, config); - const pre = parsePreTag(codeBlock); - expect(pre.getAttribute('style')).to.include('white-space: pre-wrap'); - expect(pre.getAttribute('style')).to.include('word-wrap: break-word'); - } + expect(content.children).to.have.lengthOf(2); + const [tsBlock, cssBlock] = content.children; + + expect(isHTMLString(tsBlock)).to.be.true; + expect(isHTMLString(cssBlock)).to.be.true; + + const preTs = parsePreTag(tsBlock); + expect(preTs.classList).to.include('language-ts'); + + const preCss = parsePreTag(cssBlock); + expect(preCss.classList).to.include('language-css'); + }); }); }); @@ -72,8 +97,8 @@ describe('Markdoc - syntax highlighting', () => { * @returns {import('../src/config.js').AstroMarkdocConfig} */ async function getConfigExtendingShiki(config) { - return setupConfig({ - extends: [await shiki(config)], + return await setupConfig({ + extends: [shiki(config)], }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d6401b12..c89ef58c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3973,9 +3973,12 @@ importers: packages/integrations/markdoc: dependencies: + '@astrojs/prism': + specifier: ^2.1.2 + version: link:../../astro-prism '@markdoc/markdoc': - specifier: ^0.2.2 - version: 0.2.2 + specifier: ^0.3.0 + version: 0.3.0 esbuild: specifier: ^0.17.12 version: 0.17.12 @@ -3988,6 +3991,9 @@ importers: kleur: specifier: ^4.1.5 version: 4.1.5 + shiki: + specifier: ^0.14.1 + version: 0.14.1 zod: specifier: ^3.17.3 version: 3.20.6 @@ -4025,9 +4031,6 @@ importers: rollup: specifier: ^3.20.1 version: 3.20.1 - shiki: - specifier: ^0.14.1 - version: 0.14.1 vite: specifier: ^4.3.1 version: 4.3.1(@types/node@18.16.3)(sass@1.52.2) @@ -8055,15 +8058,15 @@ packages: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.3.8 - tar: 6.1.11 + semver: 7.5.1 + tar: 6.1.14 transitivePeerDependencies: - encoding - supports-color dev: false - /@markdoc/markdoc@0.2.2: - resolution: {integrity: sha512-0TiD9jmA5h5znN4lxo7HECAu3WieU5g5vUsfByeucrdR/x88hEilpt16EydFyJwJddQ/3w5HQgW7Ovy62r4cyw==} + /@markdoc/markdoc@0.3.0: + resolution: {integrity: sha512-QWCF8krIIw52ulflfnoff0yG1eKl9CCGA3KAiOjHyYtHNzSEouFh8lO52nAaO3qV2Ctj1GTB8TTb2rTfvISQfA==} engines: {node: '>=14.7.0'} peerDependencies: '@types/react': '*' @@ -8734,8 +8737,8 @@ packages: /@types/babel__core@7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: - '@babel/parser': 7.18.4 - '@babel/types': 7.18.4 + '@babel/parser': 7.21.8 + '@babel/types': 7.21.5 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.17.1 @@ -8744,19 +8747,19 @@ packages: /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.18.4 - '@babel/types': 7.18.4 + '@babel/parser': 7.21.8 + '@babel/types': 7.21.5 dev: false /@types/babel__traverse@7.17.1: resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 /@types/better-sqlite3@7.6.4: resolution: {integrity: sha512-dzrRZCYPXIXfSR1/surNbJ/grU3scTaygS0OMzjlGf71i9sc2fGyHPXXiXmEvNIoE0cGwsanEFMVJxPXmco9Eg==} @@ -10496,7 +10499,7 @@ packages: dev: false /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} /concordance@5.0.4: resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} From 594a3aaf51c7c91fc34baa5026c7a2961ca3b88a Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 25 May 2023 15:37:55 +0000 Subject: [PATCH 12/26] [ci] format --- packages/integrations/markdoc/src/extensions/shiki.ts | 4 ++-- packages/integrations/markdoc/src/runtime.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/integrations/markdoc/src/extensions/shiki.ts b/packages/integrations/markdoc/src/extensions/shiki.ts index 491deed56..34f79779f 100644 --- a/packages/integrations/markdoc/src/extensions/shiki.ts +++ b/packages/integrations/markdoc/src/extensions/shiki.ts @@ -2,11 +2,11 @@ // @ts-expect-error Cannot find module 'astro/runtime/server/index.js' or its corresponding type declarations. import { unescapeHTML } from 'astro/runtime/server/index.js'; +import Markdoc from '@markdoc/markdoc'; import type { ShikiConfig } from 'astro'; import type * as shikiTypes from 'shiki'; -import type { AstroMarkdocConfig } from '../config.js'; -import Markdoc from '@markdoc/markdoc'; import { getHighlighter } from 'shiki'; +import type { AstroMarkdocConfig } from '../config.js'; // Map of old theme names to new names to preserve compatibility when we upgrade shiki const compatThemes: Record = { diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts index 939e6d602..5bf7e4412 100644 --- a/packages/integrations/markdoc/src/runtime.ts +++ b/packages/integrations/markdoc/src/runtime.ts @@ -3,7 +3,6 @@ import Markdoc, { type RenderableTreeNode } from '@markdoc/markdoc'; import type { ContentEntryModule } from 'astro'; import type { AstroMarkdocConfig } from './config.js'; import { setupHeadingConfig } from './heading-ids.js'; -import { MarkdocError } from './utils.js'; /** Used to call `Markdoc.transform()` and `Markdoc.Ast` in runtime modules */ export { default as Markdoc } from '@markdoc/markdoc'; From 184870e2e25607518a412f29be6b21ac8995e689 Mon Sep 17 00:00:00 2001 From: Ben Holmes Date: Thu, 25 May 2023 13:30:03 -0400 Subject: [PATCH 13/26] deps: bump @babel/core and @babel/types (#7214) --- packages/astro/package.json | 4 +- pnpm-lock.yaml | 612 ++++++++++++++++++------------------ 2 files changed, 306 insertions(+), 310 deletions(-) diff --git a/packages/astro/package.json b/packages/astro/package.json index 40020f303..714febe01 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -117,12 +117,12 @@ "@astrojs/markdown-remark": "^2.2.1", "@astrojs/telemetry": "^2.1.1", "@astrojs/webapi": "^2.1.1", - "@babel/core": "^7.18.2", + "@babel/core": "^7.21.8", "@babel/generator": "^7.18.2", "@babel/parser": "^7.18.4", "@babel/plugin-transform-react-jsx": "^7.17.12", "@babel/traverse": "^7.18.2", - "@babel/types": "^7.18.4", + "@babel/types": "^7.21.5", "@types/babel__core": "^7.1.19", "@types/yargs-parser": "^21.0.0", "acorn": "^8.8.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c89ef58c8..82cad787a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -547,8 +547,8 @@ importers: specifier: ^2.1.1 version: link:../webapi '@babel/core': - specifier: ^7.18.2 - version: 7.18.2 + specifier: ^7.21.8 + version: 7.21.8 '@babel/generator': specifier: ^7.18.2 version: 7.18.2 @@ -557,13 +557,13 @@ importers: version: 7.18.4 '@babel/plugin-transform-react-jsx': specifier: ^7.17.12 - version: 7.17.12(@babel/core@7.18.2) + version: 7.17.12(@babel/core@7.21.8) '@babel/traverse': specifier: ^7.18.2 version: 7.18.2 '@babel/types': - specifier: ^7.18.4 - version: 7.18.4 + specifier: ^7.21.5 + version: 7.21.5 '@types/babel__core': specifier: ^7.1.19 version: 7.1.19 @@ -5563,7 +5563,7 @@ packages: '@babel/parser': 7.18.4 '@babel/template': 7.20.7 '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -5579,11 +5579,11 @@ packages: dependencies: '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.5 + '@babel/generator': 7.21.9 '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8) '@babel/helper-module-transforms': 7.21.5 '@babel/helpers': 7.21.5 - '@babel/parser': 7.21.8 + '@babel/parser': 7.21.9 '@babel/template': 7.20.7 '@babel/traverse': 7.21.5 '@babel/types': 7.21.5 @@ -5600,13 +5600,13 @@ packages: resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 '@jridgewell/gen-mapping': 0.3.3 jsesc: 2.5.2 dev: false - /@babel/generator@7.21.5: - resolution: {integrity: sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==} + /@babel/generator@7.21.9: + resolution: {integrity: sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg==} engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.21.5 @@ -5663,29 +5663,6 @@ packages: semver: 6.3.0 dev: false - /@babel/helper-create-class-features-plugin@7.21.8(@babel/core@7.18.2): - resolution: {integrity: sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - peerDependenciesMeta: - '@babel/core': - optional: true - dependencies: - '@babel/core': 7.18.2 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.21.5 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-member-expression-to-functions': 7.21.5 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-replace-supers': 7.21.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/helper-split-export-declaration': 7.18.6 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: false - /@babel/helper-create-class-features-plugin@7.21.8(@babel/core@7.21.8): resolution: {integrity: sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==} engines: {node: '>=6.9.0'} @@ -5709,7 +5686,7 @@ packages: - supports-color dev: false - /@babel/helper-create-regexp-features-plugin@7.21.8(@babel/core@7.18.2): + /@babel/helper-create-regexp-features-plugin@7.21.8(@babel/core@7.21.8): resolution: {integrity: sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g==} engines: {node: '>=6.9.0'} peerDependencies: @@ -5718,13 +5695,13 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-annotate-as-pure': 7.18.6 regexpu-core: 5.3.2 semver: 6.3.0 dev: false - /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.18.2): + /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.21.8): resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} peerDependencies: '@babel/core': ^7.4.0-0 @@ -5732,8 +5709,8 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 debug: 4.3.4 lodash.debounce: 4.0.8 @@ -5812,7 +5789,7 @@ packages: engines: {node: '>=6.9.0'} dev: false - /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.18.2): + /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.21.8): resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -5821,7 +5798,7 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.21.5 '@babel/helper-wrap-function': 7.20.5 @@ -5914,7 +5891,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 + dev: false /@babel/parser@7.21.8: resolution: {integrity: sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==} @@ -5922,8 +5900,16 @@ packages: hasBin: true dependencies: '@babel/types': 7.21.5 + dev: false - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.18.2): + /@babel/parser@7.21.9: + resolution: {integrity: sha512-q5PNg/Bi1OpGgx5jYlvWZwAorZepEudDMCLtj967aeS7WMont7dUZI46M2XwcIQqvUlMxWfdLFu4S/qSxeUu5g==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.21.5 + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -5932,11 +5918,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.18.2): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.21.8): resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -5945,13 +5931,13 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.18.2) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.18.2): + /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.8): resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -5960,16 +5946,16 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-environment-visitor': 7.21.5 '@babel/helper-plugin-utils': 7.21.5 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.18.2) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.18.2) + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.8) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.8) transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -5978,14 +5964,14 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.18.2): + /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.21.8): resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -5994,15 +5980,15 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.18.2) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.8) transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6011,12 +5997,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.18.2) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.18.2): + /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.8): resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6025,12 +6011,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.18.2) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6039,12 +6025,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.18.2) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.18.2): + /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.8): resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6053,12 +6039,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.18.2) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6067,12 +6053,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.18.2) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6081,12 +6067,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.18.2) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.18.2): + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.8): resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6096,14 +6082,14 @@ packages: optional: true dependencies: '@babel/compat-data': 7.21.7 - '@babel/core': 7.18.2 - '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.18.2) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6112,12 +6098,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.18.2) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.18.2): + /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.21.8): resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6126,13 +6112,13 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.18.2) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.8) dev: false - /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6141,14 +6127,14 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.18.2): + /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.21.8): resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6157,16 +6143,16 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.18.2) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.8) transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.18.2): + /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} engines: {node: '>=4'} peerDependencies: @@ -6175,12 +6161,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.18.2): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.8): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6188,11 +6174,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.18.2): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.8): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6200,11 +6186,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.18.2): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.21.8): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6213,11 +6199,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.18.2): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.8): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6225,11 +6211,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.18.2): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.21.8): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6237,11 +6223,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.18.2): + /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.21.8): resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6250,11 +6236,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.18.2): + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.8): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6262,11 +6248,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.18.2): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.8): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6274,7 +6260,7 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false @@ -6304,7 +6290,7 @@ packages: '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.18.2): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.8): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6312,11 +6298,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.18.2): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.8): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6324,11 +6310,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.18.2): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.8): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6336,11 +6322,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.18.2): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.8): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6348,11 +6334,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.18.2): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.8): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6360,11 +6346,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.18.2): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.8): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6372,11 +6358,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.18.2): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.21.8): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6385,11 +6371,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.18.2): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.8): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6398,7 +6384,7 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false @@ -6415,7 +6401,7 @@ packages: '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-arrow-functions@7.21.5(@babel/core@7.18.2): + /@babel/plugin-transform-arrow-functions@7.21.5(@babel/core@7.21.8): resolution: {integrity: sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6424,11 +6410,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.18.2): + /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.21.8): resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6437,15 +6423,15 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-module-imports': 7.21.4 '@babel/helper-plugin-utils': 7.21.5 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.18.2) + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.8) transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6454,11 +6440,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.18.2): + /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.21.8): resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6467,11 +6453,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-classes@7.21.0(@babel/core@7.18.2): + /@babel/plugin-transform-classes@7.21.0(@babel/core@7.21.8): resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6480,9 +6466,9 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.18.2) + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8) '@babel/helper-environment-visitor': 7.21.5 '@babel/helper-function-name': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 @@ -6494,7 +6480,7 @@ packages: - supports-color dev: false - /@babel/plugin-transform-computed-properties@7.21.5(@babel/core@7.18.2): + /@babel/plugin-transform-computed-properties@7.21.5(@babel/core@7.21.8): resolution: {integrity: sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6503,12 +6489,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 '@babel/template': 7.20.7 dev: false - /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.18.2): + /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.21.8): resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6517,11 +6503,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6530,12 +6516,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.18.2): + /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.21.8): resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6544,11 +6530,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6557,12 +6543,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-builder-binary-assignment-operator-visitor': 7.21.5 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-for-of@7.21.5(@babel/core@7.18.2): + /@babel/plugin-transform-for-of@7.21.5(@babel/core@7.21.8): resolution: {integrity: sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6571,11 +6557,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.18.2): + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.21.8): resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6584,13 +6570,13 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8) '@babel/helper-function-name': 7.21.0 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-literals@7.18.9(@babel/core@7.18.2): + /@babel/plugin-transform-literals@7.18.9(@babel/core@7.21.8): resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6599,11 +6585,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6612,11 +6598,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.18.2): + /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.21.8): resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6625,14 +6611,14 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-module-transforms': 7.21.5 '@babel/helper-plugin-utils': 7.21.5 transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-transform-modules-commonjs@7.21.5(@babel/core@7.18.2): + /@babel/plugin-transform-modules-commonjs@7.21.5(@babel/core@7.21.8): resolution: {integrity: sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6641,7 +6627,7 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-module-transforms': 7.21.5 '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-simple-access': 7.21.5 @@ -6649,7 +6635,7 @@ packages: - supports-color dev: false - /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.18.2): + /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.21.8): resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6658,7 +6644,7 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.21.5 '@babel/helper-plugin-utils': 7.21.5 @@ -6667,7 +6653,7 @@ packages: - supports-color dev: false - /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6676,14 +6662,14 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-module-transforms': 7.21.5 '@babel/helper-plugin-utils': 7.21.5 transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.18.2): + /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.21.8): resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6692,12 +6678,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6706,11 +6692,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6719,14 +6705,14 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-replace-supers': 7.21.5 transitivePeerDependencies: - supports-color dev: false - /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.18.2): + /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.21.8): resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6735,11 +6721,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6748,7 +6734,7 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false @@ -6766,10 +6752,27 @@ packages: '@babel/helper-module-imports': 7.21.4 '@babel/helper-plugin-utils': 7.21.5 '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.18.2) - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 dev: false - /@babel/plugin-transform-regenerator@7.21.5(@babel/core@7.18.2): + /@babel/plugin-transform-react-jsx@7.17.12(@babel/core@7.21.8): + resolution: {integrity: sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + dependencies: + '@babel/core': 7.21.8 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-module-imports': 7.21.4 + '@babel/helper-plugin-utils': 7.21.5 + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8) + '@babel/types': 7.21.5 + dev: false + + /@babel/plugin-transform-regenerator@7.21.5(@babel/core@7.21.8): resolution: {integrity: sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6778,12 +6781,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 regenerator-transform: 0.15.1 dev: false - /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6792,11 +6795,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6805,11 +6808,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-spread@7.20.7(@babel/core@7.18.2): + /@babel/plugin-transform-spread@7.20.7(@babel/core@7.21.8): resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6818,12 +6821,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 dev: false - /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6832,11 +6835,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.18.2): + /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.21.8): resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6845,11 +6848,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.18.2): + /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.21.8): resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6858,7 +6861,7 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false @@ -6880,7 +6883,7 @@ packages: - supports-color dev: false - /@babel/plugin-transform-unicode-escapes@7.21.5(@babel/core@7.18.2): + /@babel/plugin-transform-unicode-escapes@7.21.5(@babel/core@7.21.8): resolution: {integrity: sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6889,11 +6892,11 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.18.2): + /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.21.8): resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6902,12 +6905,12 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-create-regexp-features-plugin': 7.21.8(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 dev: false - /@babel/preset-env@7.21.5(@babel/core@7.18.2): + /@babel/preset-env@7.21.5(@babel/core@7.21.8): resolution: {integrity: sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==} engines: {node: '>=6.9.0'} peerDependencies: @@ -6917,87 +6920,87 @@ packages: optional: true dependencies: '@babel/compat-data': 7.21.7 - '@babel/core': 7.18.2 - '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8) '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.18.2) - '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.18.2) - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.18.2) - '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.18.2) - '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.18.2) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.18.2) - '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.18.2) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.18.2) - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.18.2) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.18.2) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.18.2) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.18.2) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.18.2) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.18.2) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.18.2) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.18.2) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.18.2) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.18.2) - '@babel/plugin-transform-arrow-functions': 7.21.5(@babel/core@7.18.2) - '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.18.2) - '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.18.2) - '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.18.2) - '@babel/plugin-transform-computed-properties': 7.21.5(@babel/core@7.18.2) - '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.18.2) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.18.2) - '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-for-of': 7.21.5(@babel/core@7.18.2) - '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.18.2) - '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.18.2) - '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.18.2) - '@babel/plugin-transform-modules-commonjs': 7.21.5(@babel/core@7.18.2) - '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.18.2) - '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.18.2) - '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.18.2) - '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-regenerator': 7.21.5(@babel/core@7.18.2) - '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.18.2) - '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.18.2) - '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.18.2) - '@babel/plugin-transform-unicode-escapes': 7.21.5(@babel/core@7.18.2) - '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.18.2) - '@babel/preset-modules': 0.1.5(@babel/core@7.18.2) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.8) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.8) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.8) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.8) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.8) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.8) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.8) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.8) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.8) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.8) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.8) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.8) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.8) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.8) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.8) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.8) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.8) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.8) + '@babel/plugin-transform-arrow-functions': 7.21.5(@babel/core@7.21.8) + '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.8) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.8) + '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.8) + '@babel/plugin-transform-computed-properties': 7.21.5(@babel/core@7.21.8) + '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.8) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.8) + '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-for-of': 7.21.5(@babel/core@7.21.8) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.8) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.8) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.8) + '@babel/plugin-transform-modules-commonjs': 7.21.5(@babel/core@7.21.8) + '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.8) + '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.8) + '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.8) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-regenerator': 7.21.5(@babel/core@7.21.8) + '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.8) + '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.8) + '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.8) + '@babel/plugin-transform-unicode-escapes': 7.21.5(@babel/core@7.21.8) + '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.8) + '@babel/preset-modules': 0.1.5(@babel/core@7.21.8) '@babel/types': 7.21.5 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.18.2) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.18.2) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.18.2) + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.8) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.8) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.8) core-js-compat: 3.30.2 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: false - /@babel/preset-modules@0.1.5(@babel/core@7.18.2): + /@babel/preset-modules@0.1.5(@babel/core@7.21.8): resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -7005,10 +7008,10 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-plugin-utils': 7.21.5 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.18.2) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.18.2) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.8) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.8) '@babel/types': 7.21.5 esutils: 2.0.3 dev: false @@ -7028,7 +7031,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.8 + '@babel/parser': 7.21.9 '@babel/types': 7.21.5 dev: false @@ -7043,7 +7046,7 @@ packages: '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.18.4 - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: @@ -7055,12 +7058,12 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.5 + '@babel/generator': 7.21.9 '@babel/helper-environment-visitor': 7.21.5 '@babel/helper-function-name': 7.21.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.8 + '@babel/parser': 7.21.9 '@babel/types': 7.21.5 debug: 4.3.4 globals: 11.12.0 @@ -7068,13 +7071,6 @@ packages: - supports-color dev: false - /@babel/types@7.18.4: - resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - /@babel/types@7.21.5: resolution: {integrity: sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==} engines: {node: '>=6.9.0'} @@ -8519,7 +8515,7 @@ packages: slash: 3.0.0 dev: true - /@rollup/plugin-babel@5.3.1(@babel/core@7.18.2)(rollup@2.79.1): + /@rollup/plugin-babel@5.3.1(@babel/core@7.21.8)(rollup@2.79.1): resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} engines: {node: '>= 10.0.0'} peerDependencies: @@ -8534,7 +8530,7 @@ packages: rollup: optional: true dependencies: - '@babel/core': 7.18.2 + '@babel/core': 7.21.8 '@babel/helper-module-imports': 7.21.4 '@rollup/pluginutils': 3.1.0(rollup@2.79.1) rollup: 2.79.1 @@ -8752,7 +8748,7 @@ packages: /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.18.4 '@babel/types': 7.21.5 dev: false @@ -9396,7 +9392,7 @@ packages: '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8) '@babel/template': 7.20.7 '@babel/traverse': 7.18.2 - '@babel/types': 7.18.4 + '@babel/types': 7.21.5 '@vue/babel-helper-vue-transform-on': 1.0.2 camelcase: 6.3.0 html-tags: 3.3.1 @@ -9418,7 +9414,7 @@ packages: /@vue/compiler-core@3.2.47: resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.21.9 '@vue/shared': 3.2.47 estree-walker: 2.0.2 source-map: 0.6.1 @@ -9454,7 +9450,7 @@ packages: /@vue/compiler-sfc@3.2.47: resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} dependencies: - '@babel/parser': 7.18.4 + '@babel/parser': 7.21.9 '@vue/compiler-core': 3.2.47 '@vue/compiler-dom': 3.2.47 '@vue/compiler-ssr': 3.2.47 @@ -9491,7 +9487,7 @@ packages: /@vue/reactivity-transform@3.2.47: resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} dependencies: - '@babel/parser': 7.21.8 + '@babel/parser': 7.21.9 '@vue/compiler-core': 3.2.47 '@vue/shared': 3.2.47 estree-walker: 2.0.2 @@ -9893,8 +9889,8 @@ packages: optional: true dependencies: '@babel/helper-module-imports': 7.16.0 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8) - '@babel/types': 7.18.4 + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.18.2) + '@babel/types': 7.21.5 html-entities: 2.3.2 dev: false @@ -9909,7 +9905,7 @@ packages: resolve: 1.22.2 dev: false - /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.18.2): + /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.21.8): resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -9918,14 +9914,14 @@ packages: optional: true dependencies: '@babel/compat-data': 7.21.7 - '@babel/core': 7.18.2 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.8) semver: 6.3.0 transitivePeerDependencies: - supports-color dev: false - /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.18.2): + /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.21.8): resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -9933,14 +9929,14 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.8) core-js-compat: 3.30.2 transitivePeerDependencies: - supports-color dev: false - /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.18.2): + /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.21.8): resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} peerDependencies: '@babel/core': ^7.0.0-0 @@ -9948,8 +9944,8 @@ packages: '@babel/core': optional: true dependencies: - '@babel/core': 7.18.2 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.8) transitivePeerDependencies: - supports-color dev: false @@ -18063,10 +18059,10 @@ packages: engines: {node: '>=10.0.0'} dependencies: '@apideck/better-ajv-errors': 0.3.6(ajv@8.12.0) - '@babel/core': 7.18.2 - '@babel/preset-env': 7.21.5(@babel/core@7.18.2) + '@babel/core': 7.21.8 + '@babel/preset-env': 7.21.5(@babel/core@7.21.8) '@babel/runtime': 7.21.5 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.18.2)(rollup@2.79.1) + '@rollup/plugin-babel': 5.3.1(@babel/core@7.21.8)(rollup@2.79.1) '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.1) '@rollup/plugin-replace': 2.4.2(rollup@2.79.1) '@surma/rollup-plugin-off-main-thread': 2.2.3 From 52af9ad18840ffa4e2996386c82cbe34d9fd076a Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Fri, 26 May 2023 16:59:51 +0800 Subject: [PATCH 14/26] Add error message if `Astro.glob` is called outside (#7204) --- .changeset/moody-coats-develop.md | 5 +++++ packages/astro/src/runtime/server/astro-global.ts | 5 +++++ .../astro/test/units/runtime/astro-global.test.js | 13 +++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 .changeset/moody-coats-develop.md create mode 100644 packages/astro/test/units/runtime/astro-global.test.js diff --git a/.changeset/moody-coats-develop.md b/.changeset/moody-coats-develop.md new file mode 100644 index 000000000..20c12708c --- /dev/null +++ b/.changeset/moody-coats-develop.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Add error message if `Astro.glob` is called outside of an Astro file diff --git a/packages/astro/src/runtime/server/astro-global.ts b/packages/astro/src/runtime/server/astro-global.ts index 7ed511322..70c4e565b 100644 --- a/packages/astro/src/runtime/server/astro-global.ts +++ b/packages/astro/src/runtime/server/astro-global.ts @@ -4,6 +4,11 @@ import { ASTRO_VERSION } from '../../core/constants.js'; /** Create the Astro.glob() runtime function. */ function createAstroGlobFn() { const globHandler = (importMetaGlobResult: Record, globValue: () => any) => { + if (typeof importMetaGlobResult === 'string') { + throw new Error( + 'Astro.glob() does not work outside of an Astro file. Use `import.meta.glob()` instead.' + ); + } let allEntries = [...Object.values(importMetaGlobResult)]; if (allEntries.length === 0) { throw new Error(`Astro.glob(${JSON.stringify(globValue())}) - no matches found.`); diff --git a/packages/astro/test/units/runtime/astro-global.test.js b/packages/astro/test/units/runtime/astro-global.test.js new file mode 100644 index 000000000..975651b76 --- /dev/null +++ b/packages/astro/test/units/runtime/astro-global.test.js @@ -0,0 +1,13 @@ +import { expect } from 'chai'; +import { createAstro } from '../../../dist/runtime/server/index.js'; + +describe('astro global', () => { + it('Glob should error if passed incorrect value', async () => { + const Astro = createAstro(undefined); + expect(() => { + Astro.glob('./**/*.md'); + }).to.throw( + 'Astro.glob() does not work outside of an Astro file. Use `import.meta.glob()` instead.' + ); + }); +}); From bf63f615fc1b97d6fb84db55f7639084e3ada5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Alves?= <71379045+andremralves@users.noreply.github.com> Date: Fri, 26 May 2023 10:02:35 -0300 Subject: [PATCH 15/26] Add global crypto to @astrojs/webapi (#6981) * add crypto to be polyfilled * chore: changeset * chore: update deps for node types --------- Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> Co-authored-by: Princesseuh --- .changeset/fifty-months-mix.md | 5 +++++ packages/telemetry/package.json | 2 +- packages/webapi/mod.d.ts | 2 +- packages/webapi/package.json | 2 +- packages/webapi/src/polyfill.ts | 3 +++ packages/webapi/src/ponyfill.ts | 2 ++ pnpm-lock.yaml | 8 ++++---- 7 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 .changeset/fifty-months-mix.md diff --git a/.changeset/fifty-months-mix.md b/.changeset/fifty-months-mix.md new file mode 100644 index 000000000..78a009950 --- /dev/null +++ b/.changeset/fifty-months-mix.md @@ -0,0 +1,5 @@ +--- +'@astrojs/webapi': minor +--- + +Add polyfill for `crypto` diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index 63c20cba3..c50c89ba3 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@types/debug": "^4.1.7", "@types/dlv": "^1.1.2", - "@types/node": "^14.18.21", + "@types/node": "^18.7.21", "@types/which-pm-runs": "^1.0.0", "astro-scripts": "workspace:*", "chai": "^4.3.6", diff --git a/packages/webapi/mod.d.ts b/packages/webapi/mod.d.ts index ecc902364..60bdbab37 100644 --- a/packages/webapi/mod.d.ts +++ b/packages/webapi/mod.d.ts @@ -1,6 +1,6 @@ // organize-imports-ignore export { pathToPosix } from './lib/utils'; -export { alert, ByteLengthQueuingStrategy, cancelAnimationFrame, cancelIdleCallback, CanvasRenderingContext2D, CharacterData, clearTimeout, Comment, CountQueuingStrategy, CSSStyleSheet, CustomElementRegistry, CustomEvent, Document, DocumentFragment, DOMException, Element, Event, EventTarget, fetch, File, FormData, Headers, HTMLBodyElement, HTMLCanvasElement, HTMLDivElement, HTMLDocument, HTMLElement, HTMLHeadElement, HTMLHtmlElement, HTMLImageElement, HTMLSpanElement, HTMLStyleElement, HTMLTemplateElement, HTMLUnknownElement, Image, ImageData, IntersectionObserver, MediaQueryList, MutationObserver, Node, NodeFilter, NodeIterator, OffscreenCanvas, ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, Request, requestAnimationFrame, requestIdleCallback, ResizeObserver, Response, setTimeout, ShadowRoot, structuredClone, StyleSheet, Text, TransformStream, TreeWalker, URLPattern, Window, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter, } from './mod.js'; +export { alert, ByteLengthQueuingStrategy, cancelAnimationFrame, cancelIdleCallback, CanvasRenderingContext2D, CharacterData, clearTimeout, Comment, CountQueuingStrategy, crypto, CSSStyleSheet, CustomElementRegistry, CustomEvent, Document, DocumentFragment, DOMException, Element, Event, EventTarget, fetch, File, FormData, Headers, HTMLBodyElement, HTMLCanvasElement, HTMLDivElement, HTMLDocument, HTMLElement, HTMLHeadElement, HTMLHtmlElement, HTMLImageElement, HTMLSpanElement, HTMLStyleElement, HTMLTemplateElement, HTMLUnknownElement, Image, ImageData, IntersectionObserver, MediaQueryList, MutationObserver, Node, NodeFilter, NodeIterator, OffscreenCanvas, ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, Request, requestAnimationFrame, requestIdleCallback, ResizeObserver, Response, setTimeout, ShadowRoot, structuredClone, StyleSheet, Text, TransformStream, TreeWalker, URLPattern, Window, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter, } from './mod.js'; export declare const polyfill: { (target: any, options?: PolyfillOptions): any; internals(target: any, name: string): any; diff --git a/packages/webapi/package.json b/packages/webapi/package.json index 2e35c43c7..61f359a33 100644 --- a/packages/webapi/package.json +++ b/packages/webapi/package.json @@ -59,7 +59,7 @@ "@rollup/plugin-typescript": "^8.3.2", "@types/chai": "^4.3.1", "@types/mocha": "^9.1.1", - "@types/node": "^14.18.21", + "@types/node": "^18.7.21", "@ungap/structured-clone": "^0.3.4", "chai": "^4.3.6", "event-target-shim": "^6.0.2", diff --git a/packages/webapi/src/polyfill.ts b/packages/webapi/src/polyfill.ts index 4d1531a3f..5fadd5a68 100644 --- a/packages/webapi/src/polyfill.ts +++ b/packages/webapi/src/polyfill.ts @@ -8,6 +8,7 @@ import { clearTimeout, Comment, CountQueuingStrategy, + crypto, CSSStyleSheet, CustomElementRegistry, CustomEvent, @@ -87,6 +88,7 @@ export { clearTimeout, Comment, CountQueuingStrategy, + crypto, CSSStyleSheet, CustomElementRegistry, CustomEvent, @@ -211,6 +213,7 @@ export const polyfill = (target: any, options?: PolyfillOptions) => { cancelAnimationFrame, cancelIdleCallback, clearTimeout, + crypto, fetch, requestAnimationFrame, requestIdleCallback, diff --git a/packages/webapi/src/ponyfill.ts b/packages/webapi/src/ponyfill.ts index 368f7ee5b..9be4377c5 100644 --- a/packages/webapi/src/ponyfill.ts +++ b/packages/webapi/src/ponyfill.ts @@ -14,6 +14,7 @@ import { WritableStreamDefaultController, WritableStreamDefaultWriter, } from 'node:stream/web' // Remove when Node 16 is dropped for Node 18. +import { webcrypto as crypto } from 'node:crypto' // Remove when Node 18 is dropped for Node 20 import { fetch, File, FormData, Headers, Request, Response } from 'undici' // Remove when Node 16 is dropped for Node 18. import { URLPattern } from 'urlpattern-polyfill' import { @@ -133,6 +134,7 @@ export { cancelAnimationFrame, cancelIdleCallback, clearTimeout, + crypto, fetch, requestAnimationFrame, requestIdleCallback, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82cad787a..635c51a2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5201,8 +5201,8 @@ importers: specifier: ^1.1.2 version: 1.1.2 '@types/node': - specifier: ^14.18.21 - version: 14.18.21 + specifier: ^18.7.21 + version: 18.16.3 '@types/which-pm-runs': specifier: ^1.0.0 version: 1.0.0 @@ -5241,8 +5241,8 @@ importers: specifier: ^9.1.1 version: 9.1.1 '@types/node': - specifier: ^14.18.21 - version: 14.18.21 + specifier: ^18.7.21 + version: 18.16.3 '@ungap/structured-clone': specifier: ^0.3.4 version: 0.3.4 From 6b12f93b579763efd8d7d884416297d785f334e1 Mon Sep 17 00:00:00 2001 From: Princesseuh Date: Fri, 26 May 2023 13:04:57 +0000 Subject: [PATCH 16/26] [ci] format --- packages/webapi/src/ponyfill.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webapi/src/ponyfill.ts b/packages/webapi/src/ponyfill.ts index 9be4377c5..036fbc180 100644 --- a/packages/webapi/src/ponyfill.ts +++ b/packages/webapi/src/ponyfill.ts @@ -1,5 +1,6 @@ // @ts-check import { Event, EventTarget } from 'event-target-shim' // Look into removing when Node 18 is dropped for Node 20 +import { webcrypto as crypto } from 'node:crypto' // Remove when Node 18 is dropped for Node 20 import { ByteLengthQueuingStrategy, CountQueuingStrategy, @@ -14,7 +15,6 @@ import { WritableStreamDefaultController, WritableStreamDefaultWriter, } from 'node:stream/web' // Remove when Node 16 is dropped for Node 18. -import { webcrypto as crypto } from 'node:crypto' // Remove when Node 18 is dropped for Node 20 import { fetch, File, FormData, Headers, Request, Response } from 'undici' // Remove when Node 16 is dropped for Node 18. import { URLPattern } from 'urlpattern-polyfill' import { From 6c7df28ab34b756b8426443bf6976e24d4611a62 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Fri, 26 May 2023 21:30:42 +0800 Subject: [PATCH 17/26] Fix CSS deduping and missing chunks (#7218) --- .changeset/eighty-gifts-cheer.md | 5 +++ packages/astro/src/core/build/internal.ts | 11 +++-- .../src/core/build/plugins/plugin-css.ts | 41 ++++++++----------- packages/astro/test/0-css.test.js | 15 +++++++ .../_components/SvelteOnlyAndSsr.svelte | 11 +++++ .../src/pages/client-only-and-ssr/only.astro | 7 ++++ .../src/pages/client-only-and-ssr/ssr.astro | 7 ++++ 7 files changed, 69 insertions(+), 28 deletions(-) create mode 100644 .changeset/eighty-gifts-cheer.md create mode 100644 packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/_components/SvelteOnlyAndSsr.svelte create mode 100644 packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/only.astro create mode 100644 packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/ssr.astro diff --git a/.changeset/eighty-gifts-cheer.md b/.changeset/eighty-gifts-cheer.md new file mode 100644 index 000000000..41ca0d7f6 --- /dev/null +++ b/.changeset/eighty-gifts-cheer.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix CSS deduping and missing chunks diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index 18d28ef74..d1e2404d6 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -8,10 +8,13 @@ import type { PageBuildData, StylesheetAsset, ViteID } from './types'; export interface BuildInternals { /** - * The module ids of all CSS chunks, used to deduplicate CSS assets between - * SSR build and client build in vite-plugin-css. + * Each CSS module is named with a chunk id derived from the Astro pages they + * are used in by default. It's easy to crawl this relation in the SSR build as + * the Astro pages are the entrypoint, but not for the client build as hydratable + * components are the entrypoint instead. This map is used as a cache from the SSR + * build so the client can pick up the same information and use the same chunk ids. */ - cssChunkModuleIds: Set; + cssModuleToChunkIdMap: Map; // A mapping of hoisted script ids back to the exact hoisted scripts it references hoistedScriptIdToHoistedMap: Map>; @@ -92,7 +95,7 @@ export function createBuildInternals(): BuildInternals { const hoistedScriptIdToPagesMap = new Map>(); return { - cssChunkModuleIds: new Set(), + cssModuleToChunkIdMap: new Map(), hoistedScriptIdToHoistedMap, hoistedScriptIdToPagesMap, entrySpecifierToBundleMap: new Map(), diff --git a/packages/astro/src/core/build/plugins/plugin-css.ts b/packages/astro/src/core/build/plugins/plugin-css.ts index a5bdb70f1..1cec97223 100644 --- a/packages/astro/src/core/build/plugins/plugin-css.ts +++ b/packages/astro/src/core/build/plugins/plugin-css.ts @@ -64,20 +64,7 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] { const cssBuildPlugin: VitePlugin = { name: 'astro:rollup-plugin-build-css', - transform(_, id) { - // In the SSR build, styles that are bundled are tracked in `internals.cssChunkModuleIds`. - // In the client build, if we're also bundling the same style, return an empty string to - // deduplicate the final CSS output. - if (options.target === 'client' && internals.cssChunkModuleIds.has(id)) { - return ''; - } - }, - outputOptions(outputOptions) { - // Skip in client builds as its module graph doesn't have reference to Astro pages - // to be able to chunk based on it's related top-level pages. - if (options.target === 'client') return; - const assetFileNames = outputOptions.assetFileNames; const namingIncludesHash = assetFileNames?.toString().includes('[hash]'); const createNameForParentPages = namingIncludesHash @@ -89,16 +76,31 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] { // For CSS, create a hash of all of the pages that use it. // This causes CSS to be built into shared chunks when used by multiple pages. if (isBuildableCSSRequest(id)) { + // For client builds that has hydrated components as entrypoints, there's no way + // to crawl up and find the pages that use it. So we lookup the cache during SSR + // build (that has the pages information) to derive the same chunk id so they + // match up on build, making sure both builds has the CSS deduped. + // NOTE: Components that are only used with `client:only` may not exist in the cache + // and that's okay. We can use Rollup's default chunk strategy instead as these CSS + // are outside of the SSR build scope, which no dedupe is needed. + if (options.target === 'client') { + return internals.cssModuleToChunkIdMap.get(id)!; + } + for (const [pageInfo] of walkParentInfos(id, { getModuleInfo: meta.getModuleInfo, })) { if (new URL(pageInfo.id, 'file://').searchParams.has(PROPAGATED_ASSET_FLAG)) { // Split delayed assets to separate modules // so they can be injected where needed - return createNameHash(id, [id]); + const chunkId = createNameHash(id, [id]); + internals.cssModuleToChunkIdMap.set(id, chunkId); + return chunkId; } } - return createNameForParentPages(id, meta); + const chunkId = createNameForParentPages(id, meta); + internals.cssModuleToChunkIdMap.set(id, chunkId); + return chunkId; } }, }); @@ -113,15 +115,6 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] { // Skip if the chunk has no CSS, we want to handle CSS chunks only if (meta.importedCss.size < 1) continue; - // In the SSR build, keep track of all CSS chunks' modules as the client build may - // duplicate them, e.g. for `client:load` components that render in SSR and client - // for hydation. - if (options.target === 'server') { - for (const id of Object.keys(chunk.modules)) { - internals.cssChunkModuleIds.add(id); - } - } - // For the client build, client:only styles need to be mapped // over to their page. For this chunk, determine if it's a child of a // client:only component and if so, add its CSS to the page it belongs to. diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 158389132..76bfba296 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -265,6 +265,21 @@ describe('CSS', function () { new RegExp(`.svelte-scss.${scopedClass}[^{]*{font-family:ComicSansMS`) ); }); + + it('client:only and SSR in two pages, both should have styles', async () => { + const onlyHtml = await fixture.readFile('/client-only-and-ssr/only/index.html'); + const $onlyHtml = cheerio.load(onlyHtml); + const onlyHtmlCssHref = $onlyHtml('link[rel=stylesheet][href^=/_astro/]').attr('href'); + const onlyHtmlCss = await fixture.readFile(onlyHtmlCssHref.replace(/^\/?/, '/')); + + const ssrHtml = await fixture.readFile('/client-only-and-ssr/ssr/index.html'); + const $ssrHtml = cheerio.load(ssrHtml); + const ssrHtmlCssHref = $ssrHtml('link[rel=stylesheet][href^=/_astro/]').attr('href'); + const ssrHtmlCss = await fixture.readFile(ssrHtmlCssHref.replace(/^\/?/, '/')); + + expect(onlyHtmlCss).to.include('.svelte-only-and-ssr'); + expect(ssrHtmlCss).to.include('.svelte-only-and-ssr'); + }); }); describe('Vite features', () => { diff --git a/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/_components/SvelteOnlyAndSsr.svelte b/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/_components/SvelteOnlyAndSsr.svelte new file mode 100644 index 000000000..f346f9c04 --- /dev/null +++ b/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/_components/SvelteOnlyAndSsr.svelte @@ -0,0 +1,11 @@ + + +
+ Svelte only and SSR +
+ + diff --git a/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/only.astro b/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/only.astro new file mode 100644 index 000000000..b409693b5 --- /dev/null +++ b/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/only.astro @@ -0,0 +1,7 @@ +--- +import SvelteOnlyAndSsr from './_components/SvelteOnlyAndSsr.svelte' +--- + +
+ +
diff --git a/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/ssr.astro b/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/ssr.astro new file mode 100644 index 000000000..35b7fbae0 --- /dev/null +++ b/packages/astro/test/fixtures/0-css/src/pages/client-only-and-ssr/ssr.astro @@ -0,0 +1,7 @@ +--- +import SvelteOnlyAndSsr from './_components/SvelteOnlyAndSsr.svelte' +--- + +
+ +
From 1c77779dd66a6db77c81ed235da076a6118decde Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Fri, 26 May 2023 22:37:33 +0800 Subject: [PATCH 18/26] Fix `astro-static-slot` hydration mismatch error (#7196) --- .changeset/eleven-walls-explain.md | 7 +++++++ packages/astro/e2e/nested-in-react.test.js | 16 ++++++++++++++++ packages/astro/e2e/nested-in-vue.test.js | 16 ++++++++++++++++ packages/integrations/preact/src/static-html.ts | 4 ++-- packages/integrations/react/static-html.js | 2 +- packages/integrations/vue/static-html.js | 5 ++++- 6 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 .changeset/eleven-walls-explain.md diff --git a/.changeset/eleven-walls-explain.md b/.changeset/eleven-walls-explain.md new file mode 100644 index 000000000..fd2772185 --- /dev/null +++ b/.changeset/eleven-walls-explain.md @@ -0,0 +1,7 @@ +--- +'@astrojs/preact': patch +'@astrojs/react': patch +'@astrojs/vue': patch +--- + +Fix `astro-static-slot` hydration mismatch error diff --git a/packages/astro/e2e/nested-in-react.test.js b/packages/astro/e2e/nested-in-react.test.js index cd9bc2879..7fbe13c0b 100644 --- a/packages/astro/e2e/nested-in-react.test.js +++ b/packages/astro/e2e/nested-in-react.test.js @@ -14,6 +14,22 @@ test.afterAll(async () => { }); test.describe('Nested Frameworks in React', () => { + test('No hydration mismatch', async ({ page, astro }) => { + // Get browser logs + const logs = []; + page.on('console', (msg) => logs.push(msg.text())); + + await page.goto(astro.resolveUrl('/')); + + // wait for root island to hydrate + const counter = page.locator('#react-counter'); + await waitForHydrate(page, counter); + + for (const log of logs) { + expect(log, 'React hydration mismatch').not.toMatch('An error occurred during hydration'); + } + }); + test('React counter', async ({ astro, page }) => { await page.goto(astro.resolveUrl('/')); diff --git a/packages/astro/e2e/nested-in-vue.test.js b/packages/astro/e2e/nested-in-vue.test.js index 6e7c6a5c2..deed309c7 100644 --- a/packages/astro/e2e/nested-in-vue.test.js +++ b/packages/astro/e2e/nested-in-vue.test.js @@ -14,6 +14,22 @@ test.afterAll(async () => { }); test.describe('Nested Frameworks in Vue', () => { + test('no hydration mismatch', async ({ page, astro }) => { + // Get browser logs + const logs = []; + page.on('console', (msg) => logs.push(msg.text())); + + await page.goto(astro.resolveUrl('/')); + + // wait for root island to hydrate + const counter = page.locator('#vue-counter'); + await waitForHydrate(page, counter); + + for (const log of logs) { + expect(log, 'Vue hydration mismatch').not.toMatch('Hydration node mismatch'); + } + }); + test('React counter', async ({ astro, page }) => { await page.goto(astro.resolveUrl('/')); diff --git a/packages/integrations/preact/src/static-html.ts b/packages/integrations/preact/src/static-html.ts index f0fbd885c..453e72b7f 100644 --- a/packages/integrations/preact/src/static-html.ts +++ b/packages/integrations/preact/src/static-html.ts @@ -13,9 +13,9 @@ type Props = { * As a bonus, we can signal to Preact that this subtree is * entirely static and will never change via `shouldComponentUpdate`. */ -const StaticHtml = ({ value, name, hydrate }: Props) => { +const StaticHtml = ({ value, name, hydrate = true }: Props) => { if (!value) return null; - const tagName = hydrate === false ? 'astro-static-slot' : 'astro-slot'; + const tagName = hydrate ? 'astro-slot' : 'astro-static-slot'; return h(tagName, { name, dangerouslySetInnerHTML: { __html: value } }); }; diff --git a/packages/integrations/react/static-html.js b/packages/integrations/react/static-html.js index 37fda1983..e319a40c7 100644 --- a/packages/integrations/react/static-html.js +++ b/packages/integrations/react/static-html.js @@ -7,7 +7,7 @@ import { createElement as h } from 'react'; * As a bonus, we can signal to React that this subtree is * entirely static and will never change via `shouldComponentUpdate`. */ -const StaticHtml = ({ value, name, hydrate }) => { +const StaticHtml = ({ value, name, hydrate = true }) => { if (!value) return null; const tagName = hydrate ? 'astro-slot' : 'astro-static-slot'; return h(tagName, { diff --git a/packages/integrations/vue/static-html.js b/packages/integrations/vue/static-html.js index 34740f88f..885319026 100644 --- a/packages/integrations/vue/static-html.js +++ b/packages/integrations/vue/static-html.js @@ -10,7 +10,10 @@ const StaticHtml = defineComponent({ props: { value: String, name: String, - hydrate: Boolean, + hydrate: { + type: Boolean, + default: true, + }, }, setup({ name, value, hydrate }) { if (!value) return () => null; From f2f18b44055c6334a39d6379de88fe41e518aa1e Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 26 May 2023 22:03:06 +0200 Subject: [PATCH 19/26] feat: support images outside the project (#7139) --- .changeset/wise-cars-hear.md | 5 ++ packages/astro/src/assets/services/service.ts | 9 ++++ packages/astro/src/assets/utils/emitAsset.ts | 27 ++--------- .../astro/src/assets/vite-plugin-assets.ts | 9 ++-- packages/astro/src/content/runtime-assets.ts | 10 +--- packages/astro/src/content/utils.ts | 2 +- packages/astro/src/core/errors/errors-data.ts | 47 ++++++++++++++----- packages/astro/test/core-image.test.js | 42 +++++++++++++---- .../fixtures/core-image-ssr/src/pages/api.ts | 1 - .../src/pages/error-image-src-passed.astro | 6 +++ .../core-image/src/pages/outsideProject.astro | 8 ++++ 11 files changed, 107 insertions(+), 59 deletions(-) create mode 100644 .changeset/wise-cars-hear.md create mode 100644 packages/astro/test/fixtures/core-image/src/pages/error-image-src-passed.astro create mode 100644 packages/astro/test/fixtures/core-image/src/pages/outsideProject.astro diff --git a/.changeset/wise-cars-hear.md b/.changeset/wise-cars-hear.md new file mode 100644 index 000000000..21019c26f --- /dev/null +++ b/.changeset/wise-cars-hear.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +The `src` property returned by ESM importing images with `astro:assets` is now an absolute path, unlocking support for importing images outside the project. diff --git a/packages/astro/src/assets/services/service.ts b/packages/astro/src/assets/services/service.ts index 910f0f0a8..9706c3ed0 100644 --- a/packages/astro/src/assets/services/service.ts +++ b/packages/astro/src/assets/services/service.ts @@ -109,6 +109,7 @@ export type BaseServiceTransform = { */ export const baseService: Omit = { validateOptions(options) { + // `src` is missing or is `undefined`. if (!options.src || (typeof options.src !== 'string' && typeof options.src !== 'object')) { throw new AstroError({ ...AstroErrorData.ExpectedImage, @@ -117,6 +118,14 @@ export const baseService: Omit = { } if (!isESMImportedImage(options.src)) { + // User passed an `/@fs/` path instead of the full image. + if (options.src.startsWith('/@fs/')) { + throw new AstroError({ + ...AstroErrorData.LocalImageUsedWrongly, + message: AstroErrorData.LocalImageUsedWrongly.message(options.src), + }); + } + // For remote images, width and height are explicitly required as we can't infer them from the file let missingDimension: 'width' | 'height' | 'both' | undefined; if (!options.width && !options.height) { diff --git a/packages/astro/src/assets/utils/emitAsset.ts b/packages/astro/src/assets/utils/emitAsset.ts index 79775b96d..9b8d6fc08 100644 --- a/packages/astro/src/assets/utils/emitAsset.ts +++ b/packages/astro/src/assets/utils/emitAsset.ts @@ -2,14 +2,13 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import slash from 'slash'; -import type { AstroConfig, AstroSettings } from '../../@types/astro'; +import { prependForwardSlash } from '../../core/path.js'; import { imageMetadata, type Metadata } from './metadata.js'; export async function emitESMImage( id: string | undefined, watchMode: boolean, - fileEmitter: any, - settings: Pick + fileEmitter: any ): Promise { if (!id) { return undefined; @@ -40,34 +39,14 @@ export async function emitESMImage( url.searchParams.append('origHeight', meta.height.toString()); url.searchParams.append('origFormat', meta.format); - meta.src = rootRelativePath(settings.config, url); + meta.src = `/@fs` + prependForwardSlash(fileURLToNormalizedPath(url)); } return meta; } -/** - * Utilities inlined from `packages/astro/src/core/util.ts` - * Avoids ESM / CJS bundling failures when accessed from integrations - * due to Vite dependencies in core. - */ - -function rootRelativePath(config: Pick, url: URL): string { - const basePath = fileURLToNormalizedPath(url); - const rootPath = fileURLToNormalizedPath(config.root); - return prependForwardSlash(basePath.slice(rootPath.length)); -} - -function prependForwardSlash(filePath: string): string { - return filePath[0] === '/' ? filePath : '/' + filePath; -} - function fileURLToNormalizedPath(filePath: URL): string { // Uses `slash` package instead of Vite's `normalizePath` // to avoid CJS bundling issues. return slash(fileURLToPath(filePath) + filePath.search).replace(/\\/g, '/'); } - -export function emoji(char: string, fallback: string): string { - return process.platform !== 'win32' ? char : fallback; -} diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index 9a8f3e3ad..4088c7ec5 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -107,13 +107,12 @@ export default function assets({ } const url = new URL(req.url, 'file:'); - const filePath = url.searchParams.get('href'); - - if (!filePath) { + if (!url.searchParams.has('href')) { return next(); } - const filePathURL = new URL('.' + filePath, settings.config.root); + const filePath = url.searchParams.get('href')?.slice('/@fs'.length); + const filePathURL = new URL('.' + filePath, 'file:'); const file = await fs.readFile(filePathURL); // Get the file's metadata from the URL @@ -243,7 +242,7 @@ export default function assets({ const cleanedUrl = removeQueryString(id); if (/\.(jpeg|jpg|png|tiff|webp|gif|svg)$/.test(cleanedUrl)) { - const meta = await emitESMImage(id, this.meta.watchMode, this.emitFile, settings); + const meta = await emitESMImage(id, this.meta.watchMode, this.emitFile); return `export default ${JSON.stringify(meta)}`; } }, diff --git a/packages/astro/src/content/runtime-assets.ts b/packages/astro/src/content/runtime-assets.ts index b24dac8b4..122e00aa6 100644 --- a/packages/astro/src/content/runtime-assets.ts +++ b/packages/astro/src/content/runtime-assets.ts @@ -1,21 +1,15 @@ import type { PluginContext } from 'rollup'; import { z } from 'zod'; -import type { AstroSettings } from '../@types/astro.js'; import { emitESMImage } from '../assets/index.js'; -export function createImage( - settings: Pick, - pluginContext: PluginContext, - entryFilePath: string -) { +export function createImage(pluginContext: PluginContext, entryFilePath: string) { return () => { return z.string().transform(async (imagePath, ctx) => { const resolvedFilePath = (await pluginContext.resolve(imagePath, entryFilePath))?.id; const metadata = await emitESMImage( resolvedFilePath, pluginContext.meta.watchMode, - pluginContext.emitFile, - settings + pluginContext.emitFile ); if (!metadata) { diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 831b25fb4..686176096 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -111,7 +111,7 @@ export async function getEntryData( } schema = schema({ - image: createImage({ config }, pluginContext, entry._internal.filePath), + image: createImage(pluginContext, entry._internal.filePath), }); } diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index a97c18d0e..e50000c56 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -517,7 +517,7 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati * For unsupported formats such as SVGs and GIFs, you may be able to use an `img` tag directly: * ```astro * --- - * import rocket from '../assets/images/rocket.svg' + * import rocket from '../assets/images/rocket.svg'; * --- * * A rocketship in space. @@ -626,7 +626,7 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati * Making changes to the response, such as setting headers, cookies, and the status code cannot be done outside of page components. */ ResponseSentError: { - title: 'Unable to set response', + title: 'Unable to set response.', code: 3030, message: 'The response has already been sent to the browser and cannot be altered.', }, @@ -646,7 +646,7 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati * ``` */ MiddlewareNoDataOrNextCalled: { - title: "The middleware didn't return a response or call `next`", + title: "The middleware didn't return a response or call `next`.", code: 3031, message: 'The middleware needs to either return a `Response` object or call the `next` function.', @@ -666,7 +666,7 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati * ``` */ MiddlewareNotAResponse: { - title: 'The middleware returned something that is not a `Response` object', + title: 'The middleware returned something that is not a `Response` object.', code: 3032, message: 'Any data returned from middleware must be a valid `Response` object.', }, @@ -687,12 +687,38 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati * ``` */ LocalsNotAnObject: { - title: 'Value assigned to `locals` is not accepted', + title: 'Value assigned to `locals` is not accepted.', code: 3033, message: '`locals` can only be assigned to an object. Other values like numbers, strings, etc. are not accepted.', hint: 'If you tried to remove some information from the `locals` object, try to use `delete` or set the property to `undefined`.', }, + /** + * @docs + * @see + * - [Assets (Experimental)](https://docs.astro.build/en/guides/assets/) + * @description + * When using the default image services, `Image`'s and `getImage`'s `src` parameter must be either an imported image or an URL, it cannot be a filepath. + * + * ```astro + * --- + * import { Image } from "astro:assets"; + * import myImage from "../my_image.png"; + * --- + * + * + * Cool image + * + * + * Cool image + * ``` + */ + LocalImageUsedWrongly: { + title: 'ESM imported images must be passed as-is.', + code: 3034, + message: (imageFilePath: string) => + `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or an URL, it cannot be a filepath. Received \`${imageFilePath}\`.`, + }, // No headings here, that way Vite errors are merged with Astro ones in the docs, which makes more sense to users. // Vite Errors - 4xxx /** @@ -961,7 +987,6 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati }, /** * @docs - * @message A content collection schema should not contain `slug` since it is reserved for slug generation. Remove this from your `COLLECTION_NAME` collection schema. * @see * - [The reserved entry `slug` field](https://docs.astro.build/en/guides/content-collections/) * @description @@ -970,9 +995,8 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati ContentSchemaContainsSlugError: { title: 'Content Schema should not contain `slug`.', code: 9003, - message: (collection: string) => { - return `A content collection schema should not contain \`slug\` since it is reserved for slug generation. Remove this from your ${collection} collection schema.`; - }, + message: (collectionName: string) => + `A content collection schema should not contain \`slug\` since it is reserved for slug generation. Remove this from your ${collectionName} collection schema.`, hint: 'See https://docs.astro.build/en/guides/content-collections/ for more on the `slug` field.', }, @@ -985,9 +1009,8 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati CollectionDoesNotExistError: { title: 'Collection does not exist', code: 9004, - message: (collection: string) => { - return `The collection **${collection}** does not exist. Ensure a collection directory with this name exists.`; - }, + message: (collectionName: string) => + `The collection **${collectionName}** does not exist. Ensure a collection directory with this name exists.`, hint: 'See https://docs.astro.build/en/guides/content-collections/ for more on creating collections.', }, /** diff --git a/packages/astro/test/core-image.test.js b/packages/astro/test/core-image.test.js index b6b48270e..4fec48284 100644 --- a/packages/astro/test/core-image.test.js +++ b/packages/astro/test/core-image.test.js @@ -114,6 +114,32 @@ describe('astro:image', () => { expect(logs).to.have.a.lengthOf(1); expect(logs[0].message).to.contain('Received unsupported format'); }); + + it("errors when an ESM imported image's src is passed to Image/getImage instead of the full import ssss", async () => { + logs.length = 0; + let res = await fixture.fetch('/error-image-src-passed'); + await res.text(); + + expect(logs).to.have.a.lengthOf(1); + expect(logs[0].message).to.contain('must be an imported image or an URL'); + }); + + it('supports images from outside the project', async () => { + let res = await fixture.fetch('/outsideProject'); + let html = await res.text(); + $ = cheerio.load(html); + + let $img = $('img'); + expect($img).to.have.a.lengthOf(2); + expect( + $img.toArray().every((img) => { + return ( + img.attribs['src'].startsWith('/@fs/') || + img.attribs['src'].startsWith('/_image?href=%2F%40fs%2F') + ); + }) + ).to.be.true; + }); }); describe('vite-isms', () => { @@ -228,9 +254,9 @@ describe('astro:image', () => { expect($img).to.have.a.lengthOf(1); // Verbose test for the full URL to make sure the image went through the full pipeline - expect($img.attr('src')).to.equal( - '/_image?href=%2Fsrc%2Fassets%2Fpenguin1.jpg%3ForigWidth%3D207%26origHeight%3D243%26origFormat%3Djpg&f=webp' - ); + expect( + $img.attr('src').startsWith('/_image') && $img.attr('src').endsWith('f=webp') + ).to.equal(true); }); it('has width and height attributes', () => { @@ -297,12 +323,12 @@ describe('astro:image', () => { it('has proper source for directly used image', () => { let $img = $('#direct-image img'); - expect($img.attr('src').startsWith('/src/')).to.equal(true); + expect($img.attr('src').startsWith('/')).to.equal(true); }); it('has proper source for refined image', () => { let $img = $('#refined-image img'); - expect($img.attr('src').startsWith('/src/')).to.equal(true); + expect($img.attr('src').startsWith('/')).to.equal(true); }); it('has proper sources for array of images', () => { @@ -310,7 +336,7 @@ describe('astro:image', () => { const imgsSrcs = []; $img.each((i, img) => imgsSrcs.push(img.attribs['src'])); expect($img).to.have.a.lengthOf(2); - expect(imgsSrcs.every((img) => img.startsWith('/src/'))).to.be.true; + expect(imgsSrcs.every((img) => img.startsWith('/'))).to.be.true; }); it('has proper attributes for optimized image through getImage', () => { @@ -330,7 +356,7 @@ describe('astro:image', () => { it('properly handles nested images', () => { let $img = $('#nested-image img'); - expect($img.attr('src').startsWith('/src/')).to.equal(true); + expect($img.attr('src').startsWith('/')).to.equal(true); }); }); @@ -348,7 +374,7 @@ describe('astro:image', () => { }); it('includes /src in the path', async () => { - expect($('img').attr('src').startsWith('/src')).to.equal(true); + expect($('img').attr('src').includes('/src')).to.equal(true); }); }); }); diff --git a/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts b/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts index 9534383db..a5526e1a1 100644 --- a/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts +++ b/packages/astro/test/fixtures/core-image-ssr/src/pages/api.ts @@ -2,7 +2,6 @@ import { APIRoute } from "../../../../../src/@types/astro"; export const get = (async ({ params, request }) => { const url = new URL(request.url); - console.log(url) const src = url.searchParams.get("src"); return { diff --git a/packages/astro/test/fixtures/core-image/src/pages/error-image-src-passed.astro b/packages/astro/test/fixtures/core-image/src/pages/error-image-src-passed.astro new file mode 100644 index 000000000..71b34101e --- /dev/null +++ b/packages/astro/test/fixtures/core-image/src/pages/error-image-src-passed.astro @@ -0,0 +1,6 @@ +--- +import { Image } from "astro:assets"; +import myImage from "../assets/penguin1.jpg"; +--- + +hello diff --git a/packages/astro/test/fixtures/core-image/src/pages/outsideProject.astro b/packages/astro/test/fixtures/core-image/src/pages/outsideProject.astro new file mode 100644 index 000000000..268b44741 --- /dev/null +++ b/packages/astro/test/fixtures/core-image/src/pages/outsideProject.astro @@ -0,0 +1,8 @@ +--- +import { Image } from "astro:assets"; +import imageOutsideProject from "../../../core-image-base/src/assets/penguin1.jpg"; +--- + +outside project + + From df1e6f78dfb44ccdb23125aa66d03f446aa35777 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Mon, 29 May 2023 16:37:40 +0800 Subject: [PATCH 20/26] Fix markdoc image tests (#7231) --- packages/integrations/markdoc/test/image-assets.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/integrations/markdoc/test/image-assets.test.js b/packages/integrations/markdoc/test/image-assets.test.js index 313977934..7339960dd 100644 --- a/packages/integrations/markdoc/test/image-assets.test.js +++ b/packages/integrations/markdoc/test/image-assets.test.js @@ -35,8 +35,8 @@ describe('Markdoc - Image assets', () => { const res = await baseFixture.fetch('/'); const html = await res.text(); const { document } = parseHTML(html); - expect(document.querySelector('#relative > img')?.src).to.equal( - '/_image?href=%2Fsrc%2Fassets%2Frelative%2Foar.jpg%3ForigWidth%3D420%26origHeight%3D630%26origFormat%3Djpg&f=webp' + expect(document.querySelector('#relative > img')?.src).to.match( + /\/_image\?href=.*%2Fsrc%2Fassets%2Frelative%2Foar.jpg%3ForigWidth%3D420%26origHeight%3D630%26origFormat%3Djpg&f=webp/ ); }); @@ -44,8 +44,8 @@ describe('Markdoc - Image assets', () => { const res = await baseFixture.fetch('/'); const html = await res.text(); const { document } = parseHTML(html); - expect(document.querySelector('#alias > img')?.src).to.equal( - '/_image?href=%2Fsrc%2Fassets%2Falias%2Fcityscape.jpg%3ForigWidth%3D420%26origHeight%3D280%26origFormat%3Djpg&f=webp' + expect(document.querySelector('#alias > img')?.src).to.match( + /\/_image\?href=.*%2Fsrc%2Fassets%2Falias%2Fcityscape.jpg%3ForigWidth%3D420%26origHeight%3D280%26origFormat%3Djpg&f=webp/ ); }); }); From af3c5a2e25bd3e7b2a3f7f08e41ee457093c8cb1 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Mon, 29 May 2023 20:42:31 +0800 Subject: [PATCH 21/26] Use AstroError for Astro.glob errors (#7219) Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com> Co-authored-by: sarah11918 --- .changeset/strange-socks-give.md | 5 ++++ packages/astro/e2e/errors.test.js | 23 ++++++++++++-- .../src/components/AstroGlobOutsideAstro.js | 3 ++ .../src/pages/astro-glob-no-match.astro | 3 ++ .../src/pages/astro-glob-outside-astro.astro | 5 ++++ packages/astro/e2e/test-utils.js | 2 ++ packages/astro/src/core/errors/errors-data.ts | 30 +++++++++++++++++++ .../astro/src/runtime/server/astro-global.ts | 13 +++++--- .../test/units/runtime/astro-global.test.js | 11 +++++-- 9 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 .changeset/strange-socks-give.md create mode 100644 packages/astro/e2e/fixtures/errors/src/components/AstroGlobOutsideAstro.js create mode 100644 packages/astro/e2e/fixtures/errors/src/pages/astro-glob-no-match.astro create mode 100644 packages/astro/e2e/fixtures/errors/src/pages/astro-glob-outside-astro.astro diff --git a/.changeset/strange-socks-give.md b/.changeset/strange-socks-give.md new file mode 100644 index 000000000..f1e9492bd --- /dev/null +++ b/.changeset/strange-socks-give.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Use `AstroError` for `Astro.glob` errors diff --git a/packages/astro/e2e/errors.test.js b/packages/astro/e2e/errors.test.js index 35599ce97..2055f1cdf 100644 --- a/packages/astro/e2e/errors.test.js +++ b/packages/astro/e2e/errors.test.js @@ -1,14 +1,21 @@ import { expect } from '@playwright/test'; -import { getErrorOverlayContent, testFactory } from './test-utils.js'; +import { getErrorOverlayContent, silentLogging, testFactory } from './test-utils.js'; const test = testFactory({ root: './fixtures/errors/', + // Only test the error overlay, don't print to console + vite: { + logLevel: 'silent', + }, }); let devServer; test.beforeAll(async ({ astro }) => { - devServer = await astro.startDevServer(); + devServer = await astro.startDevServer({ + // Only test the error overlay, don't print to console + logging: silentLogging, + }); }); test.afterAll(async ({ astro }) => { @@ -89,4 +96,16 @@ test.describe('Error display', () => { expect(await page.locator('vite-error-overlay').count()).toEqual(0); }); + + test('astro glob no match error', async ({ page, astro }) => { + await page.goto(astro.resolveUrl('/astro-glob-no-match'), { waitUntil: 'networkidle' }); + const message = (await getErrorOverlayContent(page)).message; + expect(message).toMatch('did not return any matching files'); + }); + + test('astro glob used outside of an astro file', async ({ page, astro }) => { + await page.goto(astro.resolveUrl('/astro-glob-outside-astro'), { waitUntil: 'networkidle' }); + const message = (await getErrorOverlayContent(page)).message; + expect(message).toMatch('can only be used in'); + }); }); diff --git a/packages/astro/e2e/fixtures/errors/src/components/AstroGlobOutsideAstro.js b/packages/astro/e2e/fixtures/errors/src/components/AstroGlobOutsideAstro.js new file mode 100644 index 000000000..5307474c0 --- /dev/null +++ b/packages/astro/e2e/fixtures/errors/src/components/AstroGlobOutsideAstro.js @@ -0,0 +1,3 @@ +export function globSomething(Astro) { + return Astro.glob('./*.lua') +} diff --git a/packages/astro/e2e/fixtures/errors/src/pages/astro-glob-no-match.astro b/packages/astro/e2e/fixtures/errors/src/pages/astro-glob-no-match.astro new file mode 100644 index 000000000..a7739af58 --- /dev/null +++ b/packages/astro/e2e/fixtures/errors/src/pages/astro-glob-no-match.astro @@ -0,0 +1,3 @@ +--- +Astro.glob('./*.lua') +--- \ No newline at end of file diff --git a/packages/astro/e2e/fixtures/errors/src/pages/astro-glob-outside-astro.astro b/packages/astro/e2e/fixtures/errors/src/pages/astro-glob-outside-astro.astro new file mode 100644 index 000000000..328de56ec --- /dev/null +++ b/packages/astro/e2e/fixtures/errors/src/pages/astro-glob-outside-astro.astro @@ -0,0 +1,5 @@ +--- +import { globSomething } from '../components/AstroGlobOutsideAstro' + +globSomething(Astro) +--- \ No newline at end of file diff --git a/packages/astro/e2e/test-utils.js b/packages/astro/e2e/test-utils.js index d025527d6..72c6e8d56 100644 --- a/packages/astro/e2e/test-utils.js +++ b/packages/astro/e2e/test-utils.js @@ -5,6 +5,8 @@ import { loadFixture as baseLoadFixture } from '../test/test-utils.js'; export const isWindows = process.platform === 'win32'; +export { silentLogging } from '../test/test-utils.js'; + // Get all test files in directory, assign unique port for each of them so they don't conflict const testFiles = await fs.readdir(new URL('.', import.meta.url)); const testFileToPort = new Map(); diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index e50000c56..49ada2a19 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -719,6 +719,36 @@ See https://docs.astro.build/en/guides/server-side-rendering/ for more informati message: (imageFilePath: string) => `\`Image\`'s and \`getImage\`'s \`src\` parameter must be an imported image or an URL, it cannot be a filepath. Received \`${imageFilePath}\`.`, }, + + /** + * @docs + * @see + * - [Astro.glob](https://docs.astro.build/en/reference/api-reference/#astroglob) + * @description + * `Astro.glob()` can only be used in `.astro` files. You can use [`import.meta.glob()`](https://vitejs.dev/guide/features.html#glob-import) instead to acheive the same result. + */ + AstroGlobUsedOutside: { + title: 'Astro.glob() used outside of an Astro file.', + code: 3035, + message: (globStr: string) => + `\`Astro.glob(${globStr})\` can only be used in \`.astro\` files. \`import.meta.glob(${globStr})\` can be used instead to achieve a similar result.`, + hint: "See Vite's documentation on `import.meta.glob` for more information: https://vitejs.dev/guide/features.html#glob-import", + }, + + /** + * @docs + * @see + * - [Astro.glob](https://docs.astro.build/en/reference/api-reference/#astroglob) + * @description + * `Astro.glob()` did not return any matching files. There might be a typo in the glob pattern. + */ + AstroGlobNoMatch: { + title: 'Astro.glob() did not match any files.', + code: 3036, + message: (globStr: string) => + `\`Astro.glob(${globStr})\` did not return any matching files. Check the pattern for typos.`, + }, + // No headings here, that way Vite errors are merged with Astro ones in the docs, which makes more sense to users. // Vite Errors - 4xxx /** diff --git a/packages/astro/src/runtime/server/astro-global.ts b/packages/astro/src/runtime/server/astro-global.ts index 70c4e565b..da1f0e784 100644 --- a/packages/astro/src/runtime/server/astro-global.ts +++ b/packages/astro/src/runtime/server/astro-global.ts @@ -1,17 +1,22 @@ import type { AstroGlobalPartial } from '../../@types/astro'; import { ASTRO_VERSION } from '../../core/constants.js'; +import { AstroError, AstroErrorData } from '../../core/errors/index.js'; /** Create the Astro.glob() runtime function. */ function createAstroGlobFn() { const globHandler = (importMetaGlobResult: Record, globValue: () => any) => { if (typeof importMetaGlobResult === 'string') { - throw new Error( - 'Astro.glob() does not work outside of an Astro file. Use `import.meta.glob()` instead.' - ); + throw new AstroError({ + ...AstroErrorData.AstroGlobUsedOutside, + message: AstroErrorData.AstroGlobUsedOutside.message(JSON.stringify(importMetaGlobResult)), + }); } let allEntries = [...Object.values(importMetaGlobResult)]; if (allEntries.length === 0) { - throw new Error(`Astro.glob(${JSON.stringify(globValue())}) - no matches found.`); + throw new AstroError({ + ...AstroErrorData.AstroGlobNoMatch, + message: AstroErrorData.AstroGlobNoMatch.message(JSON.stringify(importMetaGlobResult)), + }); } // Map over the `import()` promises, calling to load them. return Promise.all(allEntries.map((fn) => fn())); diff --git a/packages/astro/test/units/runtime/astro-global.test.js b/packages/astro/test/units/runtime/astro-global.test.js index 975651b76..59585de39 100644 --- a/packages/astro/test/units/runtime/astro-global.test.js +++ b/packages/astro/test/units/runtime/astro-global.test.js @@ -6,8 +6,13 @@ describe('astro global', () => { const Astro = createAstro(undefined); expect(() => { Astro.glob('./**/*.md'); - }).to.throw( - 'Astro.glob() does not work outside of an Astro file. Use `import.meta.glob()` instead.' - ); + }).to.throw(/can only be used in/); + }); + + it('Glob should error if has no results', async () => { + const Astro = createAstro(undefined); + expect(() => { + Astro.glob([], () => './**/*.md'); + }).to.throw(/did not return any matching files/); }); }); From 29da199e9a11db7e7767e559d3344276f8b9a17e Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 30 May 2023 15:46:33 +0800 Subject: [PATCH 22/26] Document MDX optimize static logic (#7221) Co-authored-by: Emanuele Stoppa Co-authored-by: Sarah Rainsberger --- packages/integrations/mdx/src/README.md | 107 ++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 packages/integrations/mdx/src/README.md diff --git a/packages/integrations/mdx/src/README.md b/packages/integrations/mdx/src/README.md new file mode 100644 index 000000000..1043ab13c --- /dev/null +++ b/packages/integrations/mdx/src/README.md @@ -0,0 +1,107 @@ +# Internal documentation + +## rehype-optimize-static + +The `rehype-optimize-static` plugin helps optimize the intermediate [`hast`](https://github.com/syntax-tree/hast) when processing MDX, collapsing static subtrees of the `hast` as a `"static string"` in the final JSX output. Here's a "before" and "after" result: + +Before: + +```jsx +function _createMdxContent() { + return ( + <> +

My MDX Content

+
+        
+          console
+          .
+          log
+          (
+          'hello world'
+          )
+        
+      
+ + ); +} +``` + +After: + +```jsx +function _createMdxContent() { + return ( + <> +

My MDX Content

+

+    
+  );
+}
+```
+
+> NOTE: If one of the nodes in `pre` is MDX, the optimization will not be applied to `pre`, but could be applied to the inner MDX node if its children are static.
+
+This results in fewer JSX nodes, less compiled JS output, and less parsed AST, which results in faster Rollup builds and runtime rendering.
+
+To acheive this, we use an algorithm to detect `hast` subtrees that are entirely static (containing no JSX) to be inlined as `set:html` to the root of the subtree.
+
+The next section explains the algorithm, which you can follow along by pairing with the [source code](./rehype-optimize-static.ts). To analyze the `hast`, you can paste the MDX code into https://mdxjs.com/playground.
+
+### How it works
+
+Two variables:
+
+- `allPossibleElements`: A set of subtree roots where we can add a new `set:html` property with its children as value.
+- `elementStack`: The stack of elements (that could be subtree roots) while traversing the `hast` (node ancestors).
+
+Flow:
+
+1. Walk the `hast` tree.
+2. For each `node` we enter, if the `node` is static (`type` is `element` or `mdxJsxFlowElement`), record in `allPossibleElements` and push to `elementStack`.
+    - Q: Why do we record `mdxJsxFlowElement`, it's MDX? 
+ A: Because we're looking for nodes whose children are static. The node itself doesn't need to be static. + - Q: Are we sure this is the subtree root node in `allPossibleElements`?
+ A: No, but we'll clear that up later in step 3. +3. For each `node` we leave, pop from `elementStack`. If the `node`'s parent is in `allPossibleElements`, we also remove the `node` from `allPossibleElements`. + - Q: Why do we check for the node's parent?
+ A: Checking for the node's parent allows us to identify a subtree root. When we enter a subtree like `C -> D -> E`, we leave in reverse: `E -> D -> C`. When we leave `E`, we see that it's parent `D` exists, so we remove `E`. When we leave `D`, we see `C` exists, so we remove `D`. When we leave `C`, we see that its parent doesn't exist, so we keep `C`, a subtree root. +4. _(Returning to the code written for step 2's `node` enter handling)_ We also need to handle the case where we find non-static elements. If found, we remove all the elements in `elementStack` from `allPossibleElements`. This happens before the code in step 2. + - Q: Why?
+ A: Because if the `node` isn't static, that means all its ancestors (`elementStack`) have non-static children. So, the ancestors couldn't be a subtree root to be optimized anymore. + - Q: Why before step 2's `node` enter handling?
+ A: If we find a non-static `node`, the `node` should still be considered in `allPossibleElements` as its children could be static. +5. Walk done. This leaves us with `allPossibleElements` containing only subtree roots that can be optimized. +6. Add the `set:html` property to the `hast` node, and remove its children. +7. 🎉 The rest of the MDX pipeline will do its thing and generate the desired JSX like above. + +### Extra + +#### MDX custom components + +Astro's MDX implementation supports specifying `export const components` in the MDX file to render some HTML elements as Astro components or framework components. `rehype-optimize-static` also needs to parse this JS to recognize some elements as non-static. + +#### Further optimizations + +In [How it works](#how-it-works) step 4, + +> we remove all the elements in `elementStack` from `allPossibleElements` + +We can further optimize this by then also emptying the `elementStack`. This ensures that if we run this same flow for a deeper node in the tree, we don't remove the already-removed nodes from `allPossibleElements`. + +While this breaks the concept of `elementStack`, it doesn't matter as the `elementStack` array pop in the "leave" handler (in step 3) would become a no-op. + +Example `elementStack` value during walking phase: + +``` +Enter: A +Enter: A, B +Enter: A, B, C +(Non-static node found): +Enter: D +Enter: D, E +Leave: D +Leave: +Leave: +Leave: +Leave: +``` From cf621340b00fda441f4ef43196c0363d09eae70c Mon Sep 17 00:00:00 2001 From: wulinsheng123 <409187100@qq.com> Date: Tue, 30 May 2023 20:05:48 +0800 Subject: [PATCH 23/26] Bug 6672 (#7062) * fix miss a head when the templaterender has a promise * fix * add some test * test files move to md directory * fix add * delect file --------- Co-authored-by: wuls --- .changeset/small-wombats-know.md | 5 ++ .../runtime/server/render/astro/instance.ts | 3 ++ .../mdx/test/astro-content-css.test.js | 46 +++++++++++++++++++ .../astro-content-css/astro.config.mjs | 11 +++++ .../fixtures/astro-content-css/package.json | 9 ++++ .../astro-content-css/src/content/config.ts | 12 +++++ .../dynamic/FirstComponentWithJS.astro | 18 ++++++++ .../dynamic/first-component-with-js.mdx | 9 ++++ .../astro-content-css/src/pages/index.astro | 16 +++++++ pnpm-lock.yaml | 9 ++++ 10 files changed, 138 insertions(+) create mode 100644 .changeset/small-wombats-know.md create mode 100644 packages/integrations/mdx/test/astro-content-css.test.js create mode 100644 packages/integrations/mdx/test/fixtures/astro-content-css/astro.config.mjs create mode 100644 packages/integrations/mdx/test/fixtures/astro-content-css/package.json create mode 100644 packages/integrations/mdx/test/fixtures/astro-content-css/src/content/config.ts create mode 100644 packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/FirstComponentWithJS.astro create mode 100644 packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/first-component-with-js.mdx create mode 100644 packages/integrations/mdx/test/fixtures/astro-content-css/src/pages/index.astro diff --git a/.changeset/small-wombats-know.md b/.changeset/small-wombats-know.md new file mode 100644 index 000000000..ca826a1aa --- /dev/null +++ b/.changeset/small-wombats-know.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +fix miss a head when the templaterender has a promise diff --git a/packages/astro/src/runtime/server/render/astro/instance.ts b/packages/astro/src/runtime/server/render/astro/instance.ts index ed5044575..abfcd94e8 100644 --- a/packages/astro/src/runtime/server/render/astro/instance.ts +++ b/packages/astro/src/runtime/server/render/astro/instance.ts @@ -50,6 +50,9 @@ export class AstroComponentInstance { value = await value; } if (isHeadAndContent(value)) { + if (this.result.extraHead.length === 0 && value.head) { + yield renderChild(value.head); + } yield* value.content; } else { yield* renderChild(value); diff --git a/packages/integrations/mdx/test/astro-content-css.test.js b/packages/integrations/mdx/test/astro-content-css.test.js new file mode 100644 index 000000000..7168795cd --- /dev/null +++ b/packages/integrations/mdx/test/astro-content-css.test.js @@ -0,0 +1,46 @@ +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import { loadFixture } from '../../../astro/test/test-utils.js'; +import mdx from '@astrojs/mdx'; + +describe('build css from the component', async () => { + let fixture; + + before(async () => { + fixture = await loadFixture({ root: new URL('./fixtures/astro-content-css/', import.meta.url),integrations: [mdx()], }); + await fixture.build(); + }); + + describe('Build', () => { + before(async () => { + await fixture.build(); + }); + + it('including css and js from the component in pro', async () => { + const html = await fixture.readFile('/index.html'); + const $ = cheerio.load(html); + expect($('link[href$=".css"]').attr('href')).to.match(/^\/_astro\//); + expect($('script[src$=".js"]').attr('src')).to.match(/^\/_astro\//); + }); + }) + + describe('Dev', () => { + let devServer + before(async () => { + devServer = await fixture.startDevServer(); + }); + + after(async () => { + devServer.stop(); + }); + + it('ncluding css and js from the component in Dev', async () => { + let res = await fixture.fetch(`/`); + expect(res.status).to.equal(200); + const html = await res.text(); + const $ = cheerio.load(html); + expect($.html()).to.include('CornflowerBlue'); + expect($('script[src$=".js"]').attr('src')).to.include('astro'); + }); + }) +}) diff --git a/packages/integrations/mdx/test/fixtures/astro-content-css/astro.config.mjs b/packages/integrations/mdx/test/fixtures/astro-content-css/astro.config.mjs new file mode 100644 index 000000000..b67da09a9 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/astro-content-css/astro.config.mjs @@ -0,0 +1,11 @@ +import { defineConfig } from 'astro/config'; + +import mdx from "@astrojs/mdx"; + +// https://astro.build/config +export default defineConfig({ + build: { + format: 'file' + }, + integrations: [mdx()] +}); diff --git a/packages/integrations/mdx/test/fixtures/astro-content-css/package.json b/packages/integrations/mdx/test/fixtures/astro-content-css/package.json new file mode 100644 index 000000000..8d436998c --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/astro-content-css/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/astro-content-css", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*", + "@astrojs/mdx": "workspace:*" + } +} diff --git a/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/config.ts b/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/config.ts new file mode 100644 index 000000000..bf1a34c05 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/config.ts @@ -0,0 +1,12 @@ +// 1. Import utilities from `astro:content` +import { z, defineCollection } from 'astro:content'; +// 2. Define a schema for each collection you'd like to validate. +const dynamicCollection = defineCollection({ + schema: z.object({ + title: z.string(), + }), +}); +// 3. Export a single `collections` object to register your collection(s) +export const collections = { + dynamic: dynamicCollection, +}; diff --git a/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/FirstComponentWithJS.astro b/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/FirstComponentWithJS.astro new file mode 100644 index 000000000..f3b588b42 --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/FirstComponentWithJS.astro @@ -0,0 +1,18 @@ +--- +const { text } = Astro.props; +--- + + + + +
1st components with js. Props: {text}. Styles. JS:
+ + + + \ No newline at end of file diff --git a/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/first-component-with-js.mdx b/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/first-component-with-js.mdx new file mode 100644 index 000000000..0abdfbe3a --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/astro-content-css/src/content/dynamic/first-component-with-js.mdx @@ -0,0 +1,9 @@ +--- +title: 'First component' +--- + +import FirstDynamicComponentWithJS from './FirstComponentWithJS.astro'; + + + +Additional text from mdx 'first-component-with-js' diff --git a/packages/integrations/mdx/test/fixtures/astro-content-css/src/pages/index.astro b/packages/integrations/mdx/test/fixtures/astro-content-css/src/pages/index.astro new file mode 100644 index 000000000..63ea9ddbb --- /dev/null +++ b/packages/integrations/mdx/test/fixtures/astro-content-css/src/pages/index.astro @@ -0,0 +1,16 @@ +--- +import { getCollection } from 'astro:content'; + +const entries = await getCollection('dynamic'); +--- + + + + + + {entries.map(async entry => { + const { Content } = await entry.render(); + return ; + })} + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 635c51a2f..54421c429 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4244,6 +4244,15 @@ importers: specifier: ^4.3.1 version: 4.3.1(@types/node@18.16.3)(sass@1.52.2) + packages/integrations/mdx/test/fixtures/astro-content-css: + dependencies: + '@astrojs/mdx': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/mdx/test/fixtures/css-head-mdx: dependencies: '@astrojs/mdx': From e20a3b20b7f5f743c25533e81e633ad089cb2564 Mon Sep 17 00:00:00 2001 From: matthewp Date: Tue, 30 May 2023 12:08:08 +0000 Subject: [PATCH 24/26] [ci] format --- .../integrations/mdx/test/astro-content-css.test.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/integrations/mdx/test/astro-content-css.test.js b/packages/integrations/mdx/test/astro-content-css.test.js index 7168795cd..712aaf547 100644 --- a/packages/integrations/mdx/test/astro-content-css.test.js +++ b/packages/integrations/mdx/test/astro-content-css.test.js @@ -7,7 +7,10 @@ describe('build css from the component', async () => { let fixture; before(async () => { - fixture = await loadFixture({ root: new URL('./fixtures/astro-content-css/', import.meta.url),integrations: [mdx()], }); + fixture = await loadFixture({ + root: new URL('./fixtures/astro-content-css/', import.meta.url), + integrations: [mdx()], + }); await fixture.build(); }); @@ -22,10 +25,10 @@ describe('build css from the component', async () => { expect($('link[href$=".css"]').attr('href')).to.match(/^\/_astro\//); expect($('script[src$=".js"]').attr('src')).to.match(/^\/_astro\//); }); - }) + }); describe('Dev', () => { - let devServer + let devServer; before(async () => { devServer = await fixture.startDevServer(); }); @@ -42,5 +45,5 @@ describe('build css from the component', async () => { expect($.html()).to.include('CornflowerBlue'); expect($('script[src$=".js"]').attr('src')).to.include('astro'); }); - }) -}) + }); +}); From ee2aca80a71afe843af943b11966fcf77f556cfb Mon Sep 17 00:00:00 2001 From: Happydev <81974850+MoustaphaDev@users.noreply.github.com> Date: Tue, 30 May 2023 14:12:16 +0000 Subject: [PATCH 25/26] fix: prioritize dynamic prerendered routes over dynamic server routes (#7235) * test: add unit tests * fix: prioritize prerendered routes * chore: fix test * add comment * test: try avoiding race condition * chore: changeset * try avoiding race conditions attempt #2 * try avoiding race conditions (attempt 3) * final fix hopefuly * tet: add more tests * sort conflicting dynamic routes aplhabetically * test: fix test --- .changeset/hungry-peaches-speak.md | 5 + packages/astro/src/core/render/dev/index.ts | 3 +- .../astro/src/core/routing/manifest/create.ts | 1 + packages/astro/src/prerender/routing.ts | 67 +++++ .../src/vite-plugin-astro-server/route.ts | 22 +- .../test/units/routing/route-matching.test.js | 282 ++++++++++++++++++ 6 files changed, 361 insertions(+), 19 deletions(-) create mode 100644 .changeset/hungry-peaches-speak.md create mode 100644 packages/astro/src/prerender/routing.ts create mode 100644 packages/astro/test/units/routing/route-matching.test.js diff --git a/.changeset/hungry-peaches-speak.md b/.changeset/hungry-peaches-speak.md new file mode 100644 index 000000000..4467b521d --- /dev/null +++ b/.changeset/hungry-peaches-speak.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Prioritize dynamic prerendered routes over dynamic server routes diff --git a/packages/astro/src/core/render/dev/index.ts b/packages/astro/src/core/render/dev/index.ts index 26e7c85d5..c01137392 100644 --- a/packages/astro/src/core/render/dev/index.ts +++ b/packages/astro/src/core/render/dev/index.ts @@ -1,4 +1,3 @@ -import { fileURLToPath } from 'url'; import type { AstroMiddlewareInstance, AstroSettings, @@ -65,7 +64,7 @@ export async function preload({ try { // Load the module from the Vite SSR Runtime. - const mod = (await env.loader.import(fileURLToPath(filePath))) as ComponentInstance; + const mod = (await env.loader.import(viteID(filePath))) as ComponentInstance; return [renderers, mod]; } catch (error) { diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index a673e199b..a8680a104 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -173,6 +173,7 @@ function comparator(a: Item, b: Item) { } } + // endpoints are prioritized over pages if (a.isPage !== b.isPage) { return a.isPage ? 1 : -1; } diff --git a/packages/astro/src/prerender/routing.ts b/packages/astro/src/prerender/routing.ts new file mode 100644 index 000000000..928789044 --- /dev/null +++ b/packages/astro/src/prerender/routing.ts @@ -0,0 +1,67 @@ +import type { AstroSettings, RouteData } from '../@types/astro'; +import { preload, type DevelopmentEnvironment } from '../core/render/dev/index.js'; +import { getPrerenderStatus } from './metadata.js'; + +type GetSortedPreloadedMatchesParams = { + env: DevelopmentEnvironment; + matches: RouteData[]; + settings: AstroSettings; +}; +export async function getSortedPreloadedMatches({ + env, + matches, + settings, +}: GetSortedPreloadedMatchesParams) { + return ( + await preloadAndSetPrerenderStatus({ + env, + matches, + settings, + }) + ).sort((a, b) => prioritizePrerenderedMatchesComparator(a.route, b.route)); +} + +type PreloadAndSetPrerenderStatusParams = { + env: DevelopmentEnvironment; + matches: RouteData[]; + settings: AstroSettings; +}; +async function preloadAndSetPrerenderStatus({ + env, + matches, + settings, +}: PreloadAndSetPrerenderStatusParams) { + const preloaded = await Promise.all( + matches.map(async (route) => { + const filePath = new URL(`./${route.component}`, settings.config.root); + const preloadedComponent = await preload({ env, filePath }); + + // gets the prerender metadata set by the `astro:scanner` vite plugin + const prerenderStatus = getPrerenderStatus({ + filePath, + loader: env.loader, + }); + + if (prerenderStatus !== undefined) { + route.prerender = prerenderStatus; + } + + return { preloadedComponent, route, filePath } as const; + }) + ); + return preloaded; +} + +function prioritizePrerenderedMatchesComparator(a: RouteData, b: RouteData): number { + if (areRegexesEqual(a.pattern, b.pattern)) { + if (a.prerender !== b.prerender) { + return a.prerender ? -1 : 1; + } + return a.component < b.component ? -1 : 1; + } + return 0; +} + +function areRegexesEqual(regexp1: RegExp, regexp2: RegExp) { + return regexp1.source === regexp2.source && regexp1.global === regexp2.global; +} diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 86b8e5814..56fd60f14 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -16,10 +16,10 @@ import { preload, renderPage } from '../core/render/dev/index.js'; import { getParamsAndProps, GetParamsAndPropsError } from '../core/render/index.js'; import { createRequest } from '../core/request.js'; import { matchAllRoutes } from '../core/routing/index.js'; -import { getPrerenderStatus } from '../prerender/metadata.js'; import { isHybridOutput } from '../prerender/utils.js'; import { log404 } from './common.js'; import { handle404Response, writeSSRResult, writeWebResponse } from './response.js'; +import { getSortedPreloadedMatches } from '../prerender/routing.js'; type AsyncReturnType Promise> = T extends ( ...args: any @@ -47,24 +47,12 @@ export async function matchRoute( ): Promise { const { logging, settings, routeCache } = env; const matches = matchAllRoutes(pathname, manifest); + const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings }); - for await (const maybeRoute of matches) { - const filePath = new URL(`./${maybeRoute.component}`, settings.config.root); - const preloadedComponent = await preload({ env, filePath }); - - // gets the prerender metadata set by the `astro:scanner` vite plugin - const prerenderStatus = getPrerenderStatus({ - filePath, - loader: env.loader, - }); - - if (prerenderStatus !== undefined) { - maybeRoute.prerender = prerenderStatus; - } - - const [, mod] = preloadedComponent; + for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) { // attempt to get static paths // if this fails, we have a bad URL match! + const [, mod] = preloadedComponent; const paramsAndPropsRes = await getParamsAndProps({ mod, route: maybeRoute, @@ -210,7 +198,7 @@ export async function handleRoute( await writeWebResponse(res, result.response); } else { let contentType = 'text/plain'; - // Dynamic routes don’t include `route.pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg') + // Dynamic routes don't include `route.pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg') const filepath = route.pathname || route.segments.map((segment) => segment.map((p) => p.content).join('')).join('/'); diff --git a/packages/astro/test/units/routing/route-matching.test.js b/packages/astro/test/units/routing/route-matching.test.js new file mode 100644 index 000000000..692518382 --- /dev/null +++ b/packages/astro/test/units/routing/route-matching.test.js @@ -0,0 +1,282 @@ +// @ts-check +import { createFs, createRequestAndResponse } from '../test-utils.js'; +import { createRouteManifest, matchAllRoutes } from '../../../dist/core/routing/index.js'; +import { fileURLToPath } from 'url'; +import { defaultLogging } from '../../test-utils.js'; +import { createViteLoader } from '../../../dist/core/module-loader/vite.js'; +import { createDevelopmentEnvironment } from '../../../dist/core/render/dev/environment.js'; +import { expect } from 'chai'; +import { createContainer } from '../../../dist/core/dev/container.js'; +import * as cheerio from 'cheerio'; +import testAdapter from '../../test-adapter.js'; +import { getSortedPreloadedMatches } from '../../../dist/prerender/routing.js'; + +const root = new URL('../../fixtures/alias/', import.meta.url); +const fileSystem = { + '/src/pages/[serverDynamic].astro': ` + --- + export const prerender = false; + --- +

Server dynamic route! slug:{Astro.params.serverDynamic}

+ `, + + '/src/pages/[xStaticDynamic].astro': ` + --- + export function getStaticPaths() { + return [ + { + params: { + xStaticDynamic: "static-dynamic-route-here", + }, + }, + ]; + } + --- +

Prerendered dynamic route!

+ `, + '/src/pages/[aStaticDynamic].astro': ` + --- + export function getStaticPaths() { + return [ + { + params: { + aStaticDynamic: "another-static-dynamic-route-here", + }, + }, + ]; + } + --- +

Another prerendered dynamic route!

+ `, + '/src/pages/[...serverRest].astro': ` + --- + export const prerender = false; + --- +

Server rest route! slug:{Astro.params.serverRest}

+ `, + '/src/pages/[...xStaticRest].astro': ` + --- + export function getStaticPaths() { + return [ + { + params: { + xStaticRest: undefined, + }, + }, + ]; + } + --- +

Prerendered rest route!

+`, + '/src/pages/[...aStaticRest].astro': ` + --- + export function getStaticPaths() { + return [ + { + params: { + aStaticRest: "another/static-rest-route-here", + }, + }, + ]; + } + --- +

Another prerendered rest route!

+`, + + '/src/pages/nested/[...serverRest].astro': ` + --- + export const prerender = false; + --- +

Nested server rest route! slug: {Astro.params.serverRest}

+ `, + '/src/pages/nested/[...xStaticRest].astro': ` + --- + export function getStaticPaths() { + return [ + { + params: { + xStaticRest: undefined, + }, + }, + ]; + } + --- +

Nested prerendered rest route!

+`, + '/src/pages/nested/[...aStaticRest].astro': ` + --- + export function getStaticPaths() { + return [ + { + params: { + aStaticRest: "another-nested-static-dynamic-rest-route-here", + }, + }, + ]; + } + --- +

Another nested prerendered rest route!

+`, +}; + +describe('Route matching', () => { + let env; + let manifest; + let container; + let settings; + + before(async () => { + const fs = createFs(fileSystem, root); + container = await createContainer({ + fs, + root, + userConfig: { + trailingSlash: 'never', + output: 'hybrid', + experimental: { + hybridOutput: true, + }, + adapter: testAdapter(), + }, + disableTelemetry: true, + }); + settings = container.settings; + + const loader = createViteLoader(container.viteServer); + env = createDevelopmentEnvironment(container.settings, defaultLogging, loader); + manifest = createRouteManifest( + { + cwd: fileURLToPath(root), + settings, + fsMod: fs, + }, + defaultLogging + ); + }); + + after(async () => { + await container.close(); + }); + + describe('Matched routes', () => { + it('should be sorted correctly', async () => { + const matches = matchAllRoutes('/try-matching-a-route', manifest); + const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings }); + const sortedRouteNames = preloadedMatches.map((match) => match.route.route); + + expect(sortedRouteNames).to.deep.equal([ + '/[astaticdynamic]', + '/[xstaticdynamic]', + '/[serverdynamic]', + '/[...astaticrest]', + '/[...xstaticrest]', + '/[...serverrest]', + ]); + }); + it('nested should be sorted correctly', async () => { + const matches = matchAllRoutes('/nested/try-matching-a-route', manifest); + const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings }); + const sortedRouteNames = preloadedMatches.map((match) => match.route.route); + + expect(sortedRouteNames).to.deep.equal([ + '/nested/[...astaticrest]', + '/nested/[...xstaticrest]', + '/nested/[...serverrest]', + '/[...astaticrest]', + '/[...xstaticrest]', + '/[...serverrest]', + ]); + }); + }); + + describe('Request', () => { + it('should correctly match a static dynamic route I', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/static-dynamic-route-here', + }); + container.handle(req, res); + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Prerendered dynamic route!'); + }); + + it('should correctly match a static dynamic route II', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/another-static-dynamic-route-here', + }); + container.handle(req, res); + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Another prerendered dynamic route!'); + }); + + it('should correctly match a server dynamic route', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/a-random-slug-was-matched', + }); + container.handle(req, res); + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Server dynamic route! slug:a-random-slug-was-matched'); + }); + + it('should correctly match a static rest route I', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '', + }); + container.handle(req, res); + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Prerendered rest route!'); + }); + + it('should correctly match a static rest route II', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/another/static-rest-route-here', + }); + container.handle(req, res); + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Another prerendered rest route!'); + }); + + it('should correctly match a nested static rest route index', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/nested', + }); + container.handle(req, res); + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Nested prerendered rest route!'); + }); + + it('should correctly match a nested static rest route', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/nested/another-nested-static-dynamic-rest-route-here', + }); + container.handle(req, res); + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Another nested prerendered rest route!'); + }); + + it('should correctly match a nested server rest route', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/nested/a-random-slug-was-matched', + }); + container.handle(req, res); + + const html = await text(); + const $ = cheerio.load(html); + expect($('p').text()).to.equal('Nested server rest route! slug: a-random-slug-was-matched'); + }); + }); +}); From 485178219706edcfe6ae3300c7f8c395bdc8ef71 Mon Sep 17 00:00:00 2001 From: MoustaphaDev Date: Tue, 30 May 2023 14:14:52 +0000 Subject: [PATCH 26/26] [ci] format --- packages/astro/src/vite-plugin-astro-server/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 56fd60f14..dae416229 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -16,10 +16,10 @@ import { preload, renderPage } from '../core/render/dev/index.js'; import { getParamsAndProps, GetParamsAndPropsError } from '../core/render/index.js'; import { createRequest } from '../core/request.js'; import { matchAllRoutes } from '../core/routing/index.js'; +import { getSortedPreloadedMatches } from '../prerender/routing.js'; import { isHybridOutput } from '../prerender/utils.js'; import { log404 } from './common.js'; import { handle404Response, writeSSRResult, writeWebResponse } from './response.js'; -import { getSortedPreloadedMatches } from '../prerender/routing.js'; type AsyncReturnType Promise> = T extends ( ...args: any