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-
|
|
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
|
|
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-
|
|
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
|
|
230
|
+
function combineComponentMarkerPaths(left, right) {
|
|
276
231
|
return left.flatMap((leftPath) => right.map((rightPath) => [...leftPath, ...rightPath]));
|
|
277
232
|
}
|
|
278
|
-
function
|
|
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
|
-
...
|
|
284
|
-
...
|
|
238
|
+
...renderedComponentMarkerPathsFromExpression(unwrapped.whenTrue),
|
|
239
|
+
...renderedComponentMarkerPathsFromExpression(unwrapped.whenFalse)
|
|
285
240
|
];
|
|
286
241
|
}
|
|
287
242
|
if (ts.isJsxSelfClosingElement(unwrapped)) {
|
|
288
|
-
return [
|
|
243
|
+
return [componentMarkers(unwrapped.attributes.properties)];
|
|
289
244
|
}
|
|
290
245
|
if (ts.isJsxElement(unwrapped)) {
|
|
291
|
-
let paths = [
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
2420
|
-
var dataTestIdCaseRule = {
|
|
2365
|
+
var dataComponentCaseRule = {
|
|
2421
2366
|
meta: {
|
|
2422
2367
|
schema: [],
|
|
2423
2368
|
type: "problem",
|
|
2424
2369
|
docs: {
|
|
2425
|
-
description: "Enforce data-
|
|
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
|
|
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
|
-
|
|
2440
|
-
const
|
|
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-
|
|
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
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
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-
|
|
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.
|
|
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"
|