diff --git a/packages/@contentlayer/cli/src/commands/BuildCommand.ts b/packages/@contentlayer/cli/src/commands/BuildCommand.ts index 40391cac..3f4b4c3d 100644 --- a/packages/@contentlayer/cli/src/commands/BuildCommand.ts +++ b/packages/@contentlayer/cli/src/commands/BuildCommand.ts @@ -21,7 +21,7 @@ export class BuildCommand extends BaseCommand { executeSafe = () => pipe( this.clearCacheIfNeeded(), - T.chain(() => core.getConfig({ configPath: this.configPath })), + T.chain(() => core.getConfig({ configPath: this.configPath, esbuildOptions: { external: this.external } })), T.tap((config) => (config.source.options.disableImportAliasWarning ? T.unit : T.fork(core.validateTsconfig))), T.chain((config) => core.generateDotpkg({ config, verbose: this.verbose })), T.tap(core.logGenerateInfo), diff --git a/packages/@contentlayer/cli/src/commands/DevCommand.ts b/packages/@contentlayer/cli/src/commands/DevCommand.ts index c6fc17ad..45067f63 100644 --- a/packages/@contentlayer/cli/src/commands/DevCommand.ts +++ b/packages/@contentlayer/cli/src/commands/DevCommand.ts @@ -22,7 +22,7 @@ export class DevCommand extends BaseCommand { executeSafe = () => pipe( S.fromEffect(this.clearCacheIfNeeded()), - S.chain(() => core.getConfigWatch({ configPath: this.configPath })), + S.chain(() => core.getConfigWatch({ configPath: this.configPath, esbuildOptions: { external: this.external } })), S.tapSkipFirstRight(() => T.log(`Contentlayer config change detected. Updating type definitions and data...`)), S.tapRight((config) => config.source.options.disableImportAliasWarning ? T.unit : T.fork(core.validateTsconfig), diff --git a/packages/@contentlayer/cli/src/commands/PostInstallCommand.ts b/packages/@contentlayer/cli/src/commands/PostInstallCommand.ts index 4d63b8c6..4bcc1aa3 100644 --- a/packages/@contentlayer/cli/src/commands/PostInstallCommand.ts +++ b/packages/@contentlayer/cli/src/commands/PostInstallCommand.ts @@ -10,12 +10,18 @@ export class PostInstallCommand extends BaseCommand { static paths = [['postinstall']] executeSafe = () => { - const { configPath } = this + const { configPath, external } = this return pipe( T.gen(function* ($) { const artifactsDirPath = yield* $(core.ArtifactsDir.mkdir) - yield* $(generateTypes({ artifactsDirPath, moduleName: 'generated', configPath })) + yield* $( + generateTypes({ + artifactsDirPath, + moduleName: 'generated', + configOptions: { configPath, esbuildOptions: { external } }, + }), + ) yield* $(addToplevelDotpkgToGitignore()) }), @@ -30,11 +36,11 @@ export class PostInstallCommand extends BaseCommand { const generateTypes = ({ artifactsDirPath, moduleName, - configPath, + configOptions, }: { artifactsDirPath: string moduleName: string - configPath?: string + configOptions: Parameters[0] }) => T.gen(function* ($) { const dirPath = path.join(artifactsDirPath, moduleName) @@ -51,7 +57,7 @@ const generateTypes = ({ if (indexDtsFileExists && typesDtsFileExists) return - const sourceEither = yield* $(pipe(core.getConfig({ configPath }), T.either)) + const sourceEither = yield* $(pipe(core.getConfig(configOptions), T.either)) if (sourceEither._tag === 'Left') { if (sourceEither.left._tag === 'NoConfigFoundError') { yield* $(fs.writeFile(indexDtsFilePath, moduleStubFileIndexDts)) diff --git a/packages/@contentlayer/cli/src/commands/_BaseCommand.ts b/packages/@contentlayer/cli/src/commands/_BaseCommand.ts index 13c69687..cf8c4761 100644 --- a/packages/@contentlayer/cli/src/commands/_BaseCommand.ts +++ b/packages/@contentlayer/cli/src/commands/_BaseCommand.ts @@ -21,6 +21,12 @@ export abstract class BaseCommand extends Command { description: 'More verbose logging and error stack traces', }) + external = Option.String('--external', { + description: 'External dependencies to exclude from the config bundle', + validator: t.isArray(t.isString()), + required: false, + }) + abstract executeSafe: () => T.Effect execute = (): Promise => diff --git a/packages/@contentlayer/core/src/getConfig/esbuild.ts b/packages/@contentlayer/core/src/getConfig/esbuild.ts index 30545071..4e3be1a8 100644 --- a/packages/@contentlayer/core/src/getConfig/esbuild.ts +++ b/packages/@contentlayer/core/src/getConfig/esbuild.ts @@ -11,6 +11,7 @@ export abstract class EsbuildWatcher { export type BuildResult = esbuild.BuildResult export type Plugin = esbuild.Plugin +export type BuildOptions = esbuild.BuildOptions export type EsbuildError = UnknownEsbuildError | KnownEsbuildError diff --git a/packages/@contentlayer/core/src/getConfig/index.ts b/packages/@contentlayer/core/src/getConfig/index.ts index 59e1eb0c..214fe2da 100644 --- a/packages/@contentlayer/core/src/getConfig/index.ts +++ b/packages/@contentlayer/core/src/getConfig/index.ts @@ -34,12 +34,14 @@ export type Config = { export const getConfig = ({ configPath, + esbuildOptions, }: { /** Contentlayer config source path */ configPath?: string + esbuildOptions?: esbuild.BuildOptions }): T.Effect => pipe( - getConfigWatch({ configPath }), + getConfigWatch({ configPath, esbuildOptions }), S.take(1), S.runCollect, T.map(Chunk.unsafeHead), @@ -49,8 +51,10 @@ export const getConfig = ({ export const getConfigWatch = ({ configPath: configPath_, + esbuildOptions, }: { configPath?: string + esbuildOptions?: esbuild.BuildOptions }): S.Stream> => { const resolveParams = pipe( T.structPar({ @@ -66,6 +70,7 @@ export const getConfigWatch = ({ S.chainMapEitherRight(({ configPath, outfilePath, cwd }) => pipe( esbuild.makeAndSubscribe({ + ...esbuildOptions, entryPoints: [configPath], entryNames: '[name]-[hash]', outfile: outfilePath, diff --git a/packages/contentlayer-stackbit-yaml-generator/src/cli/DefaultCommand.ts b/packages/contentlayer-stackbit-yaml-generator/src/cli/DefaultCommand.ts index 66a3dd24..425c2c00 100644 --- a/packages/contentlayer-stackbit-yaml-generator/src/cli/DefaultCommand.ts +++ b/packages/contentlayer-stackbit-yaml-generator/src/cli/DefaultCommand.ts @@ -35,6 +35,12 @@ export class DefaultCommand extends Command { validator: t.isString(), }) + external = Option.String('--external', { + description: 'External dependencies to exclude from the config bundle', + validator: t.isArray(t.isString()), + required: false, + }) + // TODO refactor similar to `@contentlayer2/cli` async execute() { try { @@ -55,7 +61,7 @@ export class DefaultCommand extends Command { executeSafe = (): T.Effect => pipe( - getConfig({ configPath: this.configPath }), + getConfig({ configPath: this.configPath, esbuildOptions: { external: this.external } }), T.chain((config) => T.struct({ source: T.succeed(config.source), schema: config.source.provideSchema(config.esbuildHash) }), ), diff --git a/packages/next-contentlayer/src/plugin.ts b/packages/next-contentlayer/src/plugin.ts index 81f29458..d77dfd43 100644 --- a/packages/next-contentlayer/src/plugin.ts +++ b/packages/next-contentlayer/src/plugin.ts @@ -3,23 +3,25 @@ import '@contentlayer2/utils/effect/Tracing/Enable' import * as core from '@contentlayer2/core' import { errorToString } from '@contentlayer2/utils' import { E, OT, pipe, S, T } from '@contentlayer2/utils/effect' +import type * as esbuild from 'esbuild' import type { WebpackOptionsNormalized } from 'webpack' import { checkConstraints } from './check-constraints.js' export type NextPluginOptions = { configPath?: string | undefined + esbuildOptions?: Pick | undefined } /** 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 contentlayerInitialized = false -const runContentlayerDev = async ({ configPath }: NextPluginOptions) => { +const runContentlayerDev = async ({ configPath, esbuildOptions }: NextPluginOptions) => { if (contentlayerInitialized) return contentlayerInitialized = true await pipe( - core.getConfigWatch({ configPath }), + core.getConfigWatch({ configPath, esbuildOptions }), S.tapSkipFirstRight(() => T.log(`Contentlayer config change detected. Updating type definitions and data...`)), S.tapRight((config) => (config.source.options.disableImportAliasWarning ? T.unit : T.fork(core.validateTsconfig))), S.chainSwitchMapEitherRight((config) => core.generateDotpkgStream({ config, verbose: false, isDev: true })), @@ -29,12 +31,12 @@ const runContentlayerDev = async ({ configPath }: NextPluginOptions) => { ) } -const runContentlayerBuild = async ({ configPath }: NextPluginOptions) => { +const runContentlayerBuild = async ({ configPath, esbuildOptions }: NextPluginOptions) => { if (contentlayerInitialized) return contentlayerInitialized = true await pipe( - core.getConfig({ configPath }), + core.getConfig({ configPath, esbuildOptions }), T.chain((config) => core.generateDotpkg({ config, verbose: false })), T.tap(core.logGenerateInfo), OT.withSpan('next-contentlayer:runContentlayerBuild'), @@ -56,14 +58,14 @@ export const runBeforeWebpackCompile = async ({ const isNextDev = mode === 'development' const isBuild = mode === 'production' - const { configPath } = pluginOptions + const { configPath, esbuildOptions } = pluginOptions if (isBuild) { checkConstraints() - await runContentlayerBuild({ configPath }) + await runContentlayerBuild({ configPath, esbuildOptions }) } else if (isNextDev && !devServerStartedRef.current) { devServerStartedRef.current = true // TODO also block here until first Contentlayer run is complete - runContentlayerDev({ configPath }) + runContentlayerDev({ configPath, esbuildOptions }) } }