pasika 0.10.33 → 0.10.35

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.
@@ -214,7 +214,7 @@ declare const pasikaPlugin: {
214
214
  "component-placement": eslint.Rule.RuleModule;
215
215
  "component-casing": eslint.Rule.RuleModule;
216
216
  "application-structure": eslint.Rule.RuleModule;
217
- "data-testid-case": eslint.Rule.RuleModule;
217
+ "data-component-case": eslint.Rule.RuleModule;
218
218
  "jsx-hygiene": eslint.Rule.RuleModule;
219
219
  "interactive-component": eslint.Rule.RuleModule;
220
220
  "ui-state": eslint.Rule.RuleModule;
@@ -219,85 +219,40 @@ function findJsxReturningDeclarations(text, filename) {
219
219
  }
220
220
  return results;
221
221
  }
222
- function jsxTagName(element) {
223
- const tagName2 = ts.isJsxElement(element) ? element.openingElement.tagName : element.tagName;
224
- return ts.isIdentifier(tagName2) ? tagName2.text : void 0;
225
- }
226
- function rootFromExpression(expression) {
227
- let unwrapped = expression;
228
- while (ts.isParenthesizedExpression(unwrapped)) unwrapped = unwrapped.expression;
229
- if (ts.isJsxSelfClosingElement(unwrapped)) {
230
- const tagName2 = jsxTagName(unwrapped);
231
- if (!tagName2?.startsWith(tagName2[0]?.toLowerCase() ?? "")) return void 0;
232
- return { tagName: tagName2, attributes: [...unwrapped.attributes.properties] };
233
- }
234
- if (ts.isJsxElement(unwrapped)) {
235
- const tagName2 = jsxTagName(unwrapped);
236
- if (!tagName2?.startsWith(tagName2[0]?.toLowerCase() ?? "")) return void 0;
237
- return { tagName: tagName2, attributes: [...unwrapped.openingElement.attributes.properties] };
238
- }
239
- return void 0;
240
- }
241
- function findSimpleRoot(component, _text, _filename) {
242
- const declaration = component.declaration;
243
- let body;
244
- if (ts.isFunctionDeclaration(declaration)) body = declaration.body;
245
- else if (declaration.initializer && ts.isArrowFunction(declaration.initializer)) body = declaration.initializer.body;
246
- else if (declaration.initializer && ts.isFunctionExpression(declaration.initializer)) {
247
- body = declaration.initializer.body;
248
- }
249
- if (!body) return void 0;
250
- if (!ts.isBlock(body)) return rootFromExpression(body);
251
- const returns = [];
252
- const visit = (node) => {
253
- if (node !== body && (ts.isFunctionLike(node) || ts.isClassLike(node))) return;
254
- if (ts.isReturnStatement(node)) returns.push(node);
255
- ts.forEachChild(node, visit);
256
- };
257
- visit(body);
258
- const rendered = returns.filter((statement) => statement.expression?.kind !== ts.SyntaxKind.NullKeyword);
259
- const roots = rendered.map(
260
- (statement) => statement.expression && ts.isExpression(statement.expression) ? rootFromExpression(statement.expression) : void 0
261
- ).filter((root) => root !== void 0);
262
- if (roots.length !== rendered.length || roots.length === 0) return void 0;
263
- const firstTag = roots[0]?.tagName;
264
- if (roots.some((root) => root.tagName !== firstTag)) return void 0;
265
- return roots[0];
266
- }
267
- function literalTestIds(attributes) {
222
+ function componentMarkers(attributes) {
268
223
  return attributes.flatMap((candidate) => {
269
- if (!ts.isJsxAttribute(candidate) || !ts.isIdentifier(candidate.name) || candidate.name.text !== "data-testid") {
224
+ if (!ts.isJsxAttribute(candidate) || !ts.isIdentifier(candidate.name) || candidate.name.text !== "data-component") {
270
225
  return [];
271
226
  }
272
- return candidate.initializer && ts.isStringLiteral(candidate.initializer) ? [candidate.initializer.text] : [];
227
+ return candidate.initializer && ts.isStringLiteral(candidate.initializer) ? [candidate.initializer.text] : ["<dynamic>"];
273
228
  });
274
229
  }
275
- function combineTestIdPaths(left, right) {
230
+ function combineComponentMarkerPaths(left, right) {
276
231
  return left.flatMap((leftPath) => right.map((rightPath) => [...leftPath, ...rightPath]));
277
232
  }
278
- function renderedTestIdPathsFromExpression(expression) {
233
+ function renderedComponentMarkerPathsFromExpression(expression) {
279
234
  let unwrapped = expression;
280
235
  while (ts.isParenthesizedExpression(unwrapped)) unwrapped = unwrapped.expression;
281
236
  if (ts.isConditionalExpression(unwrapped)) {
282
237
  return [
283
- ...renderedTestIdPathsFromExpression(unwrapped.whenTrue),
284
- ...renderedTestIdPathsFromExpression(unwrapped.whenFalse)
238
+ ...renderedComponentMarkerPathsFromExpression(unwrapped.whenTrue),
239
+ ...renderedComponentMarkerPathsFromExpression(unwrapped.whenFalse)
285
240
  ];
286
241
  }
287
242
  if (ts.isJsxSelfClosingElement(unwrapped)) {
288
- return [literalTestIds(unwrapped.attributes.properties)];
243
+ return [componentMarkers(unwrapped.attributes.properties)];
289
244
  }
290
245
  if (ts.isJsxElement(unwrapped)) {
291
- let paths = [literalTestIds(unwrapped.openingElement.attributes.properties)];
246
+ let paths = [componentMarkers(unwrapped.openingElement.attributes.properties)];
292
247
  for (const child of unwrapped.children) {
293
248
  if (ts.isJsxElement(child) || ts.isJsxSelfClosingElement(child) || ts.isJsxFragment(child)) {
294
- paths = combineTestIdPaths(paths, renderedTestIdPathsFromExpression(child));
249
+ paths = combineComponentMarkerPaths(paths, renderedComponentMarkerPathsFromExpression(child));
295
250
  continue;
296
251
  }
297
252
  if (ts.isJsxExpression(child) && child.expression) {
298
253
  const childExpression = child.expression;
299
254
  if (ts.isConditionalExpression(childExpression) || ts.isJsxElement(childExpression) || ts.isJsxSelfClosingElement(childExpression) || ts.isJsxFragment(childExpression) || ts.isParenthesizedExpression(childExpression)) {
300
- paths = combineTestIdPaths(paths, renderedTestIdPathsFromExpression(childExpression));
255
+ paths = combineComponentMarkerPaths(paths, renderedComponentMarkerPathsFromExpression(childExpression));
301
256
  }
302
257
  }
303
258
  }
@@ -307,13 +262,13 @@ function renderedTestIdPathsFromExpression(expression) {
307
262
  let paths = [[]];
308
263
  for (const child of unwrapped.children) {
309
264
  if (ts.isJsxElement(child) || ts.isJsxSelfClosingElement(child) || ts.isJsxFragment(child)) {
310
- paths = combineTestIdPaths(paths, renderedTestIdPathsFromExpression(child));
265
+ paths = combineComponentMarkerPaths(paths, renderedComponentMarkerPathsFromExpression(child));
311
266
  continue;
312
267
  }
313
268
  if (ts.isJsxExpression(child) && child.expression) {
314
269
  const childExpression = child.expression;
315
270
  if (ts.isConditionalExpression(childExpression) || ts.isJsxElement(childExpression) || ts.isJsxSelfClosingElement(childExpression) || ts.isJsxFragment(childExpression) || ts.isParenthesizedExpression(childExpression)) {
316
- paths = combineTestIdPaths(paths, renderedTestIdPathsFromExpression(childExpression));
271
+ paths = combineComponentMarkerPaths(paths, renderedComponentMarkerPathsFromExpression(childExpression));
317
272
  }
318
273
  }
319
274
  }
@@ -321,7 +276,7 @@ function renderedTestIdPathsFromExpression(expression) {
321
276
  }
322
277
  return [[]];
323
278
  }
324
- function findRenderedTestIdPaths(component) {
279
+ function findRenderedComponentMarkerPaths(component) {
325
280
  const declaration = component.declaration;
326
281
  let body;
327
282
  if (ts.isFunctionDeclaration(declaration)) body = declaration.body;
@@ -330,7 +285,7 @@ function findRenderedTestIdPaths(component) {
330
285
  body = declaration.initializer.body;
331
286
  }
332
287
  if (!body) return [];
333
- if (!ts.isBlock(body)) return renderedTestIdPathsFromExpression(body);
288
+ if (!ts.isBlock(body)) return renderedComponentMarkerPathsFromExpression(body);
334
289
  const returns = [];
335
290
  const visit = (node) => {
336
291
  if (node !== body && (ts.isFunctionLike(node) || ts.isClassLike(node))) return;
@@ -341,18 +296,9 @@ function findRenderedTestIdPaths(component) {
341
296
  return returns.flatMap((statement) => {
342
297
  const expression = statement.expression;
343
298
  if (!expression || expression.kind === ts.SyntaxKind.NullKeyword) return [];
344
- return renderedTestIdPathsFromExpression(expression);
299
+ return renderedComponentMarkerPathsFromExpression(expression);
345
300
  });
346
301
  }
347
- function getTestId(root) {
348
- const attribute = root.attributes.find(
349
- (candidate) => ts.isJsxAttribute(candidate) && ts.isIdentifier(candidate.name) && candidate.name.text === "data-testid"
350
- );
351
- if (!attribute) return {};
352
- const value = attribute.initializer;
353
- if (!value || !ts.isStringLiteral(value)) return { attribute };
354
- return { value: value.text, attribute };
355
- }
356
302
 
357
303
  // eslint/rules/filename-case.ts
358
304
  var NEXT_ROUTING_FILES = /* @__PURE__ */ new Set([
@@ -2393,7 +2339,7 @@ var namedExportsRule = {
2393
2339
  }
2394
2340
  };
2395
2341
 
2396
- // eslint/rules/data-testid-case.ts
2342
+ // eslint/rules/data-component-case.ts
2397
2343
  import path15 from "path";
2398
2344
  var NEXT_ROUTING_FILES2 = /* @__PURE__ */ new Set([
2399
2345
  "default",
@@ -2416,13 +2362,12 @@ var NEXT_ROUTING_FILES2 = /* @__PURE__ */ new Set([
2416
2362
  "sitemap",
2417
2363
  "twitter-image"
2418
2364
  ]);
2419
- var isKebabCase3 = (value) => /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(value);
2420
- var dataTestIdCaseRule = {
2365
+ var dataComponentCaseRule = {
2421
2366
  meta: {
2422
2367
  schema: [],
2423
2368
  type: "problem",
2424
2369
  docs: {
2425
- description: "Enforce data-testid casing for smart and dumb components with a single intrinsic root."
2370
+ description: "Enforce stable data-component markers for React component surfaces."
2426
2371
  }
2427
2372
  },
2428
2373
  create(context) {
@@ -2430,41 +2375,29 @@ var dataTestIdCaseRule = {
2430
2375
  if (!filename.endsWith(".tsx")) return {};
2431
2376
  const base = path15.basename(filename, path15.extname(filename));
2432
2377
  if (NEXT_ROUTING_FILES2.has(base)) return {};
2433
- const text = context.sourceCode.text;
2434
- const components = parseComponentInfo(text, filename);
2378
+ const components = parseComponentInfo(context.sourceCode.text, filename);
2435
2379
  return {
2436
2380
  Program(node) {
2437
2381
  for (const component of components) {
2382
+ const expected = component.smart ? component.name : toKebabCase(component.name);
2383
+ const renderedPaths = findRenderedComponentMarkerPaths(component);
2384
+ const hasNoMarker = renderedPaths.every((values) => values.length === 0);
2385
+ const hasStableMarker = renderedPaths.length > 0 && renderedPaths.every((values) => values.length === 1 && values[0] === expected);
2438
2386
  if (component.smart) {
2439
- const expected2 = component.name;
2440
- const renderedPaths = findRenderedTestIdPaths(component);
2441
- const hasStableAnchor = renderedPaths.length > 0 && renderedPaths.every((values) => values.filter((value2) => value2 === expected2).length === 1);
2442
- if (hasStableAnchor) continue;
2443
- const hasSingleWrongCasedAnchor = renderedPaths.length > 0 && renderedPaths.every((values) => values.length === 1 && values[0] !== expected2) && new Set(renderedPaths.map((values) => values[0])).size === 1;
2444
- if (hasSingleWrongCasedAnchor) {
2445
- context.report({
2446
- node,
2447
- message: `data-testid for smart component "${component.name}" must be PascalCase: expected "${expected2}".`
2448
- });
2449
- continue;
2450
- }
2387
+ if (hasStableMarker) continue;
2388
+ const hasSingleWrongMarker2 = renderedPaths.length > 0 && renderedPaths.every((values) => values.length === 1 && values[0] !== expected) && new Set(renderedPaths.map((values) => values[0])).size === 1;
2451
2389
  context.report({
2452
2390
  node,
2453
- message: `Smart component "${component.name}" must expose exactly one stable data-testid="${expected2}" anchor for itself in every rendered result. Place it on the existing DOM surface or on a child component that forwards data-testid instead of adding an artificial wrapper. See docs/next-codebase-guide/rules/smart-vs-dumb-component-rule.md`
2391
+ message: hasSingleWrongMarker2 ? `data-component for smart component "${component.name}" must match the component name: expected "${expected}".` : `Smart component "${component.name}" must expose exactly one stable data-component="${expected}" marker in every rendered result. Place it on the existing meaningful DOM surface or forward it to the child component that renders that surface. See docs/next-codebase-guide/rules/smart-vs-dumb-component-rule.md`
2454
2392
  });
2455
2393
  continue;
2456
2394
  }
2457
- const root = findSimpleRoot(component, text, filename);
2458
- if (!root) continue;
2459
- const { value } = getTestId(root);
2460
- const expected = toKebabCase(component.name);
2461
- if (value === void 0 || value === expected) continue;
2462
- if (!isKebabCase3(value)) {
2463
- context.report({
2464
- node,
2465
- message: `data-testid for dumb component "${component.name}" must be kebab-case: expected "${expected}".`
2466
- });
2467
- }
2395
+ if (hasNoMarker || hasStableMarker) continue;
2396
+ const hasSingleWrongMarker = renderedPaths.length > 0 && renderedPaths.every((values) => values.length === 1 && values[0] !== expected) && new Set(renderedPaths.map((values) => values[0])).size === 1;
2397
+ context.report({
2398
+ node,
2399
+ message: hasSingleWrongMarker ? `data-component for dumb component "${component.name}" must be kebab-case: expected "${expected}".` : `Dumb component "${component.name}" may omit data-component, but when present it must expose exactly one stable data-component="${expected}" marker on its meaningful surface.`
2400
+ });
2468
2401
  }
2469
2402
  }
2470
2403
  };
@@ -7986,7 +7919,7 @@ var pasikaNextjsAppRules = {
7986
7919
  "component-placement": componentPlacementRule,
7987
7920
  "component-casing": componentCasingRule,
7988
7921
  "application-structure": applicationStructureRule,
7989
- "data-testid-case": dataTestIdCaseRule,
7922
+ "data-component-case": dataComponentCaseRule,
7990
7923
  "jsx-hygiene": jsxHygieneRule,
7991
7924
  "interactive-component": interactiveComponentRule,
7992
7925
  "ui-state": uiStateRule,
@@ -0,0 +1,14 @@
1
+ /** Standard HTTP request methods for runtime use. */
2
+ declare enum HttpMethod {
3
+ Get = "GET",
4
+ Head = "HEAD",
5
+ Post = "POST",
6
+ Put = "PUT",
7
+ Delete = "DELETE",
8
+ Connect = "CONNECT",
9
+ Options = "OPTIONS",
10
+ Trace = "TRACE",
11
+ Patch = "PATCH"
12
+ }
13
+
14
+ export { HttpMethod };
@@ -0,0 +1,16 @@
1
+ // helpers/http-method.ts
2
+ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
3
+ HttpMethod2["Get"] = "GET";
4
+ HttpMethod2["Head"] = "HEAD";
5
+ HttpMethod2["Post"] = "POST";
6
+ HttpMethod2["Put"] = "PUT";
7
+ HttpMethod2["Delete"] = "DELETE";
8
+ HttpMethod2["Connect"] = "CONNECT";
9
+ HttpMethod2["Options"] = "OPTIONS";
10
+ HttpMethod2["Trace"] = "TRACE";
11
+ HttpMethod2["Patch"] = "PATCH";
12
+ return HttpMethod2;
13
+ })(HttpMethod || {});
14
+ export {
15
+ HttpMethod
16
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.33",
3
+ "version": "0.10.35",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,6 +21,10 @@
21
21
  "types": "./dist/helpers/http-error.d.ts",
22
22
  "import": "./dist/helpers/http-error.js"
23
23
  },
24
+ "./http-method": {
25
+ "types": "./dist/helpers/http-method.d.ts",
26
+ "import": "./dist/helpers/http-method.js"
27
+ },
24
28
  "./response-envelope": {
25
29
  "types": "./dist/helpers/response-envelope.d.ts",
26
30
  "import": "./dist/helpers/response-envelope.js"