@elaraai/tsserver-plugin-east 1.0.90 → 1.0.92
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/dist/index.cjs +122 -13
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -793,10 +793,112 @@ var noUnexecutedEastExpression = {
|
|
|
793
793
|
// ../east-diagnostics/dist/src/rules/no-reinlined-east-binding.js
|
|
794
794
|
var NAME10 = "no-reinlined-east-binding";
|
|
795
795
|
var CODE10 = 990010;
|
|
796
|
+
var REPEATING_METHODS = /* @__PURE__ */ new Set([
|
|
797
|
+
"map",
|
|
798
|
+
"filter",
|
|
799
|
+
"filterMap",
|
|
800
|
+
"firstMap",
|
|
801
|
+
"flatMap",
|
|
802
|
+
"flattenToArray",
|
|
803
|
+
"flattenToSet",
|
|
804
|
+
"flattenToDict",
|
|
805
|
+
"forEach",
|
|
806
|
+
"reduce",
|
|
807
|
+
"scan",
|
|
808
|
+
"mapReduce",
|
|
809
|
+
"groupReduce",
|
|
810
|
+
"groupBy",
|
|
811
|
+
"groupSize",
|
|
812
|
+
"groupSum",
|
|
813
|
+
"groupMean",
|
|
814
|
+
"groupEvery",
|
|
815
|
+
"groupSome",
|
|
816
|
+
"groupMaximum",
|
|
817
|
+
"groupMinimum",
|
|
818
|
+
"groupToArrays",
|
|
819
|
+
"groupToSets",
|
|
820
|
+
"groupToDicts",
|
|
821
|
+
"groupFindAll",
|
|
822
|
+
"groupFindFirst",
|
|
823
|
+
"groupFindMaximum",
|
|
824
|
+
"groupFindMinimum",
|
|
825
|
+
"toDict",
|
|
826
|
+
"toSet",
|
|
827
|
+
"toArray",
|
|
828
|
+
"toTree",
|
|
829
|
+
"sort",
|
|
830
|
+
"sortInPlace",
|
|
831
|
+
"isSorted",
|
|
832
|
+
"findFirst",
|
|
833
|
+
"findAll",
|
|
834
|
+
"findMaximum",
|
|
835
|
+
"findMinimum",
|
|
836
|
+
"findSortedFirst",
|
|
837
|
+
"findSortedLast",
|
|
838
|
+
"findSortedRange",
|
|
839
|
+
"mergeAll",
|
|
840
|
+
"unionInPlace",
|
|
841
|
+
"mapRows",
|
|
842
|
+
"some",
|
|
843
|
+
"every",
|
|
844
|
+
"sum",
|
|
845
|
+
"mean",
|
|
846
|
+
"maximum",
|
|
847
|
+
"minimum",
|
|
848
|
+
"getKeys",
|
|
849
|
+
"generate"
|
|
850
|
+
]);
|
|
851
|
+
var LOOP_METHODS = /* @__PURE__ */ new Set(["for", "while"]);
|
|
852
|
+
function isFunctionLike(node, t) {
|
|
853
|
+
return t.isArrowFunction(node) || t.isFunctionExpression(node) || t.isFunctionDeclaration(node) || t.isMethodDeclaration(node);
|
|
854
|
+
}
|
|
855
|
+
function enclosingFunction(node, t) {
|
|
856
|
+
let current = node.parent;
|
|
857
|
+
while (current !== void 0 && !isFunctionLike(current, t))
|
|
858
|
+
current = current.parent;
|
|
859
|
+
return current;
|
|
860
|
+
}
|
|
861
|
+
function hostKind(fn, ctx) {
|
|
862
|
+
const t = ctx.ts;
|
|
863
|
+
const call = fn.parent;
|
|
864
|
+
if (call === void 0 || !t.isCallExpression(call) || !call.arguments.some((arg) => arg === fn))
|
|
865
|
+
return void 0;
|
|
866
|
+
const callee = call.expression;
|
|
867
|
+
if (isEastFunctionCall(call, t))
|
|
868
|
+
return "nested function";
|
|
869
|
+
if (!t.isPropertyAccessExpression(callee))
|
|
870
|
+
return void 0;
|
|
871
|
+
const method = callee.name.text;
|
|
872
|
+
const receiver = callee.expression;
|
|
873
|
+
if (LOOP_METHODS.has(method)) {
|
|
874
|
+
return isBlockBuilderType(ctx.checker.getTypeAtLocation(receiver)) ? "loop body" : void 0;
|
|
875
|
+
}
|
|
876
|
+
if (!REPEATING_METHODS.has(method))
|
|
877
|
+
return void 0;
|
|
878
|
+
if (isEastExprType(ctx.checker.getTypeAtLocation(receiver)))
|
|
879
|
+
return "callback";
|
|
880
|
+
if (t.isPropertyAccessExpression(receiver) && t.isIdentifier(receiver.expression) && receiver.expression.text === "East") {
|
|
881
|
+
return "callback";
|
|
882
|
+
}
|
|
883
|
+
return void 0;
|
|
884
|
+
}
|
|
885
|
+
function repeatingHostBetween(use, declFunction, ctx) {
|
|
886
|
+
let found;
|
|
887
|
+
let current = use.parent;
|
|
888
|
+
while (current !== void 0 && current !== declFunction) {
|
|
889
|
+
if (isFunctionLike(current, ctx.ts)) {
|
|
890
|
+
const kind = hostKind(current, ctx);
|
|
891
|
+
if (kind !== void 0)
|
|
892
|
+
found = kind;
|
|
893
|
+
}
|
|
894
|
+
current = current.parent;
|
|
895
|
+
}
|
|
896
|
+
return found;
|
|
897
|
+
}
|
|
796
898
|
var noReinlinedEastBinding = {
|
|
797
899
|
name: NAME10,
|
|
798
900
|
code: CODE10,
|
|
799
|
-
description: "An East Expr bound to a JS const/let and
|
|
901
|
+
description: "An East Expr bound to a JS const/let and read twice inside one East body, or read inside a per-element callback, a loop body or a nested East.function, is re-inlined \u2014 and re-evaluated \u2014 at each read; bind it once with $.let/$.const.",
|
|
800
902
|
check(node, ctx) {
|
|
801
903
|
const t = ctx.ts;
|
|
802
904
|
if (!t.isVariableDeclaration(node))
|
|
@@ -818,32 +920,39 @@ var noReinlinedEastBinding = {
|
|
|
818
920
|
if (declSymbol === void 0)
|
|
819
921
|
return;
|
|
820
922
|
const name = node.name.text;
|
|
821
|
-
const
|
|
923
|
+
const declFunction = enclosingFunction(node, t);
|
|
924
|
+
const perFunction = /* @__PURE__ */ new Map();
|
|
925
|
+
let repeating;
|
|
822
926
|
const visit = (n) => {
|
|
823
|
-
if (t.isIdentifier(n) && n !== node.name && n.text === name) {
|
|
824
|
-
if (
|
|
825
|
-
const
|
|
826
|
-
if (
|
|
827
|
-
|
|
927
|
+
if (t.isIdentifier(n) && n !== node.name && n.text === name && ctx.checker.getSymbolAtLocation(n) === declSymbol) {
|
|
928
|
+
if (enclosingBlockScope(n, ctx) !== void 0) {
|
|
929
|
+
const multiplied = repeatingHostBetween(n, declFunction, ctx);
|
|
930
|
+
if (multiplied !== void 0) {
|
|
931
|
+
repeating ??= multiplied;
|
|
932
|
+
} else {
|
|
933
|
+
const bucket = enclosingFunction(n, t) ?? ctx.sourceFile;
|
|
934
|
+
perFunction.set(bucket, (perFunction.get(bucket) ?? 0) + 1);
|
|
935
|
+
}
|
|
828
936
|
}
|
|
829
937
|
}
|
|
830
938
|
t.forEachChild(n, visit);
|
|
831
939
|
};
|
|
832
940
|
visit(ctx.sourceFile);
|
|
833
|
-
let
|
|
834
|
-
for (const count of
|
|
835
|
-
if (count >
|
|
836
|
-
|
|
837
|
-
if (
|
|
941
|
+
let worst = 0;
|
|
942
|
+
for (const count of perFunction.values())
|
|
943
|
+
if (count > worst)
|
|
944
|
+
worst = count;
|
|
945
|
+
if (worst < 2 && repeating === void 0)
|
|
838
946
|
return;
|
|
839
947
|
const sf = ctx.sourceFile;
|
|
840
948
|
const start = node.getStart(sf);
|
|
949
|
+
const messageText = worst >= 2 ? `This East expression is bound to a JS \`const\`/\`let\` and read ${worst} times inside one East body, so it is re-inlined \u2014 and re-evaluated, with a fresh identity for mutable values \u2014 at each read. Bind it once with \`$.const\`/\`$.let\`.` : `This East expression is bound to a JS \`const\`/\`let\` outside a ${repeating} and read inside it, so it is re-inlined there \u2014 and re-evaluated every time the ${repeating} runs. Bind it once with \`$.const\`/\`$.let\` before the call.`;
|
|
841
950
|
ctx.report({
|
|
842
951
|
ruleName: NAME10,
|
|
843
952
|
code: CODE10,
|
|
844
953
|
start,
|
|
845
954
|
length: node.getEnd() - start,
|
|
846
|
-
messageText
|
|
955
|
+
messageText,
|
|
847
956
|
category: "error"
|
|
848
957
|
});
|
|
849
958
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elaraai/tsserver-plugin-east",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.92",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"description": "TypeScript language service plugin for East — East idiom diagnostics and localized East type-diff error messages as native editor squiggles.",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"esbuild": "^0.27.3",
|
|
34
34
|
"typescript": "~5.9.2",
|
|
35
|
-
"@elaraai/east
|
|
36
|
-
"@elaraai/east": "1.0.
|
|
35
|
+
"@elaraai/east": "1.0.92",
|
|
36
|
+
"@elaraai/east-diagnostics": "1.0.92"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "node scripts/build.mjs",
|