@shivaedev/quality 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +22 -3
  3. package/biome/preset.json +12 -1
  4. package/dist/cli/args.d.ts +2 -1
  5. package/dist/cli/args.d.ts.map +1 -1
  6. package/dist/cli/args.js +7 -5
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/fix.d.ts +1 -1
  9. package/dist/cli/fix.d.ts.map +1 -1
  10. package/dist/cli/fix.js +4 -2
  11. package/dist/cli/fix.js.map +1 -1
  12. package/dist/cli.js +1 -1
  13. package/dist/cli.js.map +1 -1
  14. package/dist/imports/departures.d.ts +9 -0
  15. package/dist/imports/departures.d.ts.map +1 -0
  16. package/dist/imports/departures.js +49 -0
  17. package/dist/imports/departures.js.map +1 -0
  18. package/dist/imports/specifiers.d.ts +9 -0
  19. package/dist/imports/specifiers.d.ts.map +1 -0
  20. package/dist/imports/specifiers.js +71 -0
  21. package/dist/imports/specifiers.js.map +1 -0
  22. package/dist/rules/biome.d.ts.map +1 -1
  23. package/dist/rules/biome.js +1 -1
  24. package/dist/rules/biome.js.map +1 -1
  25. package/dist/rules/built-in.d.ts +1 -1
  26. package/dist/rules/built-in.d.ts.map +1 -1
  27. package/dist/rules/built-in.js +2 -0
  28. package/dist/rules/built-in.js.map +1 -1
  29. package/dist/rules/imports/aliased.d.ts +2 -0
  30. package/dist/rules/imports/aliased.d.ts.map +1 -0
  31. package/dist/rules/imports/aliased.js +23 -0
  32. package/dist/rules/imports/aliased.js.map +1 -0
  33. package/package.json +1 -1
  34. package/src/cli/args.ts +8 -6
  35. package/src/cli/fix.ts +6 -2
  36. package/src/cli.ts +1 -1
  37. package/src/imports/departures.ts +72 -0
  38. package/src/imports/specifiers.ts +94 -0
  39. package/src/rules/biome.ts +2 -1
  40. package/src/rules/built-in.ts +2 -0
  41. package/src/rules/imports/aliased.ts +26 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 - 2026-10-04
4
+
5
+ ### Added
6
+
7
+ - Add `imports/aliased`. A relative import may name only a file in its own folder; one that goes up (`../format.ts`) or down into a subfolder (`./parts/deeper.ts`) is reported, in static imports, re-exports, side-effect imports, `import()`, `require()`, `import()` types, JSDoc `@import` tags and the paths of `vi.mock` and its kin. The message names the fix for where the import lands: a `#` alias from the `imports` of its own package's `package.json`, declared if none fits; the name of another workspace package, through a path its `exports` lists; or the name of an installed package. The rule only reports: the author picks the alias. Like every rule it is an error by default, so a repository that upgrades adopts it into its baseline.
8
+
9
+ ### Changed
10
+
11
+ - The Biome preset organizes imports in five groups: Node and Bun builtins, packages, `@shivaedev/*` packages, aliases (`#…`, `@/…`, `~…`) and relative paths. `quality fix` regroups the imports of every file once; until then each file out of order is a `biome/assist/source/organizeImports` finding.
12
+
13
+ ## 0.5.1 - 2026-10-04
14
+
15
+ ### Changed
16
+
17
+ - `quality fix` applies only changes that keep behavior: it sorts every `package.json` and applies Biome's formatting and assist actions, such as organized imports and sorted keys. Biome's safe lint fixes, which can change behavior (for example, `noProcessGlobal` adds `import process from "node:process"`, which a browser bundle cannot load), are applied only with the new `quality fix --lint`.
18
+
19
+ ### Fixed
20
+
21
+ - `quality fix` no longer hangs. Biome 2.5.14 never finishes applying the safe fix of `noProcessGlobal` to `globalThis.process`, so `quality fix` ran without end on any repository with that code. It no longer applies lint fixes by default; `quality fix --lint` still runs into this Biome bug.
22
+
3
23
  ## 0.5.0 - 2026-10-04
4
24
 
5
25
  ### Added
package/README.md CHANGED
@@ -197,13 +197,14 @@ A weakening the preset makes without such a declaration is reported at the line
197
197
 
198
198
  ### Imports
199
199
 
200
- Three rules read the import graph of the TypeScript and JavaScript modules among the sources. They build it once per run with the TypeScript compiler, without a bundler or another dependency.
200
+ Four rules read the imports of the TypeScript and JavaScript modules among the sources. They build it once per run with the TypeScript compiler, without a bundler or another dependency.
201
201
 
202
202
  | Rule | Reports | Options |
203
203
  | --- | --- | --- |
204
204
  | `imports/cycles` | Modules that import each other at run time | none |
205
205
  | `imports/resolvable` | An import that resolves to nothing | `generated` |
206
206
  | `imports/fences` | An import that crosses a fence the config declares | `fences` |
207
+ | `imports/aliased` | A relative import that leaves its own folder | none |
207
208
 
208
209
  Each import resolves the way the compiler resolves it, with the options of the nearest `tsconfig.json` and the projects it references, under bundler resolution: `paths`, `package.json` `imports` and `exports` and the `source` condition hold, so a workspace package resolves to its source. The graph reads static and dynamic imports, `export ... from`, `require()`, `require.resolve()`, `import.meta.resolve()` of a bare specifier (a relative one is URL arithmetic that never checks the path), `import()` types, `/// <reference types>`, `/// <reference path>` and JSDoc `@import` tags.
209
210
 
@@ -221,6 +222,23 @@ Each import resolves the way the compiler resolves it, with the options of the n
221
222
 
222
223
  The gate fails closed: when the sources hold no module at all, the imports rules stop the run instead of passing on an empty graph. Point `sources` at the code, or turn the rules off.
223
224
 
225
+ #### Aliases
226
+
227
+ A relative import names only a file in its own folder, such as `./format.ts`: the folder is one module. Every other import goes through an alias, so moving a file never rewrites a chain of `../`:
228
+
229
+ - the `imports` of the importer's nearest `package.json`, such as `"#lib/*": "./src/lib/*"`, or `"#shared/*": "@demo/shared/*"`, which names another workspace package;
230
+ - the name of another workspace package, as far as its `exports` reach.
231
+
232
+ An import that already goes through a tsconfig `paths` alias is not relative, so the rule leaves it alone.
233
+
234
+ `imports/aliased` reports a relative import that goes up (`../format.ts`) or down into a subfolder (`./parts/deeper.ts`, or `./widgets` for `widgets/index.ts`). It reads static imports, `export ... from`, side-effect imports, `import()`, `require()`, `import()` types, JSDoc `@import` tags and the paths of `vi.mock`, `vi.doMock`, `vi.unmock`, `vi.doUnmock`, `vi.importActual` and `vi.importMock`, so a mock keeps pointing at the module it replaces. An escaped path, such as `"\x2e\x2e/format.ts"`, counts as the path it spells. Each finding has the import as its subject. Its message names the fix for where the import lands:
235
+
236
+ - inside its own package: import through a `#` alias from the `imports` of the package's `package.json`, and declare one if none fits;
237
+ - inside another workspace package: import it by that package's name, through a path its `exports` lists;
238
+ - inside an installed package under `node_modules`: import that package by its name.
239
+
240
+ The rule only reports; nothing rewrites the import. Whoever writes the import picks the alias, because only they know which conditions their runtimes, bundler and tests use.
241
+
224
242
  #### Fences
225
243
 
226
244
  A fence is one prohibition, stated in the config without patterns:
@@ -358,7 +376,7 @@ An entry covers a rule's violations in one file, for good, and must say why. Wit
358
376
 
359
377
  ```text
360
378
  quality lint [--config <file>] [--warnings summary|all]
361
- quality fix [--config <file>]
379
+ quality fix [--config <file>] [--lint]
362
380
  quality baseline write [--config <file>] [--rule <id>]...
363
381
  quality baseline prune [--config <file>] [--against <ref>]
364
382
  quality baseline tighten [--config <file>] [--staged]
@@ -389,6 +407,7 @@ The preset sets:
389
407
 
390
408
  - **Formatting:** tabs, a line width of 150, double quotes, semicolons, trailing commas and operators at the start of a wrapped line.
391
409
  - **Lint:** every rule Biome recommends, at `error`, and a list of stricter rules on top, among them `noUnsafeTypeAssertion`, `useBlockStatements`, `noEqualsToNull`, `useNumericSeparators`, `useUnicodeRegex`, `useLiteralKeys`, `readonly T[]` arrays, `noFloatingPromises`, `noMisusedPromises`, `useExhaustiveSwitchCases`, `useExhaustiveDependencies`, `it` for every test, function declarations over function expressions, interfaces for object types, a cognitive complexity limit of 15, no nested ternaries, no barrel files and no `export *`.
410
+ - **Imports:** organized in five groups: Node and Bun builtins, packages, `@shivaedev/*` packages, aliases and relative paths. Biome counts as an alias a specifier that starts with `#`, `@/`, `~`, `$` or `%`; a tsconfig path such as `@app/*` sorts with the packages, and Biome's `noUndeclaredDependencies` takes it for one, so name aliases in a form Biome recognizes.
392
411
  - **Assist:** organized imports and sorted keys, attributes, enum members, interface members and properties. Keys are sorted in JSON and in object literals alike; `package.json` is left out, because `manifests/sorted` gives it the order npm users expect.
393
412
  - **Plugins:** GritQL rules that ban ambient time, randomness, `console` and `process.env` for Effect's services, and that ask `Effect.fn` for a literal span name shaped `Owner.operation`. They load from `./node_modules/@shivaedev/quality/biome/plugins`, so the package must be installed at the repository root.
394
413
  - Files ignored by git are skipped.
@@ -397,7 +416,7 @@ The preset turns off `noUnusedVariables` and `noUnusedFunctionParameters`, becau
397
416
 
398
417
  The `biome` rule runs `biome check` with the repository's config and reports each finding as `biome/<category>`, such as `biome/lint/style/useBlockStatements`, `biome/assist/source/useSortedKeys`, `biome/format` or `biome/plugin`, so Biome's findings go through the baseline like any other rule's. `adopt: ["biome"]` and `quality baseline write --rule biome` take in every Biome category at once. A finding below `error`, such as a rule a repository declared at `warn`, is not reported. The rule also asks for a root `biome.json` or `biome.jsonc` that extends the preset, and it takes no registry exceptions. A Biome config that Biome cannot load stops the run with Biome's message.
399
418
 
400
- `quality fix` sorts every `package.json`, then applies Biome's safe fixes, assist actions and formatting. An editor that runs Biome on save uses the same version when it resolves Biome from the root `node_modules`, so a repository that wants that installs `@biomejs/biome` at the version this package pins.
419
+ `quality fix` sorts every `package.json`, then applies Biome's formatting and assist actions, such as organized imports and sorted keys. None of these changes what the code does, so it is safe to run on a whole repository. `quality fix --lint` also applies Biome's safe lint fixes. Those can change behavior, such as `noProcessGlobal` adding `import process from "node:process"` to code that runs in a browser, so review what it changed. Biome 2.5.14 never finishes the `noProcessGlobal` fix on `globalThis.process`, so `--lint` hangs on such code until that line is changed by hand. An editor that runs Biome on save uses the same version when it resolves Biome from the root `node_modules`, so a repository that wants that installs `@biomejs/biome` at the version this package pins.
401
420
 
402
421
  ## Vitest projects
403
422
 
package/biome/preset.json CHANGED
@@ -3,7 +3,18 @@
3
3
  "actions": {
4
4
  "source": {
5
5
  "noDuplicateClasses": "on",
6
- "organizeImports": "on",
6
+ "organizeImports": {
7
+ "level": "on",
8
+ "options": {
9
+ "groups": [
10
+ [":NODE:", ":BUN:"],
11
+ [":PACKAGE:", ":PACKAGE_WITH_PROTOCOL:", ":URL:", "!@shivaedev/**"],
12
+ ["@shivaedev/**"],
13
+ [":ALIAS:"],
14
+ [":PATH:"]
15
+ ]
16
+ }
17
+ },
7
18
  "useSortedAttributes": "on",
8
19
  "useSortedEnumMembers": "on",
9
20
  "useSortedInterfaceMembers": "on",
@@ -6,6 +6,7 @@ export type Command = {
6
6
  } | {
7
7
  readonly _tag: "Fix";
8
8
  readonly config: string | undefined;
9
+ readonly lint: boolean;
9
10
  } | {
10
11
  readonly _tag: "BaselineWrite";
11
12
  readonly config: string | undefined;
@@ -36,6 +37,6 @@ export type Parsed = {
36
37
  readonly _tag: "Usage";
37
38
  readonly problem: string;
38
39
  };
39
- export declare const USAGE = "Usage:\n quality lint [--config <file>] [--warnings summary|all]\n quality fix [--config <file>]\n quality baseline write [--config <file>] [--rule <id>]...\n quality baseline prune [--config <file>] [--against <ref>]\n quality baseline tighten [--config <file>] [--staged]\n quality baseline check [--config <file>] [--against <ref>]\n quality baseline migrate [--config <file>] [--from <file>]\n\nlint Run every rule. Exits 1 on an error-level violation, a file over its baseline, a stale registry entry\n or a baseline entry for a rule that is off or unknown.\nfix Apply Biome's safe fixes, assist actions and formatting.\nbaseline write Record current error-level violations. Creates the baseline, or records the named rules again, replacing their entries.\nbaseline prune Drop fixed debt, lower entries to what is left and carry entries to files git saw move. Never adds or raises an entry.\nbaseline tighten Prune only the entries of files changed since HEAD, or with --staged, in the index.\nbaseline check Compare the baseline with its version at the merge base. Exits 1 when it gained an entry or a higher count.\nbaseline migrate Move a baseline from the earlier JSON format (--from, quality/baseline.json by default) to the configured file.\n\n--config <file> Config file; its directory is the repository root. Defaults to ./quality.config.ts.\n--against <ref> The branch the work merges into. Defaults to origin/HEAD, then origin/main, then origin/master.\nExit codes: 0 passed, 1 failed the gate, 2 could not run.";
40
+ export declare const USAGE = "Usage:\n quality lint [--config <file>] [--warnings summary|all]\n quality fix [--config <file>] [--lint]\n quality baseline write [--config <file>] [--rule <id>]...\n quality baseline prune [--config <file>] [--against <ref>]\n quality baseline tighten [--config <file>] [--staged]\n quality baseline check [--config <file>] [--against <ref>]\n quality baseline migrate [--config <file>] [--from <file>]\n\nlint Run every rule. Exits 1 on an error-level violation, a file over its baseline, a stale registry entry\n or a baseline entry for a rule that is off or unknown.\nfix Sort every package.json and apply Biome's formatting and assist actions, such as organized imports and sorted keys.\n With --lint, also apply Biome's safe lint fixes, which can change behavior.\nbaseline write Record current error-level violations. Creates the baseline, or records the named rules again, replacing their entries.\nbaseline prune Drop fixed debt, lower entries to what is left and carry entries to files git saw move. Never adds or raises an entry.\nbaseline tighten Prune only the entries of files changed since HEAD, or with --staged, in the index.\nbaseline check Compare the baseline with its version at the merge base. Exits 1 when it gained an entry or a higher count.\nbaseline migrate Move a baseline from the earlier JSON format (--from, quality/baseline.json by default) to the configured file.\n\n--config <file> Config file; its directory is the repository root. Defaults to ./quality.config.ts.\n--against <ref> The branch the work merges into. Defaults to origin/HEAD, then origin/main, then origin/master.\nExit codes: 0 passed, 1 failed the gate, 2 could not run.";
40
41
  export declare const parseCommand: (args: readonly string[]) => Parsed;
41
42
  //# sourceMappingURL=args.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,MAAM,OAAO,GAChB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAChG;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC7D;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC1G;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC7G;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC7G;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACnG;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC5G;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7B,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnI,eAAO,MAAM,KAAK,2jDAoBwC,CAAC;AAiE3D,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAmBtD,CAAC"}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,MAAM,OAAO,GAChB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAChG;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACrF;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC1G;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC7G;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC7G;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACnG;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC5G;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7B,MAAM,MAAM,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnI,eAAO,MAAM,KAAK,8tDAqBwC,CAAC;AAkE3D,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAmBtD,CAAC"}
package/dist/cli/args.js CHANGED
@@ -2,7 +2,7 @@ import { parseArgs } from "node:util";
2
2
  import { Result } from "effect";
3
3
  export const USAGE = `Usage:
4
4
  quality lint [--config <file>] [--warnings summary|all]
5
- quality fix [--config <file>]
5
+ quality fix [--config <file>] [--lint]
6
6
  quality baseline write [--config <file>] [--rule <id>]...
7
7
  quality baseline prune [--config <file>] [--against <ref>]
8
8
  quality baseline tighten [--config <file>] [--staged]
@@ -11,7 +11,8 @@ export const USAGE = `Usage:
11
11
 
12
12
  lint Run every rule. Exits 1 on an error-level violation, a file over its baseline, a stale registry entry
13
13
  or a baseline entry for a rule that is off or unknown.
14
- fix Apply Biome's safe fixes, assist actions and formatting.
14
+ fix Sort every package.json and apply Biome's formatting and assist actions, such as organized imports and sorted keys.
15
+ With --lint, also apply Biome's safe lint fixes, which can change behavior.
15
16
  baseline write Record current error-level violations. Creates the baseline, or records the named rules again, replacing their entries.
16
17
  baseline prune Drop fixed debt, lower entries to what is left and carry entries to files git saw move. Never adds or raises an entry.
17
18
  baseline tighten Prune only the entries of files changed since HEAD, or with --staged, in the index.
@@ -26,20 +27,21 @@ const OPTIONS = {
26
27
  config: { type: "string" },
27
28
  from: { type: "string" },
28
29
  help: { short: "h", type: "boolean" },
30
+ lint: { type: "boolean" },
29
31
  rule: { multiple: true, type: "string" },
30
32
  staged: { type: "boolean" },
31
33
  warnings: { type: "string" },
32
34
  };
33
35
  const usage = (problem) => ({ _tag: "Usage", problem });
34
36
  const parsed = (command) => ({ _tag: "Parsed", command });
35
- const COMMAND_OPTIONS = ["against", "from", "rule", "staged", "warnings"];
37
+ const COMMAND_OPTIONS = ["against", "from", "lint", "rule", "staged", "warnings"];
36
38
  const ACCEPTS = {
37
39
  "baseline check": ["against"],
38
40
  "baseline migrate": ["from"],
39
41
  "baseline prune": ["against"],
40
42
  "baseline tighten": ["staged"],
41
43
  "baseline write": ["rule"],
42
- fix: [],
44
+ fix: ["lint"],
43
45
  lint: ["warnings"],
44
46
  };
45
47
  const misplaced = (name, values) => {
@@ -58,7 +60,7 @@ const commandFor = (name, values) => {
58
60
  : usage(`--warnings takes summary or all, not "${warnings}".`);
59
61
  }
60
62
  case "fix":
61
- return parsed({ _tag: "Fix", config });
63
+ return parsed({ _tag: "Fix", config, lint: values.lint === true });
62
64
  case "baseline write":
63
65
  return parsed({ _tag: "BaselineWrite", config, rules: values.rule ?? [] });
64
66
  case "baseline prune":
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
package/dist/cli/fix.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Effect, type FileSystem } from "effect";
2
2
  import { SetupFailure } from "../failure.ts";
3
- export declare function fix(cwd: string, config: string | undefined): Effect.Effect<void, SetupFailure, FileSystem.FileSystem>;
3
+ export declare function fix(cwd: string, config: string | undefined, lint: boolean): Effect.Effect<void, SetupFailure, FileSystem.FileSystem>;
4
4
  //# sourceMappingURL=fix.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fix.d.ts","sourceRoot":"","sources":["../../src/cli/fix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAI7C,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,CAAC,UAAU,CAAC,CAWrH"}
1
+ {"version":3,"file":"fix.d.ts","sourceRoot":"","sources":["../../src/cli/fix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAQ7C,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,CAAC,UAAU,CAAC,CAWpI"}
package/dist/cli/fix.js CHANGED
@@ -4,14 +4,16 @@ import { loadConfig } from "../config/load.js";
4
4
  import { SetupFailure } from "../failure.js";
5
5
  import { sortManifests } from "../manifests/sorted.js";
6
6
  import { plural } from "../report/plural.js";
7
- export function fix(cwd, config) {
7
+ const LANGUAGES = ["javascript", "json", "css", "graphql", "grit", "html"];
8
+ const WITHOUT_LINT = ["--linter-enabled=false", ...LANGUAGES.map((language) => `--${language}-linter-enabled=false`)];
9
+ export function fix(cwd, config, lint) {
8
10
  return Effect.gen(function* () {
9
11
  const { root } = yield* loadConfig(cwd, config);
10
12
  const sorted = yield* Effect.mapError(sortManifests(root), (failure) => new SetupFailure({ message: failure.message }));
11
13
  yield* Console.log(`quality: sort-package-json rewrote ${plural(sorted, "manifest")}.`);
12
14
  const report = yield* Effect.tryPromise({
13
15
  catch: (cause) => new SetupFailure({ message: cause instanceof Error ? cause.message : String(cause) }),
14
- try: () => biomeReport(root, ["check", "--write"]),
16
+ try: () => biomeReport(root, ["check", "--write", ...(lint ? [] : WITHOUT_LINT)]),
15
17
  });
16
18
  yield* Console.log(`quality: Biome rewrote ${plural(report.summary.changed, "file")}.`);
17
19
  });
@@ -1 +1 @@
1
- {"version":3,"file":"fix.js","sourceRoot":"","sources":["../../src/cli/fix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAmB,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,MAA0B;IAC1D,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACxH,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,sCAAsC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;QACxF,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACvG,GAAG,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;SAClD,CAAC,CAAC;QACH,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"fix.js","sourceRoot":"","sources":["../../src/cli/fix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAmB,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,MAAM,SAAS,GAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9F,MAAM,YAAY,GAAsB,CAAC,wBAAwB,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,QAAQ,uBAAuB,CAAC,CAAC,CAAC;AAEzI,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,MAA0B,EAAE,IAAa;IACzE,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACxH,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,sCAAsC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;QACxF,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YACvC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACvG,GAAG,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;SACjF,CAAC,CAAC;QACH,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;AACJ,CAAC"}
package/dist/cli.js CHANGED
@@ -22,7 +22,7 @@ const program = Effect.gen(function* () {
22
22
  case "Lint":
23
23
  return yield* lint(process.cwd(), command.config, command.warnings);
24
24
  case "Fix":
25
- return yield* fix(process.cwd(), command.config);
25
+ return yield* fix(process.cwd(), command.config, command.lint);
26
26
  case "BaselineWrite":
27
27
  return yield* writeBaseline(process.cwd(), command.config, command.rules);
28
28
  case "BaselinePrune":
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,OAAO,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACtB,KAAK,MAAM;YACV,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,KAAK,MAAM;YACV,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACrE,KAAK,KAAK;YACT,OAAO,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAClD,KAAK,eAAe;YACnB,OAAO,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3E,KAAK,eAAe;YACnB,OAAO,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7E,KAAK,iBAAiB;YACrB,OAAO,KAAK,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9E,KAAK,eAAe;YACnB,OAAO,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7E,KAAK,iBAAiB;YACrB,OAAO,KAAK,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnC,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,OAAO,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACtB,KAAK,MAAM;YACV,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,KAAK,MAAM;YACV,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QACrE,KAAK,KAAK;YACT,OAAO,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAChE,KAAK,eAAe;YACnB,OAAO,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3E,KAAK,eAAe;YACnB,OAAO,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7E,KAAK,iBAAiB;YACrB,OAAO,KAAK,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9E,KAAK,eAAe;YACnB,OAAO,KAAK,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7E,KAAK,iBAAiB;YACrB,OAAO,KAAK,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { RuleInputs } from "../rule.ts";
2
+ import { type SpecifierSite } from "./specifiers.ts";
3
+ export interface Departure extends SpecifierSite {
4
+ readonly file: string;
5
+ readonly installed: string | undefined;
6
+ readonly workspace: string | undefined;
7
+ }
8
+ export declare function departures(inputs: RuleInputs): Promise<readonly Departure[]>;
9
+ //# sourceMappingURL=departures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"departures.d.ts","sourceRoot":"","sources":["../../src/imports/departures.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,iBAAiB,CAAC;AAGrE,MAAM,WAAW,SAAU,SAAQ,aAAa;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AA4CD,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAalF"}
@@ -0,0 +1,49 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { posix } from "node:path";
3
+ import { parse } from "../rules/syntax.js";
4
+ import { projectsFor } from "./projects.js";
5
+ import { resolveImport } from "./resolve.js";
6
+ import { specifierSites } from "./specifiers.js";
7
+ import { packageOf, workspacePackages } from "./workspace.js";
8
+ const QUERY = /\?.*$/u;
9
+ const RELATIVE = /^\.\.?(?:\/|$)/u;
10
+ const MAY_BE_RELATIVE = /["'`]\.\.?[/"'`]|\\/u;
11
+ const TRAILING_SLASHES = /\/+$/u;
12
+ function noAmbient() {
13
+ return false;
14
+ }
15
+ function leavesLexically(path) {
16
+ const normal = posix.normalize(path).replace(TRAILING_SLASHES, "");
17
+ return normal === ".." || normal.startsWith("../") || normal.includes("/");
18
+ }
19
+ // A relative import leaves its folder when its path climbs or descends, or when it names a folder whose index file lies below.
20
+ function departureAt(context, site) {
21
+ const path = site.specifier.replace(QUERY, "");
22
+ if (!RELATIVE.test(path)) {
23
+ return undefined;
24
+ }
25
+ const request = { kind: "import", line: site.line, specifier: site.specifier, type: site.type };
26
+ const target = resolveImport(context.root, noAmbient, request, context.from, context.project);
27
+ const local = target?.kind === "file" ? target.path : undefined;
28
+ if (!(leavesLexically(path) || (local !== undefined && posix.dirname(local) !== posix.dirname(context.path)))) {
29
+ return undefined;
30
+ }
31
+ const owner = local === undefined ? undefined : packageOf(context.packages, local);
32
+ const workspace = owner !== undefined && owner !== packageOf(context.packages, context.path) ? owner.name : undefined;
33
+ return { ...site, file: context.path, installed: target?.kind === "external" ? target.package : undefined, workspace };
34
+ }
35
+ export async function departures(inputs) {
36
+ const root = realpathSync(inputs.root);
37
+ const projectOf = projectsFor(root);
38
+ const packages = await workspacePackages(inputs);
39
+ return inputs.sources.flatMap((file) => {
40
+ const syntax = MAY_BE_RELATIVE.test(file.text) ? parse(file) : undefined;
41
+ if (syntax === undefined) {
42
+ return [];
43
+ }
44
+ const from = posix.join(root, file.path);
45
+ const context = { from, packages, path: file.path, project: projectOf(from), root };
46
+ return specifierSites(syntax).flatMap((site) => departureAt(context, site) ?? []);
47
+ });
48
+ }
49
+ //# sourceMappingURL=departures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"departures.js","sourceRoot":"","sources":["../../src/imports/departures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import ts from "typescript";
2
+ export interface SpecifierSite {
3
+ readonly line: number;
4
+ readonly specifier: string;
5
+ readonly start: number;
6
+ readonly type: boolean;
7
+ }
8
+ export declare function specifierSites(source: ts.SourceFile): readonly SpecifierSite[];
9
+ //# sourceMappingURL=specifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specifiers.d.ts","sourceRoot":"","sources":["../../src/imports/specifiers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACvB;AAwED,wBAAgB,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,SAAS,aAAa,EAAE,CAc9E"}
@@ -0,0 +1,71 @@
1
+ import ts from "typescript";
2
+ const MOCKS = new Set(["doMock", "doUnmock", "importActual", "importMock", "mock", "unmock"]);
3
+ const MAY_CALL = /\b(?:import|require)\s*\(|\bvi\s*\.\s*(?:doMock|doUnmock|importActual|importMock|mock|unmock)\b/u;
4
+ function declared(node) {
5
+ if (ts.isImportDeclaration(node)) {
6
+ return { node: node.moduleSpecifier, type: node.importClause?.isTypeOnly === true };
7
+ }
8
+ if (ts.isExportDeclaration(node)) {
9
+ return { node: node.moduleSpecifier, type: node.isTypeOnly };
10
+ }
11
+ if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference)) {
12
+ return { node: node.moduleReference.expression, type: node.isTypeOnly };
13
+ }
14
+ return undefined;
15
+ }
16
+ function isMock(callee) {
17
+ return (ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression) && callee.expression.text === "vi" && MOCKS.has(callee.name.text));
18
+ }
19
+ function called(node) {
20
+ if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) {
21
+ return { node: node.argument.literal, type: true };
22
+ }
23
+ if (!ts.isCallExpression(node)) {
24
+ return undefined;
25
+ }
26
+ const callee = node.expression;
27
+ const loads = callee.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(callee) && callee.text === "require") || isMock(callee);
28
+ return loads ? { node: node.arguments[0], type: false } : undefined;
29
+ }
30
+ function siteOf(source, found) {
31
+ const literal = found?.node;
32
+ if (found === undefined || literal === undefined || !ts.isStringLiteralLike(literal)) {
33
+ return [];
34
+ }
35
+ const start = literal.getStart(source);
36
+ return [{ line: source.getLineAndCharacterOfPosition(start).line + 1, specifier: literal.text, start, type: found.type }];
37
+ }
38
+ const JSDOC_IMPORT = /^[ \t]*(?:\/\*\*|\*)?[ \t]*@import\b[^"'`]*?\bfrom\s*(?<quote>["'])(?<path>[^"'\r\n]*)\k<quote>/gmu;
39
+ function siteAt(source, start, specifier) {
40
+ return { line: source.getLineAndCharacterOfPosition(start).line + 1, specifier, start, type: true };
41
+ }
42
+ // A node keeps only its last JSDoc block, so @import tags are read from every comment before it.
43
+ function jsDocImports(source, node, seen) {
44
+ return (ts.getLeadingCommentRanges(source.text, node.pos) ?? []).flatMap((range) => {
45
+ const comment = source.text.slice(range.pos, range.end);
46
+ if (seen.has(range.pos) || !comment.startsWith("/**")) {
47
+ return [];
48
+ }
49
+ seen.add(range.pos);
50
+ return [...comment.matchAll(JSDOC_IMPORT)].map((match) => {
51
+ const path = match.groups?.path ?? "";
52
+ return siteAt(source, range.pos + match.index + match[0].length - path.length - 1, path);
53
+ });
54
+ });
55
+ }
56
+ export function specifierSites(source) {
57
+ const sites = [];
58
+ const nested = MAY_CALL.test(source.text);
59
+ const documented = source.text.includes("@import");
60
+ const comments = new Set();
61
+ function visit(node) {
62
+ sites.push(...siteOf(source, declared(node) ?? (nested ? called(node) : undefined)));
63
+ if (documented) {
64
+ sites.push(...jsDocImports(source, node, comments));
65
+ }
66
+ ts.forEachChild(node, visit);
67
+ }
68
+ visit(source);
69
+ return [...new Map(sites.map((site) => [site.start, site])).values()].sort((left, right) => left.start - right.start);
70
+ }
71
+ //# sourceMappingURL=specifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specifiers.js","sourceRoot":"","sources":["../../src/imports/specifiers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAc5B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE9F,MAAM,QAAQ,GAAG,kGAAkG,CAAC;AAEpH,SAAS,QAAQ,CAAC,IAAa;IAC9B,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,KAAK,IAAI,EAAE,CAAC;IACrF,CAAC;IACD,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;QAC9F,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,MAAM,CAAC,MAAqB;IACpC,OAAO,CACN,EAAE,CAAC,0BAA0B,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAC7I,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAa;IAC5B,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IACtI,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACrE,CAAC;AAED,SAAS,MAAM,CAAC,MAAqB,EAAE,KAAwB;IAC9D,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC;IAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;QACtF,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3H,CAAC;AAED,MAAM,YAAY,GAAG,oGAAoG,CAAC;AAE1H,SAAS,MAAM,CAAC,MAAqB,EAAE,KAAa,EAAE,SAAiB;IACtE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACrG,CAAC;AAED,iGAAiG;AACjG,SAAS,YAAY,CAAC,MAAqB,EAAE,IAAa,EAAE,IAAiB;IAC5E,OAAO,CAAC,EAAE,CAAC,uBAAuB,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QAClF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpB,OAAO,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACxD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;YACtC,OAAO,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1F,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAqB;IACnD,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,SAAS,KAAK,CAAC,IAAa;QAC3B,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACrF,IAAI,UAAU,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,CAAC;IACd,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;AACvH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"biome.d.ts","sourceRoot":"","sources":["../../src/rules/biome.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,IAAI,EAAmB,MAAM,YAAY,CAAC;AAGlF,eAAO,MAAM,MAAM,6BAA6B,CAAC;AA0CjD,eAAO,MAAM,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,CAA+B,CAAC"}
1
+ {"version":3,"file":"biome.d.ts","sourceRoot":"","sources":["../../src/rules/biome.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,IAAI,EAAmB,MAAM,YAAY,CAAC;AAGlF,eAAO,MAAM,MAAM,6BAA6B,CAAC;AA2CjD,eAAO,MAAM,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,CAA+B,CAAC"}
@@ -32,7 +32,7 @@ const bridge = defineRule({
32
32
  const [preset, report] = await Promise.all([presetFinding(readText), biomeReport(root, ["check"])]);
33
33
  return [...preset, ...report.diagnostics.filter((diagnostic) => FAILING.has(diagnostic.severity)).map(findingOf)];
34
34
  },
35
- description: "Biome's lint, format and assist findings under the shared preset. `quality fix` applies the safe fixes and the formatting.",
35
+ description: "Biome's lint, format and assist findings under the shared preset. `quality fix` applies the formatting and assist actions; `quality fix --lint` also applies the safe lint fixes.",
36
36
  id: "biome",
37
37
  registrable: false,
38
38
  });
@@ -1 +1 @@
1
- {"version":3,"file":"biome.js","sourceRoot":"","sources":["../../src/rules/biome.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"biome.js","sourceRoot":"","sources":["../../src/rules/biome.ts"],"names":[],"mappings":"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"}
@@ -17,5 +17,5 @@ export declare const builtInRules: readonly [import("../rule.ts").Rule<"structur
17
17
  readonly fences?: readonly import("../index.ts").Fence[];
18
18
  }>, import("../rule.ts").Rule<"imports/resolvable", {
19
19
  readonly generated?: readonly string[];
20
- }>, import("../rule.ts").Rule<"manifests/sorted", undefined>];
20
+ }>, import("../rule.ts").Rule<"imports/aliased", undefined>, import("../rule.ts").Rule<"manifests/sorted", undefined>];
21
21
  //# sourceMappingURL=built-in.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"built-in.d.ts","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;6DAiBf,CAAC"}
1
+ {"version":3,"file":"built-in.d.ts","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;sHAkBf,CAAC"}
@@ -6,6 +6,7 @@ import { noJsdoc } from "./comments/no-jsdoc.js";
6
6
  import { noLineReference } from "./comments/no-line-reference.js";
7
7
  import { noPrReference } from "./comments/no-pr-reference.js";
8
8
  import { noTodo } from "./comments/no-todo.js";
9
+ import { importsAliased } from "./imports/aliased.js";
9
10
  import { importCycles } from "./imports/cycles.js";
10
11
  import { importFences } from "./imports/fences.js";
11
12
  import { importsResolvable } from "./imports/resolvable.js";
@@ -30,6 +31,7 @@ export const builtInRules = [
30
31
  importCycles,
31
32
  importFences,
32
33
  importsResolvable,
34
+ importsAliased,
33
35
  manifestsSorted,
34
36
  ];
35
37
  //# sourceMappingURL=built-in.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"built-in.js","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,QAAQ;IACR,OAAO;IACP,eAAe;IACf,aAAa;IACb,QAAQ;IACR,MAAM;IACN,mBAAmB;IACnB,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,cAAc;IACd,KAAK;IACL,YAAY;IACZ,YAAY;IACZ,iBAAiB;IACjB,eAAe;CACN,CAAC"}
1
+ {"version":3,"file":"built-in.js","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,QAAQ;IACR,OAAO;IACP,eAAe;IACf,aAAa;IACb,QAAQ;IACR,MAAM;IACN,mBAAmB;IACnB,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,cAAc;IACd,KAAK;IACL,YAAY;IACZ,YAAY;IACZ,iBAAiB;IACjB,cAAc;IACd,eAAe;CACN,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const importsAliased: import("../../rule.ts").Rule<"imports/aliased", undefined>;
2
+ //# sourceMappingURL=aliased.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliased.d.ts","sourceRoot":"","sources":["../../../src/rules/imports/aliased.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,cAAc,4DAWzB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { departures } from "../../imports/departures.js";
2
+ import { defineRule } from "../../rule.js";
3
+ function messageOf({ installed, specifier, workspace }) {
4
+ const leaves = `"${specifier}" leaves its folder.`;
5
+ if (installed !== undefined) {
6
+ return `${leaves} It reaches into the installed package ${installed}; import that package by its name.`;
7
+ }
8
+ if (workspace !== undefined) {
9
+ return `${leaves} It reaches into the workspace package ${workspace}; import it by that name, through a path its package.json "exports" lists.`;
10
+ }
11
+ return `${leaves} Import it through a "#…" alias from the "imports" of its package.json, and declare one there if none fits.`;
12
+ }
13
+ export const importsAliased = defineRule({
14
+ check: async (inputs) => (await departures(inputs)).map((departure) => ({
15
+ file: departure.file,
16
+ line: departure.line,
17
+ message: messageOf(departure),
18
+ subject: departure.specifier,
19
+ })),
20
+ description: "A relative import names only a file in its own folder. Every other import goes through an alias: the package.json `imports` of its package or another workspace package's name.",
21
+ id: "imports/aliased",
22
+ });
23
+ //# sourceMappingURL=aliased.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliased.js","sourceRoot":"","sources":["../../../src/rules/imports/aliased.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,SAAS,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAa;IAChE,MAAM,MAAM,GAAG,IAAI,SAAS,sBAAsB,CAAC;IACnD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,GAAG,MAAM,0CAA0C,SAAS,oCAAoC,CAAC;IACzG,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,GAAG,MAAM,0CAA0C,SAAS,4EAA4E,CAAC;IACjJ,CAAC;IACD,OAAO,GAAG,MAAM,6GAA6G,CAAC;AAC/H,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;IACxC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CACvB,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC;QAC7B,OAAO,EAAE,SAAS,CAAC,SAAS;KAC5B,CAAC,CAAC;IACJ,WAAW,EACV,iLAAiL;IAClL,EAAE,EAAE,iBAAiB;CACrB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/quality",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Repository quality gate with typed rules, a shrink-only baseline and a registry of reasoned exceptions",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/quality#readme",
6
6
  "bugs": {
package/src/cli/args.ts CHANGED
@@ -4,7 +4,7 @@ import type { WarningDetail } from "../report/render.ts";
4
4
 
5
5
  export type Command =
6
6
  | { readonly _tag: "Lint"; readonly config: string | undefined; readonly warnings: WarningDetail }
7
- | { readonly _tag: "Fix"; readonly config: string | undefined }
7
+ | { readonly _tag: "Fix"; readonly config: string | undefined; readonly lint: boolean }
8
8
  | { readonly _tag: "BaselineWrite"; readonly config: string | undefined; readonly rules: readonly string[] }
9
9
  | { readonly _tag: "BaselinePrune"; readonly config: string | undefined; readonly against: string | undefined }
10
10
  | { readonly _tag: "BaselineCheck"; readonly config: string | undefined; readonly against: string | undefined }
@@ -16,7 +16,7 @@ export type Parsed = { readonly _tag: "Parsed"; readonly command: Command } | {
16
16
 
17
17
  export const USAGE = `Usage:
18
18
  quality lint [--config <file>] [--warnings summary|all]
19
- quality fix [--config <file>]
19
+ quality fix [--config <file>] [--lint]
20
20
  quality baseline write [--config <file>] [--rule <id>]...
21
21
  quality baseline prune [--config <file>] [--against <ref>]
22
22
  quality baseline tighten [--config <file>] [--staged]
@@ -25,7 +25,8 @@ export const USAGE = `Usage:
25
25
 
26
26
  lint Run every rule. Exits 1 on an error-level violation, a file over its baseline, a stale registry entry
27
27
  or a baseline entry for a rule that is off or unknown.
28
- fix Apply Biome's safe fixes, assist actions and formatting.
28
+ fix Sort every package.json and apply Biome's formatting and assist actions, such as organized imports and sorted keys.
29
+ With --lint, also apply Biome's safe lint fixes, which can change behavior.
29
30
  baseline write Record current error-level violations. Creates the baseline, or records the named rules again, replacing their entries.
30
31
  baseline prune Drop fixed debt, lower entries to what is left and carry entries to files git saw move. Never adds or raises an entry.
31
32
  baseline tighten Prune only the entries of files changed since HEAD, or with --staged, in the index.
@@ -41,6 +42,7 @@ const OPTIONS = {
41
42
  config: { type: "string" },
42
43
  from: { type: "string" },
43
44
  help: { short: "h", type: "boolean" },
45
+ lint: { type: "boolean" },
44
46
  rule: { multiple: true, type: "string" },
45
47
  staged: { type: "boolean" },
46
48
  warnings: { type: "string" },
@@ -54,7 +56,7 @@ type Values = ReturnType<typeof parseArgs<{ options: typeof OPTIONS; allowPositi
54
56
 
55
57
  type Option = Exclude<keyof Values, "config" | "help">;
56
58
 
57
- const COMMAND_OPTIONS: readonly Option[] = ["against", "from", "rule", "staged", "warnings"];
59
+ const COMMAND_OPTIONS: readonly Option[] = ["against", "from", "lint", "rule", "staged", "warnings"];
58
60
 
59
61
  const ACCEPTS: Readonly<Record<string, readonly Option[]>> = {
60
62
  "baseline check": ["against"],
@@ -62,7 +64,7 @@ const ACCEPTS: Readonly<Record<string, readonly Option[]>> = {
62
64
  "baseline prune": ["against"],
63
65
  "baseline tighten": ["staged"],
64
66
  "baseline write": ["rule"],
65
- fix: [],
67
+ fix: ["lint"],
66
68
  lint: ["warnings"],
67
69
  };
68
70
 
@@ -85,7 +87,7 @@ const commandFor = (name: string, values: Values): Parsed => {
85
87
  : usage(`--warnings takes summary or all, not "${warnings}".`);
86
88
  }
87
89
  case "fix":
88
- return parsed({ _tag: "Fix", config });
90
+ return parsed({ _tag: "Fix", config, lint: values.lint === true });
89
91
  case "baseline write":
90
92
  return parsed({ _tag: "BaselineWrite", config, rules: values.rule ?? [] });
91
93
  case "baseline prune":
package/src/cli/fix.ts CHANGED
@@ -5,14 +5,18 @@ import { SetupFailure } from "../failure.ts";
5
5
  import { sortManifests } from "../manifests/sorted.ts";
6
6
  import { plural } from "../report/plural.ts";
7
7
 
8
- export function fix(cwd: string, config: string | undefined): Effect.Effect<void, SetupFailure, FileSystem.FileSystem> {
8
+ const LANGUAGES: readonly string[] = ["javascript", "json", "css", "graphql", "grit", "html"];
9
+
10
+ const WITHOUT_LINT: readonly string[] = ["--linter-enabled=false", ...LANGUAGES.map((language) => `--${language}-linter-enabled=false`)];
11
+
12
+ export function fix(cwd: string, config: string | undefined, lint: boolean): Effect.Effect<void, SetupFailure, FileSystem.FileSystem> {
9
13
  return Effect.gen(function* () {
10
14
  const { root } = yield* loadConfig(cwd, config);
11
15
  const sorted = yield* Effect.mapError(sortManifests(root), (failure) => new SetupFailure({ message: failure.message }));
12
16
  yield* Console.log(`quality: sort-package-json rewrote ${plural(sorted, "manifest")}.`);
13
17
  const report = yield* Effect.tryPromise({
14
18
  catch: (cause) => new SetupFailure({ message: cause instanceof Error ? cause.message : String(cause) }),
15
- try: () => biomeReport(root, ["check", "--write"]),
19
+ try: () => biomeReport(root, ["check", "--write", ...(lint ? [] : WITHOUT_LINT)]),
16
20
  });
17
21
  yield* Console.log(`quality: Biome rewrote ${plural(report.summary.changed, "file")}.`);
18
22
  });
package/src/cli.ts CHANGED
@@ -24,7 +24,7 @@ const program = Effect.gen(function* () {
24
24
  case "Lint":
25
25
  return yield* lint(process.cwd(), command.config, command.warnings);
26
26
  case "Fix":
27
- return yield* fix(process.cwd(), command.config);
27
+ return yield* fix(process.cwd(), command.config, command.lint);
28
28
  case "BaselineWrite":
29
29
  return yield* writeBaseline(process.cwd(), command.config, command.rules);
30
30
  case "BaselinePrune":
@@ -0,0 +1,72 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { posix } from "node:path";
3
+ import type { RuleInputs } from "../rule.ts";
4
+ import { parse } from "../rules/syntax.ts";
5
+ import type { ImportRequest } from "./extract.ts";
6
+ import { type Project, projectsFor } from "./projects.ts";
7
+ import { resolveImport } from "./resolve.ts";
8
+ import { type SpecifierSite, specifierSites } from "./specifiers.ts";
9
+ import { packageOf, type WorkspacePackage, workspacePackages } from "./workspace.ts";
10
+
11
+ export interface Departure extends SpecifierSite {
12
+ readonly file: string;
13
+ readonly installed: string | undefined;
14
+ readonly workspace: string | undefined;
15
+ }
16
+
17
+ const QUERY = /\?.*$/u;
18
+
19
+ const RELATIVE = /^\.\.?(?:\/|$)/u;
20
+
21
+ const MAY_BE_RELATIVE = /["'`]\.\.?[/"'`]|\\/u;
22
+
23
+ const TRAILING_SLASHES = /\/+$/u;
24
+
25
+ function noAmbient(): boolean {
26
+ return false;
27
+ }
28
+
29
+ function leavesLexically(path: string): boolean {
30
+ const normal = posix.normalize(path).replace(TRAILING_SLASHES, "");
31
+ return normal === ".." || normal.startsWith("../") || normal.includes("/");
32
+ }
33
+
34
+ interface FileContext {
35
+ readonly from: string;
36
+ readonly packages: readonly WorkspacePackage[];
37
+ readonly path: string;
38
+ readonly project: Project;
39
+ readonly root: string;
40
+ }
41
+
42
+ // A relative import leaves its folder when its path climbs or descends, or when it names a folder whose index file lies below.
43
+ function departureAt(context: FileContext, site: SpecifierSite): Departure | undefined {
44
+ const path = site.specifier.replace(QUERY, "");
45
+ if (!RELATIVE.test(path)) {
46
+ return undefined;
47
+ }
48
+ const request: ImportRequest = { kind: "import", line: site.line, specifier: site.specifier, type: site.type };
49
+ const target = resolveImport(context.root, noAmbient, request, context.from, context.project);
50
+ const local = target?.kind === "file" ? target.path : undefined;
51
+ if (!(leavesLexically(path) || (local !== undefined && posix.dirname(local) !== posix.dirname(context.path)))) {
52
+ return undefined;
53
+ }
54
+ const owner = local === undefined ? undefined : packageOf(context.packages, local);
55
+ const workspace = owner !== undefined && owner !== packageOf(context.packages, context.path) ? owner.name : undefined;
56
+ return { ...site, file: context.path, installed: target?.kind === "external" ? target.package : undefined, workspace };
57
+ }
58
+
59
+ export async function departures(inputs: RuleInputs): Promise<readonly Departure[]> {
60
+ const root = realpathSync(inputs.root);
61
+ const projectOf = projectsFor(root);
62
+ const packages = await workspacePackages(inputs);
63
+ return inputs.sources.flatMap((file) => {
64
+ const syntax = MAY_BE_RELATIVE.test(file.text) ? parse(file) : undefined;
65
+ if (syntax === undefined) {
66
+ return [];
67
+ }
68
+ const from = posix.join(root, file.path);
69
+ const context = { from, packages, path: file.path, project: projectOf(from), root };
70
+ return specifierSites(syntax).flatMap((site) => departureAt(context, site) ?? []);
71
+ });
72
+ }
@@ -0,0 +1,94 @@
1
+ import ts from "typescript";
2
+
3
+ export interface SpecifierSite {
4
+ readonly line: number;
5
+ readonly specifier: string;
6
+ readonly start: number;
7
+ readonly type: boolean;
8
+ }
9
+
10
+ interface Found {
11
+ readonly node: ts.Node | undefined;
12
+ readonly type: boolean;
13
+ }
14
+
15
+ const MOCKS = new Set(["doMock", "doUnmock", "importActual", "importMock", "mock", "unmock"]);
16
+
17
+ const MAY_CALL = /\b(?:import|require)\s*\(|\bvi\s*\.\s*(?:doMock|doUnmock|importActual|importMock|mock|unmock)\b/u;
18
+
19
+ function declared(node: ts.Node): Found | undefined {
20
+ if (ts.isImportDeclaration(node)) {
21
+ return { node: node.moduleSpecifier, type: node.importClause?.isTypeOnly === true };
22
+ }
23
+ if (ts.isExportDeclaration(node)) {
24
+ return { node: node.moduleSpecifier, type: node.isTypeOnly };
25
+ }
26
+ if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference)) {
27
+ return { node: node.moduleReference.expression, type: node.isTypeOnly };
28
+ }
29
+ return undefined;
30
+ }
31
+
32
+ function isMock(callee: ts.Expression): boolean {
33
+ return (
34
+ ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression) && callee.expression.text === "vi" && MOCKS.has(callee.name.text)
35
+ );
36
+ }
37
+
38
+ function called(node: ts.Node): Found | undefined {
39
+ if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) {
40
+ return { node: node.argument.literal, type: true };
41
+ }
42
+ if (!ts.isCallExpression(node)) {
43
+ return undefined;
44
+ }
45
+ const callee = node.expression;
46
+ const loads = callee.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(callee) && callee.text === "require") || isMock(callee);
47
+ return loads ? { node: node.arguments[0], type: false } : undefined;
48
+ }
49
+
50
+ function siteOf(source: ts.SourceFile, found: Found | undefined): readonly SpecifierSite[] {
51
+ const literal = found?.node;
52
+ if (found === undefined || literal === undefined || !ts.isStringLiteralLike(literal)) {
53
+ return [];
54
+ }
55
+ const start = literal.getStart(source);
56
+ return [{ line: source.getLineAndCharacterOfPosition(start).line + 1, specifier: literal.text, start, type: found.type }];
57
+ }
58
+
59
+ const JSDOC_IMPORT = /^[ \t]*(?:\/\*\*|\*)?[ \t]*@import\b[^"'`]*?\bfrom\s*(?<quote>["'])(?<path>[^"'\r\n]*)\k<quote>/gmu;
60
+
61
+ function siteAt(source: ts.SourceFile, start: number, specifier: string): SpecifierSite {
62
+ return { line: source.getLineAndCharacterOfPosition(start).line + 1, specifier, start, type: true };
63
+ }
64
+
65
+ // A node keeps only its last JSDoc block, so @import tags are read from every comment before it.
66
+ function jsDocImports(source: ts.SourceFile, node: ts.Node, seen: Set<number>): readonly SpecifierSite[] {
67
+ return (ts.getLeadingCommentRanges(source.text, node.pos) ?? []).flatMap((range) => {
68
+ const comment = source.text.slice(range.pos, range.end);
69
+ if (seen.has(range.pos) || !comment.startsWith("/**")) {
70
+ return [];
71
+ }
72
+ seen.add(range.pos);
73
+ return [...comment.matchAll(JSDOC_IMPORT)].map((match) => {
74
+ const path = match.groups?.path ?? "";
75
+ return siteAt(source, range.pos + match.index + match[0].length - path.length - 1, path);
76
+ });
77
+ });
78
+ }
79
+
80
+ export function specifierSites(source: ts.SourceFile): readonly SpecifierSite[] {
81
+ const sites: SpecifierSite[] = [];
82
+ const nested = MAY_CALL.test(source.text);
83
+ const documented = source.text.includes("@import");
84
+ const comments = new Set<number>();
85
+ function visit(node: ts.Node): void {
86
+ sites.push(...siteOf(source, declared(node) ?? (nested ? called(node) : undefined)));
87
+ if (documented) {
88
+ sites.push(...jsDocImports(source, node, comments));
89
+ }
90
+ ts.forEachChild(node, visit);
91
+ }
92
+ visit(source);
93
+ return [...new Map(sites.map((site) => [site.start, site])).values()].sort((left, right) => left.start - right.start);
94
+ }
@@ -39,7 +39,8 @@ const bridge = defineRule({
39
39
  const [preset, report] = await Promise.all([presetFinding(readText), biomeReport(root, ["check"])]);
40
40
  return [...preset, ...report.diagnostics.filter((diagnostic) => FAILING.has(diagnostic.severity)).map(findingOf)];
41
41
  },
42
- description: "Biome's lint, format and assist findings under the shared preset. `quality fix` applies the safe fixes and the formatting.",
42
+ description:
43
+ "Biome's lint, format and assist findings under the shared preset. `quality fix` applies the formatting and assist actions; `quality fix --lint` also applies the safe lint fixes.",
43
44
  id: "biome",
44
45
  registrable: false,
45
46
  });
@@ -6,6 +6,7 @@ import { noJsdoc } from "./comments/no-jsdoc.ts";
6
6
  import { noLineReference } from "./comments/no-line-reference.ts";
7
7
  import { noPrReference } from "./comments/no-pr-reference.ts";
8
8
  import { noTodo } from "./comments/no-todo.ts";
9
+ import { importsAliased } from "./imports/aliased.ts";
9
10
  import { importCycles } from "./imports/cycles.ts";
10
11
  import { importFences } from "./imports/fences.ts";
11
12
  import { importsResolvable } from "./imports/resolvable.ts";
@@ -31,5 +32,6 @@ export const builtInRules = [
31
32
  importCycles,
32
33
  importFences,
33
34
  importsResolvable,
35
+ importsAliased,
34
36
  manifestsSorted,
35
37
  ] as const;
@@ -0,0 +1,26 @@
1
+ import { type Departure, departures } from "../../imports/departures.ts";
2
+ import { defineRule } from "../../rule.ts";
3
+
4
+ function messageOf({ installed, specifier, workspace }: Departure): string {
5
+ const leaves = `"${specifier}" leaves its folder.`;
6
+ if (installed !== undefined) {
7
+ return `${leaves} It reaches into the installed package ${installed}; import that package by its name.`;
8
+ }
9
+ if (workspace !== undefined) {
10
+ return `${leaves} It reaches into the workspace package ${workspace}; import it by that name, through a path its package.json "exports" lists.`;
11
+ }
12
+ return `${leaves} Import it through a "#…" alias from the "imports" of its package.json, and declare one there if none fits.`;
13
+ }
14
+
15
+ export const importsAliased = defineRule({
16
+ check: async (inputs) =>
17
+ (await departures(inputs)).map((departure) => ({
18
+ file: departure.file,
19
+ line: departure.line,
20
+ message: messageOf(departure),
21
+ subject: departure.specifier,
22
+ })),
23
+ description:
24
+ "A relative import names only a file in its own folder. Every other import goes through an alias: the package.json `imports` of its package or another workspace package's name.",
25
+ id: "imports/aliased",
26
+ });