@astrojs/markdown-remark 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +23 -0
- package/dist/index.js +29 -16
- package/dist/rehype-images.d.ts +2 -0
- package/dist/rehype-images.js +71 -0
- package/dist/remark-collect-images.d.ts +4 -0
- package/dist/remark-collect-images.js +28 -0
- package/dist/types.d.ts +4 -2
- package/package.json +5 -4
- package/src/index.ts +35 -20
- package/src/rehype-collect-headings.ts +2 -1
- package/src/rehype-images.ts +78 -0
- package/src/remark-collect-images.ts +32 -0
- package/src/types.ts +4 -2
- package/tsconfig.json +2 -2
- package/dist/remark-content-rel-image-error.d.ts +0 -8
- package/dist/remark-content-rel-image-error.js +0 -41
- package/src/remark-content-rel-image-error.ts +0 -53
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
[35m@astrojs/markdown-remark:build: [0mcache hit, replaying output [
|
|
1
|
+
[35m@astrojs/markdown-remark:build: [0mcache hit, replaying output [2ma9a85fd17740b5e6[0m
|
|
2
2
|
[35m@astrojs/markdown-remark:build: [0m
|
|
3
|
-
[35m@astrojs/markdown-remark:build: [0m> @astrojs/markdown-remark@2.0
|
|
3
|
+
[35m@astrojs/markdown-remark:build: [0m> @astrojs/markdown-remark@2.1.0 build /home/runner/work/astro/astro/packages/markdown/remark
|
|
4
4
|
[35m@astrojs/markdown-remark:build: [0m> astro-scripts build "src/**/*.ts" && tsc -p tsconfig.json
|
|
5
5
|
[35m@astrojs/markdown-remark:build: [0m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @astrojs/markdown-remark
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#6344](https://github.com/withastro/astro/pull/6344) [`694918a56`](https://github.com/withastro/astro/commit/694918a56b01104831296be0c25456135a63c784) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Add a new experimental flag (`experimental.assets`) to enable our new core Assets story.
|
|
8
|
+
|
|
9
|
+
This unlocks a few features:
|
|
10
|
+
|
|
11
|
+
- A new built-in image component and JavaScript API to transform and optimize images.
|
|
12
|
+
- Relative images with automatic optimization in Markdown.
|
|
13
|
+
- Support for validating assets using content collections.
|
|
14
|
+
- and more!
|
|
15
|
+
|
|
16
|
+
See [Assets (Experimental)](https://docs.astro.build/en/guides/assets/) on our docs site for more information on how to use this feature!
|
|
17
|
+
|
|
18
|
+
- [#6213](https://github.com/withastro/astro/pull/6213) [`afbbc4d5b`](https://github.com/withastro/astro/commit/afbbc4d5bfafc1779bac00b41c2a1cb1c90f2808) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Updated compilation settings to disable downlevelling for Node 14
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`fec583909`](https://github.com/withastro/astro/commit/fec583909ab62829dc0c1600e2387979365f2b94), [`b087b83fe`](https://github.com/withastro/astro/commit/b087b83fe266c431fe34a07d5c2293cc4ab011c6), [`694918a56`](https://github.com/withastro/astro/commit/694918a56b01104831296be0c25456135a63c784), [`a20610609`](https://github.com/withastro/astro/commit/a20610609863ae3b48afe96819b8f11ae4f414d5), [`a4a74ab70`](https://github.com/withastro/astro/commit/a4a74ab70cd2aa0d812a1f6b202c4e240a8913bf), [`75921b3cd`](https://github.com/withastro/astro/commit/75921b3cd916d439f6392c487c21532fde35ed13), [`afbbc4d5b`](https://github.com/withastro/astro/commit/afbbc4d5bfafc1779bac00b41c2a1cb1c90f2808)]:
|
|
23
|
+
- astro@2.1.0
|
|
24
|
+
- @astrojs/prism@2.1.0
|
|
25
|
+
|
|
3
26
|
## 2.0.1
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toRemarkInitializeAstroData } from "./frontmatter-injection.js";
|
|
2
2
|
import { loadPlugins } from "./load-plugins.js";
|
|
3
3
|
import { rehypeHeadingIds } from "./rehype-collect-headings.js";
|
|
4
|
-
import
|
|
4
|
+
import toRemarkCollectImages from "./remark-collect-images.js";
|
|
5
5
|
import remarkPrism from "./remark-prism.js";
|
|
6
6
|
import scopedStyles from "./remark-scoped-styles.js";
|
|
7
7
|
import remarkShiki from "./remark-shiki.js";
|
|
@@ -13,6 +13,7 @@ import markdownToHtml from "remark-rehype";
|
|
|
13
13
|
import remarkSmartypants from "remark-smartypants";
|
|
14
14
|
import { unified } from "unified";
|
|
15
15
|
import { VFile } from "vfile";
|
|
16
|
+
import { rehypeImages } from "./rehype-images.js";
|
|
16
17
|
import { rehypeHeadingIds as rehypeHeadingIds2 } from "./rehype-collect-headings.js";
|
|
17
18
|
export * from "./types.js";
|
|
18
19
|
const markdownConfigDefaults = {
|
|
@@ -28,6 +29,7 @@ const markdownConfigDefaults = {
|
|
|
28
29
|
gfm: true,
|
|
29
30
|
smartypants: true
|
|
30
31
|
};
|
|
32
|
+
const isPerformanceBenchmark = Boolean(process.env.ASTRO_PERFORMANCE_BENCHMARK);
|
|
31
33
|
async function renderMarkdown(content, opts) {
|
|
32
34
|
var _a;
|
|
33
35
|
let {
|
|
@@ -39,32 +41,37 @@ async function renderMarkdown(content, opts) {
|
|
|
39
41
|
remarkRehype = markdownConfigDefaults.remarkRehype,
|
|
40
42
|
gfm = markdownConfigDefaults.gfm,
|
|
41
43
|
smartypants = markdownConfigDefaults.smartypants,
|
|
42
|
-
contentDir,
|
|
43
44
|
frontmatter: userFrontmatter = {}
|
|
44
45
|
} = opts;
|
|
45
46
|
const input = new VFile({ value: content, path: fileURL });
|
|
46
47
|
const scopedClassName = (_a = opts.$) == null ? void 0 : _a.scopedClassName;
|
|
47
48
|
let parser = unified().use(markdown).use(toRemarkInitializeAstroData({ userFrontmatter })).use([]);
|
|
48
|
-
if (gfm) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
if (!isPerformanceBenchmark && gfm) {
|
|
50
|
+
if (gfm) {
|
|
51
|
+
parser.use(remarkGfm);
|
|
52
|
+
}
|
|
53
|
+
if (smartypants) {
|
|
54
|
+
parser.use(remarkSmartypants);
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
57
|
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
|
|
55
58
|
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
|
|
56
59
|
loadedRemarkPlugins.forEach(([plugin, pluginOpts]) => {
|
|
57
60
|
parser.use([[plugin, pluginOpts]]);
|
|
58
61
|
});
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
if (!isPerformanceBenchmark) {
|
|
63
|
+
if (scopedClassName) {
|
|
64
|
+
parser.use([scopedStyles(scopedClassName)]);
|
|
65
|
+
}
|
|
66
|
+
if (syntaxHighlight === "shiki") {
|
|
67
|
+
parser.use([await remarkShiki(shikiConfig, scopedClassName)]);
|
|
68
|
+
} else if (syntaxHighlight === "prism") {
|
|
69
|
+
parser.use([remarkPrism(scopedClassName)]);
|
|
70
|
+
}
|
|
71
|
+
if (opts.experimentalAssets) {
|
|
72
|
+
parser.use([toRemarkCollectImages(opts.resolveImage)]);
|
|
73
|
+
}
|
|
66
74
|
}
|
|
67
|
-
parser.use([toRemarkContentRelImageError({ contentDir })]);
|
|
68
75
|
parser.use([
|
|
69
76
|
[
|
|
70
77
|
markdownToHtml,
|
|
@@ -78,7 +85,13 @@ async function renderMarkdown(content, opts) {
|
|
|
78
85
|
loadedRehypePlugins.forEach(([plugin, pluginOpts]) => {
|
|
79
86
|
parser.use([[plugin, pluginOpts]]);
|
|
80
87
|
});
|
|
81
|
-
|
|
88
|
+
if (opts.experimentalAssets) {
|
|
89
|
+
parser.use(rehypeImages(await opts.imageService, opts.assetsDir));
|
|
90
|
+
}
|
|
91
|
+
if (!isPerformanceBenchmark) {
|
|
92
|
+
parser.use([rehypeHeadingIds]);
|
|
93
|
+
}
|
|
94
|
+
parser.use([rehypeRaw]).use(rehypeStringify, { allowDangerousHtml: true });
|
|
82
95
|
let vfile;
|
|
83
96
|
try {
|
|
84
97
|
vfile = await parser.process(input);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import sizeOf from "image-size";
|
|
2
|
+
import { join as pathJoin } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { visit } from "unist-util-visit";
|
|
5
|
+
import { pathToFileURL } from "url";
|
|
6
|
+
function rehypeImages(imageService, assetsDir) {
|
|
7
|
+
return () => function(tree, file) {
|
|
8
|
+
visit(tree, (node) => {
|
|
9
|
+
var _a;
|
|
10
|
+
if (!assetsDir)
|
|
11
|
+
return;
|
|
12
|
+
if (node.type !== "element")
|
|
13
|
+
return;
|
|
14
|
+
if (node.tagName !== "img")
|
|
15
|
+
return;
|
|
16
|
+
if ((_a = node.properties) == null ? void 0 : _a.src) {
|
|
17
|
+
if (file.dirname) {
|
|
18
|
+
if (!isRelativePath(node.properties.src) && !isAliasedPath(node.properties.src))
|
|
19
|
+
return;
|
|
20
|
+
let fileURL;
|
|
21
|
+
if (isAliasedPath(node.properties.src)) {
|
|
22
|
+
fileURL = new URL(stripAliasPath(node.properties.src), assetsDir);
|
|
23
|
+
} else {
|
|
24
|
+
fileURL = pathToFileURL(pathJoin(file.dirname, node.properties.src));
|
|
25
|
+
}
|
|
26
|
+
const fileData = sizeOf(fileURLToPath(fileURL));
|
|
27
|
+
fileURL.searchParams.append("origWidth", fileData.width.toString());
|
|
28
|
+
fileURL.searchParams.append("origHeight", fileData.height.toString());
|
|
29
|
+
fileURL.searchParams.append("origFormat", fileData.type.toString());
|
|
30
|
+
let options = {
|
|
31
|
+
src: {
|
|
32
|
+
src: fileURL,
|
|
33
|
+
width: fileData.width,
|
|
34
|
+
height: fileData.height,
|
|
35
|
+
format: fileData.type
|
|
36
|
+
},
|
|
37
|
+
alt: node.properties.alt
|
|
38
|
+
};
|
|
39
|
+
const imageURL = imageService.getURL(options);
|
|
40
|
+
node.properties = Object.assign(node.properties, {
|
|
41
|
+
src: imageURL,
|
|
42
|
+
...imageService.getHTMLAttributes !== void 0 ? imageService.getHTMLAttributes(options) : {}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function isAliasedPath(path) {
|
|
50
|
+
return path.startsWith("~/assets");
|
|
51
|
+
}
|
|
52
|
+
function stripAliasPath(path) {
|
|
53
|
+
return path.replace("~/assets/", "");
|
|
54
|
+
}
|
|
55
|
+
function isRelativePath(path) {
|
|
56
|
+
return startsWithDotDotSlash(path) || startsWithDotSlash(path);
|
|
57
|
+
}
|
|
58
|
+
function startsWithDotDotSlash(path) {
|
|
59
|
+
const c1 = path[0];
|
|
60
|
+
const c2 = path[1];
|
|
61
|
+
const c3 = path[2];
|
|
62
|
+
return c1 === "." && c2 === "." && c3 === "/";
|
|
63
|
+
}
|
|
64
|
+
function startsWithDotSlash(path) {
|
|
65
|
+
const c1 = path[0];
|
|
66
|
+
const c2 = path[1];
|
|
67
|
+
return c1 === "." && c2 === "/";
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
rehypeImages
|
|
71
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { visit } from "unist-util-visit";
|
|
2
|
+
function toRemarkCollectImages(resolveImage) {
|
|
3
|
+
return () => async function(tree, vfile) {
|
|
4
|
+
if (typeof (vfile == null ? void 0 : vfile.path) !== "string")
|
|
5
|
+
return;
|
|
6
|
+
const imagePaths = /* @__PURE__ */ new Set();
|
|
7
|
+
visit(tree, "image", function raiseError(node) {
|
|
8
|
+
imagePaths.add(node.url);
|
|
9
|
+
});
|
|
10
|
+
if (imagePaths.size === 0) {
|
|
11
|
+
vfile.data.imagePaths = [];
|
|
12
|
+
return;
|
|
13
|
+
} else if (resolveImage) {
|
|
14
|
+
const mapping = /* @__PURE__ */ new Map();
|
|
15
|
+
for (const path of Array.from(imagePaths)) {
|
|
16
|
+
const id = await resolveImage(path);
|
|
17
|
+
mapping.set(path, id);
|
|
18
|
+
}
|
|
19
|
+
visit(tree, "image", function raiseError(node) {
|
|
20
|
+
node.url = mapping.get(node.url);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
vfile.data.imagePaths = Array.from(imagePaths);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
toRemarkCollectImages as default
|
|
28
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -38,10 +38,12 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
|
|
38
38
|
$?: {
|
|
39
39
|
scopedClassName: string | null;
|
|
40
40
|
};
|
|
41
|
-
/** Used to prevent relative image imports from `src/content/` */
|
|
42
|
-
contentDir: URL;
|
|
43
41
|
/** Used for frontmatter injection plugins */
|
|
44
42
|
frontmatter?: Record<string, any>;
|
|
43
|
+
experimentalAssets?: boolean;
|
|
44
|
+
imageService?: any;
|
|
45
|
+
assetsDir?: URL;
|
|
46
|
+
resolveImage?: (path: string) => Promise<string>;
|
|
45
47
|
}
|
|
46
48
|
export interface MarkdownHeading {
|
|
47
49
|
depth: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/markdown-remark",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "withastro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
"./dist/internal.js": "./dist/internal.js"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"astro": "^2.0
|
|
20
|
+
"astro": "^2.1.0"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@astrojs/prism": "^2.
|
|
23
|
+
"@astrojs/prism": "^2.1.0",
|
|
24
24
|
"github-slugger": "^1.4.0",
|
|
25
|
+
"image-size": "^1.0.2",
|
|
25
26
|
"import-meta-resolve": "^2.1.0",
|
|
26
27
|
"rehype-raw": "^6.1.1",
|
|
27
28
|
"rehype-stringify": "^9.0.3",
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
"@types/mdast": "^3.0.10",
|
|
43
44
|
"@types/mocha": "^9.1.1",
|
|
44
45
|
"@types/unist": "^2.0.6",
|
|
45
|
-
"astro-scripts": "0.0.
|
|
46
|
+
"astro-scripts": "0.0.14",
|
|
46
47
|
"chai": "^4.3.6",
|
|
47
48
|
"mdast-util-mdx-expression": "^1.3.1",
|
|
48
49
|
"mocha": "^9.2.2"
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
import { toRemarkInitializeAstroData } from './frontmatter-injection.js';
|
|
9
9
|
import { loadPlugins } from './load-plugins.js';
|
|
10
10
|
import { rehypeHeadingIds } from './rehype-collect-headings.js';
|
|
11
|
-
import
|
|
11
|
+
import toRemarkCollectImages from './remark-collect-images.js';
|
|
12
12
|
import remarkPrism from './remark-prism.js';
|
|
13
13
|
import scopedStyles from './remark-scoped-styles.js';
|
|
14
14
|
import remarkShiki from './remark-shiki.js';
|
|
@@ -21,6 +21,7 @@ import markdownToHtml from 'remark-rehype';
|
|
|
21
21
|
import remarkSmartypants from 'remark-smartypants';
|
|
22
22
|
import { unified } from 'unified';
|
|
23
23
|
import { VFile } from 'vfile';
|
|
24
|
+
import { rehypeImages } from './rehype-images.js';
|
|
24
25
|
|
|
25
26
|
export { rehypeHeadingIds } from './rehype-collect-headings.js';
|
|
26
27
|
export * from './types.js';
|
|
@@ -39,6 +40,9 @@ export const markdownConfigDefaults: Omit<Required<AstroMarkdownOptions>, 'draft
|
|
|
39
40
|
smartypants: true,
|
|
40
41
|
};
|
|
41
42
|
|
|
43
|
+
// Skip nonessential plugins during performance benchmark runs
|
|
44
|
+
const isPerformanceBenchmark = Boolean(process.env.ASTRO_PERFORMANCE_BENCHMARK);
|
|
45
|
+
|
|
42
46
|
/** Shared utility for rendering markdown */
|
|
43
47
|
export async function renderMarkdown(
|
|
44
48
|
content: string,
|
|
@@ -53,7 +57,6 @@ export async function renderMarkdown(
|
|
|
53
57
|
remarkRehype = markdownConfigDefaults.remarkRehype,
|
|
54
58
|
gfm = markdownConfigDefaults.gfm,
|
|
55
59
|
smartypants = markdownConfigDefaults.smartypants,
|
|
56
|
-
contentDir,
|
|
57
60
|
frontmatter: userFrontmatter = {},
|
|
58
61
|
} = opts;
|
|
59
62
|
const input = new VFile({ value: content, path: fileURL });
|
|
@@ -64,12 +67,13 @@ export async function renderMarkdown(
|
|
|
64
67
|
.use(toRemarkInitializeAstroData({ userFrontmatter }))
|
|
65
68
|
.use([]);
|
|
66
69
|
|
|
67
|
-
if (gfm) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
if (!isPerformanceBenchmark && gfm) {
|
|
71
|
+
if (gfm) {
|
|
72
|
+
parser.use(remarkGfm);
|
|
73
|
+
}
|
|
74
|
+
if (smartypants) {
|
|
75
|
+
parser.use(remarkSmartypants);
|
|
76
|
+
}
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
|
|
@@ -79,18 +83,22 @@ export async function renderMarkdown(
|
|
|
79
83
|
parser.use([[plugin, pluginOpts]]);
|
|
80
84
|
});
|
|
81
85
|
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
if (!isPerformanceBenchmark) {
|
|
87
|
+
if (scopedClassName) {
|
|
88
|
+
parser.use([scopedStyles(scopedClassName)]);
|
|
89
|
+
}
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
if (syntaxHighlight === 'shiki') {
|
|
92
|
+
parser.use([await remarkShiki(shikiConfig, scopedClassName)]);
|
|
93
|
+
} else if (syntaxHighlight === 'prism') {
|
|
94
|
+
parser.use([remarkPrism(scopedClassName)]);
|
|
95
|
+
}
|
|
91
96
|
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
if (opts.experimentalAssets) {
|
|
98
|
+
// Apply later in case user plugins resolve relative image paths
|
|
99
|
+
parser.use([toRemarkCollectImages(opts.resolveImage)]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
94
102
|
|
|
95
103
|
parser.use([
|
|
96
104
|
[
|
|
@@ -107,7 +115,14 @@ export async function renderMarkdown(
|
|
|
107
115
|
parser.use([[plugin, pluginOpts]]);
|
|
108
116
|
});
|
|
109
117
|
|
|
110
|
-
|
|
118
|
+
if (opts.experimentalAssets) {
|
|
119
|
+
parser.use(rehypeImages(await opts.imageService, opts.assetsDir));
|
|
120
|
+
}
|
|
121
|
+
if (!isPerformanceBenchmark) {
|
|
122
|
+
parser.use([rehypeHeadingIds]);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
parser.use([rehypeRaw]).use(rehypeStringify, { allowDangerousHtml: true });
|
|
111
126
|
|
|
112
127
|
let vfile: MarkdownVFile;
|
|
113
128
|
try {
|
|
@@ -144,7 +159,7 @@ function prefixError(err: any, prefix: string) {
|
|
|
144
159
|
const wrappedError = new Error(`${prefix}${err ? `: ${err}` : ''}`);
|
|
145
160
|
try {
|
|
146
161
|
wrappedError.stack = err.stack;
|
|
147
|
-
// @ts-
|
|
162
|
+
// @ts-expect-error
|
|
148
163
|
wrappedError.cause = err;
|
|
149
164
|
} catch (error) {
|
|
150
165
|
// It's ok if we could not set the stack or cause - the message is the most important part
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type Expression, type Super } from 'estree';
|
|
2
2
|
import Slugger from 'github-slugger';
|
|
3
3
|
import { type MdxTextExpression } from 'mdast-util-mdx-expression';
|
|
4
|
-
import {
|
|
4
|
+
import { type Node } from 'unist';
|
|
5
|
+
import { visit } from 'unist-util-visit';
|
|
5
6
|
|
|
6
7
|
import { InvalidAstroDataError, safelyGetAstroData } from './frontmatter-injection.js';
|
|
7
8
|
import type { MarkdownAstroData, MarkdownHeading, MarkdownVFile, RehypePlugin } from './types.js';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import sizeOf from 'image-size';
|
|
2
|
+
import { join as pathJoin } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { visit } from 'unist-util-visit';
|
|
5
|
+
import { pathToFileURL } from 'url';
|
|
6
|
+
import type { MarkdownVFile } from './types.js';
|
|
7
|
+
|
|
8
|
+
export function rehypeImages(imageService: any, assetsDir: URL | undefined) {
|
|
9
|
+
return () =>
|
|
10
|
+
function (tree: any, file: MarkdownVFile) {
|
|
11
|
+
visit(tree, (node) => {
|
|
12
|
+
if (!assetsDir) return;
|
|
13
|
+
if (node.type !== 'element') return;
|
|
14
|
+
if (node.tagName !== 'img') return;
|
|
15
|
+
|
|
16
|
+
if (node.properties?.src) {
|
|
17
|
+
if (file.dirname) {
|
|
18
|
+
if (!isRelativePath(node.properties.src) && !isAliasedPath(node.properties.src)) return;
|
|
19
|
+
|
|
20
|
+
let fileURL: URL;
|
|
21
|
+
if (isAliasedPath(node.properties.src)) {
|
|
22
|
+
fileURL = new URL(stripAliasPath(node.properties.src), assetsDir);
|
|
23
|
+
} else {
|
|
24
|
+
fileURL = pathToFileURL(pathJoin(file.dirname, node.properties.src));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const fileData = sizeOf(fileURLToPath(fileURL));
|
|
28
|
+
fileURL.searchParams.append('origWidth', fileData.width!.toString());
|
|
29
|
+
fileURL.searchParams.append('origHeight', fileData.height!.toString());
|
|
30
|
+
fileURL.searchParams.append('origFormat', fileData.type!.toString());
|
|
31
|
+
|
|
32
|
+
let options = {
|
|
33
|
+
src: {
|
|
34
|
+
src: fileURL,
|
|
35
|
+
width: fileData.width,
|
|
36
|
+
height: fileData.height,
|
|
37
|
+
format: fileData.type,
|
|
38
|
+
},
|
|
39
|
+
alt: node.properties.alt,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const imageURL = imageService.getURL(options);
|
|
43
|
+
node.properties = Object.assign(node.properties, {
|
|
44
|
+
src: imageURL,
|
|
45
|
+
...(imageService.getHTMLAttributes !== undefined
|
|
46
|
+
? imageService.getHTMLAttributes(options)
|
|
47
|
+
: {}),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isAliasedPath(path: string) {
|
|
56
|
+
return path.startsWith('~/assets');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function stripAliasPath(path: string) {
|
|
60
|
+
return path.replace('~/assets/', '');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isRelativePath(path: string) {
|
|
64
|
+
return startsWithDotDotSlash(path) || startsWithDotSlash(path);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function startsWithDotDotSlash(path: string) {
|
|
68
|
+
const c1 = path[0];
|
|
69
|
+
const c2 = path[1];
|
|
70
|
+
const c3 = path[2];
|
|
71
|
+
return c1 === '.' && c2 === '.' && c3 === '/';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function startsWithDotSlash(path: string) {
|
|
75
|
+
const c1 = path[0];
|
|
76
|
+
const c2 = path[1];
|
|
77
|
+
return c1 === '.' && c2 === '/';
|
|
78
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Image } from 'mdast';
|
|
2
|
+
import { visit } from 'unist-util-visit';
|
|
3
|
+
import type { VFile } from 'vfile';
|
|
4
|
+
|
|
5
|
+
type OptionalResolveImage = ((path: string) => Promise<string>) | undefined;
|
|
6
|
+
|
|
7
|
+
export default function toRemarkCollectImages(resolveImage: OptionalResolveImage) {
|
|
8
|
+
return () =>
|
|
9
|
+
async function (tree: any, vfile: VFile) {
|
|
10
|
+
if (typeof vfile?.path !== 'string') return;
|
|
11
|
+
|
|
12
|
+
const imagePaths = new Set<string>();
|
|
13
|
+
visit(tree, 'image', function raiseError(node: Image) {
|
|
14
|
+
imagePaths.add(node.url);
|
|
15
|
+
});
|
|
16
|
+
if (imagePaths.size === 0) {
|
|
17
|
+
vfile.data.imagePaths = [];
|
|
18
|
+
return;
|
|
19
|
+
} else if (resolveImage) {
|
|
20
|
+
const mapping = new Map<string, string>();
|
|
21
|
+
for (const path of Array.from(imagePaths)) {
|
|
22
|
+
const id = await resolveImage(path);
|
|
23
|
+
mapping.set(path, id);
|
|
24
|
+
}
|
|
25
|
+
visit(tree, 'image', function raiseError(node: Image) {
|
|
26
|
+
node.url = mapping.get(node.url)!;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
vfile.data.imagePaths = Array.from(imagePaths);
|
|
31
|
+
};
|
|
32
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -58,10 +58,12 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
|
|
58
58
|
$?: {
|
|
59
59
|
scopedClassName: string | null;
|
|
60
60
|
};
|
|
61
|
-
/** Used to prevent relative image imports from `src/content/` */
|
|
62
|
-
contentDir: URL;
|
|
63
61
|
/** Used for frontmatter injection plugins */
|
|
64
62
|
frontmatter?: Record<string, any>;
|
|
63
|
+
experimentalAssets?: boolean;
|
|
64
|
+
imageService?: any;
|
|
65
|
+
assetsDir?: URL;
|
|
66
|
+
resolveImage?: (path: string) => Promise<string>;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
export interface MarkdownHeading {
|
package/tsconfig.json
CHANGED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { VFile } from 'vfile';
|
|
2
|
-
/**
|
|
3
|
-
* `src/content/` does not support relative image paths.
|
|
4
|
-
* This plugin throws an error if any are found
|
|
5
|
-
*/
|
|
6
|
-
export default function toRemarkContentRelImageError({ contentDir }: {
|
|
7
|
-
contentDir: URL;
|
|
8
|
-
}): () => (tree: any, vfile: VFile) => void;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { visit } from "unist-util-visit";
|
|
2
|
-
import { pathToFileURL } from "url";
|
|
3
|
-
function toRemarkContentRelImageError({ contentDir }) {
|
|
4
|
-
return function remarkContentRelImageError() {
|
|
5
|
-
return (tree, vfile) => {
|
|
6
|
-
if (typeof (vfile == null ? void 0 : vfile.path) !== "string")
|
|
7
|
-
return;
|
|
8
|
-
const isContentFile = pathToFileURL(vfile.path).href.startsWith(contentDir.href);
|
|
9
|
-
if (!isContentFile)
|
|
10
|
-
return;
|
|
11
|
-
const relImagePaths = /* @__PURE__ */ new Set();
|
|
12
|
-
visit(tree, "image", function raiseError(node) {
|
|
13
|
-
if (isRelativePath(node.url)) {
|
|
14
|
-
relImagePaths.add(node.url);
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
if (relImagePaths.size === 0)
|
|
18
|
-
return;
|
|
19
|
-
const errorMessage = `Relative image paths are not supported in the content/ directory. Place local images in the public/ directory and use absolute paths (see https://docs.astro.build/en/guides/images/#in-markdown-files)
|
|
20
|
-
` + [...relImagePaths].map((path) => JSON.stringify(path)).join(",\n");
|
|
21
|
-
throw errorMessage;
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function isRelativePath(path) {
|
|
26
|
-
return startsWithDotDotSlash(path) || startsWithDotSlash(path);
|
|
27
|
-
}
|
|
28
|
-
function startsWithDotDotSlash(path) {
|
|
29
|
-
const c1 = path[0];
|
|
30
|
-
const c2 = path[1];
|
|
31
|
-
const c3 = path[2];
|
|
32
|
-
return c1 === "." && c2 === "." && c3 === "/";
|
|
33
|
-
}
|
|
34
|
-
function startsWithDotSlash(path) {
|
|
35
|
-
const c1 = path[0];
|
|
36
|
-
const c2 = path[1];
|
|
37
|
-
return c1 === "." && c2 === "/";
|
|
38
|
-
}
|
|
39
|
-
export {
|
|
40
|
-
toRemarkContentRelImageError as default
|
|
41
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { Image } from 'mdast';
|
|
2
|
-
import { visit } from 'unist-util-visit';
|
|
3
|
-
import { pathToFileURL } from 'url';
|
|
4
|
-
import type { VFile } from 'vfile';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* `src/content/` does not support relative image paths.
|
|
8
|
-
* This plugin throws an error if any are found
|
|
9
|
-
*/
|
|
10
|
-
export default function toRemarkContentRelImageError({ contentDir }: { contentDir: URL }) {
|
|
11
|
-
return function remarkContentRelImageError() {
|
|
12
|
-
return (tree: any, vfile: VFile) => {
|
|
13
|
-
if (typeof vfile?.path !== 'string') return;
|
|
14
|
-
|
|
15
|
-
const isContentFile = pathToFileURL(vfile.path).href.startsWith(contentDir.href);
|
|
16
|
-
if (!isContentFile) return;
|
|
17
|
-
|
|
18
|
-
const relImagePaths = new Set<string>();
|
|
19
|
-
visit(tree, 'image', function raiseError(node: Image) {
|
|
20
|
-
if (isRelativePath(node.url)) {
|
|
21
|
-
relImagePaths.add(node.url);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
if (relImagePaths.size === 0) return;
|
|
25
|
-
|
|
26
|
-
const errorMessage =
|
|
27
|
-
`Relative image paths are not supported in the content/ directory. Place local images in the public/ directory and use absolute paths (see https://docs.astro.build/en/guides/images/#in-markdown-files)\n` +
|
|
28
|
-
[...relImagePaths].map((path) => JSON.stringify(path)).join(',\n');
|
|
29
|
-
|
|
30
|
-
// Throw raw string to use `astro:markdown` default formatting
|
|
31
|
-
throw errorMessage;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Following utils taken from `packages/astro/src/core/path.ts`:
|
|
37
|
-
|
|
38
|
-
function isRelativePath(path: string) {
|
|
39
|
-
return startsWithDotDotSlash(path) || startsWithDotSlash(path);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function startsWithDotDotSlash(path: string) {
|
|
43
|
-
const c1 = path[0];
|
|
44
|
-
const c2 = path[1];
|
|
45
|
-
const c3 = path[2];
|
|
46
|
-
return c1 === '.' && c2 === '.' && c3 === '/';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function startsWithDotSlash(path: string) {
|
|
50
|
-
const c1 = path[0];
|
|
51
|
-
const c2 = path[1];
|
|
52
|
-
return c1 === '.' && c2 === '/';
|
|
53
|
-
}
|