@isentinel/eslint-config 6.0.0-beta.42 → 6.0.0-beta.44
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/README.md +47 -0
- package/dist/cli.mjs +5 -2
- package/dist/index.d.mts +73 -3
- package/dist/index.mjs +103 -8
- package/dist/lint-cli.mjs +73 -13
- package/dist/oxlint.d.mts +68 -1
- package/dist/oxlint.mjs +117 -3
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -890,6 +890,53 @@ otherwise, you can install them manually:
|
|
|
890
890
|
pnpm i -D eslint-plugin-eslint-plugin
|
|
891
891
|
```
|
|
892
892
|
|
|
893
|
+
#### Co-located Tests
|
|
894
|
+
|
|
895
|
+
`projectStructure` requires every source file to sit next to its test file,
|
|
896
|
+
through
|
|
897
|
+
[eslint-plugin-project-structure](https://github.com/Igorkowalski94/eslint-plugin-project-structure).
|
|
898
|
+
`src/place-service.ts` then has to be joined by `src/place-service.spec.ts`.
|
|
899
|
+
Only `enforceExistence` is driven here, so the rule never enforces naming:
|
|
900
|
+
|
|
901
|
+
```ts
|
|
902
|
+
// eslint.config.ts
|
|
903
|
+
import isentinel from "@isentinel/eslint-config";
|
|
904
|
+
|
|
905
|
+
export default isentinel({
|
|
906
|
+
projectStructure: {
|
|
907
|
+
// The plugin's name placeholders, plus `{ext}` for the extension of the
|
|
908
|
+
// file that matched. Default: ["{node-name}.spec.{ext}"]
|
|
909
|
+
enforceExistence: "__tests__/{node-name}.test.{ext}",
|
|
910
|
+
// Default: every source file the preset lints
|
|
911
|
+
files: ["src/**/*.{ts,tsx}"],
|
|
912
|
+
// Default: tests, `.d.ts` files and build configs
|
|
913
|
+
ignores: ["**/index.ts"],
|
|
914
|
+
// Default: `process.cwd()`
|
|
915
|
+
projectRoot: import.meta.dirname,
|
|
916
|
+
// Restricts the check to one subtree. Default: all of `projectRoot`
|
|
917
|
+
structureRoot: "src",
|
|
918
|
+
},
|
|
919
|
+
});
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
```bash
|
|
923
|
+
pnpm i -D eslint-plugin-project-structure
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
`{node-name}` matches the kebab-case filenames the preset enforces; the others
|
|
927
|
+
are `{nodeName}`, `{NodeName}`, `{node_name}` and `{NODE_NAME}`. A file already
|
|
928
|
+
matching a template is exempt from it. A source file with no test disables the
|
|
929
|
+
rule at the source.
|
|
930
|
+
|
|
931
|
+
The rule needs no type information, so oxlint runs it too: the same option is
|
|
932
|
+
accepted by the oxlint factory, and hybrid mode (`oxlint: true`) hands the check
|
|
933
|
+
to oxlint.
|
|
934
|
+
|
|
935
|
+
Two caveats. Deleting `foo.spec.ts` does not change `foo.ts`, so a cached run
|
|
936
|
+
replays the old clean result — the check is only sound uncached. And the plugin
|
|
937
|
+
writes `projectStructure.cache.json` into `projectRoot` while it has something
|
|
938
|
+
to report, so gitignore it.
|
|
939
|
+
|
|
893
940
|
### Git hooks
|
|
894
941
|
|
|
895
942
|
If you want to apply lint and auto-fix before every commit, use
|
package/dist/cli.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import path from "node:path";
|
|
|
9
9
|
import { existsSync, readFileSync } from "fs";
|
|
10
10
|
import { execSync } from "node:child_process";
|
|
11
11
|
//#region package.json
|
|
12
|
-
var version = "6.0.0-beta.
|
|
12
|
+
var version = "6.0.0-beta.44";
|
|
13
13
|
var package_default = {
|
|
14
14
|
name: "@isentinel/eslint-config",
|
|
15
15
|
version,
|
|
@@ -155,6 +155,7 @@ var package_default = {
|
|
|
155
155
|
"eslint-plugin-jest": "catalog:peer",
|
|
156
156
|
"eslint-plugin-jest-extended": "catalog:peer",
|
|
157
157
|
"eslint-plugin-n": "catalog:peer",
|
|
158
|
+
"eslint-plugin-project-structure": "catalog:peer",
|
|
158
159
|
"eslint-plugin-react-jsx": "catalog:peer",
|
|
159
160
|
"eslint-plugin-react-naming-convention": "catalog:peer",
|
|
160
161
|
"eslint-plugin-react-x": "catalog:peer",
|
|
@@ -179,6 +180,7 @@ var package_default = {
|
|
|
179
180
|
"eslint-plugin-jest": "^29.15.0",
|
|
180
181
|
"eslint-plugin-jest-extended": "^3.0.0",
|
|
181
182
|
"eslint-plugin-n": "^17.0.0 || ^18.0.0",
|
|
183
|
+
"eslint-plugin-project-structure": "^3.14.0",
|
|
182
184
|
"eslint-plugin-react-jsx": "^5.10.0",
|
|
183
185
|
"eslint-plugin-react-naming-convention": "^5.10.0",
|
|
184
186
|
"eslint-plugin-react-x": "^5.10.0",
|
|
@@ -192,6 +194,7 @@ var package_default = {
|
|
|
192
194
|
"eslint-plugin-jest": { "optional": true },
|
|
193
195
|
"eslint-plugin-jest-extended": { "optional": true },
|
|
194
196
|
"eslint-plugin-n": { "optional": true },
|
|
197
|
+
"eslint-plugin-project-structure": { "optional": true },
|
|
195
198
|
"eslint-plugin-react-jsx": { "optional": true },
|
|
196
199
|
"eslint-plugin-react-naming-convention": { "optional": true },
|
|
197
200
|
"eslint-plugin-react-x": { "optional": true },
|
|
@@ -531,7 +534,7 @@ async function updateEslintFiles(result) {
|
|
|
531
534
|
//#endregion
|
|
532
535
|
//#region src/cli/constants-generated.ts
|
|
533
536
|
const versionsMap = {
|
|
534
|
-
"eslint": "10.
|
|
537
|
+
"eslint": "10.8.1",
|
|
535
538
|
"eslint-plugin-jest": "29.16.0",
|
|
536
539
|
"eslint-plugin-react-jsx": "5.17.3",
|
|
537
540
|
"eslint-plugin-react-naming-convention": "5.17.3",
|
package/dist/index.d.mts
CHANGED
|
@@ -6488,6 +6488,7 @@ interface OverrideSiteChains {
|
|
|
6488
6488
|
jsonc: [JsoncRuleDefaults];
|
|
6489
6489
|
markdown: [MarkdownRuleDefaults];
|
|
6490
6490
|
naming: FeatureChain;
|
|
6491
|
+
projectStructure: FeatureChain;
|
|
6491
6492
|
react: ReactChain;
|
|
6492
6493
|
roblox: MainChain;
|
|
6493
6494
|
toml: [TomlRuleDefaults];
|
|
@@ -6497,7 +6498,7 @@ interface OverrideSiteChains {
|
|
|
6497
6498
|
type TestChain = [TestRuleDefaults, SourceFeatureRuleDefaults, MainRuleDefaults];
|
|
6498
6499
|
type ValidateOptionsWith<O, VK extends VariantKey> = ValidateSite<O, "rules", MainChain, VK> & ValidateSubOptionsFromTable<O, OverrideSiteChains, VK> & ValidateTestOption<O, TestChain, VK>;
|
|
6499
6500
|
//#endregion
|
|
6500
|
-
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-config-flat-gitignore/2.3.0/
|
|
6501
|
+
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-config-flat-gitignore/2.3.0/0a594adbd94ca75e2950b1104992894d49bc93f4f2bc935ad192d7104e4c271e/node_modules/eslint-config-flat-gitignore/dist/index.d.mts
|
|
6501
6502
|
interface FlatGitignoreOptions {
|
|
6502
6503
|
/**
|
|
6503
6504
|
* Name of the configuration.
|
|
@@ -10470,6 +10471,21 @@ interface RuleOptions {
|
|
|
10470
10471
|
* @see https://eslint.org/docs/latest/rules/preserve-caught-error
|
|
10471
10472
|
*/
|
|
10472
10473
|
'preserve-caught-error'?: Linter.RuleEntry<PreserveCaughtError>;
|
|
10474
|
+
/**
|
|
10475
|
+
* Enforce advanced naming rules and prohibit the use of given selectors in a given file. Have full control over what your file can contain and the naming conventions it must follow.
|
|
10476
|
+
* @see https://github.com/Igorkowalski94/eslint-plugin-project-structure/wiki/project%E2%80%91structure-%E2%80%8Bfile%E2%80%91composition#root
|
|
10477
|
+
*/
|
|
10478
|
+
'project-structure/file-composition'?: Linter.RuleEntry<ProjectStructureFileComposition>;
|
|
10479
|
+
/**
|
|
10480
|
+
* Enforce rules on folder structure to keep your repository consistent, orderly and well thought out.
|
|
10481
|
+
* @see https://github.com/Igorkowalski94/eslint-plugin-project-structure/wiki/project%E2%80%91structure-%E2%80%8Bfolder%E2%80%91structure#root
|
|
10482
|
+
*/
|
|
10483
|
+
'project-structure/folder-structure'?: Linter.RuleEntry<ProjectStructureFolderStructure>;
|
|
10484
|
+
/**
|
|
10485
|
+
* A key principle of a healthy project is to prevent the creation of a massive dependency tree, where removing or editing one feature triggers a chain reaction that impacts the entire project. Create independent modules to keep your project scalable and easy to maintain. Get rid of dependencies between modules and create truly independent functionalities.
|
|
10486
|
+
* @see https://github.com/Igorkowalski94/eslint-plugin-project-structure/wiki/project%E2%80%91structure-%E2%80%8Bindependent%E2%80%91modules#root
|
|
10487
|
+
*/
|
|
10488
|
+
'project-structure/independent-modules'?: Linter.RuleEntry<ProjectStructureIndependentModules>;
|
|
10473
10489
|
/**
|
|
10474
10490
|
* Require returning inside each `then()` to create readable and reusable Promise chains.
|
|
10475
10491
|
* @see https://github.com/eslint-community/eslint-plugin-promise/blob/main/docs/rules/always-return.md
|
|
@@ -23278,6 +23294,18 @@ type PreserveCaughtError = [] | [{
|
|
|
23278
23294
|
argumentPosition: number;
|
|
23279
23295
|
})[];
|
|
23280
23296
|
}];
|
|
23297
|
+
// ----- project-structure/file-composition -----
|
|
23298
|
+
type ProjectStructureFileComposition = [] | [{
|
|
23299
|
+
[k: string]: unknown | undefined;
|
|
23300
|
+
}];
|
|
23301
|
+
// ----- project-structure/folder-structure -----
|
|
23302
|
+
type ProjectStructureFolderStructure = [] | [{
|
|
23303
|
+
[k: string]: unknown | undefined;
|
|
23304
|
+
}];
|
|
23305
|
+
// ----- project-structure/independent-modules -----
|
|
23306
|
+
type ProjectStructureIndependentModules = [] | [{
|
|
23307
|
+
[k: string]: unknown | undefined;
|
|
23308
|
+
}];
|
|
23281
23309
|
// ----- promise/always-return -----
|
|
23282
23310
|
type PromiseAlwaysReturn = [] | [{
|
|
23283
23311
|
ignoreLastCallback?: boolean;
|
|
@@ -27158,7 +27186,7 @@ type Yoda = [] | [("always" | "never")] | [("always" | "never"), {
|
|
|
27158
27186
|
onlyEquality?: boolean;
|
|
27159
27187
|
}];
|
|
27160
27188
|
// Names of all the configs
|
|
27161
|
-
type ConfigNames = 'isentinel/eslint/comments' | 'isentinel/eslint/comments/src' | 'isentinel/e18e/rules' | 'isentinel/eslint-plugin/setup' | 'isentinel/eslint-plugin/rules' | 'isentinel/flawless/setup' | 'isentinel/flawless/rules' | 'isentinel/flawless/markdown-code' | 'isentinel/flawless/rules-type-aware' | 'isentinel/gitignore' | 'isentinel/ignores' | 'isentinel/imports/rules' | 'isentinel/imports/game' | 'isentinel/javascript/setup' | 'isentinel/javascript/rules' | 'isentinel/jsdoc/setup' | 'isentinel/jsdoc' | 'isentinel/jsonc/setup' | 'isentinel/jsonc/rules' | 'isentinel/jsonc/tsconfig' | 'isentinel/markdown/setup' | 'isentinel/markdown/processor' | 'isentinel/markdown/parser' | 'isentinel/markdown/disables' | 'isentinel/naming/setup' | 'isentinel/naming/ts/rules-type-aware' | 'isentinel/naming/tsx/rules-type-aware' | 'isentinel/node/rules' | 'isentinel/oxfmt/setup' | 'isentinel/oxfmt/javascript' | 'isentinel/oxfmt/typescript' | 'isentinel/oxfmt/css' | 'isentinel/oxfmt/scss' | 'isentinel/oxfmt/less' | 'isentinel/oxfmt/html' | 'isentinel/oxfmt/markdown' | 'isentinel/oxfmt/graphql' | 'isentinel/oxfmt/json' | 'isentinel/oxfmt/yaml' | 'isentinel/package-json/setup' | 'isentinel/package-json' | 'isentinel/package-json/root' | 'isentinel/perfectionist/setup' | 'isentinel/perfectionist' | 'isentinel/perfectionist/jsx' | 'isentinel/pnpm/setup' | 'isentinel/pnpm/package-json' | 'isentinel/pnpm/pnpm-workspace-yaml' | 'isentinel/promise' | 'isentinel/react/setup' | 'isentinel/react/setup/naming' | 'isentinel/react/setup/testing-library' | 'isentinel/react/rules' | 'isentinel/react/type-aware-rules' | 'isentinel/roblox/setup' | 'isentinel/roblox/parser' | 'isentinel/roblox/type-aware-parser' | 'isentinel/roblox' | 'isentinel/roblox/rules-type-aware' | 'isentinel/roblox/format-lua/setup' | 'isentinel/roblox/format-lua' | 'isentinel/small-rules/setup' | 'isentinel/small-rules' | 'isentinel/sonarjs' | 'isentinel/spelling/setup' | 'isentinel/spelling' | 'isentinel/stylistic/setup' | 'isentinel/stylistic' | 'isentinel/test/jest/setup' | 'isentinel/test/jest/rules' | 'isentinel/test/jest/type-tests' | 'isentinel/test/vitest/setup' | 'isentinel/test/vitest/rules' | 'isentinel/test/vitest/type-tests' | 'isentinel/toml/setup' | 'isentinel/toml/rules' | 'isentinel/typescript/setup' | 'isentinel/typescript/parser' | 'isentinel/typescript/type-aware-parser' | 'isentinel/typescript/rules' | 'isentinel/typescript/rules-type-aware' | 'isentinel/typescript/erasable-syntax-only' | 'isentinel/unicorn/setup' | 'isentinel/unicorn/rules' | 'isentinel/unicorn/root' | 'isentinel/yaml/setup' | 'isentinel/yaml/rules';
|
|
27189
|
+
type ConfigNames = 'isentinel/eslint/comments' | 'isentinel/eslint/comments/src' | 'isentinel/e18e/rules' | 'isentinel/eslint-plugin/setup' | 'isentinel/eslint-plugin/rules' | 'isentinel/flawless/setup' | 'isentinel/flawless/rules' | 'isentinel/flawless/markdown-code' | 'isentinel/flawless/rules-type-aware' | 'isentinel/gitignore' | 'isentinel/ignores' | 'isentinel/imports/rules' | 'isentinel/imports/game' | 'isentinel/javascript/setup' | 'isentinel/javascript/rules' | 'isentinel/jsdoc/setup' | 'isentinel/jsdoc' | 'isentinel/jsonc/setup' | 'isentinel/jsonc/rules' | 'isentinel/jsonc/tsconfig' | 'isentinel/markdown/setup' | 'isentinel/markdown/processor' | 'isentinel/markdown/parser' | 'isentinel/markdown/disables' | 'isentinel/naming/setup' | 'isentinel/naming/ts/rules-type-aware' | 'isentinel/naming/tsx/rules-type-aware' | 'isentinel/node/rules' | 'isentinel/oxfmt/setup' | 'isentinel/oxfmt/javascript' | 'isentinel/oxfmt/typescript' | 'isentinel/oxfmt/css' | 'isentinel/oxfmt/scss' | 'isentinel/oxfmt/less' | 'isentinel/oxfmt/html' | 'isentinel/oxfmt/markdown' | 'isentinel/oxfmt/graphql' | 'isentinel/oxfmt/json' | 'isentinel/oxfmt/yaml' | 'isentinel/package-json/setup' | 'isentinel/package-json' | 'isentinel/package-json/root' | 'isentinel/perfectionist/setup' | 'isentinel/perfectionist' | 'isentinel/perfectionist/jsx' | 'isentinel/pnpm/setup' | 'isentinel/pnpm/package-json' | 'isentinel/pnpm/pnpm-workspace-yaml' | 'isentinel/project-structure/setup' | 'isentinel/project-structure/rules' | 'isentinel/promise' | 'isentinel/react/setup' | 'isentinel/react/setup/naming' | 'isentinel/react/setup/testing-library' | 'isentinel/react/rules' | 'isentinel/react/type-aware-rules' | 'isentinel/roblox/setup' | 'isentinel/roblox/parser' | 'isentinel/roblox/type-aware-parser' | 'isentinel/roblox' | 'isentinel/roblox/rules-type-aware' | 'isentinel/roblox/format-lua/setup' | 'isentinel/roblox/format-lua' | 'isentinel/small-rules/setup' | 'isentinel/small-rules' | 'isentinel/sonarjs' | 'isentinel/spelling/setup' | 'isentinel/spelling' | 'isentinel/stylistic/setup' | 'isentinel/stylistic' | 'isentinel/test/jest/setup' | 'isentinel/test/jest/rules' | 'isentinel/test/jest/type-tests' | 'isentinel/test/vitest/setup' | 'isentinel/test/vitest/rules' | 'isentinel/test/vitest/type-tests' | 'isentinel/toml/setup' | 'isentinel/toml/rules' | 'isentinel/typescript/setup' | 'isentinel/typescript/parser' | 'isentinel/typescript/type-aware-parser' | 'isentinel/typescript/rules' | 'isentinel/typescript/rules-type-aware' | 'isentinel/typescript/erasable-syntax-only' | 'isentinel/unicorn/setup' | 'isentinel/unicorn/rules' | 'isentinel/unicorn/root' | 'isentinel/yaml/setup' | 'isentinel/yaml/rules';
|
|
27162
27190
|
//#endregion
|
|
27163
27191
|
//#region src/utils.d.ts
|
|
27164
27192
|
type ExtractRuleOptions<T> = T extends Linter.RuleEntry<infer U> ? U : never;
|
|
@@ -27496,6 +27524,37 @@ interface NamingConfig extends OptionsOverridesTypeAware {
|
|
|
27496
27524
|
*/
|
|
27497
27525
|
selectorsTsx?: Array<NamingSelector>;
|
|
27498
27526
|
}
|
|
27527
|
+
interface ProjectStructureConfig extends OptionsOverrides {
|
|
27528
|
+
/**
|
|
27529
|
+
* The files each linted source file has to sit next to, as
|
|
27530
|
+
* `enforceExistence` templates. Takes the plugin's name placeholders
|
|
27531
|
+
* (`{nodeName}`, `{NodeName}`, `{node-name}`, `{node_name}`,
|
|
27532
|
+
* `{NODE_NAME}`) plus `{ext}` for the extension of the file that matched.
|
|
27533
|
+
* A file already matching a template is exempt from it.
|
|
27534
|
+
*
|
|
27535
|
+
* @default ["{node-name}.spec.{ext}"]
|
|
27536
|
+
*/
|
|
27537
|
+
enforceExistence?: Array<string> | string;
|
|
27538
|
+
/**
|
|
27539
|
+
* Files exempt from the check.
|
|
27540
|
+
*
|
|
27541
|
+
* @default [...GLOB_TESTS, GLOB_DTS, ...GLOB_BUILD_CONFIGS]
|
|
27542
|
+
*/
|
|
27543
|
+
ignores?: Array<string>;
|
|
27544
|
+
/**
|
|
27545
|
+
* The folder the enforced paths resolve against, and where the plugin
|
|
27546
|
+
* writes `projectStructure.cache.json` when it reports.
|
|
27547
|
+
*
|
|
27548
|
+
* @default process.cwd()
|
|
27549
|
+
*/
|
|
27550
|
+
projectRoot?: string;
|
|
27551
|
+
/**
|
|
27552
|
+
* Restricts the check to one subtree of `projectRoot`, as a relative path.
|
|
27553
|
+
*
|
|
27554
|
+
* @default undefined - the whole of `projectRoot`
|
|
27555
|
+
*/
|
|
27556
|
+
structureRoot?: string;
|
|
27557
|
+
}
|
|
27499
27558
|
interface OptionsTypeScriptParserOptions {
|
|
27500
27559
|
/**
|
|
27501
27560
|
* Glob patterns for files that should be type aware.
|
|
@@ -27844,6 +27903,14 @@ interface OptionsConfig extends OptionsComponentExtensions, OptionsProjectType {
|
|
|
27844
27903
|
* @see https://github.com/antfu/pnpm-workspace-utils
|
|
27845
27904
|
*/
|
|
27846
27905
|
pnpm?: boolean | OptionsPnpm;
|
|
27906
|
+
/**
|
|
27907
|
+
* Require every source file to have a co-located test file, via
|
|
27908
|
+
* [eslint-plugin-project-structure](https://github.com/Igorkowalski94/eslint-plugin-project-structure).
|
|
27909
|
+
*
|
|
27910
|
+
* @default false
|
|
27911
|
+
* @requires eslint-plugin-project-structure
|
|
27912
|
+
*/
|
|
27913
|
+
projectStructure?: boolean | ProjectStructureConfig;
|
|
27847
27914
|
/**
|
|
27848
27915
|
* Enable react rules.
|
|
27849
27916
|
*
|
|
@@ -28282,6 +28349,9 @@ declare function perfectionist(config?: OptionsProjectType & PerfectionistConfig
|
|
|
28282
28349
|
//#region src/eslint/configs/pnpm.d.ts
|
|
28283
28350
|
declare function pnpm(options: OptionsPnpm & Required<OptionsIsInEditor>): Promise<Array<TypedFlatConfigItem>>;
|
|
28284
28351
|
//#endregion
|
|
28352
|
+
//#region src/eslint/configs/project-structure.d.ts
|
|
28353
|
+
declare function projectStructure({ enforceExistence, files, ignores, overrides, projectRoot, structureRoot }?: ProjectStructureConfig): Promise<Array<TypedFlatConfigItem>>;
|
|
28354
|
+
//#endregion
|
|
28285
28355
|
//#region src/eslint/configs/promise.d.ts
|
|
28286
28356
|
declare function promise(): Array<TypedFlatConfigItem>;
|
|
28287
28357
|
//#endregion
|
|
@@ -28388,4 +28458,4 @@ declare function unicorn({ complementIgnores, nameReplacements, roblox, root: cu
|
|
|
28388
28458
|
//#region src/eslint/configs/yaml.d.ts
|
|
28389
28459
|
declare function yaml({ files, overrides, stylistic }?: OptionsFiles & OptionsOverrides & OptionsStylistic): Promise<Array<TypedFlatConfigItem>>;
|
|
28390
28460
|
//#endregion
|
|
28391
|
-
export { type Awaitable, type ConfigNames, type ConfigSettings, type FlatConfigComposer, GLOB_ALL_JSON, GLOB_ALL_SRC, GLOB_BIN, GLOB_BUILD_CONFIGS, GLOB_CSS, GLOB_DTS, GLOB_EXCLUDE, GLOB_GRAPHQL, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LINTABLE_EXTENSIONS, GLOB_LUA, GLOB_MARKDOWN, GLOB_MARKDOWN_BLOCKS, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_MISE, GLOB_PACKAGE_JSON, GLOB_PACKAGE_JSON_ROOT, GLOB_POSTCSS, GLOB_ROOT, GLOB_ROOT_SRC, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_SRC_EXTENSIONS, GLOB_STYLE, GLOB_TESTS, GLOB_TOML, GLOB_TS, GLOB_TSCONFIG, GLOB_TSX, GLOB_TYPE_TESTS, GLOB_XML, GLOB_YAML, GitignoreOptions, type JsdocOptions, MARKDOWN_PROCESSOR_NAME, type NamedFlatConfigItem, type NamedOptionsConfig, type NamingConfig, type NamingSelector, type OptionsComponentExtensions, type OptionsConfig, type OptionsE18e, type OptionsFiles, type OptionsFilesTypeAware, type OptionsFormatters, type OptionsHasRoblox, type OptionsHasTypeScript, type OptionsIsInEditor, type OptionsJest, type OptionsOverrides, type OptionsOverridesTypeAware, type OptionsPnpm, type OptionsProjectType, type OptionsStylistic, type OptionsTestFramework, type OptionsTypeScriptErasableOnly, type OptionsTypeScriptParserOptions, type OptionsTypeScriptWithTypes, type OptionsTypescript, type OptionsUnicorn, type OptionsVitest, type OxfmtOptions, type PerfectionistConfig, type PrettierOptions, ROBLOX_ALLOWED_WORDS, type ReactConfig, type ReactSettings, type Rules, type SpellCheckConfig, type StylisticConfig, StylisticConfigDefaults, type TypedFlatConfigItem, comments, isentinel as default, isentinel, defaultPluginRenaming, disables, e18e, eslintPlugin, flawless, gitignore, ignores$1 as ignores, imports, isAgentAutofixDisabled, isInAgentSession, isInEditorEnvironment, isInGitHooksOrLintStaged, javascript, jsdoc, jsonc, loadSmallRulesPlugin, markdown, naming, node, oxfmt, packageJson, perfectionist, pnpm, promise, react, roblox$1 as roblox, smallRules, sonarjs, sortCspell, sortGithubAction, sortMiseToml, sortPnpmWorkspace, sortRojoProject, sortTsconfig, spelling, stylistic$1 as stylistic, test, toml, typescript, unicorn, yaml };
|
|
28461
|
+
export { type Awaitable, type ConfigNames, type ConfigSettings, type FlatConfigComposer, GLOB_ALL_JSON, GLOB_ALL_SRC, GLOB_BIN, GLOB_BUILD_CONFIGS, GLOB_CSS, GLOB_DTS, GLOB_EXCLUDE, GLOB_GRAPHQL, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LINTABLE_EXTENSIONS, GLOB_LUA, GLOB_MARKDOWN, GLOB_MARKDOWN_BLOCKS, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_MISE, GLOB_PACKAGE_JSON, GLOB_PACKAGE_JSON_ROOT, GLOB_POSTCSS, GLOB_ROOT, GLOB_ROOT_SRC, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_SRC_EXTENSIONS, GLOB_STYLE, GLOB_TESTS, GLOB_TOML, GLOB_TS, GLOB_TSCONFIG, GLOB_TSX, GLOB_TYPE_TESTS, GLOB_XML, GLOB_YAML, GitignoreOptions, type JsdocOptions, MARKDOWN_PROCESSOR_NAME, type NamedFlatConfigItem, type NamedOptionsConfig, type NamingConfig, type NamingSelector, type OptionsComponentExtensions, type OptionsConfig, type OptionsE18e, type OptionsFiles, type OptionsFilesTypeAware, type OptionsFormatters, type OptionsHasRoblox, type OptionsHasTypeScript, type OptionsIsInEditor, type OptionsJest, type OptionsOverrides, type OptionsOverridesTypeAware, type OptionsPnpm, type OptionsProjectType, type OptionsStylistic, type OptionsTestFramework, type OptionsTypeScriptErasableOnly, type OptionsTypeScriptParserOptions, type OptionsTypeScriptWithTypes, type OptionsTypescript, type OptionsUnicorn, type OptionsVitest, type OxfmtOptions, type PerfectionistConfig, type PrettierOptions, type ProjectStructureConfig, ROBLOX_ALLOWED_WORDS, type ReactConfig, type ReactSettings, type Rules, type SpellCheckConfig, type StylisticConfig, StylisticConfigDefaults, type TypedFlatConfigItem, comments, isentinel as default, isentinel, defaultPluginRenaming, disables, e18e, eslintPlugin, flawless, gitignore, ignores$1 as ignores, imports, isAgentAutofixDisabled, isInAgentSession, isInEditorEnvironment, isInGitHooksOrLintStaged, javascript, jsdoc, jsonc, loadSmallRulesPlugin, markdown, naming, node, oxfmt, packageJson, perfectionist, pnpm, projectStructure, promise, react, roblox$1 as roblox, smallRules, sonarjs, sortCspell, sortGithubAction, sortMiseToml, sortPnpmWorkspace, sortRojoProject, sortTsconfig, spelling, stylistic$1 as stylistic, test, toml, typescript, unicorn, yaml };
|
package/dist/index.mjs
CHANGED
|
@@ -1576,8 +1576,10 @@ function disables({ root, workspaceRoot }) {
|
|
|
1576
1576
|
rules: {
|
|
1577
1577
|
"antfu/no-top-level-await": "off",
|
|
1578
1578
|
"no-console": "off",
|
|
1579
|
+
"roblox/no-null": "off",
|
|
1579
1580
|
"small-rules/require-async-suffix": "off",
|
|
1580
|
-
"ts/explicit-function-return-type": "off"
|
|
1581
|
+
"ts/explicit-function-return-type": "off",
|
|
1582
|
+
"unicorn/no-null": "off"
|
|
1581
1583
|
}
|
|
1582
1584
|
},
|
|
1583
1585
|
{
|
|
@@ -1640,7 +1642,8 @@ function disables({ root, workspaceRoot }) {
|
|
|
1640
1642
|
"import/no-default-export": "off",
|
|
1641
1643
|
"small-rules/require-async-suffix": "off",
|
|
1642
1644
|
"sonar/file-name-differ-from-class": "off",
|
|
1643
|
-
"unicorn/filename-case": "off"
|
|
1645
|
+
"unicorn/filename-case": "off",
|
|
1646
|
+
"unicorn/no-null": "off"
|
|
1644
1647
|
}
|
|
1645
1648
|
},
|
|
1646
1649
|
{
|
|
@@ -1886,7 +1889,7 @@ async function flawless(options = {}, prettierOptions = {}) {
|
|
|
1886
1889
|
];
|
|
1887
1890
|
}
|
|
1888
1891
|
//#endregion
|
|
1889
|
-
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@eslint/compat/2.1.0/
|
|
1892
|
+
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@eslint/compat/2.1.0/90f1da88f603538864cab10657b202d55290237a3b641df9ac72b842a3be07f2/node_modules/@eslint/compat/dist/esm/index.js
|
|
1890
1893
|
/**
|
|
1891
1894
|
* @fileoverview Ignore file utilities for the compat package.
|
|
1892
1895
|
* @author Nicholas C. Zakas
|
|
@@ -1915,7 +1918,7 @@ function convertIgnorePatternToMinimatch(pattern) {
|
|
|
1915
1918
|
}
|
|
1916
1919
|
var init_esm$1 = __esmMin((() => {}));
|
|
1917
1920
|
//#endregion
|
|
1918
|
-
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-config-flat-gitignore/2.3.0/
|
|
1921
|
+
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-config-flat-gitignore/2.3.0/0a594adbd94ca75e2950b1104992894d49bc93f4f2bc935ad192d7104e4c271e/node_modules/eslint-config-flat-gitignore/dist/index.mjs
|
|
1919
1922
|
var dist_exports = /* @__PURE__ */ __exportAll$2({ default: () => ignore });
|
|
1920
1923
|
function toArray(array) {
|
|
1921
1924
|
array = array ?? [];
|
|
@@ -3947,6 +3950,92 @@ async function detectCatalogUsage() {
|
|
|
3947
3950
|
return yaml.includes("catalog:") || yaml.includes("catalogs:");
|
|
3948
3951
|
}
|
|
3949
3952
|
//#endregion
|
|
3953
|
+
//#region src/rules/project-structure.ts
|
|
3954
|
+
init_globs();
|
|
3955
|
+
/** Stands for the extension of the file a rule matched. */
|
|
3956
|
+
const EXTENSION_TOKEN = "{ext}";
|
|
3957
|
+
const DEFAULT_ENFORCE_EXISTENCE = [`{node-name}.spec.${EXTENSION_TOKEN}`];
|
|
3958
|
+
/** Reusable-rule id for "a folder of any name". */
|
|
3959
|
+
const ANY_FOLDER = "isentinelAnyFolder";
|
|
3960
|
+
/** The files the co-location check runs on, before `ignores`. */
|
|
3961
|
+
const PROJECT_STRUCTURE_FILES = [GLOB_SRC];
|
|
3962
|
+
/** The files the co-location check skips. */
|
|
3963
|
+
const PROJECT_STRUCTURE_IGNORES = [
|
|
3964
|
+
...GLOB_TESTS,
|
|
3965
|
+
GLOB_DTS,
|
|
3966
|
+
...GLOB_BUILD_CONFIGS
|
|
3967
|
+
];
|
|
3968
|
+
/**
|
|
3969
|
+
* Co-location rules shared between the ESLint and oxlint factories.
|
|
3970
|
+
*
|
|
3971
|
+
* `folder-structure` is not type-aware and runs unchanged as an oxlint
|
|
3972
|
+
* jsPlugin, so both engines have to hand it the same options or they disagree
|
|
3973
|
+
* about which files are co-located.
|
|
3974
|
+
*
|
|
3975
|
+
* @param options - The templates to enforce, and the roots they resolve
|
|
3976
|
+
* against.
|
|
3977
|
+
* @returns The rule map.
|
|
3978
|
+
*/
|
|
3979
|
+
function folderStructureRules({ enforceExistence = DEFAULT_ENFORCE_EXISTENCE, projectRoot = process.cwd(), structureRoot } = {}) {
|
|
3980
|
+
const children = buildChildren(typeof enforceExistence === "string" ? [enforceExistence] : enforceExistence);
|
|
3981
|
+
return { "project-structure/folder-structure": ["error", {
|
|
3982
|
+
longPathsInfo: false,
|
|
3983
|
+
projectRoot,
|
|
3984
|
+
rules: { [ANY_FOLDER]: {
|
|
3985
|
+
name: "*",
|
|
3986
|
+
children
|
|
3987
|
+
} },
|
|
3988
|
+
structure: children,
|
|
3989
|
+
...structureRoot === void 0 ? {} : { structureRoot }
|
|
3990
|
+
}] };
|
|
3991
|
+
}
|
|
3992
|
+
/**
|
|
3993
|
+
* The rules matching the files inside one folder, in the order
|
|
3994
|
+
* `folder-structure` tries them - first match wins.
|
|
3995
|
+
*
|
|
3996
|
+
* Every entry is permissive: `structure` is required by the rule and an
|
|
3997
|
+
* unmatched node is an error, so anything less would turn the co-location
|
|
3998
|
+
* check into whole-tree naming enforcement.
|
|
3999
|
+
*
|
|
4000
|
+
* @param templates - The `enforceExistence` templates, before substitution.
|
|
4001
|
+
* @returns The child rules, ending in the recursive folder rule.
|
|
4002
|
+
*/
|
|
4003
|
+
function buildChildren(templates) {
|
|
4004
|
+
const children = templates.map((template) => {
|
|
4005
|
+
return { name: (template.split("/").pop() ?? template).replaceAll(/\{[^{}]*\}/g, "*") };
|
|
4006
|
+
});
|
|
4007
|
+
for (const extension of GLOB_SRC_EXTENSIONS) children.push({
|
|
4008
|
+
name: `*.${extension}`,
|
|
4009
|
+
enforceExistence: templates.map((template) => {
|
|
4010
|
+
return template.replaceAll(EXTENSION_TOKEN, () => extension);
|
|
4011
|
+
})
|
|
4012
|
+
});
|
|
4013
|
+
children.push({ ruleId: ANY_FOLDER });
|
|
4014
|
+
return children;
|
|
4015
|
+
}
|
|
4016
|
+
//#endregion
|
|
4017
|
+
//#region src/eslint/configs/project-structure.ts
|
|
4018
|
+
async function projectStructure({ enforceExistence, files = PROJECT_STRUCTURE_FILES, ignores = PROJECT_STRUCTURE_IGNORES, overrides = {}, projectRoot, structureRoot } = {}) {
|
|
4019
|
+
await ensurePackages(["eslint-plugin-project-structure"]);
|
|
4020
|
+
const { projectStructurePlugin } = await import("eslint-plugin-project-structure");
|
|
4021
|
+
return [{
|
|
4022
|
+
name: "isentinel/project-structure/setup",
|
|
4023
|
+
plugins: { "project-structure": projectStructurePlugin }
|
|
4024
|
+
}, {
|
|
4025
|
+
name: "isentinel/project-structure/rules",
|
|
4026
|
+
files,
|
|
4027
|
+
ignores,
|
|
4028
|
+
rules: {
|
|
4029
|
+
...folderStructureRules({
|
|
4030
|
+
enforceExistence,
|
|
4031
|
+
projectRoot,
|
|
4032
|
+
structureRoot
|
|
4033
|
+
}),
|
|
4034
|
+
...overrides
|
|
4035
|
+
}
|
|
4036
|
+
}];
|
|
4037
|
+
}
|
|
4038
|
+
//#endregion
|
|
3950
4039
|
//#region src/rules/promise.ts
|
|
3951
4040
|
/**
|
|
3952
4041
|
* Promise rules shared between the ESLint and oxlint factories.
|
|
@@ -6154,7 +6243,7 @@ async function unicorn({ complementIgnores, nameReplacements, roblox = true, roo
|
|
|
6154
6243
|
];
|
|
6155
6244
|
}
|
|
6156
6245
|
//#endregion
|
|
6157
|
-
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-plugin-yml/3.5.0/
|
|
6246
|
+
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-plugin-yml/3.5.0/a00fc88a04d0f2081267792f61f05ad4ceff0f52e1ff41acbbc866518b4fd9c1/node_modules/eslint-plugin-yml/lib/chunk-CfYAbeIz.mjs
|
|
6158
6247
|
var __defProp$1, __exportAll$1;
|
|
6159
6248
|
var init_chunk_CfYAbeIz = __esmMin((() => {
|
|
6160
6249
|
__defProp$1 = Object.defineProperty;
|
|
@@ -9337,7 +9426,7 @@ var init_lib$1 = __esmMin((() => {
|
|
|
9337
9426
|
({ ...meta_exports$1 });
|
|
9338
9427
|
}));
|
|
9339
9428
|
//#endregion
|
|
9340
|
-
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-plugin-yml/3.5.0/
|
|
9429
|
+
//#region ../../../setup-pnpm/node_modules/.bin/store/v11/links/@/eslint-plugin-yml/3.5.0/a00fc88a04d0f2081267792f61f05ad4ceff0f52e1ff41acbbc866518b4fd9c1/node_modules/eslint-plugin-yml/lib/index.mjs
|
|
9341
9430
|
var lib_exports = /* @__PURE__ */ __exportAll$2({
|
|
9342
9431
|
configs: () => configs,
|
|
9343
9432
|
default: () => src_default,
|
|
@@ -15344,6 +15433,7 @@ const typeAwarePrefixes = /* @__PURE__ */ new Set([
|
|
|
15344
15433
|
"package-json",
|
|
15345
15434
|
"perfectionist",
|
|
15346
15435
|
"pnpm",
|
|
15436
|
+
"project-structure",
|
|
15347
15437
|
"promise",
|
|
15348
15438
|
"react",
|
|
15349
15439
|
"react-jsx",
|
|
@@ -15594,6 +15684,7 @@ var init_adapters = __esmMin((() => {
|
|
|
15594
15684
|
"oxfmt": "eslint-plugin-oxfmt",
|
|
15595
15685
|
"oxlint-comments": "oxlint-plugin-oxlint-comments",
|
|
15596
15686
|
"perfectionist": "eslint-plugin-perfectionist",
|
|
15687
|
+
"project-structure": "eslint-plugin-project-structure",
|
|
15597
15688
|
"promise-js": "eslint-plugin-promise",
|
|
15598
15689
|
"react-jsx": "eslint-plugin-react-jsx",
|
|
15599
15690
|
"react-naming-convention": "eslint-plugin-react-naming-convention",
|
|
@@ -16483,6 +16574,9 @@ var init_oxlint_capabilities = __esmMin((() => {
|
|
|
16483
16574
|
"prefer-spread",
|
|
16484
16575
|
"prefer-template",
|
|
16485
16576
|
"preserve-caught-error",
|
|
16577
|
+
"project-structure/file-composition",
|
|
16578
|
+
"project-structure/folder-structure",
|
|
16579
|
+
"project-structure/independent-modules",
|
|
16486
16580
|
"promise/always-return",
|
|
16487
16581
|
"promise/avoid-new",
|
|
16488
16582
|
"promise/catch-or-return",
|
|
@@ -19919,7 +20013,7 @@ const flatConfigProps = [
|
|
|
19919
20013
|
"settings"
|
|
19920
20014
|
];
|
|
19921
20015
|
async function isentinel(options, ...userConfigs) {
|
|
19922
|
-
const { autoRenamePlugins = true, componentExts: componentExtensions = [], e18e: enableE18e = true, eslintPlugin: enableEslintPlugin = false, formatters, gitignore: enableGitignore = true, jsdoc: enableJsdoc = true, jsonc: enableJsonc = true, jsx: enableJsx = true, markdown: enableMarkdown = true, oxlint: enableOxlint = false, pnpm: enableCatalogs = findUpSync("pnpm-workspace.yaml") !== void 0, react: enableReact = false, root: customRootGlobs, spellCheck: enableSpellCheck, toml: enableToml = true, yaml: enableYaml = true } = options;
|
|
20016
|
+
const { autoRenamePlugins = true, componentExts: componentExtensions = [], e18e: enableE18e = true, eslintPlugin: enableEslintPlugin = false, formatters, gitignore: enableGitignore = true, jsdoc: enableJsdoc = true, jsonc: enableJsonc = true, jsx: enableJsx = true, markdown: enableMarkdown = true, oxlint: enableOxlint = false, pnpm: enableCatalogs = findUpSync("pnpm-workspace.yaml") !== void 0, projectStructure: enableProjectStructure = false, react: enableReact = false, root: customRootGlobs, spellCheck: enableSpellCheck, toml: enableToml = true, yaml: enableYaml = true } = options;
|
|
19923
20017
|
const rootGlobs = mergeGlobs(GLOB_ROOT, customRootGlobs);
|
|
19924
20018
|
const enableRoblox = options.roblox !== false;
|
|
19925
20019
|
const reactOptions = resolveSubOptions(options, "react");
|
|
@@ -20042,6 +20136,7 @@ async function isentinel(options, ...userConfigs) {
|
|
|
20042
20136
|
...testOptions
|
|
20043
20137
|
}));
|
|
20044
20138
|
}
|
|
20139
|
+
if (enableProjectStructure !== false) configs.push(projectStructure(resolveSubOptions(options, "projectStructure")));
|
|
20045
20140
|
if (enableReact !== false) configs.push(react({
|
|
20046
20141
|
...resolveSubOptions(options, "react"),
|
|
20047
20142
|
...getOverrides(options, "react"),
|
|
@@ -20169,4 +20264,4 @@ async function isentinel(options, ...userConfigs) {
|
|
|
20169
20264
|
return composer;
|
|
20170
20265
|
}
|
|
20171
20266
|
//#endregion
|
|
20172
|
-
export { GLOB_ALL_JSON, GLOB_ALL_SRC, GLOB_BIN, GLOB_BUILD_CONFIGS, GLOB_CSS, GLOB_DTS, GLOB_EXCLUDE, GLOB_GRAPHQL, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LINTABLE_EXTENSIONS, GLOB_LUA, GLOB_MARKDOWN, GLOB_MARKDOWN_BLOCKS, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_MISE, GLOB_PACKAGE_JSON, GLOB_PACKAGE_JSON_ROOT, GLOB_POSTCSS, GLOB_ROOT, GLOB_ROOT_SRC, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_SRC_EXTENSIONS, GLOB_STYLE, GLOB_TESTS, GLOB_TOML, GLOB_TS, GLOB_TSCONFIG, GLOB_TSX, GLOB_TYPE_TESTS, GLOB_XML, GLOB_YAML, MARKDOWN_PROCESSOR_NAME, ROBLOX_ALLOWED_WORDS, StylisticConfigDefaults, comments, isentinel as default, isentinel, defaultPluginRenaming, disables, e18e, eslintPlugin, flawless, gitignore, ignores, imports, isAgentAutofixDisabled, isInAgentSession, isInEditorEnvironment, isInGitHooksOrLintStaged, javascript, jsdoc, jsonc, loadSmallRulesPlugin, markdown, naming, node, oxfmt, packageJson, perfectionist, pnpm, promise, react, roblox, smallRules, sonarjs, sortCspell, sortGithubAction, sortMiseToml, sortPnpmWorkspace, sortRojoProject, sortTsconfig, spelling, stylistic, test, toml, typescript, unicorn, yaml };
|
|
20267
|
+
export { GLOB_ALL_JSON, GLOB_ALL_SRC, GLOB_BIN, GLOB_BUILD_CONFIGS, GLOB_CSS, GLOB_DTS, GLOB_EXCLUDE, GLOB_GRAPHQL, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LINTABLE_EXTENSIONS, GLOB_LUA, GLOB_MARKDOWN, GLOB_MARKDOWN_BLOCKS, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_MISE, GLOB_PACKAGE_JSON, GLOB_PACKAGE_JSON_ROOT, GLOB_POSTCSS, GLOB_ROOT, GLOB_ROOT_SRC, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_SRC_EXTENSIONS, GLOB_STYLE, GLOB_TESTS, GLOB_TOML, GLOB_TS, GLOB_TSCONFIG, GLOB_TSX, GLOB_TYPE_TESTS, GLOB_XML, GLOB_YAML, MARKDOWN_PROCESSOR_NAME, ROBLOX_ALLOWED_WORDS, StylisticConfigDefaults, comments, isentinel as default, isentinel, defaultPluginRenaming, disables, e18e, eslintPlugin, flawless, gitignore, ignores, imports, isAgentAutofixDisabled, isInAgentSession, isInEditorEnvironment, isInGitHooksOrLintStaged, javascript, jsdoc, jsonc, loadSmallRulesPlugin, markdown, naming, node, oxfmt, packageJson, perfectionist, pnpm, projectStructure, promise, react, roblox, smallRules, sonarjs, sortCspell, sortGithubAction, sortMiseToml, sortPnpmWorkspace, sortRojoProject, sortTsconfig, spelling, stylistic, test, toml, typescript, unicorn, yaml };
|
package/dist/lint-cli.mjs
CHANGED
|
@@ -38,7 +38,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
38
38
|
}) : target, mod));
|
|
39
39
|
//#endregion
|
|
40
40
|
//#region package.json
|
|
41
|
-
var version = "6.0.0-beta.
|
|
41
|
+
var version = "6.0.0-beta.44";
|
|
42
42
|
//#endregion
|
|
43
43
|
//#region src/lint-cli/lib/cli/types.ts
|
|
44
44
|
/**
|
|
@@ -1104,17 +1104,22 @@ const PACKAGE_RESOLUTION = {
|
|
|
1104
1104
|
*/
|
|
1105
1105
|
function applyHashBust(run, bust, hash) {
|
|
1106
1106
|
if (hash === void 0) return {
|
|
1107
|
-
|
|
1107
|
+
cleared: [],
|
|
1108
1108
|
firstRun: false
|
|
1109
1109
|
};
|
|
1110
1110
|
const swap = swapState(statePath(run.cwd, bust.name, run.key), hash);
|
|
1111
1111
|
if (swap !== "changed") return {
|
|
1112
|
-
|
|
1112
|
+
cleared: [],
|
|
1113
1113
|
firstRun: swap === "first"
|
|
1114
1114
|
};
|
|
1115
|
-
|
|
1115
|
+
const cleared = [];
|
|
1116
|
+
for (const base of bust.caches) {
|
|
1117
|
+
const cacheFilePath = path.resolve(run.cwd, cacheFileFor(base, run.key));
|
|
1118
|
+
fs.rmSync(cacheFilePath, { force: true });
|
|
1119
|
+
cleared.push(cacheFilePath);
|
|
1120
|
+
}
|
|
1116
1121
|
return {
|
|
1117
|
-
|
|
1122
|
+
cleared,
|
|
1118
1123
|
firstRun: false
|
|
1119
1124
|
};
|
|
1120
1125
|
}
|
|
@@ -4854,6 +4859,17 @@ function buildGateValue(resolution, options) {
|
|
|
4854
4859
|
//#region src/lint-cli/lib/typescript/affected.ts
|
|
4855
4860
|
const warned = /* @__PURE__ */ new Set();
|
|
4856
4861
|
/**
|
|
4862
|
+
* State-file base name for the builder's incremental state, shared by the
|
|
4863
|
+
* per-project path and the prefix {@link hasBuilderState} scans for.
|
|
4864
|
+
*/
|
|
4865
|
+
const BUILD_INFO_STATE = "tsbuildinfo";
|
|
4866
|
+
/**
|
|
4867
|
+
* State-file base name for the gate paired with each buildinfo. Deliberately
|
|
4868
|
+
* not a `tsbuildinfo-` suffix, so anything enumerating a variant's buildinfo
|
|
4869
|
+
* files does not pick a gate up as one (see {@link gateStatePath}).
|
|
4870
|
+
*/
|
|
4871
|
+
const GATE_STATE = "tsgate";
|
|
4872
|
+
/**
|
|
4857
4873
|
* Compute the set of files whose type-aware lint results may have changed since
|
|
4858
4874
|
* the previous run, using TypeScript's builder API. The builder does a native
|
|
4859
4875
|
* shape-hash BFS: it recomputes each dependent's emitted-`.d.ts` shape hash and
|
|
@@ -4911,6 +4927,44 @@ function computeAffectedFiles({ key, cwd }, mode) {
|
|
|
4911
4927
|
}
|
|
4912
4928
|
}
|
|
4913
4929
|
/**
|
|
4930
|
+
* Whether a previous run left builder state for this mode and config variant.
|
|
4931
|
+
*
|
|
4932
|
+
* The one thing a builder pass does that nothing else can is establish this
|
|
4933
|
+
* state. Its affected set is disposable — a caller with an empty cache re-lints
|
|
4934
|
+
* regardless — but a run that skips the builder while no state exists only
|
|
4935
|
+
* defers the cost onto the next run, which then reports `firstRun` and throws
|
|
4936
|
+
* its affected set away. A file edited in between would be re-linted on its own
|
|
4937
|
+
* mtime while its importers kept stale type-aware cache entries, which no later
|
|
4938
|
+
* run revisits. Callers that skip the builder as an optimisation ask this
|
|
4939
|
+
* first.
|
|
4940
|
+
*
|
|
4941
|
+
* Any one project's state is enough: {@link computeAffectedFiles} reports
|
|
4942
|
+
* `firstRun` only when no project at all had state, and a newly added project
|
|
4943
|
+
* contributes its whole file set rather than an empty one.
|
|
4944
|
+
*
|
|
4945
|
+
* @param run - The run context.
|
|
4946
|
+
* @param mode - The active ESLint type-aware mode (never `"off"` here).
|
|
4947
|
+
* @returns True when at least one buildinfo for this mode and variant exists.
|
|
4948
|
+
*/
|
|
4949
|
+
function hasBuilderState({ key, cwd }, mode) {
|
|
4950
|
+
const prefix = `${path.basename(statePath(cwd, BUILD_INFO_STATE, modeSuffix(mode), key))}-`;
|
|
4951
|
+
try {
|
|
4952
|
+
return fs.readdirSync(stateDirectory(cwd)).some((name) => name.startsWith(prefix) && !name.endsWith(".tmp"));
|
|
4953
|
+
} catch {
|
|
4954
|
+
return false;
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
/**
|
|
4958
|
+
* The state-file segment discriminating the type-aware mode a builder ran
|
|
4959
|
+
* under. Two modes resolve different programs, so their state cannot be shared.
|
|
4960
|
+
*
|
|
4961
|
+
* @param mode - The active ESLint type-aware mode (never `"off"` here).
|
|
4962
|
+
* @returns The file-name segment for that mode.
|
|
4963
|
+
*/
|
|
4964
|
+
function modeSuffix(mode) {
|
|
4965
|
+
return mode === "only" ? "typeaware" : "full";
|
|
4966
|
+
}
|
|
4967
|
+
/**
|
|
4914
4968
|
* Resolve the builder incremental-state (`.tsbuildinfo`) file for a mode and
|
|
4915
4969
|
* config variant.
|
|
4916
4970
|
*
|
|
@@ -4934,7 +4988,7 @@ function computeAffectedFiles({ key, cwd }, mode) {
|
|
|
4934
4988
|
* @returns The absolute path to the mode's buildinfo file.
|
|
4935
4989
|
*/
|
|
4936
4990
|
function builderStatePath(cwd, mode, key, projectId) {
|
|
4937
|
-
return statePath(cwd,
|
|
4991
|
+
return statePath(cwd, BUILD_INFO_STATE, modeSuffix(mode), key, projectId);
|
|
4938
4992
|
}
|
|
4939
4993
|
/**
|
|
4940
4994
|
* Resolve the gate state paired with one {@link builderStatePath} buildinfo:
|
|
@@ -4959,7 +5013,7 @@ function builderStatePath(cwd, mode, key, projectId) {
|
|
|
4959
5013
|
* @returns The absolute path to the gate state file.
|
|
4960
5014
|
*/
|
|
4961
5015
|
function gateStatePath(cwd, mode, key, projectId) {
|
|
4962
|
-
return statePath(cwd,
|
|
5016
|
+
return statePath(cwd, GATE_STATE, modeSuffix(mode), key, projectId);
|
|
4963
5017
|
}
|
|
4964
5018
|
/**
|
|
4965
5019
|
* Emit a warning at most once per distinct message. Keyed by message rather
|
|
@@ -5291,15 +5345,17 @@ function sizePasses(descriptors, run, inputs) {
|
|
|
5291
5345
|
* @returns The number of dirty files.
|
|
5292
5346
|
*/
|
|
5293
5347
|
function mutatingDirtyCount(descriptor, cacheLocation, targetFiles, { clearedCaches, run }) {
|
|
5294
|
-
|
|
5348
|
+
const buildsProgram = descriptor.invalidation !== "none";
|
|
5349
|
+
const mode = descriptor.invalidation === "only" ? "only" : void 0;
|
|
5350
|
+
if (clearedCaches.has(cacheLocation) && (!buildsProgram || hasBuilderState(run, mode))) return targetFiles.length;
|
|
5295
5351
|
const cache = openCache(cacheLocation, run.ci);
|
|
5296
5352
|
const dirty = new Set((cache?.getUpdatedFiles(targetFiles) ?? targetFiles).map((file) => normalizePath(file)));
|
|
5297
|
-
if (
|
|
5353
|
+
if (buildsProgram) {
|
|
5298
5354
|
const outcome = applyTypeAwareInvalidation(run, {
|
|
5299
5355
|
alreadyDirty: dirty,
|
|
5300
5356
|
cache,
|
|
5301
5357
|
cacheLocation,
|
|
5302
|
-
mode
|
|
5358
|
+
mode,
|
|
5303
5359
|
targetFiles
|
|
5304
5360
|
});
|
|
5305
5361
|
if (outcome.busted) return targetFiles.length;
|
|
@@ -5423,9 +5479,13 @@ function plan(options, run) {
|
|
|
5423
5479
|
const canMutateCaches = mutate && options.cache;
|
|
5424
5480
|
const hasTypeAwarePass = descriptors.some((descriptor) => descriptor.invalidation !== "none");
|
|
5425
5481
|
const configHash = options.cache ? computeConfigHash(cwd, files.configFiles) : void 0;
|
|
5426
|
-
|
|
5427
|
-
if (canMutateCaches
|
|
5428
|
-
|
|
5482
|
+
const cleared = [];
|
|
5483
|
+
if (canMutateCaches) {
|
|
5484
|
+
cleared.push(...applyHashBust(run, CONFIG_DRIFT, configHash).cleared);
|
|
5485
|
+
if (hasTypeAwarePass) cleared.push(...applyHashBust(run, PACKAGE_RESOLUTION, computePackageJsonHash(cwd)).cleared);
|
|
5486
|
+
cleared.push(...sweepStaleCaches(cwd, newestBustMtime));
|
|
5487
|
+
}
|
|
5488
|
+
const clearedCaches = new Set(cleared);
|
|
5429
5489
|
const sizingFiles = withoutIgnored(files, resolveIgnoredFiles(run, configHash, files.lintable));
|
|
5430
5490
|
const inputs = {
|
|
5431
5491
|
clearedCaches,
|
package/dist/oxlint.d.mts
CHANGED
|
@@ -3860,6 +3860,7 @@ interface OverrideSiteChains {
|
|
|
3860
3860
|
e18e: MainChain;
|
|
3861
3861
|
eslintPlugin: FeatureChain;
|
|
3862
3862
|
javascript: MainChain;
|
|
3863
|
+
projectStructure: FeatureChain;
|
|
3863
3864
|
react: ReactChain;
|
|
3864
3865
|
roblox: MainChain;
|
|
3865
3866
|
typescript: MainChain;
|
|
@@ -7787,6 +7788,21 @@ interface RuleOptions {
|
|
|
7787
7788
|
* @see https://eslint.org/docs/latest/rules/preserve-caught-error
|
|
7788
7789
|
*/
|
|
7789
7790
|
'preserve-caught-error'?: Linter.RuleEntry<PreserveCaughtError>;
|
|
7791
|
+
/**
|
|
7792
|
+
* Enforce advanced naming rules and prohibit the use of given selectors in a given file. Have full control over what your file can contain and the naming conventions it must follow.
|
|
7793
|
+
* @see https://github.com/Igorkowalski94/eslint-plugin-project-structure/wiki/project%E2%80%91structure-%E2%80%8Bfile%E2%80%91composition#root
|
|
7794
|
+
*/
|
|
7795
|
+
'project-structure/file-composition'?: Linter.RuleEntry<ProjectStructureFileComposition>;
|
|
7796
|
+
/**
|
|
7797
|
+
* Enforce rules on folder structure to keep your repository consistent, orderly and well thought out.
|
|
7798
|
+
* @see https://github.com/Igorkowalski94/eslint-plugin-project-structure/wiki/project%E2%80%91structure-%E2%80%8Bfolder%E2%80%91structure#root
|
|
7799
|
+
*/
|
|
7800
|
+
'project-structure/folder-structure'?: Linter.RuleEntry<ProjectStructureFolderStructure>;
|
|
7801
|
+
/**
|
|
7802
|
+
* A key principle of a healthy project is to prevent the creation of a massive dependency tree, where removing or editing one feature triggers a chain reaction that impacts the entire project. Create independent modules to keep your project scalable and easy to maintain. Get rid of dependencies between modules and create truly independent functionalities.
|
|
7803
|
+
* @see https://github.com/Igorkowalski94/eslint-plugin-project-structure/wiki/project%E2%80%91structure-%E2%80%8Bindependent%E2%80%91modules#root
|
|
7804
|
+
*/
|
|
7805
|
+
'project-structure/independent-modules'?: Linter.RuleEntry<ProjectStructureIndependentModules>;
|
|
7790
7806
|
/**
|
|
7791
7807
|
* Require returning inside each `then()` to create readable and reusable Promise chains.
|
|
7792
7808
|
* @see https://github.com/eslint-community/eslint-plugin-promise/blob/main/docs/rules/always-return.md
|
|
@@ -20595,6 +20611,18 @@ type PreserveCaughtError = [] | [{
|
|
|
20595
20611
|
argumentPosition: number;
|
|
20596
20612
|
})[];
|
|
20597
20613
|
}];
|
|
20614
|
+
// ----- project-structure/file-composition -----
|
|
20615
|
+
type ProjectStructureFileComposition = [] | [{
|
|
20616
|
+
[k: string]: unknown | undefined;
|
|
20617
|
+
}];
|
|
20618
|
+
// ----- project-structure/folder-structure -----
|
|
20619
|
+
type ProjectStructureFolderStructure = [] | [{
|
|
20620
|
+
[k: string]: unknown | undefined;
|
|
20621
|
+
}];
|
|
20622
|
+
// ----- project-structure/independent-modules -----
|
|
20623
|
+
type ProjectStructureIndependentModules = [] | [{
|
|
20624
|
+
[k: string]: unknown | undefined;
|
|
20625
|
+
}];
|
|
20598
20626
|
// ----- promise/always-return -----
|
|
20599
20627
|
type PromiseAlwaysReturn = [] | [{
|
|
20600
20628
|
ignoreLastCallback?: boolean;
|
|
@@ -24764,6 +24792,37 @@ interface NamingConfig extends OptionsOverridesTypeAware {
|
|
|
24764
24792
|
*/
|
|
24765
24793
|
selectorsTsx?: Array<NamingSelector>;
|
|
24766
24794
|
}
|
|
24795
|
+
interface ProjectStructureConfig extends OptionsOverrides {
|
|
24796
|
+
/**
|
|
24797
|
+
* The files each linted source file has to sit next to, as
|
|
24798
|
+
* `enforceExistence` templates. Takes the plugin's name placeholders
|
|
24799
|
+
* (`{nodeName}`, `{NodeName}`, `{node-name}`, `{node_name}`,
|
|
24800
|
+
* `{NODE_NAME}`) plus `{ext}` for the extension of the file that matched.
|
|
24801
|
+
* A file already matching a template is exempt from it.
|
|
24802
|
+
*
|
|
24803
|
+
* @default ["{node-name}.spec.{ext}"]
|
|
24804
|
+
*/
|
|
24805
|
+
enforceExistence?: Array<string> | string;
|
|
24806
|
+
/**
|
|
24807
|
+
* Files exempt from the check.
|
|
24808
|
+
*
|
|
24809
|
+
* @default [...GLOB_TESTS, GLOB_DTS, ...GLOB_BUILD_CONFIGS]
|
|
24810
|
+
*/
|
|
24811
|
+
ignores?: Array<string>;
|
|
24812
|
+
/**
|
|
24813
|
+
* The folder the enforced paths resolve against, and where the plugin
|
|
24814
|
+
* writes `projectStructure.cache.json` when it reports.
|
|
24815
|
+
*
|
|
24816
|
+
* @default process.cwd()
|
|
24817
|
+
*/
|
|
24818
|
+
projectRoot?: string;
|
|
24819
|
+
/**
|
|
24820
|
+
* Restricts the check to one subtree of `projectRoot`, as a relative path.
|
|
24821
|
+
*
|
|
24822
|
+
* @default undefined - the whole of `projectRoot`
|
|
24823
|
+
*/
|
|
24824
|
+
structureRoot?: string;
|
|
24825
|
+
}
|
|
24767
24826
|
interface OptionsTypeScriptParserOptions {
|
|
24768
24827
|
/**
|
|
24769
24828
|
* Glob patterns for files that should be type aware.
|
|
@@ -25112,6 +25171,14 @@ interface OptionsConfig extends OptionsComponentExtensions, OptionsProjectType {
|
|
|
25112
25171
|
* @see https://github.com/antfu/pnpm-workspace-utils
|
|
25113
25172
|
*/
|
|
25114
25173
|
pnpm?: boolean | OptionsPnpm;
|
|
25174
|
+
/**
|
|
25175
|
+
* Require every source file to have a co-located test file, via
|
|
25176
|
+
* [eslint-plugin-project-structure](https://github.com/Igorkowalski94/eslint-plugin-project-structure).
|
|
25177
|
+
*
|
|
25178
|
+
* @default false
|
|
25179
|
+
* @requires eslint-plugin-project-structure
|
|
25180
|
+
*/
|
|
25181
|
+
projectStructure?: boolean | ProjectStructureConfig;
|
|
25115
25182
|
/**
|
|
25116
25183
|
* Enable react rules.
|
|
25117
25184
|
*
|
|
@@ -33193,7 +33260,7 @@ type OxlintCommentsRequireDescription = [] | [{
|
|
|
33193
33260
|
ignore?: ("oxlint-disable" | "oxlint-disable-line" | "oxlint-disable-next-line" | "oxlint-enable")[];
|
|
33194
33261
|
}];
|
|
33195
33262
|
/** JsPlugin prefixes whose rules keep their ESLint-side names in oxlint configs. */
|
|
33196
|
-
type OxlintKeptJsPluginPrefix = '@cspell' | 'antfu' | 'better-max-params' | 'comment-length' | 'de-morgan' | 'e18e' | 'erasable-syntax-only' | 'eslint-plugin' | 'flawless' | 'jest-extended' | 'oxfmt' | 'perfectionist' | 'react-jsx' | 'react-naming-convention' | 'roblox' | 'sentinel' | 'small-rules' | 'sonar' | 'style' | 'testing-library' | 'ts' | 'unused-imports';
|
|
33263
|
+
type OxlintKeptJsPluginPrefix = '@cspell' | 'antfu' | 'better-max-params' | 'comment-length' | 'de-morgan' | 'e18e' | 'erasable-syntax-only' | 'eslint-plugin' | 'flawless' | 'jest-extended' | 'oxfmt' | 'perfectionist' | 'project-structure' | 'react-jsx' | 'react-naming-convention' | 'roblox' | 'sentinel' | 'small-rules' | 'sonar' | 'style' | 'testing-library' | 'ts' | 'unused-imports';
|
|
33197
33264
|
/** JsPlugin rules whose oxlint names match the ESLint-side names. */
|
|
33198
33265
|
type OxlintKeptJsPluginRuleOptions = { [K in keyof RuleOptions as K extends `${OxlintKeptJsPluginPrefix}/${string}` ? K : never]: RuleOptions[K]; };
|
|
33199
33266
|
/** All rules known to the oxlint factory, keyed by canonical oxlint rule name. */
|
package/dist/oxlint.mjs
CHANGED
|
@@ -616,6 +616,9 @@ const jsPluginRuleNames = /* @__PURE__ */ new Set([
|
|
|
616
616
|
"prefer-spread",
|
|
617
617
|
"prefer-template",
|
|
618
618
|
"preserve-caught-error",
|
|
619
|
+
"project-structure/file-composition",
|
|
620
|
+
"project-structure/folder-structure",
|
|
621
|
+
"project-structure/independent-modules",
|
|
619
622
|
"promise/always-return",
|
|
620
623
|
"promise/avoid-new",
|
|
621
624
|
"promise/catch-or-return",
|
|
@@ -3237,6 +3240,7 @@ const enabledPresetRuleNames = /* @__PURE__ */ new Set([
|
|
|
3237
3240
|
"prefer-rest-params",
|
|
3238
3241
|
"prefer-spread",
|
|
3239
3242
|
"prefer-template",
|
|
3243
|
+
"project-structure/folder-structure",
|
|
3240
3244
|
"promise/always-return",
|
|
3241
3245
|
"promise/catch-or-return",
|
|
3242
3246
|
"promise/no-multiple-resolved",
|
|
@@ -4746,6 +4750,7 @@ const oxlintJsPlugins = {
|
|
|
4746
4750
|
"oxfmt": "eslint-plugin-oxfmt",
|
|
4747
4751
|
"oxlint-comments": "oxlint-plugin-oxlint-comments",
|
|
4748
4752
|
"perfectionist": "eslint-plugin-perfectionist",
|
|
4753
|
+
"project-structure": "eslint-plugin-project-structure",
|
|
4749
4754
|
"promise-js": "eslint-plugin-promise",
|
|
4750
4755
|
"react-jsx": "eslint-plugin-react-jsx",
|
|
4751
4756
|
"react-naming-convention": "eslint-plugin-react-naming-convention",
|
|
@@ -5164,6 +5169,23 @@ const GLOB_ROOT = [
|
|
|
5164
5169
|
];
|
|
5165
5170
|
const GLOB_SRC_EXT = "{,c,m}[jt]s{,x}";
|
|
5166
5171
|
const GLOB_SRC = "**/*.{,c,m}[jt]s{,x}";
|
|
5172
|
+
/**
|
|
5173
|
+
* The real TS/JS-family file extensions {@link GLOB_SRC_EXT} encodes (the glob
|
|
5174
|
+
* additionally matches non-existent combinations such as `cjsx`). Kept next to
|
|
5175
|
+
* the glob so the two are maintained together; consumed by the lint CLI to size
|
|
5176
|
+
* its type-aware pass.
|
|
5177
|
+
*/
|
|
5178
|
+
const GLOB_SRC_EXTENSIONS = [
|
|
5179
|
+
"ts",
|
|
5180
|
+
"tsx",
|
|
5181
|
+
"mts",
|
|
5182
|
+
"cts",
|
|
5183
|
+
"js",
|
|
5184
|
+
"jsx",
|
|
5185
|
+
"mjs",
|
|
5186
|
+
"cjs"
|
|
5187
|
+
];
|
|
5188
|
+
[...GLOB_SRC_EXTENSIONS];
|
|
5167
5189
|
const GLOB_JSX = "**/*.{,c,m}jsx";
|
|
5168
5190
|
const GLOB_TSX = "**/*.{,c,m}tsx";
|
|
5169
5191
|
const GLOB_DTS = "**/*.d.{,c,m}ts";
|
|
@@ -6742,8 +6764,10 @@ function oxlintDisables({ configDirectory, root, workspaceRoot }) {
|
|
|
6742
6764
|
rules: {
|
|
6743
6765
|
"antfu/no-top-level-await": "off",
|
|
6744
6766
|
"no-console": "off",
|
|
6767
|
+
"roblox/no-null": "off",
|
|
6745
6768
|
"small-rules/require-async-suffix": "off",
|
|
6746
|
-
"ts/explicit-function-return-type": "off"
|
|
6769
|
+
"ts/explicit-function-return-type": "off",
|
|
6770
|
+
"unicorn/no-null": "off"
|
|
6747
6771
|
}
|
|
6748
6772
|
}),
|
|
6749
6773
|
...createOxlintConfigs({
|
|
@@ -6811,7 +6835,8 @@ function oxlintDisables({ configDirectory, root, workspaceRoot }) {
|
|
|
6811
6835
|
"import/no-default-export": "off",
|
|
6812
6836
|
"small-rules/require-async-suffix": "off",
|
|
6813
6837
|
"sonar/file-name-differ-from-class": "off",
|
|
6814
|
-
"unicorn/filename-case": "off"
|
|
6838
|
+
"unicorn/filename-case": "off",
|
|
6839
|
+
"unicorn/no-null": "off"
|
|
6815
6840
|
}
|
|
6816
6841
|
}),
|
|
6817
6842
|
...createOxlintConfigs({
|
|
@@ -8009,6 +8034,94 @@ function oxlintPerfectionist(config) {
|
|
|
8009
8034
|
})];
|
|
8010
8035
|
}
|
|
8011
8036
|
//#endregion
|
|
8037
|
+
//#region src/rules/project-structure.ts
|
|
8038
|
+
/** Stands for the extension of the file a rule matched. */
|
|
8039
|
+
const EXTENSION_TOKEN = "{ext}";
|
|
8040
|
+
const DEFAULT_ENFORCE_EXISTENCE = [`{node-name}.spec.${EXTENSION_TOKEN}`];
|
|
8041
|
+
/** Reusable-rule id for "a folder of any name". */
|
|
8042
|
+
const ANY_FOLDER = "isentinelAnyFolder";
|
|
8043
|
+
/** The files the co-location check runs on, before `ignores`. */
|
|
8044
|
+
const PROJECT_STRUCTURE_FILES = [GLOB_SRC];
|
|
8045
|
+
/** The files the co-location check skips. */
|
|
8046
|
+
const PROJECT_STRUCTURE_IGNORES = [
|
|
8047
|
+
...GLOB_TESTS,
|
|
8048
|
+
GLOB_DTS,
|
|
8049
|
+
...GLOB_BUILD_CONFIGS
|
|
8050
|
+
];
|
|
8051
|
+
/**
|
|
8052
|
+
* Co-location rules shared between the ESLint and oxlint factories.
|
|
8053
|
+
*
|
|
8054
|
+
* `folder-structure` is not type-aware and runs unchanged as an oxlint
|
|
8055
|
+
* jsPlugin, so both engines have to hand it the same options or they disagree
|
|
8056
|
+
* about which files are co-located.
|
|
8057
|
+
*
|
|
8058
|
+
* @param options - The templates to enforce, and the roots they resolve
|
|
8059
|
+
* against.
|
|
8060
|
+
* @returns The rule map.
|
|
8061
|
+
*/
|
|
8062
|
+
function folderStructureRules({ enforceExistence = DEFAULT_ENFORCE_EXISTENCE, projectRoot = process.cwd(), structureRoot } = {}) {
|
|
8063
|
+
const children = buildChildren(typeof enforceExistence === "string" ? [enforceExistence] : enforceExistence);
|
|
8064
|
+
return { "project-structure/folder-structure": ["error", {
|
|
8065
|
+
longPathsInfo: false,
|
|
8066
|
+
projectRoot,
|
|
8067
|
+
rules: { [ANY_FOLDER]: {
|
|
8068
|
+
name: "*",
|
|
8069
|
+
children
|
|
8070
|
+
} },
|
|
8071
|
+
structure: children,
|
|
8072
|
+
...structureRoot === void 0 ? {} : { structureRoot }
|
|
8073
|
+
}] };
|
|
8074
|
+
}
|
|
8075
|
+
/**
|
|
8076
|
+
* The rules matching the files inside one folder, in the order
|
|
8077
|
+
* `folder-structure` tries them - first match wins.
|
|
8078
|
+
*
|
|
8079
|
+
* Every entry is permissive: `structure` is required by the rule and an
|
|
8080
|
+
* unmatched node is an error, so anything less would turn the co-location
|
|
8081
|
+
* check into whole-tree naming enforcement.
|
|
8082
|
+
*
|
|
8083
|
+
* @param templates - The `enforceExistence` templates, before substitution.
|
|
8084
|
+
* @returns The child rules, ending in the recursive folder rule.
|
|
8085
|
+
*/
|
|
8086
|
+
function buildChildren(templates) {
|
|
8087
|
+
const children = templates.map((template) => {
|
|
8088
|
+
return { name: (template.split("/").pop() ?? template).replaceAll(/\{[^{}]*\}/g, "*") };
|
|
8089
|
+
});
|
|
8090
|
+
for (const extension of GLOB_SRC_EXTENSIONS) children.push({
|
|
8091
|
+
name: `*.${extension}`,
|
|
8092
|
+
enforceExistence: templates.map((template) => {
|
|
8093
|
+
return template.replaceAll(EXTENSION_TOKEN, () => extension);
|
|
8094
|
+
})
|
|
8095
|
+
});
|
|
8096
|
+
children.push({ ruleId: ANY_FOLDER });
|
|
8097
|
+
return children;
|
|
8098
|
+
}
|
|
8099
|
+
//#endregion
|
|
8100
|
+
//#region src/oxlint/configs/project-structure.ts
|
|
8101
|
+
/**
|
|
8102
|
+
* Co-location rules for standalone oxlint.
|
|
8103
|
+
*
|
|
8104
|
+
* `folder-structure` runs as a jsPlugin: it visits `Program` once per file and
|
|
8105
|
+
* answers from the filesystem, so oxlint needs no type information to run it.
|
|
8106
|
+
*
|
|
8107
|
+
* @param options - The templates to enforce, and the roots they resolve
|
|
8108
|
+
* against.
|
|
8109
|
+
* @returns The generated config fragments.
|
|
8110
|
+
*/
|
|
8111
|
+
function oxlintProjectStructure({ enforceExistence, files = PROJECT_STRUCTURE_FILES, ignores = PROJECT_STRUCTURE_IGNORES, overrides = {}, projectRoot, structureRoot } = {}) {
|
|
8112
|
+
return createOxlintConfigs({
|
|
8113
|
+
name: "isentinel/project-structure",
|
|
8114
|
+
excludeFiles: ignores,
|
|
8115
|
+
files: files.flat(),
|
|
8116
|
+
overrides,
|
|
8117
|
+
rules: folderStructureRules({
|
|
8118
|
+
enforceExistence,
|
|
8119
|
+
projectRoot,
|
|
8120
|
+
structureRoot
|
|
8121
|
+
})
|
|
8122
|
+
});
|
|
8123
|
+
}
|
|
8124
|
+
//#endregion
|
|
8012
8125
|
//#region src/rules/promise.ts
|
|
8013
8126
|
/**
|
|
8014
8127
|
* Promise rules shared between the ESLint and oxlint factories.
|
|
@@ -9226,7 +9339,7 @@ const NATIVE_ONLY_JS_PLUGINS = /* @__PURE__ */ new Set(["oxlint-comments"]);
|
|
|
9226
9339
|
*/
|
|
9227
9340
|
function isentinel(factoryOptions, ...userConfigs) {
|
|
9228
9341
|
const options = factoryOptions ?? { name: "isentinel" };
|
|
9229
|
-
const { categories, componentExts: componentExtensions = [], e18e: enableE18e = true, env, eslintPlugin: enableEslintPlugin = false, formatters, gitignore: enableGitignore = true, globals, ignores, jsdoc: enableJsdoc = true, jsPlugins: userJsPlugins, jsx: enableJsx = true, options: linterOptions, oxc: enableOxc = true, react: enableReact = false, root: customRootGlobs, rules = {}, spellCheck: enableSpellCheck, test: enableTest = false, warnDroppedOverrides: enableDroppedOverrideWarning = true } = options;
|
|
9342
|
+
const { categories, componentExts: componentExtensions = [], e18e: enableE18e = true, env, eslintPlugin: enableEslintPlugin = false, formatters, gitignore: enableGitignore = true, globals, ignores, jsdoc: enableJsdoc = true, jsPlugins: userJsPlugins, jsx: enableJsx = true, options: linterOptions, oxc: enableOxc = true, projectStructure: enableProjectStructure = false, react: enableReact = false, root: customRootGlobs, rules = {}, spellCheck: enableSpellCheck, test: enableTest = false, warnDroppedOverrides: enableDroppedOverrideWarning = true } = options;
|
|
9230
9343
|
const rootGlobs = mergeGlobs(GLOB_ROOT, customRootGlobs);
|
|
9231
9344
|
const enableRoblox = options.roblox !== false;
|
|
9232
9345
|
const reactOptions = resolveSubOptions(options, "react");
|
|
@@ -9355,6 +9468,7 @@ function isentinel(factoryOptions, ...userConfigs) {
|
|
|
9355
9468
|
isInEditor
|
|
9356
9469
|
}));
|
|
9357
9470
|
if (enableEslintPlugin !== false) configs.push(oxlintEslintPlugin({ ...getOverrides(options, "eslintPlugin") }));
|
|
9471
|
+
if (enableProjectStructure !== false) configs.push(oxlintProjectStructure(resolveSubOptions(options, "projectStructure")));
|
|
9358
9472
|
if (enableOxc) {
|
|
9359
9473
|
configs.push(oxlintOxc({ roblox: enableRoblox }));
|
|
9360
9474
|
if (needsComplementOverlay) configs.push(oxlintOxc({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isentinel/eslint-config",
|
|
3
|
-
"version": "6.0.0-beta.
|
|
3
|
+
"version": "6.0.0-beta.44",
|
|
4
4
|
"description": "iSentinel's ESLint config",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint-config",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"yargs": "18.0.0"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
|
-
"@antfu/ni": "30.
|
|
112
|
+
"@antfu/ni": "30.5.0",
|
|
113
113
|
"@eslint-react/shared": "5.17.3",
|
|
114
114
|
"@eslint/config-inspector": "3.1.0",
|
|
115
115
|
"@isentinel/tsconfig": "1.2.0",
|
|
@@ -122,12 +122,13 @@
|
|
|
122
122
|
"@types/yargs": "17.0.35",
|
|
123
123
|
"@vitest/eslint-plugin": "1.6.26",
|
|
124
124
|
"bumpp": "12.0.0",
|
|
125
|
-
"eslint": "10.
|
|
125
|
+
"eslint": "10.8.1",
|
|
126
126
|
"eslint-plugin-erasable-syntax-only": "0.4.2",
|
|
127
127
|
"eslint-plugin-eslint-plugin": "7.5.0",
|
|
128
128
|
"eslint-plugin-jest": "29.16.0",
|
|
129
129
|
"eslint-plugin-jest-extended": "3.0.1",
|
|
130
130
|
"eslint-plugin-n": "18.2.2",
|
|
131
|
+
"eslint-plugin-project-structure": "3.14.3",
|
|
131
132
|
"eslint-plugin-react-jsx": "5.17.3",
|
|
132
133
|
"eslint-plugin-react-naming-convention": "5.17.3",
|
|
133
134
|
"eslint-plugin-react-x": "5.17.3",
|
|
@@ -144,7 +145,7 @@
|
|
|
144
145
|
"typescript": "6.0.3",
|
|
145
146
|
"unplugin-unused": "0.5.7",
|
|
146
147
|
"vitest": "4.1.10",
|
|
147
|
-
"@isentinel/eslint-config": "6.0.0-beta.
|
|
148
|
+
"@isentinel/eslint-config": "6.0.0-beta.44"
|
|
148
149
|
},
|
|
149
150
|
"peerDependencies": {
|
|
150
151
|
"@vitest/eslint-plugin": "^1.6.4",
|
|
@@ -153,6 +154,7 @@
|
|
|
153
154
|
"eslint-plugin-jest": "^29.15.0",
|
|
154
155
|
"eslint-plugin-jest-extended": "^3.0.0",
|
|
155
156
|
"eslint-plugin-n": "^17.0.0 || ^18.0.0",
|
|
157
|
+
"eslint-plugin-project-structure": "^3.14.0",
|
|
156
158
|
"eslint-plugin-react-jsx": "^5.10.0",
|
|
157
159
|
"eslint-plugin-react-naming-convention": "^5.10.0",
|
|
158
160
|
"eslint-plugin-react-x": "^5.10.0",
|
|
@@ -176,6 +178,9 @@
|
|
|
176
178
|
"eslint-plugin-n": {
|
|
177
179
|
"optional": true
|
|
178
180
|
},
|
|
181
|
+
"eslint-plugin-project-structure": {
|
|
182
|
+
"optional": true
|
|
183
|
+
},
|
|
179
184
|
"eslint-plugin-react-jsx": {
|
|
180
185
|
"optional": true
|
|
181
186
|
},
|