Skip to content
Merged
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
1 change: 1 addition & 0 deletions e2e/cases/javascript-api/build-and-load-env/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PUBLIC_FOO=foo
1 change: 1 addition & 0 deletions e2e/cases/javascript-api/build-and-load-env/.env.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PUBLIC_BAR=bar
46 changes: 46 additions & 0 deletions e2e/cases/javascript-api/build-and-load-env/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { expect } from '@playwright/test';
import { createRsbuild } from '@rsbuild/core';
import { rspackOnlyTest } from 'scripts';

rspackOnlyTest('should not load env by default', async () => {
const rsbuild = await createRsbuild({
cwd: __dirname,
loadEnv: false,
rsbuildConfig: {
performance: {
printFileSize: false,
},
},
});

expect(process.env.PUBLIC_FOO).toBe(undefined);
expect(process.env.PUBLIC_BAR).toBe(undefined);
const { close } = await rsbuild.build();
await close();
});

rspackOnlyTest(
'should allow to call `build` with `loadEnv` options',
async () => {
const rsbuild = await createRsbuild({
cwd: __dirname,
loadEnv: {
mode: 'prod',
},
rsbuildConfig: {
performance: {
printFileSize: false,
},
},
});

expect(process.env.PUBLIC_FOO).toBe('foo');
expect(process.env.PUBLIC_BAR).toBe('bar');

const { close } = await rsbuild.build();
await close();

expect(process.env.PUBLIC_FOO).toBe(undefined);
expect(process.env.PUBLIC_BAR).toBe(undefined);
},
);
1 change: 1 addition & 0 deletions e2e/cases/javascript-api/build-and-load-env/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hello');
61 changes: 21 additions & 40 deletions packages/core/src/cli/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import path from 'node:path';
import { loadConfig as baseLoadConfig, watchFilesForRestart } from '../config';
import { createRsbuild } from '../createRsbuild';
import { castArray, getAbsolutePath } from '../helpers';
import { type LoadEnvResult, loadEnv } from '../loadEnv';
import { logger } from '../logger';
import type { RsbuildInstance } from '../types';
import type { CommonOptions } from './commands';
Expand All @@ -16,22 +15,19 @@ const getEnvDir = (cwd: string, envDir?: string) => {
return cwd;
};

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

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

if (commonOpts.base) {
config.server ||= {};
config.server.base = commonOpts.base;
}

Expand All @@ -44,38 +40,31 @@ const loadConfig = async (root: string, envs: LoadEnvResult) => {
}

if (commonOpts.open && !config.server?.open) {
config.server ||= {};
config.server.open = commonOpts.open;
}

if (commonOpts.host) {
config.server ||= {};
config.server.host = commonOpts.host;
}

if (commonOpts.port) {
config.server ||= {};
config.server.port = commonOpts.port;
}

// enable CLI shortcuts by default when using Rsbuild CLI
if (config.dev?.cliShortcuts === undefined) {
config.dev ||= {};
if (config.dev.cliShortcuts === undefined) {
config.dev.cliShortcuts = true;
}

// add env files to build dependencies, so that the build cache
// can be invalidated when the env files are changed.
if (config.performance?.buildCache && envs.filePaths.length > 0) {
const { buildCache } = config.performance;
if (buildCache === true) {
config.performance.buildCache = {
buildDependencies: envs.filePaths,
};
} else {
buildCache.buildDependencies ||= [];
buildCache.buildDependencies.push(...envs.filePaths);
}
// watch the config file
if (filePath) {
config.dev.watchFiles = [
...(config.dev.watchFiles ? castArray(config.dev.watchFiles) : []),
{
paths: filePath,
type: 'reload-server',
},
];
}

return config;
Expand All @@ -97,28 +86,23 @@ export async function init({
try {
const cwd = process.cwd();
const root = commonOpts.root ? getAbsolutePath(cwd, commonOpts.root) : cwd;
const envs = loadEnv({
cwd: getEnvDir(root, commonOpts.envDir),
mode: commonOpts.envMode,
});

const rsbuild = await createRsbuild({
cwd: root,
rsbuildConfig: async () => loadConfig(root, envs),
rsbuildConfig: () => loadConfig(root),
environment: commonOpts.environment,
loadEnv: {
cwd: getEnvDir(root, commonOpts.envDir),
mode: commonOpts.envMode,
},
});

rsbuild.onBeforeCreateCompiler(() => {
const command = process.argv[2];
if (command === 'dev' || isBuildWatch) {
const files = [...envs.filePaths];

const { _privateMeta } = rsbuild.getNormalizedConfig();
if (_privateMeta) {
files.push(_privateMeta.configFilePath);
}

const files: string[] = [];
const config = rsbuild.getNormalizedConfig();

if (config.dev?.watchFiles) {
for (const watchFilesConfig of castArray(config.dev.watchFiles)) {
if (watchFilesConfig.type !== 'reload-server') {
Expand All @@ -143,9 +127,6 @@ export async function init({
}
});

rsbuild.onCloseBuild(envs.cleanup);
rsbuild.onCloseDevServer(envs.cleanup);

return rsbuild;
} catch (err) {
if (isRestart) {
Expand Down
111 changes: 85 additions & 26 deletions packages/core/src/createRsbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
import { isPromise } from 'node:util/types';
import { createContext } from './createContext';
import {
castArray,
color,
getNodeEnv,
isEmptyDir,
Expand All @@ -10,6 +11,7 @@ import {
setNodeEnv,
} from './helpers';
import { initPluginAPI } from './initPlugins';
import { type LoadEnvResult, loadEnv } from './loadEnv';
import { logger } from './logger';
import { createPluginManager } from './pluginManager';
import { pluginAppIcon } from './plugins/appIcon';
Expand Down Expand Up @@ -59,6 +61,7 @@ import type {
PluginManager,
PreviewOptions,
ResolvedCreateRsbuildOptions,
RsbuildConfig,
RsbuildInstance,
RsbuildPlugin,
RsbuildPlugins,
Expand Down Expand Up @@ -114,25 +117,75 @@ async function applyDefaultPlugins(
]);
}

function applyEnvsToConfig(config: RsbuildConfig, envs: LoadEnvResult | null) {
if (envs === null) {
return;
}

// define the public env variables
config.source ||= {};
config.source.define = {
...envs.publicVars,
...config.source.define,
};

if (envs.filePaths.length === 0) {
return;
}

// watch the env files
config.dev ||= {};
config.dev.watchFiles = [
...(config.dev.watchFiles ? castArray(config.dev.watchFiles) : []),
{
paths: envs.filePaths,
type: 'reload-server',
},
];

// add env files to build dependencies, so that the build cache
// can be invalidated when the env files are changed.
if (config.performance?.buildCache) {
const { buildCache } = config.performance;
if (buildCache === true) {
config.performance.buildCache = {
buildDependencies: envs.filePaths,
};
} else {
buildCache.buildDependencies ||= [];
buildCache.buildDependencies.push(...envs.filePaths);
}
}
}

/**
* Create an Rsbuild instance.
*/
export async function createRsbuild(
options: CreateRsbuildOptions = {},
): Promise<RsbuildInstance> {
const rsbuildConfig = isFunction(options.rsbuildConfig)
const envs = options.loadEnv
? loadEnv({
cwd: options.cwd,
...(typeof options.loadEnv === 'boolean' ? {} : options.loadEnv),
})
: null;

const config = isFunction(options.rsbuildConfig)
? await options.rsbuildConfig()
: options.rsbuildConfig || {};

applyEnvsToConfig(config, envs);

const resolvedOptions: ResolvedCreateRsbuildOptions = {
cwd: process.cwd(),
...options,
rsbuildConfig,
rsbuildConfig: config,
};

const pluginManager = createPluginManager();

const context = await createContext(resolvedOptions, rsbuildConfig);
const context = await createContext(resolvedOptions, config);

const getPluginAPI = initPluginAPI({ context, pluginManager });
context.getPluginAPI = getPluginAPI;
Expand All @@ -142,8 +195,7 @@ export async function createRsbuild(
await applyDefaultPlugins(pluginManager, context);
logger.debug('add default plugins done');

const provider =
(rsbuildConfig.provider as RsbuildProvider) || rspackProvider;
const provider = (config.provider as RsbuildProvider) || rspackProvider;

const providerInstance = await provider({
context,
Expand Down Expand Up @@ -260,6 +312,11 @@ export async function createRsbuild(
...pick(providerInstance, ['initConfigs', 'inspectConfig']),
};

if (envs) {
rsbuild.onCloseBuild(envs.cleanup);
rsbuild.onCloseDevServer(envs.cleanup);
}

const getFlattenedPlugins = async (pluginOptions: RsbuildPlugins) => {
let plugins = pluginOptions;
do {
Expand All @@ -271,32 +328,34 @@ export async function createRsbuild(
return plugins as Array<RsbuildPlugin | Falsy>;
};

if (rsbuildConfig.plugins) {
const plugins = await getFlattenedPlugins(rsbuildConfig.plugins);
if (config.plugins) {
const plugins = await getFlattenedPlugins(config.plugins);
rsbuild.addPlugins(plugins);
}

// Register environment plugin
if (rsbuildConfig.environments) {
if (config.environments) {
await Promise.all(
Object.entries(rsbuildConfig.environments).map(async ([name, config]) => {
if (!config.plugins) {
return;
}

// If the current environment is not specified, skip it
if (
context.specifiedEnvironments &&
!context.specifiedEnvironments.includes(name)
) {
return;
}

const plugins = await getFlattenedPlugins(config.plugins);
rsbuild.addPlugins(plugins, {
environment: name,
});
}),
Object.entries(config.environments).map(
async ([name, environmentConfig]) => {
if (!environmentConfig.plugins) {
return;
}

// If the current environment is not specified, skip it
if (
context.specifiedEnvironments &&
!context.specifiedEnvironments.includes(name)
) {
return;
}

const plugins = await getFlattenedPlugins(environmentConfig.plugins);
rsbuild.addPlugins(plugins, {
environment: name,
});
},
),
);
}

Expand Down
17 changes: 12 additions & 5 deletions packages/core/src/types/rsbuild.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Compiler, MultiCompiler } from '@rspack/core';
import type { LoadEnvOptions } from '../loadEnv';
import type * as providerHelpers from '../provider/helpers';
import type { RsbuildDevServer } from '../server/devServer';
import type { StartServerResult } from '../server/helper';
Expand Down Expand Up @@ -126,14 +127,20 @@ export type CreateRsbuildOptions = {
* Passing a function to load the config asynchronously with custom logic.
*/
rsbuildConfig?: RsbuildConfig | (() => Promise<RsbuildConfig>);
/**
* Whether to call `loadEnv` to load environment variables and define them
* as global variables.
* @default false
*/
loadEnv?: boolean | LoadEnvOptions;
};

export type ResolvedCreateRsbuildOptions = Required<
Omit<CreateRsbuildOptions, 'environment' | 'rsbuildConfig'>
> & {
rsbuildConfig: RsbuildConfig;
environment?: CreateRsbuildOptions['environment'];
};
Pick<CreateRsbuildOptions, 'cwd'>
> &
Pick<CreateRsbuildOptions, 'loadEnv' | 'environment'> & {
rsbuildConfig: RsbuildConfig;
};

export type CreateDevServer = (
options?: CreateDevServerOptions,
Expand Down
Loading