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
4 changes: 3 additions & 1 deletion packages/rstack/src/fmt/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,9 @@ const runFmtCLI = async (args: string[]): Promise<void> => {
});

if (files.length === 0) {
if (noErrorOnUnmatchedPattern) {
// Staged tasks may pass only paths excluded by formatter ignore rules.
const allowUnmatched = noErrorOnUnmatchedPattern || process.env.RSTACK_STAGED === '1';
if (allowUnmatched) {
return;
}
reportNoSupportedFiles(patterns);
Expand Down
3 changes: 3 additions & 0 deletions packages/rstack/src/staged.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ export async function runStagedCLI(args: string[]): Promise<void> {
);
}

// Let child commands detect that they are running through `rs staged`.
process.env.RSTACK_STAGED = '1';

const success = await lintStaged({
allowEmpty: values.allowEmpty,
concurrent: values.concurrent === undefined ? undefined : JSON.parse(values.concurrent),
Expand Down
23 changes: 23 additions & 0 deletions packages/rstack/tests/cli/staged/fmt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,29 @@ test('formats staged files with rs fmt and applies ignore rules', () => {
expect(git(['show', ':ignored-by-git.ts'])).toBe('const gitIgnored = "git ignored";\n');
});

test('allows rs fmt when all staged files are ignored', () => {
const source = 'const fmtIgnored="fmt ignored"';
writeProjectFile('ignored-by-fmt.ts', source);
git(['add', '--', 'ignored-by-fmt.ts']);

const result = runStaged();

expect(result.status).toBe(0);
expect(readProjectFile('ignored-by-fmt.ts')).toBe(source);
expect(git(['show', ':ignored-by-fmt.ts'])).toBe(source);
expect(`${result.stdout}\n${result.stderr}`).not.toContain('No supported files matched');
});

test('still rejects staged files unsupported by rs fmt', () => {
writeProjectFile('notes.unknown', 'plain text');
git(['add', '--', 'notes.unknown']);

const result = runStaged();

expect(result.status).toBe(1);
expect(`${result.stdout}\n${result.stderr}`).toContain('No supported files matched');
});

test('propagates rs fmt failures', () => {
writeProjectFile('invalid.ts', 'const value = ;');
git(['add', '--', 'invalid.ts']);
Expand Down
18 changes: 17 additions & 1 deletion packages/rstack/tests/cli/staged/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import lintStaged from 'lint-staged';
import { beforeEach, rs } from 'rstack/test';
import { afterEach, beforeEach, rs } from 'rstack/test';
import { test } from '#test-helpers';
import { loadRstackConfig } from '../../../src/config.ts';
import { runStagedCLI, type StagedConfig } from '../../../src/staged.ts';
Expand All @@ -17,6 +17,7 @@ const stagedConfig: StagedConfig = {
};

beforeEach(() => {
delete process.env.RSTACK_STAGED;
rs.resetAllMocks();
mocks.lintStaged.mockResolvedValue(true);
mocks.loadRstackConfig.mockResolvedValue({
Expand All @@ -26,6 +27,10 @@ beforeEach(() => {
});
});

afterEach(() => {
delete process.env.RSTACK_STAGED;
});

test('should display the staged help message', ({ execCli, expect }) => {
const output = execCli('staged --help');

Expand Down Expand Up @@ -62,6 +67,17 @@ test('should pass default options to lint-staged', async ({ expect }) => {
});
});

test('should set the staged environment', async ({ expect }) => {
mocks.lintStaged.mockImplementation(async () => {
expect(process.env.RSTACK_STAGED).toBe('1');
return true;
});

await runStagedCLI([]);

expect(process.env.RSTACK_STAGED).toBe('1');
});

test('should pass long options to lint-staged', async ({ expect }) => {
await runStagedCLI([
'--allow-empty',
Expand Down
2 changes: 2 additions & 0 deletions website/docs/en/guide/cli/fmt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ rs fmt --no-error-on-unmatched-pattern 'src/**/*.ts'

For example, a pre-commit script may always run `rs fmt`, even when the staged changes contain no supported files. This option lets the command exit successfully in that case instead of blocking the commit.

> [`rs staged`](./staged) enables this behavior automatically for its `rs fmt` tasks.

### `--parallel-workers <count>`

Set the maximum number of formatting workers to a positive integer:
Expand Down
2 changes: 2 additions & 0 deletions website/docs/zh/guide/cli/fmt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ rs fmt --no-error-on-unmatched-pattern 'src/**/*.ts'

例如,pre-commit 脚本可能会始终运行 `rs fmt`,即使暂存的改动中没有支持的文件。此选项可让命令在这种情况下成功退出,避免阻止提交。

> [`rs staged`](./staged) 会为其中的 `rs fmt` 任务自动启用此行为。

### `--parallel-workers <count>`

将格式化 worker 的最大数量设置为正整数:
Expand Down