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 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/surface-utility` | Repeated canvas+ink combos become a named surface utility |
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
- "surface-utility": _eslint_css.CSSRuleDefinition;
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
- "surface-utility": _eslint_css.CSSRuleDefinition;
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((statement) => statement.expression && ts.isExpression(statement.expression) ? rootFromExpression(statement.expression) : void 0).filter((root) => root !== void 0);
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(["onClick", "onChange", "onSubmit", "href", "onKeyDown", "onKeyUp", "onFocus", "onBlur", "htmlFor"]);
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(name);
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
- try {
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 custom utility."
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 custom Tailwind utility for it. See docs/next-tailwind-guide/rules/theme-and-utility-definition-rule.md`
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: "Overview must exist, contain at most two sentences, and not link to other documents.",
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 background variables to end in -canvas and text variables to end in -ink."
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/surface-utility.ts
5418
- var surfaceUtilityRule = {
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 repeated canvas+ink combinations to become a *-surface utility."
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 (!classes.some((name) => name.includes("canvas")) || !classes.some((name) => name.includes("ink"))) {
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 *-surface custom Tailwind utility for it.`
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
- "surface-utility": surfaceUtilityRule,
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.6.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.48",
65
+ "zirka": "0.0.65",
66
66
  "zod": "4.4.3"
67
67
  },
68
68
  "peerDependencies": {