Core: [Remark Image] Support relative imports

This commit is contained in:
fuma-nama
2024-08-31 15:54:30 +08:00
parent 54b8e1e0c4
commit 7dabbc16f5
13 changed files with 234 additions and 47 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
Remark Image: Support relative imports
+1 -10
View File
@@ -31,15 +31,6 @@ const config = {
},
};
const withMDX = createMDX({
buildSearchIndex: {
filter: (v) => {
return !v.match(/.+\.model\.mdx/) && !v.startsWith('blog');
},
},
mdxOptions: {
lastModifiedTime: 'git',
},
});
const withMDX = createMDX();
export default withAnalyzer(withMDX(config));
+5 -5
View File
@@ -1,16 +1,16 @@
import env from '@next/env';
import { updateSearchIndexes } from './update-index.mjs';
import { readFile } from 'node:fs/promises';
import type { SearchIndex } from 'fumadocs-mdx';
import type { Manifest } from 'fumadocs-mdx';
env.loadEnvConfig(process.cwd());
async function main() {
const indexes = JSON.parse(
(await readFile('.next/server/chunks/fumadocs_search.json')).toString(),
) as SearchIndex[];
const manifest = JSON.parse(
(await readFile('.source/manifest.json')).toString(),
) as Manifest;
await Promise.all([updateSearchIndexes(indexes)]);
await updateSearchIndexes(manifest);
}
await main().catch((e) => {
+25 -12
View File
@@ -1,10 +1,10 @@
import algosearch from 'algoliasearch';
import { sync } from 'fumadocs-core/search-algolia/server';
import type { SearchIndex } from 'fumadocs-mdx';
import type { Manifest } from 'fumadocs-mdx';
import { createGetUrl, getSlugs, parseFilePath } from 'fumadocs-core/source';
import path from 'node:path';
export async function updateSearchIndexes(
indexes: SearchIndex[],
): Promise<void> {
export async function updateSearchIndexes(manifest: Manifest): Promise<void> {
if (!process.env.ALGOLIA_API_KEY) {
console.warn('Algolia API Key not found, skip updating search index.');
return;
@@ -27,16 +27,29 @@ export async function updateSearchIndexes(
},
);
const getUrl = createGetUrl('/docs');
await sync(client, {
document: process.env.NEXT_PUBLIC_ALGOLIA_INDEX ?? 'document',
documents: indexes.map((docs) => ({
_id: docs.id,
title: docs.title,
description: docs.description,
url: docs.url,
structured: docs.structuredData,
tag: docs.url.split('/')[2],
})),
documents: manifest.files
.filter((file) => file.collection === 'docs')
.map((docs) => {
const url = getUrl(
getSlugs(parseFilePath(path.relative('content/docs', docs.path))),
);
if (!docs.data.structuredData)
throw new Error('`structuredData` is required');
return {
_id: docs.path,
title: docs.data.frontmatter.title as string,
description: docs.data.frontmatter.description as string,
url,
structured: docs.data.structuredData,
tag: url.split('/')[2],
};
}),
});
console.log('search updated');
+1
View File
@@ -41,6 +41,7 @@ export const blog = defineCollections({
export default defineConfig({
generateManifest: true,
lastModifiedTime: 'git',
mdxOptions: {
rehypeCodeOptions: {
themes: {
+17 -5
View File
@@ -56,10 +56,25 @@ export function remarkImage({
useImport = true,
publicDir = path.join(process.cwd(), 'public'),
}: RemarkImageOptions = {}): Transformer<Root, Root> {
return async (tree) => {
return async (tree, file) => {
const importsToInject: { variableName: string; importPath: string }[] = [];
const promises: Promise<void>[] = [];
function getImportPath(src: string): string {
if (!src.startsWith('/')) return src;
if (file.path) {
const relative = path.relative(
path.dirname(file.path),
path.join(publicDir, src),
);
return relative.startsWith('./') ? relative : `./${relative}`;
}
return path.join(publicDir, src);
}
visit(tree, 'image', (node) => {
const src = decodeURI(node.url);
if (!src) return;
@@ -107,10 +122,7 @@ export function remarkImage({
importsToInject.push({
variableName,
importPath: slash(
// with imports, relative paths don't have to be absolute
src.startsWith('/') ? path.join(publicDir, src) : src,
),
importPath: slash(getImportPath(src)),
});
Object.assign(node, {
+169
View File
@@ -0,0 +1,169 @@
{
"children": [
{
"data": {
"estree": {
"body": [
{
"source": {
"type": "Literal",
"value": "./test.png",
},
"specifiers": [
{
"local": {
"name": "__img0",
"type": "Identifier",
},
"type": "ImportDefaultSpecifier",
},
],
"type": "ImportDeclaration",
},
],
},
},
"type": "mdxjsEsm",
},
{
"children": [
{
"alt": "Test",
"attributes": [
{
"name": "alt",
"type": "mdxJsxAttribute",
"value": "Test",
},
{
"name": "placeholder",
"type": "mdxJsxAttribute",
"value": "blur",
},
{
"name": "src",
"type": "mdxJsxAttribute",
"value": {
"data": {
"estree": {
"body": [
{
"expression": {
"name": "__img0",
"type": "Identifier",
},
"type": "ExpressionStatement",
},
],
},
},
"type": "mdxJsxAttributeValueExpression",
"value": "__img0",
},
},
],
"name": "img",
"position": {
"end": {
"column": 20,
"line": 1,
"offset": 19,
},
"start": {
"column": 1,
"line": 1,
"offset": 0,
},
},
"title": null,
"type": "mdxJsxFlowElement",
"url": "./test.png",
},
],
"position": {
"end": {
"column": 20,
"line": 1,
"offset": 19,
},
"start": {
"column": 1,
"line": 1,
"offset": 0,
},
},
"type": "paragraph",
},
{
"children": [
{
"alt": "External",
"attributes": [
{
"name": "alt",
"type": "mdxJsxAttribute",
"value": "External",
},
{
"name": "src",
"type": "mdxJsxAttribute",
"value": "https://picsum.photos/id/237/200/300",
},
{
"name": "width",
"type": "mdxJsxAttribute",
"value": "200",
},
{
"name": "height",
"type": "mdxJsxAttribute",
"value": "300",
},
],
"name": "img",
"position": {
"end": {
"column": 50,
"line": 3,
"offset": 70,
},
"start": {
"column": 1,
"line": 3,
"offset": 21,
},
},
"title": null,
"type": "mdxJsxFlowElement",
"url": "https://picsum.photos/id/237/200/300",
},
],
"position": {
"end": {
"column": 50,
"line": 3,
"offset": 70,
},
"start": {
"column": 1,
"line": 3,
"offset": 21,
},
},
"type": "paragraph",
},
],
"position": {
"end": {
"column": 1,
"line": 4,
"offset": 71,
},
"start": {
"column": 1,
"line": 1,
"offset": 0,
},
},
"type": "root",
}
-5
View File
@@ -1,5 +0,0 @@
<img alt="Test" placeholder="blur" src={__img0} />
<img alt="External" src="https://picsum.photos/id/237/200/300" width="200" height="300" />
+6 -5
View File
@@ -55,13 +55,14 @@ test('Remark Admonition', async () => {
test('Remark Image', async () => {
const content = readFileSync(path.resolve(cwd, './fixtures/remark-image.md'));
const result = await remark()
const processor = remark()
.use(remarkImage, { publicDir: path.resolve(cwd, './fixtures') })
.use(remarkMdx)
.process(content);
.use(remarkMdx);
expect(result.value).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/remark-image.output.mdx'),
const result = await processor.run(processor.parse(content));
expect(result).toMatchFileSnapshot(
path.resolve(cwd, './fixtures/remark-image.output.json'),
);
});
+1 -1
View File
@@ -1 +1 @@
{"fumadocs-core":"13.3.2","fumadocs-ui":"13.3.2","fumadocs-mdx":"9.0.2","@fumadocs/content-collections":"1.1.2"}
{"fumadocs-core":"13.4.0","fumadocs-ui":"13.4.0","fumadocs-mdx":"9.0.3","@fumadocs/content-collections":"1.1.3"}
+2 -2
View File
@@ -5,7 +5,7 @@ import grayMatter from 'gray-matter';
import { type LoaderContext } from 'webpack';
import { type StructuredData } from 'fumadocs-core/mdx-plugins';
import { findCollection } from '@/utils/find-collection';
import { loadConfigCached } from '@/config/cached';
import { getConfigHash, loadConfigCached } from '@/config/cached';
import { buildMDX } from '@/utils/build-mdx';
import { getDefaultMDXOptions, type TransformContext } from '@/config';
import { getKey } from '@/map/manifest';
@@ -75,7 +75,7 @@ export default async function loader(
const config = await loadConfigCached(
_ctx.configPath,
// if no hash provided, always load a new config
hash ?? Date.now().toString(),
hash ?? (await getConfigHash(_ctx.configPath)),
);
const collection =
collectionId !== undefined
+1 -1
View File
@@ -60,7 +60,7 @@ export async function start(
fs.writeFileSync(typeOut, generateTypes(configPath, config, typeOut));
console.log('[MDX] initialized map file');
if (config.global?.generateManifest) {
if (config.global?.generateManifest && !dev) {
process.on('exit', () => {
console.log('[MDX] writing manifest');
writeManifest(manifestPath, config);
+1 -1
View File
@@ -30,7 +30,7 @@ export function toRuntime(
}
return {
...file,
...(file.default as object),
_file: info,
};
}