From 20c82380416a4b9bd701998d4f8596e9a5d2eb9c Mon Sep 17 00:00:00 2001 From: Maxwell Weru Date: Tue, 5 Mar 2024 07:57:00 +0300 Subject: [PATCH] Support passing config with registration of the plugin --- .changeset/tidy-houses-agree.md | 5 ++++ .vscode/settings.json | 1 + examples/starter/next.config.js | 16 +++++++++- packages/markdownlayer/package.json | 2 ++ .../src/core/generation/bundle.ts | 4 +-- .../src/core/generation/config-file.ts | 28 +++++++++++++++++- .../src/core/generation/errors.ts | 2 +- .../src/core/generation/index.ts | 9 ++++-- packages/markdownlayer/src/index.ts | 29 +++++++++++++++++-- packages/markdownlayer/src/plugin.ts | 6 ++-- pnpm-lock.yaml | 20 +++++++++++-- 11 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 .changeset/tidy-houses-agree.md diff --git a/.changeset/tidy-houses-agree.md b/.changeset/tidy-houses-agree.md new file mode 100644 index 0000000..8c98e0b --- /dev/null +++ b/.changeset/tidy-houses-agree.md @@ -0,0 +1,5 @@ +--- +"markdownlayer": minor +--- + +Support passing config with registration of the plugin which ignores markdownlayer.config.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 9c77f8e..06b3b8e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "cSpell.words": [ "copyfiles", + "esast", "estree", "frontmatter", "headlessui", diff --git a/examples/starter/next.config.js b/examples/starter/next.config.js index e72a23a..acc81a5 100644 --- a/examples/starter/next.config.js +++ b/examples/starter/next.config.js @@ -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], +// }); diff --git a/packages/markdownlayer/package.json b/packages/markdownlayer/package.json index 256a2f5..69fd73b 100644 --- a/packages/markdownlayer/package.json +++ b/packages/markdownlayer/package.json @@ -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", @@ -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", diff --git a/packages/markdownlayer/src/core/generation/bundle.ts b/packages/markdownlayer/src/core/generation/bundle.ts index cf61aa9..b0eba92 100644 --- a/packages/markdownlayer/src/core/generation/bundle.ts +++ b/packages/markdownlayer/src/core/generation/bundle.ts @@ -144,7 +144,7 @@ function getCompileOptions({ mode, format, plugins }: GetCompileOptionsProps): C format: format, // configure recma plugins - recmaPlugins: recmaPlugins, + recmaPlugins: recmaPlugins ?? [], // configure remark plugins remarkPlugins: [ @@ -162,7 +162,7 @@ function getCompileOptions({ mode, format, plugins }: GetCompileOptionsProps): C ], // configure rehype plugins - rehypePlugins: rehypePlugins, + rehypePlugins: rehypePlugins ?? [], remarkRehypeOptions: remarkRehypeOptions, }; diff --git a/packages/markdownlayer/src/core/generation/config-file.ts b/packages/markdownlayer/src/core/generation/config-file.ts index a8b7797..51c7878 100644 --- a/packages/markdownlayer/src/core/generation/config-file.ts +++ b/packages/markdownlayer/src/core/generation/config-file.ts @@ -1,5 +1,6 @@ import * as esbuild from 'esbuild'; import fs from 'fs'; +import hash from 'object-hash'; import path from 'path'; import type { MarkdownlayerConfig } from '../types'; @@ -7,17 +8,42 @@ import { ConfigNoDefaultExportError, ConfigReadError, NoConfigFoundError } from 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 { +export async function getConfig({ cwd, outputFolder, pluginConfig }: GetConfigOptions): Promise { + 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); diff --git a/packages/markdownlayer/src/core/generation/errors.ts b/packages/markdownlayer/src/core/generation/errors.ts index b16b646..70bfa9a 100644 --- a/packages/markdownlayer/src/core/generation/errors.ts +++ b/packages/markdownlayer/src/core/generation/errors.ts @@ -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.`, ); } } diff --git a/packages/markdownlayer/src/core/generation/index.ts b/packages/markdownlayer/src/core/generation/index.ts index 06f5c39..4d21e73 100644 --- a/packages/markdownlayer/src/core/generation/index.ts +++ b/packages/markdownlayer/src/core/generation/index.ts @@ -39,10 +39,13 @@ 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) { @@ -50,9 +53,9 @@ export async function generate(options: GenerateOptions) { 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 }); diff --git a/packages/markdownlayer/src/index.ts b/packages/markdownlayer/src/index.ts index e7010d5..78b32b9 100644 --- a/packages/markdownlayer/src/index.ts +++ b/packages/markdownlayer/src/index.ts @@ -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 }; @@ -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 @@ -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): Partial { +export function withMarkdownlayer( + nextConfig?: Partial, + pluginConfig?: MarkdownlayerConfig, +): Partial { return { ...nextConfig, onDemandEntries: { @@ -47,7 +69,7 @@ export function withMarkdownlayer(nextConfig?: Partial): Partial): Partial { await runBeforeWebpackCompile({ devServerStartedRef, mode: compiler.options.mode, + pluginConfig: this.pluginConfig, }); }); } diff --git a/packages/markdownlayer/src/plugin.ts b/packages/markdownlayer/src/plugin.ts index 942d112..56456fa 100644 --- a/packages/markdownlayer/src/plugin.ts +++ b/packages/markdownlayer/src/plugin.ts @@ -1,6 +1,6 @@ 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; @@ -8,9 +8,11 @@ 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; @@ -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); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e3c743..b735032 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,7 +128,7 @@ importers: version: 2.1.2 '@markdoc/markdoc': specifier: 0.4.0 - version: 0.4.0(@types/react@18.2.62)(react@18.2.0) + version: 0.4.0(@types/react@18.2.62) '@mdx-js/esbuild': specifier: 3.0.1 version: 3.0.1(esbuild@0.20.1) @@ -159,6 +159,9 @@ importers: next: specifier: 13 || 14 version: 14.1.1(react-dom@18.2.0)(react@18.2.0) + object-hash: + specifier: 3.0.0 + version: 3.0.0 react: specifier: '18' version: 18.2.0 @@ -208,6 +211,9 @@ importers: '@types/mdx': specifier: 2.0.11 version: 2.0.11 + '@types/object-hash': + specifier: 3.0.6 + version: 3.0.6 '@types/shelljs': specifier: 0.8.15 version: 0.8.15 @@ -1007,7 +1013,7 @@ packages: read-yaml-file: 1.1.0 dev: true - /@markdoc/markdoc@0.4.0(@types/react@18.2.62)(react@18.2.0): + /@markdoc/markdoc@0.4.0(@types/react@18.2.62): resolution: {integrity: sha512-fSh4P3Y4E7oaKYc2oNzSIJVPDto7SMzAuQN1Iyx53UxzleA6QzRdNWRxmiPqtVDaDi5dELd2yICoG91csrGrAw==} engines: {node: '>=14.7.0'} peerDependencies: @@ -1020,7 +1026,6 @@ packages: optional: true dependencies: '@types/react': 18.2.62 - react: 18.2.0 optionalDependencies: '@types/markdown-it': 12.2.3 dev: false @@ -1449,6 +1454,10 @@ packages: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: true + /@types/object-hash@3.0.6: + resolution: {integrity: sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w==} + dev: true + /@types/prop-types@15.7.11: resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} @@ -5072,6 +5081,11 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false + /object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: true