summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Ben Holmes <hey@bholmes.dev> 2023-03-24 07:58:56 -0400
committerGravatar GitHub <noreply@github.com> 2023-03-24 07:58:56 -0400
commitcfcf2e2ffdaa68ace5c84329c05b83559a29d638 (patch)
tree9f979bf4ac02ebea69192bd239f9ed41efac3c43
parentdfbd09b711f45da230e75a09b12a186320a632a9 (diff)
downloadastro-cfcf2e2ffdaa68ace5c84329c05b83559a29d638.tar.gz
astro-cfcf2e2ffdaa68ace5c84329c05b83559a29d638.tar.zst
astro-cfcf2e2ffdaa68ace5c84329c05b83559a29d638.zip
[Markdoc] Support automatic image optimization with `experimental.assets` (#6630)
* wip: scrappy implementation. It works! 🥳 * chore: add code comments on inline utils * fix: code cleanup, run on experimental.assets * feat: support ~/assets alias * fix: spoof `astro:assets` when outside experimental * test: image paths in dev and prod * feat: support any vite alias with ctx.resolve * fix: avoid trying to process absolute paths * fix: raise helpful error for invalid vite paths * refactor: revert URL support on emitAsset * chore: lint * refactor: expose emitESMImage from assets base * wip: why doesn't assets exist * scary chore: make @astrojs/markdoc truly depend on astro * fix: import emitESMImage straight from dist * chore: remove type def from assets package * chore: screw it, just ts ignore * deps: rollup types * refactor: optimize images during parse step * chore: remove unneeded `.flat()` * fix: use file-based relative paths * fix: add back helpful error * chore: changeset * deps: move astro back to dev dep * fix: put emit assets behind flag * chore: change to markdoc patch
-rw-r--r--.changeset/big-rice-rest.md13
-rw-r--r--packages/astro/src/@types/astro.ts9
-rw-r--r--packages/astro/src/assets/index.ts1
-rw-r--r--packages/astro/src/assets/utils/emitAsset.ts34
-rw-r--r--packages/astro/src/content/vite-plugin-content-imports.ts2
-rw-r--r--packages/integrations/markdoc/components/TreeNode.ts10
-rw-r--r--packages/integrations/markdoc/package.json6
-rw-r--r--packages/integrations/markdoc/src/index.ts157
-rw-r--r--packages/integrations/markdoc/src/utils.ts9
-rw-r--r--packages/integrations/markdoc/test/fixtures/image-assets/astro.config.mjs10
-rw-r--r--packages/integrations/markdoc/test/fixtures/image-assets/package.json9
-rw-r--r--packages/integrations/markdoc/test/fixtures/image-assets/src/assets/alias/cityscape.jpgbin0 -> 21402 bytes
-rw-r--r--packages/integrations/markdoc/test/fixtures/image-assets/src/assets/relative/oar.jpgbin0 -> 17569 bytes
-rw-r--r--packages/integrations/markdoc/test/fixtures/image-assets/src/content/docs/intro.mdoc7
-rw-r--r--packages/integrations/markdoc/test/fixtures/image-assets/src/pages/index.astro19
-rw-r--r--packages/integrations/markdoc/test/fixtures/image-assets/src/public/favicon.svg9
-rw-r--r--packages/integrations/markdoc/test/image-assets.test.js76
-rw-r--r--pnpm-lock.yaml18
18 files changed, 368 insertions, 21 deletions
diff --git a/.changeset/big-rice-rest.md b/.changeset/big-rice-rest.md
new file mode 100644
index 000000000..2b7efbaa5
--- /dev/null
+++ b/.changeset/big-rice-rest.md
@@ -0,0 +1,13 @@
+---
+'@astrojs/markdoc': patch
+'astro': patch
+---
+
+Support automatic image optimization for Markdoc images when using `experimental.assets`. You can [follow our Assets guide](https://docs.astro.build/en/guides/assets/#enabling-assets-in-your-project) to enable this feature in your project. Then, start using relative or aliased image sources in your Markdoc files for automatic optimization:
+
+```md
+<!--Relative paths-->
+![The Milky Way Galaxy](../assets/galaxy.jpg)
+<!--Or configured aliases-->
+![Houston smiling and looking cute](~/assets/houston-smiling.jpg)
+```
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 0af084217..4eca60b34 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -1053,9 +1053,12 @@ export interface ContentEntryType {
fileUrl: URL;
contents: string;
}): GetEntryInfoReturnType | Promise<GetEntryInfoReturnType>;
- getRenderModule?(params: {
- entry: ContentEntryModule;
- }): rollup.LoadResult | Promise<rollup.LoadResult>;
+ getRenderModule?(
+ this: rollup.PluginContext,
+ params: {
+ entry: ContentEntryModule;
+ }
+ ): rollup.LoadResult | Promise<rollup.LoadResult>;
contentModuleTypes?: string;
}
diff --git a/packages/astro/src/assets/index.ts b/packages/astro/src/assets/index.ts
index f768c58dd..04dde5979 100644
--- a/packages/astro/src/assets/index.ts
+++ b/packages/astro/src/assets/index.ts
@@ -2,3 +2,4 @@ export { getConfiguredImageService, getImage } from './internal.js';
export { baseService } from './services/service.js';
export { type LocalImageProps, type RemoteImageProps } from './types.js';
export { imageMetadata } from './utils/metadata.js';
+export { emitESMImage } from './utils/emitAsset.js';
diff --git a/packages/astro/src/assets/utils/emitAsset.ts b/packages/astro/src/assets/utils/emitAsset.ts
index 74b851eed..d6b34d9aa 100644
--- a/packages/astro/src/assets/utils/emitAsset.ts
+++ b/packages/astro/src/assets/utils/emitAsset.ts
@@ -1,15 +1,15 @@
import fs from 'node:fs';
import path from 'node:path';
-import { pathToFileURL } from 'node:url';
-import type { AstroSettings } from '../../@types/astro';
-import { rootRelativePath } from '../../core/util.js';
+import { fileURLToPath, pathToFileURL } from 'node:url';
+import slash from 'slash';
+import type { AstroSettings, AstroConfig } from '../../@types/astro';
import { imageMetadata } from './metadata.js';
export async function emitESMImage(
id: string,
watchMode: boolean,
fileEmitter: any,
- settings: AstroSettings
+ settings: Pick<AstroSettings, 'config'>
) {
const url = pathToFileURL(id);
const meta = await imageMetadata(url);
@@ -41,3 +41,29 @@ export async function emitESMImage(
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<AstroConfig, 'root'>, url: URL) {
+ const basePath = fileURLToNormalizedPath(url);
+ const rootPath = fileURLToNormalizedPath(config.root);
+ return prependForwardSlash(basePath.slice(rootPath.length));
+}
+
+function prependForwardSlash(filePath: 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) {
+ return process.platform !== 'win32' ? char : fallback;
+}
diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts
index b699ea73e..7ad71b31e 100644
--- a/packages/astro/src/content/vite-plugin-content-imports.ts
+++ b/packages/astro/src/content/vite-plugin-content-imports.ts
@@ -139,7 +139,7 @@ export const _internal = {
});
}
- return contentRenderer({ entry });
+ return contentRenderer.bind(this)({ entry });
},
});
}
diff --git a/packages/integrations/markdoc/components/TreeNode.ts b/packages/integrations/markdoc/components/TreeNode.ts
index b9b4c5c4d..f46355d5c 100644
--- a/packages/integrations/markdoc/components/TreeNode.ts
+++ b/packages/integrations/markdoc/components/TreeNode.ts
@@ -1,6 +1,8 @@
import type { AstroInstance } from 'astro';
import type { RenderableTreeNode } from '@markdoc/markdoc';
import { createComponent, renderComponent, render } from 'astro/runtime/server/index.js';
+// @ts-expect-error Cannot find module 'astro:markdoc-assets' or its corresponding type declarations
+import { Image } from 'astro:markdoc-assets';
import Markdoc from '@markdoc/markdoc';
import { MarkdocError, isCapitalized } from '../dist/utils.js';
@@ -45,10 +47,16 @@ export const ComponentNode = createComponent({
propagation: 'none',
});
+const builtInComponents: Record<string, AstroInstance['default']> = {
+ Image,
+};
+
export function createTreeNode(
node: RenderableTreeNode,
- components: Record<string, AstroInstance['default']> = {}
+ userComponents: Record<string, AstroInstance['default']> = {}
): TreeNode {
+ const components = { ...userComponents, ...builtInComponents };
+
if (typeof node === 'string' || typeof node === 'number') {
return { type: 'text', content: String(node) };
} else if (node === null || typeof node !== 'object' || !Markdoc.Tag.isTag(node)) {
diff --git a/packages/integrations/markdoc/package.json b/packages/integrations/markdoc/package.json
index 12bc6bacd..70c0eea99 100644
--- a/packages/integrations/markdoc/package.json
+++ b/packages/integrations/markdoc/package.json
@@ -35,16 +35,20 @@
"gray-matter": "^4.0.3",
"zod": "^3.17.3"
},
+ "peerDependencies": {
+ "astro": "workspace:*"
+ },
"devDependencies": {
+ "astro": "workspace:*",
"@types/chai": "^4.3.1",
"@types/html-escaper": "^3.0.0",
"@types/mocha": "^9.1.1",
- "astro": "workspace:*",
"astro-scripts": "workspace:*",
"chai": "^4.3.6",
"devalue": "^4.2.0",
"linkedom": "^0.14.12",
"mocha": "^9.2.2",
+ "rollup": "^3.20.1",
"vite": "^4.0.3"
},
"engines": {
diff --git a/packages/integrations/markdoc/src/index.ts b/packages/integrations/markdoc/src/index.ts
index 70d005ee5..1d3556db7 100644
--- a/packages/integrations/markdoc/src/index.ts
+++ b/packages/integrations/markdoc/src/index.ts
@@ -1,9 +1,23 @@
-import type { Config } from '@markdoc/markdoc';
+import type {
+ Config as ReadonlyMarkdocConfig,
+ ConfigType as MarkdocConfig,
+ Node,
+} from '@markdoc/markdoc';
import Markdoc from '@markdoc/markdoc';
import type { AstroConfig, AstroIntegration, ContentEntryType, HookParameters } from 'astro';
import fs from 'node:fs';
+import type * as rollup from 'rollup';
import { fileURLToPath } from 'node:url';
-import { getAstroConfigPath, MarkdocError, parseFrontmatter } from './utils.js';
+import {
+ getAstroConfigPath,
+ isValidUrl,
+ MarkdocError,
+ parseFrontmatter,
+ prependForwardSlash,
+} from './utils.js';
+// @ts-expect-error Cannot find module 'astro/assets' or its corresponding type declarations.
+import { emitESMImage } from 'astro/assets';
+import type { Plugin as VitePlugin } from 'vite';
type SetupHookParams = HookParameters<'astro:config:setup'> & {
// `contentEntryType` is not a public API
@@ -11,12 +25,24 @@ type SetupHookParams = HookParameters<'astro:config:setup'> & {
addContentEntryType: (contentEntryType: ContentEntryType) => void;
};
-export default function markdoc(markdocConfig: Config = {}): AstroIntegration {
+export default function markdocIntegration(
+ userMarkdocConfig: ReadonlyMarkdocConfig = {}
+): AstroIntegration {
return {
name: '@astrojs/markdoc',
hooks: {
'astro:config:setup': async (params) => {
- const { updateConfig, config, addContentEntryType } = params as SetupHookParams;
+ const {
+ updateConfig,
+ config: astroConfig,
+ addContentEntryType,
+ } = params as SetupHookParams;
+
+ updateConfig({
+ vite: {
+ plugins: [safeAssetsVirtualModulePlugin({ astroConfig })],
+ },
+ });
function getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
@@ -30,16 +56,44 @@ export default function markdoc(markdocConfig: Config = {}): AstroIntegration {
addContentEntryType({
extensions: ['.mdoc'],
getEntryInfo,
- getRenderModule({ entry }) {
- validateRenderProperties(markdocConfig, config);
+ async getRenderModule({ entry }) {
+ validateRenderProperties(userMarkdocConfig, astroConfig);
const ast = Markdoc.parse(entry.body);
- const content = Markdoc.transform(ast, {
- ...markdocConfig,
+ const pluginContext = this;
+ const markdocConfig: MarkdocConfig = {
+ ...userMarkdocConfig,
variables: {
- ...markdocConfig.variables,
+ ...userMarkdocConfig.variables,
entry,
},
- });
+ };
+
+ if (astroConfig.experimental?.assets) {
+ await emitOptimizedImages(ast.children, {
+ astroConfig,
+ pluginContext,
+ filePath: entry._internal.filePath,
+ });
+
+ markdocConfig.nodes ??= {};
+ markdocConfig.nodes.image = {
+ ...Markdoc.nodes.image,
+ transform(node, config) {
+ const attributes = node.transformAttributes(config);
+ const children = node.transformChildren(config);
+
+ if (node.type === 'image' && '__optimizedSrc' in node.attributes) {
+ const { __optimizedSrc, ...rest } = node.attributes;
+ return new Markdoc.Tag('Image', { ...rest, src: __optimizedSrc }, children);
+ } else {
+ return new Markdoc.Tag('img', attributes, children);
+ }
+ },
+ };
+ }
+
+ const content = Markdoc.transform(ast, markdocConfig);
+
return {
code: `import { jsx as h } from 'astro/jsx-runtime';\nimport { Renderer } from '@astrojs/markdoc/components';\nconst transformedContent = ${JSON.stringify(
content
@@ -56,7 +110,54 @@ export default function markdoc(markdocConfig: Config = {}): AstroIntegration {
};
}
-function validateRenderProperties(markdocConfig: Config, astroConfig: AstroConfig) {
+/**
+ * Emits optimized images, and appends the generated `src` to each AST node
+ * via the `__optimizedSrc` attribute.
+ */
+async function emitOptimizedImages(
+ nodeChildren: Node[],
+ ctx: {
+ pluginContext: rollup.PluginContext;
+ filePath: string;
+ astroConfig: AstroConfig;
+ }
+) {
+ for (const node of nodeChildren) {
+ if (
+ node.type === 'image' &&
+ typeof node.attributes.src === 'string' &&
+ shouldOptimizeImage(node.attributes.src)
+ ) {
+ // Attempt to resolve source with Vite.
+ // This handles relative paths and configured aliases
+ const resolved = await ctx.pluginContext.resolve(node.attributes.src, ctx.filePath);
+
+ if (resolved?.id && fs.existsSync(new URL(prependForwardSlash(resolved.id), 'file://'))) {
+ const src = await emitESMImage(
+ resolved.id,
+ ctx.pluginContext.meta.watchMode,
+ ctx.pluginContext.emitFile,
+ { config: ctx.astroConfig }
+ );
+ node.attributes.__optimizedSrc = src;
+ } else {
+ throw new MarkdocError({
+ message: `Could not resolve image ${JSON.stringify(
+ node.attributes.src
+ )} from ${JSON.stringify(ctx.filePath)}. Does the file exist?`,
+ });
+ }
+ }
+ await emitOptimizedImages(node.children, ctx);
+ }
+}
+
+function shouldOptimizeImage(src: string) {
+ // Optimize anything that is NOT external or an absolute path to `public/`
+ return !isValidUrl(src) && !src.startsWith('/');
+}
+
+function validateRenderProperties(markdocConfig: ReadonlyMarkdocConfig, astroConfig: AstroConfig) {
const tags = markdocConfig.tags ?? {};
const nodes = markdocConfig.nodes ?? {};
@@ -105,3 +206,37 @@ function validateRenderProperty({
function isCapitalized(str: string) {
return str.length > 0 && str[0] === str[0].toUpperCase();
}
+
+/**
+ * TODO: remove when `experimental.assets` is baselined.
+ *
+ * `astro:assets` will fail to resolve if the `experimental.assets` flag is not enabled.
+ * This ensures a fallback for the Markdoc renderer to safely import at the top level.
+ * @see ../components/TreeNode.ts
+ */
+function safeAssetsVirtualModulePlugin({
+ astroConfig,
+}: {
+ astroConfig: Pick<AstroConfig, 'experimental'>;
+}): VitePlugin {
+ const virtualModuleId = 'astro:markdoc-assets';
+ const resolvedVirtualModuleId = '\0' + virtualModuleId;
+
+ return {
+ name: 'astro:markdoc-safe-assets-virtual-module',
+ resolveId(id) {
+ if (id === virtualModuleId) {
+ return resolvedVirtualModuleId;
+ }
+ },
+ load(id) {
+ if (id !== resolvedVirtualModuleId) return;
+
+ if (astroConfig.experimental?.assets) {
+ return `export { Image } from 'astro:assets';`;
+ } else {
+ return `export const Image = () => { throw new Error('Cannot use the Image component without the \`experimental.assets\` flag.'); }`;
+ }
+ },
+ };
+}
diff --git a/packages/integrations/markdoc/src/utils.ts b/packages/integrations/markdoc/src/utils.ts
index 275c711f0..9d6e5af26 100644
--- a/packages/integrations/markdoc/src/utils.ts
+++ b/packages/integrations/markdoc/src/utils.ts
@@ -145,3 +145,12 @@ const componentsPropValidator = z.record(
export function isCapitalized(str: string) {
return str.length > 0 && str[0] === str[0].toUpperCase();
}
+
+export function isValidUrl(str: string): boolean {
+ try {
+ new URL(str);
+ return true;
+ } catch {
+ return false;
+ }
+}
diff --git a/packages/integrations/markdoc/test/fixtures/image-assets/astro.config.mjs b/packages/integrations/markdoc/test/fixtures/image-assets/astro.config.mjs
new file mode 100644
index 000000000..9e64af363
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/image-assets/astro.config.mjs
@@ -0,0 +1,10 @@
+import { defineConfig } from 'astro/config';
+import markdoc from '@astrojs/markdoc';
+
+// https://astro.build/config
+export default defineConfig({
+ experimental: {
+ assets: true,
+ },
+ integrations: [markdoc()],
+});
diff --git a/packages/integrations/markdoc/test/fixtures/image-assets/package.json b/packages/integrations/markdoc/test/fixtures/image-assets/package.json
new file mode 100644
index 000000000..30df52c2f
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/image-assets/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/image-assets",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/markdoc": "workspace:*",
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/integrations/markdoc/test/fixtures/image-assets/src/assets/alias/cityscape.jpg b/packages/integrations/markdoc/test/fixtures/image-assets/src/assets/alias/cityscape.jpg
new file mode 100644
index 000000000..6130e2c14
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/image-assets/src/assets/alias/cityscape.jpg
Binary files differ
diff --git a/packages/integrations/markdoc/test/fixtures/image-assets/src/assets/relative/oar.jpg b/packages/integrations/markdoc/test/fixtures/image-assets/src/assets/relative/oar.jpg
new file mode 100644
index 000000000..e15f2e038
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/image-assets/src/assets/relative/oar.jpg
Binary files differ
diff --git a/packages/integrations/markdoc/test/fixtures/image-assets/src/content/docs/intro.mdoc b/packages/integrations/markdoc/test/fixtures/image-assets/src/content/docs/intro.mdoc
new file mode 100644
index 000000000..ae5fced49
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/image-assets/src/content/docs/intro.mdoc
@@ -0,0 +1,7 @@
+# Image assets
+
+![Favicon](/favicon.svg) {% #public %}
+
+![Oar](../../assets/relative/oar.jpg) {% #relative %}
+
+![Gray cityscape arial view](~/assets/alias/cityscape.jpg) {% #alias %}
diff --git a/packages/integrations/markdoc/test/fixtures/image-assets/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/image-assets/src/pages/index.astro
new file mode 100644
index 000000000..51810b4a8
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/image-assets/src/pages/index.astro
@@ -0,0 +1,19 @@
+---
+import { getEntryBySlug } from 'astro:content';
+
+const intro = await getEntryBySlug('docs', 'intro');
+const { Content } = await intro.render();
+---
+
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+ <meta name="viewport" content="width=device-width" />
+ <meta name="generator" content={Astro.generator} />
+ <title>Astro</title>
+ </head>
+ <body>
+ <Content />
+ </body>
+</html>
diff --git a/packages/integrations/markdoc/test/fixtures/image-assets/src/public/favicon.svg b/packages/integrations/markdoc/test/fixtures/image-assets/src/public/favicon.svg
new file mode 100644
index 000000000..f157bd1c5
--- /dev/null
+++ b/packages/integrations/markdoc/test/fixtures/image-assets/src/public/favicon.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
+ <path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
+ <style>
+ path { fill: #000; }
+ @media (prefers-color-scheme: dark) {
+ path { fill: #FFF; }
+ }
+ </style>
+</svg>
diff --git a/packages/integrations/markdoc/test/image-assets.test.js b/packages/integrations/markdoc/test/image-assets.test.js
new file mode 100644
index 000000000..313977934
--- /dev/null
+++ b/packages/integrations/markdoc/test/image-assets.test.js
@@ -0,0 +1,76 @@
+import { parseHTML } from 'linkedom';
+import { expect } from 'chai';
+import { loadFixture } from '../../../astro/test/test-utils.js';
+
+const root = new URL('./fixtures/image-assets/', import.meta.url);
+
+describe('Markdoc - Image assets', () => {
+ let baseFixture;
+
+ before(async () => {
+ baseFixture = await loadFixture({
+ root,
+ });
+ });
+
+ describe('dev', () => {
+ let devServer;
+
+ before(async () => {
+ devServer = await baseFixture.startDevServer();
+ });
+
+ after(async () => {
+ await devServer.stop();
+ });
+
+ it('uses public/ image paths unchanged', async () => {
+ const res = await baseFixture.fetch('/');
+ const html = await res.text();
+ const { document } = parseHTML(html);
+ expect(document.querySelector('#public > img')?.src).to.equal('/favicon.svg');
+ });
+
+ it('transforms relative image paths to optimized path', async () => {
+ 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'
+ );
+ });
+
+ it('transforms aliased image paths to optimized path', async () => {
+ 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'
+ );
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await baseFixture.build();
+ });
+
+ it('uses public/ image paths unchanged', async () => {
+ const html = await baseFixture.readFile('/index.html');
+ const { document } = parseHTML(html);
+ expect(document.querySelector('#public > img')?.src).to.equal('/favicon.svg');
+ });
+
+ it('transforms relative image paths to optimized path', async () => {
+ const html = await baseFixture.readFile('/index.html');
+ const { document } = parseHTML(html);
+ expect(document.querySelector('#relative > img')?.src).to.match(/^\/_astro\/oar.*\.webp$/);
+ });
+
+ it('transforms aliased image paths to optimized path', async () => {
+ const html = await baseFixture.readFile('/index.html');
+ const { document } = parseHTML(html);
+ expect(document.querySelector('#alias > img')?.src).to.match(/^\/_astro\/cityscape.*\.webp$/);
+ });
+ });
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 37491d8c7..441fe16a7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -3080,6 +3080,7 @@ importers:
gray-matter: ^4.0.3
linkedom: ^0.14.12
mocha: ^9.2.2
+ rollup: ^3.20.1
vite: ^4.0.3
zod: ^3.17.3
dependencies:
@@ -3096,6 +3097,7 @@ importers:
devalue: 4.2.3
linkedom: 0.14.21
mocha: 9.2.2
+ rollup: 3.20.1
vite: 4.1.2
packages/integrations/markdoc/test/fixtures/content-collections:
@@ -3119,6 +3121,14 @@ importers:
'@astrojs/markdoc': link:../../..
astro: link:../../../../../astro
+ packages/integrations/markdoc/test/fixtures/image-assets:
+ specifiers:
+ '@astrojs/markdoc': workspace:*
+ astro: workspace:*
+ dependencies:
+ '@astrojs/markdoc': link:../../..
+ astro: link:../../../../../astro
+
packages/integrations/mdx:
specifiers:
'@astrojs/markdown-remark': ^2.1.2
@@ -14921,6 +14931,14 @@ packages:
optionalDependencies:
fsevents: 2.3.2
+ /rollup/3.20.1:
+ resolution: {integrity: sha512-sz2w8cBJlWQ2E17RcpvHuf4sk2BQx4tfKDnjNPikEpLEevrbIAR7CH3PGa2hpPwWbNgPaA9yh9Jzljds5bc9zg==}
+ engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+ hasBin: true
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
/run-parallel/1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies: