Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f84edff

Browse files
committed
Fix type-aware
1 parent 078d7e2 commit f84edff

11 files changed

Lines changed: 65 additions & 114 deletions

File tree

.changeset/all-coats-fold.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"ultracite": patch
3+
---
4+
5+
Fix --type-aware for Biome

apps/docs/setup.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ You can optionally pass the following flags to the `init` command:
2424
| `--agents` | The AI agents to configure. | Space-separated: `claude`, `codex`, `jules`, `copilot`, `cline`, `amp`, `aider`, `firebase-studio`, `open-hands`, `gemini`, `junie`, `augmentcode`, `kilo-code`, `goose`, `roo-code`, `warp`, `droid`, `opencode`, `crush`, `qwen`, `amazon-q-cli`, `firebender`, `cursor-cli`, `mistral-vibe`, `vercel` |
2525
| `--hooks` | The agent hooks to configure. | Space-separated: `cursor`, `windsurf`, `claude` |
2626
| `--integrations` | Additional integrations to setup. | Space-separated: `husky`, `lefthook`, `lint-staged`, `pre-commit` |
27-
| `--type-aware` | Enable type-aware linting (oxlint only). | (no value needed) |
27+
| `--type-aware` | Enable type-aware linting. Adds project/scanner rules for Biome, installs `oxlint-tsgolint` for Oxlint. | (no value needed) |
2828
| `--skip-install` | Skip installing dependencies. | (no value needed) |
2929
| `--quiet` | Suppress interactive prompts and visual output. | (no value needed, automatically enabled in CI) |
3030

apps/docs/usage.mdx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,23 @@ You can also apply unsafe fixes (fixes that may change code behavior):
5757
npx ultracite fix --unsafe
5858
```
5959

60-
### Type-Aware Linting (Oxlint)
60+
### Type-Aware Linting
6161

62-
When using Oxlint, you can enable type-aware linting rules that leverage TypeScript's type system.
62+
Type-aware linting enables deeper analysis rules that leverage TypeScript's type system and project structure. The `--type-aware` flag is passed during `ultracite init` and configures your project accordingly.
6363

64-
To set up type-aware linting during initialization:
64+
#### Biome
65+
66+
For Biome, `--type-aware` adds the `ultracite/biome/type-aware` preset to your Biome config's `extends` array. This enables project/scanner rules like `noPrivateImports`, `noUndeclaredDependencies`, `noUnresolvedImports`, `noImportCycles`, and `noDeprecatedImports`.
6567

6668
```bash title="Terminal"
67-
npx ultracite init --linter oxlint --type-aware
69+
npx ultracite init --linter biome --type-aware
6870
```
6971

70-
This installs the required `oxlint-tsgolint` dependency automatically.
72+
Since the rules are baked into your Biome config, they work in both the CLI and your IDE automatically — no runtime flags needed.
73+
74+
#### Oxlint
7175

72-
Then use the flags when checking or fixing:
76+
For Oxlint, `--type-aware` installs the required `oxlint-tsgolint` dependency. You then pass the flag at runtime when checking or fixing:
7377

7478
```bash title="Terminal"
7579
npx ultracite check --type-aware
@@ -92,7 +96,7 @@ npx ultracite fix --type-aware --type-check
9296
```
9397

9498
<Callout>
95-
These flags only apply when using Oxlint. They have no effect with Biome or ESLint.
99+
The `--type-aware` and `--type-check` runtime flags only apply to Oxlint. For Biome, type-aware rules are configured at init time and require no runtime flags.
96100
</Callout>
97101

98102
### Validating Setup

packages/cli/__tests__/check.test.ts

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,41 +45,6 @@ describe("check", () => {
4545
expect(callArgs[1]).toContain("src/test.ts");
4646
});
4747

48-
test("adds --skip=project to biome by default", async () => {
49-
const mockSpawn = mock(() => ({ status: 0 }));
50-
mock.module("node:child_process", () => ({
51-
spawnSync: mockSpawn,
52-
}));
53-
mock.module("../src/utils", () => ({
54-
detectLinter: mock(async () => "biome"),
55-
parseFilePaths,
56-
}));
57-
58-
await check();
59-
60-
expect(mockSpawn).toHaveBeenCalled();
61-
const callArgs = mockSpawn.mock.calls[0];
62-
expect(callArgs[1]).toContain("--skip=project");
63-
});
64-
65-
test("does not add --skip=project when --type-aware is passed to biome", async () => {
66-
const mockSpawn = mock(() => ({ status: 0 }));
67-
mock.module("node:child_process", () => ({
68-
spawnSync: mockSpawn,
69-
}));
70-
mock.module("../src/utils", () => ({
71-
detectLinter: mock(async () => "biome"),
72-
parseFilePaths,
73-
}));
74-
75-
await check([], ["--type-aware"]);
76-
77-
expect(mockSpawn).toHaveBeenCalled();
78-
const callArgs = mockSpawn.mock.calls[0];
79-
expect(callArgs[1]).not.toContain("--skip=project");
80-
expect(callArgs[1]).not.toContain("--type-aware");
81-
});
82-
8348
test("passes through unknown options to biome", async () => {
8449
const mockSpawn = mock(() => ({ status: 0 }));
8550
mock.module("node:child_process", () => ({

packages/cli/__tests__/fix.test.ts

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -46,41 +46,6 @@ describe("fix", () => {
4646
expect(callArgs[1]).toContain("src/test.ts");
4747
});
4848

49-
test("adds --skip=project to biome by default", async () => {
50-
const mockSpawn = mock(() => ({ status: 0 }));
51-
mock.module("node:child_process", () => ({
52-
spawnSync: mockSpawn,
53-
}));
54-
mock.module("../src/utils", () => ({
55-
detectLinter: mock(async () => "biome"),
56-
parseFilePaths,
57-
}));
58-
59-
await fix([]);
60-
61-
expect(mockSpawn).toHaveBeenCalled();
62-
const callArgs = mockSpawn.mock.calls[0];
63-
expect(callArgs[1]).toContain("--skip=project");
64-
});
65-
66-
test("does not add --skip=project when --type-aware is passed to biome", async () => {
67-
const mockSpawn = mock(() => ({ status: 0 }));
68-
mock.module("node:child_process", () => ({
69-
spawnSync: mockSpawn,
70-
}));
71-
mock.module("../src/utils", () => ({
72-
detectLinter: mock(async () => "biome"),
73-
parseFilePaths,
74-
}));
75-
76-
await fix([], ["--type-aware"]);
77-
78-
expect(mockSpawn).toHaveBeenCalled();
79-
const callArgs = mockSpawn.mock.calls[0];
80-
expect(callArgs[1]).not.toContain("--skip=project");
81-
expect(callArgs[1]).not.toContain("--type-aware");
82-
});
83-
8449
test("passes through --unsafe option to biome", async () => {
8550
const mockSpawn = mock(() => ({ status: 0 }));
8651
mock.module("node:child_process", () => ({

packages/cli/config/biome/core/biome.jsonc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,12 @@
226226
"noProcessGlobal": "off",
227227
"useUniqueElementIds": "off",
228228

229-
/** ------ Project/Scanner rules. Enabled in config but skipped by default at runtime for performance. Use --type-aware to enable. ------ **/
230-
"noPrivateImports": "error",
231-
"noUndeclaredDependencies": "error",
232-
"noUnresolvedImports": "error",
233-
"useImportExtensions": "error",
234-
"useJsonImportAttributes": "error",
229+
/** ------ Project/Scanner rules. Use ultracite/biome/type-aware to enable. ------ **/
230+
"noPrivateImports": "off",
231+
"noUndeclaredDependencies": "off",
232+
"noUnresolvedImports": "off",
233+
"useImportExtensions": "off",
234+
"useJsonImportAttributes": "off",
235235

236236
/** ------------------------ CSS Rules ------------------------ **/
237237
"noInvalidDirectionInLinearGradient": "error",
@@ -461,9 +461,9 @@
461461

462462
"noConsole": "off",
463463

464-
/** ------ Project/Scanner rules. Enabled in config but skipped by default at runtime for performance. Use --type-aware to enable. ------ **/
465-
"noDeprecatedImports": "error",
466-
"noImportCycles": "error",
464+
/** ------ Project/Scanner rules. Use ultracite/biome/type-aware to enable. ------ **/
465+
"noDeprecatedImports": "off",
466+
"noImportCycles": "off",
467467

468468
/** ------------------------ CSS Rules ------------------------ **/
469469
"noDuplicateAtImportRules": "error",
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"root": false,
3+
"$schema": "../../../node_modules/@biomejs/biome/configuration_schema.json",
4+
"linter": {
5+
"rules": {
6+
"correctness": {
7+
"noPrivateImports": "error",
8+
"noUndeclaredDependencies": "error",
9+
"noUnresolvedImports": "error",
10+
"useImportExtensions": "error",
11+
"useJsonImportAttributes": "error"
12+
},
13+
"suspicious": {
14+
"noDeprecatedImports": "error",
15+
"noImportCycles": "error"
16+
}
17+
}
18+
}
19+
}

packages/cli/src/commands/check.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,7 @@ import process from "node:process";
33
import { detectLinter, parseFilePaths, shellOption } from "../utils";
44

55
const runBiomeCheck = (files: string[], passthrough: string[]): void => {
6-
const hasTypeAware = passthrough.includes("--type-aware");
7-
const filtered = passthrough.filter((arg) => arg !== "--type-aware");
8-
9-
const args = ["check", "--no-errors-on-unmatched"];
10-
11-
if (!hasTypeAware) {
12-
args.push("--skip=project");
13-
}
14-
15-
args.push(...filtered);
6+
const args = ["check", "--no-errors-on-unmatched", ...passthrough];
167

178
if (files.length > 0) {
189
args.push(...parseFilePaths(files));

packages/cli/src/commands/fix.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,7 @@ import process from "node:process";
33
import { detectLinter, parseFilePaths, shellOption } from "../utils";
44

55
const runBiomeFix = (files: string[], passthrough: string[]): void => {
6-
const hasTypeAware = passthrough.includes("--type-aware");
7-
const filtered = passthrough.filter((arg) => arg !== "--type-aware");
8-
9-
const args = ["check", "--write", "--no-errors-on-unmatched"];
10-
11-
if (!hasTypeAware) {
12-
args.push("--skip=project");
13-
}
14-
15-
args.push(...filtered);
6+
const args = ["check", "--write", "--no-errors-on-unmatched", ...passthrough];
167

178
if (files.length > 0) {
189
args.push(...parseFilePaths(files));

packages/cli/src/initialize.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,10 @@ export const installDependencies = async (
186186
}
187187

188188
// Add ultracite scripts to package.json
189-
const typeAwareFlag = typeAware ? " --type-aware" : "";
190189
await updatePackageJson({
191190
scripts: {
192-
check: `ultracite check${typeAwareFlag}`,
193-
fix: `ultracite fix${typeAwareFlag}`,
191+
check: "ultracite check",
192+
fix: "ultracite fix",
194193
},
195194
});
196195

@@ -307,7 +306,8 @@ export const upsertEditorConfig = async (
307306

308307
export const upsertBiomeConfig = async (
309308
frameworks?: (typeof options.frameworks)[number][],
310-
quiet = false
309+
quiet = false,
310+
typeAware = false
311311
) => {
312312
const s = spinner();
313313

@@ -319,7 +319,7 @@ export const upsertBiomeConfig = async (
319319
if (!quiet) {
320320
s.message("Biome configuration found, updating...");
321321
}
322-
await biome.update({ frameworks });
322+
await biome.update({ frameworks, typeAware });
323323
if (!quiet) {
324324
s.stop("Biome configuration updated.");
325325
}
@@ -329,7 +329,7 @@ export const upsertBiomeConfig = async (
329329
if (!quiet) {
330330
s.message("Biome configuration not found, creating...");
331331
}
332-
await biome.create({ frameworks });
332+
await biome.create({ frameworks, typeAware });
333333
if (!quiet) {
334334
s.stop("Biome configuration created.");
335335
}
@@ -949,7 +949,7 @@ export const initialize = async (flags?: InitializeFlags) => {
949949

950950
// Create config for selected linter
951951
if (linter === "biome") {
952-
await upsertBiomeConfig(frameworks, quiet);
952+
await upsertBiomeConfig(frameworks, quiet, opts["type-aware"]);
953953
}
954954
if (linter === "eslint") {
955955
await upsertEslintConfig(frameworks, quiet);

0 commit comments

Comments
 (0)