From 25d1ceaa449f2309fe6fd2d8652f94520d16e75b Mon Sep 17 00:00:00 2001 From: Josh <37798644+joshuaellis@users.noreply.github.com> Date: Mon, 16 Oct 2023 08:55:43 +0100 Subject: [PATCH] feat(pack-up): enable config via nodeAPI & allow bundles to have own tsconfig (#18440) * feat: add ability to pass config & have tsconfig per bundle * feat: use node:module builtinModules for node stuff * chore: remove builtins from externals --- .../cli/create-strapi-app/packup.config.ts | 1 - .../create-strapi-starter/packup.config.ts | 1 - packages/core/data-transfer/packup.config.ts | 1 - packages/core/database/packup.config.ts | 7 ---- packages/core/utils/packup.config.ts | 1 - packages/generators/app/packup.config.ts | 1 - .../generators/generators/packup.config.ts | 1 - .../providers/upload-local/packup.config.ts | 1 - packages/utils/pack-up/packup.config.ts | 2 +- .../utils/pack-up/src/cli/commands/build.ts | 4 +- .../utils/pack-up/src/cli/commands/watch.ts | 4 +- packages/utils/pack-up/src/index.ts | 8 +++- packages/utils/pack-up/src/node/build.ts | 42 +++++++++++++++---- .../utils/pack-up/src/node/core/config.ts | 9 +++- .../pack-up/src/node/createBuildContext.ts | 2 +- .../utils/pack-up/src/node/createTasks.ts | 1 + .../utils/pack-up/src/node/tasks/dts/build.ts | 16 ++++++- .../utils/pack-up/src/node/tasks/dts/types.ts | 6 +++ .../utils/pack-up/src/node/tasks/dts/watch.ts | 16 ++++++- .../pack-up/src/node/tasks/vite/config.ts | 11 ++++- packages/utils/pack-up/src/node/watch.ts | 30 ++++++++++--- 21 files changed, 126 insertions(+), 39 deletions(-) diff --git a/packages/cli/create-strapi-app/packup.config.ts b/packages/cli/create-strapi-app/packup.config.ts index c98817ddbe..edfa961f40 100644 --- a/packages/cli/create-strapi-app/packup.config.ts +++ b/packages/cli/create-strapi-app/packup.config.ts @@ -10,6 +10,5 @@ export default defineConfig({ }, ], dist: './dist', - externals: ['node:fs', 'node:path'], runtime: 'node', }); diff --git a/packages/cli/create-strapi-starter/packup.config.ts b/packages/cli/create-strapi-starter/packup.config.ts index 1e29bb658d..55f9eba04f 100644 --- a/packages/cli/create-strapi-starter/packup.config.ts +++ b/packages/cli/create-strapi-starter/packup.config.ts @@ -10,6 +10,5 @@ export default defineConfig({ }, ], dist: './dist', - externals: ['child_process', 'node:fs', 'node:path', 'os', 'path'], runtime: 'node', }); diff --git a/packages/core/data-transfer/packup.config.ts b/packages/core/data-transfer/packup.config.ts index 614d5be834..95e06b2131 100644 --- a/packages/core/data-transfer/packup.config.ts +++ b/packages/core/data-transfer/packup.config.ts @@ -3,5 +3,4 @@ import { defineConfig } from '@strapi/pack-up'; export default defineConfig({ runtime: 'node', - externals: ['crypto', 'http', 'https', 'os', 'path', 'stream', 'zlib'], }); diff --git a/packages/core/database/packup.config.ts b/packages/core/database/packup.config.ts index 6880e66607..17ef45d477 100644 --- a/packages/core/database/packup.config.ts +++ b/packages/core/database/packup.config.ts @@ -3,13 +3,6 @@ import { defineConfig } from '@strapi/pack-up'; export default defineConfig({ externals: [ - 'crypto', - 'node:async_hooks', - 'node:path', - 'path', - 'stream', - 'timers', - 'tty', /** * Knex dependencies, if we don't mark these as external * they will be included in the bundle which means they diff --git a/packages/core/utils/packup.config.ts b/packages/core/utils/packup.config.ts index 4be00a28bd..95e06b2131 100644 --- a/packages/core/utils/packup.config.ts +++ b/packages/core/utils/packup.config.ts @@ -2,6 +2,5 @@ import { defineConfig } from '@strapi/pack-up'; export default defineConfig({ - externals: ['node:stream'], runtime: 'node', }); diff --git a/packages/generators/app/packup.config.ts b/packages/generators/app/packup.config.ts index 354dbbc23e..5d3926ca5b 100644 --- a/packages/generators/app/packup.config.ts +++ b/packages/generators/app/packup.config.ts @@ -2,7 +2,6 @@ import { defineConfig } from '@strapi/pack-up'; export default defineConfig({ - externals: ['crypto', 'fs', 'node:fs', 'node:os', 'node:path', 'node:readline', 'os', 'path'], preserveModules: true, runtime: 'node', }); diff --git a/packages/generators/generators/packup.config.ts b/packages/generators/generators/packup.config.ts index 0454bba8fb..69949dba8b 100644 --- a/packages/generators/generators/packup.config.ts +++ b/packages/generators/generators/packup.config.ts @@ -9,6 +9,5 @@ export default defineConfig({ import: './dist/plopfile.mjs', }, ], - externals: ['node:path', 'path'], runtime: 'node', }); diff --git a/packages/providers/upload-local/packup.config.ts b/packages/providers/upload-local/packup.config.ts index b2b7fcdd87..95e06b2131 100644 --- a/packages/providers/upload-local/packup.config.ts +++ b/packages/providers/upload-local/packup.config.ts @@ -2,6 +2,5 @@ import { defineConfig } from '@strapi/pack-up'; export default defineConfig({ - externals: ['stream', 'fs', 'path'], runtime: 'node', }); diff --git a/packages/utils/pack-up/packup.config.ts b/packages/utils/pack-up/packup.config.ts index a60d9921ce..9fbcf654d0 100644 --- a/packages/utils/pack-up/packup.config.ts +++ b/packages/utils/pack-up/packup.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ require: './dist/cli.js', }, ], - externals: ['fs', 'path', 'child_process', 'fs/promises', 'module', 'os'], + externals: ['node:module'], runtime: 'node', minify: false, sourcemap: true, diff --git a/packages/utils/pack-up/src/cli/commands/build.ts b/packages/utils/pack-up/src/cli/commands/build.ts index c3d993acc0..3e5796bb25 100644 --- a/packages/utils/pack-up/src/cli/commands/build.ts +++ b/packages/utils/pack-up/src/cli/commands/build.ts @@ -1,7 +1,7 @@ -import { build as nodeBuild, BuildOptions } from '../../node/build'; +import { build as nodeBuild, BuildCLIOptions } from '../../node/build'; import { handleError } from '../errors'; -export const build = async (options: Omit) => { +export const build = async (options: BuildCLIOptions) => { try { await nodeBuild(options); } catch (err) { diff --git a/packages/utils/pack-up/src/cli/commands/watch.ts b/packages/utils/pack-up/src/cli/commands/watch.ts index b49ab2cce6..ddc1773c0d 100644 --- a/packages/utils/pack-up/src/cli/commands/watch.ts +++ b/packages/utils/pack-up/src/cli/commands/watch.ts @@ -1,7 +1,7 @@ -import { WatchOptions, watch as nodeWatch } from '../../node/watch'; +import { WatchCLIOptions, watch as nodeWatch } from '../../node/watch'; import { handleError } from '../errors'; -export const watch = async (options: WatchOptions) => { +export const watch = async (options: WatchCLIOptions) => { try { await nodeWatch(options); } catch (err) { diff --git a/packages/utils/pack-up/src/index.ts b/packages/utils/pack-up/src/index.ts index 26d740aba1..31da30ae76 100644 --- a/packages/utils/pack-up/src/index.ts +++ b/packages/utils/pack-up/src/index.ts @@ -4,7 +4,13 @@ export * from './node/check'; export * from './node/init'; export { defineConfig } from './node/core/config'; -export type { Config } from './node/core/config'; +export type { + Config, + ConfigBundle, + ConfigOptions, + ConfigProperty, + ConfigPropertyResolver, +} from './node/core/config'; export { defineTemplate, definePackageFeature, definePackageOption } from './node/templates/create'; export type { diff --git a/packages/utils/pack-up/src/node/build.ts b/packages/utils/pack-up/src/node/build.ts index b1f71201eb..1386c86304 100644 --- a/packages/utils/pack-up/src/node/build.ts +++ b/packages/utils/pack-up/src/node/build.ts @@ -4,7 +4,7 @@ import os from 'os'; import { CommonCLIOptions } from '../types'; -import { loadConfig } from './core/config'; +import { loadConfig, type Config } from './core/config'; import { isError } from './core/errors'; import { getExportExtensionMap, validateExportsOrdering } from './core/exports'; import { createLogger } from './core/logger'; @@ -13,14 +13,34 @@ import { createBuildContext } from './createBuildContext'; import { BuildTask, createBuildTasks } from './createTasks'; import { TaskHandler, taskHandlers } from './tasks'; -export interface BuildOptions extends CommonCLIOptions { - cwd?: string; +interface BuildCLIOptions extends CommonCLIOptions { minify?: boolean; sourcemap?: boolean; } -export const build = async (opts: BuildOptions = {}) => { - const { silent, debug, cwd = process.cwd(), ...configOptions } = opts; +interface BuildWithConfigFile extends BuildCLIOptions { + configFile?: true; + config?: never; + cwd?: string; +} + +interface BuildWithoutConfigFile extends BuildCLIOptions { + configFile: false; + config?: Config; + cwd?: string; +} + +type BuildOptions = BuildWithConfigFile | BuildWithoutConfigFile; + +const build = async (opts: BuildOptions = {}) => { + const { + silent, + debug, + cwd = process.cwd(), + configFile = true, + config: providedConfig, + ...configOptions + } = opts; const logger = createLogger({ silent, debug }); @@ -66,13 +86,18 @@ export const build = async (opts: BuildOptions = {}) => { packageJsonLoader.succeed('Verified package.json'); + /** + * If configFile is true – which is the default, atempt to load the config + * otherwise if it's explicitly false then we suspect there might be a config passed + * in the options, so we'll use that instead. + */ + const config = configFile ? await loadConfig({ cwd, logger }) : providedConfig; + /** * We create tasks based on the exports of the package.json * their handlers are then ran in the order of the exports map * and results are logged to see gradual progress. */ - const config = await loadConfig({ cwd, logger }); - const buildContextLoader = ora(`Creating build context ${os.EOL}`).start(); const extMap = getExportExtensionMap(); @@ -126,3 +151,6 @@ export const build = async (opts: BuildOptions = {}) => { }); } }; + +export { build }; +export type { BuildOptions, BuildCLIOptions, BuildWithConfigFile, BuildWithoutConfigFile }; diff --git a/packages/utils/pack-up/src/node/core/config.ts b/packages/utils/pack-up/src/node/core/config.ts index 5b0d9040be..f1e62973d7 100644 --- a/packages/utils/pack-up/src/node/core/config.ts +++ b/packages/utils/pack-up/src/node/core/config.ts @@ -70,6 +70,7 @@ interface ConfigBundle { import?: string; require?: string; runtime?: Runtime; + tsconfig?: string; types?: string; } @@ -99,6 +100,12 @@ interface ConfigOptions { preserveModules?: boolean; sourcemap?: boolean; runtime?: Runtime; + /** + * @description path to the tsconfig file to use for the bundle. + * + * @default tsconfig.build.json + */ + tsconfig?: string; } /** @@ -128,4 +135,4 @@ export function resolveConfigProperty(prop: ConfigProperty | undefined, in } export { loadConfig, defineConfig, CONFIG_FILE_NAMES }; -export type { Config }; +export type { Config, ConfigOptions, ConfigBundle, ConfigPropertyResolver, ConfigProperty }; diff --git a/packages/utils/pack-up/src/node/createBuildContext.ts b/packages/utils/pack-up/src/node/createBuildContext.ts index 460688395d..04eb2ebbca 100644 --- a/packages/utils/pack-up/src/node/createBuildContext.ts +++ b/packages/utils/pack-up/src/node/createBuildContext.ts @@ -65,7 +65,7 @@ const createBuildContext = async ({ }: BuildContextArgs): Promise => { const tsConfig = loadTsConfig({ cwd, - path: 'tsconfig.build.json', + path: resolveConfigProperty(config.tsconfig, 'tsconfig.build.json'), logger, }); diff --git a/packages/utils/pack-up/src/node/createTasks.ts b/packages/utils/pack-up/src/node/createTasks.ts index e7b317e4ee..90911ddd83 100644 --- a/packages/utils/pack-up/src/node/createTasks.ts +++ b/packages/utils/pack-up/src/node/createTasks.ts @@ -143,6 +143,7 @@ const createTasks = exportPath: bundle.source, sourcePath: bundle.source, targetPath: bundle.types, + tsconfig: bundle.tsconfig, }); } } diff --git a/packages/utils/pack-up/src/node/tasks/dts/build.ts b/packages/utils/pack-up/src/node/tasks/dts/build.ts index 1dbe401213..89f7e6437d 100644 --- a/packages/utils/pack-up/src/node/tasks/dts/build.ts +++ b/packages/utils/pack-up/src/node/tasks/dts/build.ts @@ -4,6 +4,7 @@ import { Observable } from 'rxjs'; import ts from 'typescript'; import { isError } from '../../core/errors'; +import { loadTsConfig } from '../../core/tsconfig'; import { printDiagnostic } from './diagnostic'; import { DtsBaseTask } from './types'; @@ -33,7 +34,18 @@ const dtsBuildTask: TaskHandler = { return new Observable((subscriber) => { Promise.all( task.entries.map(async (entry) => { - if (!ctx.ts) { + /** + * Entry level tsconfig's take precedence + */ + const tsconfig = entry.tsconfig + ? loadTsConfig({ + cwd: ctx.cwd, + path: entry.tsconfig, + logger: ctx.logger, + }) + : ctx.ts; + + if (!tsconfig) { ctx.logger.warn( `You've added a types entry but no tsconfig.json was found for ${entry.targetPath}. Skipping...` ); @@ -41,7 +53,7 @@ const dtsBuildTask: TaskHandler = { return; } - const program = ts.createProgram(ctx.ts.config.fileNames, ctx.ts.config.options); + const program = ts.createProgram(tsconfig.config.fileNames, tsconfig.config.options); const emitResult = program.emit(); diff --git a/packages/utils/pack-up/src/node/tasks/dts/types.ts b/packages/utils/pack-up/src/node/tasks/dts/types.ts index 119848b4bc..7eea2a9f29 100644 --- a/packages/utils/pack-up/src/node/tasks/dts/types.ts +++ b/packages/utils/pack-up/src/node/tasks/dts/types.ts @@ -3,6 +3,12 @@ interface DtsTaskEntry { exportPath: string; sourcePath: string; targetPath: string; + /** + * Allow a particular task to have it's own tsconfig + * great for when you're creating a server & web bundle + * package. + */ + tsconfig?: string; } interface DtsBaseTask { diff --git a/packages/utils/pack-up/src/node/tasks/dts/watch.ts b/packages/utils/pack-up/src/node/tasks/dts/watch.ts index 16b6e83d6b..5d04c3bff1 100644 --- a/packages/utils/pack-up/src/node/tasks/dts/watch.ts +++ b/packages/utils/pack-up/src/node/tasks/dts/watch.ts @@ -4,6 +4,7 @@ import { Observable } from 'rxjs'; import ts from 'typescript'; import { isError } from '../../core/errors'; +import { loadTsConfig } from '../../core/tsconfig'; import { printDiagnostic } from './diagnostic'; import { DtsBaseTask } from './types'; @@ -36,7 +37,18 @@ const dtsWatchTask: TaskHandler = { return new Observable((subscriber) => { Promise.all( task.entries.map(async (entry) => { - if (!ctx.ts) { + /** + * Entry level tsconfig's take precedence + */ + const tsconfig = entry.tsconfig + ? loadTsConfig({ + cwd: ctx.cwd, + path: entry.tsconfig, + logger: ctx.logger, + }) + : ctx.ts; + + if (!tsconfig) { ctx.logger.warn( `You've added a types entry but no tsconfig.json was found for ${entry.targetPath}. Skipping...` ); @@ -46,7 +58,7 @@ const dtsWatchTask: TaskHandler = { const compilerHost = ts.createWatchCompilerHost( 'tsconfig.build.json', - ctx.ts.config.options, + tsconfig.config.options, ts.sys, ts.createEmitAndSemanticDiagnosticsBuilderProgram, (diagnostic) => { diff --git a/packages/utils/pack-up/src/node/tasks/vite/config.ts b/packages/utils/pack-up/src/node/tasks/vite/config.ts index a58607d147..9b5dc0ea04 100644 --- a/packages/utils/pack-up/src/node/tasks/vite/config.ts +++ b/packages/utils/pack-up/src/node/tasks/vite/config.ts @@ -1,4 +1,5 @@ import react from '@vitejs/plugin-react'; +import { builtinModules } from 'node:module'; import path from 'path'; import { InlineConfig, createLogger } from 'vite'; @@ -82,7 +83,15 @@ const resolveViteConfig = (ctx: BuildContext, task: ViteBaseTask) => { const name = idParts[0].startsWith('@') ? `${idParts[0]}/${idParts[1]}` : idParts[0]; - if (name && external.includes(name)) { + const builtinModulesWithNodePrefix = [ + ...builtinModules, + ...builtinModules.map((modName) => `node:${modName}`), + ]; + + if ( + (name && external.includes(name)) || + (name && builtinModulesWithNodePrefix.includes(name)) + ) { return true; } diff --git a/packages/utils/pack-up/src/node/watch.ts b/packages/utils/pack-up/src/node/watch.ts index 592f50234a..2e426a5274 100644 --- a/packages/utils/pack-up/src/node/watch.ts +++ b/packages/utils/pack-up/src/node/watch.ts @@ -4,7 +4,7 @@ import { Observable, distinctUntilChanged, scan, startWith, switchMap } from 'rx import { CommonCLIOptions } from '../types'; -import { CONFIG_FILE_NAMES, loadConfig } from './core/config'; +import { CONFIG_FILE_NAMES, Config, loadConfig } from './core/config'; import { getExportExtensionMap, validateExportsOrdering } from './core/exports'; import { createLogger } from './core/logger'; import { loadPkg, validatePkg } from './core/pkg'; @@ -12,12 +12,24 @@ import { createBuildContext } from './createBuildContext'; import { WatchTask, createWatchTasks } from './createTasks'; import { TaskHandler, taskHandlers } from './tasks'; -export interface WatchOptions extends CommonCLIOptions { +interface WatchCLIOptions extends CommonCLIOptions {} + +interface WatchOptionsWithoutConfig extends WatchCLIOptions { + configFile?: true; + config?: never; cwd?: string; } -export const watch = async (opts: WatchOptions) => { - const { silent, debug, cwd = process.cwd() } = opts; +interface WatchOptionsWithConfig extends WatchCLIOptions { + configFile: false; + config?: Config; + cwd?: string; +} + +type WatchOptions = WatchOptionsWithConfig | WatchOptionsWithoutConfig; + +const watch = async (opts: WatchOptions) => { + const { silent, debug, cwd = process.cwd(), configFile = true, config: providedConfig } = opts; const logger = createLogger({ silent, debug }); @@ -111,12 +123,17 @@ export const watch = async (opts: WatchOptions) => { } ); + /** + * If configFile is true – which is the default, atempt to load the config + * otherwise if it's explicitly false then we suspect there might be a config passed + * in the options, so we'll use that instead. + */ + const config = configFile ? await loadConfig({ cwd, logger }) : providedConfig; /** * We create tasks based on the exports of the package.json * their handlers are then ran in the order of the exports map * and results are logged to see gradual progress. */ - const config = await loadConfig({ cwd, logger }); const extMap = getExportExtensionMap(); @@ -154,3 +171,6 @@ export const watch = async (opts: WatchOptions) => { } }); }; + +export { watch }; +export type { WatchOptions, WatchOptionsWithConfig, WatchOptionsWithoutConfig, WatchCLIOptions };