@motifx/compiler 1.1.0 → 1.2.0-rc.1
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 +2 -2
- package/bin/motif-lint.cjs +4 -2
- package/dist/index.cjs +720 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.esm.js +720 -49
- package/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -15,6 +15,7 @@ var htmlTags = require('html-tags');
|
|
|
15
15
|
var svgTags = require('svg-tags');
|
|
16
16
|
var generatorNs = require('@babel/generator');
|
|
17
17
|
var path = require('node:path');
|
|
18
|
+
var node_fs = require('node:fs');
|
|
18
19
|
var ts = require('typescript');
|
|
19
20
|
var pPath = require('path');
|
|
20
21
|
|
|
@@ -510,12 +511,6 @@ const makeHtmlAttr = (prop, name, value, path) => {
|
|
|
510
511
|
return t__namespace.objectProperty(t__namespace.stringLiteral(name), value || t__namespace.booleanLiteral(true));
|
|
511
512
|
}
|
|
512
513
|
else if (t__namespace.isArrowFunctionExpression(value) && value !== null) {
|
|
513
|
-
if (t__namespace.isBlockStatement(value.body)) {
|
|
514
|
-
t__namespace.functionExpression(null, value.params, value.body);
|
|
515
|
-
}
|
|
516
|
-
else {
|
|
517
|
-
t__namespace.functionExpression(null, value.params, t__namespace.blockStatement([t__namespace.returnStatement(value.body)]));
|
|
518
|
-
}
|
|
519
514
|
return t__namespace.objectProperty(t__namespace.stringLiteral(name), value);
|
|
520
515
|
}
|
|
521
516
|
else if (t__namespace.isFunctionExpression(value)) {
|
|
@@ -740,33 +735,9 @@ const mergeHtmlAttributes = (htmlAttrlist) => {
|
|
|
740
735
|
};
|
|
741
736
|
const makeHtmlEvents = (prop, name, value, path) => {
|
|
742
737
|
if (!t__namespace.isStringLiteral(value) && !t__namespace.isBooleanLiteral(value) && value !== null) {
|
|
743
|
-
if (t__namespace.
|
|
744
|
-
var el;
|
|
745
|
-
if (t__namespace.isBlockStatement(value.body)) {
|
|
746
|
-
el = t__namespace.arrowFunctionExpression(value.params, value.body);
|
|
747
|
-
}
|
|
748
|
-
else {
|
|
749
|
-
el = t__namespace.arrowFunctionExpression(value.params, t__namespace.blockStatement([t__namespace.returnStatement(value.body)]));
|
|
750
|
-
}
|
|
751
|
-
var evName = name.toLowerCase().replace("on", '').replace('doubleclick', 'dblclick');
|
|
752
|
-
return t__namespace.callExpression(t__namespace.identifier("sender.motif.on"), [t__namespace.stringLiteral(evName), el]);
|
|
753
|
-
}
|
|
754
|
-
else if (t__namespace.isFunctionExpression(value)) {
|
|
755
|
-
var el;
|
|
756
|
-
if (t__namespace.isBlockStatement(value.body)) {
|
|
757
|
-
el = t__namespace.functionExpression(null, value.params, value.body);
|
|
758
|
-
}
|
|
759
|
-
else {
|
|
760
|
-
el = t__namespace.functionExpression(null, value.params, t__namespace.blockStatement([t__namespace.returnStatement(value.body)]));
|
|
761
|
-
}
|
|
738
|
+
if (t__namespace.isExpression(value)) {
|
|
762
739
|
var evName = name.toLowerCase().replace("on", '').replace('doubleclick', 'dblclick');
|
|
763
|
-
return t__namespace.callExpression(t__namespace.identifier("sender.motif.on"), [t__namespace.stringLiteral(evName),
|
|
764
|
-
}
|
|
765
|
-
else {
|
|
766
|
-
if (t__namespace.isExpression(value)) {
|
|
767
|
-
var evName = name.toLowerCase().replace("on", '').replace('doubleclick', 'dblclick');
|
|
768
|
-
return t__namespace.callExpression(t__namespace.identifier("sender.motif.on"), [t__namespace.stringLiteral(evName), value]);
|
|
769
|
-
}
|
|
740
|
+
return t__namespace.callExpression(t__namespace.identifier("sender.motif.on"), [t__namespace.stringLiteral(evName), value]);
|
|
770
741
|
}
|
|
771
742
|
}
|
|
772
743
|
else {
|
|
@@ -823,7 +794,10 @@ const ParentIfStatement = (path, state) => {
|
|
|
823
794
|
else ;
|
|
824
795
|
return state;
|
|
825
796
|
};
|
|
826
|
-
const returnIfStatement = (value, enableDefaultreturn = true, defaultLiteral = t__namespace.booleanLiteral(false)) => {
|
|
797
|
+
const returnIfStatement = (value, enableDefaultreturn = true, defaultLiteral = t__namespace.booleanLiteral(false), callFunctions = false) => {
|
|
798
|
+
if (callFunctions && t__namespace.isFunctionExpression(value)) {
|
|
799
|
+
return value;
|
|
800
|
+
}
|
|
827
801
|
var ifStatement = findIfStatements(value);
|
|
828
802
|
var mx = t__namespace.binaryExpression('===', t__namespace.unaryExpression('typeof', value), t__namespace.stringLiteral('function'));
|
|
829
803
|
var rtrn;
|
|
@@ -837,13 +811,15 @@ const returnIfStatement = (value, enableDefaultreturn = true, defaultLiteral = t
|
|
|
837
811
|
}
|
|
838
812
|
}
|
|
839
813
|
else if (t__namespace.isMemberExpression(value) || t__namespace.isIdentifier(value)) {
|
|
840
|
-
rtrn = t__namespace.returnStatement(
|
|
814
|
+
rtrn = t__namespace.returnStatement(callFunctions
|
|
815
|
+
? t__namespace.conditionalExpression(t__namespace.binaryExpression('===', t__namespace.unaryExpression('typeof', value), t__namespace.stringLiteral('function')), t__namespace.callExpression(value, []), value)
|
|
816
|
+
: value);
|
|
841
817
|
var rtrns = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([rtrn]));
|
|
842
818
|
if (enableDefaultreturn) {
|
|
843
|
-
return t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns, t__namespace.returnStatement(defaultLiteral)]));
|
|
819
|
+
return cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns, t__namespace.returnStatement(defaultLiteral)])), [value]);
|
|
844
820
|
}
|
|
845
821
|
else {
|
|
846
|
-
return t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns]));
|
|
822
|
+
return cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns])), [value]);
|
|
847
823
|
}
|
|
848
824
|
}
|
|
849
825
|
else if (t__namespace.isFunctionExpression(value)) {
|
|
@@ -862,20 +838,20 @@ const returnIfStatement = (value, enableDefaultreturn = true, defaultLiteral = t
|
|
|
862
838
|
var le = t__namespace.logicalExpression('&&', ifStatementA, ifStatementB);
|
|
863
839
|
rtrn = t__namespace.returnStatement(value);
|
|
864
840
|
if (enableDefaultreturn) {
|
|
865
|
-
return t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([t__namespace.ifStatement(le, t__namespace.blockStatement([rtrn])), t__namespace.returnStatement(defaultLiteral)]));
|
|
841
|
+
return cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([t__namespace.ifStatement(le, t__namespace.blockStatement([rtrn])), t__namespace.returnStatement(defaultLiteral)])), [value.left, value.right]);
|
|
866
842
|
}
|
|
867
843
|
else {
|
|
868
|
-
return t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([t__namespace.ifStatement(le, t__namespace.blockStatement([rtrn]))]));
|
|
844
|
+
return cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([t__namespace.ifStatement(le, t__namespace.blockStatement([rtrn]))])), [value.left, value.right]);
|
|
869
845
|
}
|
|
870
846
|
}
|
|
871
847
|
else if (t__namespace.isCallExpression(value)) {
|
|
872
848
|
rtrn = t__namespace.returnStatement(t__namespace.conditionalExpression(mx, t__namespace.callExpression(value, []), value));
|
|
873
849
|
var rtrns = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([rtrn]));
|
|
874
850
|
if (enableDefaultreturn) {
|
|
875
|
-
return t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns, t__namespace.returnStatement(defaultLiteral)]));
|
|
851
|
+
return cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns, t__namespace.returnStatement(defaultLiteral)])), [value]);
|
|
876
852
|
}
|
|
877
853
|
else {
|
|
878
|
-
return t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns]));
|
|
854
|
+
return cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns])), [value]);
|
|
879
855
|
}
|
|
880
856
|
}
|
|
881
857
|
else {
|
|
@@ -945,6 +921,95 @@ const findIfStatements = (value) => {
|
|
|
945
921
|
}
|
|
946
922
|
return ifStatement;
|
|
947
923
|
};
|
|
924
|
+
const isEffectNode = (node) => t__namespace.isCallExpression(node) || t__namespace.isOptionalCallExpression(node) || t__namespace.isNewExpression(node) ||
|
|
925
|
+
t__namespace.isTaggedTemplateExpression(node) || t__namespace.isUpdateExpression(node) || t__namespace.isAssignmentExpression(node) ||
|
|
926
|
+
t__namespace.isAwaitExpression(node) || t__namespace.isYieldExpression(node);
|
|
927
|
+
const hasEffect = (node) => {
|
|
928
|
+
if (!node || t__namespace.isFunction(node) || t__namespace.isClass(node))
|
|
929
|
+
return false;
|
|
930
|
+
if (isEffectNode(node))
|
|
931
|
+
return true;
|
|
932
|
+
for (const key of t__namespace.VISITOR_KEYS[node.type] ?? []) {
|
|
933
|
+
const child = node[key];
|
|
934
|
+
if (Array.isArray(child) ? child.some(c => hasEffect(c)) : hasEffect(child))
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
937
|
+
return false;
|
|
938
|
+
};
|
|
939
|
+
const guardCallLinks = (root, found) => {
|
|
940
|
+
if (!t__namespace.isExpression(root) || t__namespace.isFunction(root))
|
|
941
|
+
return;
|
|
942
|
+
if (!t__namespace.isIdentifier(root) && !t__namespace.isMemberExpression(root) && !t__namespace.isThisExpression(root) && !t__namespace.isLiteral(root) && hasEffect(root)) {
|
|
943
|
+
found.add(root);
|
|
944
|
+
}
|
|
945
|
+
if (t__namespace.isMemberExpression(root))
|
|
946
|
+
guardCallLinks(root.object, found);
|
|
947
|
+
else if (t__namespace.isCallExpression(root) && t__namespace.isExpression(root.callee))
|
|
948
|
+
guardCallLinks(root.callee, found);
|
|
949
|
+
};
|
|
950
|
+
const collectNames = (node, names) => {
|
|
951
|
+
if (!node)
|
|
952
|
+
return;
|
|
953
|
+
if (t__namespace.isIdentifier(node))
|
|
954
|
+
names.add(node.name);
|
|
955
|
+
for (const key of t__namespace.VISITOR_KEYS[node.type] ?? []) {
|
|
956
|
+
const child = node[key];
|
|
957
|
+
if (Array.isArray(child))
|
|
958
|
+
child.forEach(c => collectNames(c, names));
|
|
959
|
+
else
|
|
960
|
+
collectNames(child, names);
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
const cacheGuardCalls = (fn, roots) => {
|
|
964
|
+
if (!t__namespace.isBlockStatement(fn.body))
|
|
965
|
+
return fn;
|
|
966
|
+
const links = new Set();
|
|
967
|
+
roots.forEach(r => guardCallLinks(r, links));
|
|
968
|
+
if (links.size === 0)
|
|
969
|
+
return fn;
|
|
970
|
+
const used = new Set();
|
|
971
|
+
collectNames(fn, used);
|
|
972
|
+
const assigned = new Map();
|
|
973
|
+
let counter = 0;
|
|
974
|
+
const nextName = () => {
|
|
975
|
+
let name;
|
|
976
|
+
do {
|
|
977
|
+
name = `__r${++counter}`;
|
|
978
|
+
} while (used.has(name));
|
|
979
|
+
return name;
|
|
980
|
+
};
|
|
981
|
+
const rewrite = (node) => {
|
|
982
|
+
if (!node || typeof node !== 'object' || !node.type)
|
|
983
|
+
return node;
|
|
984
|
+
if (links.has(node)) {
|
|
985
|
+
const known = assigned.get(node);
|
|
986
|
+
if (known)
|
|
987
|
+
return t__namespace.identifier(known);
|
|
988
|
+
const name = nextName();
|
|
989
|
+
assigned.set(node, name);
|
|
990
|
+
return t__namespace.assignmentExpression('=', t__namespace.identifier(name), rewriteChildren(node));
|
|
991
|
+
}
|
|
992
|
+
if (t__namespace.isFunction(node) || t__namespace.isClass(node))
|
|
993
|
+
return node;
|
|
994
|
+
return rewriteChildren(node);
|
|
995
|
+
};
|
|
996
|
+
const rewriteChildren = (node) => {
|
|
997
|
+
const copy = t__namespace.cloneNode(node, false);
|
|
998
|
+
for (const key of t__namespace.VISITOR_KEYS[node.type] ?? []) {
|
|
999
|
+
const child = node[key];
|
|
1000
|
+
copy[key] = Array.isArray(child) ? child.map(rewrite) : rewrite(child);
|
|
1001
|
+
}
|
|
1002
|
+
return copy;
|
|
1003
|
+
};
|
|
1004
|
+
const body = rewriteChildren(fn.body);
|
|
1005
|
+
if (assigned.size === 0)
|
|
1006
|
+
return fn;
|
|
1007
|
+
const declaration = t__namespace.variableDeclaration('let', [...assigned.values()].map(name => t__namespace.variableDeclarator(t__namespace.identifier(name))));
|
|
1008
|
+
body.body.unshift(declaration);
|
|
1009
|
+
const result = t__namespace.cloneNode(fn, false);
|
|
1010
|
+
result.body = body;
|
|
1011
|
+
return result;
|
|
1012
|
+
};
|
|
948
1013
|
const makeDirectives = (prop, name, value, path, state) => {
|
|
949
1014
|
var result = null;
|
|
950
1015
|
if (value != null) {
|
|
@@ -983,7 +1048,7 @@ const makeDirectives = (prop, name, value, path, state) => {
|
|
|
983
1048
|
}
|
|
984
1049
|
else {
|
|
985
1050
|
if (t__namespace.isExpression(value)) {
|
|
986
|
-
result = t__namespace.expressionStatement(t__namespace.callExpression(t__namespace.identifier(callerName), [returnIfStatement(value, false)]));
|
|
1051
|
+
result = t__namespace.expressionStatement(t__namespace.callExpression(t__namespace.identifier(callerName), [returnIfStatement(value, false, t__namespace.booleanLiteral(false), true)]));
|
|
987
1052
|
}
|
|
988
1053
|
}
|
|
989
1054
|
}
|
|
@@ -1065,7 +1130,7 @@ const makePreDirectives = (prop, name, value, path) => {
|
|
|
1065
1130
|
fail('MJX012', `Directive "${name}" does not accept an object literal.`, prop);
|
|
1066
1131
|
}
|
|
1067
1132
|
else if (t__namespace.isExpression(value)) {
|
|
1068
|
-
result = t__namespace.expressionStatement(t__namespace.callExpression(t__namespace.identifier(callerName), [returnIfStatement(value, true, t__namespace.booleanLiteral(true))]));
|
|
1133
|
+
result = t__namespace.expressionStatement(t__namespace.callExpression(t__namespace.identifier(callerName), [returnIfStatement(value, true, t__namespace.booleanLiteral(true), true)]));
|
|
1069
1134
|
}
|
|
1070
1135
|
}
|
|
1071
1136
|
else {
|
|
@@ -1606,7 +1671,7 @@ const getProps = (props, path, state) => {
|
|
|
1606
1671
|
refEntries.push({ prop, lowered: t__namespace.arrowFunctionExpression([t__namespace.identifier('sender')], t__namespace.blockStatement(body)) });
|
|
1607
1672
|
}
|
|
1608
1673
|
else if (t__namespace.isFunctionExpression(value) || t__namespace.isArrowFunctionExpression(value)) {
|
|
1609
|
-
refEntries.push({ prop, lowered: t__namespace.arrowFunctionExpression(value.params, value.body) });
|
|
1674
|
+
refEntries.push({ prop, lowered: t__namespace.arrowFunctionExpression(value.params, value.body, value.async) });
|
|
1610
1675
|
}
|
|
1611
1676
|
else if (t__namespace.isExpression(value)) {
|
|
1612
1677
|
var cx = t__namespace.expressionStatement(value);
|
|
@@ -2239,7 +2304,7 @@ const mkText = (exp) => {
|
|
|
2239
2304
|
var mx = t__namespace.binaryExpression('===', t__namespace.unaryExpression('typeof', exp), t__namespace.stringLiteral('function'));
|
|
2240
2305
|
var rtrn = t__namespace.returnStatement(t__namespace.conditionalExpression(mx, t__namespace.callExpression(exp, []), t__namespace.arrowFunctionExpression([], exp)));
|
|
2241
2306
|
var rtrns = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([rtrn]));
|
|
2242
|
-
var kx = t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns]));
|
|
2307
|
+
var kx = cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns])), [exp]);
|
|
2243
2308
|
a1 = t__namespace.callExpression(id('sender.bindings.add'), [t__namespace.stringLiteral("textContent"), kx]);
|
|
2244
2309
|
}
|
|
2245
2310
|
}
|
|
@@ -2305,7 +2370,7 @@ const mkText = (exp) => {
|
|
|
2305
2370
|
// var a1 = t.callExpression(id('sender.bind.logic'), [kx, exp.right]);
|
|
2306
2371
|
var rtrns = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([t__namespace.returnStatement(exp.left)]));
|
|
2307
2372
|
var tryStatement = t__namespace.tryStatement(t__namespace.blockStatement([rtrns]), t__namespace.catchClause(null, t__namespace.blockStatement([t__namespace.returnStatement(t__namespace.nullLiteral())])), null);
|
|
2308
|
-
var kx = t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([tryStatement]));
|
|
2373
|
+
var kx = cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([tryStatement])), [exp.left]);
|
|
2309
2374
|
if (!t__namespace.isFunctionDeclaration(exp.right) && !t__namespace.isArrowFunctionExpression(exp.right)) {
|
|
2310
2375
|
t__namespace.ifStatement(t__namespace.identifier('__v'), t__namespace.blockStatement([t__namespace.returnStatement(exp.right)]), t__namespace.blockStatement([t__namespace.returnStatement(t__namespace.callExpression(t__namespace.identifier(motifComponent()), [t__namespace.objectExpression([])]))]));
|
|
2311
2376
|
exp.right = t__namespace.arrowFunctionExpression([t__namespace.identifier("__v")], t__namespace.blockStatement([t__namespace.returnStatement(exp.right)]));
|
|
@@ -2325,7 +2390,7 @@ const mkText = (exp) => {
|
|
|
2325
2390
|
var mx = t__namespace.binaryExpression('===', t__namespace.unaryExpression('typeof', exp), t__namespace.stringLiteral('function'));
|
|
2326
2391
|
var rtrn = t__namespace.returnStatement(t__namespace.conditionalExpression(mx, t__namespace.callExpression(exp, []), t__namespace.arrowFunctionExpression([], exp)));
|
|
2327
2392
|
var rtrns = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([rtrn]));
|
|
2328
|
-
var kx = t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns]));
|
|
2393
|
+
var kx = cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns])), [exp]);
|
|
2329
2394
|
a1 = t__namespace.callExpression(id('sender.bindings.add'), [t__namespace.stringLiteral("textContent"), kx]);
|
|
2330
2395
|
}
|
|
2331
2396
|
else {
|
|
@@ -2333,7 +2398,7 @@ const mkText = (exp) => {
|
|
|
2333
2398
|
var mx = t__namespace.binaryExpression('===', t__namespace.unaryExpression('typeof', exp), t__namespace.stringLiteral('function'));
|
|
2334
2399
|
var rtrn = t__namespace.returnStatement(t__namespace.conditionalExpression(mx, t__namespace.callExpression(exp, []), t__namespace.arrowFunctionExpression([], exp)));
|
|
2335
2400
|
var rtrns = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([rtrn]));
|
|
2336
|
-
var kx = t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns]));
|
|
2401
|
+
var kx = cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([rtrns])), [exp]);
|
|
2337
2402
|
a1 = t__namespace.callExpression(id('sender.bindings.add'), [t__namespace.stringLiteral("textContent"), kx]);
|
|
2338
2403
|
}
|
|
2339
2404
|
var arrayBody = t__namespace.arrowFunctionExpression([id('sender')], t__namespace.blockStatement([t__namespace.expressionStatement(a1)]));
|
|
@@ -2499,7 +2564,7 @@ function ParseComponent(path, state) {
|
|
|
2499
2564
|
// var rtrn = t.returnStatement(t.conditionalExpression(mx, t.callExpression(cx.left, []), t.arrowFunctionExpression([], cx.left)));
|
|
2500
2565
|
var rtrns = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([t__namespace.returnStatement(cx.left)]));
|
|
2501
2566
|
var tryStatement = t__namespace.tryStatement(t__namespace.blockStatement([rtrns]), t__namespace.catchClause(null, t__namespace.blockStatement([t__namespace.returnStatement(t__namespace.nullLiteral())])), null);
|
|
2502
|
-
var kx = t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([tryStatement]));
|
|
2567
|
+
var kx = cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([tryStatement])), [cx.left]);
|
|
2503
2568
|
if (!t__namespace.isFunctionDeclaration(cx.right) && !t__namespace.isArrowFunctionExpression(cx.right)) {
|
|
2504
2569
|
t__namespace.ifStatement(t__namespace.identifier('__v'), t__namespace.blockStatement([t__namespace.returnStatement(cx.right)]), t__namespace.blockStatement([t__namespace.returnStatement(t__namespace.callExpression(t__namespace.identifier(motifComponent()), [t__namespace.objectExpression([])]))]));
|
|
2505
2570
|
cx.right = t__namespace.arrowFunctionExpression([t__namespace.identifier("__v")], t__namespace.blockStatement([t__namespace.returnStatement(cx.right)]));
|
|
@@ -2644,7 +2709,7 @@ function ParseFrament(path, state) {
|
|
|
2644
2709
|
else {
|
|
2645
2710
|
var rtrnsPlain = t__namespace.ifStatement(ifStatement, t__namespace.blockStatement([t__namespace.returnStatement(exp.left)]));
|
|
2646
2711
|
var tryStatement = t__namespace.tryStatement(t__namespace.blockStatement([rtrnsPlain]), t__namespace.catchClause(null, t__namespace.blockStatement([t__namespace.returnStatement(t__namespace.nullLiteral())])), null);
|
|
2647
|
-
condFn = t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([tryStatement]));
|
|
2712
|
+
condFn = cacheGuardCalls(t__namespace.arrowFunctionExpression([], t__namespace.blockStatement([tryStatement])), [exp.left]);
|
|
2648
2713
|
}
|
|
2649
2714
|
if (!t__namespace.isFunctionDeclaration(exp.right) && !t__namespace.isArrowFunctionExpression(exp.right)) {
|
|
2650
2715
|
exp.right = t__namespace.arrowFunctionExpression([t__namespace.identifier("val")], t__namespace.blockStatement([t__namespace.returnStatement(exp.right)]));
|
|
@@ -3153,11 +3218,492 @@ class Compiler {
|
|
|
3153
3218
|
}
|
|
3154
3219
|
}
|
|
3155
3220
|
|
|
3221
|
+
const SUPER_METHODS = ['build', 'setState', 'reState', 'setText', 'style', 'dispose', 'disposeAsync', 'using', 'doWork', 'getService', '$', 'useModel'];
|
|
3222
|
+
const SUPER_ACCESSORS = ['context', 'siblings', 'serviceProvider', 'isWait'];
|
|
3223
|
+
const CORE_COMPONENT_CLASSES = ['Component', 'ComponentBase', 'ContentBlock', 'ContentBody', 'FragmentNode', 'Frame', 'RouterLink', 'RouterView', 'Transport', 'TransportTo', 'Virtualization'];
|
|
3224
|
+
const coreComponents = new Set(CORE_COMPONENT_CLASSES);
|
|
3225
|
+
const superNames = new Set([...SUPER_METHODS, ...SUPER_ACCESSORS]);
|
|
3226
|
+
const quickMember = new RegExp(`(?:^|[\\s;{}])(?:async\\s+|get\\s+|set\\s+|override\\s+|public\\s+|protected\\s+)*(?:${[...superNames].map(n => n.replace('$', '\\$')).join('|')})\\s*\\(`, 'm');
|
|
3227
|
+
function mayHaveSuperOverrides(code) {
|
|
3228
|
+
return /\bextends\b/.test(code) && quickMember.test(code);
|
|
3229
|
+
}
|
|
3230
|
+
function isCoreSource(source) {
|
|
3231
|
+
return source === '@motifx/core' || source.startsWith('@motifx/core/');
|
|
3232
|
+
}
|
|
3233
|
+
function isCoreComponent(name) {
|
|
3234
|
+
return coreComponents.has(name);
|
|
3235
|
+
}
|
|
3236
|
+
function parseModule(code, filename) {
|
|
3237
|
+
const isTs = /\.[mc]?tsx?$/.test(filename);
|
|
3238
|
+
const isTsx = /\.tsx$/.test(filename);
|
|
3239
|
+
const plugins = isTs ? ['typescript', 'decorators-legacy', 'classProperties'] : ['jsx', 'decorators-legacy', 'classProperties'];
|
|
3240
|
+
if (isTsx)
|
|
3241
|
+
plugins.push('jsx');
|
|
3242
|
+
try {
|
|
3243
|
+
const ast = babel__namespace.parseSync(code, {
|
|
3244
|
+
filename,
|
|
3245
|
+
babelrc: false,
|
|
3246
|
+
configFile: false,
|
|
3247
|
+
browserslistConfigFile: false,
|
|
3248
|
+
sourceType: 'unambiguous',
|
|
3249
|
+
parserOpts: { plugins, errorRecovery: true },
|
|
3250
|
+
});
|
|
3251
|
+
return ast ?? null;
|
|
3252
|
+
}
|
|
3253
|
+
catch {
|
|
3254
|
+
return null;
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
function isSuperMember(node, name) {
|
|
3258
|
+
if (!node || !(t__namespace.isMemberExpression(node) || t__namespace.isOptionalMemberExpression(node)) || !t__namespace.isSuper(node.object))
|
|
3259
|
+
return false;
|
|
3260
|
+
if (!node.computed && t__namespace.isIdentifier(node.property))
|
|
3261
|
+
return node.property.name === name;
|
|
3262
|
+
return t__namespace.isStringLiteral(node.property) && node.property.value === name;
|
|
3263
|
+
}
|
|
3264
|
+
function directUse(node, name, use) {
|
|
3265
|
+
if (use === 'call' && (t__namespace.isCallExpression(node) || t__namespace.isOptionalCallExpression(node))) {
|
|
3266
|
+
const callee = node.callee;
|
|
3267
|
+
if (isSuperMember(callee, name))
|
|
3268
|
+
return true;
|
|
3269
|
+
if ((t__namespace.isMemberExpression(callee) || t__namespace.isOptionalMemberExpression(callee)) && isSuperMember(callee.object, name)
|
|
3270
|
+
&& t__namespace.isIdentifier(callee.property) && (callee.property.name === 'call' || callee.property.name === 'apply'))
|
|
3271
|
+
return true;
|
|
3272
|
+
}
|
|
3273
|
+
if (use === 'get' && isSuperMember(node, name))
|
|
3274
|
+
return true;
|
|
3275
|
+
if (use === 'set' && t__namespace.isAssignmentExpression(node) && isSuperMember(node.left, name))
|
|
3276
|
+
return true;
|
|
3277
|
+
return false;
|
|
3278
|
+
}
|
|
3279
|
+
function definitely(node, name, use) {
|
|
3280
|
+
if (!node || typeof node !== 'object')
|
|
3281
|
+
return false;
|
|
3282
|
+
if (t__namespace.isFunction(node) || t__namespace.isClass(node))
|
|
3283
|
+
return false;
|
|
3284
|
+
if (directUse(node, name, use))
|
|
3285
|
+
return true;
|
|
3286
|
+
if (use === 'get' && t__namespace.isAssignmentExpression(node) && isSuperMember(node.left, name))
|
|
3287
|
+
return definitely(node.right, name, use);
|
|
3288
|
+
if (t__namespace.isLogicalExpression(node))
|
|
3289
|
+
return definitely(node.left, name, use);
|
|
3290
|
+
if (t__namespace.isConditionalExpression(node))
|
|
3291
|
+
return definitely(node.test, name, use) || (definitely(node.consequent, name, use) && definitely(node.alternate, name, use));
|
|
3292
|
+
if (t__namespace.isOptionalCallExpression(node) || t__namespace.isOptionalMemberExpression(node)) {
|
|
3293
|
+
const head = t__namespace.isOptionalCallExpression(node) ? node.callee : node.object;
|
|
3294
|
+
return definitely(head, name, use);
|
|
3295
|
+
}
|
|
3296
|
+
for (const key of t__namespace.VISITOR_KEYS[node.type] ?? []) {
|
|
3297
|
+
const child = node[key];
|
|
3298
|
+
if (Array.isArray(child) ? child.some(c => definitely(c, name, use)) : definitely(child, name, use))
|
|
3299
|
+
return true;
|
|
3300
|
+
}
|
|
3301
|
+
return false;
|
|
3302
|
+
}
|
|
3303
|
+
function mentions(node, name, use) {
|
|
3304
|
+
if (!node || typeof node !== 'object')
|
|
3305
|
+
return false;
|
|
3306
|
+
if (directUse(node, name, use))
|
|
3307
|
+
return true;
|
|
3308
|
+
if (t__namespace.isClass(node))
|
|
3309
|
+
return false;
|
|
3310
|
+
for (const key of t__namespace.VISITOR_KEYS[node.type] ?? []) {
|
|
3311
|
+
const child = node[key];
|
|
3312
|
+
if (Array.isArray(child) ? child.some(c => mentions(c, name, use)) : mentions(child, name, use))
|
|
3313
|
+
return true;
|
|
3314
|
+
}
|
|
3315
|
+
return false;
|
|
3316
|
+
}
|
|
3317
|
+
const join = (a, b) => a === null ? b : b === null ? a : a && b;
|
|
3318
|
+
const GUARDS = {
|
|
3319
|
+
build: ['isBuilt', 'isDisposed', 'isWait'],
|
|
3320
|
+
dispose: ['isDisposed'],
|
|
3321
|
+
disposeAsync: ['isDisposed'],
|
|
3322
|
+
};
|
|
3323
|
+
function isGuardTest(test, member) {
|
|
3324
|
+
const allowed = GUARDS[member];
|
|
3325
|
+
if (!allowed)
|
|
3326
|
+
return false;
|
|
3327
|
+
if (t__namespace.isLogicalExpression(test) && test.operator === '||')
|
|
3328
|
+
return isGuardTest(test.left, member) && isGuardTest(test.right, member);
|
|
3329
|
+
return t__namespace.isMemberExpression(test) && t__namespace.isThisExpression(test.object) && !test.computed && t__namespace.isIdentifier(test.property) && allowed.includes(test.property.name);
|
|
3330
|
+
}
|
|
3331
|
+
function isBareReturn(node) {
|
|
3332
|
+
if (t__namespace.isReturnStatement(node))
|
|
3333
|
+
return true;
|
|
3334
|
+
return t__namespace.isBlockStatement(node) && node.body.length === 1 && t__namespace.isReturnStatement(node.body[0]);
|
|
3335
|
+
}
|
|
3336
|
+
function flow(node, called, name, use, member) {
|
|
3337
|
+
const plain = (c) => ({ normal: c, returns: true, breaks: null, continues: null });
|
|
3338
|
+
if (!node)
|
|
3339
|
+
return plain(called);
|
|
3340
|
+
const d = (e) => called || definitely(e, name, use);
|
|
3341
|
+
if (t__namespace.isBlockStatement(node) || t__namespace.isProgram(node))
|
|
3342
|
+
return block(node.body, called, name, use, member);
|
|
3343
|
+
if (t__namespace.isExpressionStatement(node))
|
|
3344
|
+
return plain(d(node.expression));
|
|
3345
|
+
if (t__namespace.isVariableDeclaration(node))
|
|
3346
|
+
return plain(called || node.declarations.some(x => definitely(x.init, name, use)));
|
|
3347
|
+
if (t__namespace.isReturnStatement(node))
|
|
3348
|
+
return { normal: null, returns: d(node.argument), breaks: null, continues: null };
|
|
3349
|
+
if (t__namespace.isThrowStatement(node))
|
|
3350
|
+
return { normal: null, returns: true, breaks: null, continues: null };
|
|
3351
|
+
if (t__namespace.isBreakStatement(node))
|
|
3352
|
+
return { normal: null, returns: true, breaks: called, continues: null };
|
|
3353
|
+
if (t__namespace.isContinueStatement(node))
|
|
3354
|
+
return { normal: null, returns: true, breaks: null, continues: called };
|
|
3355
|
+
if (t__namespace.isIfStatement(node)) {
|
|
3356
|
+
const tc = d(node.test);
|
|
3357
|
+
if (!tc && isGuardTest(node.test, member) && isBareReturn(node.consequent)) {
|
|
3358
|
+
const alt = node.alternate ? flow(node.alternate, tc, name, use, member) : plain(tc);
|
|
3359
|
+
return { normal: alt.normal, returns: alt.returns, breaks: alt.breaks, continues: alt.continues };
|
|
3360
|
+
}
|
|
3361
|
+
const cons = flow(node.consequent, tc, name, use, member);
|
|
3362
|
+
const alt = node.alternate ? flow(node.alternate, tc, name, use, member) : plain(tc);
|
|
3363
|
+
return { normal: join(cons.normal, alt.normal), returns: cons.returns && alt.returns, breaks: join(cons.breaks, alt.breaks), continues: join(cons.continues, alt.continues) };
|
|
3364
|
+
}
|
|
3365
|
+
if (t__namespace.isWhileStatement(node) || t__namespace.isForStatement(node) || t__namespace.isForInStatement(node) || t__namespace.isForOfStatement(node)) {
|
|
3366
|
+
let tc = called;
|
|
3367
|
+
if (t__namespace.isForStatement(node))
|
|
3368
|
+
tc = called || definitely(node.init, name, use) || definitely(node.test, name, use);
|
|
3369
|
+
else if (t__namespace.isWhileStatement(node))
|
|
3370
|
+
tc = d(node.test);
|
|
3371
|
+
else
|
|
3372
|
+
tc = d(node.right);
|
|
3373
|
+
const body = flow(node.body, tc, name, use, member);
|
|
3374
|
+
return { normal: join(tc, body.breaks), returns: body.returns, breaks: null, continues: null };
|
|
3375
|
+
}
|
|
3376
|
+
if (t__namespace.isDoWhileStatement(node)) {
|
|
3377
|
+
const body = flow(node.body, called, name, use, member);
|
|
3378
|
+
const afterBody = join(body.normal, body.continues);
|
|
3379
|
+
const afterTest = afterBody === null ? null : afterBody || definitely(node.test, name, use);
|
|
3380
|
+
return { normal: join(afterTest, body.breaks), returns: body.returns, breaks: null, continues: null };
|
|
3381
|
+
}
|
|
3382
|
+
if (t__namespace.isSwitchStatement(node)) {
|
|
3383
|
+
const dc = d(node.discriminant);
|
|
3384
|
+
let fall = null;
|
|
3385
|
+
let returns = true;
|
|
3386
|
+
let breaks = null;
|
|
3387
|
+
let continues = null;
|
|
3388
|
+
let hasDefault = false;
|
|
3389
|
+
for (const c of node.cases) {
|
|
3390
|
+
if (!c.test)
|
|
3391
|
+
hasDefault = true;
|
|
3392
|
+
const entry = join(fall, dc || definitely(c.test, name, use));
|
|
3393
|
+
const out = entry === null ? null : block(c.consequent, entry, name, use, member);
|
|
3394
|
+
if (out) {
|
|
3395
|
+
returns = returns && out.returns;
|
|
3396
|
+
breaks = join(breaks, out.breaks);
|
|
3397
|
+
continues = join(continues, out.continues);
|
|
3398
|
+
fall = out.normal;
|
|
3399
|
+
}
|
|
3400
|
+
else {
|
|
3401
|
+
fall = null;
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
let normal = join(fall, breaks);
|
|
3405
|
+
if (!hasDefault)
|
|
3406
|
+
normal = join(normal, dc);
|
|
3407
|
+
return { normal, returns, breaks: null, continues };
|
|
3408
|
+
}
|
|
3409
|
+
if (t__namespace.isTryStatement(node)) {
|
|
3410
|
+
const tryOut = flow(node.block, called, name, use, member);
|
|
3411
|
+
const catchOut = node.handler ? flow(node.handler.body, called, name, use, member) : null;
|
|
3412
|
+
let normal = catchOut ? join(tryOut.normal, catchOut.normal) : tryOut.normal;
|
|
3413
|
+
let returns = tryOut.returns && (catchOut ? catchOut.returns : true);
|
|
3414
|
+
let breaks = catchOut ? join(tryOut.breaks, catchOut.breaks) : tryOut.breaks;
|
|
3415
|
+
let continues = catchOut ? join(tryOut.continues, catchOut.continues) : tryOut.continues;
|
|
3416
|
+
if (node.finalizer) {
|
|
3417
|
+
const fin = flow(node.finalizer, false, name, use, member);
|
|
3418
|
+
if (fin.normal === true) {
|
|
3419
|
+
normal = normal === null ? null : true;
|
|
3420
|
+
returns = true;
|
|
3421
|
+
breaks = breaks === null ? null : true;
|
|
3422
|
+
continues = continues === null ? null : true;
|
|
3423
|
+
}
|
|
3424
|
+
else if (fin.normal === null) {
|
|
3425
|
+
return { normal: null, returns: returns && fin.returns, breaks: fin.breaks, continues: fin.continues };
|
|
3426
|
+
}
|
|
3427
|
+
returns = returns && fin.returns;
|
|
3428
|
+
}
|
|
3429
|
+
return { normal, returns, breaks, continues };
|
|
3430
|
+
}
|
|
3431
|
+
if (t__namespace.isLabeledStatement(node)) {
|
|
3432
|
+
const body = flow(node.body, called, name, use, member);
|
|
3433
|
+
return { normal: join(body.normal, body.breaks), returns: body.returns, breaks: null, continues: body.continues };
|
|
3434
|
+
}
|
|
3435
|
+
if (t__namespace.isFunctionDeclaration(node) || t__namespace.isClassDeclaration(node))
|
|
3436
|
+
return plain(called);
|
|
3437
|
+
let c = called;
|
|
3438
|
+
for (const key of t__namespace.VISITOR_KEYS[node.type] ?? []) {
|
|
3439
|
+
const child = node[key];
|
|
3440
|
+
if (Array.isArray(child) ? child.some(x => definitely(x, name, use)) : definitely(child, name, use)) {
|
|
3441
|
+
c = true;
|
|
3442
|
+
break;
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
return plain(c);
|
|
3446
|
+
}
|
|
3447
|
+
function block(body, called, name, use, member) {
|
|
3448
|
+
let state = called;
|
|
3449
|
+
let returns = true;
|
|
3450
|
+
let breaks = null;
|
|
3451
|
+
let continues = null;
|
|
3452
|
+
for (const s of body) {
|
|
3453
|
+
if (state === null)
|
|
3454
|
+
break;
|
|
3455
|
+
const out = flow(s, state, name, use, member);
|
|
3456
|
+
returns = returns && out.returns;
|
|
3457
|
+
breaks = join(breaks, out.breaks);
|
|
3458
|
+
continues = join(continues, out.continues);
|
|
3459
|
+
state = out.normal;
|
|
3460
|
+
}
|
|
3461
|
+
return { normal: state, returns, breaks, continues };
|
|
3462
|
+
}
|
|
3463
|
+
function checkMemberBody(body, member, accessor) {
|
|
3464
|
+
const use = accessor === 'method' ? 'call' : accessor;
|
|
3465
|
+
if (!mentions(body, member, use))
|
|
3466
|
+
return 'never';
|
|
3467
|
+
const out = flow(body, false, member, use, member);
|
|
3468
|
+
return out.normal !== false && out.returns ? 'ok' : 'path';
|
|
3469
|
+
}
|
|
3470
|
+
function memberName(m) {
|
|
3471
|
+
const key = m.key;
|
|
3472
|
+
if (m.computed)
|
|
3473
|
+
return t__namespace.isStringLiteral(key) ? key.value : null;
|
|
3474
|
+
if (t__namespace.isIdentifier(key))
|
|
3475
|
+
return key.name;
|
|
3476
|
+
if (t__namespace.isStringLiteral(key))
|
|
3477
|
+
return key.value;
|
|
3478
|
+
return null;
|
|
3479
|
+
}
|
|
3480
|
+
function analyzeModule(ast) {
|
|
3481
|
+
const imports = new Map();
|
|
3482
|
+
const classes = new Map();
|
|
3483
|
+
const exportsMap = new Map();
|
|
3484
|
+
const starSources = [];
|
|
3485
|
+
const findings = [];
|
|
3486
|
+
const classNodes = [];
|
|
3487
|
+
const noteClass = (node, name) => {
|
|
3488
|
+
if (name)
|
|
3489
|
+
classes.set(name, node);
|
|
3490
|
+
classNodes.push({ node, name: name ?? 'An anonymous class' });
|
|
3491
|
+
};
|
|
3492
|
+
const walk = (node, parent) => {
|
|
3493
|
+
if (!node || typeof node !== 'object' || !node.type)
|
|
3494
|
+
return;
|
|
3495
|
+
if (t__namespace.isImportDeclaration(node)) {
|
|
3496
|
+
for (const s of node.specifiers) {
|
|
3497
|
+
if (t__namespace.isImportSpecifier(s))
|
|
3498
|
+
imports.set(s.local.name, { source: node.source.value, name: t__namespace.isIdentifier(s.imported) ? s.imported.name : s.imported.value });
|
|
3499
|
+
else if (t__namespace.isImportDefaultSpecifier(s))
|
|
3500
|
+
imports.set(s.local.name, { source: node.source.value, name: 'default' });
|
|
3501
|
+
else if (t__namespace.isImportNamespaceSpecifier(s))
|
|
3502
|
+
imports.set(s.local.name, { source: node.source.value, name: '*' });
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
else if (t__namespace.isExportNamedDeclaration(node)) {
|
|
3506
|
+
if (node.source) {
|
|
3507
|
+
for (const s of node.specifiers) {
|
|
3508
|
+
if (t__namespace.isExportSpecifier(s)) {
|
|
3509
|
+
const exported = t__namespace.isIdentifier(s.exported) ? s.exported.name : s.exported.value;
|
|
3510
|
+
exportsMap.set(exported, { from: node.source.value, imported: s.local.name });
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
else {
|
|
3515
|
+
for (const s of node.specifiers) {
|
|
3516
|
+
if (t__namespace.isExportSpecifier(s)) {
|
|
3517
|
+
const exported = t__namespace.isIdentifier(s.exported) ? s.exported.name : s.exported.value;
|
|
3518
|
+
exportsMap.set(exported, { local: s.local.name });
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
const decl = node.declaration;
|
|
3522
|
+
if (t__namespace.isClassDeclaration(decl) && decl.id)
|
|
3523
|
+
exportsMap.set(decl.id.name, { local: decl.id.name });
|
|
3524
|
+
if (t__namespace.isVariableDeclaration(decl)) {
|
|
3525
|
+
for (const v of decl.declarations)
|
|
3526
|
+
if (t__namespace.isIdentifier(v.id))
|
|
3527
|
+
exportsMap.set(v.id.name, { local: v.id.name });
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
else if (t__namespace.isExportDefaultDeclaration(node)) {
|
|
3532
|
+
const decl = node.declaration;
|
|
3533
|
+
if (t__namespace.isClassDeclaration(decl)) {
|
|
3534
|
+
const local = decl.id?.name ?? '*default*';
|
|
3535
|
+
if (!decl.id)
|
|
3536
|
+
classes.set(local, decl);
|
|
3537
|
+
exportsMap.set('default', { local });
|
|
3538
|
+
}
|
|
3539
|
+
else if (t__namespace.isIdentifier(decl)) {
|
|
3540
|
+
exportsMap.set('default', { local: decl.name });
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
else if (t__namespace.isExportAllDeclaration(node)) {
|
|
3544
|
+
starSources.push(node.source.value);
|
|
3545
|
+
}
|
|
3546
|
+
if (t__namespace.isClassDeclaration(node)) {
|
|
3547
|
+
noteClass(node, node.id?.name ?? (t__namespace.isExportDefaultDeclaration(parent) ? '*default*' : null));
|
|
3548
|
+
}
|
|
3549
|
+
else if (t__namespace.isClassExpression(node)) {
|
|
3550
|
+
const name = node.id?.name ?? (t__namespace.isVariableDeclarator(parent) && t__namespace.isIdentifier(parent.id) ? parent.id.name : null);
|
|
3551
|
+
noteClass(node, name);
|
|
3552
|
+
}
|
|
3553
|
+
for (const key of t__namespace.VISITOR_KEYS[node.type] ?? []) {
|
|
3554
|
+
const child = node[key];
|
|
3555
|
+
if (Array.isArray(child))
|
|
3556
|
+
child.forEach(c => walk(c, node));
|
|
3557
|
+
else
|
|
3558
|
+
walk(child, node);
|
|
3559
|
+
}
|
|
3560
|
+
};
|
|
3561
|
+
walk(ast.program, null);
|
|
3562
|
+
const baseOf = (cls, seen) => {
|
|
3563
|
+
if (seen.has(cls))
|
|
3564
|
+
return { kind: 'unknown' };
|
|
3565
|
+
seen.add(cls);
|
|
3566
|
+
const sup = cls.superClass;
|
|
3567
|
+
if (t__namespace.isIdentifier(sup)) {
|
|
3568
|
+
const imp = imports.get(sup.name);
|
|
3569
|
+
if (imp && imp.name !== '*')
|
|
3570
|
+
return { kind: 'import', source: imp.source, name: imp.name };
|
|
3571
|
+
const local = classes.get(sup.name);
|
|
3572
|
+
if (local)
|
|
3573
|
+
return baseOf(local, seen);
|
|
3574
|
+
return { kind: 'unknown' };
|
|
3575
|
+
}
|
|
3576
|
+
if (t__namespace.isMemberExpression(sup) && t__namespace.isIdentifier(sup.object) && !sup.computed && t__namespace.isIdentifier(sup.property)) {
|
|
3577
|
+
const imp = imports.get(sup.object.name);
|
|
3578
|
+
if (imp && imp.name === '*')
|
|
3579
|
+
return { kind: 'import', source: imp.source, name: sup.property.name };
|
|
3580
|
+
}
|
|
3581
|
+
return { kind: 'unknown' };
|
|
3582
|
+
};
|
|
3583
|
+
for (const { node, name } of classNodes) {
|
|
3584
|
+
if (!node.superClass)
|
|
3585
|
+
continue;
|
|
3586
|
+
let base;
|
|
3587
|
+
for (const m of node.body.body) {
|
|
3588
|
+
if (!(t__namespace.isClassMethod(m) || t__namespace.isClassPrivateMethod(m)) || m.static || t__namespace.isClassPrivateMethod(m))
|
|
3589
|
+
continue;
|
|
3590
|
+
if (m.kind === 'constructor')
|
|
3591
|
+
continue;
|
|
3592
|
+
const member = memberName(m);
|
|
3593
|
+
if (!member || !superNames.has(member))
|
|
3594
|
+
continue;
|
|
3595
|
+
const accessor = m.kind === 'get' ? 'get' : m.kind === 'set' ? 'set' : 'method';
|
|
3596
|
+
if (accessor === 'method' && !SUPER_METHODS.includes(member))
|
|
3597
|
+
continue;
|
|
3598
|
+
if (accessor !== 'method' && !SUPER_ACCESSORS.includes(member))
|
|
3599
|
+
continue;
|
|
3600
|
+
const result = checkMemberBody(m.body, member, accessor);
|
|
3601
|
+
if (result === 'ok')
|
|
3602
|
+
continue;
|
|
3603
|
+
base ??= baseOf(node, new Set());
|
|
3604
|
+
findings.push({
|
|
3605
|
+
className: name === '*default*' ? 'The default-exported class' : name,
|
|
3606
|
+
member,
|
|
3607
|
+
accessor,
|
|
3608
|
+
kind: result,
|
|
3609
|
+
line: m.loc?.start.line ?? 0,
|
|
3610
|
+
column: m.loc?.start.column ?? 0,
|
|
3611
|
+
classLine: node.loc?.start.line ?? 0,
|
|
3612
|
+
base,
|
|
3613
|
+
});
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
return {
|
|
3617
|
+
findings,
|
|
3618
|
+
classBase(exportedOrLocal) {
|
|
3619
|
+
const cls = classes.get(exportedOrLocal);
|
|
3620
|
+
return cls ? baseOf(cls, new Set()) : null;
|
|
3621
|
+
},
|
|
3622
|
+
exportOf(name) {
|
|
3623
|
+
return exportsMap.get(name) ?? null;
|
|
3624
|
+
},
|
|
3625
|
+
starSources,
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
function superCallMessage(f) {
|
|
3629
|
+
const target = f.accessor === 'get' ? `read super.${f.member}` : f.accessor === 'set' ? `assign super.${f.member}` : `call super.${f.member}(…)`;
|
|
3630
|
+
return f.kind === 'never'
|
|
3631
|
+
? `${f.className}.${f.member} overrides a ComponentBase member and does not ${target}; MotifJS relies on it. Rename the member, or ${target} on every path.`
|
|
3632
|
+
: `${f.className}.${f.member} can finish without reaching super.${f.member} (a return, a branch, a loop or a catch skips it); MotifJS relies on it. ${target[0].toUpperCase() + target.slice(1)} on every path.`;
|
|
3633
|
+
}
|
|
3634
|
+
function createComponentResolver(resolve, read) {
|
|
3635
|
+
const cache = new Map();
|
|
3636
|
+
const shapes = new Map();
|
|
3637
|
+
const shapeOf = (file) => {
|
|
3638
|
+
let p = shapes.get(file);
|
|
3639
|
+
if (!p) {
|
|
3640
|
+
p = (async () => {
|
|
3641
|
+
const code = await read(file);
|
|
3642
|
+
if (code == null || code.length > 2_000_000)
|
|
3643
|
+
return null;
|
|
3644
|
+
const ast = parseModule(code, file);
|
|
3645
|
+
return ast ? analyzeModule(ast) : null;
|
|
3646
|
+
})();
|
|
3647
|
+
shapes.set(file, p);
|
|
3648
|
+
}
|
|
3649
|
+
return p;
|
|
3650
|
+
};
|
|
3651
|
+
const isComponentExport = (file, name, depth) => {
|
|
3652
|
+
const key = `${file}#${name}`;
|
|
3653
|
+
let p = cache.get(key);
|
|
3654
|
+
if (!p) {
|
|
3655
|
+
cache.set(key, Promise.resolve(false));
|
|
3656
|
+
p = (async () => {
|
|
3657
|
+
if (depth > 12)
|
|
3658
|
+
return false;
|
|
3659
|
+
const shape = await shapeOf(file);
|
|
3660
|
+
if (!shape)
|
|
3661
|
+
return false;
|
|
3662
|
+
const exp = shape.exportOf(name);
|
|
3663
|
+
if (exp?.from)
|
|
3664
|
+
return isComponentBase({ kind: 'import', source: exp.from, name: exp.imported ?? name }, file, depth + 1);
|
|
3665
|
+
const local = exp?.local ?? (name === 'default' ? '*default*' : null);
|
|
3666
|
+
if (local) {
|
|
3667
|
+
const base = shape.classBase(local);
|
|
3668
|
+
if (base)
|
|
3669
|
+
return isComponentBase(base, file, depth + 1);
|
|
3670
|
+
return false;
|
|
3671
|
+
}
|
|
3672
|
+
for (const star of shape.starSources) {
|
|
3673
|
+
if (await isComponentBase({ kind: 'import', source: star, name }, file, depth + 1))
|
|
3674
|
+
return true;
|
|
3675
|
+
}
|
|
3676
|
+
return false;
|
|
3677
|
+
})();
|
|
3678
|
+
cache.set(key, p);
|
|
3679
|
+
}
|
|
3680
|
+
return p;
|
|
3681
|
+
};
|
|
3682
|
+
const isComponentBase = async (base, importer, depth = 0) => {
|
|
3683
|
+
if (base.kind !== 'import')
|
|
3684
|
+
return false;
|
|
3685
|
+
if (isCoreSource(base.source))
|
|
3686
|
+
return isCoreComponent(base.name);
|
|
3687
|
+
let file = null;
|
|
3688
|
+
try {
|
|
3689
|
+
file = await resolve(base.source, importer);
|
|
3690
|
+
}
|
|
3691
|
+
catch {
|
|
3692
|
+
file = null;
|
|
3693
|
+
}
|
|
3694
|
+
if (!file)
|
|
3695
|
+
return false;
|
|
3696
|
+
return isComponentExport(file, base.name, depth);
|
|
3697
|
+
};
|
|
3698
|
+
return isComponentBase;
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3156
3701
|
const fileRegex = /\.(jsx|tsx|aio|mjsx|mtsx)$/;
|
|
3157
3702
|
const decoratorFileRegex = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
3158
3703
|
const declarationFileRegex = /\.d\.[mc]?ts$/;
|
|
3159
3704
|
const decoratorSyntaxRegex = /(?:^|[\s(,;{}])@[A-Za-z_$]/;
|
|
3160
3705
|
const assetQueryRegex = /(?:^|&)(?:raw|url|worker|sharedworker|inline)(?:[&=]|$)/;
|
|
3706
|
+
const analyzableFileRegex = /\.(jsx|tsx|mjsx|mtsx|ts|mts|cts|js|mjs|cjs)$/;
|
|
3161
3707
|
function sourceFile(id) {
|
|
3162
3708
|
if (id.charCodeAt(0) === 0)
|
|
3163
3709
|
return null;
|
|
@@ -3178,6 +3724,41 @@ function shouldLowerDecorators(id, src) {
|
|
|
3178
3724
|
return false;
|
|
3179
3725
|
return decoratorSyntaxRegex.test(src);
|
|
3180
3726
|
}
|
|
3727
|
+
function shouldCheckSuperCalls(id, src) {
|
|
3728
|
+
const file = sourceFile(id);
|
|
3729
|
+
if (file === null || !analyzableFileRegex.test(file) || declarationFileRegex.test(file))
|
|
3730
|
+
return false;
|
|
3731
|
+
if (file.replace(/\\/g, '/').includes('/node_modules/'))
|
|
3732
|
+
return false;
|
|
3733
|
+
return mayHaveSuperOverrides(src);
|
|
3734
|
+
}
|
|
3735
|
+
function lineFrame(src, line, column) {
|
|
3736
|
+
const lines = src.split(/\r?\n/);
|
|
3737
|
+
if (line < 1 || line > lines.length)
|
|
3738
|
+
return undefined;
|
|
3739
|
+
const from = Math.max(1, line - 1);
|
|
3740
|
+
const to = Math.min(lines.length, line + 1);
|
|
3741
|
+
const width = String(to).length;
|
|
3742
|
+
const out = [];
|
|
3743
|
+
for (let i = from; i <= to; i++) {
|
|
3744
|
+
out.push(`${i === line ? '>' : ' '} ${String(i).padStart(width)} | ${lines[i - 1]}`);
|
|
3745
|
+
if (i === line)
|
|
3746
|
+
out.push(` ${' '.repeat(width)} | ${' '.repeat(column)}^`);
|
|
3747
|
+
}
|
|
3748
|
+
return out.join('\n');
|
|
3749
|
+
}
|
|
3750
|
+
async function checkSuperCalls(src, file, isComponentBase) {
|
|
3751
|
+
const ast = parseModule(src, file);
|
|
3752
|
+
if (!ast)
|
|
3753
|
+
return [];
|
|
3754
|
+
const out = [];
|
|
3755
|
+
for (const f of analyzeModule(ast).findings) {
|
|
3756
|
+
if (!(await isComponentBase(f.base, file)))
|
|
3757
|
+
continue;
|
|
3758
|
+
out.push({ code: 'MJX015', message: superCallMessage(f), file, line: f.line, frame: lineFrame(src, f.line, f.column) });
|
|
3759
|
+
}
|
|
3760
|
+
return out;
|
|
3761
|
+
}
|
|
3181
3762
|
function legacyDecoratorsChecker() {
|
|
3182
3763
|
const byConfig = new Map();
|
|
3183
3764
|
return (file) => {
|
|
@@ -3205,10 +3786,45 @@ function explainMatches(opt, id) {
|
|
|
3205
3786
|
function vitePlugin(options = {}) {
|
|
3206
3787
|
const showDiagnostics = options.diagnostics !== false;
|
|
3207
3788
|
const usesLegacyDecorators = legacyDecoratorsChecker();
|
|
3789
|
+
let context = null;
|
|
3790
|
+
const makeResolver = () => createComponentResolver(async (source, importer) => {
|
|
3791
|
+
if (!context?.resolve)
|
|
3792
|
+
return null;
|
|
3793
|
+
const r = await context.resolve(source, importer, { skipSelf: true });
|
|
3794
|
+
if (!r || r.external)
|
|
3795
|
+
return null;
|
|
3796
|
+
return sourceFile(r.id);
|
|
3797
|
+
}, async (file) => {
|
|
3798
|
+
try {
|
|
3799
|
+
return await node_fs.promises.readFile(file, 'utf8');
|
|
3800
|
+
}
|
|
3801
|
+
catch {
|
|
3802
|
+
return null;
|
|
3803
|
+
}
|
|
3804
|
+
});
|
|
3805
|
+
let isComponentBase = makeResolver();
|
|
3806
|
+
const pending = new Set();
|
|
3208
3807
|
return {
|
|
3209
3808
|
name: 'transform-file-motif-jsx',
|
|
3210
3809
|
enforce: 'pre',
|
|
3810
|
+
watchChange() {
|
|
3811
|
+
isComponentBase = makeResolver();
|
|
3812
|
+
},
|
|
3813
|
+
handleHotUpdate() {
|
|
3814
|
+
isComponentBase = makeResolver();
|
|
3815
|
+
},
|
|
3816
|
+
async buildEnd() {
|
|
3817
|
+
await Promise.all([...pending]);
|
|
3818
|
+
},
|
|
3211
3819
|
transform(src, id) {
|
|
3820
|
+
if (showDiagnostics && shouldCheckSuperCalls(id, src)) {
|
|
3821
|
+
context = this;
|
|
3822
|
+
const check = checkSuperCalls(src, sourceFile(id), isComponentBase)
|
|
3823
|
+
.then(found => { if (found.length > 0)
|
|
3824
|
+
printDiagnostics(found); }, () => { })
|
|
3825
|
+
.finally(() => { pending.delete(check); });
|
|
3826
|
+
pending.add(check);
|
|
3827
|
+
}
|
|
3212
3828
|
if (shouldLowerDecorators(id, src)) {
|
|
3213
3829
|
if (usesLegacyDecorators(sourceFile(id)))
|
|
3214
3830
|
return;
|
|
@@ -3311,6 +3927,7 @@ function lintProgram(program, files) {
|
|
|
3311
3927
|
continue;
|
|
3312
3928
|
if (wanted && !wanted.has(normalize(sf.fileName)))
|
|
3313
3929
|
continue;
|
|
3930
|
+
out.push(...superCallFindings(sf, checker));
|
|
3314
3931
|
if (!/\.(tsx|jsx|mtsx|mjsx)$/.test(sf.fileName))
|
|
3315
3932
|
continue;
|
|
3316
3933
|
const visit = (node) => {
|
|
@@ -3366,6 +3983,60 @@ function lintProgram(program, files) {
|
|
|
3366
3983
|
}
|
|
3367
3984
|
return out;
|
|
3368
3985
|
}
|
|
3986
|
+
const coreDeclarationFile = /[\\/](?:@motifx[\\/]core|motifjs[\\/](?:dist|src))[\\/]/;
|
|
3987
|
+
function derivesFromComponentBase(node, checker) {
|
|
3988
|
+
try {
|
|
3989
|
+
let type = checker.getTypeAtLocation(node);
|
|
3990
|
+
const seen = new Set();
|
|
3991
|
+
const queue = type ? [type] : [];
|
|
3992
|
+
while (queue.length > 0) {
|
|
3993
|
+
type = queue.shift();
|
|
3994
|
+
const target = (type.target ?? type);
|
|
3995
|
+
if (seen.has(target))
|
|
3996
|
+
continue;
|
|
3997
|
+
seen.add(target);
|
|
3998
|
+
const sym = target.getSymbol();
|
|
3999
|
+
if (sym && sym.getName() === 'ComponentBase' && sym.declarations?.some(d => coreDeclarationFile.test(d.getSourceFile().fileName)))
|
|
4000
|
+
return true;
|
|
4001
|
+
if (!(target.objectFlags & (ts__namespace.ObjectFlags.Class | ts__namespace.ObjectFlags.Interface)))
|
|
4002
|
+
continue;
|
|
4003
|
+
for (const b of checker.getBaseTypes(target) ?? [])
|
|
4004
|
+
queue.push(b);
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
catch { }
|
|
4008
|
+
return false;
|
|
4009
|
+
}
|
|
4010
|
+
function superCallFindings(sf, checker) {
|
|
4011
|
+
if (!mayHaveSuperOverrides(sf.text))
|
|
4012
|
+
return [];
|
|
4013
|
+
const ast = parseModule(sf.text, sf.fileName);
|
|
4014
|
+
if (!ast)
|
|
4015
|
+
return [];
|
|
4016
|
+
const findings = analyzeModule(ast).findings;
|
|
4017
|
+
if (findings.length === 0)
|
|
4018
|
+
return [];
|
|
4019
|
+
const membersByLine = new Map();
|
|
4020
|
+
const collect = (n) => {
|
|
4021
|
+
if ((ts__namespace.isMethodDeclaration(n) || ts__namespace.isGetAccessorDeclaration(n) || ts__namespace.isSetAccessorDeclaration(n)) && (ts__namespace.isClassDeclaration(n.parent) || ts__namespace.isClassExpression(n.parent))) {
|
|
4022
|
+
const name = n.name && (ts__namespace.isIdentifier(n.name) || ts__namespace.isStringLiteral(n.name)) ? n.name.text : '';
|
|
4023
|
+
const lines = [n.getStart(sf), n.name.getStart(sf)].map(p => sf.getLineAndCharacterOfPosition(p).line + 1);
|
|
4024
|
+
for (const l of lines)
|
|
4025
|
+
membersByLine.set(`${l}:${name}`, n);
|
|
4026
|
+
}
|
|
4027
|
+
ts__namespace.forEachChild(n, collect);
|
|
4028
|
+
};
|
|
4029
|
+
collect(sf);
|
|
4030
|
+
const out = [];
|
|
4031
|
+
for (const f of findings) {
|
|
4032
|
+
const member = membersByLine.get(`${f.line}:${f.member}`);
|
|
4033
|
+
const cls = member?.parent;
|
|
4034
|
+
if (!member || !cls || !derivesFromComponentBase(cls, checker))
|
|
4035
|
+
continue;
|
|
4036
|
+
out.push({ code: 'MJX015', file: sf.fileName, line: f.line, message: superCallMessage(f), frame: codeFrame(sf, member) });
|
|
4037
|
+
}
|
|
4038
|
+
return out;
|
|
4039
|
+
}
|
|
3369
4040
|
/** tsconfig'den program kurup denetler. CLI bunu kullanır. */
|
|
3370
4041
|
function lintProject(options = {}) {
|
|
3371
4042
|
const projectPath = options.project ?? 'tsconfig.json';
|