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.
- package/dist/eslint/pasika/index.js +112 -43
- package/package.json +3 -3
|
@@ -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_
|
|
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 {
|
|
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 (!
|
|
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", "
|
|
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
|
|
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
|
|
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
|
|
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) =>
|
|
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
|
|
6198
|
-
const
|
|
6199
|
-
if (
|
|
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:
|
|
6202
|
-
message: 'package.json must declare a "test:unit:
|
|
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
|
|
6207
|
-
(member) => SOURCE_GLOB_PATTERN2.test(memberName5(member)) && stringValues2(member.value).some((command) => command.includes("npm run test:unit:
|
|
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 (!
|
|
6288
|
+
if (!hasStagedTests) {
|
|
6210
6289
|
context.report({
|
|
6211
6290
|
node: lintStaged ?? node,
|
|
6212
|
-
message: 'package.json lint-staged must run "npm run test:unit:
|
|
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
|
|
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 (!
|
|
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 (
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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.
|
|
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:
|
|
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:
|
|
40
|
+
"npm run test:unit:staged --"
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|