pasika 0.10.99 → 0.10.101
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 +79 -5
- package/package.json +1 -1
|
@@ -3832,6 +3832,7 @@ var huskyHookRule = {
|
|
|
3832
3832
|
return;
|
|
3833
3833
|
}
|
|
3834
3834
|
const content = readFileSync6(hookPath, "utf8");
|
|
3835
|
+
const commands = content.replace(/^[ \t]*#[^\r\n]*$/gm, "");
|
|
3835
3836
|
const scripts = root.members.find((member) => memberName(member) === "scripts");
|
|
3836
3837
|
const scriptNames = new Set(scripts?.value.type === "Object" ? scripts.value.members.map(memberName) : []);
|
|
3837
3838
|
const requireNamedScript = (name) => {
|
|
@@ -3847,6 +3848,52 @@ var huskyHookRule = {
|
|
|
3847
3848
|
}
|
|
3848
3849
|
requireNamedScript("typecheck");
|
|
3849
3850
|
requireNamedScript("test:unit:coverage");
|
|
3851
|
+
const autoStageAll = /^\s*git\s+add\s+(?:--\s+)?(?:-A|--all|-u|\.)(?:\s*(?:#.*)?)?$/m.test(commands);
|
|
3852
|
+
const coverageIndex = commands.indexOf("npm run test:unit:coverage");
|
|
3853
|
+
const coverageGuard = /if\s+!\s+git\s+diff\s+--quiet\s+--\s+vitest\.config\.ts\s*;\s*then[\s\S]*?\bexit\s+1\b[\s\S]*?\bfi\b/.exec(
|
|
3854
|
+
commands
|
|
3855
|
+
);
|
|
3856
|
+
if (coverageIndex >= 0 && (!coverageGuard || coverageGuard.index <= coverageIndex)) {
|
|
3857
|
+
context.report({
|
|
3858
|
+
node,
|
|
3859
|
+
message: ".husky/pre-commit must reject unstaged vitest.config.ts changes after coverage."
|
|
3860
|
+
});
|
|
3861
|
+
}
|
|
3862
|
+
if (autoStageAll || /\bgit\s+add\s+(?:--\s+)?vitest\.config\.ts\b/.test(commands)) {
|
|
3863
|
+
context.report({ node, message: ".husky/pre-commit must not auto-stage vitest.config.ts." });
|
|
3864
|
+
}
|
|
3865
|
+
if (existsSync4(path30.join(context.cwd, "vulyk.config.ts"))) {
|
|
3866
|
+
const syncIndex = commands.search(/\bnpx\s+vulyk\s+sync\b/);
|
|
3867
|
+
const agentsIndex = commands.search(/\bnpx\s+vulyk\s+agents\b/);
|
|
3868
|
+
const stagedIndex = commands.indexOf("lint-staged");
|
|
3869
|
+
if (syncIndex < 0 || agentsIndex <= syncIndex || stagedIndex <= agentsIndex) {
|
|
3870
|
+
context.report({
|
|
3871
|
+
node,
|
|
3872
|
+
message: ".husky/pre-commit must run npx vulyk sync and npx vulyk agents before lint-staged."
|
|
3873
|
+
});
|
|
3874
|
+
}
|
|
3875
|
+
const vulykGuard = /if\s+!\s+git\s+diff\s+--quiet\s+--[\s\S]*?git\s+ls-files\s+--others\s+--exclude-standard\s+--[\s\S]*?\bexit\s+1\b[\s\S]*?\bfi\b/.exec(
|
|
3876
|
+
commands
|
|
3877
|
+
);
|
|
3878
|
+
const tracked = /git diff --quiet -- (?<paths>[^\r\n]*)/.exec(vulykGuard?.[0] ?? "")?.groups?.paths;
|
|
3879
|
+
const untracked = /git ls-files --others --exclude-standard -- (?<paths>[^\r\n)]*)/.exec(
|
|
3880
|
+
vulykGuard?.[0] ?? ""
|
|
3881
|
+
)?.groups?.paths;
|
|
3882
|
+
const outputPaths = ["AGENTS.md", "CLAUDE.md", "docs/managed"];
|
|
3883
|
+
const hasPaths = outputPaths.every((output) => tracked?.includes(output) && untracked?.includes(output));
|
|
3884
|
+
if (!hasPaths || !vulykGuard || vulykGuard.index <= agentsIndex || vulykGuard.index >= stagedIndex) {
|
|
3885
|
+
context.report({
|
|
3886
|
+
node,
|
|
3887
|
+
message: ".husky/pre-commit must reject unstaged and untracked Vulyk-generated files before lint-staged."
|
|
3888
|
+
});
|
|
3889
|
+
}
|
|
3890
|
+
if (autoStageAll || /\bgit\s+add\s+(?:-A\s+|--all\s+|--\s+)?(?:AGENTS\.md|CLAUDE\.md|docs\/managed)\b/.test(commands)) {
|
|
3891
|
+
context.report({
|
|
3892
|
+
node,
|
|
3893
|
+
message: ".husky/pre-commit must not auto-stage Vulyk-generated files."
|
|
3894
|
+
});
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3850
3897
|
if (!content.includes("libyear --limit-major-individual=1")) {
|
|
3851
3898
|
context.report({ node, message: ".husky/pre-commit must run npx libyear --limit-major-individual=1." });
|
|
3852
3899
|
}
|
|
@@ -5315,7 +5362,7 @@ function getSemanticLiteralIndex(index) {
|
|
|
5315
5362
|
|
|
5316
5363
|
// eslint/rules/no-inline-runtime-literal.ts
|
|
5317
5364
|
var DOC4 = "See docs/next-codebase-guide/rules/constants-rule.md";
|
|
5318
|
-
var MESSAGE2 = `Every runtime string or numeric value must use a named constant or enum member, including single-use values.
|
|
5365
|
+
var MESSAGE2 = `Every runtime string or numeric value must use a named constant or enum member, including single-use values. Inline JSX className and canonical locale strings are exempt. ${DOC4}`;
|
|
5319
5366
|
function isClassNameProp(node) {
|
|
5320
5367
|
let current = node;
|
|
5321
5368
|
while (current.parent) {
|
|
@@ -5369,28 +5416,55 @@ function isSyntacticString(node) {
|
|
|
5369
5416
|
}
|
|
5370
5417
|
return false;
|
|
5371
5418
|
}
|
|
5419
|
+
function isRuntimeMockSource(file, root) {
|
|
5420
|
+
const parts = path40.relative(root, file).split(path40.sep);
|
|
5421
|
+
return parts.includes("mocks") && !parts.some((part) => part === "__tests__" || part === "__mocks__") && !/\.(?:test|spec|mock)\.[^.]+$/.test(path40.basename(file));
|
|
5422
|
+
}
|
|
5423
|
+
function isCanonicalLocaleString(node, root, file) {
|
|
5424
|
+
if (path40.relative(root, file) !== path40.join("locales", "index.ts")) return false;
|
|
5425
|
+
let current = node;
|
|
5426
|
+
let depth = 0;
|
|
5427
|
+
while (current.parent?.type === "Property" && current.parent.value === current) {
|
|
5428
|
+
const object = current.parent.parent;
|
|
5429
|
+
if (object?.type !== "ObjectExpression") return false;
|
|
5430
|
+
current = object;
|
|
5431
|
+
depth += 1;
|
|
5432
|
+
}
|
|
5433
|
+
if (depth < 1 || depth > 2) return false;
|
|
5434
|
+
while ((current.parent?.type === "TSAsExpression" || current.parent?.type === "TSSatisfiesExpression") && current.parent.expression === current) {
|
|
5435
|
+
current = current.parent;
|
|
5436
|
+
}
|
|
5437
|
+
const declaration = current.parent;
|
|
5438
|
+
if (declaration?.type !== "VariableDeclarator" || declaration.init !== current) return false;
|
|
5439
|
+
const id = declaration.id;
|
|
5440
|
+
return typeof id === "object" && id !== null && "type" in id && id.type === "Identifier" && "name" in id && id.name === "locales" && declaration.parent?.type === "VariableDeclaration" && declaration.parent.kind === "const" && declaration.parent.parent?.type === "ExportNamedDeclaration";
|
|
5441
|
+
}
|
|
5372
5442
|
var noInlineRuntimeLiteralRule = {
|
|
5373
5443
|
meta: {
|
|
5374
5444
|
schema: [],
|
|
5375
5445
|
type: "problem",
|
|
5376
|
-
docs: { description: "Require named runtime
|
|
5446
|
+
docs: { description: "Require named runtime values except JSX className and canonical locale strings." }
|
|
5377
5447
|
},
|
|
5378
5448
|
create(context) {
|
|
5379
5449
|
const root = sourceRootOf(context);
|
|
5380
5450
|
const file = path40.resolve(context.filename);
|
|
5381
|
-
if (!file.startsWith(root + path40.sep) || !isProductionSource(file, root))
|
|
5451
|
+
if (!file.startsWith(root + path40.sep) || !isProductionSource(file, root) && !isRuntimeMockSource(file, root)) {
|
|
5452
|
+
return {};
|
|
5453
|
+
}
|
|
5382
5454
|
return {
|
|
5383
5455
|
Literal(node) {
|
|
5384
5456
|
if (typeof node.value !== "string" && typeof node.value !== "number" && typeof node.value !== "bigint" && !("bigint" in node && typeof node.bigint === "string")) {
|
|
5385
5457
|
return;
|
|
5386
5458
|
}
|
|
5387
|
-
if (typeof node.value === "string" && isClassNameProp(node) || isNamedValue(node) || isSyntacticString(node)) {
|
|
5459
|
+
if (typeof node.value === "string" && (isClassNameProp(node) || isCanonicalLocaleString(node, root, file)) || isNamedValue(node) || isSyntacticString(node)) {
|
|
5388
5460
|
return;
|
|
5389
5461
|
}
|
|
5390
5462
|
context.report({ node, message: MESSAGE2 });
|
|
5391
5463
|
},
|
|
5392
5464
|
TemplateLiteral(node) {
|
|
5393
|
-
if (isClassNameProp(node) || isNamedValue(node))
|
|
5465
|
+
if (isClassNameProp(node) || isNamedValue(node) || node.expressions.length === 0 && isCanonicalLocaleString(node, root, file)) {
|
|
5466
|
+
return;
|
|
5467
|
+
}
|
|
5394
5468
|
if (node.quasis.every((quasi) => quasi.value.raw === "")) return;
|
|
5395
5469
|
context.report({ node, message: MESSAGE2 });
|
|
5396
5470
|
},
|