@elaraai/east-diagnostics 1.0.80 → 1.0.82
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 +26 -5
- package/dist/src/east-value.d.ts +42 -0
- package/dist/src/east-value.d.ts.map +1 -0
- package/dist/src/east-value.js +223 -0
- package/dist/src/east-value.js.map +1 -0
- package/dist/src/rules/index.d.ts +18 -0
- package/dist/src/rules/index.d.ts.map +1 -1
- package/dist/src/rules/index.js +38 -0
- package/dist/src/rules/index.js.map +1 -1
- package/dist/src/rules/no-handrolled-value-type-mirror.d.ts.map +1 -1
- package/dist/src/rules/no-handrolled-value-type-mirror.js +91 -18
- package/dist/src/rules/no-handrolled-value-type-mirror.js.map +1 -1
- package/dist/src/rules/no-handrolled-variant.d.ts.map +1 -1
- package/dist/src/rules/no-handrolled-variant.js +60 -3
- package/dist/src/rules/no-handrolled-variant.js.map +1 -1
- package/dist/src/rules/no-host-comparison-on-east-values.d.ts.map +1 -1
- package/dist/src/rules/no-host-comparison-on-east-values.js +78 -33
- package/dist/src/rules/no-host-comparison-on-east-values.js.map +1 -1
- package/dist/src/rules/no-host-parse-to-east-values.d.ts +3 -0
- package/dist/src/rules/no-host-parse-to-east-values.d.ts.map +1 -0
- package/dist/src/rules/no-host-parse-to-east-values.js +84 -0
- package/dist/src/rules/no-host-parse-to-east-values.js.map +1 -0
- package/dist/src/rules/no-host-print-of-east-values.d.ts +3 -0
- package/dist/src/rules/no-host-print-of-east-values.d.ts.map +1 -0
- package/dist/src/rules/no-host-print-of-east-values.js +92 -0
- package/dist/src/rules/no-host-print-of-east-values.js.map +1 -0
- package/dist/src/rules/no-js-collection-for-east-collection.d.ts +3 -0
- package/dist/src/rules/no-js-collection-for-east-collection.d.ts.map +1 -0
- package/dist/src/rules/no-js-collection-for-east-collection.js +70 -0
- package/dist/src/rules/no-js-collection-for-east-collection.js.map +1 -0
- package/dist/src/rules/no-js-type-dispatch-on-east-values.d.ts +7 -0
- package/dist/src/rules/no-js-type-dispatch-on-east-values.d.ts.map +1 -0
- package/dist/src/rules/no-js-type-dispatch-on-east-values.js +40 -0
- package/dist/src/rules/no-js-type-dispatch-on-east-values.js.map +1 -0
- package/dist/src/rules/no-module-scope-east-macro.d.ts.map +1 -1
- package/dist/src/rules/no-module-scope-east-macro.js +105 -17
- package/dist/src/rules/no-module-scope-east-macro.js.map +1 -1
- package/dist/src/rules/prefer-some-none.d.ts +0 -4
- package/dist/src/rules/prefer-some-none.d.ts.map +1 -1
- package/dist/src/rules/prefer-some-none.js +75 -4
- package/dist/src/rules/prefer-some-none.js.map +1 -1
- package/dist/src/run.d.ts.map +1 -1
- package/dist/src/run.js +2 -1
- package/dist/src/run.js.map +1 -1
- package/dist/src/types.d.ts +3 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-handrolled-value-type-mirror.js","sourceRoot":"","sources":["../../../src/rules/no-handrolled-value-type-mirror.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-handrolled-value-type-mirror.js","sourceRoot":"","sources":["../../../src/rules/no-handrolled-value-type-mirror.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,IAAI,GAAG,iCAAiC,CAAC;AAC/C,MAAM,IAAI,GAAG,MAAM,CAAC;AAEpB;;6DAE6D;AAC7D,SAAS,mBAAmB,CAAC,IAAY,EAAE,GAAgB;IACzD,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QAC7C,IAAI,KAAgC,CAAC;QACrC,IAAI,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,aAAa,KAAK,SAAS,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC;YACvI,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;gBAC5D,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU;oBAAE,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC;YACrE,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,CAAC;gBAClD,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI;oBAAE,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC;YACrE,CAAC;QACH,CAAC;QACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;YAC7D,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QACvE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6DAA6D;AAC7D,SAAS,SAAS,CAAC,IAAkD;IACnE,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC;AAED,0EAA0E;AAC1E,SAAS,kBAAkB,CAAC,QAAiB,EAAE,GAAgB;IAC7D,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,MAAM,KAAK,GAAG,CAAC,CAAU,EAAQ,EAAE;QACjC,IAAI,KAAK;YAAE,OAAO;QAClB,IAAI,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3H,IAAI,IAAI,KAAK,SAAS,IAAI,oBAAoB,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;gBACrE,KAAK,GAAG,IAAI,CAAC;gBACb,OAAO;YACT,CAAC;QACH,CAAC;QACD,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC,CAAC;IACF,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,CAAmB,EAAE,CAAW;IAChD,IAAI,GAAG,GAAkB,CAAC,CAAC;IAC3B,OAAO,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC;QAAE,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC;IAC9C,OAAO,GAAG,CAAC;AACb,CAAC;AAED;wDACwD;AACxD,SAAS,cAAc,CAAC,KAAmD,EAAE,GAAgB;IAC3F,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;QAClF,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AACpE,CAAC;AAED;8EAC8E;AAC9E,SAAS,cAAc,CAAC,IAAkD,EAAE,GAAgB;IAC1F,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,MAAM,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;IAC1B,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,OAAO,IAAI,KAAK,SAAS,IAAI,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;AACL,CAAC;AAED,kFAAkF;AAClF,SAAS,sBAAsB,CAAC,IAAiB,EAAE,CAAW;IAC5D,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1H,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,MAAM,CAAC,GAAgB,EAAE,IAAa,EAAE,WAAmB;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC5C,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC;AACxH,CAAC;AAED,2EAA2E;AAC3E,kFAAkF;AAClF,kFAAkF;AAClF,mEAAmE;AACnE,wEAAwE;AACxE,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,iFAAiF;AACjF,EAAE;AACF,+EAA+E;AAC/E,2CAA2C;AAC3C,MAAM,CAAC,MAAM,2BAA2B,GAAa;IACnD,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;IACV,WAAW,EACT,2KAA2K;IAC7K,KAAK,CAAC,IAAI,EAAE,GAAG;QACb,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC;YAChE,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBAAE,OAAO;YAClD,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;gBAAE,OAAO;YACnD,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,kMAAkM,CAAC,CAAC;YAC3N,OAAO;QACT,CAAC;QAED,IAAI,IAA+B,CAAC;QACpC,IAAI,KAA+D,CAAC;QACpE,IAAI,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACjB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;aAAM,IAAI,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5E,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACjB,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO;QACtD,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;YAAE,OAAO;QACnD,IAAI,cAAc,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO;QAEvC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,cAAc,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YACrI,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI,+KAA+K,CAAC,CAAC;YACjN,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3F,MAAM,WAAW,GAAG,GAAG,IAAI,MAAM,CAAC;QAClC,IAAI,WAAW,KAAK,IAAI,CAAC,IAAI;YAAE,OAAO;QACtC,IAAI,CAAC,mBAAmB,CAAC,WAAW,EAAE,GAAG,CAAC;YAAE,OAAO;QAEnD,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI,mCAAmC,WAAW,+EAA+E,IAAI,CAAC,IAAI,yBAAyB,WAAW,MAAM,CAAC,CAAC;IACpN,CAAC;CACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-handrolled-variant.d.ts","sourceRoot":"","sources":["../../../src/rules/no-handrolled-variant.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"no-handrolled-variant.d.ts","sourceRoot":"","sources":["../../../src/rules/no-handrolled-variant.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAyB,MAAM,aAAa,CAAC;AA6EnE,eAAO,MAAM,mBAAmB,EAAE,QA8BjC,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { importsEastPackage } from "../east-source.js";
|
|
1
2
|
const NAME = "no-handrolled-variant";
|
|
2
3
|
const CODE = 990004;
|
|
3
4
|
const VARIANT_TYPE_NAMES = new Set(["variant", "some", "none", "option", "VariantExpr"]);
|
|
@@ -19,16 +20,72 @@ function expectsVariant(type) {
|
|
|
19
20
|
}
|
|
20
21
|
return false;
|
|
21
22
|
}
|
|
23
|
+
/** The plain or shorthand property of `node` called `name`, if it has one. */
|
|
24
|
+
function propertyNamed(node, name, t) {
|
|
25
|
+
for (const p of node.properties) {
|
|
26
|
+
if ((t.isPropertyAssignment(p) || t.isShorthandPropertyAssignment(p)) &&
|
|
27
|
+
(t.isIdentifier(p.name) || t.isStringLiteral(p.name)) && p.name.text === name) {
|
|
28
|
+
return p;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
/** Is `node` a variant's shape — exactly a String tag and a payload, `{ type: "Tag", value }`? */
|
|
34
|
+
function spelledAsVariant(node, ctx) {
|
|
35
|
+
const t = ctx.ts;
|
|
36
|
+
if (node.properties.length !== 2)
|
|
37
|
+
return false;
|
|
38
|
+
const tag = propertyNamed(node, "type", t);
|
|
39
|
+
if (tag === undefined || propertyNamed(node, "value", t) === undefined)
|
|
40
|
+
return false;
|
|
41
|
+
const tagType = ctx.checker.getTypeAtLocation(t.isPropertyAssignment(tag) ? tag.initializer : tag.name);
|
|
42
|
+
const members = tagType.isUnion() ? tagType.types : [tagType];
|
|
43
|
+
return members.every((m) => (m.flags & t.TypeFlags.StringLike) !== 0);
|
|
44
|
+
}
|
|
45
|
+
/** Is `type` a published API's own type — every member an object type declared
|
|
46
|
+
* in a package's declarations: a library's (a React input's props, a DOM type),
|
|
47
|
+
* or a host-side type of East's own (`Resolution`, what `mergeWithResolutionsFor`
|
|
48
|
+
* takes)? An `any`, a type parameter, or a `{ type; value }` shape this
|
|
49
|
+
* project's source wrote is not. */
|
|
50
|
+
function publishedShape(type, t) {
|
|
51
|
+
const members = (type.isUnion() ? type.types : [type])
|
|
52
|
+
.filter((m) => (m.flags & (t.TypeFlags.Null | t.TypeFlags.Undefined)) === 0);
|
|
53
|
+
return members.length > 0 && members.every((m) => {
|
|
54
|
+
if ((m.flags & t.TypeFlags.Object) === 0)
|
|
55
|
+
return false;
|
|
56
|
+
const declarations = (m.aliasSymbol ?? m.symbol)?.declarations ?? [];
|
|
57
|
+
return declarations.length > 0 && declarations.every((d) => d.getSourceFile().isDeclarationFile);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// A variant built by hand — a plain `{ type, value }` object — lacks the symbol
|
|
61
|
+
// East's variants carry, so East's encoders, printers and comparers do not treat
|
|
62
|
+
// it as one, and it drifts silently from the East type it spells. Two spellings
|
|
63
|
+
// are caught:
|
|
64
|
+
//
|
|
65
|
+
// - any object spelling a `type` where a variant is expected;
|
|
66
|
+
// - in a file that uses East, an object that IS a variant's shape — exactly a
|
|
67
|
+
// String tag and a payload — whatever slot it lands in: a fixture handed
|
|
68
|
+
// through `unknown`, a test matcher, a hand-written `{ type; value }`
|
|
69
|
+
// parameter, or no slot at all. Only a published API's own type declaring
|
|
70
|
+
// `type` and `value` is something else: a library's (a React input's props),
|
|
71
|
+
// or East's host-side `Resolution`, which `mergeWithResolutionsFor` takes.
|
|
22
72
|
export const noHandrolledVariant = {
|
|
23
73
|
name: NAME,
|
|
24
74
|
code: CODE,
|
|
25
|
-
description: "Disallow plain object
|
|
75
|
+
description: "Disallow a variant built as a plain `{ type, value }` object literal — where a variant is expected, or anywhere in East source; use variant()/some()/none.",
|
|
26
76
|
check(node, ctx) {
|
|
27
77
|
const t = ctx.ts;
|
|
28
78
|
if (!t.isObjectLiteralExpression(node))
|
|
29
79
|
return;
|
|
80
|
+
// A hand-rolled variant spells its tag: an object with no `type` is some other
|
|
81
|
+
// input the expected union admits (a factory's options object).
|
|
82
|
+
if (propertyNamed(node, "type", t) === undefined)
|
|
83
|
+
return;
|
|
30
84
|
const contextualType = ctx.checker.getContextualType(node);
|
|
31
|
-
|
|
85
|
+
const inVariantSlot = contextualType !== undefined && expectsVariant(contextualType);
|
|
86
|
+
const byHand = !inVariantSlot && importsEastPackage(ctx.sourceFile, t) && spelledAsVariant(node, ctx) &&
|
|
87
|
+
(contextualType === undefined || !publishedShape(contextualType, t));
|
|
88
|
+
if (!inVariantSlot && !byHand)
|
|
32
89
|
return;
|
|
33
90
|
const sf = ctx.sourceFile;
|
|
34
91
|
const start = node.getStart(sf);
|
|
@@ -37,7 +94,7 @@ export const noHandrolledVariant = {
|
|
|
37
94
|
code: CODE,
|
|
38
95
|
start,
|
|
39
96
|
length: node.getEnd() - start,
|
|
40
|
-
messageText: 'Hand-rolled variant: build with `variant("Tag", value)`, `some(value)`, or `none` from @elaraai/east — never a plain `{ type, value }` object literal.',
|
|
97
|
+
messageText: 'Hand-rolled variant: build with `variant("Tag", value)`, `some(value)`, or `none` from @elaraai/east — never a plain `{ type, value }` object literal, which lacks the symbol East\'s variants carry.',
|
|
41
98
|
category: "warning",
|
|
42
99
|
});
|
|
43
100
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-handrolled-variant.js","sourceRoot":"","sources":["../../../src/rules/no-handrolled-variant.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"no-handrolled-variant.js","sourceRoot":"","sources":["../../../src/rules/no-handrolled-variant.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-host-comparison-on-east-values.d.ts","sourceRoot":"","sources":["../../../src/rules/no-host-comparison-on-east-values.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"no-host-comparison-on-east-values.d.ts","sourceRoot":"","sources":["../../../src/rules/no-host-comparison-on-east-values.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAyB,MAAM,aAAa,CAAC;AA4DnE,eAAO,MAAM,4BAA4B,EAAE,QAmD1C,CAAC"}
|
|
@@ -1,41 +1,86 @@
|
|
|
1
1
|
import { importsEastPackage } from "../east-source.js";
|
|
2
|
+
import { isEastValueExpression, isEastValueShape, skipValueWrappers } from "../east-value.js";
|
|
2
3
|
const NAME = "no-host-comparison-on-east-values";
|
|
3
4
|
const CODE = 990029;
|
|
4
|
-
// The DECODED-value shapes whose host comparison is broken: variants/options
|
|
5
|
-
// (tagged structs — `===` is reference equality, always false for two decoded
|
|
6
|
-
// values) and the sorted containers (no meaningful `<`). Same name-keyed
|
|
7
|
-
// recognition as `no-handrolled-variant`.
|
|
8
|
-
const VALUE_SHAPE_NAMES = new Set(["variant", "option", "SortedMap", "SortedSet"]);
|
|
9
|
-
function isEastValueShapeType(type) {
|
|
10
|
-
const seen = new Set();
|
|
11
|
-
const stack = [type];
|
|
12
|
-
while (stack.length > 0) {
|
|
13
|
-
const current = stack.pop();
|
|
14
|
-
if (current === undefined || seen.has(current))
|
|
15
|
-
continue;
|
|
16
|
-
seen.add(current);
|
|
17
|
-
const name = current.aliasSymbol?.name ?? current.symbol?.name;
|
|
18
|
-
if (name !== undefined && VALUE_SHAPE_NAMES.has(name))
|
|
19
|
-
return true;
|
|
20
|
-
if (current.isUnionOrIntersection())
|
|
21
|
-
stack.push(...current.types);
|
|
22
|
-
}
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
5
|
function isNullish(e, t) {
|
|
26
6
|
return e.kind === t.SyntaxKind.NullKeyword || (t.isIdentifier(e) && e.text === "undefined");
|
|
27
7
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
8
|
+
/** Is `e` a decoded East value JavaScript holds as an object — a struct, a
|
|
9
|
+
* variant, a collection, a DateTime, a Blob — so `===` compares its identity? */
|
|
10
|
+
function isEastObject(e, ctx) {
|
|
11
|
+
const type = ctx.checker.getTypeAtLocation(e);
|
|
12
|
+
if (isEastValueShape(type, ctx))
|
|
13
|
+
return true;
|
|
14
|
+
const objectLike = (type.flags & ctx.ts.TypeFlags.Object) !== 0 ||
|
|
15
|
+
(type.isUnion() && type.types.some((m) => (m.flags & ctx.ts.TypeFlags.Object) !== 0));
|
|
16
|
+
return objectLike && isEastValueExpression(e, ctx);
|
|
17
|
+
}
|
|
18
|
+
/** The expressions a comparator callback returns. */
|
|
19
|
+
function returnedExpressions(fn, t) {
|
|
20
|
+
if (!t.isBlock(fn.body))
|
|
21
|
+
return [fn.body];
|
|
22
|
+
const out = [];
|
|
23
|
+
const visit = (n) => {
|
|
24
|
+
if (t.isFunctionLike(n))
|
|
25
|
+
return;
|
|
26
|
+
if (t.isReturnStatement(n) && n.expression !== undefined)
|
|
27
|
+
out.push(n.expression);
|
|
28
|
+
t.forEachChild(n, visit);
|
|
29
|
+
};
|
|
30
|
+
t.forEachChild(fn.body, visit);
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
/** The subtraction of East values a `sort` / `toSorted` comparator returns, if any. */
|
|
34
|
+
function subtractingComparator(call, ctx) {
|
|
35
|
+
const t = ctx.ts;
|
|
36
|
+
const callee = call.expression;
|
|
37
|
+
if (!t.isPropertyAccessExpression(callee) || (callee.name.text !== "sort" && callee.name.text !== "toSorted"))
|
|
38
|
+
return undefined;
|
|
39
|
+
const fn = call.arguments[0];
|
|
40
|
+
if (fn === undefined || (!t.isArrowFunction(fn) && !t.isFunctionExpression(fn)))
|
|
41
|
+
return undefined;
|
|
42
|
+
for (const r of returnedExpressions(fn, t)) {
|
|
43
|
+
const e = skipValueWrappers(r, t);
|
|
44
|
+
if (t.isBinaryExpression(e) && e.operatorToken.kind === t.SyntaxKind.MinusToken &&
|
|
45
|
+
(isEastValueExpression(e.left, ctx) || isEastValueExpression(e.right, ctx))) {
|
|
46
|
+
return e;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
// Host code handling DECODED East values (a `ValueTypeOf<…>` world: structs,
|
|
52
|
+
// variants, options, collections) must compare with `equalFor(T)` and order with
|
|
53
|
+
// `compareFor(T)` / `lessFor(T)`: `===` compares object identity (always false for
|
|
54
|
+
// two decoded structs, variants, DateTimes or Sets), `<` / `>` compare the wrong
|
|
55
|
+
// representation, and a comparator that subtracts is inconsistent over a `NaN`
|
|
56
|
+
// and rounds an Integer past 2^53. Code that does mean identity (a fold handing
|
|
57
|
+
// back the value it was given, a fast path ahead of `equalFor`) says so with
|
|
58
|
+
// `Object.is(a, b)`, which this rule leaves alone. Nothing else covers host-side
|
|
59
|
+
// code: the block rules only see East blocks.
|
|
33
60
|
export const noHostComparisonOnEastValues = {
|
|
34
61
|
name: NAME,
|
|
35
62
|
code: CODE,
|
|
36
|
-
description: "Flag ===/!==/</> on decoded East values
|
|
63
|
+
description: "Flag ===/!==/</> and subtracting sort comparators on decoded East values — use equalFor(T) / compareFor(T).",
|
|
37
64
|
check(node, ctx) {
|
|
38
65
|
const t = ctx.ts;
|
|
66
|
+
const sf = ctx.sourceFile;
|
|
67
|
+
if (t.isCallExpression(node)) {
|
|
68
|
+
if (!importsEastPackage(sf, t))
|
|
69
|
+
return;
|
|
70
|
+
const subtraction = subtractingComparator(node, ctx);
|
|
71
|
+
if (subtraction === undefined)
|
|
72
|
+
return;
|
|
73
|
+
const start = subtraction.getStart(sf);
|
|
74
|
+
ctx.report({
|
|
75
|
+
ruleName: NAME,
|
|
76
|
+
code: CODE,
|
|
77
|
+
start,
|
|
78
|
+
length: subtraction.getEnd() - start,
|
|
79
|
+
messageText: "A comparator that subtracts East values mis-orders them — a `NaN` makes it inconsistent and an Integer past 2^53 rounds. Order with `compareFor(T)` (`xs.sort((a, b) => compare(a.v, b.v))`).",
|
|
80
|
+
category: "warning",
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
39
84
|
if (!t.isBinaryExpression(node))
|
|
40
85
|
return;
|
|
41
86
|
const k = t.SyntaxKind;
|
|
@@ -44,16 +89,16 @@ export const noHostComparisonOnEastValues = {
|
|
|
44
89
|
const relational = op === k.LessThanToken || op === k.LessThanEqualsToken || op === k.GreaterThanToken || op === k.GreaterThanEqualsToken;
|
|
45
90
|
if (!equality && !relational)
|
|
46
91
|
return;
|
|
47
|
-
if (!importsEastPackage(
|
|
92
|
+
if (!importsEastPackage(sf, t))
|
|
48
93
|
return;
|
|
49
94
|
// `v === null` / `v !== undefined` are legitimate presence checks.
|
|
50
95
|
if (isNullish(node.left, t) || isNullish(node.right, t))
|
|
51
96
|
return;
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
97
|
+
const flagged = equality
|
|
98
|
+
? isEastObject(node.left, ctx) || isEastObject(node.right, ctx)
|
|
99
|
+
: isEastValueShape(ctx.checker.getTypeAtLocation(node.left), ctx) || isEastValueShape(ctx.checker.getTypeAtLocation(node.right), ctx);
|
|
100
|
+
if (!flagged)
|
|
55
101
|
return;
|
|
56
|
-
const sf = ctx.sourceFile;
|
|
57
102
|
const start = node.getStart(sf);
|
|
58
103
|
ctx.report({
|
|
59
104
|
ruleName: NAME,
|
|
@@ -61,7 +106,7 @@ export const noHostComparisonOnEastValues = {
|
|
|
61
106
|
start,
|
|
62
107
|
length: node.getEnd() - start,
|
|
63
108
|
messageText: equality
|
|
64
|
-
? "Host equality on a decoded East value compares object identity — two equal variants are never `===`. Use `equalFor(T)(a, b)
|
|
109
|
+
? "Host equality on a decoded East value compares object identity — two equal structs, variants, DateTimes or Sets are never `===`. Use `equalFor(T)(a, b)`, or `Object.is(a, b)` where you mean the same object."
|
|
65
110
|
: "Host ordering on a decoded East value compares the wrong representation. Use `compareFor(T)` / `lessFor(T)` (e.g. `arr.sort(compareFor(T))`).",
|
|
66
111
|
category: "warning",
|
|
67
112
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-host-comparison-on-east-values.js","sourceRoot":"","sources":["../../../src/rules/no-host-comparison-on-east-values.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-host-comparison-on-east-values.js","sourceRoot":"","sources":["../../../src/rules/no-host-comparison-on-east-values.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-host-parse-to-east-values.d.ts","sourceRoot":"","sources":["../../../src/rules/no-host-parse-to-east-values.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,aAAa,CAAC;AA2DzD,eAAO,MAAM,uBAAuB,EAAE,QAuBrC,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { importsEastPackage } from "../east-source.js";
|
|
2
|
+
import { isGlobalBuiltin, landsInEastSlot } from "../east-value.js";
|
|
3
|
+
const NAME = "no-host-parse-to-east-values";
|
|
4
|
+
const CODE = 990034;
|
|
5
|
+
/** Global functions that read text into a JavaScript value. */
|
|
6
|
+
const PARSERS = new Set(["BigInt", "Number", "parseFloat", "parseInt"]);
|
|
7
|
+
/** Is `e` text? */
|
|
8
|
+
function isText(e, ctx) {
|
|
9
|
+
return (ctx.checker.getTypeAtLocation(e).flags & ctx.ts.TypeFlags.StringLike) !== 0;
|
|
10
|
+
}
|
|
11
|
+
/** The text of a string literal, or `undefined` for text computed at run time. */
|
|
12
|
+
function literalText(e, t) {
|
|
13
|
+
return t.isStringLiteral(e) || t.isNoSubstitutionTemplateLiteral(e) ? e.text : undefined;
|
|
14
|
+
}
|
|
15
|
+
/** A date and time with no zone — the one literal JavaScript reads in local time. */
|
|
16
|
+
const ZONELESS_DATE_TIME = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/;
|
|
17
|
+
/** Does `node` read text the JavaScript way — wrongly for East? Text written in
|
|
18
|
+
* the source is its author's to check, save a zoneless date-time handed to
|
|
19
|
+
* `new Date` / `Date.parse`, which reads it in local time. */
|
|
20
|
+
function parsesText(node, ctx) {
|
|
21
|
+
const t = ctx.ts;
|
|
22
|
+
const misread = (text, date) => {
|
|
23
|
+
if (!isText(text, ctx))
|
|
24
|
+
return false;
|
|
25
|
+
const literal = literalText(text, t);
|
|
26
|
+
return literal === undefined || (date && ZONELESS_DATE_TIME.test(literal));
|
|
27
|
+
};
|
|
28
|
+
if (t.isCallExpression(node)) {
|
|
29
|
+
const callee = node.expression;
|
|
30
|
+
const text = node.arguments[0];
|
|
31
|
+
if (text === undefined)
|
|
32
|
+
return false;
|
|
33
|
+
if (t.isIdentifier(callee))
|
|
34
|
+
return PARSERS.has(callee.text) && isGlobalBuiltin(callee, ctx) && misread(text, false);
|
|
35
|
+
// `JSON.parse(s)`, `Date.parse(s)`
|
|
36
|
+
if (!t.isPropertyAccessExpression(callee) || !t.isIdentifier(callee.expression) || !isGlobalBuiltin(callee.expression, ctx))
|
|
37
|
+
return false;
|
|
38
|
+
if (callee.expression.text === "JSON" && callee.name.text === "parse")
|
|
39
|
+
return misread(text, false);
|
|
40
|
+
if (callee.expression.text === "Date" && callee.name.text === "parse")
|
|
41
|
+
return misread(text, true);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// `new Date(s)`
|
|
45
|
+
if (t.isNewExpression(node) && t.isIdentifier(node.expression) && node.expression.text === "Date") {
|
|
46
|
+
const text = node.arguments?.[0];
|
|
47
|
+
return node.arguments?.length === 1 && text !== undefined && isGlobalBuiltin(node.expression, ctx) && misread(text, true);
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
// Text read into an East value by a JavaScript parser is read wrongly: `BigInt("")`
|
|
52
|
+
// is 0, `BigInt("0x10")` is 16 and `BigInt` takes values past 64 bits; `Number`
|
|
53
|
+
// and `parseFloat` read `""` as 0 and stop at the first bad character;
|
|
54
|
+
// `new Date(text)` reads a zoneless instant in local time. East reads text with
|
|
55
|
+
// `parseFor(T)` — or a pattern with East's datetime format — and says whether it
|
|
56
|
+
// succeeded. Flagged where the value lands in an East slot: an East function
|
|
57
|
+
// value's argument, an East API's, or a field of a decoded East struct.
|
|
58
|
+
export const noHostParseToEastValues = {
|
|
59
|
+
name: NAME,
|
|
60
|
+
code: CODE,
|
|
61
|
+
description: "Flag BigInt()/Number()/parseFloat()/parseInt()/JSON.parse()/new Date() reading text into an East slot — use parseFor(T) or East's datetime format.",
|
|
62
|
+
check(node, ctx) {
|
|
63
|
+
const t = ctx.ts;
|
|
64
|
+
if (!t.isCallExpression(node) && !t.isNewExpression(node))
|
|
65
|
+
return;
|
|
66
|
+
if (!importsEastPackage(ctx.sourceFile, t))
|
|
67
|
+
return;
|
|
68
|
+
if (!parsesText(node, ctx))
|
|
69
|
+
return;
|
|
70
|
+
if (!landsInEastSlot(node, ctx))
|
|
71
|
+
return;
|
|
72
|
+
const sf = ctx.sourceFile;
|
|
73
|
+
const start = node.getStart(sf);
|
|
74
|
+
ctx.report({
|
|
75
|
+
ruleName: NAME,
|
|
76
|
+
code: CODE,
|
|
77
|
+
start,
|
|
78
|
+
length: node.getEnd() - start,
|
|
79
|
+
messageText: "A JavaScript parser reads this text into an East value wrongly — `BigInt(\"\")` is 0, `BigInt(\"0x10\")` is 16, `new Date(text)` reads local time. Read it with `parseFor(T)` (or East's datetime format) and act on its `success`.",
|
|
80
|
+
category: "warning",
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=no-host-parse-to-east-values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-host-parse-to-east-values.js","sourceRoot":"","sources":["../../../src/rules/no-host-parse-to-east-values.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEpE,MAAM,IAAI,GAAG,8BAA8B,CAAC;AAC5C,MAAM,IAAI,GAAG,MAAM,CAAC;AAEpB,+DAA+D;AAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;AAExE,mBAAmB;AACnB,SAAS,MAAM,CAAC,CAAgB,EAAE,GAAgB;IAChD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACtF,CAAC;AAED,kFAAkF;AAClF,SAAS,WAAW,CAAC,CAAgB,EAAE,CAAoB;IACzD,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3F,CAAC;AAED,qFAAqF;AACrF,MAAM,kBAAkB,GAAG,yDAAyD,CAAC;AAErF;;8DAE8D;AAC9D,SAAS,UAAU,CAAC,IAAa,EAAE,GAAgB;IACjD,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IACjB,MAAM,OAAO,GAAG,CAAC,IAAmB,EAAE,IAAa,EAAW,EAAE;QAC9D,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrC,OAAO,OAAO,KAAK,SAAS,IAAI,CAAC,IAAI,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC;IACF,IAAI,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACrC,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACpH,mCAAmC;QACnC,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1I,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACnG,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClG,OAAO,KAAK,CAAC;IACf,CAAC;IACD,gBAAgB;IAChB,IAAI,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAClG,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5H,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,oFAAoF;AACpF,gFAAgF;AAChF,uEAAuE;AACvE,gFAAgF;AAChF,iFAAiF;AACjF,6EAA6E;AAC7E,wEAAwE;AACxE,MAAM,CAAC,MAAM,uBAAuB,GAAa;IAC/C,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,IAAI;IACV,WAAW,EACT,oJAAoJ;IACtJ,KAAK,CAAC,IAAI,EAAE,GAAG;QACb,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC;YAAE,OAAO;QAClE,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;YAAE,OAAO;QACnD,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC;YAAE,OAAO;QACnC,IAAI,CAAC,eAAe,CAAC,IAAqB,EAAE,GAAG,CAAC;YAAE,OAAO;QACzD,MAAM,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChC,GAAG,CAAC,MAAM,CAAC;YACT,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,IAAI;YACV,KAAK;YACL,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK;YAC7B,WAAW,EACT,qOAAqO;YACvO,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-host-print-of-east-values.d.ts","sourceRoot":"","sources":["../../../src/rules/no-host-print-of-east-values.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,aAAa,CAAC;AAkEzD,eAAO,MAAM,uBAAuB,EAAE,QAyBrC,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { importsEastPackage } from "../east-source.js";
|
|
2
|
+
import { isEastValueExpression, isGlobalBuiltin, jsPrintDiffers } from "../east-value.js";
|
|
3
|
+
const NAME = "no-host-print-of-east-values";
|
|
4
|
+
const CODE = 990033;
|
|
5
|
+
/** Methods that print their receiver the JavaScript way. */
|
|
6
|
+
const PRINTING_METHODS = new Set(["toString", "toISOString", "toJSON"]);
|
|
7
|
+
/** The CSS units a number is written in to size or turn an element. `%`, `s`,
|
|
8
|
+
* `ms` and `pt` read as prose too ("50%", "5 s"), so a number printed with
|
|
9
|
+
* those is still a print. */
|
|
10
|
+
const CSS_UNIT = /^(?:px|r?em|ch|fr|deg|turn|[sdl]?v(?:h|w|min|max))(?![A-Za-z])/;
|
|
11
|
+
/** Does `span` write a number as a CSS length (`${w}px`), which the browser
|
|
12
|
+
* reads and no person does? East's printer (`180.0px`) and the locale
|
|
13
|
+
* formatters (`1.234,5px`) are wrong there, and JavaScript's number is right. */
|
|
14
|
+
function writesCssLength(span, ctx) {
|
|
15
|
+
if (!CSS_UNIT.test(span.literal.text))
|
|
16
|
+
return false;
|
|
17
|
+
const type = ctx.checker.getTypeAtLocation(span.expression);
|
|
18
|
+
const members = type.isUnion() ? type.types : [type];
|
|
19
|
+
return members.every((m) => (m.flags & ctx.ts.TypeFlags.NumberLike) !== 0);
|
|
20
|
+
}
|
|
21
|
+
/** The value `node` prints the JavaScript way, if it prints one. */
|
|
22
|
+
function printedValue(node, ctx) {
|
|
23
|
+
const t = ctx.ts;
|
|
24
|
+
if (t.isCallExpression(node)) {
|
|
25
|
+
const callee = node.expression;
|
|
26
|
+
// `String(x)`
|
|
27
|
+
if (t.isIdentifier(callee) && callee.text === "String" && node.arguments.length === 1 && isGlobalBuiltin(callee, ctx)) {
|
|
28
|
+
return [node.arguments[0]];
|
|
29
|
+
}
|
|
30
|
+
if (t.isPropertyAccessExpression(callee)) {
|
|
31
|
+
// `JSON.stringify(x, …)`
|
|
32
|
+
if (t.isIdentifier(callee.expression) && callee.expression.text === "JSON" && callee.name.text === "stringify" && node.arguments.length > 0 && isGlobalBuiltin(callee.expression, ctx)) {
|
|
33
|
+
return [node.arguments[0]];
|
|
34
|
+
}
|
|
35
|
+
// `x.toString()`, `x.toISOString()`, `x.toJSON()`
|
|
36
|
+
if (PRINTING_METHODS.has(callee.name.text))
|
|
37
|
+
return [callee.expression];
|
|
38
|
+
}
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
// `` `…${x}…` `` — an untagged template prints each span, save a number
|
|
42
|
+
// written as a CSS length.
|
|
43
|
+
if (t.isTemplateExpression(node) && !t.isTaggedTemplateExpression(node.parent)) {
|
|
44
|
+
return node.templateSpans.filter((span) => !writesCssLength(span, ctx)).map((span) => span.expression);
|
|
45
|
+
}
|
|
46
|
+
// `"…" + x`
|
|
47
|
+
if (t.isBinaryExpression(node) && node.operatorToken.kind === t.SyntaxKind.PlusToken) {
|
|
48
|
+
const stringy = (e) => (ctx.checker.getTypeAtLocation(e).flags & t.TypeFlags.StringLike) !== 0;
|
|
49
|
+
if (stringy(node.left))
|
|
50
|
+
return [node.right];
|
|
51
|
+
if (stringy(node.right))
|
|
52
|
+
return [node.left];
|
|
53
|
+
}
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
// A decoded East value printed the JavaScript way prints something East never
|
|
57
|
+
// does: a Float `5` for East's `5.0` (and `0` for `-0.0`), a DateTime in local
|
|
58
|
+
// time or with a `Z`, a Blob as `1,2,3`, a struct or variant as `[object Object]`
|
|
59
|
+
// or JSON East cannot read back. East prints a value with `printFor(T)`; a display
|
|
60
|
+
// prints it in the viewer's language with the locale `Formatters`. A String, an
|
|
61
|
+
// Integer and a Boolean print the same either way, so they pass, and so does a
|
|
62
|
+
// number written as a CSS length, which styles an element rather than telling
|
|
63
|
+
// anyone anything.
|
|
64
|
+
export const noHostPrintOfEastValues = {
|
|
65
|
+
name: NAME,
|
|
66
|
+
code: CODE,
|
|
67
|
+
description: "Flag String()/toString()/toISOString()/JSON.stringify/template printing of a decoded East value JavaScript prints differently — use printFor(T) or the locale Formatters.",
|
|
68
|
+
check(node, ctx) {
|
|
69
|
+
const t = ctx.ts;
|
|
70
|
+
if (!t.isCallExpression(node) && !t.isTemplateExpression(node) && !t.isBinaryExpression(node))
|
|
71
|
+
return;
|
|
72
|
+
if (!importsEastPackage(ctx.sourceFile, t))
|
|
73
|
+
return;
|
|
74
|
+
for (const value of printedValue(node, ctx)) {
|
|
75
|
+
if (!jsPrintDiffers(ctx.checker.getTypeAtLocation(value), t))
|
|
76
|
+
continue;
|
|
77
|
+
if (!isEastValueExpression(value, ctx))
|
|
78
|
+
continue;
|
|
79
|
+
const sf = ctx.sourceFile;
|
|
80
|
+
const start = value.getStart(sf);
|
|
81
|
+
ctx.report({
|
|
82
|
+
ruleName: NAME,
|
|
83
|
+
code: CODE,
|
|
84
|
+
start,
|
|
85
|
+
length: value.getEnd() - start,
|
|
86
|
+
messageText: "JavaScript prints this East value its own way — `5` for East's `5.0`, a DateTime in local time or with a `Z`, `[object Object]` for a struct. Print it with `printFor(T)`, or in the viewer's language with the locale `Formatters`.",
|
|
87
|
+
category: "warning",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=no-host-print-of-east-values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-host-print-of-east-values.js","sourceRoot":"","sources":["../../../src/rules/no-host-print-of-east-values.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-js-collection-for-east-collection.d.ts","sourceRoot":"","sources":["../../../src/rules/no-js-collection-for-east-collection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAyB,MAAM,aAAa,CAAC;AAsCnE,eAAO,MAAM,+BAA+B,EAAE,QA4B7C,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { importsEastPackage } from "../east-source.js";
|
|
2
|
+
import { isGlobalBuiltin, landsInEastSlot } from "../east-value.js";
|
|
3
|
+
const NAME = "no-js-collection-for-east-collection";
|
|
4
|
+
const CODE = 990036;
|
|
5
|
+
/** The key type of the `Set<K>` / `Map<K, V>` `node` builds: its first type argument. */
|
|
6
|
+
function keyTypeOf(node, ctx) {
|
|
7
|
+
const t = ctx.ts;
|
|
8
|
+
const type = ctx.checker.getTypeAtLocation(node);
|
|
9
|
+
if ((type.flags & t.TypeFlags.Object) === 0 || (type.objectFlags & t.ObjectFlags.Reference) === 0)
|
|
10
|
+
return undefined;
|
|
11
|
+
return ctx.checker.getTypeArguments(type)[0];
|
|
12
|
+
}
|
|
13
|
+
/** Does JavaScript find a key of `type` by identity? It does for an object (a struct,
|
|
14
|
+
* a variant, a DateTime's `Date`, a Blob's `Uint8Array`, a vector), and finds a
|
|
15
|
+
* string, a number, a bigint, a boolean or `null` by value. A branded primitive
|
|
16
|
+
* (`string & { brand }`) is a primitive at run time; `any`, `unknown` and a type
|
|
17
|
+
* parameter say nothing. */
|
|
18
|
+
function keyedByIdentity(type, t) {
|
|
19
|
+
const primitive = t.TypeFlags.StringLike | t.TypeFlags.NumberLike | t.TypeFlags.BigIntLike | t.TypeFlags.BooleanLike |
|
|
20
|
+
t.TypeFlags.EnumLike | t.TypeFlags.ESSymbolLike | t.TypeFlags.Null | t.TypeFlags.Undefined;
|
|
21
|
+
const members = type.isUnion() ? type.types : [type];
|
|
22
|
+
return members.some((m) => {
|
|
23
|
+
if (m.isIntersection())
|
|
24
|
+
return m.types.every((part) => (part.flags & primitive) === 0);
|
|
25
|
+
return (m.flags & (t.TypeFlags.Object | t.TypeFlags.NonPrimitive)) !== 0;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// East writes a Set or Dict keyed by strings, numbers, bigints, booleans or null
|
|
29
|
+
// as a JavaScript `Set` / `Map` (the `east` skill), and one keyed by structs,
|
|
30
|
+
// variants, DateTimes or Blobs as a `SortedSet` / `SortedMap` over
|
|
31
|
+
// `compareFor(K)`. JavaScript finds an object key by identity, so a plain
|
|
32
|
+
// collection of them never finds an equal key built elsewhere (the struct decoded
|
|
33
|
+
// from the store, the variant a callback returned) and holds two equal members
|
|
34
|
+
// side by side, where East finds keys by value. The order a plain collection
|
|
35
|
+
// keeps is East's to handle (#968), not this rule's.
|
|
36
|
+
export const noJsCollectionForEastCollection = {
|
|
37
|
+
name: NAME,
|
|
38
|
+
code: CODE,
|
|
39
|
+
description: "Flag new Set()/new Map() of object keys (structs, variants, DateTimes, Blobs) landing in an East Set or Dict — build SortedSet/SortedMap with compareFor(K).",
|
|
40
|
+
check(node, ctx) {
|
|
41
|
+
const t = ctx.ts;
|
|
42
|
+
if (!t.isNewExpression(node) || !t.isIdentifier(node.expression))
|
|
43
|
+
return;
|
|
44
|
+
const kind = node.expression.text;
|
|
45
|
+
if (kind !== "Set" && kind !== "Map")
|
|
46
|
+
return;
|
|
47
|
+
if (!importsEastPackage(ctx.sourceFile, t))
|
|
48
|
+
return;
|
|
49
|
+
if (!isGlobalBuiltin(node.expression, ctx))
|
|
50
|
+
return;
|
|
51
|
+
const key = keyTypeOf(node, ctx);
|
|
52
|
+
if (key === undefined || !keyedByIdentity(key, t))
|
|
53
|
+
return;
|
|
54
|
+
if (!landsInEastSlot(node, ctx))
|
|
55
|
+
return;
|
|
56
|
+
const sf = ctx.sourceFile;
|
|
57
|
+
const start = node.getStart(sf);
|
|
58
|
+
ctx.report({
|
|
59
|
+
ruleName: NAME,
|
|
60
|
+
code: CODE,
|
|
61
|
+
start,
|
|
62
|
+
length: node.getEnd() - start,
|
|
63
|
+
messageText: kind === "Set"
|
|
64
|
+
? "A JavaScript `Set` finds a struct, variant, DateTime or Blob member by identity, so an equal value built elsewhere misses it. Build `new SortedSet(values, compareFor(K))`."
|
|
65
|
+
: "A JavaScript `Map` finds a struct, variant, DateTime or Blob key by identity, so an equal key built elsewhere misses its entry. Build `new SortedMap(entries, compareFor(K))`.",
|
|
66
|
+
category: "warning",
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=no-js-collection-for-east-collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-js-collection-for-east-collection.js","sourceRoot":"","sources":["../../../src/rules/no-js-collection-for-east-collection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
import type { EastRule } from "../types.js";
|
|
6
|
+
export declare const noJsTypeDispatchOnEastValues: EastRule;
|
|
7
|
+
//# sourceMappingURL=no-js-type-dispatch-on-east-values.d.ts.map
|