Skip to content

Commit

Permalink
Support passing config with registration of the plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mburumaxwell committed Mar 5, 2024
1 parent 25beae6 commit 20c8238
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changeset/tidy-houses-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"markdownlayer": minor
---

Support passing config with registration of the plugin which ignores markdownlayer.config.ts
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"copyfiles",
"esast",
"estree",
"frontmatter",
"headlessui",
Expand Down
16 changes: 15 additions & 1 deletion examples/starter/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,18 @@ const nextConfig = {
reactStrictMode: true,
};

export default withMarkdownlayer(nextConfig);
export default withMarkdownlayer(nextConfig); // requires markdownlayer.config.ts

// You can also pass the config object to the `withMarkdownlayer` function (markdownlayer.config.ts will be ignored)
// export default withMarkdownlayer(nextConfig, {
// contentDirPath: './src/content',
// definitions: [
// { type: 'LegalDoc', patterns: 'legal/*.{md,mdoc,mdx}' },

// // blog
// { type: 'BlogPost', patterns: 'blog/posts/*.{md,mdoc,mdx}' },
// { type: 'Changelog', patterns: 'blog/changelog/*.{md,mdoc,mdx}' },
// ],
// remarkPlugins: [],
// rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings, rehypePrettyCode],
// });
2 changes: 2 additions & 0 deletions packages/markdownlayer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"gray-matter": "4.0.3",
"inflection": "3.0.0",
"mdast-util-to-string": "4.0.0",
"object-hash": "3.0.0",
"reading-time": "1.5.0",
"rehype-raw": "7.0.0",
"remark-directive": "3.0.0",
Expand All @@ -72,6 +73,7 @@
"@types/inflection": "1.13.2",
"@types/mdast": "4.0.3",
"@types/mdx": "2.0.11",
"@types/object-hash": "3.0.6",
"@types/shelljs": "0.8.15",
"@types/source-map-support": "0.5.10",
"@types/unist": "3.0.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/markdownlayer/src/core/generation/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function getCompileOptions({ mode, format, plugins }: GetCompileOptionsProps): C
format: format,

// configure recma plugins
recmaPlugins: recmaPlugins,
recmaPlugins: recmaPlugins ?? [],

// configure remark plugins
remarkPlugins: [
Expand All @@ -162,7 +162,7 @@ function getCompileOptions({ mode, format, plugins }: GetCompileOptionsProps): C
],

// configure rehype plugins
rehypePlugins: rehypePlugins,
rehypePlugins: rehypePlugins ?? [],

remarkRehypeOptions: remarkRehypeOptions,
};
Expand Down
28 changes: 27 additions & 1 deletion packages/markdownlayer/src/core/generation/config-file.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,49 @@
import * as esbuild from 'esbuild';
import fs from 'fs';
import hash from 'object-hash';
import path from 'path';

import type { MarkdownlayerConfig } from '../types';
import { ConfigNoDefaultExportError, ConfigReadError, NoConfigFoundError } from './errors';

const possibleConfigFileNames = ['markdownlayer.config.ts', 'markdownlayer.config.js'];

/** Represents the options for getting the configuration. */
export type GetConfigOptions = {
/** The current working directory. */
cwd: string;

/**
* The output folder for the compiled file.
*
* @example /Users/mike/Documents/markdownlayer/examples/starter/.markdownlayer
*/
outputFolder: string;

/** The configuration, if any, that is passed via the NextJS plugin. */
pluginConfig: MarkdownlayerConfig | undefined | null;
};

/** Represents the result of getting the configuration. */
export type GetConfigResult = {
/**
* The hash of the configuration.
* This is used to determine if the configuration has changed.
*/
configHash: string;

/** The configuration. */
config: MarkdownlayerConfig;
};

export async function getConfig({ cwd, outputFolder }: GetConfigOptions): Promise<GetConfigResult> {
export async function getConfig({ cwd, outputFolder, pluginConfig }: GetConfigOptions): Promise<GetConfigResult> {
if (pluginConfig !== null && pluginConfig !== undefined) {
return {
configHash: hash(pluginConfig, {}),
config: pluginConfig,
};
}

let configPath: string | null = null;
for (const name of possibleConfigFileNames) {
configPath = path.join(cwd, name);
Expand Down
2 changes: 1 addition & 1 deletion packages/markdownlayer/src/core/generation/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export class NoConfigFoundError extends Error {
super(
configPath
? `Couldn't find ${configPath}`
: `Could not find markdownlayer.config.ts or markdownlayer.config.js in ${cwd}`,
: `Could not find markdownlayer.config.ts or markdownlayer.config.js in ${cwd}. Create one or pass the config in the 'withMarkdownlayer' function.`,
);
}
}
Expand Down
9 changes: 6 additions & 3 deletions packages/markdownlayer/src/core/generation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,23 @@ export type GenerateOptions = {

/** Current working directory. */
cwd?: string;

/** Plugin configuration. */
pluginConfig: MarkdownlayerConfig | undefined | null;
};

export async function generate(options: GenerateOptions) {
const { mode, cwd = process.cwd() } = options;
const { mode, cwd = process.cwd(), pluginConfig } = options;

// close the watcher if it exists
if (contentWatcher) {
contentWatcher.close();
contentWatcher = null;
}

// get the config (compiled from the config file)
// get the config (provided in the plugin or compiled from the config file)
let outputFolder = path.join(cwd, '.markdownlayer');
const { config, configHash } = await getConfig({ cwd, outputFolder });
const { config, configHash } = await getConfig({ cwd, outputFolder, pluginConfig });

// generate the documents (initial)
await generateInner({ mode, cwd, outputFolder, config, configHash });
Expand Down
29 changes: 27 additions & 2 deletions packages/markdownlayer/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { NextConfig } from 'next';
import webpack from 'webpack';

import { type MarkdownlayerConfig } from './core';
import { runBeforeWebpackCompile } from './plugin';

const devServerStartedRef = { current: false };
Expand All @@ -20,6 +21,12 @@ interface WebpackConfigContext {

/**
* Next.js plugin for markdownlayer.
* @argument nextConfig - The Next.js configuration, if any.
* @argument pluginConfig
* The markdownlayer configuration, if any.
* When provided, markdownlayer.config.ts will be ignored.
* This will become the only supported configuration method
* once next.config.ts is supported in https://github.com/vercel/next.js/pull/57656.
*
* @example
* ```js
Expand All @@ -30,8 +37,23 @@ interface WebpackConfigContext {
* // My Next.js config
* })
* ```
*
* @example
* ```js
* // next.config.mjs
* import { withMarkdownlayer } from 'markdownlayer'
*
* export default withMarkdownlayer({
* // My Next.js config
* }, {
* // My markdownlayer config (will ignore markdownlayer.config.js)
* })
* ```
*/
export function withMarkdownlayer(nextConfig?: Partial<NextConfig>): Partial<NextConfig> {
export function withMarkdownlayer(
nextConfig?: Partial<NextConfig>,
pluginConfig?: MarkdownlayerConfig,
): Partial<NextConfig> {
return {
...nextConfig,
onDemandEntries: {
Expand All @@ -47,7 +69,7 @@ export function withMarkdownlayer(nextConfig?: Partial<NextConfig>): Partial<Nex
ignored: ['**/node_modules/!(.markdownlayer)/**/*'],
};

config.plugins!.push(new MarkdownWebpackPlugin());
config.plugins!.push(new MarkdownWebpackPlugin(pluginConfig));

if (typeof nextConfig?.webpack === 'function') {
return nextConfig.webpack(config, options);
Expand All @@ -59,11 +81,14 @@ export function withMarkdownlayer(nextConfig?: Partial<NextConfig>): Partial<Nex
}

class MarkdownWebpackPlugin {
constructor(private readonly pluginConfig: MarkdownlayerConfig | undefined | null) {}

apply(compiler: webpack.Compiler) {
compiler.hooks.beforeCompile.tapPromise('MarkdownlayerWebpackPlugin', async () => {
await runBeforeWebpackCompile({
devServerStartedRef,
mode: compiler.options.mode,
pluginConfig: this.pluginConfig,
});
});
}
Expand Down
6 changes: 4 additions & 2 deletions packages/markdownlayer/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { WebpackOptionsNormalized } from 'webpack';

import { generate, type GenerateOptions } from '@/core';
import { generate, type GenerateOptions, type MarkdownlayerConfig } from '@/core';

/** Seems like the next.config.js export function might be executed multiple times, so we need to make sure we only run it once */
let markdownInitialized = false;

export async function runBeforeWebpackCompile({
mode,
devServerStartedRef,
pluginConfig,
}: {
mode: WebpackOptionsNormalized['mode'];
devServerStartedRef: { current: boolean };
pluginConfig: MarkdownlayerConfig | undefined | null;
}) {
if (markdownInitialized) return;
markdownInitialized = true;
Expand All @@ -19,7 +21,7 @@ export async function runBeforeWebpackCompile({
const prod = mode === 'production';
if (!dev && !prod) throw new Error(`Unexpected mode: ${mode}`);

const options: GenerateOptions = { mode };
const options: GenerateOptions = { mode, pluginConfig };

if (prod) {
await generate(options);
Expand Down
20 changes: 17 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 20c8238

Please sign in to comment.