Skip to content
Open
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
2 changes: 1 addition & 1 deletion packages/rstack/src/cli/commands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { join, resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
import { CLI_CONFIG_PATH_ENV, getConfigState } from '../config.ts';
import { CLI_CONFIG_PATH_ENV, getConfigState } from './config.ts';
import { insertConfigArg, parseArgs, parseCliArgs } from './args.ts';
import { hasHelpFlag, printCommandHelp } from './help.ts';

Expand Down
35 changes: 35 additions & 0 deletions packages/rstack/src/cli/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
loadRstackConfig,
type LoadedRstackConfig,
type LoadRstackConfigOptions,
} from '../config.ts';

type ConfigState = {
/**
* Absolute config path from the global `--config` flag, resolved at parse
* time so a later cwd (such as an LSP workspace root) cannot change it.
*/
configPath?: string;
};

declare global {
// rslint-disable-next-line no-var
var __rstackCliState: ConfigState | undefined;
}

export const getConfigState = (): ConfigState => {
// The CLI and internal tool configs may be loaded as separate module instances.
return (globalThis.__rstackCliState ??= {});
};

/** Passes `--config` to Rslint's JS plugin workers, which run in a fresh realm. */
export const CLI_CONFIG_PATH_ENV = 'RSTACK_CLI_CONFIG_PATH';

/** Loads a root tool config using the CLI path unless explicitly overridden. */
export const loadCliRstackConfig = (
options: LoadRstackConfigOptions = {},
): Promise<LoadedRstackConfig> =>
loadRstackConfig({
...options,
configFilePath: options.configFilePath ?? getConfigState().configPath,
});
41 changes: 4 additions & 37 deletions packages/rstack/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,9 @@ export type LoadRstackConfigOptions = {
/**
* The path to the Rstack config file, can be a relative or absolute path.
* A relative path is resolved from `cwd`.
* If `configFilePath` is omitted, the config path set by the CLI is used.
* If neither path is provided, the function will search for the config file in `cwd`.
* Pass `null` to search `cwd` and ignore the CLI config path.
* If omitted, the function searches for the config file in `cwd`.
*/
configFilePath?: string | null;
configFilePath?: string;
/**
* The directory the config file is searched in and relative config paths are resolved from.
* Defaults to the current working directory.
Expand All @@ -102,21 +100,10 @@ type ConfigSession = {
active: boolean;
};

type ConfigState = {
/**
* Config file path from the global `--config` flag. Always absolute: the CLI
* resolves it at parse time so it stays independent of later cwd choices
* (`loadRstackConfig` may be called with an LSP workspace root as `cwd`).
*/
configPath?: string;
};

declare global {
// rslint-disable-next-line no-var
var __rstackConfigSessionStorage:
AsyncLocalStorage<ConfigSession> | undefined;
// rslint-disable-next-line no-var
var __rstackCliState: ConfigState | undefined;
}

const getConfigSessionStorage = (): AsyncLocalStorage<ConfigSession> => {
Expand All @@ -131,23 +118,6 @@ const getConfigSessionStorage = (): AsyncLocalStorage<ConfigSession> => {
return globalThis.__rstackConfigSessionStorage;
};

export const getConfigState = (): ConfigState => {
// The CLI and its internal tool config can also be loaded as separate module
// instances. Keep only the CLI config path in its own global state.
if (!globalThis.__rstackCliState) {
globalThis.__rstackCliState = {};
}

return globalThis.__rstackCliState;
};

/**
* Passes the `--config` path to Rslint's JS plugin workers, which import the
* internal lint config in a fresh realm without the CLI state above. Only that
* config reads it.
*/
export const CLI_CONFIG_PATH_ENV = 'RSTACK_CLI_CONFIG_PATH';

type Define = {
/**
* Inherits shared configs from left to right, followed by the project's own
Expand Down Expand Up @@ -269,9 +239,6 @@ export const loadRstackConfig = async ({
configFilePath,
cwd,
}: LoadRstackConfigOptions = {}): Promise<LoadedRstackConfig> => {
const state = getConfigState();
const configPath =
configFilePath === null ? undefined : (configFilePath ?? state.configPath);
const session: ConfigSession = {
configs: {},
active: true,
Expand All @@ -284,8 +251,8 @@ export const loadRstackConfig = async ({
exportName: false,
fresh: true,
cwd,
...(configPath !== undefined
? { path: configPath }
...(configFilePath !== undefined
? { path: configFilePath }
: {
configFileNames: [
'rstack.config.ts',
Expand Down
5 changes: 3 additions & 2 deletions packages/rstack/src/fmt/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { performance } from 'node:perf_hooks';
import { color, logger } from 'rslog';
import { parseArgs } from '../cli/args.ts';
import { printCommandHelp } from '../cli/help.ts';
import { loadRstackConfig, type LoadedRstackConfig } from '../config.ts';
import type { LoadedRstackConfig } from '../config.ts';
import { loadCliRstackConfig } from '../cli/config.ts';
import { ensureProjectCacheDir } from '../projectCache.ts';
import { fmtCacheFileName } from './cacheStore.ts';
import { resolveFmtConfig } from './config.ts';
Expand Down Expand Up @@ -250,7 +251,7 @@ const loadFmtConfig = async (
loadedConfig?: LoadedRstackConfig,
): Promise<ResolvedFmtConfig> => {
const { configs, filePath } =
loadedConfig ?? (await loadRstackConfig({ cwd }));
loadedConfig ?? (await loadCliRstackConfig({ cwd }));

return resolveFmtConfig({
layers: [configs],
Expand Down
5 changes: 3 additions & 2 deletions packages/rstack/src/rsbuildConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
mergeRsbuildConfig,
} from '@rsbuild/core';
import { withConfigMeta } from '@rstackjs/load-config';
import { loadRstackConfig, type RstackConfigDefinitions } from './config.ts';
import type { RstackConfigDefinitions } from './config.ts';
import { loadCliRstackConfig } from './cli/config.ts';
import { resolveConfigLayers } from './configLayers.ts';

export const resolveRsbuildConfig = async (
Expand All @@ -19,7 +20,7 @@ export const resolveRsbuildConfig = async (
};

const loadRsbuildConfig: RsbuildConfigDefinition = async (params) => {
const { configs, filePath, dependencies } = await loadRstackConfig();
const { configs, filePath, dependencies } = await loadCliRstackConfig();
const config = await resolveRsbuildConfig([configs], params);

return withConfigMeta(config, { filePath, dependencies });
Expand Down
5 changes: 3 additions & 2 deletions packages/rstack/src/rslibConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
mergeRslibConfig,
} from '@rslib/core';
import { withConfigMeta } from '@rstackjs/load-config';
import { loadRstackConfig, type RstackConfigDefinitions } from './config.ts';
import type { RstackConfigDefinitions } from './config.ts';
import { loadCliRstackConfig } from './cli/config.ts';
import { resolveConfigLayers } from './configLayers.ts';

export const resolveRslibConfig = async (
Expand All @@ -17,7 +18,7 @@ export const resolveRslibConfig = async (
};

const loadRslibConfig = (async (params: ConfigParams) => {
const { configs, filePath, dependencies } = await loadRstackConfig();
const { configs, filePath, dependencies } = await loadCliRstackConfig();
const config = await resolveRslibConfig([configs], params);

return withConfigMeta(config, { filePath, dependencies });
Expand Down
9 changes: 3 additions & 6 deletions packages/rstack/src/rslintConfig.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import {
CLI_CONFIG_PATH_ENV,
loadRstackConfig,
type LoadedRstackConfig,
} from './config.ts';
import { CLI_CONFIG_PATH_ENV, loadCliRstackConfig } from './cli/config.ts';
import type { LoadedRstackConfig } from './config.ts';
import type { RslintConfig } from '@rslint/core';
import { color, logger } from 'rslog';
import { resolveRslintConfig } from './configLayers.ts';

// Expose the loaded config so `rs check` can pass it to fmt instead of loading
// and executing the Rstack config a second time. Rslint also imports this
// module in its JS plugin workers, which get `--config` from the environment.
export const loadedConfig: LoadedRstackConfig = await loadRstackConfig({
export const loadedConfig: LoadedRstackConfig = await loadCliRstackConfig({
configFilePath: process.env[CLI_CONFIG_PATH_ENV],
});
const { configs } = loadedConfig;
Expand Down
5 changes: 3 additions & 2 deletions packages/rstack/src/rspressConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { WatchFiles } from '@rsbuild/core';
import type { UserConfig } from '@rspress/core';
import { loadRstackConfig, type RstackConfigDefinitions } from './config.ts';
import type { RstackConfigDefinitions } from './config.ts';
import { loadCliRstackConfig } from './cli/config.ts';
import { resolveConfigLayers } from './configLayers.ts';

export const resolveRspressConfig = async (
Expand All @@ -16,7 +17,7 @@ export const resolveRspressConfig = async (
};

export default async (): Promise<UserConfig> => {
const { configs, filePath, dependencies } = await loadRstackConfig();
const { configs, filePath, dependencies } = await loadCliRstackConfig();
const config = await resolveRspressConfig([configs]);

if (!filePath) {
Expand Down
7 changes: 3 additions & 4 deletions packages/rstack/src/rstestConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@rstest/core';
import { type LoadProjectConfig, PROJECT_LOADER } from '@rstest/core/api';
import { loadRstackConfig, type RstackConfigDefinitions } from './config.ts';
import { loadCliRstackConfig } from './cli/config.ts';
import { resolveConfigLayers } from './configLayers.ts';

const resolveAutomaticExtends = async (
Expand Down Expand Up @@ -120,11 +121,9 @@ export const resolveRstestConfig = async (
const createProjectLoader =
(params: ConfigParams): LoadProjectConfig =>
async ({ dirPath, filePath }) => {
// `null` searches the project directory with the default config file names
// instead of reusing the `rs --config` path that loads the root config.
const loaded = await loadRstackConfig({
cwd: dirPath,
configFilePath: filePath ?? null,
configFilePath: filePath,
});
if (loaded.filePath === null) {
return null;
Expand All @@ -140,7 +139,7 @@ const createProjectLoader =
};

const loadRstestConfig = (async (params: ConfigParams) => {
const { configs } = await loadRstackConfig();
const { configs } = await loadCliRstackConfig();
const config = await resolveRstestConfig([configs], params);
return {
...config,
Expand Down
4 changes: 2 additions & 2 deletions packages/rstack/src/staged.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import lintStaged from 'lint-staged';
import { parseArgs } from './cli/args.ts';
import { printCommandHelp } from './cli/help.ts';
import { loadRstackConfig } from './config.ts';
import { loadCliRstackConfig } from './cli/config.ts';
import { resolveStagedConfig } from './configLayers.ts';

export type StagedSyncTaskGenerator = (
Expand Down Expand Up @@ -51,7 +51,7 @@ export async function runStagedCLI(args: string[]): Promise<void> {
return;
}

const { configs } = await loadRstackConfig();
const { configs } = await loadCliRstackConfig();
const stagedConfig = await resolveStagedConfig([configs]);
if (stagedConfig === undefined) {
throw new Error(
Expand Down
8 changes: 4 additions & 4 deletions packages/rstack/tests/cli/staged/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import lintStaged from 'lint-staged';
import { afterEach, beforeEach, rs } from 'rstack/test';
import { normalizeHelpOutput, test } from '#test-helpers';
import { loadRstackConfig } from '../../../src/config.ts';
import { loadCliRstackConfig } from '../../../src/cli/config.ts';
import { runStagedCLI, type StagedConfig } from '../../../src/staged.ts';

rs.mock('lint-staged');
rs.mock('../../../src/config.ts');
rs.mock('../../../src/cli/config.ts');

const mocks = {
lintStaged: rs.mocked(lintStaged),
loadRstackConfig: rs.mocked(loadRstackConfig),
loadCliRstackConfig: rs.mocked(loadCliRstackConfig),
};

const stagedConfig: StagedConfig = {
Expand All @@ -20,7 +20,7 @@ beforeEach(() => {
delete process.env.RSTACK_STAGED;
rs.resetAllMocks();
mocks.lintStaged.mockResolvedValue(true);
mocks.loadRstackConfig.mockResolvedValue({
mocks.loadCliRstackConfig.mockResolvedValue({
configs: { staged: stagedConfig },
filePath: null,
dependencies: [],
Expand Down
14 changes: 8 additions & 6 deletions packages/rstack/tests/config/load-config/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import path from 'node:path';
import { afterEach, expect, test } from 'rstack/test';
import { define, loadRstackConfig } from '../../../src/config.ts';
import {
define,
getConfigState,
loadRstackConfig,
} from '../../../src/config.ts';
loadCliRstackConfig,
} from '../../../src/cli/config.ts';

type Deferred = ReturnType<typeof Promise.withResolvers<void>>;

Expand Down Expand Up @@ -38,7 +38,7 @@ afterEach(() => {
test('should discard a config session after loading fails', async () => {
state.configPath = configPath('rstack.config.ts');

await expect(loadRstackConfig()).rejects.toThrow('test config error');
await expect(loadCliRstackConfig()).rejects.toThrow('test config error');

const { configs } = await loadConfigFile('explicit.config.ts');
expect(configs).toEqual({
Expand All @@ -50,7 +50,7 @@ test('should prefer an explicit config path over the state config path', async (
const explicitConfigPath = configPath('explicit.config.ts');
state.configPath = configPath('rstack.config.ts');

const { configs, filePath } = await loadRstackConfig({
const { configs, filePath } = await loadCliRstackConfig({
configFilePath: explicitConfigPath,
});

Expand Down Expand Up @@ -86,7 +86,9 @@ test('should resolve a relative explicit config path from cwd', async () => {
expect(filePath).toBe(configPath('explicit.config.ts'));
});

test('should search for the config file in cwd', async () => {
test('should search cwd without inheriting the CLI config path', async () => {
state.configPath = configPath('explicit.config.ts');

await expect(loadRstackConfig({ cwd: import.meta.dirname })).rejects.toThrow(
'test config error',
);
Expand Down
6 changes: 6 additions & 0 deletions packages/rstack/tests/config/project-loader/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ test('should load each project from its own Rstack config', ({ execCli }) => {
execCli('test');
});

test('should load directory projects independently of the CLI config path', ({
execCli,
}) => {
execCli('test --config rstack.config.ts');
});

test('should load file project entries from their Rstack configs', ({
execCli,
}) => {
Expand Down
2 changes: 1 addition & 1 deletion website/docs/en/guide/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const { configs, filePath, dependencies } = await loadRstackConfig({
The function accepts these optional parameters:

- `cwd`: the directory to search for a configuration file. Relative `configFilePath` values are also resolved from this directory. Defaults to the current working directory.
- `configFilePath`: a relative or absolute path to the configuration file. If omitted, the loader uses the path specified by the CLI's `--config` option. If neither is set, it searches `cwd` for the [default file names](./configuration#configuration-file).
- `configFilePath`: a relative or absolute path to the configuration file. If omitted, it searches `cwd` for the [default file names](./configuration#configuration-file). This API does not read the CLI's `--config` option.

The returned object has these fields:

Expand Down
2 changes: 1 addition & 1 deletion website/docs/zh/guide/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const { configs, filePath, dependencies } = await loadRstackConfig({
支持以下可选参数:

- `cwd`:查找配置文件的目录。`configFilePath` 为相对路径时,也以此目录为基准。默认为当前工作目录。
- `configFilePath`:配置文件路径,可以是相对路径或绝对路径。省略时,优先使用 CLI 通过 `--config` 指定的路径;若也未指定,则在 `cwd` 中按[默认文件名](./configuration#configuration-file)查找配置。
- `configFilePath`:配置文件路径,可以是相对路径或绝对路径。省略时,在 `cwd` 中按[默认文件名](./configuration#configuration-file)查找配置。此 API 不读取 CLI 的 `--config` 选项。

返回对象包含以下字段:

Expand Down
Loading