diff options
author | 2022-12-22 10:38:03 -0500 | |
---|---|---|
committer | 2022-12-22 10:38:03 -0500 | |
commit | 853081d1c857d8ad8a9634c37ed8fd123d32d241 (patch) | |
tree | 39bbad51f665c2f771fc0d9900656e7d09640b76 /packages/markdown/remark/src | |
parent | b64081deed88271a4aa55d66468a10fea235e0a9 (diff) | |
download | astro-853081d1c857d8ad8a9634c37ed8fd123d32d241.tar.gz astro-853081d1c857d8ad8a9634c37ed8fd123d32d241.tar.zst astro-853081d1c857d8ad8a9634c37ed8fd123d32d241.zip |
[Content] Throw on relative image usage (#5648)
* chore: add rel image error plugin
* deps: mdast, mdast types
* chore: add rel image throw to mdx
* refactor: doc rel image path plugin
* fix: respect experimental flag in md remark
* chore: changeset
* deps: remove mdast package
* fix: resolve contentDir from config
* fix: apply MDX plugin after user plugins
* fix: stub out contentDir
Diffstat (limited to 'packages/markdown/remark/src')
-rw-r--r-- | packages/markdown/remark/src/index.ts | 8 | ||||
-rw-r--r-- | packages/markdown/remark/src/remark-content-rel-image-error.ts | 52 | ||||
-rw-r--r-- | packages/markdown/remark/src/types.ts | 4 |
3 files changed, 64 insertions, 0 deletions
diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index 6d69bcd20..07df39ee8 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -14,6 +14,7 @@ import remarkPrism from './remark-prism.js'; import scopedStyles from './remark-scoped-styles.js'; import remarkShiki from './remark-shiki.js'; import remarkUnwrap from './remark-unwrap.js'; +import toRemarkContentRelImageError from './remark-content-rel-image-error.js'; import rehypeRaw from 'rehype-raw'; import rehypeStringify from 'rehype-stringify'; @@ -42,6 +43,8 @@ export async function renderMarkdown( remarkRehype = {}, extendDefaultPlugins = false, isAstroFlavoredMd = false, + isExperimentalContentCollections = false, + contentDir, } = opts; const input = new VFile({ value: content, path: fileURL }); const scopedClassName = opts.$?.scopedClassName; @@ -73,6 +76,11 @@ export async function renderMarkdown( parser.use([remarkPrism(scopedClassName)]); } + // Apply later in case user plugins resolve relative image paths + if (isExperimentalContentCollections) { + parser.use([toRemarkContentRelImageError({ contentDir })]); + } + parser.use([ [ markdownToHtml as any, diff --git a/packages/markdown/remark/src/remark-content-rel-image-error.ts b/packages/markdown/remark/src/remark-content-rel-image-error.ts new file mode 100644 index 000000000..0704ebdd1 --- /dev/null +++ b/packages/markdown/remark/src/remark-content-rel-image-error.ts @@ -0,0 +1,52 @@ +import type { Image } from 'mdast'; +import { visit } from 'unist-util-visit'; +import { pathToFileURL } from 'url'; +import type { VFile } from 'vfile'; + +/** + * `src/content/` does not support relative image paths. + * This plugin throws an error if any are found + */ +export default function toRemarkContentRelImageError({ contentDir }: { contentDir: URL }) { + return function remarkContentRelImageError() { + return (tree: any, vfile: VFile) => { + const isContentFile = pathToFileURL(vfile.path).href.startsWith(contentDir.href); + if (!isContentFile) return; + + const relImagePaths = new Set<string>(); + visit(tree, 'image', function raiseError(node: Image) { + console.log(node.url); + if (isRelativePath(node.url)) { + relImagePaths.add(node.url); + } + }); + if (relImagePaths.size === 0) return; + + 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)\n` + + [...relImagePaths].map((path) => JSON.stringify(path)).join(',\n'); + + // Throw raw string to use `astro:markdown` default formatting + throw errorMessage; + }; + }; +} + +// Following utils taken from `packages/astro/src/core/path.ts`: + +function isRelativePath(path: string) { + return startsWithDotDotSlash(path) || startsWithDotSlash(path); +} + +function startsWithDotDotSlash(path: string) { + const c1 = path[0]; + const c2 = path[1]; + const c3 = path[2]; + return c1 === '.' && c2 === '.' && c3 === '/'; +} + +function startsWithDotSlash(path: string) { + const c1 = path[0]; + const c2 = path[1]; + return c1 === '.' && c2 === '/'; +} diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index 76dfe9b73..15465d950 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -54,6 +54,10 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions { scopedClassName: string | null; }; isAstroFlavoredMd?: boolean; + /** Used to prevent relative image imports from `src/content/` */ + isExperimentalContentCollections?: boolean; + /** Used to prevent relative image imports from `src/content/` */ + contentDir: URL; } export interface MarkdownHeading { |