mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 23:49:51 +00:00
MDX: watch the output config path on Webpack
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-mdx': major
|
||||
---
|
||||
|
||||
Change `postInstall()` signature to `postInstall({ configPath, outDir, ... })`
|
||||
@@ -12,10 +12,10 @@ async function start() {
|
||||
|
||||
if (isNext) {
|
||||
const { postInstall } = await import('./next');
|
||||
await postInstall(configPath);
|
||||
await postInstall({ configPath });
|
||||
} else {
|
||||
const { postInstall } = await import('./vite');
|
||||
await postInstall(configPath);
|
||||
await postInstall({ configPath });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { BunPlugin } from 'bun';
|
||||
import { createMdxLoader } from '@/loaders/mdx';
|
||||
import { buildConfig } from '@/config/build';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { type CoreOptions, createCore, findConfigFile } from '@/core';
|
||||
import { _Defaults, type CoreOptions, createCore } from '@/core';
|
||||
import { createIntegratedConfigLoader } from '@/loaders/config';
|
||||
import { createMetaLoader } from '@/loaders/meta';
|
||||
import { toBun } from '@/loaders/adapter';
|
||||
@@ -17,8 +17,8 @@ export interface MdxPluginOptions extends Partial<CoreOptions> {
|
||||
export function createMdxPlugin(options: MdxPluginOptions = {}): BunPlugin {
|
||||
const {
|
||||
environment = 'bun',
|
||||
outDir = '.source',
|
||||
configPath = findConfigFile(),
|
||||
outDir = _Defaults.outDir,
|
||||
configPath = _Defaults.configPath,
|
||||
disableMetaFile = false,
|
||||
} = options;
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import * as path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import type { LoadedConfig } from '@/config/build';
|
||||
import { buildConfig } from '@/config/build';
|
||||
import type { Core } from '@/core';
|
||||
|
||||
async function compileConfig(configPath: string, outDir: string) {
|
||||
async function compileConfig(core: Core) {
|
||||
const { build } = await import('esbuild');
|
||||
|
||||
const transformed = await build({
|
||||
entryPoints: [{ in: configPath, out: 'source.config' }],
|
||||
entryPoints: [{ in: core._options.configPath, out: 'source.config' }],
|
||||
bundle: true,
|
||||
outdir: outDir,
|
||||
outdir: core._options.outDir,
|
||||
target: 'node20',
|
||||
write: true,
|
||||
platform: 'node',
|
||||
@@ -29,18 +29,15 @@ async function compileConfig(configPath: string, outDir: string) {
|
||||
/**
|
||||
* Load config
|
||||
*
|
||||
* @param configPath - config path
|
||||
* @param outDir - output directory
|
||||
* @param build - By default, it assumes the TypeScript config has been compiled to `.source/source.config.mjs`. Set this `true` to compile the config first.
|
||||
* @param build - By default, it assumes the config file has been compiled. Set this `true` to compile the config first.
|
||||
*/
|
||||
export async function loadConfig(
|
||||
configPath: string,
|
||||
outDir: string,
|
||||
core: Core,
|
||||
build = false,
|
||||
): Promise<LoadedConfig> {
|
||||
if (build) await compileConfig(configPath, outDir);
|
||||
if (build) await compileConfig(core);
|
||||
|
||||
const url = pathToFileURL(path.resolve(outDir, 'source.config.mjs'));
|
||||
const url = pathToFileURL(core.getCompiledConfigPath());
|
||||
// always return a new config
|
||||
url.searchParams.set('hash', Date.now().toString());
|
||||
|
||||
|
||||
@@ -67,9 +67,10 @@ export interface EmitOptions {
|
||||
filterPlugin?: (plugin: Plugin) => boolean;
|
||||
}
|
||||
|
||||
export function findConfigFile(): string {
|
||||
return path.resolve('source.config.ts');
|
||||
}
|
||||
export const _Defaults = {
|
||||
configPath: 'source.config.ts',
|
||||
outDir: '.source',
|
||||
};
|
||||
|
||||
async function getPlugins(pluginOptions: PluginOption[]): Promise<Plugin[]> {
|
||||
const plugins: Plugin[] = [];
|
||||
@@ -120,6 +121,12 @@ export function createCore(
|
||||
getConfig(): LoadedConfig {
|
||||
return config;
|
||||
},
|
||||
/**
|
||||
* The file path of compiled config file, the file may not exist (e.g. on Vite, or still compiling)
|
||||
*/
|
||||
getCompiledConfigPath(): string {
|
||||
return path.join(options.outDir, 'source.config.mjs');
|
||||
},
|
||||
async initServer(server: ServerContext): Promise<void> {
|
||||
server.watcher?.on('all', async (event, file) => {
|
||||
if (event === 'change') removeFileCache(file);
|
||||
|
||||
@@ -36,11 +36,7 @@ export function createStandaloneConfigLoader({
|
||||
async function newConfig() {
|
||||
const { loadConfig } = await import('../config/load-from-file');
|
||||
await core.init({
|
||||
config: loadConfig(
|
||||
core._options.configPath,
|
||||
core._options.outDir,
|
||||
buildConfig,
|
||||
),
|
||||
config: loadConfig(core, buildConfig),
|
||||
});
|
||||
|
||||
return core.getConfig();
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as path from 'node:path';
|
||||
import { loadConfig } from '@/config/load-from-file';
|
||||
import { ValidationError } from '@/utils/validation';
|
||||
import next from '@/plugins/next';
|
||||
import { type Core, createCore, findConfigFile } from '@/core';
|
||||
import { _Defaults, type Core, createCore } from '@/core';
|
||||
import { mdxLoaderGlob, metaLoaderGlob } from '@/loaders';
|
||||
import type { IndexFilePluginOptions } from '@/plugins/index-file';
|
||||
|
||||
@@ -32,18 +32,19 @@ export interface CreateMDXOptions {
|
||||
const defaultPageExtensions = ['mdx', 'md', 'jsx', 'js', 'tsx', 'ts'];
|
||||
|
||||
export function createMDX(createOptions: CreateMDXOptions = {}) {
|
||||
const options = applyDefaults(createOptions);
|
||||
const core = createNextCore(applyDefaults(createOptions));
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
if (process.env._FUMADOCS_MDX !== '1') {
|
||||
process.env._FUMADOCS_MDX = '1';
|
||||
|
||||
void init(isDev, options);
|
||||
void init(isDev, core);
|
||||
}
|
||||
|
||||
return (nextConfig: NextConfig = {}): NextConfig => {
|
||||
const loaderOptions: WebpackLoaderOptions = {
|
||||
...options,
|
||||
...core._options,
|
||||
compiledConfigPath: core.getCompiledConfigPath(),
|
||||
isDev,
|
||||
};
|
||||
|
||||
@@ -122,15 +123,10 @@ export function createMDX(createOptions: CreateMDXOptions = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
async function init(
|
||||
dev: boolean,
|
||||
options: Required<CreateMDXOptions>,
|
||||
): Promise<void> {
|
||||
const core = createNextCore(options);
|
||||
|
||||
async function init(dev: boolean, core: Core): Promise<void> {
|
||||
async function initOrReload() {
|
||||
await core.init({
|
||||
config: loadConfig(options.configPath, options.outDir, true),
|
||||
config: loadConfig(core, true),
|
||||
});
|
||||
await core.emitAndWrite();
|
||||
}
|
||||
@@ -140,10 +136,10 @@ async function init(
|
||||
const watcher = new FSWatcher({
|
||||
ignoreInitial: true,
|
||||
persistent: true,
|
||||
ignored: [options.outDir],
|
||||
ignored: [core._options.outDir],
|
||||
});
|
||||
|
||||
watcher.add(options.configPath);
|
||||
watcher.add(core._options.configPath);
|
||||
for (const collection of core.getConfig().collectionList) {
|
||||
if (collection.type === 'docs') {
|
||||
watcher.add(collection.docs.dir);
|
||||
@@ -158,7 +154,7 @@ async function init(
|
||||
});
|
||||
|
||||
watcher.on('all', async (_event, file) => {
|
||||
if (path.resolve(file) === path.resolve(options.configPath)) {
|
||||
if (path.resolve(file) === path.resolve(core._options.configPath)) {
|
||||
// skip plugin listeners
|
||||
watcher.removeAllListeners();
|
||||
|
||||
@@ -185,26 +181,19 @@ async function init(
|
||||
}
|
||||
}
|
||||
|
||||
export async function postInstall(
|
||||
configPath = findConfigFile(),
|
||||
outDir = '.source',
|
||||
) {
|
||||
const core = await createNextCore({
|
||||
index: {},
|
||||
outDir,
|
||||
configPath,
|
||||
}).init({
|
||||
config: loadConfig(configPath, outDir, true),
|
||||
export async function postInstall(options: CreateMDXOptions) {
|
||||
const core = createNextCore(applyDefaults(options));
|
||||
await core.init({
|
||||
config: loadConfig(core, true),
|
||||
});
|
||||
|
||||
await core.emitAndWrite();
|
||||
}
|
||||
|
||||
function applyDefaults(options: CreateMDXOptions): Required<CreateMDXOptions> {
|
||||
return {
|
||||
index: {},
|
||||
outDir: options.outDir ?? '.source',
|
||||
configPath: options.configPath ?? findConfigFile(),
|
||||
outDir: options.outDir ?? _Defaults.outDir,
|
||||
configPath: options.configPath ?? _Defaults.configPath,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createCore, findConfigFile } from '@/core';
|
||||
import { _Defaults, createCore } from '@/core';
|
||||
import { createMdxLoader } from '@/loaders/mdx';
|
||||
import { toNode } from '@/loaders/adapter';
|
||||
import { createStandaloneConfigLoader } from '@/loaders/config';
|
||||
@@ -7,8 +7,8 @@ import { createMetaLoader } from '@/loaders/meta';
|
||||
|
||||
const core = createCore({
|
||||
environment: 'node',
|
||||
configPath: findConfigFile(),
|
||||
outDir: '.source',
|
||||
configPath: _Defaults.configPath,
|
||||
outDir: _Defaults.outDir,
|
||||
});
|
||||
|
||||
const configLoader = createStandaloneConfigLoader({
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createMdxLoader } from '@/loaders/mdx';
|
||||
import { toVite } from '@/loaders/adapter';
|
||||
import vite from '@/plugins/vite';
|
||||
import type { FSWatcher } from 'chokidar';
|
||||
import { createCore, findConfigFile } from '@/core';
|
||||
import { _Defaults, createCore } from '@/core';
|
||||
import { createIntegratedConfigLoader } from '@/loaders/config';
|
||||
import { createMetaLoader } from '@/loaders/meta';
|
||||
import { IndexFilePluginOptions } from '@/plugins/index-file';
|
||||
@@ -102,16 +102,13 @@ export default async function mdx(
|
||||
};
|
||||
}
|
||||
|
||||
export async function postInstall(
|
||||
configPath = findConfigFile(),
|
||||
pluginOptions: PluginOptions = {},
|
||||
) {
|
||||
export async function postInstall(pluginOptions: PluginOptions = {}) {
|
||||
const { loadConfig } = await import('@/config/load-from-file');
|
||||
const options = applyDefaults(pluginOptions);
|
||||
const core = await createViteCore(options).init({
|
||||
config: loadConfig(configPath, options.outDir, true),
|
||||
});
|
||||
const core = createViteCore(applyDefaults(pluginOptions));
|
||||
|
||||
await core.init({
|
||||
config: loadConfig(core, true),
|
||||
});
|
||||
await core.emitAndWrite();
|
||||
}
|
||||
|
||||
@@ -130,7 +127,7 @@ function applyDefaults(options: PluginOptions): Required<PluginOptions> {
|
||||
return {
|
||||
updateViteConfig: options.updateViteConfig ?? true,
|
||||
index: options.index ?? true,
|
||||
configPath: options.configPath ?? 'source.config.ts',
|
||||
outDir: options.outDir ?? '.source',
|
||||
configPath: options.configPath ?? _Defaults.configPath,
|
||||
outDir: options.outDir ?? _Defaults.outDir,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Core, createCore } from '@/core';
|
||||
import { type Core, type CoreOptions, createCore } from '@/core';
|
||||
|
||||
export interface WebpackLoaderOptions {
|
||||
configPath: string;
|
||||
outDir: string;
|
||||
export interface WebpackLoaderOptions extends CoreOptions {
|
||||
compiledConfigPath: string;
|
||||
isDev: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,8 @@ export default async function loader(
|
||||
callback: LoaderContext<WebpackLoaderOptions>['callback'],
|
||||
): Promise<void> {
|
||||
const options = this.getOptions();
|
||||
const { configPath, isDev } = options;
|
||||
this.cacheable(true);
|
||||
this.addDependency(configPath);
|
||||
this.addDependency(options.compiledConfigPath);
|
||||
|
||||
if (!instance) {
|
||||
instance = toWebpack(
|
||||
@@ -22,7 +21,7 @@ export default async function loader(
|
||||
createStandaloneConfigLoader({
|
||||
core: getCore(options),
|
||||
buildConfig: false,
|
||||
mode: isDev ? 'dev' : 'production',
|
||||
mode: options.isDev ? 'dev' : 'production',
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -6,23 +6,22 @@ import { getCore, type WebpackLoaderOptions } from '@/webpack';
|
||||
|
||||
let instance: WebpackLoader | undefined;
|
||||
|
||||
// TODO: improve the performance, this loader is also used for other json/yaml files that's not in a Fumadocs MDX collection.
|
||||
export default async function loader(
|
||||
this: LoaderContext<WebpackLoaderOptions>,
|
||||
source: string,
|
||||
callback: LoaderContext<WebpackLoaderOptions>['callback'],
|
||||
): Promise<void> {
|
||||
const { isDev, configPath } = this.getOptions();
|
||||
const options = this.getOptions();
|
||||
this.cacheable(true);
|
||||
this.addDependency(configPath);
|
||||
this.addDependency(options.compiledConfigPath);
|
||||
|
||||
if (!instance) {
|
||||
instance = toWebpack(
|
||||
createMetaLoader(
|
||||
createStandaloneConfigLoader({
|
||||
core: getCore(this.getOptions()),
|
||||
core: getCore(options),
|
||||
buildConfig: false,
|
||||
mode: isDev ? 'dev' : 'production',
|
||||
mode: options.isDev ? 'dev' : 'production',
|
||||
}),
|
||||
{
|
||||
json: 'json',
|
||||
|
||||
Reference in New Issue
Block a user