pasika 0.6.0 → 0.7.0
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 +1 -1
- package/dist/eslint/pasika/index.d.ts +4 -2
- package/dist/eslint/pasika/index.js +138 -29
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -191,7 +191,7 @@ Applied to `src/**/globals.css` (and other stylesheets) through `@eslint/css` wi
|
|
|
191
191
|
| `pasika/stylesheet-ordering` | Imports → `@custom-variant` → `:root` → `@theme` → `@utility` → `@layer base` |
|
|
192
192
|
| `pasika/css-variable-naming` | Background vars named `--<role>-canvas`, text vars `--<role>-ink` |
|
|
193
193
|
| `pasika/custom-utility-apply` | `@utility` blocks use `@apply` |
|
|
194
|
-
| `pasika/
|
|
194
|
+
| `pasika/skin-utility` | Repeated style combinations become a named skin utility |
|
|
195
195
|
| `pasika/theme-variable-namespace` | Utility class groups share a namespace prefix |
|
|
196
196
|
| `pasika/css-entry-point` † | One global entry, imported by one module, project CSS only in a stylesheet the entry imports directly |
|
|
197
197
|
| `pasika/unused-utility` | A custom `@utility` no source file references is reported as dead |
|
|
@@ -98,6 +98,7 @@ declare const documentationRules: {
|
|
|
98
98
|
"no-nested-how-to": _eslint_markdown.MarkdownRuleDefinition;
|
|
99
99
|
"glossary-term-linking": _eslint_markdown.MarkdownRuleDefinition;
|
|
100
100
|
"guide-mentions-documents": _eslint_markdown.MarkdownRuleDefinition;
|
|
101
|
+
"guide-section-shape": _eslint_markdown.MarkdownRuleDefinition;
|
|
101
102
|
};
|
|
102
103
|
|
|
103
104
|
declare const tailwindRules: {
|
|
@@ -108,7 +109,7 @@ declare const tailwindRules: {
|
|
|
108
109
|
"stylesheet-ordering": _eslint_css.CSSRuleDefinition;
|
|
109
110
|
"css-variable-naming": _eslint_css.CSSRuleDefinition;
|
|
110
111
|
"custom-utility-apply": _eslint_css.CSSRuleDefinition;
|
|
111
|
-
"
|
|
112
|
+
"skin-utility": _eslint_css.CSSRuleDefinition;
|
|
112
113
|
"theme-variable-namespace": _eslint_css.CSSRuleDefinition;
|
|
113
114
|
"css-entry-point": _eslint_css.CSSRuleDefinition;
|
|
114
115
|
"global-stylesheet": _eslint_css.CSSRuleDefinition;
|
|
@@ -158,7 +159,7 @@ declare const pasikaPlugin: {
|
|
|
158
159
|
"stylesheet-ordering": _eslint_css.CSSRuleDefinition;
|
|
159
160
|
"css-variable-naming": _eslint_css.CSSRuleDefinition;
|
|
160
161
|
"custom-utility-apply": _eslint_css.CSSRuleDefinition;
|
|
161
|
-
"
|
|
162
|
+
"skin-utility": _eslint_css.CSSRuleDefinition;
|
|
162
163
|
"theme-variable-namespace": _eslint_css.CSSRuleDefinition;
|
|
163
164
|
"css-entry-point": _eslint_css.CSSRuleDefinition;
|
|
164
165
|
"global-stylesheet": _eslint_css.CSSRuleDefinition;
|
|
@@ -186,6 +187,7 @@ declare const pasikaPlugin: {
|
|
|
186
187
|
"no-nested-how-to": _eslint_markdown.MarkdownRuleDefinition;
|
|
187
188
|
"glossary-term-linking": _eslint_markdown.MarkdownRuleDefinition;
|
|
188
189
|
"guide-mentions-documents": _eslint_markdown.MarkdownRuleDefinition;
|
|
190
|
+
"guide-section-shape": _eslint_markdown.MarkdownRuleDefinition;
|
|
189
191
|
"filename-case": eslint.Rule.RuleModule;
|
|
190
192
|
"import-boundaries": eslint.Rule.RuleModule;
|
|
191
193
|
"named-exports": eslint.Rule.RuleModule;
|
|
@@ -174,7 +174,9 @@ function findSimpleRoot(component, _text, _filename) {
|
|
|
174
174
|
};
|
|
175
175
|
visit(body);
|
|
176
176
|
const rendered = returns.filter((statement) => statement.expression?.kind !== ts.SyntaxKind.NullKeyword);
|
|
177
|
-
const roots = rendered.map(
|
|
177
|
+
const roots = rendered.map(
|
|
178
|
+
(statement) => statement.expression && ts.isExpression(statement.expression) ? rootFromExpression(statement.expression) : void 0
|
|
179
|
+
).filter((root) => root !== void 0);
|
|
178
180
|
if (roots.length !== rendered.length || roots.length === 0) return void 0;
|
|
179
181
|
const firstTag = roots[0]?.tagName;
|
|
180
182
|
if (roots.some((root) => root.tagName !== firstTag)) return void 0;
|
|
@@ -2302,7 +2304,17 @@ var NEXT_ROUTING_FILES3 = /* @__PURE__ */ new Set([
|
|
|
2302
2304
|
"sitemap",
|
|
2303
2305
|
"twitter-image"
|
|
2304
2306
|
]);
|
|
2305
|
-
var INTERACTIVE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
2307
|
+
var INTERACTIVE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
2308
|
+
"onClick",
|
|
2309
|
+
"onChange",
|
|
2310
|
+
"onSubmit",
|
|
2311
|
+
"href",
|
|
2312
|
+
"onKeyDown",
|
|
2313
|
+
"onKeyUp",
|
|
2314
|
+
"onFocus",
|
|
2315
|
+
"onBlur",
|
|
2316
|
+
"htmlFor"
|
|
2317
|
+
]);
|
|
2306
2318
|
function tagName(node) {
|
|
2307
2319
|
const name = node.openingElement?.name;
|
|
2308
2320
|
if (name?.type !== "JSXIdentifier") return void 0;
|
|
@@ -2375,7 +2387,9 @@ function isDecorative(node) {
|
|
|
2375
2387
|
return !meaningfulText && !isContentElement(name);
|
|
2376
2388
|
}
|
|
2377
2389
|
function isContentElement(name) {
|
|
2378
|
-
return /^(?:h[1-6]|p|li|dt|dd|blockquote|pre|code|figcaption|caption|th|td|form|fieldset|select|textarea)$/.test(
|
|
2390
|
+
return /^(?:h[1-6]|p|li|dt|dd|blockquote|pre|code|figcaption|caption|th|td|form|fieldset|select|textarea)$/.test(
|
|
2391
|
+
name
|
|
2392
|
+
);
|
|
2379
2393
|
}
|
|
2380
2394
|
function isSoleContentOfWrapper(node) {
|
|
2381
2395
|
const parent = node.parent;
|
|
@@ -3667,11 +3681,7 @@ function combosFor(index) {
|
|
|
3667
3681
|
const files = [...index.modules.keys()].filter((file) => file.endsWith(".tsx") || file.endsWith(".jsx")).sort((left, right) => left.localeCompare(right));
|
|
3668
3682
|
let fingerprint2 = "";
|
|
3669
3683
|
for (const file of files) {
|
|
3670
|
-
|
|
3671
|
-
fingerprint2 += String(statSync3(file).mtimeMs);
|
|
3672
|
-
} catch {
|
|
3673
|
-
fingerprint2 += "0";
|
|
3674
|
-
}
|
|
3684
|
+
fingerprint2 += String(statSync3(file).mtimeMs);
|
|
3675
3685
|
}
|
|
3676
3686
|
if (comboCache?.sourceRoot === index.sourceRoot && comboCache.fingerprint === fingerprint2) {
|
|
3677
3687
|
return comboCache.combos;
|
|
@@ -3696,7 +3706,7 @@ var sharedStyleDedupRule = {
|
|
|
3696
3706
|
schema: [],
|
|
3697
3707
|
type: "problem",
|
|
3698
3708
|
docs: {
|
|
3699
|
-
description: "Require a className combination used by two or more components to become a named
|
|
3709
|
+
description: "Require a className combination used by two or more components to become a named *-skin utility."
|
|
3700
3710
|
}
|
|
3701
3711
|
},
|
|
3702
3712
|
create(context) {
|
|
@@ -3715,7 +3725,7 @@ var sharedStyleDedupRule = {
|
|
|
3715
3725
|
context.report({
|
|
3716
3726
|
node,
|
|
3717
3727
|
loc: { line: 1, column: 0 },
|
|
3718
|
-
message: `Class combination "${combo}" is used by ${String(users.size)} components and should change together; create a named
|
|
3728
|
+
message: `Class combination "${combo}" is used by ${String(users.size)} components and should change together; create a named *-skin Tailwind utility for it. See docs/next-tailwind-guide/rules/theme-and-utility-definition-rule.md`
|
|
3719
3729
|
});
|
|
3720
3730
|
}
|
|
3721
3731
|
}
|
|
@@ -4153,7 +4163,7 @@ var overviewRule = {
|
|
|
4153
4163
|
meta: {
|
|
4154
4164
|
type: "problem",
|
|
4155
4165
|
docs: {
|
|
4156
|
-
description: "
|
|
4166
|
+
description: "Required documentation overviews must exist, contain at most two sentences, and not link to other documents.",
|
|
4157
4167
|
recommended: true
|
|
4158
4168
|
}
|
|
4159
4169
|
},
|
|
@@ -4196,6 +4206,41 @@ var overviewRule = {
|
|
|
4196
4206
|
if (!found) {
|
|
4197
4207
|
context.report({ node, message: "no overview follows the title" });
|
|
4198
4208
|
}
|
|
4209
|
+
const validateHeadedOverview = (heading, index, label) => {
|
|
4210
|
+
const overview = node.children[index + 1];
|
|
4211
|
+
if (overview?.type !== "paragraph") {
|
|
4212
|
+
context.report({
|
|
4213
|
+
node: heading,
|
|
4214
|
+
message: `no overview follows ${label}`
|
|
4215
|
+
});
|
|
4216
|
+
return;
|
|
4217
|
+
}
|
|
4218
|
+
if (containsLink(overview)) {
|
|
4219
|
+
context.report({
|
|
4220
|
+
node: overview,
|
|
4221
|
+
message: `overview of ${label} links another document`
|
|
4222
|
+
});
|
|
4223
|
+
}
|
|
4224
|
+
const sectionSentenceCount = countSentences(getTextContent(overview).trim());
|
|
4225
|
+
if (sectionSentenceCount > 2) {
|
|
4226
|
+
context.report({
|
|
4227
|
+
node: overview,
|
|
4228
|
+
message: `overview of ${label} uses ${String(sectionSentenceCount)} sentences, at most two are allowed`
|
|
4229
|
+
});
|
|
4230
|
+
}
|
|
4231
|
+
};
|
|
4232
|
+
const isGuide = filename.endsWith("-guide.md");
|
|
4233
|
+
const isReference = filename.endsWith("-reference.md");
|
|
4234
|
+
if (!isGuide && !isReference) return;
|
|
4235
|
+
for (const [index, child] of node.children.entries()) {
|
|
4236
|
+
if (child.type !== "heading" || child.depth === 1) continue;
|
|
4237
|
+
const title = getTextContent(child).trim();
|
|
4238
|
+
if (isGuide && child.depth === 2 && /^How To \S/.test(title)) {
|
|
4239
|
+
validateHeadedOverview(child, index, `guide section "${title}"`);
|
|
4240
|
+
} else if (isReference) {
|
|
4241
|
+
validateHeadedOverview(child, index, `reference block "${title}"`);
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4199
4244
|
}
|
|
4200
4245
|
};
|
|
4201
4246
|
}
|
|
@@ -5049,6 +5094,56 @@ var guideMentionsDocumentsRule = {
|
|
|
5049
5094
|
}
|
|
5050
5095
|
};
|
|
5051
5096
|
|
|
5097
|
+
// eslint/rules/documentation/guide-section-shape.ts
|
|
5098
|
+
var guideSectionShapeRule = {
|
|
5099
|
+
meta: {
|
|
5100
|
+
type: "problem",
|
|
5101
|
+
docs: {
|
|
5102
|
+
description: "Guide sections use How To headings, an overview, and numbered steps.",
|
|
5103
|
+
recommended: true
|
|
5104
|
+
}
|
|
5105
|
+
},
|
|
5106
|
+
create(context) {
|
|
5107
|
+
return {
|
|
5108
|
+
root(node) {
|
|
5109
|
+
if (!getFilename(context).endsWith("-guide.md")) return;
|
|
5110
|
+
const sections = [];
|
|
5111
|
+
for (const [index, child] of node.children.entries()) {
|
|
5112
|
+
if (child.type !== "heading" || child.depth === 1) continue;
|
|
5113
|
+
sections.push({ heading: child, index, title: getTextContent(child).trim() });
|
|
5114
|
+
}
|
|
5115
|
+
if (sections.length === 0) {
|
|
5116
|
+
context.report({ node, message: "guide must contain at least one How To section" });
|
|
5117
|
+
return;
|
|
5118
|
+
}
|
|
5119
|
+
for (const [sectionIndex, section] of sections.entries()) {
|
|
5120
|
+
if (section.heading.depth !== 2) {
|
|
5121
|
+
context.report({
|
|
5122
|
+
node: section.heading,
|
|
5123
|
+
message: `guide section heading "${section.title}" must be level two`
|
|
5124
|
+
});
|
|
5125
|
+
}
|
|
5126
|
+
if (!/^How To \S/.test(section.title)) {
|
|
5127
|
+
context.report({
|
|
5128
|
+
node: section.heading,
|
|
5129
|
+
message: `guide section heading "${section.title}" must start with "How To "`
|
|
5130
|
+
});
|
|
5131
|
+
}
|
|
5132
|
+
const nextSection = sections[sectionIndex + 1];
|
|
5133
|
+
const content = node.children.slice(section.index + 1, nextSection?.index ?? node.children.length);
|
|
5134
|
+
const hasOverviewThenNumberedList = content.length === 2 && content[0]?.type === "paragraph" && content[1]?.type === "list" && content[1].ordered === true;
|
|
5135
|
+
if (!hasOverviewThenNumberedList) {
|
|
5136
|
+
context.report({
|
|
5137
|
+
node: section.heading,
|
|
5138
|
+
message: `guide section "${section.title}" must consist of its overview followed by one numbered list`
|
|
5139
|
+
});
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
};
|
|
5144
|
+
}
|
|
5145
|
+
};
|
|
5146
|
+
|
|
5052
5147
|
// eslint/rules/documentation/index.ts
|
|
5053
5148
|
var documentationRules = {
|
|
5054
5149
|
"doc-kind-suffix": docKindSuffixRule,
|
|
@@ -5073,7 +5168,8 @@ var documentationRules = {
|
|
|
5073
5168
|
"guide-link-anchors": guideLinkAnchorsRule,
|
|
5074
5169
|
"no-nested-how-to": noNestedHowToRule,
|
|
5075
5170
|
"glossary-term-linking": glossaryTermLinkingRule,
|
|
5076
|
-
"guide-mentions-documents": guideMentionsDocumentsRule
|
|
5171
|
+
"guide-mentions-documents": guideMentionsDocumentsRule,
|
|
5172
|
+
"guide-section-shape": guideSectionShapeRule
|
|
5077
5173
|
};
|
|
5078
5174
|
|
|
5079
5175
|
// eslint/rules/tailwind/helpers.ts
|
|
@@ -5341,12 +5437,18 @@ var stylesheetOrderingRule = {
|
|
|
5341
5437
|
};
|
|
5342
5438
|
|
|
5343
5439
|
// 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"]);
|
|
5442
|
+
function looksLikeEdgeColor(lower) {
|
|
5443
|
+
const words = lower.slice(2).split("-");
|
|
5444
|
+
return words.some((word) => EDGE_PROPERTY_WORDS.has(word)) && !words.some((word) => NON_COLOR_WORDS.has(word));
|
|
5445
|
+
}
|
|
5344
5446
|
var cssVariableNamingRule = {
|
|
5345
5447
|
meta: {
|
|
5346
5448
|
schema: [],
|
|
5347
5449
|
type: "problem",
|
|
5348
5450
|
docs: {
|
|
5349
|
-
description: "Require
|
|
5451
|
+
description: "Require property-specific color variables to use canvas, ink, and edge role suffixes."
|
|
5350
5452
|
}
|
|
5351
5453
|
},
|
|
5352
5454
|
create(context) {
|
|
@@ -5367,6 +5469,12 @@ var cssVariableNamingRule = {
|
|
|
5367
5469
|
message: `CSS variable "${property}" looks like a text token; name it --<role>-ink instead.`
|
|
5368
5470
|
});
|
|
5369
5471
|
}
|
|
5472
|
+
if (looksLikeEdgeColor(lower) && !lower.endsWith("-edge")) {
|
|
5473
|
+
context.report({
|
|
5474
|
+
node,
|
|
5475
|
+
message: `CSS variable "${property}" looks like a boundary token; name it --<role>-edge instead.`
|
|
5476
|
+
});
|
|
5477
|
+
}
|
|
5370
5478
|
}
|
|
5371
5479
|
};
|
|
5372
5480
|
}
|
|
@@ -5414,24 +5522,31 @@ var customUtilityApplyRule = {
|
|
|
5414
5522
|
}
|
|
5415
5523
|
};
|
|
5416
5524
|
|
|
5417
|
-
// eslint/rules/tailwind/
|
|
5418
|
-
var
|
|
5525
|
+
// eslint/rules/tailwind/skin-utility.ts
|
|
5526
|
+
var skinUtilityRule = {
|
|
5419
5527
|
meta: {
|
|
5420
5528
|
schema: [],
|
|
5421
5529
|
type: "problem",
|
|
5422
5530
|
docs: {
|
|
5423
|
-
description: "Require
|
|
5531
|
+
description: "Require reusable style combinations to become *-skin utilities."
|
|
5424
5532
|
}
|
|
5425
5533
|
},
|
|
5426
5534
|
create(context) {
|
|
5427
5535
|
return {
|
|
5428
5536
|
"StyleSheet:exit"(node) {
|
|
5537
|
+
for (const utility of atrulesNamed(node, "utility")) {
|
|
5538
|
+
const name = preludeIdentifiers(utility)[0];
|
|
5539
|
+
const applied = new Set(atrulesNamed(utility, "apply").flatMap((apply) => preludeIdentifiers(apply)));
|
|
5540
|
+
if (applied.size < 2 || name?.endsWith("-skin") === true) continue;
|
|
5541
|
+
context.report({
|
|
5542
|
+
node: utility,
|
|
5543
|
+
message: `Custom utility "${name ?? "unknown"}" combines ${String(applied.size)} styles; use the *-skin suffix.`
|
|
5544
|
+
});
|
|
5545
|
+
}
|
|
5429
5546
|
const combinations = /* @__PURE__ */ new Map();
|
|
5430
5547
|
for (const apply of atrulesNamed(node, "apply")) {
|
|
5431
5548
|
const classes = preludeIdentifiers(apply);
|
|
5432
|
-
if (
|
|
5433
|
-
continue;
|
|
5434
|
-
}
|
|
5549
|
+
if (new Set(classes).size < 2) continue;
|
|
5435
5550
|
const key = [...classes].sort().join(" ");
|
|
5436
5551
|
combinations.set(key, (combinations.get(key) ?? 0) + 1);
|
|
5437
5552
|
}
|
|
@@ -5439,7 +5554,7 @@ var surfaceUtilityRule = {
|
|
|
5439
5554
|
if (count < 2) continue;
|
|
5440
5555
|
context.report({
|
|
5441
5556
|
node,
|
|
5442
|
-
message: `Combination "${combo}" appears ${String(count)} times. Create a *-
|
|
5557
|
+
message: `Combination "${combo}" appears ${String(count)} times. Create a *-skin custom Tailwind utility for it.`
|
|
5443
5558
|
});
|
|
5444
5559
|
}
|
|
5445
5560
|
}
|
|
@@ -5739,7 +5854,7 @@ var tailwindRules = {
|
|
|
5739
5854
|
"stylesheet-ordering": stylesheetOrderingRule,
|
|
5740
5855
|
"css-variable-naming": cssVariableNamingRule,
|
|
5741
5856
|
"custom-utility-apply": customUtilityApplyRule,
|
|
5742
|
-
"
|
|
5857
|
+
"skin-utility": skinUtilityRule,
|
|
5743
5858
|
"theme-variable-namespace": themeVariableNamespaceRule,
|
|
5744
5859
|
"css-entry-point": cssEntryPointRule,
|
|
5745
5860
|
"global-stylesheet": globalStylesheetRule,
|
|
@@ -6374,9 +6489,7 @@ var tailwindStructureRules = {
|
|
|
6374
6489
|
},
|
|
6375
6490
|
language: "css/css",
|
|
6376
6491
|
languageOptions: { tolerant: true },
|
|
6377
|
-
rules: Object.fromEntries(
|
|
6378
|
-
tailwindRuleIds.filter((id) => id !== "pasika/css-entry-point").map((id) => [id, "error"])
|
|
6379
|
-
)
|
|
6492
|
+
rules: Object.fromEntries(tailwindRuleIds.filter((id) => id !== "pasika/css-entry-point").map((id) => [id, "error"]))
|
|
6380
6493
|
};
|
|
6381
6494
|
var tailwindImportGraph = {
|
|
6382
6495
|
files: ["src/**/*.css"],
|
|
@@ -6424,11 +6537,7 @@ var documentationConfig = {
|
|
|
6424
6537
|
language: "markdown/gfm",
|
|
6425
6538
|
rules: Object.fromEntries(documentationRuleIds.map((id) => [id, "error"]))
|
|
6426
6539
|
};
|
|
6427
|
-
var pasikaApp = [
|
|
6428
|
-
pasikaAppPackageJsonConfig,
|
|
6429
|
-
zirkaConfig,
|
|
6430
|
-
documentationConfig
|
|
6431
|
-
];
|
|
6540
|
+
var pasikaApp = [pasikaAppPackageJsonConfig, zirkaConfig, documentationConfig];
|
|
6432
6541
|
var pasikaNextjsAppWithDiagnostic = (preset) => {
|
|
6433
6542
|
let checked = false;
|
|
6434
6543
|
return new Proxy(preset, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pasika",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Reusable agent setup package",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"tsx": "4.23.12",
|
|
63
63
|
"typescript": "6.0.3",
|
|
64
64
|
"vitest": "5.0.0",
|
|
65
|
-
"zirka": "0.0.
|
|
65
|
+
"zirka": "0.0.65",
|
|
66
66
|
"zod": "4.4.3"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|