Skip to content

Commit

Permalink
do not emit assets during SSG
Browse files Browse the repository at this point in the history
  • Loading branch information
slorber committed Aug 20, 2024
1 parent a0804f8 commit 3508a26
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 46 deletions.
24 changes: 18 additions & 6 deletions packages/docusaurus-mdx-loader/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ import type {LoaderContext} from 'webpack';
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
type Pluggable = any; // TODO fix this asap

const {
loaders: {inlineMarkdownAssetImageFileLoader},
} = getFileLoaderUtils();

export type MDXPlugin = Pluggable;

export type Options = Partial<MDXOptions> & {
Expand Down Expand Up @@ -72,7 +68,13 @@ async function readMetadataPath(metadataPath: string) {
*
* `{image: "./myImage.png"}` => `{image: require("./myImage.png")}`
*/
function createAssetsExportCode(assets: unknown) {
function createAssetsExportCode({
assets,
inlineMarkdownAssetImageFileLoader,
}: {
assets: unknown;
inlineMarkdownAssetImageFileLoader: string;
}) {
if (
typeof assets !== 'object' ||
!assets ||
Expand Down Expand Up @@ -245,13 +247,23 @@ ${JSON.stringify(frontMatter, null, 2)}`;
? options.createAssets({frontMatter, metadata})
: undefined;

const fileLoaderUtils = getFileLoaderUtils(compilerName === 'server');

// TODO use remark plugins to insert extra exports instead of string concat?
// cf how the toc is exported
const exportsCode = `
export const frontMatter = ${stringifyObject(frontMatter)};
export const contentTitle = ${stringifyObject(contentTitle)};
${metadataJsonString ? `export const metadata = ${metadataJsonString};` : ''}
${assets ? `export const assets = ${createAssetsExportCode(assets)};` : ''}
${
assets
? `export const assets = ${createAssetsExportCode({
assets,
inlineMarkdownAssetImageFileLoader:
fileLoaderUtils.loaders.inlineMarkdownAssetImageFileLoader,
})};`
: ''
}
`;

const code = `
Expand Down
32 changes: 17 additions & 15 deletions packages/docusaurus-mdx-loader/src/remark/transformImage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
toMessageRelativeFilePath,
posixPath,
escapePath,
getFileLoaderUtils,
findAsyncSequential,
getFileLoaderUtils,
} from '@docusaurus/utils';
import escapeHtml from 'escape-html';
import sizeOf from 'image-size';
Expand All @@ -27,39 +27,36 @@ import type {MdxJsxTextElement} from 'mdast-util-mdx';
import type {Image} from 'mdast';
import type {Parent} from 'unist';

const {
loaders: {inlineMarkdownImageFileLoader},
} = getFileLoaderUtils();

type PluginOptions = {
staticDirs: string[];
siteDir: string;
};

type Context = PluginOptions & {
filePath: string;
inlineMarkdownImageFileLoader: string;
};

type Target = [node: Image, index: number, parent: Parent];

async function toImageRequireNode(
[node]: Target,
imagePath: string,
filePath: string,
context: Context,
) {
// MdxJsxTextElement => see https://github.com/facebook/docusaurus/pull/8288#discussion_r1125871405
const jsxNode = node as unknown as MdxJsxTextElement;
const attributes: MdxJsxTextElement['attributes'] = [];

let relativeImagePath = posixPath(
path.relative(path.dirname(filePath), imagePath),
path.relative(path.dirname(context.filePath), imagePath),
);
relativeImagePath = `./${relativeImagePath}`;

const parsedUrl = url.parse(node.url);
const hash = parsedUrl.hash ?? '';
const search = parsedUrl.search ?? '';
const requireString = `${inlineMarkdownImageFileLoader}${
const requireString = `${context.inlineMarkdownImageFileLoader}${
escapePath(relativeImagePath) + search
}`;
if (node.alt) {
Expand Down Expand Up @@ -186,21 +183,26 @@ async function processImageNode(target: Target, context: Context) {
// We try to convert image urls without protocol to images with require calls
// going through webpack ensures that image assets exist at build time
const imagePath = await getImageAbsolutePath(parsedUrl.pathname, context);
await toImageRequireNode(target, imagePath, context.filePath);
await toImageRequireNode(target, imagePath, context);
}

export default function plugin(options: PluginOptions): Transformer {
return async (root, vfile) => {
const {visit} = await import('unist-util-visit');

const fileLoaderUtils = getFileLoaderUtils(
vfile.data.compilerName === 'server',
);
const context: Context = {
...options,
filePath: vfile.path!,
inlineMarkdownImageFileLoader:
fileLoaderUtils.loaders.inlineMarkdownImageFileLoader,
};

const promises: Promise<void>[] = [];
visit(root, 'image', (node: Image, index, parent) => {
promises.push(
processImageNode([node, index, parent!], {
...options,
filePath: vfile.path!,
}),
);
promises.push(processImageNode([node, index, parent!], context));
});
await Promise.all(promises);
};
Expand Down
34 changes: 19 additions & 15 deletions packages/docusaurus-mdx-loader/src/remark/transformLinks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
toMessageRelativeFilePath,
posixPath,
escapePath,
getFileLoaderUtils,
findAsyncSequential,
getFileLoaderUtils,
} from '@docusaurus/utils';
import escapeHtml from 'escape-html';
import {assetRequireAttributeValue, transformNode} from '../utils';
Expand All @@ -24,17 +24,14 @@ import type {MdxJsxTextElement} from 'mdast-util-mdx';
import type {Parent} from 'unist';
import type {Link, Literal} from 'mdast';

const {
loaders: {inlineMarkdownLinkFileLoader},
} = getFileLoaderUtils();

type PluginOptions = {
staticDirs: string[];
siteDir: string;
};

type Context = PluginOptions & {
filePath: string;
inlineMarkdownLinkFileLoader: string;
};

type Target = [node: Link, index: number, parent: Parent];
Expand All @@ -45,15 +42,15 @@ type Target = [node: Link, index: number, parent: Parent];
async function toAssetRequireNode(
[node]: Target,
assetPath: string,
filePath: string,
context: Context,
) {
// MdxJsxTextElement => see https://github.com/facebook/docusaurus/pull/8288#discussion_r1125871405
const jsxNode = node as unknown as MdxJsxTextElement;
const attributes: MdxJsxTextElement['attributes'] = [];

// require("assets/file.pdf") means requiring from a package called assets
const relativeAssetPath = `./${posixPath(
path.relative(path.dirname(filePath), assetPath),
path.relative(path.dirname(context.filePath), assetPath),
)}`;

const parsedUrl = url.parse(node.url);
Expand All @@ -65,7 +62,9 @@ async function toAssetRequireNode(
path.extname(relativeAssetPath) === '.json'
? `${relativeAssetPath.replace('.json', '.raw')}!=`
: ''
}${inlineMarkdownLinkFileLoader}${escapePath(relativeAssetPath) + search}`;
}${context.inlineMarkdownLinkFileLoader}${
escapePath(relativeAssetPath) + search
}`;

attributes.push({
type: 'mdxJsxAttribute',
Expand Down Expand Up @@ -196,22 +195,27 @@ async function processLinkNode(target: Target, context: Context) {
context,
);
if (assetPath) {
await toAssetRequireNode(target, assetPath, context.filePath);
await toAssetRequireNode(target, assetPath, context);
}
}

export default function plugin(options: PluginOptions): Transformer {
return async (root, vfile) => {
const {visit} = await import('unist-util-visit');

const fileLoaderUtils = getFileLoaderUtils(
vfile.data.compilerName === 'server',
);
const context: Context = {
...options,
filePath: vfile.path!,
inlineMarkdownLinkFileLoader:
fileLoaderUtils.loaders.inlineMarkdownLinkFileLoader,
};

const promises: Promise<void>[] = [];
visit(root, 'link', (node: Link, index, parent) => {
promises.push(
processLinkNode([node, index, parent!], {
...options,
filePath: vfile.path!,
}),
);
promises.push(processLinkNode([node, index, parent!], context));
});
await Promise.all(promises);
};
Expand Down
40 changes: 31 additions & 9 deletions packages/docusaurus-utils/src/webpackUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ type FileLoaderUtils = {
};
};

/**
* Returns unified loader configurations to be used for various file types.
*
* Inspired by https://github.com/gatsbyjs/gatsby/blob/8e6e021014da310b9cc7d02e58c9b3efe938c665/packages/gatsby/src/utils/webpack-utils.ts#L447
*/
export function getFileLoaderUtils(): FileLoaderUtils {
// TODO this historical code is quite messy
// We should try to get rid of it and move to assets pipeline
function createFileLoaderUtils({
isServer,
}: {
isServer: boolean;
}): FileLoaderUtils {
// Files/images < urlLoaderLimit will be inlined as base64 strings directly in
// the html
const urlLoaderLimit = WEBPACK_URL_LOADER_LIMIT;
Expand All @@ -72,6 +73,7 @@ export function getFileLoaderUtils(): FileLoaderUtils {
loader: require.resolve(`file-loader`),
options: {
name: fileLoaderFileName(options.folder),
emitFile: !isServer,
},
}),
url: (options: {folder: AssetFolder}) => ({
Expand All @@ -80,6 +82,7 @@ export function getFileLoaderUtils(): FileLoaderUtils {
limit: urlLoaderLimit,
name: fileLoaderFileName(options.folder),
fallback: require.resolve('file-loader'),
emitFile: !isServer,
},
}),

Expand All @@ -92,13 +95,19 @@ export function getFileLoaderUtils(): FileLoaderUtils {
require.resolve('url-loader'),
)}?limit=${urlLoaderLimit}&name=${fileLoaderFileName(
'images',
)}&fallback=${escapePath(require.resolve('file-loader'))}!`,
)}&fallback=${escapePath(require.resolve('file-loader'))}${
isServer ? `&emitFile=false` : ''
}!`,
inlineMarkdownAssetImageFileLoader: `!${escapePath(
require.resolve('file-loader'),
)}?name=${fileLoaderFileName('images')}!`,
)}?name=${fileLoaderFileName('images')}${
isServer ? `&emitFile=false` : ''
}!`,
inlineMarkdownLinkFileLoader: `!${escapePath(
require.resolve('file-loader'),
)}?name=${fileLoaderFileName('files')}!`,
)}?name=${fileLoaderFileName('files')}${
isServer ? `&emitFile=false` : ''
}!`,
};

const rules: FileLoaderUtils['rules'] = {
Expand Down Expand Up @@ -173,3 +182,16 @@ export function getFileLoaderUtils(): FileLoaderUtils {

return {loaders, rules};
}

const FileLoaderUtilsMap = {
server: createFileLoaderUtils({isServer: true}),
client: createFileLoaderUtils({isServer: false}),
};

/**
* Returns unified loader configurations to be used for various file types.
* Inspired by https://github.com/gatsbyjs/gatsby/blob/8e6e021014da310b9cc7d02e58c9b3efe938c665/packages/gatsby/src/utils/webpack-utils.ts#L447
*/
export function getFileLoaderUtils(isServer: boolean): FileLoaderUtils {
return isServer ? FileLoaderUtilsMap.server : FileLoaderUtilsMap.client;
}
2 changes: 1 addition & 1 deletion packages/docusaurus/src/webpack/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function createBaseConfig({
const isProd = process.env.NODE_ENV === 'production';
const minimizeEnabled = minify && isProd;

const fileLoaderUtils = getFileLoaderUtils();
const fileLoaderUtils = getFileLoaderUtils(isServer);

const name = isServer ? 'server' : 'client';
const mode = isProd ? 'production' : 'development';
Expand Down

0 comments on commit 3508a26

Please sign in to comment.