pasika 0.5.17 → 0.5.18

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 CHANGED
@@ -88,8 +88,8 @@ The script refuses a hash no requirement has, a `ref` naming a rule that does no
88
88
 
89
89
  ```bash
90
90
  npm run build
91
- npm run dogfood -- ../some/repo # nextjsApp preset (default)
92
- npm run dogfood -- ../some/repo --preset=typescriptApp
91
+ npm run dogfood -- ../some/repo # pasikaNextjsApp preset (default)
92
+ npm run dogfood -- ../some/repo --preset=pasikaApp
93
93
  npm run dogfood -- ../some/repo --pasika-only # tally only pasika/* rules
94
94
  npm run dogfood -- ../some/repo --rule=css-entry-point --findings
95
95
  npm run dogfood -- ../some/repo --json # machine-readable report
@@ -115,18 +115,18 @@ const { eslintConfig } = styleguide({
115
115
  export default eslintConfig;
116
116
  ```
117
117
 
118
- For a plain TypeScript repository, enable `pasikaTypescriptApp` instead of `pasikaNextjsApp`; that preset covers the manifest, the zirka contract, and the docs only — the `src/**` source rules belong to the Next.js app preset. `zirka` composes the pasika ruleset over four file scopes: TS/TSX under `src/**`, `globals.css` and other stylesheets, `package.json`, and markdown — each with its own ESLint language.
118
+ For a plain TypeScript repository, enable `pasikaApp` instead of `pasikaNextjsApp`; that preset covers the manifest, the zirka contract, and the docs only — the `src/**` source rules belong to the Next.js app preset. `zirka` composes the pasika ruleset over four file scopes: TS/TSX under `src/**`, `globals.css` and other stylesheets, `package.json`, and markdown — each with its own ESLint language.
119
119
 
120
120
  ### Without Zirka
121
121
 
122
122
  ```ts
123
123
  // eslint.config.ts — plain TypeScript repository
124
- import { typescriptApp } from "pasika/eslint";
124
+ import { pasikaApp } from "pasika/eslint";
125
125
 
126
- export default typescriptApp;
126
+ export default pasikaApp;
127
127
  ```
128
128
 
129
- `typescriptApp` is the plain-TypeScript-repository preset — the package.json manifest, the zirka configuration contract, and the docs. It carries no `src/**` source block: source linting is the Next.js app's job. `nextjsApp` is the full framework preset: everything in `typescriptApp` plus the Next.js-stack manifest requirement, the `src/**` app source rules, and the Tailwind stylesheet blocks. The granular rule objects (`tailwindRules`, `repoPackageJsonRules`, `documentationRules`) stay exported for manual wiring.
129
+ `pasikaApp` is the plain-TypeScript-repository preset — the package.json manifest, the zirka configuration contract, and the docs. It carries no `src/**` source block: source linting is the Next.js app's job. `pasikaNextjsApp` is the full framework preset: everything in `pasikaApp` plus the Next.js-stack manifest requirement, the `src/**` app source rules, and the Tailwind stylesheet blocks. The granular rule objects (`tailwindRules`, `repoPackageJsonRules`, `documentationRules`) stay exported for manual wiring.
130
130
 
131
131
  The `src/**` blocks ship `@typescript-eslint/parser` themselves, so a standalone preset parses `.ts`/`.tsx` correctly on its own (`pasika` lists it as a runtime dependency).
132
132
 
@@ -246,13 +246,16 @@ declare const allPasikaRuleIds: string[];
246
246
  * source block: source linting is the Next.js app's job.
247
247
  * Use this for a plain TypeScript repository that does not adopt the framework.
248
248
  */
249
- declare const typescriptApp: Linter.Config[];
249
+ declare const pasikaApp: Linter.Config[];
250
250
  /**
251
251
  * Next.js app preset: the full adopted-to-the-framework stack. Anything in
252
- * `typescriptApp` plus the framework-only blocks — the Next.js-stack manifest
252
+ * `pasikaApp` plus the framework-only blocks — the Next.js-stack manifest
253
253
  * requirement, the `src/**` app source rules, and the Tailwind stylesheet
254
- * rules. `typescriptApp` is a strict subset of `nextjsApp`.
254
+ * rules. `pasikaApp` is a strict subset of `pasikaNextjsApp`. Consuming this
255
+ * preset (spreading or iterating it) runs the TypeScript alignment diagnostic
256
+ * first, so a mismatched compiler major fails config load with an actionable
257
+ * error instead of crashing every type-aware rule later.
255
258
  */
256
- declare const nextjsApp: Linter.Config[];
259
+ declare const pasikaNextjsApp: Linter.Config[];
257
260
 
258
- export { allPasikaRuleIds, documentationRules, huskyRules, nextjsApp, nextjsPackageJsonRules, pasikaPlugin, repoPackageJsonRules, tailwindRules, typescriptApp, vulykRules };
261
+ export { allPasikaRuleIds, documentationRules, huskyRules, nextjsPackageJsonRules, pasikaApp, pasikaNextjsApp, pasikaPlugin, repoPackageJsonRules, tailwindRules, vulykRules };
@@ -4,6 +4,53 @@ import jsonPlugin from "@eslint/json";
4
4
  import markdown from "@eslint/markdown";
5
5
  import tsParser from "@typescript-eslint/parser";
6
6
 
7
+ // eslint/ts-alignment.ts
8
+ import { readFileSync } from "fs";
9
+ import { createRequire } from "module";
10
+ import { join } from "path";
11
+ var ownRequire = createRequire(import.meta.url);
12
+ var majorOf = (version) => Number(version.split(".")[0]);
13
+ function versionOf(packagePath) {
14
+ try {
15
+ const parsed = JSON.parse(readFileSync(packagePath, "utf8"));
16
+ if (typeof parsed !== "object" || parsed === null || !("version" in parsed)) return null;
17
+ return typeof parsed.version === "string" ? parsed.version : null;
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ function resolveTypescriptAlignment() {
23
+ let bundled = null;
24
+ try {
25
+ bundled = versionOf(ownRequire.resolve("typescript/package.json"));
26
+ } catch {
27
+ bundled = null;
28
+ }
29
+ let resolved = null;
30
+ try {
31
+ const consumerRequire = createRequire(join(process.cwd(), "package.json"));
32
+ resolved = versionOf(consumerRequire.resolve("typescript/package.json"));
33
+ } catch {
34
+ resolved = null;
35
+ }
36
+ return { bundled, resolved };
37
+ }
38
+ function alignmentError(bundled, resolved) {
39
+ if (bundled === null || resolved === null) return null;
40
+ const bundledMajor = majorOf(bundled);
41
+ const resolvedMajor = majorOf(resolved);
42
+ if (bundledMajor === resolvedMajor) return null;
43
+ const direction = resolvedMajor < bundledMajor ? `Upgrade your typescript to ^${String(bundledMajor)} (pinned exact) so a single hoisted copy serves the whole pipeline.` : `Your repository is ahead of the compiler pasika ships. Pin typescript to ^${String(bundledMajor)} for now and upgrade pasika when it bundles the newer major.`;
44
+ return new Error(
45
+ `[pasika] TypeScript major mismatch: the pasikaNextjsApp preset parses src/** with its bundled @typescript-eslint/parser, which runs on typescript ${bundled}; your repository resolves typescript ${resolved} for the type-aware rules. Two TypeScript majors mean the types the parser builds use a different TypeFlags layout than the rules expect, so every type-aware rule crashes (e.g. "undefined is not iterable" in ts-api-utils). Align them on ${String(bundledMajor)}, then lint will run again. ${direction}`
46
+ );
47
+ }
48
+ function assertTypescriptAlignment() {
49
+ const { bundled, resolved } = resolveTypescriptAlignment();
50
+ const error = alignmentError(bundled, resolved);
51
+ if (error) throw error;
52
+ }
53
+
7
54
  // eslint/rules/filename-case.ts
8
55
  import path2 from "path";
9
56
 
@@ -473,7 +520,7 @@ var noArbitraryTailwindRule = {
473
520
  };
474
521
 
475
522
  // eslint/rules/unknown-utility.ts
476
- import { readdirSync, readFileSync, statSync } from "fs";
523
+ import { readdirSync, readFileSync as readFileSync2, statSync } from "fs";
477
524
  import path5 from "path";
478
525
  var PREFIX_NAMESPACES = {
479
526
  bg: ["color"],
@@ -752,7 +799,7 @@ function readInventory(files) {
752
799
  for (const file of files) {
753
800
  let css2;
754
801
  try {
755
- css2 = readFileSync(file, "utf8");
802
+ css2 = readFileSync2(file, "utf8");
756
803
  } catch {
757
804
  continue;
758
805
  }
@@ -922,7 +969,7 @@ import path7 from "path";
922
969
 
923
970
  // eslint/project/parse-module.ts
924
971
  import path6 from "path";
925
- import { readFileSync as readFileSync2 } from "fs";
972
+ import { readFileSync as readFileSync3 } from "fs";
926
973
  import ts2 from "typescript";
927
974
  var isPascalCase3 = (name) => /^[A-Z][A-Za-z0-9]*$/.test(name);
928
975
  var isHookName = (name) => /^use[A-Z]/.test(name);
@@ -959,7 +1006,7 @@ function classifyValue(name, initializer, isTsx) {
959
1006
  return "constant";
960
1007
  }
961
1008
  function parseModule(file) {
962
- const text = readFileSync2(file, "utf8");
1009
+ const text = readFileSync3(file, "utf8");
963
1010
  const isTsx = file.endsWith(".tsx") || file.endsWith(".jsx");
964
1011
  const sourceFile = ts2.createSourceFile(file, text, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
965
1012
  const imports = [];
@@ -3207,7 +3254,7 @@ var typeExtractionRule = {
3207
3254
 
3208
3255
  // eslint/rules/locale-placement.ts
3209
3256
  import path32 from "path";
3210
- import { readFileSync as readFileSync3 } from "fs";
3257
+ import { readFileSync as readFileSync4 } from "fs";
3211
3258
  import ts5 from "typescript";
3212
3259
  var LOCALE_ACCESS = /\blocales\.(?<key>[A-Za-z_$][\w$]*)/g;
3213
3260
  var camelCase = (name) => name.replace(/-[a-z]/g, (match) => match.slice(1).toUpperCase());
@@ -3264,7 +3311,7 @@ var localePlacementRule = {
3264
3311
  return candidateSegments.length === 2 && candidateSegments[0] === "locales" && candidateSegments[1]?.startsWith("index.");
3265
3312
  });
3266
3313
  if (!localesFile || file !== localesFile) return {};
3267
- const placement = localePlacement(readFileSync3(localesFile, "utf8"));
3314
+ const placement = localePlacement(readFileSync4(localesFile, "utf8"));
3268
3315
  if (!placement) return {};
3269
3316
  const keyReaders = /* @__PURE__ */ new Map();
3270
3317
  const keyFeatures = /* @__PURE__ */ new Map();
@@ -3276,7 +3323,7 @@ var localePlacementRule = {
3276
3323
  );
3277
3324
  if (!importsLocales) continue;
3278
3325
  const candidateSegments = segmentsOf(candidateFile, sourceRoot);
3279
- for (const match of readFileSync3(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
3326
+ for (const match of readFileSync4(candidateFile, "utf8").matchAll(LOCALE_ACCESS)) {
3280
3327
  const key = match.groups?.key;
3281
3328
  if (key === void 0) continue;
3282
3329
  const readers = keyReaders.get(key) ?? /* @__PURE__ */ new Set();
@@ -3613,7 +3660,7 @@ var localeKeyShapeRule = {
3613
3660
 
3614
3661
  // eslint/rules/shared-style-dedup.ts
3615
3662
  import path35 from "path";
3616
- import { readFileSync as readFileSync4, statSync as statSync3 } from "fs";
3663
+ import { readFileSync as readFileSync5, statSync as statSync3 } from "fs";
3617
3664
  var CLASS_NAME = /className="(?<classes>[^"]+)"/g;
3618
3665
  var comboCache;
3619
3666
  function combosFor(index) {
@@ -3631,7 +3678,7 @@ function combosFor(index) {
3631
3678
  }
3632
3679
  const combos = /* @__PURE__ */ new Map();
3633
3680
  for (const file of files) {
3634
- const text = readFileSync4(file, "utf8");
3681
+ const text = readFileSync5(file, "utf8");
3635
3682
  for (const match of text.matchAll(CLASS_NAME)) {
3636
3683
  const classes = (match.groups?.classes ?? "").split(/\s+/).filter(Boolean);
3637
3684
  if (classes.length < 2) continue;
@@ -4541,7 +4588,7 @@ var noTemplatePromptRule = {
4541
4588
  import path41 from "path";
4542
4589
 
4543
4590
  // eslint/rules/documentation/project-index.ts
4544
- import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
4591
+ import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
4545
4592
  import path40 from "path";
4546
4593
  var KIND_BY_SUFFIX = [
4547
4594
  ["-rule.md", "rule"],
@@ -4559,7 +4606,7 @@ function listMarkdownFiles(dir) {
4559
4606
  });
4560
4607
  }
4561
4608
  function extractTitle(filePath) {
4562
- const content = readFileSync5(filePath, "utf8");
4609
+ const content = readFileSync6(filePath, "utf8");
4563
4610
  const match = /^# (?<title>.+)$/m.exec(content);
4564
4611
  return match?.groups?.title?.trim() ?? "";
4565
4612
  }
@@ -4842,10 +4889,10 @@ var noNestedHowToRule = {
4842
4889
  };
4843
4890
 
4844
4891
  // eslint/rules/documentation/glossary-term-linking.ts
4845
- import { readFileSync as readFileSync6 } from "fs";
4892
+ import { readFileSync as readFileSync7 } from "fs";
4846
4893
  import path42 from "path";
4847
4894
  function extractGlossaryTerms(filePath) {
4848
- const content = readFileSync6(filePath, "utf8");
4895
+ const content = readFileSync7(filePath, "utf8");
4849
4896
  const terms = [];
4850
4897
  const headingPattern = /^## (?<term>.+)$/gm;
4851
4898
  let match;
@@ -5444,7 +5491,7 @@ import { statSync as statSync6 } from "fs";
5444
5491
  import path46 from "path";
5445
5492
 
5446
5493
  // eslint/rules/tailwind/source-files.ts
5447
- import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
5494
+ import { readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
5448
5495
  import path44 from "path";
5449
5496
  var CSS_EXTENSIONS = [".css"];
5450
5497
  var MODULE_EXTENSIONS3 = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
@@ -5475,7 +5522,7 @@ function cachedTextReader() {
5475
5522
  let text = texts.get(file);
5476
5523
  if (text === void 0) {
5477
5524
  try {
5478
- text = readFileSync7(file, "utf8");
5525
+ text = readFileSync8(file, "utf8");
5479
5526
  } catch {
5480
5527
  text = "";
5481
5528
  }
@@ -5843,7 +5890,7 @@ var nextjsPackageJsonRules = {
5843
5890
  };
5844
5891
 
5845
5892
  // eslint/rules/husky/husky-hook.ts
5846
- import { existsSync as existsSync2, readFileSync as readFileSync8 } from "fs";
5893
+ import { existsSync as existsSync2, readFileSync as readFileSync9 } from "fs";
5847
5894
  import path47 from "path";
5848
5895
  function memberName4(member) {
5849
5896
  return member.name.type === "String" ? member.name.value : member.name.name;
@@ -5871,7 +5918,7 @@ var huskyHookRule = {
5871
5918
  });
5872
5919
  return;
5873
5920
  }
5874
- const content = readFileSync8(hookPath, "utf8");
5921
+ const content = readFileSync9(hookPath, "utf8");
5875
5922
  if (!content.includes("lint-staged")) {
5876
5923
  context.report({ node, message: ".husky/pre-commit must run lint-staged." });
5877
5924
  }
@@ -5902,7 +5949,7 @@ var huskyRules = {
5902
5949
  };
5903
5950
 
5904
5951
  // eslint/rules/vulyk/vulyk-docs.ts
5905
- import { existsSync as existsSync3, readFileSync as readFileSync9 } from "fs";
5952
+ import { existsSync as existsSync3, readFileSync as readFileSync10 } from "fs";
5906
5953
  import path48 from "path";
5907
5954
  var PASIKA_REPO = "Bredansky/pasika";
5908
5955
  var BASE_REQUIRED_DOCS = [
@@ -5945,7 +5992,7 @@ var vulykDocsRule = {
5945
5992
  });
5946
5993
  return;
5947
5994
  }
5948
- const config = readFileSync9(configPath, "utf8");
5995
+ const config = readFileSync10(configPath, "utf8");
5949
5996
  if (!config.includes(PASIKA_REPO)) {
5950
5997
  context.report({
5951
5998
  node,
@@ -5980,7 +6027,7 @@ var vulykRules = {
5980
6027
  };
5981
6028
 
5982
6029
  // eslint/index.ts
5983
- var nextjsAppRules = {
6030
+ var pasikaNextjsAppRules = {
5984
6031
  // Framework-agnostic TypeScript rules.
5985
6032
  "filename-case": filenameCaseRule,
5986
6033
  "import-boundaries": importBoundariesRule,
@@ -6030,7 +6077,7 @@ var pasikaPlugin = {
6030
6077
  rules: {
6031
6078
  // The shared plugin must register every rule the preset blocks reference,
6032
6079
  // so all rule sets live here.
6033
- ...nextjsAppRules,
6080
+ ...pasikaNextjsAppRules,
6034
6081
  ...documentationRules,
6035
6082
  ...tailwindRules,
6036
6083
  ...repoPackageJsonRules,
@@ -6043,7 +6090,7 @@ var jsonLanguage = { languages: { json: jsonPlugin.languages.json } };
6043
6090
  function ruleIds(rules2) {
6044
6091
  return Object.keys(rules2).map((name) => `pasika/${name}`);
6045
6092
  }
6046
- var nextjsAppRuleIds = ruleIds(nextjsAppRules);
6093
+ var pasikaNextjsAppRuleIds = ruleIds(pasikaNextjsAppRules);
6047
6094
  var documentationRuleIds = ruleIds(documentationRules);
6048
6095
  var tailwindRuleIds = ruleIds(tailwindRules);
6049
6096
  var repoPackageJsonRuleIds = ruleIds(repoPackageJsonRules);
@@ -6051,7 +6098,7 @@ var nextjsPackageJsonRuleIds = ruleIds(nextjsPackageJsonRules);
6051
6098
  var huskyRuleIds = ruleIds(huskyRules);
6052
6099
  var vulykRuleIds = ruleIds(vulykRules);
6053
6100
  var allPasikaRuleIds = [
6054
- ...nextjsAppRuleIds,
6101
+ ...pasikaNextjsAppRuleIds,
6055
6102
  ...documentationRuleIds,
6056
6103
  ...tailwindRuleIds,
6057
6104
  ...repoPackageJsonRuleIds,
@@ -6059,7 +6106,7 @@ var allPasikaRuleIds = [
6059
6106
  ...huskyRuleIds,
6060
6107
  ...vulykRuleIds
6061
6108
  ];
6062
- var typescriptAppLanguageOptions = {
6109
+ var pasikaAppLanguageOptions = {
6063
6110
  parser: tsParser,
6064
6111
  parserOptions: {
6065
6112
  ecmaVersion: "latest",
@@ -6067,13 +6114,13 @@ var typescriptAppLanguageOptions = {
6067
6114
  ecmaFeatures: { jsx: true }
6068
6115
  }
6069
6116
  };
6070
- var nextjsAppConfig = {
6117
+ var pasikaNextjsAppConfig = {
6071
6118
  files: ["src/**/*.{cjs,cts,js,jsx,mjs,mts,ts,tsx}"],
6072
- languageOptions: typescriptAppLanguageOptions,
6119
+ languageOptions: pasikaAppLanguageOptions,
6073
6120
  plugins: {
6074
6121
  pasika: pasikaPlugin
6075
6122
  },
6076
- rules: Object.fromEntries(nextjsAppRuleIds.map((id) => [id, "error"]))
6123
+ rules: Object.fromEntries(pasikaNextjsAppRuleIds.map((id) => [id, "error"]))
6077
6124
  };
6078
6125
  var tailwindStructureRules = {
6079
6126
  files: ["src/**/globals.css"],
@@ -6097,7 +6144,7 @@ var tailwindImportGraph = {
6097
6144
  languageOptions: { tolerant: true },
6098
6145
  rules: { "pasika/css-entry-point": "error" }
6099
6146
  };
6100
- var typescriptAppPackageJsonConfig = {
6147
+ var pasikaAppPackageJsonConfig = {
6101
6148
  files: ["package.json"],
6102
6149
  plugins: {
6103
6150
  json: jsonLanguage,
@@ -6106,7 +6153,7 @@ var typescriptAppPackageJsonConfig = {
6106
6153
  language: "json/json",
6107
6154
  rules: Object.fromEntries([...repoPackageJsonRuleIds, ...huskyRuleIds, ...vulykRuleIds].map((id) => [id, "error"]))
6108
6155
  };
6109
- var nextjsAppPackageJsonConfig = {
6156
+ var pasikaNextjsAppPackageJsonConfig = {
6110
6157
  files: ["package.json"],
6111
6158
  plugins: {
6112
6159
  json: jsonLanguage,
@@ -6133,27 +6180,39 @@ var documentationConfig = {
6133
6180
  language: "markdown/gfm",
6134
6181
  rules: Object.fromEntries(documentationRuleIds.map((id) => [id, "error"]))
6135
6182
  };
6136
- var typescriptApp = [
6137
- typescriptAppPackageJsonConfig,
6183
+ var pasikaApp = [
6184
+ pasikaAppPackageJsonConfig,
6138
6185
  zirkaConfig,
6139
6186
  documentationConfig
6140
6187
  ];
6141
- var nextjsApp = [
6142
- ...typescriptApp,
6143
- nextjsAppPackageJsonConfig,
6144
- nextjsAppConfig,
6188
+ var pasikaNextjsAppWithDiagnostic = (preset) => {
6189
+ let checked = false;
6190
+ return new Proxy(preset, {
6191
+ get(target, property, receiver) {
6192
+ if (!checked && (property === Symbol.iterator || property === "length")) {
6193
+ checked = true;
6194
+ assertTypescriptAlignment();
6195
+ }
6196
+ return Reflect.get(target, property, receiver);
6197
+ }
6198
+ });
6199
+ };
6200
+ var pasikaNextjsApp = pasikaNextjsAppWithDiagnostic([
6201
+ ...pasikaApp,
6202
+ pasikaNextjsAppPackageJsonConfig,
6203
+ pasikaNextjsAppConfig,
6145
6204
  tailwindStructureRules,
6146
6205
  tailwindImportGraph
6147
- ];
6206
+ ]);
6148
6207
  export {
6149
6208
  allPasikaRuleIds,
6150
6209
  documentationRules,
6151
6210
  huskyRules,
6152
- nextjsApp,
6153
6211
  nextjsPackageJsonRules,
6212
+ pasikaApp,
6213
+ pasikaNextjsApp,
6154
6214
  pasikaPlugin,
6155
6215
  repoPackageJsonRules,
6156
6216
  tailwindRules,
6157
- typescriptApp,
6158
6217
  vulykRules
6159
6218
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.5.17",
3
+ "version": "0.5.18",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",