oxlint-plugin-arch 0.2.3 → 0.2.4

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.4
4
+
5
+ - Add `trailingRoles` and `roleSeparators` so `export-file-prefix`, `declaration-name`, and `no-extra-exports` can treat `onchain-utils.ts` like `onchain.utils.ts`.
6
+
3
7
  ## 0.2.3
4
8
 
5
9
  - Add `declaration-name` so selected declarations can follow a file prefix or pattern per glob.
package/README.md CHANGED
@@ -47,13 +47,13 @@ Copy the shapes that match your repository; do not copy globs or naming policy b
47
47
 
48
48
  ## Rules
49
49
 
50
- - `declaration-name` — require selected declarations to match a filename-derived prefix or a consumer pattern.
51
- - `export-file-prefix` — require export names, or every function and type, to start with a filename-derived prefix, optionally singularized.
50
+ - `declaration-name` — require selected declarations to match a filename-derived prefix or a consumer pattern. Optional `trailingRoles` treat `onchain-utils.ts` like `onchain.utils.ts`.
51
+ - `export-file-prefix` — require export names, or every function and type, to start with a filename-derived prefix, optionally singularized. Optional `trailingRoles` drop a configured role suffix from that prefix.
52
52
  - `export-name-pattern` — require export names, or every function and type, to match a configured regular expression.
53
53
  - `filename-export-name` — derive expected function names from filename templates, optionally including locals.
54
54
  - `filename-match` — require filenames to match a configured pattern.
55
55
  - `folder-prefix` — require filenames to start with their parent folder name, or the folders after a configured root.
56
- - `no-extra-exports` — restrict files to configured export templates.
56
+ - `no-extra-exports` — restrict files to configured export templates. Optional `trailingRoles` apply to `{Domain}` the same way as the prefix rules.
57
57
  - `no-extra-factory-keys` — restrict direct factory return keys.
58
58
  - `no-file-level-helpers` — keep unapproved helpers out of module scope.
59
59
  - `no-imported-type-alias` — reject exported aliases that only rename imported types.
package/dist/index.js CHANGED
@@ -422,9 +422,37 @@ function namingMatchTemplate(basename, template, placeholderPattern = "[a-z0-9-]
422
422
  }
423
423
  return new RegExp(`^${pattern}$`).exec(basename)?.groups ?? null;
424
424
  }
425
+ var namingStemModes = ["before-first-dot", "full-basename"];
425
426
  function namingNormalized(value, normalize) {
426
427
  return normalize === "none" ? value : value.replaceAll(/[-_]/g, "").toLowerCase();
427
428
  }
429
+ function namingFileStem(basename, mode = "before-first-dot", trailingRoles = [], roleSeparators = [".", "-"]) {
430
+ const stem = mode === "full-basename" ? basename : basename.split(".")[0] ?? "";
431
+ const roles = [...new Set(trailingRoles.filter(Boolean))].sort((left, right) => right.length - left.length);
432
+ const separators = [...new Set(roleSeparators.filter(Boolean))];
433
+ if (roles.length === 0 || separators.length === 0) {
434
+ return stem;
435
+ }
436
+ let remaining = stem;
437
+ let changed = true;
438
+ while (changed) {
439
+ changed = false;
440
+ for (const role of roles) {
441
+ for (const separator of separators) {
442
+ const suffix = `${separator}${role}`;
443
+ if (remaining.length > suffix.length && remaining.endsWith(suffix)) {
444
+ remaining = remaining.slice(0, -suffix.length);
445
+ changed = true;
446
+ break;
447
+ }
448
+ }
449
+ if (changed) {
450
+ break;
451
+ }
452
+ }
453
+ }
454
+ return remaining;
455
+ }
428
456
  function namingFilePrefixes(stem, normalize = "remove-separators", singularize = "none") {
429
457
  const prefixes = [namingNormalized(stem, normalize)];
430
458
  const parts = stem.split(/[-_]+/).filter(Boolean);
@@ -699,7 +727,9 @@ var declarationName = defineRule({
699
727
  },
700
728
  pattern: { type: "string" },
701
729
  flags: { type: "string" },
702
- stem: { type: "string", enum: ["before-first-dot", "full-basename"] },
730
+ stem: { type: "string", enum: [...namingStemModes] },
731
+ trailingRoles: { type: "array", items: { type: "string" } },
732
+ roleSeparators: { type: "array", items: { type: "string" } },
703
733
  normalize: { type: "string", enum: ["remove-separators", "none"] },
704
734
  singularize: { type: "string", enum: ["none", "trailing-s"] },
705
735
  allowPattern: { type: "string" }
@@ -720,7 +750,7 @@ var declarationName = defineRule({
720
750
  const allowed = options2.allowPattern ? new RegExp(options2.allowPattern) : null;
721
751
  const expected = options2.pattern ? new RegExp(options2.pattern, options2.flags) : null;
722
752
  const basename = namingFileBasename(context.filename).replace(/\.(tsx?|jsx?)$/, "");
723
- const stem = options2.stem === "full-basename" ? basename : basename.split(".")[0] ?? "";
753
+ const stem = namingFileStem(basename, options2.stem, options2.trailingRoles, options2.roleSeparators);
724
754
  const prefixes = namingFilePrefixes(stem, options2.normalize ?? "remove-separators", options2.singularize ?? "none");
725
755
  const seen = new Set;
726
756
  for (const item of declarationsCollectNamed(program, options2.kinds)) {
@@ -751,7 +781,9 @@ var exportFilePrefix = defineRule2({
751
781
  type: "object",
752
782
  additionalProperties: false,
753
783
  properties: {
754
- stem: { type: "string", enum: ["before-first-dot", "full-basename"] },
784
+ stem: { type: "string", enum: [...namingStemModes] },
785
+ trailingRoles: { type: "array", items: { type: "string" } },
786
+ roleSeparators: { type: "array", items: { type: "string" } },
755
787
  normalize: { type: "string", enum: ["remove-separators", "none"] },
756
788
  singularize: { type: "string", enum: ["none", "trailing-s"] },
757
789
  allDeclarations: { type: "boolean" },
@@ -770,7 +802,7 @@ var exportFilePrefix = defineRule2({
770
802
  normalize: "remove-separators"
771
803
  });
772
804
  const basename = namingFileBasename(context.filename).replace(/\.(tsx?|jsx?)$/, "");
773
- const stem = options2.stem === "full-basename" ? basename : basename.split(".")[0] ?? "";
805
+ const stem = namingFileStem(basename, options2.stem, options2.trailingRoles, options2.roleSeparators);
774
806
  const prefixes = namingFilePrefixes(stem, options2.normalize ?? "remove-separators", options2.singularize ?? "none");
775
807
  const allowed = options2.allowPattern ? new RegExp(options2.allowPattern) : null;
776
808
  const names = options2.allDeclarations ? declarationsCollectNamed(program) : exportsCollect(program).map((binding) => ({
@@ -1003,7 +1035,9 @@ var noExtraExports = defineRule7({
1003
1035
  additionalProperties: false,
1004
1036
  properties: {
1005
1037
  names: { type: "array", items: { type: "string" } },
1006
- domainStem: { type: "string", enum: ["before-first-dot", "full-basename"] },
1038
+ domainStem: { type: "string", enum: [...namingStemModes] },
1039
+ trailingRoles: { type: "array", items: { type: "string" } },
1040
+ roleSeparators: { type: "array", items: { type: "string" } },
1007
1041
  allowTypeExports: { type: "boolean" }
1008
1042
  },
1009
1043
  required: ["names"]
@@ -1017,7 +1051,7 @@ var noExtraExports = defineRule7({
1017
1051
  Program(program) {
1018
1052
  const options2 = optionsFirst(context);
1019
1053
  const basename = namingFileBasename(context.filename).replace(/\.(tsx?|jsx?)$/, "");
1020
- const domain = options2.domainStem === "full-basename" ? basename : basename.split(".")[0] ?? "";
1054
+ const domain = namingFileStem(basename, options2.domainStem, options2.trailingRoles, options2.roleSeparators);
1021
1055
  const allowed = options2.names.map((template) => template.replaceAll("{Domain}", namingPascalCase(domain)).replaceAll("{domain}", namingCamelCase(domain)));
1022
1056
  for (const binding of exportsCollect(program)) {
1023
1057
  if (options2.allowTypeExports && binding.typeOnly) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-arch",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Configurable architecture rules for Oxlint JavaScript plugins.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",