@astrojs/markdoc 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- @astrojs/markdoc:build: cache hit, replaying output a6e7d1873a50f12c
2
- @astrojs/markdoc:build: 
3
- @astrojs/markdoc:build: > @astrojs/markdoc@0.0.4 build /home/runner/work/astro/astro/packages/integrations/markdoc
4
- @astrojs/markdoc:build: > astro-scripts build "src/**/*.ts" && tsc
5
- @astrojs/markdoc:build: 
1
+ @astrojs/markdoc:build: cache hit, replaying output 0cf4b43fcaef6989
2
+ @astrojs/markdoc:build: 
3
+ @astrojs/markdoc:build: > @astrojs/markdoc@0.0.5 build /home/runner/work/astro/astro/packages/integrations/markdoc
4
+ @astrojs/markdoc:build: > astro-scripts build "src/**/*.ts" && tsc
5
+ @astrojs/markdoc:build: 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @astrojs/markdoc
2
2
 
3
+ ## 0.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#6630](https://github.com/withastro/astro/pull/6630) [`cfcf2e2ff`](https://github.com/withastro/astro/commit/cfcf2e2ffdaa68ace5c84329c05b83559a29d638) Thanks [@bholmesdev](https://github.com/bholmesdev)! - 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:
8
+
9
+ ```md
10
+ <!--Relative paths-->
11
+
12
+ ![The Milky Way Galaxy](../assets/galaxy.jpg)
13
+
14
+ <!--Or configured aliases-->
15
+
16
+ ![Houston smiling and looking cute](~/assets/houston-smiling.jpg)
17
+ ```
18
+
19
+ - Updated dependencies [[`b7194103e`](https://github.com/withastro/astro/commit/b7194103e39267bf59dcd6ba00f522e424219d16), [`cfcf2e2ff`](https://github.com/withastro/astro/commit/cfcf2e2ffdaa68ace5c84329c05b83559a29d638), [`45da39a86`](https://github.com/withastro/astro/commit/45da39a8642d64eb318840b18dfc2b5ccc6561bc), [`7daef9a29`](https://github.com/withastro/astro/commit/7daef9a2993b5d457f3d243a1ebfd1dd383b3327)]:
20
+ - astro@2.1.7
21
+
3
22
  ## 0.0.4
4
23
 
5
24
  ### Patch Changes
@@ -1,6 +1,8 @@
1
1
  import type { AstroInstance } from 'astro';
2
2
  import type { RenderableTreeNode } from '@markdoc/markdoc';
3
3
  import { createComponent, renderComponent, render } from 'astro/runtime/server/index.js';
4
+ // @ts-expect-error Cannot find module 'astro:markdoc-assets' or its corresponding type declarations
5
+ import { Image } from 'astro:markdoc-assets';
4
6
  import Markdoc from '@markdoc/markdoc';
5
7
  import { MarkdocError, isCapitalized } from '../dist/utils.js';
6
8
 
@@ -45,10 +47,16 @@ export const ComponentNode = createComponent({
45
47
  propagation: 'none',
46
48
  });
47
49
 
50
+ const builtInComponents: Record<string, AstroInstance['default']> = {
51
+ Image,
52
+ };
53
+
48
54
  export function createTreeNode(
49
55
  node: RenderableTreeNode,
50
- components: Record<string, AstroInstance['default']> = {}
56
+ userComponents: Record<string, AstroInstance['default']> = {}
51
57
  ): TreeNode {
58
+ const components = { ...userComponents, ...builtInComponents };
59
+
52
60
  if (typeof node === 'string' || typeof node === 'number') {
53
61
  return { type: 'text', content: String(node) };
54
62
  } else if (node === null || typeof node !== 'object' || !Markdoc.Tag.isTag(node)) {
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import type { Config } from '@markdoc/markdoc';
1
+ import type { Config as ReadonlyMarkdocConfig } from '@markdoc/markdoc';
2
2
  import type { AstroIntegration } from 'astro';
3
- export default function markdoc(markdocConfig?: Config): AstroIntegration;
3
+ export default function markdocIntegration(userMarkdocConfig?: ReadonlyMarkdocConfig): AstroIntegration;
package/dist/index.js CHANGED
@@ -1,13 +1,29 @@
1
1
  import Markdoc from "@markdoc/markdoc";
2
2
  import fs from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
- import { getAstroConfigPath, MarkdocError, parseFrontmatter } from "./utils.js";
5
- function markdoc(markdocConfig = {}) {
4
+ import {
5
+ getAstroConfigPath,
6
+ isValidUrl,
7
+ MarkdocError,
8
+ parseFrontmatter,
9
+ prependForwardSlash
10
+ } from "./utils.js";
11
+ import { emitESMImage } from "astro/assets";
12
+ function markdocIntegration(userMarkdocConfig = {}) {
6
13
  return {
7
14
  name: "@astrojs/markdoc",
8
15
  hooks: {
9
16
  "astro:config:setup": async (params) => {
10
- const { updateConfig, config, addContentEntryType } = params;
17
+ const {
18
+ updateConfig,
19
+ config: astroConfig,
20
+ addContentEntryType
21
+ } = params;
22
+ updateConfig({
23
+ vite: {
24
+ plugins: [safeAssetsVirtualModulePlugin({ astroConfig })]
25
+ }
26
+ });
11
27
  function getEntryInfo({ fileUrl, contents }) {
12
28
  const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
13
29
  return {
@@ -20,16 +36,40 @@ function markdoc(markdocConfig = {}) {
20
36
  addContentEntryType({
21
37
  extensions: [".mdoc"],
22
38
  getEntryInfo,
23
- getRenderModule({ entry }) {
24
- validateRenderProperties(markdocConfig, config);
39
+ async getRenderModule({ entry }) {
40
+ var _a;
41
+ validateRenderProperties(userMarkdocConfig, astroConfig);
25
42
  const ast = Markdoc.parse(entry.body);
26
- const content = Markdoc.transform(ast, {
27
- ...markdocConfig,
43
+ const pluginContext = this;
44
+ const markdocConfig = {
45
+ ...userMarkdocConfig,
28
46
  variables: {
29
- ...markdocConfig.variables,
47
+ ...userMarkdocConfig.variables,
30
48
  entry
31
49
  }
32
- });
50
+ };
51
+ if ((_a = astroConfig.experimental) == null ? void 0 : _a.assets) {
52
+ await emitOptimizedImages(ast.children, {
53
+ astroConfig,
54
+ pluginContext,
55
+ filePath: entry._internal.filePath
56
+ });
57
+ markdocConfig.nodes ??= {};
58
+ markdocConfig.nodes.image = {
59
+ ...Markdoc.nodes.image,
60
+ transform(node, config) {
61
+ const attributes = node.transformAttributes(config);
62
+ const children = node.transformChildren(config);
63
+ if (node.type === "image" && "__optimizedSrc" in node.attributes) {
64
+ const { __optimizedSrc, ...rest } = node.attributes;
65
+ return new Markdoc.Tag("Image", { ...rest, src: __optimizedSrc }, children);
66
+ } else {
67
+ return new Markdoc.Tag("img", attributes, children);
68
+ }
69
+ }
70
+ };
71
+ }
72
+ const content = Markdoc.transform(ast, markdocConfig);
33
73
  return {
34
74
  code: `import { jsx as h } from 'astro/jsx-runtime';
35
75
  import { Renderer } from '@astrojs/markdoc/components';
@@ -49,6 +89,32 @@ Content[Symbol.for('astro.needsHeadRendering')] = true;`
49
89
  }
50
90
  };
51
91
  }
92
+ async function emitOptimizedImages(nodeChildren, ctx) {
93
+ for (const node of nodeChildren) {
94
+ if (node.type === "image" && typeof node.attributes.src === "string" && shouldOptimizeImage(node.attributes.src)) {
95
+ const resolved = await ctx.pluginContext.resolve(node.attributes.src, ctx.filePath);
96
+ if ((resolved == null ? void 0 : resolved.id) && fs.existsSync(new URL(prependForwardSlash(resolved.id), "file://"))) {
97
+ const src = await emitESMImage(
98
+ resolved.id,
99
+ ctx.pluginContext.meta.watchMode,
100
+ ctx.pluginContext.emitFile,
101
+ { config: ctx.astroConfig }
102
+ );
103
+ node.attributes.__optimizedSrc = src;
104
+ } else {
105
+ throw new MarkdocError({
106
+ message: `Could not resolve image ${JSON.stringify(
107
+ node.attributes.src
108
+ )} from ${JSON.stringify(ctx.filePath)}. Does the file exist?`
109
+ });
110
+ }
111
+ }
112
+ await emitOptimizedImages(node.children, ctx);
113
+ }
114
+ }
115
+ function shouldOptimizeImage(src) {
116
+ return !isValidUrl(src) && !src.startsWith("/");
117
+ }
52
118
  function validateRenderProperties(markdocConfig, astroConfig) {
53
119
  const tags = markdocConfig.tags ?? {};
54
120
  const nodes = markdocConfig.nodes ?? {};
@@ -88,6 +154,30 @@ function validateRenderProperty({
88
154
  function isCapitalized(str) {
89
155
  return str.length > 0 && str[0] === str[0].toUpperCase();
90
156
  }
157
+ function safeAssetsVirtualModulePlugin({
158
+ astroConfig
159
+ }) {
160
+ const virtualModuleId = "astro:markdoc-assets";
161
+ const resolvedVirtualModuleId = "\0" + virtualModuleId;
162
+ return {
163
+ name: "astro:markdoc-safe-assets-virtual-module",
164
+ resolveId(id) {
165
+ if (id === virtualModuleId) {
166
+ return resolvedVirtualModuleId;
167
+ }
168
+ },
169
+ load(id) {
170
+ var _a;
171
+ if (id !== resolvedVirtualModuleId)
172
+ return;
173
+ if ((_a = astroConfig.experimental) == null ? void 0 : _a.assets) {
174
+ return `export { Image } from 'astro:assets';`;
175
+ } else {
176
+ return `export const Image = () => { throw new Error('Cannot use the Image component without the \`experimental.assets\` flag.'); }`;
177
+ }
178
+ }
179
+ };
180
+ }
91
181
  export {
92
- markdoc as default
182
+ markdocIntegration as default
93
183
  };
package/dist/utils.d.ts CHANGED
@@ -47,4 +47,5 @@ export declare function getAstroConfigPath(fs: typeof fsMod, root: string): stri
47
47
  export declare function prependForwardSlash(str: string): string;
48
48
  export declare function validateComponentsProp(components: Record<string, AstroInstance['default']>): void;
49
49
  export declare function isCapitalized(str: string): boolean;
50
+ export declare function isValidUrl(str: string): boolean;
50
51
  export {};
package/dist/utils.js CHANGED
@@ -84,10 +84,19 @@ const componentsPropValidator = z.record(
84
84
  function isCapitalized(str) {
85
85
  return str.length > 0 && str[0] === str[0].toUpperCase();
86
86
  }
87
+ function isValidUrl(str) {
88
+ try {
89
+ new URL(str);
90
+ return true;
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
87
95
  export {
88
96
  MarkdocError,
89
97
  getAstroConfigPath,
90
98
  isCapitalized,
99
+ isValidUrl,
91
100
  parseFrontmatter,
92
101
  prependForwardSlash,
93
102
  validateComponentsProp
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astrojs/markdoc",
3
3
  "description": "Add support for Markdoc pages in your Astro site",
4
- "version": "0.0.4",
4
+ "version": "0.0.5",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "withastro",
@@ -28,16 +28,20 @@
28
28
  "gray-matter": "^4.0.3",
29
29
  "zod": "^3.17.3"
30
30
  },
31
+ "peerDependencies": {
32
+ "astro": "2.1.7"
33
+ },
31
34
  "devDependencies": {
35
+ "astro": "2.1.7",
32
36
  "@types/chai": "^4.3.1",
33
37
  "@types/html-escaper": "^3.0.0",
34
38
  "@types/mocha": "^9.1.1",
35
- "astro": "2.1.5",
36
39
  "astro-scripts": "0.0.14",
37
40
  "chai": "^4.3.6",
38
41
  "devalue": "^4.2.0",
39
42
  "linkedom": "^0.14.12",
40
43
  "mocha": "^9.2.2",
44
+ "rollup": "^3.20.1",
41
45
  "vite": "^4.0.3"
42
46
  },
43
47
  "engines": {
package/src/index.ts CHANGED
@@ -1,9 +1,23 @@
1
- import type { Config } from '@markdoc/markdoc';
1
+ import type {
2
+ Config as ReadonlyMarkdocConfig,
3
+ ConfigType as MarkdocConfig,
4
+ Node,
5
+ } from '@markdoc/markdoc';
2
6
  import Markdoc from '@markdoc/markdoc';
3
7
  import type { AstroConfig, AstroIntegration, ContentEntryType, HookParameters } from 'astro';
4
8
  import fs from 'node:fs';
5
9
  import { fileURLToPath } from 'node:url';
6
- import { getAstroConfigPath, MarkdocError, parseFrontmatter } from './utils.js';
10
+ import type * as rollup from 'rollup';
11
+ import {
12
+ getAstroConfigPath,
13
+ isValidUrl,
14
+ MarkdocError,
15
+ parseFrontmatter,
16
+ prependForwardSlash,
17
+ } from './utils.js';
18
+ // @ts-expect-error Cannot find module 'astro/assets' or its corresponding type declarations.
19
+ import { emitESMImage } from 'astro/assets';
20
+ import type { Plugin as VitePlugin } from 'vite';
7
21
 
8
22
  type SetupHookParams = HookParameters<'astro:config:setup'> & {
9
23
  // `contentEntryType` is not a public API
@@ -11,12 +25,24 @@ type SetupHookParams = HookParameters<'astro:config:setup'> & {
11
25
  addContentEntryType: (contentEntryType: ContentEntryType) => void;
12
26
  };
13
27
 
14
- export default function markdoc(markdocConfig: Config = {}): AstroIntegration {
28
+ export default function markdocIntegration(
29
+ userMarkdocConfig: ReadonlyMarkdocConfig = {}
30
+ ): AstroIntegration {
15
31
  return {
16
32
  name: '@astrojs/markdoc',
17
33
  hooks: {
18
34
  'astro:config:setup': async (params) => {
19
- const { updateConfig, config, addContentEntryType } = params as SetupHookParams;
35
+ const {
36
+ updateConfig,
37
+ config: astroConfig,
38
+ addContentEntryType,
39
+ } = params as SetupHookParams;
40
+
41
+ updateConfig({
42
+ vite: {
43
+ plugins: [safeAssetsVirtualModulePlugin({ astroConfig })],
44
+ },
45
+ });
20
46
 
21
47
  function getEntryInfo({ fileUrl, contents }: { fileUrl: URL; contents: string }) {
22
48
  const parsed = parseFrontmatter(contents, fileURLToPath(fileUrl));
@@ -30,16 +56,44 @@ export default function markdoc(markdocConfig: Config = {}): AstroIntegration {
30
56
  addContentEntryType({
31
57
  extensions: ['.mdoc'],
32
58
  getEntryInfo,
33
- getRenderModule({ entry }) {
34
- validateRenderProperties(markdocConfig, config);
59
+ async getRenderModule({ entry }) {
60
+ validateRenderProperties(userMarkdocConfig, astroConfig);
35
61
  const ast = Markdoc.parse(entry.body);
36
- const content = Markdoc.transform(ast, {
37
- ...markdocConfig,
62
+ const pluginContext = this;
63
+ const markdocConfig: MarkdocConfig = {
64
+ ...userMarkdocConfig,
38
65
  variables: {
39
- ...markdocConfig.variables,
66
+ ...userMarkdocConfig.variables,
40
67
  entry,
41
68
  },
42
- });
69
+ };
70
+
71
+ if (astroConfig.experimental?.assets) {
72
+ await emitOptimizedImages(ast.children, {
73
+ astroConfig,
74
+ pluginContext,
75
+ filePath: entry._internal.filePath,
76
+ });
77
+
78
+ markdocConfig.nodes ??= {};
79
+ markdocConfig.nodes.image = {
80
+ ...Markdoc.nodes.image,
81
+ transform(node, config) {
82
+ const attributes = node.transformAttributes(config);
83
+ const children = node.transformChildren(config);
84
+
85
+ if (node.type === 'image' && '__optimizedSrc' in node.attributes) {
86
+ const { __optimizedSrc, ...rest } = node.attributes;
87
+ return new Markdoc.Tag('Image', { ...rest, src: __optimizedSrc }, children);
88
+ } else {
89
+ return new Markdoc.Tag('img', attributes, children);
90
+ }
91
+ },
92
+ };
93
+ }
94
+
95
+ const content = Markdoc.transform(ast, markdocConfig);
96
+
43
97
  return {
44
98
  code: `import { jsx as h } from 'astro/jsx-runtime';\nimport { Renderer } from '@astrojs/markdoc/components';\nconst transformedContent = ${JSON.stringify(
45
99
  content
@@ -56,7 +110,54 @@ export default function markdoc(markdocConfig: Config = {}): AstroIntegration {
56
110
  };
57
111
  }
58
112
 
59
- function validateRenderProperties(markdocConfig: Config, astroConfig: AstroConfig) {
113
+ /**
114
+ * Emits optimized images, and appends the generated `src` to each AST node
115
+ * via the `__optimizedSrc` attribute.
116
+ */
117
+ async function emitOptimizedImages(
118
+ nodeChildren: Node[],
119
+ ctx: {
120
+ pluginContext: rollup.PluginContext;
121
+ filePath: string;
122
+ astroConfig: AstroConfig;
123
+ }
124
+ ) {
125
+ for (const node of nodeChildren) {
126
+ if (
127
+ node.type === 'image' &&
128
+ typeof node.attributes.src === 'string' &&
129
+ shouldOptimizeImage(node.attributes.src)
130
+ ) {
131
+ // Attempt to resolve source with Vite.
132
+ // This handles relative paths and configured aliases
133
+ const resolved = await ctx.pluginContext.resolve(node.attributes.src, ctx.filePath);
134
+
135
+ if (resolved?.id && fs.existsSync(new URL(prependForwardSlash(resolved.id), 'file://'))) {
136
+ const src = await emitESMImage(
137
+ resolved.id,
138
+ ctx.pluginContext.meta.watchMode,
139
+ ctx.pluginContext.emitFile,
140
+ { config: ctx.astroConfig }
141
+ );
142
+ node.attributes.__optimizedSrc = src;
143
+ } else {
144
+ throw new MarkdocError({
145
+ message: `Could not resolve image ${JSON.stringify(
146
+ node.attributes.src
147
+ )} from ${JSON.stringify(ctx.filePath)}. Does the file exist?`,
148
+ });
149
+ }
150
+ }
151
+ await emitOptimizedImages(node.children, ctx);
152
+ }
153
+ }
154
+
155
+ function shouldOptimizeImage(src: string) {
156
+ // Optimize anything that is NOT external or an absolute path to `public/`
157
+ return !isValidUrl(src) && !src.startsWith('/');
158
+ }
159
+
160
+ function validateRenderProperties(markdocConfig: ReadonlyMarkdocConfig, astroConfig: AstroConfig) {
60
161
  const tags = markdocConfig.tags ?? {};
61
162
  const nodes = markdocConfig.nodes ?? {};
62
163
 
@@ -105,3 +206,37 @@ function validateRenderProperty({
105
206
  function isCapitalized(str: string) {
106
207
  return str.length > 0 && str[0] === str[0].toUpperCase();
107
208
  }
209
+
210
+ /**
211
+ * TODO: remove when `experimental.assets` is baselined.
212
+ *
213
+ * `astro:assets` will fail to resolve if the `experimental.assets` flag is not enabled.
214
+ * This ensures a fallback for the Markdoc renderer to safely import at the top level.
215
+ * @see ../components/TreeNode.ts
216
+ */
217
+ function safeAssetsVirtualModulePlugin({
218
+ astroConfig,
219
+ }: {
220
+ astroConfig: Pick<AstroConfig, 'experimental'>;
221
+ }): VitePlugin {
222
+ const virtualModuleId = 'astro:markdoc-assets';
223
+ const resolvedVirtualModuleId = '\0' + virtualModuleId;
224
+
225
+ return {
226
+ name: 'astro:markdoc-safe-assets-virtual-module',
227
+ resolveId(id) {
228
+ if (id === virtualModuleId) {
229
+ return resolvedVirtualModuleId;
230
+ }
231
+ },
232
+ load(id) {
233
+ if (id !== resolvedVirtualModuleId) return;
234
+
235
+ if (astroConfig.experimental?.assets) {
236
+ return `export { Image } from 'astro:assets';`;
237
+ } else {
238
+ return `export const Image = () => { throw new Error('Cannot use the Image component without the \`experimental.assets\` flag.'); }`;
239
+ }
240
+ },
241
+ };
242
+ }
package/src/utils.ts CHANGED
@@ -145,3 +145,12 @@ const componentsPropValidator = z.record(
145
145
  export function isCapitalized(str: string) {
146
146
  return str.length > 0 && str[0] === str[0].toUpperCase();
147
147
  }
148
+
149
+ export function isValidUrl(str: string): boolean {
150
+ try {
151
+ new URL(str);
152
+ return true;
153
+ } catch {
154
+ return false;
155
+ }
156
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'astro/config';
2
+ import markdoc from '@astrojs/markdoc';
3
+
4
+ // https://astro.build/config
5
+ export default defineConfig({
6
+ experimental: {
7
+ assets: true,
8
+ },
9
+ integrations: [markdoc()],
10
+ });
@@ -0,0 +1,17 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
6
+ esac
7
+
8
+ if [ -z "$NODE_PATH" ]; then
9
+ export NODE_PATH="/home/runner/work/astro/astro/node_modules/.pnpm/node_modules"
10
+ else
11
+ export NODE_PATH="$NODE_PATH:/home/runner/work/astro/astro/node_modules/.pnpm/node_modules"
12
+ fi
13
+ if [ -x "$basedir/node" ]; then
14
+ exec "$basedir/node" "$basedir/../../../../../../../astro/astro.js" "$@"
15
+ else
16
+ exec node "$basedir/../../../../../../../astro/astro.js" "$@"
17
+ fi
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@test/image-assets",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "dependencies": {
6
+ "@astrojs/markdoc": "workspace:*",
7
+ "astro": "workspace:*"
8
+ }
9
+ }
@@ -0,0 +1,7 @@
1
+ # Image assets
2
+
3
+ ![Favicon](/favicon.svg) {% #public %}
4
+
5
+ ![Oar](../../assets/relative/oar.jpg) {% #relative %}
6
+
7
+ ![Gray cityscape arial view](~/assets/alias/cityscape.jpg) {% #alias %}
@@ -0,0 +1,19 @@
1
+ ---
2
+ import { getEntryBySlug } from 'astro:content';
3
+
4
+ const intro = await getEntryBySlug('docs', 'intro');
5
+ const { Content } = await intro.render();
6
+ ---
7
+
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="utf-8" />
11
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
12
+ <meta name="viewport" content="width=device-width" />
13
+ <meta name="generator" content={Astro.generator} />
14
+ <title>Astro</title>
15
+ </head>
16
+ <body>
17
+ <Content />
18
+ </body>
19
+ </html>
@@ -0,0 +1,9 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
2
+ <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" />
3
+ <style>
4
+ path { fill: #000; }
5
+ @media (prefers-color-scheme: dark) {
6
+ path { fill: #FFF; }
7
+ }
8
+ </style>
9
+ </svg>
@@ -0,0 +1,76 @@
1
+ import { parseHTML } from 'linkedom';
2
+ import { expect } from 'chai';
3
+ import { loadFixture } from '../../../astro/test/test-utils.js';
4
+
5
+ const root = new URL('./fixtures/image-assets/', import.meta.url);
6
+
7
+ describe('Markdoc - Image assets', () => {
8
+ let baseFixture;
9
+
10
+ before(async () => {
11
+ baseFixture = await loadFixture({
12
+ root,
13
+ });
14
+ });
15
+
16
+ describe('dev', () => {
17
+ let devServer;
18
+
19
+ before(async () => {
20
+ devServer = await baseFixture.startDevServer();
21
+ });
22
+
23
+ after(async () => {
24
+ await devServer.stop();
25
+ });
26
+
27
+ it('uses public/ image paths unchanged', async () => {
28
+ const res = await baseFixture.fetch('/');
29
+ const html = await res.text();
30
+ const { document } = parseHTML(html);
31
+ expect(document.querySelector('#public > img')?.src).to.equal('/favicon.svg');
32
+ });
33
+
34
+ it('transforms relative image paths to optimized path', async () => {
35
+ const res = await baseFixture.fetch('/');
36
+ const html = await res.text();
37
+ const { document } = parseHTML(html);
38
+ expect(document.querySelector('#relative > img')?.src).to.equal(
39
+ '/_image?href=%2Fsrc%2Fassets%2Frelative%2Foar.jpg%3ForigWidth%3D420%26origHeight%3D630%26origFormat%3Djpg&f=webp'
40
+ );
41
+ });
42
+
43
+ it('transforms aliased image paths to optimized path', async () => {
44
+ const res = await baseFixture.fetch('/');
45
+ const html = await res.text();
46
+ const { document } = parseHTML(html);
47
+ expect(document.querySelector('#alias > img')?.src).to.equal(
48
+ '/_image?href=%2Fsrc%2Fassets%2Falias%2Fcityscape.jpg%3ForigWidth%3D420%26origHeight%3D280%26origFormat%3Djpg&f=webp'
49
+ );
50
+ });
51
+ });
52
+
53
+ describe('build', () => {
54
+ before(async () => {
55
+ await baseFixture.build();
56
+ });
57
+
58
+ it('uses public/ image paths unchanged', async () => {
59
+ const html = await baseFixture.readFile('/index.html');
60
+ const { document } = parseHTML(html);
61
+ expect(document.querySelector('#public > img')?.src).to.equal('/favicon.svg');
62
+ });
63
+
64
+ it('transforms relative image paths to optimized path', async () => {
65
+ const html = await baseFixture.readFile('/index.html');
66
+ const { document } = parseHTML(html);
67
+ expect(document.querySelector('#relative > img')?.src).to.match(/^\/_astro\/oar.*\.webp$/);
68
+ });
69
+
70
+ it('transforms aliased image paths to optimized path', async () => {
71
+ const html = await baseFixture.readFile('/index.html');
72
+ const { document } = parseHTML(html);
73
+ expect(document.querySelector('#alias > img')?.src).to.match(/^\/_astro\/cityscape.*\.webp$/);
74
+ });
75
+ });
76
+ });