pasika 0.4.2 → 0.4.3
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 +63 -52
- package/package.json +1 -1
|
@@ -66,11 +66,12 @@ function componentFromVariable(node) {
|
|
|
66
66
|
smart: containsSmartCall(initializer)
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
function parseComponentInfo(text, filename) {
|
|
69
|
+
function parseComponentInfo(text, filename, options) {
|
|
70
70
|
const sourceFile = ts.createSourceFile(path.resolve(filename), text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
71
71
|
const components = [];
|
|
72
72
|
for (const statement of sourceFile.statements) {
|
|
73
|
-
|
|
73
|
+
const exported = isExported(statement);
|
|
74
|
+
if (!exported && !options?.includeNonExported) continue;
|
|
74
75
|
if (ts.isFunctionDeclaration(statement)) {
|
|
75
76
|
const component = componentFromFunction(statement);
|
|
76
77
|
if (component) components.push(component);
|
|
@@ -350,59 +351,26 @@ var importBoundariesRule = {
|
|
|
350
351
|
};
|
|
351
352
|
|
|
352
353
|
// eslint/rules/no-mixed-concerns.ts
|
|
353
|
-
function getExportName(declaration) {
|
|
354
|
-
if (declaration?.type === "FunctionDeclaration" && declaration.id) {
|
|
355
|
-
return declaration.id.name ?? null;
|
|
356
|
-
}
|
|
357
|
-
if (declaration?.type === "VariableDeclaration") {
|
|
358
|
-
const declarator = declaration.declarations?.[0];
|
|
359
|
-
if (declarator?.id?.type === "Identifier" && (declarator.init?.type === "ArrowFunctionExpression" || declarator.init?.type === "FunctionExpression")) {
|
|
360
|
-
return declarator.id.name ?? null;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
if (declaration?.type === "ArrowFunctionExpression") {
|
|
364
|
-
return "default";
|
|
365
|
-
}
|
|
366
|
-
if (declaration?.type === "FunctionExpression" && declaration.id) {
|
|
367
|
-
return declaration.id.name ?? null;
|
|
368
|
-
}
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
371
354
|
var noMixedConcernsRule = {
|
|
372
355
|
meta: {
|
|
373
356
|
schema: [],
|
|
374
357
|
type: "problem",
|
|
375
358
|
docs: {
|
|
376
|
-
description: "Enforce one
|
|
359
|
+
description: "Enforce one React component per .tsx file, counting private components."
|
|
377
360
|
}
|
|
378
361
|
},
|
|
379
362
|
create(context) {
|
|
380
363
|
if (!context.filename.endsWith(".tsx")) return {};
|
|
381
|
-
|
|
382
|
-
const extraExports = [];
|
|
383
|
-
function registerExport(name) {
|
|
384
|
-
exportedComponentCount++;
|
|
385
|
-
if (exportedComponentCount > 1) {
|
|
386
|
-
extraExports.push({ name });
|
|
387
|
-
}
|
|
388
|
-
}
|
|
364
|
+
const sourceCode = context.sourceCode.text;
|
|
389
365
|
return {
|
|
390
|
-
ExportNamedDeclaration(node) {
|
|
391
|
-
const name = getExportName(node.declaration);
|
|
392
|
-
if (name) registerExport(name);
|
|
393
|
-
},
|
|
394
|
-
ExportDefaultDeclaration(node) {
|
|
395
|
-
const name = getExportName(node.declaration);
|
|
396
|
-
if (name) registerExport(name);
|
|
397
|
-
},
|
|
398
366
|
"Program:exit"() {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
}
|
|
367
|
+
const components = parseComponentInfo(sourceCode, context.filename, { includeNonExported: true });
|
|
368
|
+
if (components.length <= 1) return;
|
|
369
|
+
for (const extra of components.slice(1)) {
|
|
370
|
+
context.report({
|
|
371
|
+
loc: { line: 1, column: 0 },
|
|
372
|
+
message: `File defines multiple components. "${extra.name}" is an extra component. Move it to its own file. Each component file MUST define exactly one component. See docs/code-organization-guide/rules/no-mixed-concerns-rule.md`
|
|
373
|
+
});
|
|
406
374
|
}
|
|
407
375
|
}
|
|
408
376
|
};
|
|
@@ -885,7 +853,9 @@ function isOuterLayoutClass(className) {
|
|
|
885
853
|
function stringArguments(node) {
|
|
886
854
|
if (node?.type === "Literal" && typeof node.value === "string") return [node.value];
|
|
887
855
|
if (node?.type === "TemplateLiteral") return node.quasis.map((quasi) => quasi.value.raw);
|
|
888
|
-
if (node?.type === "ConditionalExpression")
|
|
856
|
+
if (node?.type === "ConditionalExpression") {
|
|
857
|
+
return [...stringArguments(node.consequent), ...stringArguments(node.alternate)];
|
|
858
|
+
}
|
|
889
859
|
if (node?.type === "LogicalExpression") return [...stringArguments(node.left), ...stringArguments(node.right)];
|
|
890
860
|
if (node?.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "cn") {
|
|
891
861
|
return node.arguments.flatMap((argument) => stringArguments(argument));
|
|
@@ -901,7 +871,10 @@ var enforceCnMergeRule = {
|
|
|
901
871
|
const element = node.parent.parent;
|
|
902
872
|
const isComponentProp = isComponentName(element.openingElement?.name);
|
|
903
873
|
if (isComponentProp && attributeName !== "className" && attributeName.endsWith("ClassName")) {
|
|
904
|
-
context.report({
|
|
874
|
+
context.report({
|
|
875
|
+
node,
|
|
876
|
+
message: "Expose appearance through typed variant props instead of separate internal class-name props. See docs/styling-guide/rules/class-composition-rule.md"
|
|
877
|
+
});
|
|
905
878
|
return;
|
|
906
879
|
}
|
|
907
880
|
if (attributeName !== "className" && attributeName !== "class") return;
|
|
@@ -910,31 +883,60 @@ var enforceCnMergeRule = {
|
|
|
910
883
|
if (isComponentProp && attributeName === "className") {
|
|
911
884
|
const expression = valueNode.type === "JSXExpressionContainer" ? valueNode.expression : valueNode;
|
|
912
885
|
const invalidClass = stringArguments(expression).flatMap((value) => value.split(/\s+/).filter(Boolean)).find((className) => !isOuterLayoutClass(className));
|
|
913
|
-
if (invalidClass)
|
|
886
|
+
if (invalidClass) {
|
|
887
|
+
context.report({
|
|
888
|
+
node,
|
|
889
|
+
message: `Passed className contains non-layout utility "${invalidClass}". Expose appearance through typed variant props. See docs/styling-guide/rules/class-composition-rule.md`
|
|
890
|
+
});
|
|
891
|
+
}
|
|
914
892
|
return;
|
|
915
893
|
}
|
|
916
894
|
if (valueNode.type === "Literal" && typeof valueNode.value === "string") {
|
|
917
|
-
if (classCount(valueNode.value) > 5)
|
|
895
|
+
if (classCount(valueNode.value) > 5) {
|
|
896
|
+
context.report({
|
|
897
|
+
node,
|
|
898
|
+
message: "Static className with more than 5 classes must use cn() with grouped string literals. See docs/styling-guide/rules/class-composition-rule.md"
|
|
899
|
+
});
|
|
900
|
+
}
|
|
918
901
|
return;
|
|
919
902
|
}
|
|
920
903
|
if (valueNode.type !== "JSXExpressionContainer") return;
|
|
921
904
|
const expr = valueNode.expression;
|
|
922
905
|
if (expr.type === "BinaryExpression" && expr.operator === "+") {
|
|
923
|
-
context.report({
|
|
906
|
+
context.report({
|
|
907
|
+
node,
|
|
908
|
+
message: "Use cn() instead of + operator for className. See docs/styling-guide/rules/class-composition-rule.md"
|
|
909
|
+
});
|
|
924
910
|
return;
|
|
925
911
|
}
|
|
926
912
|
if (expr.type === "TemplateLiteral" && expr.expressions.length > 0) {
|
|
927
|
-
context.report({
|
|
913
|
+
context.report({
|
|
914
|
+
node,
|
|
915
|
+
message: "Use cn() instead of template literals with conditionals for className. See docs/styling-guide/rules/class-composition-rule.md"
|
|
916
|
+
});
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (expr.type === "LogicalExpression" || expr.type === "ConditionalExpression") {
|
|
920
|
+
context.report({
|
|
921
|
+
node,
|
|
922
|
+
message: "Use cn() for conditional classes in className. See docs/styling-guide/rules/class-composition-rule.md"
|
|
923
|
+
});
|
|
928
924
|
return;
|
|
929
925
|
}
|
|
930
926
|
if (expr.type === "Literal" && typeof expr.value === "string" && classCount(expr.value) > 5) {
|
|
931
|
-
context.report({
|
|
927
|
+
context.report({
|
|
928
|
+
node,
|
|
929
|
+
message: "Static className with more than 5 classes must use cn() with grouped string literals. See docs/styling-guide/rules/class-composition-rule.md"
|
|
930
|
+
});
|
|
932
931
|
return;
|
|
933
932
|
}
|
|
934
933
|
if (expr.type === "CallExpression" && expr.callee.type === "Identifier" && expr.callee.name === "cn") {
|
|
935
934
|
for (const arg of expr.arguments) {
|
|
936
935
|
if (arg.type === "Literal" && typeof arg.value === "string" && classCount(arg.value) > 5) {
|
|
937
|
-
context.report({
|
|
936
|
+
context.report({
|
|
937
|
+
node: arg,
|
|
938
|
+
message: "Each cn() string argument must contain at most 5 class names. Group by styling concern. See docs/styling-guide/rules/class-composition-rule.md"
|
|
939
|
+
});
|
|
938
940
|
}
|
|
939
941
|
}
|
|
940
942
|
}
|
|
@@ -3284,6 +3286,7 @@ var ROLE_POSTFIXES = /* @__PURE__ */ new Set([
|
|
|
3284
3286
|
"ScrollBar"
|
|
3285
3287
|
]);
|
|
3286
3288
|
var CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
|
|
3289
|
+
var ENGLISH = /^[A-Za-z0-9_]*$/;
|
|
3287
3290
|
function isLocalesFile2(filename) {
|
|
3288
3291
|
const segments = path31.resolve(filename).split(path31.sep);
|
|
3289
3292
|
const srcIdx = segments.lastIndexOf("src");
|
|
@@ -3298,6 +3301,14 @@ function reportFor(context, node, message) {
|
|
|
3298
3301
|
context.report({ node, message });
|
|
3299
3302
|
}
|
|
3300
3303
|
function checkKey(context, node, name) {
|
|
3304
|
+
if (!ENGLISH.test(name)) {
|
|
3305
|
+
reportFor(
|
|
3306
|
+
context,
|
|
3307
|
+
node,
|
|
3308
|
+
`Locale key "${name}" must be English; write it in the Latin alphabet. See docs/code-organization-guide/rules/locales-rule.md`
|
|
3309
|
+
);
|
|
3310
|
+
return;
|
|
3311
|
+
}
|
|
3301
3312
|
if (!CAMEL_CASE.test(name)) {
|
|
3302
3313
|
reportFor(
|
|
3303
3314
|
context,
|