pasika 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -522,7 +522,7 @@ var noArbitraryTailwindRule = {
522
522
  };
523
523
 
524
524
  // eslint/rules/unknown-utility.ts
525
- import { readdirSync, readFileSync as readFileSync2, statSync } from "fs";
525
+ import { existsSync, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
526
526
  import path5 from "path";
527
527
  var PREFIX_NAMESPACES = {
528
528
  bg: ["color"],
@@ -749,6 +749,53 @@ function stylesheetFiles(dir) {
749
749
  return path5.extname(entry) === ".css" ? [entryPath] : [];
750
750
  });
751
751
  }
752
+ function packageNameOf(specifier) {
753
+ if (specifier.startsWith(".") || specifier.startsWith("/")) return void 0;
754
+ const segments = specifier.split("/");
755
+ return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
756
+ }
757
+ function resolvePackageStylesheet(fromFile, specifier) {
758
+ const packageName = packageNameOf(specifier);
759
+ if (!packageName || packageName === "tailwindcss") return void 0;
760
+ let directory = path5.dirname(fromFile);
761
+ while (directory !== path5.dirname(directory)) {
762
+ const packageRoot = path5.join(directory, "node_modules", packageName);
763
+ const manifestPath = path5.join(packageRoot, "package.json");
764
+ if (existsSync(manifestPath)) {
765
+ try {
766
+ const manifest = readFileSync2(manifestPath, "utf8");
767
+ const stylesheet = /"style"\s*:\s*"(?<file>[^"]+\.css)"/.exec(manifest)?.groups?.file;
768
+ const main = /"main"\s*:\s*"(?<file>[^"]+\.css)"/.exec(manifest)?.groups?.file;
769
+ const target = stylesheet ?? main;
770
+ if (!target) return void 0;
771
+ const resolved = path5.resolve(packageRoot, target);
772
+ return existsSync(resolved) ? resolved : void 0;
773
+ } catch {
774
+ return void 0;
775
+ }
776
+ }
777
+ directory = path5.dirname(directory);
778
+ }
779
+ return void 0;
780
+ }
781
+ function inventoryStylesheetFiles(projectFiles) {
782
+ const files = new Set(projectFiles);
783
+ for (const file of projectFiles) {
784
+ let css2;
785
+ try {
786
+ css2 = readFileSync2(file, "utf8");
787
+ } catch {
788
+ continue;
789
+ }
790
+ for (const match of css2.matchAll(/@import\s+(?:url\(\s*)?["'](?<specifier>[^"']+)["']/gi)) {
791
+ const specifier = match.groups?.specifier;
792
+ if (!specifier) continue;
793
+ const imported = resolvePackageStylesheet(file, specifier);
794
+ if (imported) files.add(imported);
795
+ }
796
+ }
797
+ return [...files];
798
+ }
752
799
  function themeBlocks(css2) {
753
800
  const blocks = [];
754
801
  let index = 0;
@@ -794,6 +841,7 @@ function classSelectors(css2) {
794
841
  }
795
842
  function readInventory(files) {
796
843
  const utilities = /* @__PURE__ */ new Set();
844
+ const wildcardUtilities = /* @__PURE__ */ new Set();
797
845
  const utilityPrefixes = /* @__PURE__ */ new Set();
798
846
  const themeTokensByNamespace = /* @__PURE__ */ new Map();
799
847
  const plainClasses = /* @__PURE__ */ new Set();
@@ -805,9 +853,10 @@ function readInventory(files) {
805
853
  } catch {
806
854
  continue;
807
855
  }
808
- for (const name of css2.matchAll(/@utility\s+(?<utilityName>[a-zA-Z0-9_-]+)/g)) {
856
+ for (const name of css2.matchAll(/@utility\s+(?<utilityName>[a-zA-Z0-9_*-]+)/g)) {
809
857
  const utilityName = name.groups?.utilityName;
810
858
  if (!utilityName) continue;
859
+ if (utilityName.endsWith("-*")) wildcardUtilities.add(utilityName.slice(0, -1));
811
860
  utilities.add(utilityName);
812
861
  const dash = utilityName.indexOf("-");
813
862
  if (dash > 0) utilityPrefixes.add(utilityName.slice(0, dash));
@@ -828,7 +877,14 @@ function readInventory(files) {
828
877
  }
829
878
  }
830
879
  }
831
- return { utilities, utilityPrefixes, themeTokens: themeTokensByNamespace, defaultsReset, plainClasses };
880
+ return {
881
+ utilities,
882
+ wildcardUtilities,
883
+ utilityPrefixes,
884
+ themeTokens: themeTokensByNamespace,
885
+ defaultsReset,
886
+ plainClasses
887
+ };
832
888
  }
833
889
  function isColorToken(token, inventory) {
834
890
  if (inventory.themeTokens.get("color")?.has(token)) return true;
@@ -852,6 +908,7 @@ function isKnown(className, inventory) {
852
908
  const token = compound ? utility.slice(compound.length + 1) : utility.slice(dash + 1);
853
909
  if (!token) return true;
854
910
  if (inventory.utilities.has(utility)) return true;
911
+ if ([...inventory.wildcardUtilities].some((wildcardPrefix) => utility.startsWith(wildcardPrefix))) return true;
855
912
  const namespaces = PREFIX_NAMESPACES[prefix];
856
913
  if (!namespaces) {
857
914
  if (STATIC_VALUE_TOKENS[prefix]?.includes(token)) return true;
@@ -890,7 +947,7 @@ var unknownUtilityRule = {
890
947
  let inventory;
891
948
  try {
892
949
  if (!statSync(sourceRoot).isDirectory()) return {};
893
- inventory = readInventory(stylesheetFiles(sourceRoot));
950
+ inventory = readInventory(inventoryStylesheetFiles(stylesheetFiles(sourceRoot)));
894
951
  } catch {
895
952
  return {};
896
953
  }
@@ -5015,7 +5072,7 @@ var glossaryTermLinkingRule = {
5015
5072
  };
5016
5073
 
5017
5074
  // eslint/rules/documentation/guide-mentions-documents.ts
5018
- import { existsSync } from "fs";
5075
+ import { existsSync as existsSync2 } from "fs";
5019
5076
  import path43 from "path";
5020
5077
  function visitSteps3(node, check) {
5021
5078
  if (node.type === "list" && node.ordered) {
@@ -5082,7 +5139,7 @@ var guideMentionsDocumentsRule = {
5082
5139
  const target = linkTarget(link.url);
5083
5140
  if (!target.endsWith(".md")) continue;
5084
5141
  const resolved = path43.normalize(path43.join(guideDir, target));
5085
- if (!existsSync(resolved)) {
5142
+ if (!existsSync2(resolved)) {
5086
5143
  context.report({
5087
5144
  node: link,
5088
5145
  message: `Guide links a document that does not exist: ${link.url}`
@@ -5437,12 +5494,39 @@ var stylesheetOrderingRule = {
5437
5494
  };
5438
5495
 
5439
5496
  // eslint/rules/tailwind/css-variable-naming.ts
5440
- var EDGE_PROPERTY_WORDS = /* @__PURE__ */ new Set(["border", "divider", "outline", "ring", "stroke"]);
5441
- var NON_COLOR_WORDS = /* @__PURE__ */ new Set(["offset", "radius", "spacing", "style", "width"]);
5497
+ var EDGE_PROPERTY_WORDS = /* @__PURE__ */ new Set(["border", "outline", "ring", "stroke"]);
5498
+ var NON_COLOR_WORDS = /* @__PURE__ */ new Set(["height", "leading", "offset", "radius", "size", "spacing", "style", "width"]);
5499
+ var TAILWIND_THEME_NAMESPACES = /* @__PURE__ */ new Set([
5500
+ "animate",
5501
+ "aspect",
5502
+ "blur",
5503
+ "breakpoint",
5504
+ "color",
5505
+ "container",
5506
+ "drop",
5507
+ "ease",
5508
+ "font",
5509
+ "inset",
5510
+ "leading",
5511
+ "perspective",
5512
+ "radius",
5513
+ "shadow",
5514
+ "spacing",
5515
+ "text",
5516
+ "tracking"
5517
+ ]);
5518
+ function isTailwindThemeVariable(property) {
5519
+ const namespace = property.slice(2).split("-")[0];
5520
+ return namespace !== void 0 && TAILWIND_THEME_NAMESPACES.has(namespace);
5521
+ }
5442
5522
  function looksLikeEdgeColor(lower) {
5443
5523
  const words = lower.slice(2).split("-");
5444
5524
  return words.some((word) => EDGE_PROPERTY_WORDS.has(word)) && !words.some((word) => NON_COLOR_WORDS.has(word));
5445
5525
  }
5526
+ function looksLikeTextColor(lower) {
5527
+ const words = lower.slice(2).split("-");
5528
+ return words.includes("text") && !words.some((word) => NON_COLOR_WORDS.has(word));
5529
+ }
5446
5530
  var cssVariableNamingRule = {
5447
5531
  meta: {
5448
5532
  schema: [],
@@ -5456,6 +5540,7 @@ var cssVariableNamingRule = {
5456
5540
  Declaration(node) {
5457
5541
  const property = node.property;
5458
5542
  if (!property.startsWith("--")) return;
5543
+ if (isTailwindThemeVariable(property)) return;
5459
5544
  const lower = property.toLowerCase();
5460
5545
  if ((lower.includes("bg") || lower.includes("background")) && !lower.endsWith("-canvas")) {
5461
5546
  context.report({
@@ -5463,7 +5548,7 @@ var cssVariableNamingRule = {
5463
5548
  message: `CSS variable "${property}" looks like a background token; name it --<role>-canvas instead.`
5464
5549
  });
5465
5550
  }
5466
- if (lower.includes("text") && !lower.endsWith("-ink")) {
5551
+ if (looksLikeTextColor(lower) && !lower.endsWith("-ink")) {
5467
5552
  context.report({
5468
5553
  node,
5469
5554
  message: `CSS variable "${property}" looks like a text token; name it --<role>-ink instead.`
@@ -6098,7 +6183,7 @@ var nextjsStackRule = {
6098
6183
  };
6099
6184
 
6100
6185
  // eslint/rules/package-json/vitest-coverage.ts
6101
- import { existsSync as existsSync2, readFileSync as readFileSync9 } from "fs";
6186
+ import { existsSync as existsSync3, readFileSync as readFileSync9 } from "fs";
6102
6187
  import path47 from "path";
6103
6188
  function memberName5(member) {
6104
6189
  return member.name.type === "String" ? member.name.value : member.name.name;
@@ -6124,18 +6209,12 @@ var SOURCE_GLOB_PATTERN2 = /(?:^|[^a-z])(?:[cm]?[jt]sx?)(?:[^a-z]|$)/i;
6124
6209
  var COVERAGE_FLAG_PATTERN = /(?:^|\s)--coverage(?:[=\s]|$)/;
6125
6210
  var RELATED_PATTERN = /\brelated\b/;
6126
6211
  var AUTO_UPDATE_PATTERN = /autoUpdate\s*:\s*true/;
6127
- var CHANGED_FLAG_PATTERN = /--coverage\.changed\b/;
6128
- var PER_FILE_FLAG_PATTERN = /--coverage\.thresholds\.perFile\b/;
6129
- var AUTO_UPDATE_DISABLED_FLAG_PATTERN = /--coverage\.thresholds\.autoUpdate=false\b/;
6130
- function hasEightyOrAboveFlag(command, metric) {
6131
- return new RegExp(`--coverage\\.thresholds\\.${metric}=(?:8\\d|9\\d|100)\\b`).test(command);
6132
- }
6133
6212
  var vitestCoverageRule = {
6134
6213
  meta: {
6135
6214
  schema: [],
6136
6215
  type: "problem",
6137
6216
  docs: {
6138
- description: "Require Vitest unit-test scripts, the V8 provider, a rising coverage threshold, and an 80% floor on staged files via lint-staged."
6217
+ description: "Require Vitest unit-test scripts, the V8 provider, a rising aggregate coverage threshold, and related tests via lint-staged."
6139
6218
  }
6140
6219
  },
6141
6220
  create(context) {
@@ -6174,7 +6253,7 @@ var vitestCoverageRule = {
6174
6253
  message: 'package.json must declare a "test:unit:coverage" script that runs Vitest with coverage.'
6175
6254
  });
6176
6255
  }
6177
- const configName = VITEST_CONFIG_NAMES.find((name) => existsSync2(path47.join(context.cwd, name)));
6256
+ const configName = VITEST_CONFIG_NAMES.find((name) => existsSync3(path47.join(context.cwd, name)));
6178
6257
  if (!configName) {
6179
6258
  context.report({
6180
6259
  node,
@@ -6194,32 +6273,22 @@ var vitestCoverageRule = {
6194
6273
  message: `${configName} must set coverage.thresholds.autoUpdate to true.`
6195
6274
  });
6196
6275
  }
6197
- const changedCoverage = scriptMembers.find((member) => memberName5(member) === "test:unit:coverage:staged");
6198
- const changedCoverageCommand = memberValue3(changedCoverage);
6199
- if (changedCoverageCommand === void 0 || !/\bvitest\b/.test(changedCoverageCommand) || !RELATED_PATTERN.test(changedCoverageCommand) || !COVERAGE_FLAG_PATTERN.test(changedCoverageCommand)) {
6276
+ const stagedTests = scriptMembers.find((member) => memberName5(member) === "test:unit:staged");
6277
+ const stagedTestsCommand = memberValue3(stagedTests);
6278
+ if (stagedTestsCommand === void 0 || !/\bvitest\b/.test(stagedTestsCommand) || !RELATED_PATTERN.test(stagedTestsCommand) || COVERAGE_FLAG_PATTERN.test(stagedTestsCommand)) {
6200
6279
  context.report({
6201
- node: changedCoverage ?? node,
6202
- message: 'package.json must declare a "test:unit:coverage:staged" script that runs vitest related with coverage.'
6280
+ node: stagedTests ?? node,
6281
+ message: 'package.json must declare a "test:unit:staged" script that runs vitest related without coverage.'
6203
6282
  });
6204
6283
  }
6205
6284
  const lintStaged = root.members.find((member) => memberName5(member) === "lint-staged");
6206
- const hasStagedChangedCoverage = lintStaged?.value.type === "Object" && lintStaged.value.members.some(
6207
- (member) => SOURCE_GLOB_PATTERN2.test(memberName5(member)) && stringValues2(member.value).some((command) => command.includes("npm run test:unit:coverage:staged"))
6285
+ const hasStagedTests = lintStaged?.value.type === "Object" && lintStaged.value.members.some(
6286
+ (member) => SOURCE_GLOB_PATTERN2.test(memberName5(member)) && stringValues2(member.value).some((command) => command.includes("npm run test:unit:staged"))
6208
6287
  );
6209
- if (!hasStagedChangedCoverage) {
6288
+ if (!hasStagedTests) {
6210
6289
  context.report({
6211
6290
  node: lintStaged ?? node,
6212
- message: 'package.json lint-staged must run "npm run test:unit:coverage:staged" for staged JavaScript or TypeScript files.'
6213
- });
6214
- }
6215
- const changedCoverageCommandText = changedCoverageCommand ?? "";
6216
- const hasEightyFloor = THRESHOLD_METRICS.every(
6217
- (metric) => hasEightyOrAboveFlag(changedCoverageCommandText, metric)
6218
- );
6219
- if (!CHANGED_FLAG_PATTERN.test(changedCoverageCommandText) || !PER_FILE_FLAG_PATTERN.test(changedCoverageCommandText) || !hasEightyFloor || !AUTO_UPDATE_DISABLED_FLAG_PATTERN.test(changedCoverageCommandText)) {
6220
- context.report({
6221
- node: changedCoverage ?? node,
6222
- message: 'package.json "test:unit:coverage:staged" script must pass --coverage.changed, a --coverage.thresholds.perFile of at least 80 for lines, functions, branches, and statements, and --coverage.thresholds.autoUpdate=false.'
6291
+ message: 'package.json lint-staged must run "npm run test:unit:staged" for staged JavaScript or TypeScript files.'
6223
6292
  });
6224
6293
  }
6225
6294
  }
@@ -6239,7 +6308,7 @@ var nextjsPackageJsonRules = {
6239
6308
  };
6240
6309
 
6241
6310
  // eslint/rules/husky/husky-hook.ts
6242
- import { existsSync as existsSync3, readFileSync as readFileSync10 } from "fs";
6311
+ import { existsSync as existsSync4, readFileSync as readFileSync10 } from "fs";
6243
6312
  import path48 from "path";
6244
6313
  function memberName6(member) {
6245
6314
  return member.name.type === "String" ? member.name.value : member.name.name;
@@ -6259,7 +6328,7 @@ var huskyHookRule = {
6259
6328
  if (root.type !== "Object") return;
6260
6329
  if (!context.filename.endsWith("package.json")) return;
6261
6330
  const hookPath = path48.join(context.cwd, ".husky", "pre-commit");
6262
- if (!existsSync3(hookPath)) {
6331
+ if (!existsSync4(hookPath)) {
6263
6332
  context.report({
6264
6333
  node,
6265
6334
  message: "No .husky/pre-commit hook found. Configure husky to run checks before commits."
@@ -6285,7 +6354,7 @@ var huskyHookRule = {
6285
6354
  context.report({ node, message: ".husky/pre-commit must run npx libyear --limit-major-individual=1." });
6286
6355
  }
6287
6356
  const suppressionsPath = path48.join(context.cwd, "eslint-suppressions.json");
6288
- if (existsSync3(suppressionsPath)) {
6357
+ if (existsSync4(suppressionsPath)) {
6289
6358
  requireNamedScript("lint:prune");
6290
6359
  }
6291
6360
  if (scripts?.value.type === "Object") {
@@ -6308,7 +6377,7 @@ var huskyRules = {
6308
6377
  };
6309
6378
 
6310
6379
  // eslint/rules/vulyk/vulyk-docs.ts
6311
- import { existsSync as existsSync4, readFileSync as readFileSync11 } from "fs";
6380
+ import { existsSync as existsSync5, readFileSync as readFileSync11 } from "fs";
6312
6381
  import path49 from "path";
6313
6382
  var PASIKA_REPO = "Bredansky/pasika";
6314
6383
  var BASE_REQUIRED_DOCS = [
@@ -6344,7 +6413,7 @@ var vulykDocsRule = {
6344
6413
  if (root.type !== "Object") return;
6345
6414
  const projectRoot = path49.dirname(path49.resolve(context.filename));
6346
6415
  const configPath = path49.join(projectRoot, "vulyk.config.ts");
6347
- if (!existsSync4(configPath)) {
6416
+ if (!existsSync5(configPath)) {
6348
6417
  context.report({
6349
6418
  node,
6350
6419
  message: "No vulyk.config.ts found. Run npx vulyk@latest init to create one that tracks the framework's docs."
@@ -6369,7 +6438,7 @@ var vulykDocsRule = {
6369
6438
  }
6370
6439
  }
6371
6440
  const agentsPath = path49.join(projectRoot, "AGENTS.md");
6372
- if (!existsSync4(agentsPath)) {
6441
+ if (!existsSync5(agentsPath)) {
6373
6442
  context.report({
6374
6443
  node,
6375
6444
  message: "No AGENTS.md found. Run npx vulyk@latest agents to generate the agent file that routes to the tracked docs."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,14 +30,14 @@
30
30
  "prepare": "husky",
31
31
  "test:unit": "vitest run",
32
32
  "test:unit:coverage": "vitest run --coverage",
33
- "test:unit:coverage:staged": "vitest related --run --coverage --coverage.changed --coverage.thresholds.perFile --coverage.thresholds.lines=80 --coverage.thresholds.functions=80 --coverage.thresholds.branches=80 --coverage.thresholds.statements=80 --coverage.thresholds.autoUpdate=false",
33
+ "test:unit:staged": "vitest related --run",
34
34
  "typecheck": "tsc --noEmit"
35
35
  },
36
36
  "lint-staged": {
37
37
  "!(*.ts|*.mts|*.cts|*.tsx|*.js|*.cjs|*.mjs|*.jsx)": "npm run format:staged --",
38
38
  "*.ts|*.mts|*.cts|*.tsx|*.js|*.cjs|*.mjs|*.jsx": [
39
39
  "npm run lint:staged --",
40
- "npm run test:unit:coverage:staged --"
40
+ "npm run test:unit:staged --"
41
41
  ]
42
42
  },
43
43
  "dependencies": {