@shivaedev/quality 0.8.0 → 0.8.2
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.
- package/CHANGELOG.md +20 -0
- package/README.md +21 -8
- package/biome/declarations.json +5 -0
- package/biome/plugins/handler-names.grit +2 -1
- package/biome/plugins/key-names.grit +12 -0
- package/biome/preset.json +17 -3
- package/dist/imports/graph.d.ts.map +1 -1
- package/dist/imports/graph.js +9 -6
- package/dist/imports/graph.js.map +1 -1
- package/dist/imports/resolve.d.ts +1 -0
- package/dist/imports/resolve.d.ts.map +1 -1
- package/dist/imports/resolve.js +14 -0
- package/dist/imports/resolve.js.map +1 -1
- package/dist/naming/ownExports.d.ts.map +1 -1
- package/dist/naming/ownExports.js +37 -11
- package/dist/naming/ownExports.js.map +1 -1
- package/dist/rules/files/otherNames.d.ts.map +1 -1
- package/dist/rules/files/otherNames.js +8 -16
- package/dist/rules/files/otherNames.js.map +1 -1
- package/dist/rules/files/packageImports.d.ts +4 -0
- package/dist/rules/files/packageImports.d.ts.map +1 -0
- package/dist/rules/files/packageImports.js +60 -0
- package/dist/rules/files/packageImports.js.map +1 -0
- package/dist/rules/files/stylesheetImporters.d.ts +3 -0
- package/dist/rules/files/stylesheetImporters.d.ts.map +1 -0
- package/dist/rules/files/stylesheetImporters.js +48 -0
- package/dist/rules/files/stylesheetImporters.js.map +1 -0
- package/package.json +1 -1
- package/src/imports/graph.ts +11 -7
- package/src/imports/resolve.ts +18 -0
- package/src/naming/ownExports.ts +40 -11
- package/src/rules/files/otherNames.ts +9 -19
- package/src/rules/files/packageImports.ts +68 -0
- package/src/rules/files/stylesheetImporters.ts +65 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.2 - 2026-10-04
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- A quoted object key or type property is a foreign name, such as a query parameter an outside API reads or an environment variable, and the naming rules skip it: `{ "per_page": 50 }` passes, `{ per_page: 50 }` is a finding. The new `key-names` plugin checks unquoted keys in camelCase or PascalCase in place of `useNamingConvention`, which cannot tell a quoted key from an unquoted one, and the formatter keeps the quotes a key is written with. `schema-struct-keys` still reports a snake_case `Schema.Struct` field when it is quoted. A key finding now reports under `biome/plugin` instead of `biome/lint/style/useNamingConvention`, so a repository that upgrades records its key findings again with `quality baseline write --rule biome/plugin` and prunes the old entries with `quality baseline prune`.
|
|
8
|
+
- `handler-names` accepts a state setter passed as it is to an `onX` prop, such as `onOpenChange={setOpen}`. Only a handler function defined in the component needs the `handleX` name.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- `files/other-names` accepts a double underscore in a content file name, which separates the parts of an id, as in `forest_path__clearing.json`.
|
|
13
|
+
- `files/named-after-export` names a file after its default export: `export default ItemPanel`, `export default function ItemPanel`, `export default memo(ItemPanel)` and `export { ItemPanel as default }` all count as the export `ItemPanel`, where it used to report the file as exporting nothing. `noDefaultExport` still reports the default export itself. An overloaded function counts as one export, so a file whose only export is an overloaded function is now named after it.
|
|
14
|
+
- The preset turns off `useComponentExportOnlyModules` in `*.test.*` and `*.spec.*` files, which define the helper and harness components they render.
|
|
15
|
+
- `files/other-names` counts as an import of a stylesheet only a static `import`, relative or through a `#` alias that the `imports` of its `package.json` resolves, and a CSS `@import`. A quoted path in a test, such as one passed to `resolve()`, no longer counts, and a stylesheet that another stylesheet imports is shared and named in kebab-case.
|
|
16
|
+
|
|
17
|
+
## 0.8.1 - 2026-10-04
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `imports/resolvable` takes a `#` package import of a missing file as resolved when its `imports` entry in `package.json` points into a folder that `generated` names, as it already did for a relative import. A test can then import a generated client through its alias before the generator runs, and fences see the import as an edge to that path.
|
|
22
|
+
|
|
3
23
|
## 0.8.0 - 2026-10-04
|
|
4
24
|
|
|
5
25
|
### Added
|
package/README.md
CHANGED
|
@@ -210,7 +210,7 @@ Each import resolves the way the compiler resolves it, with the options of the n
|
|
|
210
210
|
- An import of a stylesheet, an image or JSON resolves to the file, and only when the file exists, whatever declaration describes it. A Node builtin resolves.
|
|
211
211
|
- A runtime import must resolve to code: a declaration file (`.d.ts`, `.d.mts`, `.d.cts`, or an asset declaration such as `.d.css.ts`) satisfies only `import type` and `export type`, however the import reaches it, so a package that has only its `@types` package installed, or an `exports` entry that points at a declaration, is reported.
|
|
212
212
|
- A bare import resolves when a declaration file of the importer's tsconfig project declares the module in a script (`declare module "virtual:*"`). A relative import always needs a real file, `declare module "*"` and patterns with more than one `*` never count, and a `declare module` inside a module is an augmentation, which declares nothing new.
|
|
213
|
-
- A relative import of a missing file resolves only inside a folder that `generated` names, such as a client a generator writes before the tests run. Each folder must be ignored by git, or at least every file an import names in it must be, hold no file git tracks but a `.gitkeep` or `.keep` placeholder, lie outside `node_modules` and hold a file that an import names. The import stays an edge to its path, so fences apply to it before the file exists.
|
|
213
|
+
- A relative import or a `#` package import of a missing file resolves only inside a folder that `generated` names, such as a client a generator writes before the tests run. A `#` import lands where its `imports` entry in `package.json` points once the file exists. Each folder must be ignored by git, or at least every file an import names in it must be, hold no file git tracks but a `.gitkeep` or `.keep` placeholder, lie outside `node_modules` and hold a file that an import names. The import stays an edge to its path, so fences apply to it before the file exists.
|
|
214
214
|
- A module that resolves into `node_modules` or outside the root is external, and its package is the one it resolves into, whatever alias the import uses. A `@types` package counts as the package it describes: `@types/hast` is `hast`, `@types/scope__name` is `@scope/name`.
|
|
215
215
|
|
|
216
216
|
```ts
|
|
@@ -383,14 +383,14 @@ quality baseline migrate [--config <file>] [--from <file>]
|
|
|
383
383
|
|
|
384
384
|
The preset sets:
|
|
385
385
|
|
|
386
|
-
- **Formatting:** tabs, a line width of 150, double quotes, semicolons, trailing commas and operators at the start of a wrapped line.
|
|
386
|
+
- **Formatting:** tabs, a line width of 150, double quotes, semicolons, trailing commas and operators at the start of a wrapped line. An object key keeps its quotes, because a quoted key marks a [foreign name](#foreign-names).
|
|
387
387
|
- **Lint:** every rule Biome recommends, at `error`, and a list of stricter rules on top, among them `noUnsafeTypeAssertion`, `useBlockStatements`, `useNumericSeparators`, `useUnicodeRegex`, `useLiteralKeys`, `noFloatingPromises`, `useExhaustiveDependencies`, `it` for every test, function declarations over function expressions, interfaces for object types, a cognitive complexity limit of 15, no nested ternaries, and the naming rules under [Naming](#naming).
|
|
388
388
|
- **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.
|
|
389
389
|
- **Assist:** organized imports and sorted keys, attributes, enum members, interface members and properties. Keys are sorted in JSON and in object literals alike, but only reported: `quality fix` leaves the order to the author. `package.json` is left out, because `manifests/sorted` gives it the order npm users expect.
|
|
390
390
|
- **Plugins:** GritQL rules that ban ambient time, randomness, `console` and `process.env` for Effect's services, and the naming plugins under [Naming](#naming). They load from `./node_modules/@shivaedev/quality/biome/plugins`, so the package must be installed at the repository root.
|
|
391
391
|
- Files ignored by git are skipped.
|
|
392
392
|
|
|
393
|
-
The preset turns off `noUnusedVariables` and `noUnusedFunctionParameters`, because the tsconfig presets report them through TypeScript,
|
|
393
|
+
The preset turns off `noUnusedVariables` and `noUnusedFunctionParameters`, because the tsconfig presets report them through TypeScript, allows default exports in `*.config.*` files, which tools load through the default export, and turns off `useComponentExportOnlyModules` in `*.test.*` and `*.spec.*` files, which define the helper and harness components they render. It also turns off `noProcessGlobal`, `useJsonImportAttributes`, `noMisusedPromises`, `useExhaustiveSwitchCases`, `useSortedClasses`, `noDelete`, `useConsistentArrayType`, `useConsistentCurlyBraces`, `noEqualsToNull` and `noSkippedTests`, because `quality fix` applies every lint fix and their fixes changed behavior or did not terminate on real code. It declares these weakenings in its `declarations.json`, so `suppressions/biome-overrides` takes them as declared. Every other weakening a repository adds is an override it declares with a reason.
|
|
394
394
|
|
|
395
395
|
An autofix applies only a change that removes no decision; a fix that can change behavior or delete something written on purpose reports only, and the author decides. The preset keeps `noAccessKey`, `noAriaHiddenOnFocusable`, `noAutofocus`, `noInteractiveElementToNoninteractiveRole`, `noNoninteractiveElementToInteractiveRole`, `noNoninteractiveTabindex`, `noRedundantRoles`, `useValidAriaProps`, `useValidAriaRole`, `noImportantStyles`, `noConstAssign`, `noUnusedPrivateClassMembers`, `useExhaustiveDependencies`, `noFloatingPromises`, `useRegexpTest`, `useUnicodeRegex`, `noNonNullAssertion`, `useAtIndex`, `noParametersOnlyUsedInRecursion` and `useNamingConvention` at `error` with their fixes off, and `quality fix` skips `noDuplicateObjectKeys` and `useSortedKeys`, whose fixes Biome's config cannot turn off.
|
|
396
396
|
|
|
@@ -411,15 +411,27 @@ A name says what a thing is, and the same kind of thing is named the same way ev
|
|
|
411
411
|
| Types, interfaces, classes and enums | PascalCase, without an `I` prefix: `Item`, not `IItem`. | `useNamingConvention` |
|
|
412
412
|
| Type parameters | `T`, or `T` followed by a PascalCase name: `TItem`, `TResult`. Effect's positional `A`, `E` and `R` are valid as they are. | `useNamingConvention` |
|
|
413
413
|
| Module-level constants | A string or number literal at module level is CONSTANT_CASE: `const MAX_ITEMS = 50`. A constant inside a function is camelCase. | `constant-names` plugin, `useNamingConvention` |
|
|
414
|
-
| Object keys and type properties | camelCase or PascalCase, after any leading `_` or `$` (`_tag`, `$transaction`)
|
|
414
|
+
| Object keys and type properties | camelCase or PascalCase, after any leading `_` or `$` (`_tag`, `$transaction`), also in a `Record`. A quoted key is a [foreign name](#foreign-names) and is not checked. | `key-names` plugin |
|
|
415
415
|
| Schemas | PascalCase and named like their type: `const Item = Schema.Struct(...)` with `type Item = typeof Item.Type`. | `schema-names` plugin |
|
|
416
|
-
| `Schema.Struct` fields | camelCase. Outside data keeps its keys only at the edge: `Schema.Struct({ createdAt: Schema.String }).pipe(Schema.encodeKeys({ createdAt: "created_at" }))`. | `schema-struct-keys` plugin, `
|
|
416
|
+
| `Schema.Struct` fields | camelCase, also when quoted. Outside data keeps its keys only at the edge: `Schema.Struct({ createdAt: Schema.String }).pipe(Schema.encodeKeys({ createdAt: "created_at" }))`. | `schema-struct-keys` plugin, `key-names` plugin |
|
|
417
417
|
| `Effect.fn` spans | `"Owner.operation"`, where the owner is the service or module and the operation is the name the function is bound to: `const loadItem = Effect.fn("ItemStore.loadItem")`, and `loadItem: Effect.fn("ItemStore.loadItem")` in an object. | `effect-fn-spans` plugin |
|
|
418
418
|
| Service Layers | A static `layer` on the service, read as `ItemStore.layer`. No exported `ItemStoreLive` or `ItemStoreLayer` constant. A Layer that wires an application together stays unexported. | `service-layers` plugin |
|
|
419
419
|
| Private members | A `#field`. No `private`, `protected` or `public` modifier. | `useConsistentMemberAccessibility` |
|
|
420
|
-
| React | A function passed to an `onX` prop is `handleX`,
|
|
420
|
+
| React | A function passed to an `onX` prop is `handleX`, an `onX` prop forwarded as it is, or a state setter passed as it is: `onOpenChange={setOpen}`. A context is `ThemeContext`, a ref `inputRef`, an id `fieldId`. A module that exports a component exports only components; a test may define the components it renders. | `handler-names` plugin, `useReactNamingConvention`, `useComponentExportOnlyModules` |
|
|
421
421
|
| Booleans | A question: `isOpen`, `hasSave`, `canRetry`, `shouldFlush`. A name the DOM or React gives, such as `open` or `disabled`, stays. This is a convention only; no rule checks it. | Review |
|
|
422
422
|
|
|
423
|
+
### Foreign names
|
|
424
|
+
|
|
425
|
+
Some names are not ours to choose: a query parameter an outside API reads, an environment variable, an operator a query builder takes. Write such a name as a quoted key, and the naming rules skip it:
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
const query = { "per_page": 50, "sort_by": "created" };
|
|
429
|
+
const env = { "DATABASE_URL": url };
|
|
430
|
+
const where = { "OR": [{ id }, { slug }] };
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
An unquoted key is our own name and is camelCase or PascalCase. The formatter keeps the quotes as written, so a quoted key stays quoted. A `Schema.Struct` field is the exception: the struct is our model of the data, so the `schema-struct-keys` plugin reports a snake_case field even when it is quoted, and points to `Schema.encodeKeys`, which maps camelCase fields to the outside names at the edge.
|
|
434
|
+
|
|
423
435
|
### Re-exports
|
|
424
436
|
|
|
425
437
|
No file re-exports, package entry files included: every module is imported from the file that defines it. `noBarrelFile` reports `export { … } from`, `noReExportAll` reports `export *`, the `type-re-exports` plugin reports `export type … from`, and `noExportedImports` reports an import that is exported again. Its message suggests `export … from`, which is a re-export as well: import the name where it is used instead.
|
|
@@ -437,6 +449,7 @@ The plugins report under the one Biome category `plugin`, so their findings shar
|
|
|
437
449
|
| `item/Panel.tsx` or `ItemPanel.tsx` | `ItemPanel`, with `ItemPanelProps` beside it | Yes |
|
|
438
450
|
| `Panel/Panel.tsx` | `Panel`: the main file of a module folder repeats the folder | Yes |
|
|
439
451
|
| `limits.ts` | `MAX_ITEMS` and `MAX_DEPTH`: a topic file of several exports is camelCase | Yes |
|
|
452
|
+
| `ItemPanel.tsx` | `export default memo(ItemPanel)`: a default export counts under the name it resolves to | Yes |
|
|
440
453
|
| `setupTests.ts` | Nothing, so it is camelCase | Yes |
|
|
441
454
|
| `script/build-docs.ts` | Run by a `package.json` script or `bin`, or starts with `#!`, so it is kebab-case | Yes |
|
|
442
455
|
| `order.ts` | `type Order` | No: the file's case follows the export, so it is `Order.ts` |
|
|
@@ -461,9 +474,9 @@ The `toolOwned` option lists folders a tool names, `generated/` and `migrations/
|
|
|
461
474
|
`files/other-names` names the files that are not code.
|
|
462
475
|
|
|
463
476
|
- Markdown, JSON, GritQL, images, fonts and SVG are kebab-case, with optional dotted parts: `release-notes.md`, `icons.sprite.svg`. Conventional upper-case names stay: `README.md`, `CHANGELOG.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, `SECURITY.md` and `SKILL.md`.
|
|
464
|
-
- A stylesheet that one component imports is named after it: `ItemPanel.css` beside `ItemPanel.tsx`. A shared stylesheet is kebab-case: `form-controls.css`.
|
|
477
|
+
- A stylesheet that one component imports is named after it: `ItemPanel.css` beside `ItemPanel.tsx`. A shared stylesheet is kebab-case: `form-controls.css`. An import is a static `import` in a code file, relative or through a `#` alias from the `imports` of its `package.json`, or an `@import` in a stylesheet; a stylesheet that another stylesheet imports is shared. A path in a string, such as a test that reads the file, is not an import.
|
|
465
478
|
- A Prisma schema is camelCase: `schema.prisma`.
|
|
466
|
-
- A file the `content` option lists is snake_case: `forest_path.json`.
|
|
479
|
+
- A file the `content` option lists is snake_case: `forest_path.json`. A double underscore separates the parts of an id: `forest_path__clearing.json`.
|
|
467
480
|
|
|
468
481
|
The `toolOwned` option lists files a tool names: dotfiles, `package.json`, `tsconfig*.json`, `biome.json`, `*.config.*`, `migrations/` and `generated/` by default.
|
|
469
482
|
|
package/biome/declarations.json
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"reason": "Tools load their config files through the default export.",
|
|
15
15
|
"rule": "lint/style/noDefaultExport"
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
"includes": ["**/*.test.*", "**/*.spec.*"],
|
|
19
|
+
"reason": "A test defines the helper and harness components it renders, and no test module is hot-reloaded, so the rule's fast-refresh boundary does not apply.",
|
|
20
|
+
"rule": "lint/style/useComponentExportOnlyModules"
|
|
21
|
+
},
|
|
17
22
|
{
|
|
18
23
|
"includes": ["**"],
|
|
19
24
|
"reason": "quality fix applies every Biome lint fix, and this rule's fix changed behavior or did not finish on real code, so the rule is off until it is reviewed.",
|
|
@@ -6,5 +6,6 @@ JsxAttribute(name=$attribute, initializer=JsxAttributeInitializerClause(value=Js
|
|
|
6
6
|
$handler <: JsIdentifierExpression(),
|
|
7
7
|
! $handler <: r"handle[A-Z][A-Za-z0-9]*",
|
|
8
8
|
! $handler <: r"on[A-Z][A-Za-z0-9]*",
|
|
9
|
-
|
|
9
|
+
! $handler <: r"set[A-Z][A-Za-z0-9]*",
|
|
10
|
+
register_diagnostic(span=$handler, message="A function passed to an onX prop is named handleX, such as onClick={handleClick}, or is an onX prop forwarded as it is, or a state setter passed as it is, such as onChange={setValue}.")
|
|
10
11
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
engine biome(1.0)
|
|
2
|
+
language js(typescript, jsx)
|
|
3
|
+
|
|
4
|
+
or {
|
|
5
|
+
JsPropertyObjectMember(name=$key),
|
|
6
|
+
TsPropertySignatureTypeMember(name=$key)
|
|
7
|
+
} where {
|
|
8
|
+
$key <: JsLiteralMemberName(),
|
|
9
|
+
$key <: r"[A-Za-z_$][A-Za-z0-9_$]*",
|
|
10
|
+
! $key <: r"[_$]*(?:[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*[A-Z]?|(?:[A-Z][0-9]+)*[A-Z][0-9]*[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*[A-Z]?|[A-Z][0-9]*)",
|
|
11
|
+
register_diagnostic(span=$key, message="An object key or type property is camelCase or PascalCase after any leading _ or $, with acronyms spelled as words: perPage, userId. A name an outside API or the environment decides is written quoted: { 'per_page': 50 }.")
|
|
12
|
+
}
|
package/biome/preset.json
CHANGED
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"arrowParentheses": "always",
|
|
78
78
|
"jsxQuoteStyle": "double",
|
|
79
79
|
"operatorLinebreak": "before",
|
|
80
|
-
"quoteProperties": "
|
|
80
|
+
"quoteProperties": "preserve",
|
|
81
81
|
"quoteStyle": "double",
|
|
82
82
|
"semicolons": "always",
|
|
83
83
|
"trailingCommas": "all"
|
|
@@ -462,8 +462,8 @@
|
|
|
462
462
|
{ "formats": ["PascalCase"], "match": "(T|A|E|R)|T([A-Z][A-Za-z0-9]*)", "selector": { "kind": "typeParameter" } },
|
|
463
463
|
{ "formats": ["PascalCase"], "match": "([A-HJ-Z][A-Za-z0-9]*|I[a-z0-9][A-Za-z0-9]*|I)", "selector": { "kind": "typeLike" } },
|
|
464
464
|
{ "formats": ["camelCase", "PascalCase"], "selector": { "kind": "function" } },
|
|
465
|
-
{ "
|
|
466
|
-
{ "
|
|
465
|
+
{ "match": ".*", "selector": { "kind": "objectLiteralProperty" } },
|
|
466
|
+
{ "match": ".*", "selector": { "kind": "typeProperty" } }
|
|
467
467
|
],
|
|
468
468
|
"requireAscii": true,
|
|
469
469
|
"strictCase": true
|
|
@@ -622,6 +622,16 @@
|
|
|
622
622
|
}
|
|
623
623
|
}
|
|
624
624
|
}
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
"includes": ["**/*.test.*", "**/*.spec.*"],
|
|
628
|
+
"linter": {
|
|
629
|
+
"rules": {
|
|
630
|
+
"style": {
|
|
631
|
+
"useComponentExportOnlyModules": "off"
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
625
635
|
}
|
|
626
636
|
],
|
|
627
637
|
"plugins": [
|
|
@@ -641,6 +651,10 @@
|
|
|
641
651
|
"includes": ["**/*.ts", "**/*.tsx", "!**/*.d.ts"],
|
|
642
652
|
"path": "./node_modules/@shivaedev/quality/biome/plugins/handler-names.grit"
|
|
643
653
|
},
|
|
654
|
+
{
|
|
655
|
+
"includes": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts", "**/*.js", "**/*.jsx", "**/*.mjs", "**/*.cjs"],
|
|
656
|
+
"path": "./node_modules/@shivaedev/quality/biome/plugins/key-names.grit"
|
|
657
|
+
},
|
|
644
658
|
{
|
|
645
659
|
"includes": ["**/*.ts", "**/*.tsx", "!**/*.d.ts"],
|
|
646
660
|
"path": "./node_modules/@shivaedev/quality/biome/plugins/schema-names.grit"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/imports/graph.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,UAAU,CAAC;AAGvD,OAAO,EAAE,KAAK,UAAU,EAAiC,MAAM,cAAc,CAAC;AAE9E,OAAO,EAAE,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/imports/graph.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,UAAU,CAAC;AAGvD,OAAO,EAAE,KAAK,UAAU,EAAiC,MAAM,cAAc,CAAC;AAE9E,OAAO,EAAE,KAAK,QAAQ,EAAoD,MAAM,cAAc,CAAC;AAC/F,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,gBAAgB,CAAC;AAE1E,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjG,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACjD;AAsDD,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAIpE"}
|
package/dist/imports/graph.js
CHANGED
|
@@ -4,15 +4,18 @@ import { parse } from "#rules/syntax.ts";
|
|
|
4
4
|
import { ambientModules } from "./ambient.js";
|
|
5
5
|
import { importsOf } from "./extract.js";
|
|
6
6
|
import { projectsFor } from "./projects.js";
|
|
7
|
-
import { isDeclarationFile, resolveImport } from "./resolve.js";
|
|
7
|
+
import { isDeclarationFile, presumedTarget, resolveImport } from "./resolve.js";
|
|
8
8
|
import { workspacePackages } from "./workspace.js";
|
|
9
9
|
const QUERY = /\?.*$/u;
|
|
10
10
|
const NO_MODULES = "the import graph covers no modules: the sources hold no TypeScript or JavaScript module. Point `sources` at the code, or turn the imports rules off.";
|
|
11
|
-
|
|
12
|
-
function missingTarget(from, request) {
|
|
11
|
+
function relativeTarget(from, request) {
|
|
13
12
|
const relative = request.kind === "path-reference" || request.specifier.startsWith("./") || request.specifier.startsWith("../");
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
return relative ? posix.join(posix.dirname(from), request.specifier.replace(QUERY, "")) : undefined;
|
|
14
|
+
}
|
|
15
|
+
// A relative import or a package import of a missing file still names a path, so fences see it before its generator writes it.
|
|
16
|
+
function missingTarget(root, from, request, project) {
|
|
17
|
+
const target = presumedTarget(root, request, join(root, from), project) ?? relativeTarget(from, request);
|
|
18
|
+
return target === undefined || target.startsWith("../") ? undefined : target;
|
|
16
19
|
}
|
|
17
20
|
function walk(inputs, root) {
|
|
18
21
|
const projectOf = projectsFor(root);
|
|
@@ -28,7 +31,7 @@ function walk(inputs, root) {
|
|
|
28
31
|
for (const request of importsOf(syntax, isDeclarationFile(file.path))) {
|
|
29
32
|
const to = resolveImport(root, ambient, request, path, project);
|
|
30
33
|
if (to === undefined) {
|
|
31
|
-
const missing = missingTarget(file.path, request);
|
|
34
|
+
const missing = missingTarget(root, file.path, request, project);
|
|
32
35
|
unresolved.push({ ...request, from: file.path, missing });
|
|
33
36
|
edges.push(...(missing === undefined ? [] : [{ ...request, from: file.path, to: { kind: "file", path: missing } }]));
|
|
34
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/imports/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAuC,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/imports/graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,5 +12,6 @@ export type Endpoint = {
|
|
|
12
12
|
export declare function builtinName(specifier: string): string | undefined;
|
|
13
13
|
export declare function packageNameOf(specifier: string): string;
|
|
14
14
|
export declare function isDeclarationFile(path: string): boolean;
|
|
15
|
+
export declare function presumedTarget(root: string, request: ImportRequest, from: string, project: Project): string | undefined;
|
|
15
16
|
export declare function resolveImport(root: string, ambient: Ambient, request: ImportRequest, from: string, project: Project): Endpoint | undefined;
|
|
16
17
|
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/imports/resolve.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,QAAQ,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/imports/resolve.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,QAAQ,GACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAsBvF,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKjE;AAmBD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAYD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AA6BD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAMvH;AAYD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAY1I"}
|
package/dist/imports/resolve.js
CHANGED
|
@@ -8,6 +8,13 @@ const NODE_MODULES = "/node_modules/";
|
|
|
8
8
|
const ASSET_DECLARATION = /\.d(?<extension>\.[^./]+)\.ts$/u;
|
|
9
9
|
const DECLARATION = /\.d\.[cm]?ts$/u;
|
|
10
10
|
const QUERY = /\?.*$/u;
|
|
11
|
+
const PACKAGE_IMPORT = "#";
|
|
12
|
+
const PACKAGE_JSON = "package.json";
|
|
13
|
+
const presumingHost = {
|
|
14
|
+
...ts.sys,
|
|
15
|
+
directoryExists: () => true,
|
|
16
|
+
fileExists: (path) => !path.endsWith(`/${PACKAGE_JSON}`) || ts.sys.fileExists(path),
|
|
17
|
+
};
|
|
11
18
|
export function builtinName(specifier) {
|
|
12
19
|
if (!isBuiltin(specifier)) {
|
|
13
20
|
return undefined;
|
|
@@ -63,6 +70,13 @@ function resolvedPath(request, from, project) {
|
|
|
63
70
|
const path = located(request, from, project);
|
|
64
71
|
return path === undefined ? undefined : fitting(path, request.type);
|
|
65
72
|
}
|
|
73
|
+
export function presumedTarget(root, request, from, project) {
|
|
74
|
+
if (request.kind !== "import" || !request.specifier.startsWith(PACKAGE_IMPORT)) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const path = ts.resolveModuleName(request.specifier.replace(QUERY, ""), from, project.options, presumingHost).resolvedModule?.resolvedFileName;
|
|
78
|
+
return path === undefined ? undefined : posix(relative(root, path));
|
|
79
|
+
}
|
|
66
80
|
function endpointAt(root, specifier, path) {
|
|
67
81
|
const real = posix(realpathSync(path));
|
|
68
82
|
const installed = real.lastIndexOf(NODE_MODULES);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/imports/resolve.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/imports/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ownExports.d.ts","sourceRoot":"","sources":["../../src/naming/ownExports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"ownExports.d.ts","sourceRoot":"","sources":["../../src/naming/ownExports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAiHD,wBAAgB,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,SAAS,SAAS,EAAE,CAItE"}
|
|
@@ -19,9 +19,7 @@ function isPrimitiveLiteral(node) {
|
|
|
19
19
|
}
|
|
20
20
|
function isExported(statement) {
|
|
21
21
|
const modifiers = ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : undefined;
|
|
22
|
-
|
|
23
|
-
const isDefault = modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
|
|
24
|
-
return exported && !isDefault;
|
|
22
|
+
return modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
25
23
|
}
|
|
26
24
|
function variables(statement) {
|
|
27
25
|
const constant = (statement.declarationList.flags & ts.NodeFlags.Const) !== 0;
|
|
@@ -57,22 +55,50 @@ function listed(statement, known) {
|
|
|
57
55
|
return [];
|
|
58
56
|
}
|
|
59
57
|
return clause.elements.flatMap((element) => {
|
|
60
|
-
const name = element.name.text;
|
|
61
58
|
const local = known.get((element.propertyName ?? element.name).text);
|
|
62
|
-
if (
|
|
59
|
+
if (local === undefined) {
|
|
63
60
|
return [];
|
|
64
61
|
}
|
|
62
|
+
const name = element.name.text === "default" ? local.name : element.name.text;
|
|
65
63
|
const typeOnly = statement.isTypeOnly || element.isTypeOnly;
|
|
66
64
|
return [{ constant: local.constant, kind: typeOnly ? "type" : local.kind, name }];
|
|
67
65
|
});
|
|
68
66
|
}
|
|
67
|
+
function wrappedName(expression) {
|
|
68
|
+
if (ts.isIdentifier(expression)) {
|
|
69
|
+
return expression.text;
|
|
70
|
+
}
|
|
71
|
+
if (ts.isFunctionExpression(expression) || ts.isClassExpression(expression)) {
|
|
72
|
+
return expression.name?.text;
|
|
73
|
+
}
|
|
74
|
+
if (ts.isCallExpression(expression)) {
|
|
75
|
+
const [wrapped] = expression.arguments;
|
|
76
|
+
return wrapped === undefined ? undefined : wrappedName(wrapped);
|
|
77
|
+
}
|
|
78
|
+
return ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isSatisfiesExpression(expression)
|
|
79
|
+
? wrappedName(expression.expression)
|
|
80
|
+
: undefined;
|
|
81
|
+
}
|
|
82
|
+
// The default export counts under the name it resolves to, as in memo(ItemPanel); noDefaultExport reports the default export itself.
|
|
83
|
+
function defaulted(statement, known) {
|
|
84
|
+
const name = statement.isExportEquals ? undefined : wrappedName(statement.expression);
|
|
85
|
+
if (name === undefined) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
return [known.get(name) ?? { constant: false, kind: "value", name }];
|
|
89
|
+
}
|
|
90
|
+
function exportsOf(statement, known) {
|
|
91
|
+
if (ts.isExportDeclaration(statement)) {
|
|
92
|
+
return listed(statement, known);
|
|
93
|
+
}
|
|
94
|
+
if (ts.isExportAssignment(statement)) {
|
|
95
|
+
return defaulted(statement, known);
|
|
96
|
+
}
|
|
97
|
+
return isExported(statement) ? declared(statement) : [];
|
|
98
|
+
}
|
|
69
99
|
export function ownExports(source) {
|
|
70
100
|
const known = locals(source);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return listed(statement, known);
|
|
74
|
-
}
|
|
75
|
-
return isExported(statement) ? declared(statement) : [];
|
|
76
|
-
});
|
|
101
|
+
const all = source.statements.flatMap((statement) => exportsOf(statement, known));
|
|
102
|
+
return [...new Map(all.map((entry) => [`${entry.kind} ${entry.name}`, entry])).values()];
|
|
77
103
|
}
|
|
78
104
|
//# sourceMappingURL=ownExports.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ownExports.js","sourceRoot":"","sources":["../../src/naming/ownExports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAQ5B,MAAM,aAAa,GAAG,kCAAkC,CAAC;AAEzD,MAAM,kBAAkB,GAA+B,IAAI,GAAG,CAAC;IAC9D,EAAE,CAAC,UAAU,CAAC,aAAa;IAC3B,EAAE,CAAC,UAAU,CAAC,YAAY;IAC1B,EAAE,CAAC,UAAU,CAAC,6BAA6B;IAC3C,EAAE,CAAC,UAAU,CAAC,cAAc;IAC5B,EAAE,CAAC,UAAU,CAAC,aAAa;IAC3B,EAAE,CAAC,UAAU,CAAC,WAAW;CACzB,CAAC,CAAC;AAEH,SAAS,kBAAkB,CAAC,IAA+B;IAC1D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,UAAU,CAAC,SAAuB;IAC1C,MAAM,SAAS,GAAG,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,
|
|
1
|
+
{"version":3,"file":"ownExports.js","sourceRoot":"","sources":["../../src/naming/ownExports.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otherNames.d.ts","sourceRoot":"","sources":["../../../src/rules/files/otherNames.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"otherNames.d.ts","sourceRoot":"","sources":["../../../src/rules/files/otherNames.ts"],"names":[],"mappings":"AA2FA,eAAO,MAAM,UAAU;;;EAgBrB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { Effect, Schema } from "effect";
|
|
|
3
3
|
import ignore, {} from "ignore";
|
|
4
4
|
import { CAMEL, KEBAB } from "#naming/words.ts";
|
|
5
5
|
import { defineRule } from "#rule.ts";
|
|
6
|
+
import { stylesheetImporters } from "./stylesheetImporters.js";
|
|
6
7
|
const DEFAULT_TOOL_OWNED = [
|
|
7
8
|
".*",
|
|
8
9
|
"package.json",
|
|
@@ -35,26 +36,15 @@ const KEBAB_KINDS = new Set([
|
|
|
35
36
|
]);
|
|
36
37
|
const STYLESHEETS = new Set(["css", "scss"]);
|
|
37
38
|
const DOTTED_KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)*(?:\.[a-z0-9]+(?:-[a-z0-9]+)*)*$/u;
|
|
38
|
-
const SNAKE = /^[a-z0-9]+(?:
|
|
39
|
-
const STYLESHEET_IMPORT = /["'](?<stylesheet>\.{1,2}\/[^"'\n]+\.s?css)["']/gu;
|
|
39
|
+
const SNAKE = /^[a-z0-9]+(?:__?[a-z0-9]+)*$/u;
|
|
40
40
|
const CODE = /\.[cm]?[jt]sx?$/u;
|
|
41
41
|
const OtherNamesOptions = Schema.Struct({
|
|
42
42
|
content: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
|
|
43
43
|
toolOwned: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed(DEFAULT_TOOL_OWNED))),
|
|
44
44
|
});
|
|
45
|
-
function importers(sources) {
|
|
46
|
-
const found = new Map();
|
|
47
|
-
for (const source of sources) {
|
|
48
|
-
for (const match of source.text.matchAll(STYLESHEET_IMPORT)) {
|
|
49
|
-
const target = posix.normalize(posix.join(posix.dirname(source.path), match.groups?.stylesheet ?? ""));
|
|
50
|
-
found.set(target, [...new Set([...(found.get(target) ?? []), source.path])]);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return found;
|
|
54
|
-
}
|
|
55
45
|
function stylesheetMessage(path, stem, importedBy) {
|
|
56
46
|
const [only] = importedBy;
|
|
57
|
-
if (importedBy.length === 1 && only !== undefined) {
|
|
47
|
+
if (importedBy.length === 1 && only !== undefined && CODE.test(only)) {
|
|
58
48
|
const owner = posix.basename(only).replace(CODE, "");
|
|
59
49
|
return stem === owner ? undefined : `"${stem}" is styling for ${posix.basename(only)} alone, so it is named ${owner}${posix.extname(path)}.`;
|
|
60
50
|
}
|
|
@@ -68,7 +58,9 @@ function messageFor(path, context) {
|
|
|
68
58
|
return undefined;
|
|
69
59
|
}
|
|
70
60
|
if (context.content.ignores(path)) {
|
|
71
|
-
return SNAKE.test(stem)
|
|
61
|
+
return SNAKE.test(stem)
|
|
62
|
+
? undefined
|
|
63
|
+
: `"${name}" is content, so it is named in snake_case, such as forest_path.json, with a double underscore between the parts of an id, such as forest_path__clearing.json.`;
|
|
72
64
|
}
|
|
73
65
|
if (STYLESHEETS.has(kind)) {
|
|
74
66
|
return stylesheetMessage(path, stem, context.stylesheets.get(path) ?? []);
|
|
@@ -81,10 +73,10 @@ function messageFor(path, context) {
|
|
|
81
73
|
: undefined;
|
|
82
74
|
}
|
|
83
75
|
export const otherNames = defineRule({
|
|
84
|
-
check: ({ files, options, sources }) => {
|
|
76
|
+
check: async ({ files, options, readText, sources }) => {
|
|
85
77
|
const context = {
|
|
86
78
|
content: ignore().add([...options.content]),
|
|
87
|
-
stylesheets:
|
|
79
|
+
stylesheets: await stylesheetImporters({ files, readText, sources }),
|
|
88
80
|
toolOwned: ignore().add([...options.toolOwned]),
|
|
89
81
|
};
|
|
90
82
|
return files.flatMap((path) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otherNames.js","sourceRoot":"","sources":["../../../src/rules/files/otherNames.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,MAAM,EAAE,EAAe,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"otherNames.js","sourceRoot":"","sources":["../../../src/rules/files/otherNames.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RuleInputs } from "#rule.ts";
|
|
2
|
+
export type PackageImports = (importer: string, specifier: string) => string | undefined;
|
|
3
|
+
export declare function packageImports({ files, readText }: Pick<RuleInputs, "files" | "readText">): Promise<PackageImports>;
|
|
4
|
+
//# sourceMappingURL=packageImports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageImports.d.ts","sourceRoot":"","sources":["../../../src/rules/files/packageImports.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAgDzF,wBAAsB,cAAc,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAgBzH"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
const MANIFEST = "package.json";
|
|
3
|
+
function importsField(text) {
|
|
4
|
+
if (text === undefined) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
const manifest = JSON.parse(text);
|
|
9
|
+
return typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "imports") : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function targetsIn(value) {
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
return [value];
|
|
18
|
+
}
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return value.flatMap(targetsIn);
|
|
21
|
+
}
|
|
22
|
+
return typeof value === "object" && value !== null ? Object.values(value).flatMap(targetsIn) : [];
|
|
23
|
+
}
|
|
24
|
+
function matched(imports, specifier) {
|
|
25
|
+
if (typeof imports !== "object" || imports === null) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
const entries = Object.entries(imports);
|
|
29
|
+
const exact = entries.find(([key]) => key === specifier);
|
|
30
|
+
if (exact !== undefined) {
|
|
31
|
+
return targetsIn(exact[1]);
|
|
32
|
+
}
|
|
33
|
+
const patterns = entries.flatMap(([key, value]) => {
|
|
34
|
+
const [prefix = "", suffix, ...rest] = key.split("*");
|
|
35
|
+
const fits = suffix !== undefined && rest.length === 0 && specifier.length >= prefix.length + suffix.length;
|
|
36
|
+
return fits && specifier.startsWith(prefix) && specifier.endsWith(suffix) ? [{ prefix, suffix, value }] : [];
|
|
37
|
+
});
|
|
38
|
+
const [best] = patterns.toSorted((left, right) => right.prefix.length - left.prefix.length);
|
|
39
|
+
if (best === undefined) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
const star = specifier.slice(best.prefix.length, specifier.length - best.suffix.length);
|
|
43
|
+
return targetsIn(best.value).map((target) => target.replaceAll("*", star));
|
|
44
|
+
}
|
|
45
|
+
export async function packageImports({ files, readText }) {
|
|
46
|
+
const directories = files.filter((path) => posix.basename(path) === MANIFEST).map((path) => posix.dirname(path));
|
|
47
|
+
const manifests = await Promise.all(directories.map(async (directory) => ({ directory, imports: importsField(await readText(posix.join(directory, MANIFEST))) })));
|
|
48
|
+
const deepestFirst = manifests.toSorted((left, right) => right.directory.length - left.directory.length);
|
|
49
|
+
const present = new Set(files);
|
|
50
|
+
return (importer, specifier) => {
|
|
51
|
+
const owner = deepestFirst.find(({ directory }) => directory === "." || importer.startsWith(`${directory}/`));
|
|
52
|
+
if (owner === undefined) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return matched(owner.imports, specifier)
|
|
56
|
+
.map((target) => posix.normalize(posix.join(owner.directory, target)))
|
|
57
|
+
.find((path) => present.has(path));
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=packageImports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageImports.js","sourceRoot":"","sources":["../../../src/rules/files/packageImports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stylesheetImporters.d.ts","sourceRoot":"","sources":["../../../src/rules/files/stylesheetImporters.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,UAAU,CAAC;AAyCvD,wBAAsB,mBAAmB,CACxC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC,GACxD,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAmBjD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { parse } from "#rules/syntax.ts";
|
|
4
|
+
import { packageImports } from "./packageImports.js";
|
|
5
|
+
const STYLESHEET = /\.s?css$/u;
|
|
6
|
+
const MENTIONS_STYLESHEET = /\.s?css["'?]/u;
|
|
7
|
+
const CSS_IMPORT = /@import\s+(?:url\(\s*)?(?<quote>["']?)(?<target>[^"'()\s;]+)\k<quote>/gu;
|
|
8
|
+
const CSS_COMMENT = /\/\*[\s\S]*?\*\//gu;
|
|
9
|
+
const QUERY = /\?.*$/u;
|
|
10
|
+
const PACKAGE_IMPORT = "#";
|
|
11
|
+
const EXTERNAL = /^(?:[a-z][a-z0-9+.-]*:|\/)/iu;
|
|
12
|
+
function moduleImports(file) {
|
|
13
|
+
const source = MENTIONS_STYLESHEET.test(file.text) ? parse(file) : undefined;
|
|
14
|
+
return (source?.statements ?? []).flatMap((statement) => ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier) ? [statement.moduleSpecifier.text] : []);
|
|
15
|
+
}
|
|
16
|
+
function cssImports(text) {
|
|
17
|
+
return [...text.replace(CSS_COMMENT, "").matchAll(CSS_IMPORT)].flatMap((match) => match.groups?.target ?? []);
|
|
18
|
+
}
|
|
19
|
+
function resolver(aliases, present, fromStylesheet) {
|
|
20
|
+
return (importer, raw) => {
|
|
21
|
+
const specifier = raw.replace(QUERY, "");
|
|
22
|
+
if (specifier.startsWith(PACKAGE_IMPORT)) {
|
|
23
|
+
return aliases(importer, specifier);
|
|
24
|
+
}
|
|
25
|
+
const relative = specifier.startsWith(".") || (fromStylesheet && !EXTERNAL.test(specifier));
|
|
26
|
+
const path = posix.normalize(posix.join(posix.dirname(importer), specifier));
|
|
27
|
+
return relative && present.has(path) ? path : undefined;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export async function stylesheetImporters(inputs) {
|
|
31
|
+
const present = new Set(inputs.files);
|
|
32
|
+
const aliases = await packageImports(inputs);
|
|
33
|
+
const stylesheets = await Promise.all(inputs.files.filter((path) => STYLESHEET.test(path)).map(async (path) => ({ path, text: (await inputs.readText(path)) ?? "" })));
|
|
34
|
+
const fromModule = resolver(aliases, present, false);
|
|
35
|
+
const fromStylesheet = resolver(aliases, present, true);
|
|
36
|
+
const edges = [
|
|
37
|
+
...inputs.sources.flatMap((source) => moduleImports(source).map((specifier) => [source.path, fromModule(source.path, specifier)])),
|
|
38
|
+
...stylesheets.flatMap((sheet) => cssImports(sheet.text).map((specifier) => [sheet.path, fromStylesheet(sheet.path, specifier)])),
|
|
39
|
+
];
|
|
40
|
+
const found = new Map();
|
|
41
|
+
for (const [importer, target] of edges) {
|
|
42
|
+
if (target !== undefined && STYLESHEET.test(target)) {
|
|
43
|
+
found.set(target, [...new Set([...(found.get(target) ?? []), importer])]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return found;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=stylesheetImporters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stylesheetImporters.js","sourceRoot":"","sources":["../../../src/rules/files/stylesheetImporters.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/quality",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Repository quality gate with typed rules, a baseline of existing debt and a registry of reasoned exceptions",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/quality#readme",
|
|
6
6
|
"bugs": {
|
package/src/imports/graph.ts
CHANGED
|
@@ -4,8 +4,8 @@ import type { RuleInputs, SourceFile } from "#rule.ts";
|
|
|
4
4
|
import { parse } from "#rules/syntax.ts";
|
|
5
5
|
import { ambientModules } from "./ambient.ts";
|
|
6
6
|
import { type ImportKind, type ImportRequest, importsOf } from "./extract.ts";
|
|
7
|
-
import { projectsFor } from "./projects.ts";
|
|
8
|
-
import { type Endpoint, isDeclarationFile, resolveImport } from "./resolve.ts";
|
|
7
|
+
import { type Project, projectsFor } from "./projects.ts";
|
|
8
|
+
import { type Endpoint, isDeclarationFile, presumedTarget, resolveImport } from "./resolve.ts";
|
|
9
9
|
import { type WorkspacePackage, workspacePackages } from "./workspace.ts";
|
|
10
10
|
|
|
11
11
|
export interface ImportEdge {
|
|
@@ -31,11 +31,15 @@ const QUERY = /\?.*$/u;
|
|
|
31
31
|
const NO_MODULES =
|
|
32
32
|
"the import graph covers no modules: the sources hold no TypeScript or JavaScript module. Point `sources` at the code, or turn the imports rules off.";
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
function missingTarget(from: string, request: ImportRequest): string | undefined {
|
|
34
|
+
function relativeTarget(from: string, request: ImportRequest): string | undefined {
|
|
36
35
|
const relative = request.kind === "path-reference" || request.specifier.startsWith("./") || request.specifier.startsWith("../");
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
return relative ? posix.join(posix.dirname(from), request.specifier.replace(QUERY, "")) : undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// A relative import or a package import of a missing file still names a path, so fences see it before its generator writes it.
|
|
40
|
+
function missingTarget(root: string, from: string, request: ImportRequest, project: Project): string | undefined {
|
|
41
|
+
const target = presumedTarget(root, request, join(root, from), project) ?? relativeTarget(from, request);
|
|
42
|
+
return target === undefined || target.startsWith("../") ? undefined : target;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
function walk(inputs: RuleInputs, root: string): Pick<ImportGraph, "edges" | "modules" | "unresolved"> {
|
|
@@ -52,7 +56,7 @@ function walk(inputs: RuleInputs, root: string): Pick<ImportGraph, "edges" | "mo
|
|
|
52
56
|
for (const request of importsOf(syntax, isDeclarationFile(file.path))) {
|
|
53
57
|
const to = resolveImport(root, ambient, request, path, project);
|
|
54
58
|
if (to === undefined) {
|
|
55
|
-
const missing = missingTarget(file.path, request);
|
|
59
|
+
const missing = missingTarget(root, file.path, request, project);
|
|
56
60
|
unresolved.push({ ...request, from: file.path, missing });
|
|
57
61
|
edges.push(...(missing === undefined ? [] : [{ ...request, from: file.path, to: { kind: "file" as const, path: missing } }]));
|
|
58
62
|
} else {
|
package/src/imports/resolve.ts
CHANGED
|
@@ -21,6 +21,16 @@ const DECLARATION = /\.d\.[cm]?ts$/u;
|
|
|
21
21
|
|
|
22
22
|
const QUERY = /\?.*$/u;
|
|
23
23
|
|
|
24
|
+
const PACKAGE_IMPORT = "#";
|
|
25
|
+
|
|
26
|
+
const PACKAGE_JSON = "package.json";
|
|
27
|
+
|
|
28
|
+
const presumingHost: ts.ModuleResolutionHost = {
|
|
29
|
+
...ts.sys,
|
|
30
|
+
directoryExists: () => true,
|
|
31
|
+
fileExists: (path) => !path.endsWith(`/${PACKAGE_JSON}`) || ts.sys.fileExists(path),
|
|
32
|
+
};
|
|
33
|
+
|
|
24
34
|
export function builtinName(specifier: string): string | undefined {
|
|
25
35
|
if (!isBuiltin(specifier)) {
|
|
26
36
|
return undefined;
|
|
@@ -90,6 +100,14 @@ function resolvedPath(request: ImportRequest, from: string, project: Project): s
|
|
|
90
100
|
return path === undefined ? undefined : fitting(path, request.type);
|
|
91
101
|
}
|
|
92
102
|
|
|
103
|
+
export function presumedTarget(root: string, request: ImportRequest, from: string, project: Project): string | undefined {
|
|
104
|
+
if (request.kind !== "import" || !request.specifier.startsWith(PACKAGE_IMPORT)) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const path = ts.resolveModuleName(request.specifier.replace(QUERY, ""), from, project.options, presumingHost).resolvedModule?.resolvedFileName;
|
|
108
|
+
return path === undefined ? undefined : posix(relative(root, path));
|
|
109
|
+
}
|
|
110
|
+
|
|
93
111
|
function endpointAt(root: string, specifier: string, path: string): Endpoint {
|
|
94
112
|
const real = posix(realpathSync(path));
|
|
95
113
|
const installed = real.lastIndexOf(NODE_MODULES);
|
package/src/naming/ownExports.ts
CHANGED
|
@@ -29,9 +29,7 @@ function isPrimitiveLiteral(node: ts.Expression | undefined): boolean {
|
|
|
29
29
|
|
|
30
30
|
function isExported(statement: ts.Statement): boolean {
|
|
31
31
|
const modifiers = ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : undefined;
|
|
32
|
-
|
|
33
|
-
const isDefault = modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
|
|
34
|
-
return exported && !isDefault;
|
|
32
|
+
return modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
function variables(statement: ts.VariableStatement): readonly OwnExport[] {
|
|
@@ -74,22 +72,53 @@ function listed(statement: ts.ExportDeclaration, known: ReadonlyMap<string, OwnE
|
|
|
74
72
|
return [];
|
|
75
73
|
}
|
|
76
74
|
return clause.elements.flatMap((element) => {
|
|
77
|
-
const name = element.name.text;
|
|
78
75
|
const local = known.get((element.propertyName ?? element.name).text);
|
|
79
|
-
if (
|
|
76
|
+
if (local === undefined) {
|
|
80
77
|
return [];
|
|
81
78
|
}
|
|
79
|
+
const name = element.name.text === "default" ? local.name : element.name.text;
|
|
82
80
|
const typeOnly = statement.isTypeOnly || element.isTypeOnly;
|
|
83
81
|
return [{ constant: local.constant, kind: typeOnly ? ("type" as const) : local.kind, name }];
|
|
84
82
|
});
|
|
85
83
|
}
|
|
86
84
|
|
|
85
|
+
function wrappedName(expression: ts.Expression): string | undefined {
|
|
86
|
+
if (ts.isIdentifier(expression)) {
|
|
87
|
+
return expression.text;
|
|
88
|
+
}
|
|
89
|
+
if (ts.isFunctionExpression(expression) || ts.isClassExpression(expression)) {
|
|
90
|
+
return expression.name?.text;
|
|
91
|
+
}
|
|
92
|
+
if (ts.isCallExpression(expression)) {
|
|
93
|
+
const [wrapped] = expression.arguments;
|
|
94
|
+
return wrapped === undefined ? undefined : wrappedName(wrapped);
|
|
95
|
+
}
|
|
96
|
+
return ts.isParenthesizedExpression(expression) || ts.isAsExpression(expression) || ts.isSatisfiesExpression(expression)
|
|
97
|
+
? wrappedName(expression.expression)
|
|
98
|
+
: undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// The default export counts under the name it resolves to, as in memo(ItemPanel); noDefaultExport reports the default export itself.
|
|
102
|
+
function defaulted(statement: ts.ExportAssignment, known: ReadonlyMap<string, OwnExport>): readonly OwnExport[] {
|
|
103
|
+
const name = statement.isExportEquals ? undefined : wrappedName(statement.expression);
|
|
104
|
+
if (name === undefined) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return [known.get(name) ?? { constant: false, kind: "value", name }];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function exportsOf(statement: ts.Statement, known: ReadonlyMap<string, OwnExport>): readonly OwnExport[] {
|
|
111
|
+
if (ts.isExportDeclaration(statement)) {
|
|
112
|
+
return listed(statement, known);
|
|
113
|
+
}
|
|
114
|
+
if (ts.isExportAssignment(statement)) {
|
|
115
|
+
return defaulted(statement, known);
|
|
116
|
+
}
|
|
117
|
+
return isExported(statement) ? declared(statement) : [];
|
|
118
|
+
}
|
|
119
|
+
|
|
87
120
|
export function ownExports(source: ts.SourceFile): readonly OwnExport[] {
|
|
88
121
|
const known = locals(source);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return listed(statement, known);
|
|
92
|
-
}
|
|
93
|
-
return isExported(statement) ? declared(statement) : [];
|
|
94
|
-
});
|
|
122
|
+
const all = source.statements.flatMap((statement) => exportsOf(statement, known));
|
|
123
|
+
return [...new Map(all.map((entry) => [`${entry.kind} ${entry.name}`, entry])).values()];
|
|
95
124
|
}
|
|
@@ -2,7 +2,8 @@ import { posix } from "node:path";
|
|
|
2
2
|
import { Effect, Schema } from "effect";
|
|
3
3
|
import ignore, { type Ignore } from "ignore";
|
|
4
4
|
import { CAMEL, KEBAB } from "#naming/words.ts";
|
|
5
|
-
import { defineRule, type Finding
|
|
5
|
+
import { defineRule, type Finding } from "#rule.ts";
|
|
6
|
+
import { stylesheetImporters } from "./stylesheetImporters.ts";
|
|
6
7
|
|
|
7
8
|
const DEFAULT_TOOL_OWNED: readonly string[] = [
|
|
8
9
|
".*",
|
|
@@ -41,9 +42,7 @@ const STYLESHEETS: ReadonlySet<string> = new Set(["css", "scss"]);
|
|
|
41
42
|
|
|
42
43
|
const DOTTED_KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)*(?:\.[a-z0-9]+(?:-[a-z0-9]+)*)*$/u;
|
|
43
44
|
|
|
44
|
-
const SNAKE = /^[a-z0-9]+(?:
|
|
45
|
-
|
|
46
|
-
const STYLESHEET_IMPORT = /["'](?<stylesheet>\.{1,2}\/[^"'\n]+\.s?css)["']/gu;
|
|
45
|
+
const SNAKE = /^[a-z0-9]+(?:__?[a-z0-9]+)*$/u;
|
|
47
46
|
|
|
48
47
|
const CODE = /\.[cm]?[jt]sx?$/u;
|
|
49
48
|
|
|
@@ -52,20 +51,9 @@ const OtherNamesOptions = Schema.Struct({
|
|
|
52
51
|
toolOwned: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed(DEFAULT_TOOL_OWNED))),
|
|
53
52
|
});
|
|
54
53
|
|
|
55
|
-
function importers(sources: readonly SourceFile[]): ReadonlyMap<string, readonly string[]> {
|
|
56
|
-
const found = new Map<string, string[]>();
|
|
57
|
-
for (const source of sources) {
|
|
58
|
-
for (const match of source.text.matchAll(STYLESHEET_IMPORT)) {
|
|
59
|
-
const target = posix.normalize(posix.join(posix.dirname(source.path), match.groups?.stylesheet ?? ""));
|
|
60
|
-
found.set(target, [...new Set([...(found.get(target) ?? []), source.path])]);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return found;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
54
|
function stylesheetMessage(path: string, stem: string, importedBy: readonly string[]): string | undefined {
|
|
67
55
|
const [only] = importedBy;
|
|
68
|
-
if (importedBy.length === 1 && only !== undefined) {
|
|
56
|
+
if (importedBy.length === 1 && only !== undefined && CODE.test(only)) {
|
|
69
57
|
const owner = posix.basename(only).replace(CODE, "");
|
|
70
58
|
return stem === owner ? undefined : `"${stem}" is styling for ${posix.basename(only)} alone, so it is named ${owner}${posix.extname(path)}.`;
|
|
71
59
|
}
|
|
@@ -86,7 +74,9 @@ function messageFor(path: string, context: Context): string | undefined {
|
|
|
86
74
|
return undefined;
|
|
87
75
|
}
|
|
88
76
|
if (context.content.ignores(path)) {
|
|
89
|
-
return SNAKE.test(stem)
|
|
77
|
+
return SNAKE.test(stem)
|
|
78
|
+
? undefined
|
|
79
|
+
: `"${name}" is content, so it is named in snake_case, such as forest_path.json, with a double underscore between the parts of an id, such as forest_path__clearing.json.`;
|
|
90
80
|
}
|
|
91
81
|
if (STYLESHEETS.has(kind)) {
|
|
92
82
|
return stylesheetMessage(path, stem, context.stylesheets.get(path) ?? []);
|
|
@@ -100,10 +90,10 @@ function messageFor(path: string, context: Context): string | undefined {
|
|
|
100
90
|
}
|
|
101
91
|
|
|
102
92
|
export const otherNames = defineRule({
|
|
103
|
-
check: ({ files, options, sources }) => {
|
|
93
|
+
check: async ({ files, options, readText, sources }) => {
|
|
104
94
|
const context: Context = {
|
|
105
95
|
content: ignore().add([...options.content]),
|
|
106
|
-
stylesheets:
|
|
96
|
+
stylesheets: await stylesheetImporters({ files, readText, sources }),
|
|
107
97
|
toolOwned: ignore().add([...options.toolOwned]),
|
|
108
98
|
};
|
|
109
99
|
return files.flatMap((path): readonly Finding[] => {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import type { RuleInputs } from "#rule.ts";
|
|
3
|
+
|
|
4
|
+
export type PackageImports = (importer: string, specifier: string) => string | undefined;
|
|
5
|
+
|
|
6
|
+
const MANIFEST = "package.json";
|
|
7
|
+
|
|
8
|
+
function importsField(text: string | undefined): unknown {
|
|
9
|
+
if (text === undefined) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const manifest: unknown = JSON.parse(text);
|
|
14
|
+
return typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "imports") : undefined;
|
|
15
|
+
} catch {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function targetsIn(value: unknown): readonly string[] {
|
|
21
|
+
if (typeof value === "string") {
|
|
22
|
+
return [value];
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
return value.flatMap(targetsIn);
|
|
26
|
+
}
|
|
27
|
+
return typeof value === "object" && value !== null ? Object.values(value).flatMap(targetsIn) : [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function matched(imports: unknown, specifier: string): readonly string[] {
|
|
31
|
+
if (typeof imports !== "object" || imports === null) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const entries = Object.entries(imports);
|
|
35
|
+
const exact = entries.find(([key]) => key === specifier);
|
|
36
|
+
if (exact !== undefined) {
|
|
37
|
+
return targetsIn(exact[1]);
|
|
38
|
+
}
|
|
39
|
+
const patterns = entries.flatMap(([key, value]) => {
|
|
40
|
+
const [prefix = "", suffix, ...rest] = key.split("*");
|
|
41
|
+
const fits = suffix !== undefined && rest.length === 0 && specifier.length >= prefix.length + suffix.length;
|
|
42
|
+
return fits && specifier.startsWith(prefix) && specifier.endsWith(suffix) ? [{ prefix, suffix, value }] : [];
|
|
43
|
+
});
|
|
44
|
+
const [best] = patterns.toSorted((left, right) => right.prefix.length - left.prefix.length);
|
|
45
|
+
if (best === undefined) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const star = specifier.slice(best.prefix.length, specifier.length - best.suffix.length);
|
|
49
|
+
return targetsIn(best.value).map((target) => target.replaceAll("*", star));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function packageImports({ files, readText }: Pick<RuleInputs, "files" | "readText">): Promise<PackageImports> {
|
|
53
|
+
const directories = files.filter((path) => posix.basename(path) === MANIFEST).map((path) => posix.dirname(path));
|
|
54
|
+
const manifests = await Promise.all(
|
|
55
|
+
directories.map(async (directory) => ({ directory, imports: importsField(await readText(posix.join(directory, MANIFEST))) })),
|
|
56
|
+
);
|
|
57
|
+
const deepestFirst = manifests.toSorted((left, right) => right.directory.length - left.directory.length);
|
|
58
|
+
const present = new Set(files);
|
|
59
|
+
return (importer, specifier) => {
|
|
60
|
+
const owner = deepestFirst.find(({ directory }) => directory === "." || importer.startsWith(`${directory}/`));
|
|
61
|
+
if (owner === undefined) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return matched(owner.imports, specifier)
|
|
65
|
+
.map((target) => posix.normalize(posix.join(owner.directory, target)))
|
|
66
|
+
.find((path) => present.has(path));
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import type { RuleInputs, SourceFile } from "#rule.ts";
|
|
4
|
+
import { parse } from "#rules/syntax.ts";
|
|
5
|
+
import { type PackageImports, packageImports } from "./packageImports.ts";
|
|
6
|
+
|
|
7
|
+
const STYLESHEET = /\.s?css$/u;
|
|
8
|
+
|
|
9
|
+
const MENTIONS_STYLESHEET = /\.s?css["'?]/u;
|
|
10
|
+
|
|
11
|
+
const CSS_IMPORT = /@import\s+(?:url\(\s*)?(?<quote>["']?)(?<target>[^"'()\s;]+)\k<quote>/gu;
|
|
12
|
+
|
|
13
|
+
const CSS_COMMENT = /\/\*[\s\S]*?\*\//gu;
|
|
14
|
+
|
|
15
|
+
const QUERY = /\?.*$/u;
|
|
16
|
+
|
|
17
|
+
const PACKAGE_IMPORT = "#";
|
|
18
|
+
|
|
19
|
+
const EXTERNAL = /^(?:[a-z][a-z0-9+.-]*:|\/)/iu;
|
|
20
|
+
|
|
21
|
+
function moduleImports(file: SourceFile): readonly string[] {
|
|
22
|
+
const source = MENTIONS_STYLESHEET.test(file.text) ? parse(file) : undefined;
|
|
23
|
+
return (source?.statements ?? []).flatMap((statement) =>
|
|
24
|
+
ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier) ? [statement.moduleSpecifier.text] : [],
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function cssImports(text: string): readonly string[] {
|
|
29
|
+
return [...text.replace(CSS_COMMENT, "").matchAll(CSS_IMPORT)].flatMap((match) => match.groups?.target ?? []);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function resolver(aliases: PackageImports, present: ReadonlySet<string>, fromStylesheet: boolean) {
|
|
33
|
+
return (importer: string, raw: string): string | undefined => {
|
|
34
|
+
const specifier = raw.replace(QUERY, "");
|
|
35
|
+
if (specifier.startsWith(PACKAGE_IMPORT)) {
|
|
36
|
+
return aliases(importer, specifier);
|
|
37
|
+
}
|
|
38
|
+
const relative = specifier.startsWith(".") || (fromStylesheet && !EXTERNAL.test(specifier));
|
|
39
|
+
const path = posix.normalize(posix.join(posix.dirname(importer), specifier));
|
|
40
|
+
return relative && present.has(path) ? path : undefined;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function stylesheetImporters(
|
|
45
|
+
inputs: Pick<RuleInputs, "files" | "readText" | "sources">,
|
|
46
|
+
): Promise<ReadonlyMap<string, readonly string[]>> {
|
|
47
|
+
const present = new Set(inputs.files);
|
|
48
|
+
const aliases = await packageImports(inputs);
|
|
49
|
+
const stylesheets = await Promise.all(
|
|
50
|
+
inputs.files.filter((path) => STYLESHEET.test(path)).map(async (path) => ({ path, text: (await inputs.readText(path)) ?? "" })),
|
|
51
|
+
);
|
|
52
|
+
const fromModule = resolver(aliases, present, false);
|
|
53
|
+
const fromStylesheet = resolver(aliases, present, true);
|
|
54
|
+
const edges = [
|
|
55
|
+
...inputs.sources.flatMap((source) => moduleImports(source).map((specifier) => [source.path, fromModule(source.path, specifier)] as const)),
|
|
56
|
+
...stylesheets.flatMap((sheet) => cssImports(sheet.text).map((specifier) => [sheet.path, fromStylesheet(sheet.path, specifier)] as const)),
|
|
57
|
+
];
|
|
58
|
+
const found = new Map<string, readonly string[]>();
|
|
59
|
+
for (const [importer, target] of edges) {
|
|
60
|
+
if (target !== undefined && STYLESHEET.test(target)) {
|
|
61
|
+
found.set(target, [...new Set([...(found.get(target) ?? []), importer])]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return found;
|
|
65
|
+
}
|