Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions packages/core/bin/rslib.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,8 @@ if (enableCompileCache) {
}

async function main() {
const { logger, prepareCli, runCli } = await import('../dist/index.js');
prepareCli();

try {
runCli();
} catch (err) {
logger.error(err);
}
const { runCLI } = await import('../dist/index.js');
runCLI();
}

main();
37 changes: 0 additions & 37 deletions packages/core/src/cli/build.ts

This file was deleted.

76 changes: 36 additions & 40 deletions packages/core/src/cli/commands.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { LogLevel, RsbuildMode, RsbuildPlugin } from '@rsbuild/core';
import type { LogLevel, RsbuildMode } from '@rsbuild/core';
import cac, { type CAC } from 'cac';
import type { ConfigLoader } from '../config';
import type { Format, Syntax } from '../types/config';
import type { ConfigLoader } from '../loadConfig';
import { watchFilesForRestart } from '../restart';
import type { Format, Syntax } from '../types';
import { color } from '../utils/color';
import { logger } from '../utils/logger';
import { build } from './build';
import { initConfig } from './initConfig';
import { inspect } from './inspect';
import { startMFDevServer } from './mf';
import { watchFilesForRestart } from './restart';
import { init } from './init';

export const RSPACK_BUILD_ERROR = 'Rspack build failed.';

export type CommonOptions = {
root?: string;
config?: string;
configLoader?: ConfigLoader;
env?: boolean;
envDir?: string;
envMode?: string;
lib?: string[];
configLoader?: ConfigLoader;
logLevel?: LogLevel;
};

Expand Down Expand Up @@ -51,6 +51,13 @@ const applyCommonOptions = (cli: CAC) => {
'-c, --config <config>',
'specify the configuration file, can be a relative or absolute path',
)
.option(
'--config-loader <loader>',
'Set the config file loader (auto | jiti | native)',
{
default: 'auto',
},
)
.option(
'-r, --root <root>',
'specify the project root directory, can be an absolute path or a path relative to cwd',
Expand All @@ -59,13 +66,6 @@ const applyCommonOptions = (cli: CAC) => {
'--env-mode <mode>',
'specify the env mode to load the `.env.[mode]` file',
)
.option(
'--config-loader <loader>',
'Set the config file loader (auto | jiti | native)',
{
default: 'auto',
},
)
.option('--env-dir <dir>', 'specify the directory to load `.env` files')
.option(
'--log-level <level>',
Expand All @@ -78,14 +78,16 @@ const applyCommonOptions = (cli: CAC) => {
type: [String],
default: [],
},
);
)
.option('--no-env', 'Disable loading of `.env` files');
};

export function runCli(): void {
export function setupCommands(): void {
const cli = cac('rslib');

cli.version(RSLIB_VERSION);

// Apply common options to all commands
applyCommonOptions(cli);

const buildDescription = `build the library for production ${color.dim('(default if no command is given)')}`;
Expand Down Expand Up @@ -138,31 +140,24 @@ export function runCli(): void {
.action(async (options: BuildOptions) => {
try {
const cliBuild = async () => {
const { config, watchFiles } = await initConfig(options);
const rslib = await init(options);

if (options.watch) {
config.plugins = config.plugins || [];
config.plugins.push({
name: 'rslib:on-after-build',
setup(api) {
api.onAfterBuild(({ isFirstCompile }) => {
if (isFirstCompile) {
logger.success('build complete, watching for changes...');
}
});
},
} satisfies RsbuildPlugin);

watchFilesForRestart(watchFiles, async () => {
watchFilesForRestart(rslib.context.watchFiles, async () => {
await cliBuild();
});
}
await build(config, options);

await rslib.build(options);
};

await cliBuild();
} catch (err) {
logger.error('Failed to build.');
const isRspackError =
err instanceof Error && err.message === RSPACK_BUILD_ERROR;
if (!isRspackError) {
logger.error('Failed to build.');
}
if (err instanceof AggregateError) {
for (const error of err.errors) {
logger.error(error);
Expand All @@ -182,12 +177,13 @@ export function runCli(): void {
.action(async (options: InspectOptions) => {
try {
// TODO: inspect should output Rslib's config
const { config } = await initConfig(options);
await inspect(config, {
const rslib = await init(options);
await rslib.inspectConfig({
lib: options.lib,
mode: options.mode,
output: options.output,
outputPath: options.output,
verbose: options.verbose,
writeToDisk: true,
});
} catch (err) {
logger.error('Failed to inspect config.');
Expand All @@ -199,12 +195,12 @@ export function runCli(): void {
mfDevCommand.action(async (options: MfDevOptions) => {
try {
const cliMfDev = async () => {
const { config, watchFiles } = await initConfig(options);
await startMFDevServer(config, {
const rslib = await init(options);
await rslib.startMFDevServer({
lib: options.lib,
});

watchFilesForRestart(watchFiles, async () => {
watchFilesForRestart(rslib.context.watchFiles, async () => {
await cliMfDev();
});
};
Expand Down
54 changes: 54 additions & 0 deletions packages/core/src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { LogLevel } from '@rsbuild/core';
import { isDebug, logger } from '../utils/logger';
import { setupCommands } from './commands';

function initNodeEnv() {
if (!process.env.NODE_ENV) {
const command = process.argv[2] ?? '';
process.env.NODE_ENV = ['build'].includes(command)
? 'production'
: 'development';
}
}

function showGreeting() {
// Ensure consistent spacing before the greeting message.
// Different package managers handle output formatting differently - some automatically
// add a blank line before command output, while others do not.
const { npm_execpath, npm_lifecycle_event, NODE_RUN_SCRIPT_NAME } =
process.env;
const isNpx = npm_lifecycle_event === 'npx';
const isBun = npm_execpath?.includes('.bun');
const isNodeRun = Boolean(NODE_RUN_SCRIPT_NAME);
const prefix = isNpx || isBun || isNodeRun ? '\n' : '';
logger.greet(`${prefix}Rslib v${RSLIB_VERSION}\n`);
}

// ensure log level is set before any log is printed
function setupLogLevel() {
const logLevelIndex = process.argv.findIndex(
(item) => item === '--log-level' || item === '--logLevel',
);
if (logLevelIndex !== -1) {
const level = process.argv[logLevelIndex + 1];
if (level && ['warn', 'error', 'silent'].includes(level) && !isDebug()) {
logger.level = level as LogLevel;
}
}
}

export function runCLI(): void {
// make it easier to identify the process via activity monitor or other tools
process.title = 'rslib-node';

initNodeEnv();
setupLogLevel();
showGreeting();

try {
setupCommands();
} catch (err) {
logger.error('Failed to start Rslib CLI.');
logger.error(err);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import path from 'node:path';
import util from 'node:util';
import { loadEnv, type RsbuildEntry } from '@rsbuild/core';
import { loadConfig } from '../config';
import type { RsbuildEntry } from '@rsbuild/core';
import { createRslib } from '../createRslib';
import { loadConfig as baseLoadConfig } from '../loadConfig';
import type {
LibConfig,
RsbuildConfigOutputTarget,
RslibConfig,
RslibInstance,
} from '../types';
import { getAbsolutePath } from '../utils/helper';
import { isDebugKey, logger } from '../utils/logger';
import { ensureAbsolutePath } from '../utils/helper';
import { logger } from '../utils/logger';
import type { BuildOptions, CommonOptions } from './commands';
import { onBeforeRestart } from './restart';

const getEnvDir = (cwd: string, envDir?: string) => {
if (envDir) {
Expand Down Expand Up @@ -67,8 +67,13 @@ export const applyCliOptions = (
options: BuildOptions,
root: string,
): void => {
if (options.root) config.root = root;
if (options.logLevel) config.logLevel = options.logLevel;
if (options.root) {
config.root = root;
}

if (options.logLevel) {
config.logLevel = options.logLevel;
}

for (const lib of config.lib) {
if (options.format !== undefined) lib.format = options.format;
Expand Down Expand Up @@ -105,51 +110,39 @@ export const applyCliOptions = (
}
};

export async function initConfig(options: CommonOptions): Promise<{
config: RslibConfig;
configFilePath?: string;
watchFiles: string[];
}> {
const cwd = process.cwd();
const root = options.root ? getAbsolutePath(cwd, options.root) : cwd;
const envs = loadEnv({
cwd: getEnvDir(root, options.envDir),
mode: options.envMode,
});

onBeforeRestart(envs.cleanup);

const { content: config, filePath: configFilePath } = await loadConfig({
const loadConfig = async (options: CommonOptions, root: string) => {
const { content: config, filePath: configFilePath } = await baseLoadConfig({
cwd: root,
path: options.config,
envMode: options.envMode,
loader: options.configLoader,
});

if (configFilePath === undefined) {
if (configFilePath === null) {
config.lib = [{} satisfies LibConfig];
logger.debug(
'No config file found. Falling back to CLI options for the default library.',
);
logger.debug('Falling back to CLI options for the default library.');
}

config.source ||= {};
config.source.define = {
...envs.publicVars,
...config.source.define,
};

applyCliOptions(config, options, root);

// only debug serialized rslib config when DEBUG=rslib
if (isDebugKey(['rslib'])) {
logger.debug('Rslib config used to generate Rsbuild environments:');
logger.debug(`\n${util.inspect(config, { depth: null, colors: true })}`);
}
return config;
};

export async function init(options: CommonOptions): Promise<RslibInstance> {
const cwd = process.cwd();
const root = options.root ? ensureAbsolutePath(cwd, options.root) : cwd;

const rslib = await createRslib({
cwd: root,
config: () => loadConfig(options, root),
loadEnv:
options.env === false
? false
: {
cwd: getEnvDir(root, options.envDir),
mode: options.envMode,
},
});

return {
config,
configFilePath,
watchFiles: [configFilePath, ...envs.filePaths].filter(Boolean) as string[],
};
return rslib;
}
Loading
Loading