@wcstack/typescript 3.2.0 → 3.3.0
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.esm.js +1 -1
- package/dist/schema-core.cjs +2271 -2012
- package/dist/wcs-schema.mjs +1 -1
- package/dist/wcs-tsc.mjs +1 -1
- package/package.json +1 -1
package/dist/schema-core.cjs
CHANGED
|
@@ -109,7 +109,7 @@ var WcsDiagnosticCode = {
|
|
|
109
109
|
// 依存する wcs/recursion-context は runtime 専用(静的側は出さない)。
|
|
110
110
|
//
|
|
111
111
|
// `**` を解釈しない場所へ `**` が渡った(data-wcs / mustache / $watch キー / $listKeys
|
|
112
|
-
// キー / $resolve / $postUpdate / $trackDependency / 代入)、または `$recursion` 宣言が
|
|
112
|
+
// キー / $resolve / $postUpdate / $dependOn(旧名 $trackDependency) / 代入)、または `$recursion` 宣言が
|
|
113
113
|
// 無いのに `**` を使った。runtime は PathInfo の不変条件として raiseError するか
|
|
114
114
|
//(API 経由)、getter を黙って無視する(宣言なしの `**` getter)。
|
|
115
115
|
RecursionUnsupported: "wcs/recursion-unsupported",
|
|
@@ -153,6 +153,19 @@ var WcsDiagnosticCode = {
|
|
|
153
153
|
// 3.x の間だけ残る旧名(フィルタ `uc` → `upper`、`$trackDependency` → `$dependOn` …)を書いた(info)。
|
|
154
154
|
// 動くが 4.0 で外れるので正式名を提案する(@wcstack/state 3.2・要件 B12 / 3.x 計画 D39)
|
|
155
155
|
NameAlias: "wcs/name-alias",
|
|
156
|
+
// 旧名の宣言キーを `this.` 越しに**読んだ**(`this.$streams`)。宣言と違い旧名のままでは
|
|
157
|
+
// 3.x でも動かない — 正規化(@wcstack/state declarationAliases.ts)が正式名へ写したあと
|
|
158
|
+
// 旧名の自前プロパティを delete するので、読み出しは例外も出さずに undefined になる。
|
|
159
|
+
// `wcs/name-alias`(「動くが 4.0 で外れる」)とは別の事実なので code を分ける: 移行中に
|
|
160
|
+
// name-alias を抑制したチームが、この「今日すでに壊れている」まで一緒に消さないため。
|
|
161
|
+
// severity は検出経路で変わる — AST で断定できたら warning、読めない形(class 構文など)の
|
|
162
|
+
// 正規表現フォールバックは info(誤検出しうる経路を warning にしない)。
|
|
163
|
+
DeclarationAliasRead: "wcs/declaration-alias-read",
|
|
164
|
+
// 旧名と正式名の宣言キーを**両方**書いた(`$streams` と `$stream` 等)。どちらが効くのか
|
|
165
|
+
// 書き手に見えないので、ランタイム(@wcstack/state declarationAliases.ts)は正規化の時点で
|
|
166
|
+
// 名指しで raiseError する = ページ初期化ごと止まるので error。3.2 への移行中
|
|
167
|
+
//(新名を足して旧名を消し忘れる)にちょうど起きる形。
|
|
168
|
+
DeclarationAlias: "wcs/declaration-alias",
|
|
156
169
|
// wcBindable 無宣言タグ(wcs-fetch-header 等のヘルパー)への spread。
|
|
157
170
|
// ランタイム(expandSpread)は raiseError で落とす。
|
|
158
171
|
SpreadNoBindable: "wcs/spread-no-bindable",
|
|
@@ -667,488 +680,1319 @@ function escapePtr(key) {
|
|
|
667
680
|
return key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
668
681
|
}
|
|
669
682
|
|
|
670
|
-
// ../state/dist/
|
|
671
|
-
var
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
var
|
|
683
|
+
// ../state/dist/parser.esm.js
|
|
684
|
+
var DELIMITER = ".";
|
|
685
|
+
var WILDCARD = "*";
|
|
686
|
+
var MAX_WILDCARD_DEPTH = 128;
|
|
687
|
+
var MAX_PATH_SEGMENTS = 512;
|
|
688
|
+
var BINDING_SEPARATOR = ";";
|
|
689
|
+
var PROP_VALUE_SEPARATOR = ":";
|
|
690
|
+
var MODIFIER_SEPARATOR = "#";
|
|
691
|
+
var FILTER_SEPARATOR = "|";
|
|
692
|
+
var ELSE_KEYWORD = "else";
|
|
693
|
+
var SPREAD_PROP = "...";
|
|
694
|
+
var EVENT_PROP_PREFIX = "on";
|
|
695
|
+
var EVENT_TOKEN_NAMESPACE = "eventToken";
|
|
696
|
+
var VOLUME_INJECTION_PROP = "state";
|
|
697
|
+
var COMMAND_NAMESPACE = "command";
|
|
698
|
+
var CLASS_NAMESPACE = "class";
|
|
699
|
+
var ATTR_NAMESPACE = "attr";
|
|
700
|
+
var STYLE_NAMESPACE = "style";
|
|
701
|
+
var INDEX_PARAM_PREFIX = "$";
|
|
702
|
+
var tmpIndexByIndexName = {};
|
|
703
|
+
for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
|
|
704
|
+
tmpIndexByIndexName[`${INDEX_PARAM_PREFIX}${i + 1}`] = i;
|
|
705
|
+
}
|
|
706
|
+
Object.freeze(tmpIndexByIndexName);
|
|
707
|
+
var RECURSION_WILDCARD = "**";
|
|
679
708
|
function raiseError(message) {
|
|
680
709
|
throw new Error(`[@wcstack/state] ${message}`);
|
|
681
710
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
function optionMustBeNumber(fnName) {
|
|
686
|
-
raiseError(`filter ${fnName} requires a number as option`);
|
|
687
|
-
}
|
|
688
|
-
function valueMustBeNumber(fnName) {
|
|
689
|
-
raiseError(`filter ${fnName} requires a number value`);
|
|
690
|
-
}
|
|
691
|
-
function valueMustBeBoolean(fnName) {
|
|
692
|
-
raiseError(`filter ${fnName} requires a boolean value`);
|
|
693
|
-
}
|
|
694
|
-
function valueMustBeDate(fnName) {
|
|
695
|
-
raiseError(`filter ${fnName} requires a date value`);
|
|
696
|
-
}
|
|
697
|
-
function valueMustBeArray(fnName) {
|
|
698
|
-
raiseError(`filter ${fnName} requires an array value`);
|
|
699
|
-
}
|
|
700
|
-
var builtinFilterAliases = {
|
|
701
|
-
inc: "add",
|
|
702
|
-
dec: "sub",
|
|
703
|
-
fix: "toFixed",
|
|
704
|
-
uc: "upper",
|
|
705
|
-
lc: "lower",
|
|
706
|
-
cap: "capitalize",
|
|
707
|
-
rep: "repeat",
|
|
708
|
-
rev: "reverse",
|
|
709
|
-
pad: "padStart",
|
|
710
|
-
null: "nullIfEmpty"
|
|
711
|
-
};
|
|
712
|
-
function validateNumberString(value) {
|
|
713
|
-
if (!value || isNaN(Number(value))) {
|
|
714
|
-
return false;
|
|
715
|
-
}
|
|
716
|
-
return true;
|
|
711
|
+
var _cache = /* @__PURE__ */ new Map();
|
|
712
|
+
function clearPathInfoCacheForTooling() {
|
|
713
|
+
_cache.clear();
|
|
717
714
|
}
|
|
718
|
-
var
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
if (!validateNumberString(opt)) {
|
|
724
|
-
optionMustBeNumber("eq");
|
|
725
|
-
}
|
|
726
|
-
return value === Number(opt);
|
|
727
|
-
}
|
|
728
|
-
if (typeof value === "string") {
|
|
729
|
-
return value === opt;
|
|
730
|
-
}
|
|
731
|
-
return value === literal2;
|
|
732
|
-
};
|
|
733
|
-
};
|
|
734
|
-
var ne = (options, literals) => {
|
|
735
|
-
const opt = options?.[0] ?? optionsRequired("ne");
|
|
736
|
-
const literal2 = literals !== void 0 && literals.length > 0 ? literals[0] : opt;
|
|
737
|
-
return (value) => {
|
|
738
|
-
if (typeof value === "number") {
|
|
739
|
-
if (!validateNumberString(opt)) {
|
|
740
|
-
optionMustBeNumber("ne");
|
|
741
|
-
}
|
|
742
|
-
return value !== Number(opt);
|
|
743
|
-
}
|
|
744
|
-
if (typeof value === "string") {
|
|
745
|
-
return value !== opt;
|
|
746
|
-
}
|
|
747
|
-
return value !== literal2;
|
|
748
|
-
};
|
|
749
|
-
};
|
|
750
|
-
var not = (_options) => {
|
|
751
|
-
return (value) => {
|
|
752
|
-
if (typeof value !== "boolean") {
|
|
753
|
-
valueMustBeBoolean("not");
|
|
754
|
-
}
|
|
755
|
-
return !value;
|
|
756
|
-
};
|
|
757
|
-
};
|
|
758
|
-
var lt = (options) => {
|
|
759
|
-
const opt = options?.[0] ?? optionsRequired("lt");
|
|
760
|
-
if (!validateNumberString(opt)) {
|
|
761
|
-
optionMustBeNumber("lt");
|
|
762
|
-
}
|
|
763
|
-
return (value) => {
|
|
764
|
-
if (typeof value !== "number") {
|
|
765
|
-
valueMustBeNumber("lt");
|
|
766
|
-
}
|
|
767
|
-
return value < Number(opt);
|
|
768
|
-
};
|
|
769
|
-
};
|
|
770
|
-
var le = (options) => {
|
|
771
|
-
const opt = options?.[0] ?? optionsRequired("le");
|
|
772
|
-
if (!validateNumberString(opt)) {
|
|
773
|
-
optionMustBeNumber("le");
|
|
715
|
+
var id = 0;
|
|
716
|
+
function getPathInfo(path) {
|
|
717
|
+
let pathInfo = _cache.get(path);
|
|
718
|
+
if (typeof pathInfo !== "undefined") {
|
|
719
|
+
return pathInfo;
|
|
774
720
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
valueMustBeNumber("le");
|
|
778
|
-
}
|
|
779
|
-
return value <= Number(opt);
|
|
780
|
-
};
|
|
781
|
-
};
|
|
782
|
-
var gt = (options) => {
|
|
783
|
-
const opt = options?.[0] ?? optionsRequired("gt");
|
|
784
|
-
if (!validateNumberString(opt)) {
|
|
785
|
-
optionMustBeNumber("gt");
|
|
721
|
+
if (path.indexOf(RECURSION_WILDCARD) !== -1) {
|
|
722
|
+
raiseError(`[wcs/recursion-unsupported] "${path}" uses "${RECURSION_WILDCARD}", which is not accepted here. It is only meaningful in a $recursion declaration, in a recursive getter key, and in the path argument of $getAll / $setAll \u2014 and only when the state declares a $recursion anchor.`);
|
|
786
723
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
}
|
|
791
|
-
return value > Number(opt);
|
|
792
|
-
};
|
|
793
|
-
};
|
|
794
|
-
var ge = (options) => {
|
|
795
|
-
const opt = options?.[0] ?? optionsRequired("ge");
|
|
796
|
-
if (!validateNumberString(opt)) {
|
|
797
|
-
optionMustBeNumber("ge");
|
|
724
|
+
const segmentCount = countSegments(path);
|
|
725
|
+
if (segmentCount > MAX_PATH_SEGMENTS) {
|
|
726
|
+
raiseError(`[wcs/binding-syntax] "${path}" has ${segmentCount} path segments \u2014 the limit is ${MAX_PATH_SEGMENTS}. Every prefix of a path is interned, so the cost grows with the square of the depth.`);
|
|
798
727
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
728
|
+
pathInfo = Object.freeze(new PathInfo(path));
|
|
729
|
+
_cache.set(path, pathInfo);
|
|
730
|
+
return pathInfo;
|
|
731
|
+
}
|
|
732
|
+
function countSegments(path) {
|
|
733
|
+
let count = 1;
|
|
734
|
+
for (let i = 0; i < path.length; i++) {
|
|
735
|
+
if (path[i] === DELIMITER) {
|
|
736
|
+
count++;
|
|
802
737
|
}
|
|
803
|
-
return value >= Number(opt);
|
|
804
|
-
};
|
|
805
|
-
};
|
|
806
|
-
var inc = (options) => {
|
|
807
|
-
const opt = options?.[0] ?? optionsRequired("add");
|
|
808
|
-
if (!validateNumberString(opt)) {
|
|
809
|
-
optionMustBeNumber("add");
|
|
810
738
|
}
|
|
811
|
-
return
|
|
812
|
-
|
|
813
|
-
|
|
739
|
+
return count;
|
|
740
|
+
}
|
|
741
|
+
var PathInfo = class {
|
|
742
|
+
id = ++id;
|
|
743
|
+
path;
|
|
744
|
+
segments;
|
|
745
|
+
lastSegment;
|
|
746
|
+
cumulativePaths;
|
|
747
|
+
cumulativePathSet;
|
|
748
|
+
cumulativePathInfos;
|
|
749
|
+
cumulativePathInfoSet;
|
|
750
|
+
parentPath;
|
|
751
|
+
wildcardPaths;
|
|
752
|
+
wildcardPathSet;
|
|
753
|
+
indexByWildcardPath;
|
|
754
|
+
wildcardPathInfos;
|
|
755
|
+
wildcardPathInfoSet;
|
|
756
|
+
wildcardParentPaths;
|
|
757
|
+
wildcardParentPathSet;
|
|
758
|
+
wildcardParentPathInfos;
|
|
759
|
+
wildcardParentPathInfoSet;
|
|
760
|
+
wildcardPositions;
|
|
761
|
+
lastWildcardPath;
|
|
762
|
+
lastWildcardInfo;
|
|
763
|
+
wildcardCount;
|
|
764
|
+
parentPathInfo;
|
|
765
|
+
constructor(path) {
|
|
766
|
+
const getPattern = (_path) => {
|
|
767
|
+
return path === _path ? this : getPathInfo(_path);
|
|
768
|
+
};
|
|
769
|
+
const segments = path.split(".");
|
|
770
|
+
const cumulativePaths = [];
|
|
771
|
+
const cumulativePathInfos = [];
|
|
772
|
+
const wildcardPaths = [];
|
|
773
|
+
const indexByWildcardPath = {};
|
|
774
|
+
const wildcardPathInfos = [];
|
|
775
|
+
const wildcardParentPaths = [];
|
|
776
|
+
const wildcardParentPathInfos = [];
|
|
777
|
+
const wildcardPositions = [];
|
|
778
|
+
let currentPatternPath = "", prevPatternPath = "";
|
|
779
|
+
let wildcardCount = 0;
|
|
780
|
+
for (let i = 0; i < segments.length; i++) {
|
|
781
|
+
currentPatternPath += segments[i];
|
|
782
|
+
if (segments[i] === WILDCARD) {
|
|
783
|
+
wildcardPaths.push(currentPatternPath);
|
|
784
|
+
indexByWildcardPath[currentPatternPath] = wildcardCount;
|
|
785
|
+
wildcardPathInfos.push(getPattern(currentPatternPath));
|
|
786
|
+
wildcardParentPaths.push(prevPatternPath);
|
|
787
|
+
wildcardParentPathInfos.push(getPattern(prevPatternPath));
|
|
788
|
+
wildcardPositions.push(i);
|
|
789
|
+
wildcardCount++;
|
|
790
|
+
}
|
|
791
|
+
cumulativePaths.push(currentPatternPath);
|
|
792
|
+
cumulativePathInfos.push(getPattern(currentPatternPath));
|
|
793
|
+
prevPatternPath = currentPatternPath;
|
|
794
|
+
currentPatternPath += ".";
|
|
814
795
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
796
|
+
const lastWildcardPath = wildcardPaths.length > 0 ? wildcardPaths[wildcardPaths.length - 1] : null;
|
|
797
|
+
const parentPath = cumulativePaths.length > 1 ? cumulativePaths[cumulativePaths.length - 2] : null;
|
|
798
|
+
this.path = path;
|
|
799
|
+
this.segments = segments;
|
|
800
|
+
this.lastSegment = segments[segments.length - 1];
|
|
801
|
+
this.cumulativePaths = cumulativePaths;
|
|
802
|
+
this.cumulativePathSet = new Set(cumulativePaths);
|
|
803
|
+
this.cumulativePathInfos = cumulativePathInfos;
|
|
804
|
+
this.cumulativePathInfoSet = new Set(cumulativePathInfos);
|
|
805
|
+
this.wildcardPaths = wildcardPaths;
|
|
806
|
+
this.wildcardPathSet = new Set(wildcardPaths);
|
|
807
|
+
this.indexByWildcardPath = indexByWildcardPath;
|
|
808
|
+
this.wildcardPathInfos = wildcardPathInfos;
|
|
809
|
+
this.wildcardPathInfoSet = new Set(wildcardPathInfos);
|
|
810
|
+
this.wildcardParentPaths = wildcardParentPaths;
|
|
811
|
+
this.wildcardParentPathSet = new Set(wildcardParentPaths);
|
|
812
|
+
this.wildcardParentPathInfos = wildcardParentPathInfos;
|
|
813
|
+
this.wildcardParentPathInfoSet = new Set(wildcardParentPathInfos);
|
|
814
|
+
this.wildcardPositions = wildcardPositions;
|
|
815
|
+
this.lastWildcardPath = lastWildcardPath;
|
|
816
|
+
this.lastWildcardInfo = lastWildcardPath ? getPattern(lastWildcardPath) : null;
|
|
817
|
+
this.parentPath = parentPath;
|
|
818
|
+
this.parentPathInfo = parentPath ? getPattern(parentPath) : null;
|
|
819
|
+
this.wildcardCount = wildcardCount;
|
|
822
820
|
}
|
|
823
|
-
return (value) => {
|
|
824
|
-
if (typeof value !== "number") {
|
|
825
|
-
valueMustBeNumber("sub");
|
|
826
|
-
}
|
|
827
|
-
return value - Number(opt);
|
|
828
|
-
};
|
|
829
821
|
};
|
|
830
|
-
var
|
|
831
|
-
|
|
832
|
-
if
|
|
833
|
-
|
|
822
|
+
var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
|
|
823
|
+
var STRUCTURAL_BINDING_TYPE_SET = /* @__PURE__ */ new Set([
|
|
824
|
+
"if",
|
|
825
|
+
"elseif",
|
|
826
|
+
"else",
|
|
827
|
+
"for"
|
|
828
|
+
]);
|
|
829
|
+
var resolvedByKey = /* @__PURE__ */ new Map();
|
|
830
|
+
function clearFilterResolutionCache() {
|
|
831
|
+
resolvedByKey.clear();
|
|
832
|
+
}
|
|
833
|
+
function finalizeArg(text, firstQuoteStart, lastQuoteEnd) {
|
|
834
|
+
const startLimit = firstQuoteStart === -1 ? text.length : firstQuoteStart;
|
|
835
|
+
let start = 0;
|
|
836
|
+
while (start < startLimit && /\s/.test(text[start])) {
|
|
837
|
+
start++;
|
|
834
838
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
return value * Number(opt);
|
|
840
|
-
};
|
|
841
|
-
};
|
|
842
|
-
var div = (options) => {
|
|
843
|
-
const opt = options?.[0] ?? optionsRequired("div");
|
|
844
|
-
if (!validateNumberString(opt)) {
|
|
845
|
-
optionMustBeNumber("div");
|
|
839
|
+
const endLimit = lastQuoteEnd === -1 ? 0 : lastQuoteEnd;
|
|
840
|
+
let end = text.length;
|
|
841
|
+
while (end > endLimit && /\s/.test(text[end - 1])) {
|
|
842
|
+
end--;
|
|
846
843
|
}
|
|
847
|
-
return (
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
844
|
+
return text.slice(start, end);
|
|
845
|
+
}
|
|
846
|
+
var NUMBER_LITERAL = /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
|
|
847
|
+
function toLiteral(text, quoted) {
|
|
848
|
+
if (quoted)
|
|
849
|
+
return text;
|
|
850
|
+
if (text === "true")
|
|
851
|
+
return true;
|
|
852
|
+
if (text === "false")
|
|
853
|
+
return false;
|
|
854
|
+
if (text === "null")
|
|
855
|
+
return null;
|
|
856
|
+
return NUMBER_LITERAL.test(text) ? Number(text) : text;
|
|
857
|
+
}
|
|
858
|
+
function parseFilterArgsWithLiterals(argsText) {
|
|
859
|
+
const args = [];
|
|
860
|
+
const literals = [];
|
|
861
|
+
let current2 = "";
|
|
862
|
+
let inQuote = null;
|
|
863
|
+
let hasQuote = false;
|
|
864
|
+
let firstQuoteStart = -1;
|
|
865
|
+
let lastQuoteEnd = -1;
|
|
866
|
+
const flush = () => {
|
|
867
|
+
const arg = finalizeArg(current2, firstQuoteStart, lastQuoteEnd);
|
|
868
|
+
args.push(arg);
|
|
869
|
+
literals.push(toLiteral(arg, hasQuote));
|
|
870
|
+
current2 = "";
|
|
871
|
+
hasQuote = false;
|
|
872
|
+
firstQuoteStart = -1;
|
|
873
|
+
lastQuoteEnd = -1;
|
|
852
874
|
};
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
875
|
+
for (let i = 0; i < argsText.length; i++) {
|
|
876
|
+
const char = argsText[i];
|
|
877
|
+
if (inQuote) {
|
|
878
|
+
if (char === inQuote) {
|
|
879
|
+
inQuote = null;
|
|
880
|
+
} else {
|
|
881
|
+
if (firstQuoteStart === -1) {
|
|
882
|
+
firstQuoteStart = current2.length;
|
|
883
|
+
}
|
|
884
|
+
current2 += char;
|
|
885
|
+
lastQuoteEnd = current2.length;
|
|
886
|
+
}
|
|
887
|
+
} else if (char === '"' || char === "'") {
|
|
888
|
+
inQuote = char;
|
|
889
|
+
hasQuote = true;
|
|
890
|
+
} else if (char === ",") {
|
|
891
|
+
flush();
|
|
892
|
+
} else {
|
|
893
|
+
current2 += char;
|
|
862
894
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
};
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
895
|
+
}
|
|
896
|
+
if (inQuote !== null) {
|
|
897
|
+
raiseError(`[wcs/binding-syntax] unterminated ${inQuote} quote in the filter arguments "(${argsText})". Close the quote.${LINT_HINT}`);
|
|
898
|
+
}
|
|
899
|
+
const last = finalizeArg(current2, firstQuoteStart, lastQuoteEnd);
|
|
900
|
+
if (last || hasQuote) {
|
|
901
|
+
args.push(last);
|
|
902
|
+
literals.push(toLiteral(last, hasQuote));
|
|
903
|
+
}
|
|
904
|
+
return { args, literals };
|
|
905
|
+
}
|
|
906
|
+
var trimFn = (s) => s.trim();
|
|
907
|
+
var isQuote = (c) => c === "'" || c === '"';
|
|
908
|
+
function indexOfOutsideQuotes(text, char) {
|
|
909
|
+
let quote = null;
|
|
910
|
+
for (let i = 0; i < text.length; i++) {
|
|
911
|
+
const c = text[i];
|
|
912
|
+
if (quote !== null) {
|
|
913
|
+
if (c === quote)
|
|
914
|
+
quote = null;
|
|
915
|
+
} else if (isQuote(c)) {
|
|
916
|
+
quote = c;
|
|
917
|
+
} else if (c === char) {
|
|
918
|
+
return i;
|
|
870
919
|
}
|
|
871
|
-
return Math.abs(value);
|
|
872
|
-
};
|
|
873
|
-
};
|
|
874
|
-
var clamp = (options) => {
|
|
875
|
-
const opt1 = options?.[0] ?? optionsRequired("clamp");
|
|
876
|
-
if (!validateNumberString(opt1)) {
|
|
877
|
-
optionMustBeNumber("clamp");
|
|
878
920
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
921
|
+
return -1;
|
|
922
|
+
}
|
|
923
|
+
function lastIndexOfOutsideQuotes(text, char) {
|
|
924
|
+
let quote = null;
|
|
925
|
+
let found = -1;
|
|
926
|
+
for (let i = 0; i < text.length; i++) {
|
|
927
|
+
const c = text[i];
|
|
928
|
+
if (quote !== null) {
|
|
929
|
+
if (c === quote)
|
|
930
|
+
quote = null;
|
|
931
|
+
} else if (isQuote(c)) {
|
|
932
|
+
quote = c;
|
|
933
|
+
} else if (c === char) {
|
|
934
|
+
found = i;
|
|
935
|
+
}
|
|
882
936
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
937
|
+
return found;
|
|
938
|
+
}
|
|
939
|
+
function splitOutsideQuotes(text, separator) {
|
|
940
|
+
const parts = [];
|
|
941
|
+
let quote = null;
|
|
942
|
+
let start = 0;
|
|
943
|
+
for (let i = 0; i < text.length; i++) {
|
|
944
|
+
const c = text[i];
|
|
945
|
+
if (quote !== null) {
|
|
946
|
+
if (c === quote)
|
|
947
|
+
quote = null;
|
|
948
|
+
} else if (isQuote(c)) {
|
|
949
|
+
quote = c;
|
|
950
|
+
} else if (c === separator) {
|
|
951
|
+
parts.push(text.slice(start, i));
|
|
952
|
+
start = i + 1;
|
|
888
953
|
}
|
|
889
|
-
return Math.min(Math.max(value, min), max);
|
|
890
|
-
};
|
|
891
|
-
};
|
|
892
|
-
var fix = (options) => {
|
|
893
|
-
const opt = options?.[0] ?? "0";
|
|
894
|
-
if (!validateNumberString(opt)) {
|
|
895
|
-
optionMustBeNumber("toFixed");
|
|
896
954
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
955
|
+
parts.push(text.slice(start));
|
|
956
|
+
return parts;
|
|
957
|
+
}
|
|
958
|
+
function clearFilterFnCacheForTooling() {
|
|
959
|
+
clearFilterResolutionCache();
|
|
960
|
+
}
|
|
961
|
+
function parseFilters(filterTextList, filterIOType, sourceText) {
|
|
962
|
+
const source = sourceText ?? filterTextList.join("|");
|
|
963
|
+
return filterTextList.map((filterText) => {
|
|
964
|
+
const openParenIndex = indexOfOutsideQuotes(filterText, "(");
|
|
965
|
+
let closeParenIndex = lastIndexOfOutsideQuotes(filterText, ")");
|
|
966
|
+
if (closeParenIndex === -1) {
|
|
967
|
+
closeParenIndex = filterText.lastIndexOf(")");
|
|
900
968
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
};
|
|
904
|
-
var locale = (options) => {
|
|
905
|
-
const explicit = options?.[0];
|
|
906
|
-
return (value) => {
|
|
907
|
-
if (typeof value !== "number") {
|
|
908
|
-
valueMustBeNumber("locale");
|
|
969
|
+
if (openParenIndex !== -1 && closeParenIndex === -1) {
|
|
970
|
+
raiseError(`[wcs/binding-syntax] Invalid filter format: missing closing parenthesis in "${filterText}".${LINT_HINT}`);
|
|
909
971
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
};
|
|
913
|
-
var uc = (_options) => {
|
|
914
|
-
return (value) => {
|
|
915
|
-
return String(value).toUpperCase();
|
|
916
|
-
};
|
|
917
|
-
};
|
|
918
|
-
var lc = (_options) => {
|
|
919
|
-
return (value) => {
|
|
920
|
-
return String(value).toLowerCase();
|
|
921
|
-
};
|
|
922
|
-
};
|
|
923
|
-
var cap = (_options) => {
|
|
924
|
-
return (value) => {
|
|
925
|
-
const v = String(value);
|
|
926
|
-
if (v.length === 0) {
|
|
927
|
-
return v;
|
|
972
|
+
if (closeParenIndex !== -1 && openParenIndex === -1) {
|
|
973
|
+
raiseError(`[wcs/binding-syntax] Invalid filter format: missing opening parenthesis in "${filterText}".${LINT_HINT}`);
|
|
928
974
|
}
|
|
929
|
-
if (
|
|
930
|
-
|
|
975
|
+
if (closeParenIndex !== -1 && closeParenIndex < openParenIndex) {
|
|
976
|
+
raiseError(`[wcs/binding-syntax] Invalid filter format: ")" comes before "(" in "${filterText}".${LINT_HINT}`);
|
|
931
977
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
};
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
978
|
+
if (closeParenIndex !== -1 && filterText.slice(closeParenIndex + 1).trim().length > 0) {
|
|
979
|
+
const trailing = filterText.slice(closeParenIndex + 1).trim();
|
|
980
|
+
raiseError(`[wcs/binding-syntax] "${filterText}": unexpected "${trailing}" after the filter's closing ")" \u2014 separate filters with "|" (write "${filterText.slice(0, closeParenIndex + 1)}|${trailing}").${LINT_HINT}`);
|
|
981
|
+
}
|
|
982
|
+
const filterName = (openParenIndex === -1 ? filterText : filterText.substring(0, openParenIndex)).trim();
|
|
983
|
+
if (filterName.length === 0) {
|
|
984
|
+
raiseError(`[wcs/binding-syntax] an empty filter in "${source}" \u2014 remove the extra "|" or name the filter.${LINT_HINT}`);
|
|
985
|
+
}
|
|
986
|
+
if (filterName.includes(MODIFIER_SEPARATOR)) {
|
|
987
|
+
const [name, modifiers] = filterName.split(MODIFIER_SEPARATOR);
|
|
988
|
+
raiseError(filterIOType === "input" ? `[wcs/binding-syntax] "${filterName}" is not a filter name: a modifier list "${MODIFIER_SEPARATOR}${modifiers}" comes before the input filters, not inside one \u2014 write "<property>${MODIFIER_SEPARATOR}${modifiers}|${name}".${LINT_HINT}` : `[wcs/binding-syntax] "${filterName}" is not a filter name: "${MODIFIER_SEPARATOR}" cannot appear in one. Modifiers belong on the left side of the binding, before the ":" \u2014 write "${name}" here.${LINT_HINT}`);
|
|
989
|
+
}
|
|
990
|
+
if (openParenIndex === -1) {
|
|
991
|
+
return { filterName, args: [], literals: [] };
|
|
992
|
+
}
|
|
993
|
+
const argsText = filterText.substring(openParenIndex + 1, closeParenIndex);
|
|
994
|
+
return { filterName, ...parseFilterArgsWithLiterals(argsText) };
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
var cacheFilterInfos$1 = /* @__PURE__ */ new Map();
|
|
998
|
+
function clearPropPartCacheForTooling() {
|
|
999
|
+
cacheFilterInfos$1.clear();
|
|
1000
|
+
}
|
|
1001
|
+
function parsePropPart(propPart) {
|
|
1002
|
+
const pos = indexOfOutsideQuotes(propPart, FILTER_SEPARATOR);
|
|
1003
|
+
let propText = "";
|
|
1004
|
+
let filterTexts = [];
|
|
1005
|
+
let filtersText = "";
|
|
1006
|
+
let filters = [];
|
|
1007
|
+
if (pos !== -1) {
|
|
1008
|
+
propText = propPart.slice(0, pos).trim();
|
|
1009
|
+
filtersText = propPart.slice(pos + 1).trim();
|
|
1010
|
+
if (cacheFilterInfos$1.has(filtersText)) {
|
|
1011
|
+
filters = cacheFilterInfos$1.get(filtersText);
|
|
1012
|
+
} else {
|
|
1013
|
+
filterTexts = splitOutsideQuotes(filtersText, FILTER_SEPARATOR).map(trimFn);
|
|
1014
|
+
filters = parseFilters(filterTexts, "input", propPart);
|
|
1015
|
+
cacheFilterInfos$1.set(filtersText, filters);
|
|
951
1016
|
}
|
|
952
|
-
|
|
1017
|
+
} else {
|
|
1018
|
+
propText = propPart.trim();
|
|
953
1019
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
};
|
|
958
|
-
var substr = (options) => {
|
|
959
|
-
const opt1 = options?.[0] ?? optionsRequired("substr");
|
|
960
|
-
if (!validateNumberString(opt1)) {
|
|
961
|
-
optionMustBeNumber("substr");
|
|
1020
|
+
const modifierParts = propText.split(MODIFIER_SEPARATOR).map(trimFn);
|
|
1021
|
+
if (modifierParts.length > 2) {
|
|
1022
|
+
raiseError(`[wcs/binding-syntax] "${propText}": a binding takes one modifier list after a single "${MODIFIER_SEPARATOR}" \u2014 write "${modifierParts[0]}${MODIFIER_SEPARATOR}${modifierParts.slice(1).join(",")}".${LINT_HINT}`);
|
|
962
1023
|
}
|
|
963
|
-
const
|
|
964
|
-
|
|
965
|
-
|
|
1024
|
+
const [propName, propModifiersText] = modifierParts;
|
|
1025
|
+
const propSegments = propName.split(DELIMITER).map(trimFn);
|
|
1026
|
+
const isExplicitProperty = propSegments.length > 1 && propSegments[0] === "";
|
|
1027
|
+
if (!isExplicitProperty && (propName.length === 0 || propSegments.some((segment) => segment.length === 0))) {
|
|
1028
|
+
raiseError(`[wcs/binding-syntax] "${propPart}": the left side of a binding must name a property \u2014 write "<property>: <path>" (modifiers and input filters come after the name).${LINT_HINT}`);
|
|
966
1029
|
}
|
|
967
|
-
|
|
968
|
-
|
|
1030
|
+
const propModifiers = propModifiersText ? propModifiersText.split(",").map(trimFn) : [];
|
|
1031
|
+
return {
|
|
1032
|
+
propName,
|
|
1033
|
+
propSegments,
|
|
1034
|
+
propModifiers,
|
|
1035
|
+
inFilters: filters
|
|
969
1036
|
};
|
|
970
|
-
}
|
|
971
|
-
var
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1037
|
+
}
|
|
1038
|
+
var cacheFilterInfos = /* @__PURE__ */ new Map();
|
|
1039
|
+
function clearStatePartCacheForTooling() {
|
|
1040
|
+
cacheFilterInfos.clear();
|
|
1041
|
+
}
|
|
1042
|
+
function parseStatePart(statePart) {
|
|
1043
|
+
const pos = indexOfOutsideQuotes(statePart, FILTER_SEPARATOR);
|
|
1044
|
+
let stateAndPath = "";
|
|
1045
|
+
let filterTexts = [];
|
|
1046
|
+
let filtersText = "";
|
|
1047
|
+
let filters = [];
|
|
1048
|
+
if (pos !== -1) {
|
|
1049
|
+
stateAndPath = statePart.slice(0, pos).trim();
|
|
1050
|
+
filtersText = statePart.slice(pos + 1).trim();
|
|
1051
|
+
if (cacheFilterInfos.has(filtersText)) {
|
|
1052
|
+
filters = cacheFilterInfos.get(filtersText);
|
|
1053
|
+
} else {
|
|
1054
|
+
filterTexts = splitOutsideQuotes(filtersText, FILTER_SEPARATOR).map(trimFn);
|
|
1055
|
+
filters = parseFilters(filterTexts, "output", statePart);
|
|
1056
|
+
cacheFilterInfos.set(filtersText, filters);
|
|
1057
|
+
}
|
|
1058
|
+
} else {
|
|
1059
|
+
stateAndPath = statePart.trim();
|
|
975
1060
|
}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
return String(value).padStart(Number(opt1), opt2);
|
|
979
|
-
};
|
|
980
|
-
};
|
|
981
|
-
var padEnd = (options) => {
|
|
982
|
-
const opt1 = options?.[0] ?? optionsRequired("padEnd");
|
|
983
|
-
if (!validateNumberString(opt1)) {
|
|
984
|
-
optionMustBeNumber("padEnd");
|
|
1061
|
+
if (stateAndPath.indexOf("@") !== -1) {
|
|
1062
|
+
raiseError(`"${stateAndPath}": the "@name" selector was removed in v2 \u2014 there is a single state tree. Mount the named state onto the tree (<wcs-state mount="...">) and read it by its path prefix instead.`);
|
|
985
1063
|
}
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
const opt = options?.[0] ?? optionsRequired("repeat");
|
|
993
|
-
if (!validateNumberString(opt)) {
|
|
994
|
-
optionMustBeNumber("repeat");
|
|
1064
|
+
const statePathName = stateAndPath;
|
|
1065
|
+
const isLoopRelative = statePathName.startsWith(DELIMITER);
|
|
1066
|
+
const body = isLoopRelative ? statePathName.slice(DELIMITER.length) : statePathName;
|
|
1067
|
+
const hasEmptySegment = body.length > 0 && body.split(DELIMITER).some((segment) => segment.length === 0);
|
|
1068
|
+
if (hasEmptySegment || !isLoopRelative && body.length === 0) {
|
|
1069
|
+
raiseError(`[wcs/binding-syntax] "${statePart}": the right side of a binding must name a state path \u2014 write "<property>: <path>" (a path segment cannot be empty; "." alone and a leading "." are the loop-relative shorthand).${LINT_HINT}`);
|
|
995
1070
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
return (value) => {
|
|
1002
|
-
return String(value).split("").reverse().join("");
|
|
1003
|
-
};
|
|
1004
|
-
};
|
|
1005
|
-
var int = (_options) => {
|
|
1006
|
-
return (value) => {
|
|
1007
|
-
return parseInt(String(value), 10);
|
|
1008
|
-
};
|
|
1009
|
-
};
|
|
1010
|
-
var float = (_options) => {
|
|
1011
|
-
return (value) => {
|
|
1012
|
-
return parseFloat(String(value));
|
|
1013
|
-
};
|
|
1014
|
-
};
|
|
1015
|
-
var round = (options) => {
|
|
1016
|
-
const opt = options?.[0] ?? "0";
|
|
1017
|
-
if (!validateNumberString(opt)) {
|
|
1018
|
-
optionMustBeNumber("round");
|
|
1019
|
-
}
|
|
1020
|
-
return (value) => {
|
|
1021
|
-
if (typeof value !== "number") {
|
|
1022
|
-
valueMustBeNumber("round");
|
|
1023
|
-
}
|
|
1024
|
-
const optValue = Math.pow(10, Number(opt));
|
|
1025
|
-
return Math.round(value * optValue) / optValue;
|
|
1071
|
+
const pathInfo = getPathInfo(statePathName);
|
|
1072
|
+
return {
|
|
1073
|
+
statePathName,
|
|
1074
|
+
statePathInfo: pathInfo,
|
|
1075
|
+
outFilters: filters
|
|
1026
1076
|
};
|
|
1027
|
-
}
|
|
1028
|
-
var
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1077
|
+
}
|
|
1078
|
+
var KEYWORDS_WITHOUT_MODIFIERS = /* @__PURE__ */ new Set([ELSE_KEYWORD, "if", "elseif", "for", SPREAD_PROP]);
|
|
1079
|
+
var EXPLICIT_PROPERTY_REJECTED_HEADS = /* @__PURE__ */ new Set([
|
|
1080
|
+
CLASS_NAMESPACE,
|
|
1081
|
+
ATTR_NAMESPACE,
|
|
1082
|
+
STYLE_NAMESPACE,
|
|
1083
|
+
COMMAND_NAMESPACE,
|
|
1084
|
+
EVENT_TOKEN_NAMESPACE,
|
|
1085
|
+
VOLUME_INJECTION_PROP
|
|
1086
|
+
]);
|
|
1087
|
+
function splitBindTexts(bindText) {
|
|
1088
|
+
return splitOutsideQuotes(bindText, BINDING_SEPARATOR);
|
|
1089
|
+
}
|
|
1090
|
+
function parseBindTextsForElement(bindText) {
|
|
1091
|
+
const [...bindTexts] = splitBindTexts(bindText).map(trimFn).filter((s) => s.length > 0);
|
|
1092
|
+
const results = bindTexts.map((bindText2) => {
|
|
1093
|
+
const separatorIndex = indexOfOutsideQuotes(bindText2, PROP_VALUE_SEPARATOR);
|
|
1094
|
+
if (separatorIndex === -1) {
|
|
1095
|
+
raiseError(`[wcs/binding-syntax] Invalid bindText: "${bindText2}". Missing ':' separator between propPart and statePart.${LINT_HINT}`);
|
|
1036
1096
|
}
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
const opt = options?.[0] ?? "0";
|
|
1043
|
-
if (!validateNumberString(opt)) {
|
|
1044
|
-
optionMustBeNumber("ceil");
|
|
1045
|
-
}
|
|
1046
|
-
return (value) => {
|
|
1047
|
-
if (typeof value !== "number") {
|
|
1048
|
-
valueMustBeNumber("ceil");
|
|
1097
|
+
const propPart = bindText2.slice(0, separatorIndex).trim();
|
|
1098
|
+
const statePart = bindText2.slice(separatorIndex + 1).trim();
|
|
1099
|
+
const keyword = propPart.split(MODIFIER_SEPARATOR)[0].split(FILTER_SEPARATOR)[0].trim();
|
|
1100
|
+
if (keyword !== propPart && KEYWORDS_WITHOUT_MODIFIERS.has(keyword)) {
|
|
1101
|
+
raiseError(`[wcs/binding-syntax] "${bindText2}": "${keyword}" takes no modifiers or filters on its left side \u2014 write "${keyword}:".${LINT_HINT}`);
|
|
1049
1102
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1103
|
+
if (propPart === ELSE_KEYWORD) {
|
|
1104
|
+
if (statePart.length > 0) {
|
|
1105
|
+
raiseError(`[wcs/binding-syntax] "${bindText2}": "else" takes no value \u2014 write "else:".${LINT_HINT}`);
|
|
1106
|
+
}
|
|
1107
|
+
const pathInfo = getPathInfo("#else");
|
|
1108
|
+
return {
|
|
1109
|
+
propName: ELSE_KEYWORD,
|
|
1110
|
+
propSegments: [ELSE_KEYWORD],
|
|
1111
|
+
propModifiers: [],
|
|
1112
|
+
statePathName: "#else",
|
|
1113
|
+
statePathInfo: pathInfo,
|
|
1114
|
+
inFilters: [],
|
|
1115
|
+
outFilters: [],
|
|
1116
|
+
bindingType: "else"
|
|
1117
|
+
};
|
|
1118
|
+
} else if (propPart === SPREAD_PROP) {
|
|
1119
|
+
if (statePart.length === 0) {
|
|
1120
|
+
raiseError(`[wcs/binding-syntax] Invalid spread binding "${bindText2}": spread target path is required.${LINT_HINT}`);
|
|
1121
|
+
}
|
|
1122
|
+
const stateResult = parseStatePart(statePart);
|
|
1123
|
+
if (stateResult.outFilters.length > 0) {
|
|
1124
|
+
raiseError(`[wcs/binding-syntax] Invalid spread binding "${bindText2}": filters are not allowed on spread targets.${LINT_HINT}`);
|
|
1125
|
+
}
|
|
1126
|
+
return {
|
|
1127
|
+
propName: SPREAD_PROP,
|
|
1128
|
+
propSegments: [SPREAD_PROP],
|
|
1129
|
+
propModifiers: [],
|
|
1130
|
+
inFilters: [],
|
|
1131
|
+
...stateResult,
|
|
1132
|
+
bindingType: "spread"
|
|
1133
|
+
};
|
|
1134
|
+
} else if (propPart === "if" || propPart === "elseif" || propPart === "for") {
|
|
1135
|
+
const stateResult = parseStatePart(statePart);
|
|
1136
|
+
return {
|
|
1137
|
+
propName: propPart,
|
|
1138
|
+
propSegments: [propPart],
|
|
1139
|
+
propModifiers: [],
|
|
1140
|
+
inFilters: [],
|
|
1141
|
+
...stateResult,
|
|
1142
|
+
bindingType: propPart
|
|
1143
|
+
};
|
|
1144
|
+
} else if (keyword === "radio" || keyword === "checkbox") {
|
|
1145
|
+
const stateResult = parseStatePart(statePart);
|
|
1146
|
+
const propResult = parsePropPart(propPart);
|
|
1147
|
+
return {
|
|
1148
|
+
...propResult,
|
|
1149
|
+
...stateResult,
|
|
1150
|
+
bindingType: keyword
|
|
1151
|
+
};
|
|
1152
|
+
} else {
|
|
1153
|
+
const stateResult = parseStatePart(statePart);
|
|
1154
|
+
const propResult = parsePropPart(propPart);
|
|
1155
|
+
if (propResult.propSegments[0] === "" && propResult.propSegments.length > 1) {
|
|
1156
|
+
const propSegments = propResult.propSegments.slice(1);
|
|
1157
|
+
if (propSegments.includes("") || EXPLICIT_PROPERTY_REJECTED_HEADS.has(propSegments[0])) {
|
|
1158
|
+
raiseError(`[wcs/binding-syntax] "${propPart}": a leading "." binds an element property by name \u2014 write a non-empty property that is not a namespace (${[...EXPLICIT_PROPERTY_REJECTED_HEADS].join(", ")}).${LINT_HINT}`);
|
|
1159
|
+
}
|
|
1160
|
+
return {
|
|
1161
|
+
...propResult,
|
|
1162
|
+
propName: propSegments.join(DELIMITER),
|
|
1163
|
+
propSegments,
|
|
1164
|
+
...stateResult,
|
|
1165
|
+
bindingType: "prop"
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
if (propResult.propSegments[0] === EVENT_TOKEN_NAMESPACE) {
|
|
1169
|
+
return {
|
|
1170
|
+
...propResult,
|
|
1171
|
+
...stateResult,
|
|
1172
|
+
bindingType: "event"
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
if (propResult.propSegments[0].startsWith(EVENT_PROP_PREFIX)) {
|
|
1176
|
+
return {
|
|
1177
|
+
...propResult,
|
|
1178
|
+
...stateResult,
|
|
1179
|
+
bindingType: "event"
|
|
1180
|
+
};
|
|
1181
|
+
} else {
|
|
1182
|
+
return {
|
|
1183
|
+
...propResult,
|
|
1184
|
+
...stateResult,
|
|
1185
|
+
bindingType: "prop"
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1062
1188
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return (value) => {
|
|
1069
|
-
if (value === null || typeof value === "undefined") {
|
|
1070
|
-
return value;
|
|
1189
|
+
});
|
|
1190
|
+
if (results.length > 1) {
|
|
1191
|
+
const isIncludeSingleBinding = results.some((r) => STRUCTURAL_BINDING_TYPE_SET.has(r.bindingType));
|
|
1192
|
+
if (isIncludeSingleBinding) {
|
|
1193
|
+
raiseError(`[wcs/template-syntax] Invalid bindText: "${bindText}". 'if', 'elseif', 'else', and 'for' bindings must be single binding. Put the structural binding alone in its own data-wcs (e.g. <template data-wcs="for: items">).${LINT_HINT}`);
|
|
1071
1194
|
}
|
|
1072
|
-
|
|
1195
|
+
}
|
|
1196
|
+
return results;
|
|
1197
|
+
}
|
|
1198
|
+
function parseBindTextForEmbeddedNode(bindText) {
|
|
1199
|
+
const stateResult = parseStatePart(bindText);
|
|
1200
|
+
return {
|
|
1201
|
+
propName: "textContent",
|
|
1202
|
+
propSegments: ["textContent"],
|
|
1203
|
+
propModifiers: [],
|
|
1204
|
+
inFilters: [],
|
|
1205
|
+
...stateResult,
|
|
1206
|
+
bindingType: "text"
|
|
1073
1207
|
};
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1208
|
+
}
|
|
1209
|
+
function clearParserCaches() {
|
|
1210
|
+
clearPathInfoCacheForTooling();
|
|
1211
|
+
clearPropPartCacheForTooling();
|
|
1212
|
+
clearStatePartCacheForTooling();
|
|
1213
|
+
clearFilterFnCacheForTooling();
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// src/language/htmlParse.ts
|
|
1217
|
+
function findStartTagRegions(html) {
|
|
1218
|
+
const out = [];
|
|
1219
|
+
let i = 0;
|
|
1220
|
+
while (i < html.length) {
|
|
1221
|
+
const lt2 = html.indexOf("<", i);
|
|
1222
|
+
if (lt2 === -1) break;
|
|
1223
|
+
const next = html[lt2 + 1];
|
|
1224
|
+
if (html.startsWith("<!--", lt2)) {
|
|
1225
|
+
const close = html.indexOf("-->", lt2 + 4);
|
|
1226
|
+
i = close === -1 ? html.length : close + 3;
|
|
1227
|
+
continue;
|
|
1080
1228
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
const opt1 = options?.[0] ?? optionsRequired("truncate");
|
|
1086
|
-
if (!validateNumberString(opt1)) {
|
|
1087
|
-
optionMustBeNumber("truncate");
|
|
1088
|
-
}
|
|
1089
|
-
const maxLength = Number(opt1);
|
|
1090
|
-
const suffix = options?.[1] ?? "\u2026";
|
|
1091
|
-
return (value) => {
|
|
1092
|
-
const v = String(value);
|
|
1093
|
-
if (v.length <= maxLength) {
|
|
1094
|
-
return v;
|
|
1229
|
+
if (next === "!" || next === "?" || next === "/") {
|
|
1230
|
+
const close = html.indexOf(">", lt2 + 1);
|
|
1231
|
+
i = close === -1 ? html.length : close + 1;
|
|
1232
|
+
continue;
|
|
1095
1233
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
var date = (options) => {
|
|
1100
|
-
const explicit = options?.[0];
|
|
1101
|
-
return (value) => {
|
|
1102
|
-
if (!(value instanceof Date)) {
|
|
1103
|
-
valueMustBeDate("date");
|
|
1234
|
+
if (next === void 0 || !/[A-Za-z]/.test(next)) {
|
|
1235
|
+
i = lt2 + 1;
|
|
1236
|
+
continue;
|
|
1104
1237
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1238
|
+
let n = lt2 + 1;
|
|
1239
|
+
while (n < html.length && /[^\s/>]/.test(html[n])) n++;
|
|
1240
|
+
const tagName = html.slice(lt2 + 1, n).toLowerCase();
|
|
1241
|
+
let j = n;
|
|
1242
|
+
let quote = null;
|
|
1243
|
+
while (j < html.length) {
|
|
1244
|
+
const c = html[j];
|
|
1245
|
+
if (quote !== null) {
|
|
1246
|
+
if (c === quote) quote = null;
|
|
1247
|
+
} else if (c === '"' || c === "'") {
|
|
1248
|
+
quote = c;
|
|
1249
|
+
} else if (c === ">") {
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
j++;
|
|
1113
1253
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
};
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
valueMustBeDate("datetime");
|
|
1254
|
+
const gt2 = j < html.length ? j : html.length;
|
|
1255
|
+
const selfClosing = html[gt2 - 1] === "/";
|
|
1256
|
+
out.push({ tagName, start: n, end: selfClosing ? gt2 - 1 : gt2 });
|
|
1257
|
+
i = gt2 + 1;
|
|
1258
|
+
if (tagName === "script" || tagName === "style") {
|
|
1259
|
+
const close = html.toLowerCase().indexOf(`</${tagName}`, i);
|
|
1260
|
+
i = close === -1 ? html.length : close;
|
|
1122
1261
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1262
|
+
}
|
|
1263
|
+
return out;
|
|
1264
|
+
}
|
|
1265
|
+
function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
|
|
1266
|
+
const blocks = [];
|
|
1267
|
+
let pos = 0;
|
|
1268
|
+
const len = html.length;
|
|
1269
|
+
while (pos < len) {
|
|
1270
|
+
if (html.startsWith("<!--", pos)) {
|
|
1271
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1272
|
+
if (commentEnd === -1) break;
|
|
1273
|
+
pos = commentEnd + 3;
|
|
1274
|
+
continue;
|
|
1131
1275
|
}
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
};
|
|
1137
|
-
};
|
|
1138
|
-
var hms = (options) => {
|
|
1139
|
-
const opt = options?.[0] ?? ":";
|
|
1140
|
-
return (value) => {
|
|
1141
|
-
if (!(value instanceof Date)) {
|
|
1142
|
-
valueMustBeDate("hms");
|
|
1276
|
+
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
1277
|
+
if (wcsMatch === null) {
|
|
1278
|
+
pos++;
|
|
1279
|
+
continue;
|
|
1143
1280
|
}
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1281
|
+
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
1282
|
+
pos = wcsMatch.end;
|
|
1283
|
+
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
1284
|
+
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
1285
|
+
while (pos < wcsEnd) {
|
|
1286
|
+
if (html.startsWith("<!--", pos)) {
|
|
1287
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1288
|
+
if (commentEnd === -1) break;
|
|
1289
|
+
pos = commentEnd + 3;
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1293
|
+
if (scriptMatch === null) {
|
|
1294
|
+
pos++;
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1298
|
+
if (typeAttr?.toLowerCase() !== "module") {
|
|
1299
|
+
pos = scriptMatch.end;
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
const contentStart = scriptMatch.end;
|
|
1303
|
+
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1304
|
+
if (scriptCloseIdx === -1) {
|
|
1305
|
+
pos = contentStart;
|
|
1306
|
+
break;
|
|
1307
|
+
}
|
|
1308
|
+
const contentEnd = scriptCloseIdx;
|
|
1309
|
+
blocks.push({
|
|
1310
|
+
contentStart,
|
|
1311
|
+
contentEnd,
|
|
1312
|
+
content: html.slice(contentStart, contentEnd),
|
|
1313
|
+
mountPath
|
|
1314
|
+
});
|
|
1315
|
+
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
1316
|
+
if (pos === 0) break;
|
|
1317
|
+
}
|
|
1318
|
+
pos = wcsEnd;
|
|
1319
|
+
if (wcsCloseIdx !== -1) {
|
|
1320
|
+
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
1321
|
+
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return blocks;
|
|
1325
|
+
}
|
|
1326
|
+
function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
1327
|
+
const elements = [];
|
|
1328
|
+
let pos = 0;
|
|
1329
|
+
const len = html.length;
|
|
1330
|
+
while (pos < len) {
|
|
1331
|
+
if (html.startsWith("<!--", pos)) {
|
|
1332
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1333
|
+
if (commentEnd === -1) break;
|
|
1334
|
+
pos = commentEnd + 3;
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
1338
|
+
if (wcsMatch === null) {
|
|
1339
|
+
pos++;
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
1343
|
+
const bindComponent = parseAttributeNames(wcsMatch.tagContent).has("bind-component");
|
|
1344
|
+
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
1345
|
+
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
1346
|
+
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
1347
|
+
const tagStart = pos;
|
|
1348
|
+
const tagEnd = wcsMatch.end;
|
|
1349
|
+
pos = wcsMatch.end;
|
|
1350
|
+
const scriptBlocks = [];
|
|
1351
|
+
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
1352
|
+
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
1353
|
+
while (pos < wcsEnd) {
|
|
1354
|
+
if (html.startsWith("<!--", pos)) {
|
|
1355
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1356
|
+
if (commentEnd === -1) break;
|
|
1357
|
+
pos = commentEnd + 3;
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1361
|
+
if (scriptMatch === null) {
|
|
1362
|
+
pos++;
|
|
1363
|
+
continue;
|
|
1364
|
+
}
|
|
1365
|
+
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1366
|
+
if (typeAttr?.toLowerCase() !== "module") {
|
|
1367
|
+
pos = scriptMatch.end;
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
const contentStart = scriptMatch.end;
|
|
1371
|
+
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1372
|
+
if (scriptCloseIdx === -1) {
|
|
1373
|
+
pos = contentStart;
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
scriptBlocks.push({
|
|
1377
|
+
contentStart,
|
|
1378
|
+
contentEnd: scriptCloseIdx,
|
|
1379
|
+
content: html.slice(contentStart, scriptCloseIdx),
|
|
1380
|
+
mountPath
|
|
1381
|
+
});
|
|
1382
|
+
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
1383
|
+
if (pos === 0) break;
|
|
1384
|
+
}
|
|
1385
|
+
elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
1386
|
+
pos = wcsEnd;
|
|
1387
|
+
if (wcsCloseIdx !== -1) {
|
|
1388
|
+
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
1389
|
+
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return elements;
|
|
1393
|
+
}
|
|
1394
|
+
function findScriptJsonById(html, id2) {
|
|
1395
|
+
let pos = 0;
|
|
1396
|
+
const len = html.length;
|
|
1397
|
+
while (pos < len) {
|
|
1398
|
+
if (html.startsWith("<!--", pos)) {
|
|
1399
|
+
const commentEnd = html.indexOf("-->", pos + 4);
|
|
1400
|
+
if (commentEnd === -1) break;
|
|
1401
|
+
pos = commentEnd + 3;
|
|
1402
|
+
continue;
|
|
1403
|
+
}
|
|
1404
|
+
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
1405
|
+
if (scriptMatch === null) {
|
|
1406
|
+
pos++;
|
|
1407
|
+
continue;
|
|
1408
|
+
}
|
|
1409
|
+
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
1410
|
+
const idAttr = extractAttribute(scriptMatch.tagContent, "id");
|
|
1411
|
+
if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
|
|
1412
|
+
const contentStart = scriptMatch.end;
|
|
1413
|
+
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
1414
|
+
if (scriptCloseIdx === -1) return null;
|
|
1415
|
+
return html.slice(contentStart, scriptCloseIdx);
|
|
1416
|
+
}
|
|
1417
|
+
pos = scriptMatch.end;
|
|
1418
|
+
}
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1421
|
+
function matchOpenTag(html, pos, tagName) {
|
|
1422
|
+
if (html[pos] !== "<") return null;
|
|
1423
|
+
const nameStart = pos + 1;
|
|
1424
|
+
const nameEnd = nameStart + tagName.length;
|
|
1425
|
+
if (nameEnd > html.length) return null;
|
|
1426
|
+
const slice2 = html.slice(nameStart, nameEnd);
|
|
1427
|
+
if (slice2.toLowerCase() !== tagName.toLowerCase()) return null;
|
|
1428
|
+
const charAfter = html[nameEnd];
|
|
1429
|
+
if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
|
|
1430
|
+
return null;
|
|
1431
|
+
}
|
|
1432
|
+
let i = nameEnd;
|
|
1433
|
+
let inSingleQuote = false;
|
|
1434
|
+
let inDoubleQuote = false;
|
|
1435
|
+
while (i < html.length) {
|
|
1436
|
+
const ch = html[i];
|
|
1437
|
+
if (inSingleQuote) {
|
|
1438
|
+
if (ch === "'") inSingleQuote = false;
|
|
1439
|
+
} else if (inDoubleQuote) {
|
|
1440
|
+
if (ch === '"') inDoubleQuote = false;
|
|
1441
|
+
} else if (ch === "'") {
|
|
1442
|
+
inSingleQuote = true;
|
|
1443
|
+
} else if (ch === '"') {
|
|
1444
|
+
inDoubleQuote = true;
|
|
1445
|
+
} else if (ch === ">") {
|
|
1446
|
+
return {
|
|
1447
|
+
start: pos,
|
|
1448
|
+
end: i + 1,
|
|
1449
|
+
tagContent: html.slice(nameEnd, i)
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
i++;
|
|
1453
|
+
}
|
|
1454
|
+
return null;
|
|
1455
|
+
}
|
|
1456
|
+
function findCloseTag(html, startPos, tagName) {
|
|
1457
|
+
const pattern = "</" + tagName;
|
|
1458
|
+
const patternLower = pattern.toLowerCase();
|
|
1459
|
+
const htmlLower = html.toLowerCase();
|
|
1460
|
+
let pos = startPos;
|
|
1461
|
+
while (pos < html.length) {
|
|
1462
|
+
const idx = htmlLower.indexOf(patternLower, pos);
|
|
1463
|
+
if (idx === -1) return -1;
|
|
1464
|
+
const afterIdx = idx + pattern.length;
|
|
1465
|
+
if (afterIdx < html.length) {
|
|
1466
|
+
const ch = html[afterIdx];
|
|
1467
|
+
if (ch === ">" || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
1468
|
+
return idx;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
pos = idx + 1;
|
|
1472
|
+
}
|
|
1473
|
+
return -1;
|
|
1474
|
+
}
|
|
1475
|
+
function parseAttributeNames(tagContent) {
|
|
1476
|
+
const names = /* @__PURE__ */ new Set();
|
|
1477
|
+
const attribute = /([^\s"'<>/=]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'<>`=]+))?/g;
|
|
1478
|
+
let match;
|
|
1479
|
+
while ((match = attribute.exec(tagContent)) !== null) {
|
|
1480
|
+
names.add(match[1].toLowerCase());
|
|
1481
|
+
}
|
|
1482
|
+
return names;
|
|
1483
|
+
}
|
|
1484
|
+
function extractAttribute(tagContent, attrName) {
|
|
1485
|
+
const regex = new RegExp(
|
|
1486
|
+
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
1487
|
+
"i"
|
|
1488
|
+
);
|
|
1489
|
+
const match = tagContent.match(regex);
|
|
1490
|
+
if (!match) return null;
|
|
1491
|
+
return match[1] ?? match[2] ?? match[3] ?? null;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// src/core/parser/quoteAware.ts
|
|
1495
|
+
var isQuote2 = (c) => c === "'" || c === '"';
|
|
1496
|
+
function indexOfOutsideQuotes2(text, char) {
|
|
1497
|
+
let quote = null;
|
|
1498
|
+
for (let i = 0; i < text.length; i++) {
|
|
1499
|
+
const c = text[i];
|
|
1500
|
+
if (quote !== null) {
|
|
1501
|
+
if (c === quote) quote = null;
|
|
1502
|
+
} else if (isQuote2(c)) {
|
|
1503
|
+
quote = c;
|
|
1504
|
+
} else if (c === char) {
|
|
1505
|
+
return i;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
return -1;
|
|
1509
|
+
}
|
|
1510
|
+
function splitOutsideQuotes2(text, separator) {
|
|
1511
|
+
const parts = [];
|
|
1512
|
+
let quote = null;
|
|
1513
|
+
let start = 0;
|
|
1514
|
+
for (let i = 0; i < text.length; i++) {
|
|
1515
|
+
const c = text[i];
|
|
1516
|
+
if (quote !== null) {
|
|
1517
|
+
if (c === quote) quote = null;
|
|
1518
|
+
} else if (isQuote2(c)) {
|
|
1519
|
+
quote = c;
|
|
1520
|
+
} else if (c === separator) {
|
|
1521
|
+
parts.push(text.slice(start, i));
|
|
1522
|
+
start = i + 1;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
parts.push(text.slice(start));
|
|
1526
|
+
return parts;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
// ../state/dist/manifest.esm.js
|
|
1530
|
+
var _config = {
|
|
1531
|
+
bindAttributeName: "data-wcs",
|
|
1532
|
+
tagNames: {
|
|
1533
|
+
state: "wcs-state"
|
|
1534
|
+
},
|
|
1535
|
+
locale: "en"
|
|
1536
|
+
};
|
|
1537
|
+
var config = _config;
|
|
1538
|
+
var DELIMITER2 = ".";
|
|
1539
|
+
var WILDCARD2 = "*";
|
|
1540
|
+
var MAX_WILDCARD_DEPTH2 = 128;
|
|
1541
|
+
var BINDING_SEPARATOR2 = ";";
|
|
1542
|
+
var PROP_VALUE_SEPARATOR2 = ":";
|
|
1543
|
+
var MODIFIER_SEPARATOR2 = "#";
|
|
1544
|
+
var FILTER_SEPARATOR2 = "|";
|
|
1545
|
+
var MODIFIER_PREVENT = "prevent";
|
|
1546
|
+
var MODIFIER_STOP = "stop";
|
|
1547
|
+
var MODIFIER_READONLY = "ro";
|
|
1548
|
+
var MODIFIER_FLAGS = Object.freeze([
|
|
1549
|
+
MODIFIER_PREVENT,
|
|
1550
|
+
MODIFIER_STOP,
|
|
1551
|
+
MODIFIER_READONLY
|
|
1552
|
+
]);
|
|
1553
|
+
var MODIFIER_KEY_INIT = "init";
|
|
1554
|
+
var MODIFIER_KEY_SYNC = "sync";
|
|
1555
|
+
var MODIFIER_KEYS = Object.freeze([
|
|
1556
|
+
MODIFIER_KEY_INIT,
|
|
1557
|
+
MODIFIER_KEY_SYNC
|
|
1558
|
+
]);
|
|
1559
|
+
var ELSE_KEYWORD2 = "else";
|
|
1560
|
+
var SPREAD_PROP2 = "...";
|
|
1561
|
+
var EVENT_PROP_PREFIX2 = "on";
|
|
1562
|
+
var EVENT_TOKEN_NAMESPACE2 = "eventToken";
|
|
1563
|
+
var COMMAND_NAMESPACE2 = "command";
|
|
1564
|
+
var CLASS_NAMESPACE2 = "class";
|
|
1565
|
+
var ATTR_NAMESPACE2 = "attr";
|
|
1566
|
+
var STYLE_NAMESPACE2 = "style";
|
|
1567
|
+
var INDEX_PARAM_PREFIX2 = "$";
|
|
1568
|
+
var tmpIndexByIndexName2 = {};
|
|
1569
|
+
for (let i = 0; i < MAX_WILDCARD_DEPTH2; i++) {
|
|
1570
|
+
tmpIndexByIndexName2[`${INDEX_PARAM_PREFIX2}${i + 1}`] = i;
|
|
1571
|
+
}
|
|
1572
|
+
Object.freeze(tmpIndexByIndexName2);
|
|
1573
|
+
var STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
|
|
1574
|
+
var STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
|
|
1575
|
+
var STATE_RENDERED_CALLBACK_NAME = "$renderedCallback";
|
|
1576
|
+
var STATE_ERROR_CALLBACK_NAME = "$errorCallback";
|
|
1577
|
+
var WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
|
|
1578
|
+
var STATE_BINDABLES_NAME = "$bindables";
|
|
1579
|
+
var STATE_COMMANDS_NAME = "$commands";
|
|
1580
|
+
var STATE_COMMAND_TOKENS_NAME = "$commandTokens";
|
|
1581
|
+
var STATE_COMMAND_NAMESPACE_NAME = "$command";
|
|
1582
|
+
var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
1583
|
+
var STATE_ON_NAME = "$on";
|
|
1584
|
+
var STATE_STREAM_NAME = "$stream";
|
|
1585
|
+
var STATE_WATCH_NAME = "$watch";
|
|
1586
|
+
var STATE_SCAN_NAME = "$scan";
|
|
1587
|
+
var STATE_RECURSION_NAME = "$recursion";
|
|
1588
|
+
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
1589
|
+
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
1590
|
+
var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
|
|
1591
|
+
function raiseError2(message) {
|
|
1592
|
+
throw new Error(`[@wcstack/state] ${message}`);
|
|
1593
|
+
}
|
|
1594
|
+
var DECLARATION_ALIASES = {
|
|
1595
|
+
$updatedCallback: STATE_RENDERED_CALLBACK_NAME,
|
|
1596
|
+
$streams: STATE_STREAM_NAME
|
|
1597
|
+
};
|
|
1598
|
+
function optionsRequired(fnName) {
|
|
1599
|
+
raiseError2(`filter ${fnName} requires at least one option`);
|
|
1600
|
+
}
|
|
1601
|
+
function optionMustBeNumber(fnName) {
|
|
1602
|
+
raiseError2(`filter ${fnName} requires a number as option`);
|
|
1603
|
+
}
|
|
1604
|
+
function valueMustBeNumber(fnName) {
|
|
1605
|
+
raiseError2(`filter ${fnName} requires a number value`);
|
|
1606
|
+
}
|
|
1607
|
+
function valueMustBeDate(fnName) {
|
|
1608
|
+
raiseError2(`filter ${fnName} requires a date value`);
|
|
1609
|
+
}
|
|
1610
|
+
function valueMustBeArray(fnName) {
|
|
1611
|
+
raiseError2(`filter ${fnName} requires an array value`);
|
|
1612
|
+
}
|
|
1613
|
+
var builtinFilterAliases = {
|
|
1614
|
+
inc: "add",
|
|
1615
|
+
dec: "sub",
|
|
1616
|
+
fix: "toFixed",
|
|
1617
|
+
uc: "upper",
|
|
1618
|
+
lc: "lower",
|
|
1619
|
+
cap: "capitalize",
|
|
1620
|
+
rep: "repeat",
|
|
1621
|
+
rev: "reverse",
|
|
1622
|
+
pad: "padStart",
|
|
1623
|
+
null: "nullIfEmpty"
|
|
1624
|
+
};
|
|
1625
|
+
function validateNumberString(value) {
|
|
1626
|
+
if (!value || isNaN(Number(value))) {
|
|
1627
|
+
return false;
|
|
1628
|
+
}
|
|
1629
|
+
return true;
|
|
1630
|
+
}
|
|
1631
|
+
function numberOption(value, fnName) {
|
|
1632
|
+
if (!validateNumberString(value)) {
|
|
1633
|
+
optionMustBeNumber(fnName);
|
|
1634
|
+
}
|
|
1635
|
+
return Number(value);
|
|
1636
|
+
}
|
|
1637
|
+
function requiredNumberOption(options, index, fnName) {
|
|
1638
|
+
return numberOption(options?.[index] ?? optionsRequired(fnName), fnName);
|
|
1639
|
+
}
|
|
1640
|
+
var nullishPassthrough = (factory) => (options) => {
|
|
1641
|
+
const filterFn = factory(options);
|
|
1642
|
+
return (value) => value == null ? value : filterFn(value);
|
|
1643
|
+
};
|
|
1644
|
+
var eq = (options, literals) => {
|
|
1645
|
+
const opt = options?.[0] ?? optionsRequired("eq");
|
|
1646
|
+
const literal2 = literals !== void 0 && literals.length > 0 ? literals[0] : opt;
|
|
1647
|
+
return (value) => {
|
|
1648
|
+
if (typeof value === "number") {
|
|
1649
|
+
if (typeof literal2 !== "string") {
|
|
1650
|
+
return value === literal2;
|
|
1651
|
+
}
|
|
1652
|
+
if (!validateNumberString(opt)) {
|
|
1653
|
+
optionMustBeNumber("eq");
|
|
1654
|
+
}
|
|
1655
|
+
return value === Number(opt);
|
|
1656
|
+
}
|
|
1657
|
+
if (typeof value === "string") {
|
|
1658
|
+
return value === opt;
|
|
1659
|
+
}
|
|
1660
|
+
return value === literal2;
|
|
1661
|
+
};
|
|
1662
|
+
};
|
|
1663
|
+
var ne = (options, literals) => {
|
|
1664
|
+
const opt = options?.[0] ?? optionsRequired("ne");
|
|
1665
|
+
const literal2 = literals !== void 0 && literals.length > 0 ? literals[0] : opt;
|
|
1666
|
+
return (value) => {
|
|
1667
|
+
if (typeof value === "number") {
|
|
1668
|
+
if (typeof literal2 !== "string") {
|
|
1669
|
+
return value !== literal2;
|
|
1670
|
+
}
|
|
1671
|
+
if (!validateNumberString(opt)) {
|
|
1672
|
+
optionMustBeNumber("ne");
|
|
1673
|
+
}
|
|
1674
|
+
return value !== Number(opt);
|
|
1675
|
+
}
|
|
1676
|
+
if (typeof value === "string") {
|
|
1677
|
+
return value !== opt;
|
|
1678
|
+
}
|
|
1679
|
+
return value !== literal2;
|
|
1680
|
+
};
|
|
1681
|
+
};
|
|
1682
|
+
var not = (_options) => {
|
|
1683
|
+
return (value) => !value;
|
|
1684
|
+
};
|
|
1685
|
+
var lt = (options) => {
|
|
1686
|
+
const opt = requiredNumberOption(options, 0, "lt");
|
|
1687
|
+
return (value) => {
|
|
1688
|
+
if (typeof value !== "number") {
|
|
1689
|
+
valueMustBeNumber("lt");
|
|
1690
|
+
}
|
|
1691
|
+
return value < opt;
|
|
1692
|
+
};
|
|
1693
|
+
};
|
|
1694
|
+
var le = (options) => {
|
|
1695
|
+
const opt = requiredNumberOption(options, 0, "le");
|
|
1696
|
+
return (value) => {
|
|
1697
|
+
if (typeof value !== "number") {
|
|
1698
|
+
valueMustBeNumber("le");
|
|
1699
|
+
}
|
|
1700
|
+
return value <= opt;
|
|
1701
|
+
};
|
|
1702
|
+
};
|
|
1703
|
+
var gt = (options) => {
|
|
1704
|
+
const opt = requiredNumberOption(options, 0, "gt");
|
|
1705
|
+
return (value) => {
|
|
1706
|
+
if (typeof value !== "number") {
|
|
1707
|
+
valueMustBeNumber("gt");
|
|
1708
|
+
}
|
|
1709
|
+
return value > opt;
|
|
1710
|
+
};
|
|
1711
|
+
};
|
|
1712
|
+
var ge = (options) => {
|
|
1713
|
+
const opt = requiredNumberOption(options, 0, "ge");
|
|
1714
|
+
return (value) => {
|
|
1715
|
+
if (typeof value !== "number") {
|
|
1716
|
+
valueMustBeNumber("ge");
|
|
1717
|
+
}
|
|
1718
|
+
return value >= opt;
|
|
1719
|
+
};
|
|
1720
|
+
};
|
|
1721
|
+
var inc = (options) => {
|
|
1722
|
+
const opt = requiredNumberOption(options, 0, "add");
|
|
1723
|
+
return (value) => {
|
|
1724
|
+
if (typeof value !== "number") {
|
|
1725
|
+
valueMustBeNumber("add");
|
|
1726
|
+
}
|
|
1727
|
+
return value + opt;
|
|
1728
|
+
};
|
|
1729
|
+
};
|
|
1730
|
+
var dec = (options) => {
|
|
1731
|
+
const opt = requiredNumberOption(options, 0, "sub");
|
|
1732
|
+
return (value) => {
|
|
1733
|
+
if (typeof value !== "number") {
|
|
1734
|
+
valueMustBeNumber("sub");
|
|
1735
|
+
}
|
|
1736
|
+
return value - opt;
|
|
1737
|
+
};
|
|
1738
|
+
};
|
|
1739
|
+
var mul = (options) => {
|
|
1740
|
+
const opt = requiredNumberOption(options, 0, "mul");
|
|
1741
|
+
return (value) => {
|
|
1742
|
+
if (typeof value !== "number") {
|
|
1743
|
+
valueMustBeNumber("mul");
|
|
1744
|
+
}
|
|
1745
|
+
return value * opt;
|
|
1746
|
+
};
|
|
1747
|
+
};
|
|
1748
|
+
var div = (options) => {
|
|
1749
|
+
const opt = requiredNumberOption(options, 0, "div");
|
|
1750
|
+
return (value) => {
|
|
1751
|
+
if (typeof value !== "number") {
|
|
1752
|
+
valueMustBeNumber("div");
|
|
1753
|
+
}
|
|
1754
|
+
return value / opt;
|
|
1755
|
+
};
|
|
1756
|
+
};
|
|
1757
|
+
var mod = (options) => {
|
|
1758
|
+
const opt = requiredNumberOption(options, 0, "mod");
|
|
1759
|
+
return (value) => {
|
|
1760
|
+
if (typeof value !== "number") {
|
|
1761
|
+
valueMustBeNumber("mod");
|
|
1762
|
+
}
|
|
1763
|
+
return value % opt;
|
|
1764
|
+
};
|
|
1765
|
+
};
|
|
1766
|
+
var abs = (_options) => {
|
|
1767
|
+
return (value) => {
|
|
1768
|
+
if (typeof value !== "number") {
|
|
1769
|
+
valueMustBeNumber("abs");
|
|
1770
|
+
}
|
|
1771
|
+
return Math.abs(value);
|
|
1772
|
+
};
|
|
1773
|
+
};
|
|
1774
|
+
var clamp = (options) => {
|
|
1775
|
+
const min = requiredNumberOption(options, 0, "clamp");
|
|
1776
|
+
const max = requiredNumberOption(options, 1, "clamp");
|
|
1777
|
+
return (value) => {
|
|
1778
|
+
if (typeof value !== "number") {
|
|
1779
|
+
valueMustBeNumber("clamp");
|
|
1780
|
+
}
|
|
1781
|
+
return Math.min(Math.max(value, min), max);
|
|
1782
|
+
};
|
|
1783
|
+
};
|
|
1784
|
+
var fix = (options) => {
|
|
1785
|
+
const opt = numberOption(options?.[0] ?? "0", "toFixed");
|
|
1786
|
+
return (value) => {
|
|
1787
|
+
if (typeof value !== "number") {
|
|
1788
|
+
valueMustBeNumber("toFixed");
|
|
1789
|
+
}
|
|
1790
|
+
return value.toFixed(opt);
|
|
1791
|
+
};
|
|
1792
|
+
};
|
|
1793
|
+
var locale = (options) => {
|
|
1794
|
+
const explicit = options?.[0];
|
|
1795
|
+
return (value) => {
|
|
1796
|
+
if (typeof value !== "number") {
|
|
1797
|
+
valueMustBeNumber("locale");
|
|
1798
|
+
}
|
|
1799
|
+
return value.toLocaleString(explicit ?? config.locale);
|
|
1800
|
+
};
|
|
1801
|
+
};
|
|
1802
|
+
var uc = (_options) => {
|
|
1803
|
+
return (value) => {
|
|
1804
|
+
return String(value).toUpperCase();
|
|
1805
|
+
};
|
|
1806
|
+
};
|
|
1807
|
+
var lc = (_options) => {
|
|
1808
|
+
return (value) => {
|
|
1809
|
+
return String(value).toLowerCase();
|
|
1810
|
+
};
|
|
1811
|
+
};
|
|
1812
|
+
var cap = (_options) => {
|
|
1813
|
+
return (value) => {
|
|
1814
|
+
const v = String(value);
|
|
1815
|
+
if (v.length === 0) {
|
|
1816
|
+
return v;
|
|
1817
|
+
}
|
|
1818
|
+
if (v.length === 1) {
|
|
1819
|
+
return v.toUpperCase();
|
|
1820
|
+
}
|
|
1821
|
+
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
1822
|
+
};
|
|
1823
|
+
};
|
|
1824
|
+
var trim = (_options) => {
|
|
1825
|
+
return (value) => {
|
|
1826
|
+
return String(value).trim();
|
|
1827
|
+
};
|
|
1828
|
+
};
|
|
1829
|
+
var slice = (options) => {
|
|
1830
|
+
const numberedOpts = [requiredNumberOption(options, 0, "slice")];
|
|
1831
|
+
const opt2 = options?.[1];
|
|
1832
|
+
if (typeof opt2 !== "undefined") {
|
|
1833
|
+
numberedOpts.push(numberOption(opt2, "slice"));
|
|
1834
|
+
}
|
|
1835
|
+
return (value) => {
|
|
1836
|
+
return String(value).slice(...numberedOpts);
|
|
1837
|
+
};
|
|
1838
|
+
};
|
|
1839
|
+
var substr = (options) => {
|
|
1840
|
+
const opt1 = requiredNumberOption(options, 0, "substr");
|
|
1841
|
+
const opt2 = requiredNumberOption(options, 1, "substr");
|
|
1842
|
+
return (value) => {
|
|
1843
|
+
return String(value).substr(opt1, opt2);
|
|
1844
|
+
};
|
|
1845
|
+
};
|
|
1846
|
+
var pad = (options) => {
|
|
1847
|
+
const opt1 = requiredNumberOption(options, 0, "padStart");
|
|
1848
|
+
const opt2 = options?.[1] ?? "0";
|
|
1849
|
+
return (value) => {
|
|
1850
|
+
return String(value).padStart(opt1, opt2);
|
|
1851
|
+
};
|
|
1852
|
+
};
|
|
1853
|
+
var padEnd = (options) => {
|
|
1854
|
+
const opt1 = requiredNumberOption(options, 0, "padEnd");
|
|
1855
|
+
const opt2 = options?.[1] ?? " ";
|
|
1856
|
+
return (value) => {
|
|
1857
|
+
return String(value).padEnd(opt1, opt2);
|
|
1858
|
+
};
|
|
1859
|
+
};
|
|
1860
|
+
var rep = (options) => {
|
|
1861
|
+
const opt = requiredNumberOption(options, 0, "repeat");
|
|
1862
|
+
return (value) => {
|
|
1863
|
+
return String(value).repeat(opt);
|
|
1864
|
+
};
|
|
1865
|
+
};
|
|
1866
|
+
var rev = (_options) => {
|
|
1867
|
+
return (value) => {
|
|
1868
|
+
return String(value).split("").reverse().join("");
|
|
1869
|
+
};
|
|
1870
|
+
};
|
|
1871
|
+
var int = (_options) => {
|
|
1872
|
+
return (value) => {
|
|
1873
|
+
return parseInt(String(value), 10);
|
|
1874
|
+
};
|
|
1875
|
+
};
|
|
1876
|
+
var float = (_options) => {
|
|
1877
|
+
return (value) => {
|
|
1878
|
+
return parseFloat(String(value));
|
|
1879
|
+
};
|
|
1880
|
+
};
|
|
1881
|
+
var round = (options) => {
|
|
1882
|
+
const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "round"));
|
|
1883
|
+
return (value) => {
|
|
1884
|
+
if (typeof value !== "number") {
|
|
1885
|
+
valueMustBeNumber("round");
|
|
1886
|
+
}
|
|
1887
|
+
return Math.round(value * optValue) / optValue;
|
|
1888
|
+
};
|
|
1889
|
+
};
|
|
1890
|
+
var floor = (options) => {
|
|
1891
|
+
const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "floor"));
|
|
1892
|
+
return (value) => {
|
|
1893
|
+
if (typeof value !== "number") {
|
|
1894
|
+
valueMustBeNumber("floor");
|
|
1895
|
+
}
|
|
1896
|
+
return Math.floor(value * optValue) / optValue;
|
|
1897
|
+
};
|
|
1898
|
+
};
|
|
1899
|
+
var ceil = (options) => {
|
|
1900
|
+
const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "ceil"));
|
|
1901
|
+
return (value) => {
|
|
1902
|
+
if (typeof value !== "number") {
|
|
1903
|
+
valueMustBeNumber("ceil");
|
|
1904
|
+
}
|
|
1905
|
+
return Math.ceil(value * optValue) / optValue;
|
|
1906
|
+
};
|
|
1907
|
+
};
|
|
1908
|
+
var percent = (options) => {
|
|
1909
|
+
const opt = numberOption(options?.[0] ?? "0", "percent");
|
|
1910
|
+
return (value) => {
|
|
1911
|
+
if (typeof value !== "number") {
|
|
1912
|
+
valueMustBeNumber("percent");
|
|
1913
|
+
}
|
|
1914
|
+
return `${(value * 100).toFixed(opt)}%`;
|
|
1915
|
+
};
|
|
1916
|
+
};
|
|
1917
|
+
var unit = (options) => {
|
|
1918
|
+
const opt = options?.[0] ?? optionsRequired("unit");
|
|
1919
|
+
return (value) => {
|
|
1920
|
+
return String(value) + opt;
|
|
1921
|
+
};
|
|
1922
|
+
};
|
|
1923
|
+
var join = (options) => {
|
|
1924
|
+
const opt = options?.[0] ?? ", ";
|
|
1925
|
+
return (value) => {
|
|
1926
|
+
if (!Array.isArray(value)) {
|
|
1927
|
+
valueMustBeArray("join");
|
|
1928
|
+
}
|
|
1929
|
+
return value.join(opt);
|
|
1930
|
+
};
|
|
1931
|
+
};
|
|
1932
|
+
var truncate = (options) => {
|
|
1933
|
+
const maxLength = requiredNumberOption(options, 0, "truncate");
|
|
1934
|
+
const suffix = options?.[1] ?? "\u2026";
|
|
1935
|
+
return (value) => {
|
|
1936
|
+
const v = String(value);
|
|
1937
|
+
if (v.length <= maxLength) {
|
|
1938
|
+
return v;
|
|
1939
|
+
}
|
|
1940
|
+
return v.slice(0, maxLength) + suffix;
|
|
1941
|
+
};
|
|
1942
|
+
};
|
|
1943
|
+
var date = (options) => {
|
|
1944
|
+
const explicit = options?.[0];
|
|
1945
|
+
return (value) => {
|
|
1946
|
+
if (!(value instanceof Date)) {
|
|
1947
|
+
valueMustBeDate("date");
|
|
1948
|
+
}
|
|
1949
|
+
return value.toLocaleDateString(explicit ?? config.locale);
|
|
1950
|
+
};
|
|
1951
|
+
};
|
|
1952
|
+
var time = (options) => {
|
|
1953
|
+
const explicit = options?.[0];
|
|
1954
|
+
return (value) => {
|
|
1955
|
+
if (!(value instanceof Date)) {
|
|
1956
|
+
valueMustBeDate("time");
|
|
1957
|
+
}
|
|
1958
|
+
return value.toLocaleTimeString(explicit ?? config.locale);
|
|
1959
|
+
};
|
|
1960
|
+
};
|
|
1961
|
+
var datetime = (options) => {
|
|
1962
|
+
const explicit = options?.[0];
|
|
1963
|
+
return (value) => {
|
|
1964
|
+
if (!(value instanceof Date)) {
|
|
1965
|
+
valueMustBeDate("datetime");
|
|
1966
|
+
}
|
|
1967
|
+
return value.toLocaleString(explicit ?? config.locale);
|
|
1968
|
+
};
|
|
1969
|
+
};
|
|
1970
|
+
var ymd = (options) => {
|
|
1971
|
+
const opt = options?.[0] ?? "-";
|
|
1972
|
+
return (value) => {
|
|
1973
|
+
if (!(value instanceof Date)) {
|
|
1974
|
+
valueMustBeDate("ymd");
|
|
1975
|
+
}
|
|
1976
|
+
const year = value.getFullYear().toString();
|
|
1977
|
+
const month = (value.getMonth() + 1).toString().padStart(2, "0");
|
|
1978
|
+
const day = value.getDate().toString().padStart(2, "0");
|
|
1979
|
+
return `${year}${opt}${month}${opt}${day}`;
|
|
1980
|
+
};
|
|
1981
|
+
};
|
|
1982
|
+
var hms = (options) => {
|
|
1983
|
+
const opt = options?.[0] ?? ":";
|
|
1984
|
+
return (value) => {
|
|
1985
|
+
if (!(value instanceof Date)) {
|
|
1986
|
+
valueMustBeDate("hms");
|
|
1987
|
+
}
|
|
1988
|
+
const hours = value.getHours().toString().padStart(2, "0");
|
|
1989
|
+
const minutes = value.getMinutes().toString().padStart(2, "0");
|
|
1990
|
+
const seconds = value.getSeconds().toString().padStart(2, "0");
|
|
1991
|
+
return `${hours}${opt}${minutes}${opt}${seconds}`;
|
|
1992
|
+
};
|
|
1993
|
+
};
|
|
1994
|
+
var falsy = (_options) => {
|
|
1995
|
+
return (value) => !value;
|
|
1152
1996
|
};
|
|
1153
1997
|
var truthy = (_options) => {
|
|
1154
1998
|
return (value) => !!value;
|
|
@@ -1205,17 +2049,18 @@ var builtinFilters = {
|
|
|
1205
2049
|
"clamp": clamp,
|
|
1206
2050
|
"toFixed": fix,
|
|
1207
2051
|
"locale": locale,
|
|
1208
|
-
|
|
1209
|
-
"
|
|
1210
|
-
"
|
|
1211
|
-
"
|
|
1212
|
-
"
|
|
1213
|
-
"
|
|
1214
|
-
"
|
|
1215
|
-
"
|
|
1216
|
-
"
|
|
1217
|
-
"
|
|
1218
|
-
"
|
|
2052
|
+
// `String(value)` で組み立てる族は、値が無いときに素通しする(要件 B8 — nullishPassthrough を参照)
|
|
2053
|
+
"upper": nullishPassthrough(uc),
|
|
2054
|
+
"lower": nullishPassthrough(lc),
|
|
2055
|
+
"capitalize": nullishPassthrough(cap),
|
|
2056
|
+
"trim": nullishPassthrough(trim),
|
|
2057
|
+
"slice": nullishPassthrough(slice),
|
|
2058
|
+
"substr": nullishPassthrough(substr),
|
|
2059
|
+
"padStart": nullishPassthrough(pad),
|
|
2060
|
+
"padEnd": nullishPassthrough(padEnd),
|
|
2061
|
+
"repeat": nullishPassthrough(rep),
|
|
2062
|
+
"reverse": nullishPassthrough(rev),
|
|
2063
|
+
"truncate": nullishPassthrough(truncate),
|
|
1219
2064
|
"join": join,
|
|
1220
2065
|
"int": int,
|
|
1221
2066
|
"float": float,
|
|
@@ -1223,7 +2068,7 @@ var builtinFilters = {
|
|
|
1223
2068
|
"floor": floor,
|
|
1224
2069
|
"ceil": ceil,
|
|
1225
2070
|
"percent": percent,
|
|
1226
|
-
"unit": unit,
|
|
2071
|
+
"unit": nullishPassthrough(unit),
|
|
1227
2072
|
"date": date,
|
|
1228
2073
|
"time": time,
|
|
1229
2074
|
"datetime": datetime,
|
|
@@ -1243,7 +2088,9 @@ var builtinFilterMeta = {
|
|
|
1243
2088
|
// 比較・論理
|
|
1244
2089
|
eq: { description: "\u7B49\u3057\u3044\u304B\u6BD4\u8F03", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
|
|
1245
2090
|
ne: { description: "\u7570\u306A\u308B\u304B\u6BD4\u8F03", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
|
|
1246
|
-
not
|
|
2091
|
+
// `not` は真偽性(truthy / falsy)の反転。`if:` が `Boolean()` で寄せるのと同じ規則で、
|
|
2092
|
+
// `else:` はこのフィルタを足した束縛として組み立てられる(structural/createNotFilter.ts)
|
|
2093
|
+
not: { description: "\u771F\u507D\u6027\u3092\u53CD\u8EE2\uFF08falsy \u2192 true\uFF09", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
1247
2094
|
lt: { description: "\u3088\u308A\u5C0F\u3055\u3044\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
1248
2095
|
le: { description: "\u4EE5\u4E0B\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
1249
2096
|
gt: { description: "\u3088\u308A\u5927\u304D\u3044\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
@@ -1265,13 +2112,15 @@ var builtinFilterMeta = {
|
|
|
1265
2112
|
capitalize: { description: "\u5148\u982D\u6587\u5B57\u3092\u5927\u6587\u5B57\u306B", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
1266
2113
|
trim: { description: "\u524D\u5F8C\u306E\u7A7A\u767D\u3092\u524A\u9664", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
1267
2114
|
slice: { description: "\u90E8\u5206\u6587\u5B57\u5217 (start[,end])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
2115
|
+
// 長さは省略できない(実装が両方読む)。minArgs: 1 だった頃は補完・lint が `substr(0)` を
|
|
2116
|
+
// 通し、実行時にだけ落ちていた
|
|
2117
|
+
substr: { description: "\u90E8\u5206\u6587\u5B57\u5217 (pos,len)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 2, maxArgs: 2, argTypes: ["number", "number"] },
|
|
2118
|
+
padStart: { description: "\u5148\u982D\u3092\u57CB\u3081\u308B (length[,char]\u3002char \u306E\u65E2\u5B9A\u306F 0 \u2014 JS \u306E\u65E2\u5B9A\u306F\u7A7A\u767D\u306A\u306E\u3067\u6CE8\u610F)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
|
|
2119
|
+
padEnd: { description: "\u672B\u5C3E\u3092\u57CB\u3081\u308B (length[,char]\u3002char \u306E\u65E2\u5B9A\u306F\u7A7A\u767D \u2014 JS \u3068\u540C\u3058)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
|
|
1271
2120
|
repeat: { description: "\u7E70\u308A\u8FD4\u3057 (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
1272
2121
|
reverse: { description: "\u6587\u5B57\u9806\u3092\u53CD\u8EE2", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
1273
|
-
truncate: { description: "\u5207\u308A\u8A70\u3081\u3066\u7701\u7565\u8A18\u53F7 (length[,suffix])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
|
|
1274
|
-
join: { description: "\u914D\u5217\u3092\u9023\u7D50 ([separator])", hasArgs: true, resultType: "string", acceptTypes: ["array"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
2122
|
+
truncate: { description: "\u5207\u308A\u8A70\u3081\u3066\u7701\u7565\u8A18\u53F7 (length[,suffix]\u3002suffix \u306E\u65E2\u5B9A\u306F \u2026 \u2014 U+2026 \u306E 1 \u6587\u5B57)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "string"] },
|
|
2123
|
+
join: { description: "\u914D\u5217\u3092\u9023\u7D50 ([separator]\u3002\u65E2\u5B9A\u306F\u30AB\u30F3\u30DE + \u7A7A\u767D)", hasArgs: true, resultType: "string", acceptTypes: ["array"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
1275
2124
|
// 数値パース・丸め
|
|
1276
2125
|
int: { description: "\u6574\u6570\u306B\u30D1\u30FC\u30B9", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
|
|
1277
2126
|
float: { description: "\u6D6E\u52D5\u5C0F\u6570\u70B9\u6570\u306B\u30D1\u30FC\u30B9", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
|
|
@@ -1283,117 +2132,70 @@ var builtinFilterMeta = {
|
|
|
1283
2132
|
// それらは既に string を返すため(builtinFilters.ts の unit を参照)
|
|
1284
2133
|
unit: { description: "\u5358\u4F4D\uFF08\u63A5\u5C3E\u8F9E\uFF09\u3092\u4ED8\u52A0", hasArgs: true, resultType: "string", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["string"] },
|
|
1285
2134
|
// 日付・時刻
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
2135
|
+
// `locale` と同じくロケールを 1 つ受ける(`date(ja-JP)`)。実装は最初からこれを読んでいたが、
|
|
2136
|
+
// メタデータ側が maxArgs: 0 だったため lint と補完が正しい書き方を誤りとして報告していた
|
|
2137
|
+
date: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u4ED8 ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
2138
|
+
time: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u6642\u523B ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
2139
|
+
datetime: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u306E\u65E5\u6642 ([locale]\u3002\u65E2\u5B9A\u306F config.locale)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
2140
|
+
ymd: { description: "YYYY-MM-DD \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F -)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
2141
|
+
hms: { description: "HH:MM:SS \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F :)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
1291
2142
|
// 真偽値・変換
|
|
1292
2143
|
falsy: { description: "\u507D\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
1293
2144
|
truthy: { description: "\u771F\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
1294
2145
|
defaults: { description: "\u507D\u5024\u306E\u5834\u5408\u30C7\u30D5\u30A9\u30EB\u30C8\u5024", hasArgs: true, resultType: "passthrough", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
|
|
1295
2146
|
coalesce: { description: "null / undefined \u306E\u5834\u5408\u30C7\u30D5\u30A9\u30EB\u30C8\u5024", hasArgs: true, resultType: "passthrough", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
|
|
1296
|
-
boolean: { description: "\u30D6\u30FC\u30EB\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
1297
|
-
number: { description: "\u6570\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "number", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
1298
|
-
string: { description: "\u6587\u5B57\u5217\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
1299
|
-
nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 }
|
|
1300
|
-
};
|
|
1301
|
-
var
|
|
1302
|
-
"if",
|
|
1303
|
-
"elseif",
|
|
1304
|
-
"else",
|
|
1305
|
-
"for"
|
|
1306
|
-
]);
|
|
1307
|
-
var
|
|
1308
|
-
var
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
var MODIFIER_SEPARATOR = "#";
|
|
1313
|
-
var FILTER_SEPARATOR = "|";
|
|
1314
|
-
var MODIFIER_PREVENT = "prevent";
|
|
1315
|
-
var MODIFIER_STOP = "stop";
|
|
1316
|
-
var MODIFIER_READONLY = "ro";
|
|
1317
|
-
var MODIFIER_FLAGS = Object.freeze([
|
|
1318
|
-
MODIFIER_PREVENT,
|
|
1319
|
-
MODIFIER_STOP,
|
|
1320
|
-
MODIFIER_READONLY
|
|
1321
|
-
]);
|
|
1322
|
-
var MODIFIER_KEY_INIT = "init";
|
|
1323
|
-
var MODIFIER_KEY_SYNC = "sync";
|
|
1324
|
-
var MODIFIER_KEYS = Object.freeze([
|
|
1325
|
-
MODIFIER_KEY_INIT,
|
|
1326
|
-
MODIFIER_KEY_SYNC
|
|
1327
|
-
]);
|
|
1328
|
-
var ELSE_KEYWORD = "else";
|
|
1329
|
-
var SPREAD_PROP = "...";
|
|
1330
|
-
var EVENT_PROP_PREFIX = "on";
|
|
1331
|
-
var EVENT_TOKEN_NAMESPACE = "eventToken";
|
|
1332
|
-
var COMMAND_NAMESPACE = "command";
|
|
1333
|
-
var CLASS_NAMESPACE = "class";
|
|
1334
|
-
var ATTR_NAMESPACE = "attr";
|
|
1335
|
-
var STYLE_NAMESPACE = "style";
|
|
1336
|
-
var INDEX_PARAM_PREFIX = "$";
|
|
1337
|
-
var tmpIndexByIndexName = {};
|
|
1338
|
-
for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
|
|
1339
|
-
tmpIndexByIndexName[`${INDEX_PARAM_PREFIX}${i + 1}`] = i;
|
|
1340
|
-
}
|
|
1341
|
-
Object.freeze(tmpIndexByIndexName);
|
|
1342
|
-
var STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
|
|
1343
|
-
var STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
|
|
1344
|
-
var STATE_UPDATED_CALLBACK_NAME = "$renderedCallback";
|
|
1345
|
-
var STATE_ERROR_CALLBACK_NAME = "$errorCallback";
|
|
1346
|
-
var WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
|
|
1347
|
-
var STATE_BINDABLES_NAME = "$bindables";
|
|
1348
|
-
var STATE_COMMANDS_NAME = "$commands";
|
|
1349
|
-
var STATE_COMMAND_TOKENS_NAME = "$commandTokens";
|
|
1350
|
-
var STATE_COMMAND_NAMESPACE_NAME = "$command";
|
|
1351
|
-
var STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
1352
|
-
var STATE_ON_NAME = "$on";
|
|
1353
|
-
var STATE_STREAMS_NAME = "$stream";
|
|
1354
|
-
var STATE_WATCH_NAME = "$watch";
|
|
1355
|
-
var STATE_SCAN_NAME = "$scan";
|
|
1356
|
-
var STATE_RECURSION_NAME = "$recursion";
|
|
1357
|
-
var STATE_LIST_KEYS_NAME = "$listKeys";
|
|
1358
|
-
var STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
1359
|
-
var STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
|
|
1360
|
-
var WCS_MANIFEST_VERSION = 1;
|
|
2147
|
+
boolean: { description: "\u30D6\u30FC\u30EB\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
2148
|
+
number: { description: "\u6570\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "number", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
2149
|
+
string: { description: "\u6587\u5B57\u5217\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
2150
|
+
nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 }
|
|
2151
|
+
};
|
|
2152
|
+
var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
|
|
2153
|
+
"if",
|
|
2154
|
+
"elseif",
|
|
2155
|
+
"else",
|
|
2156
|
+
"for"
|
|
2157
|
+
]);
|
|
2158
|
+
var WCS_MANIFEST_VERSION = 2;
|
|
2159
|
+
var STATE_API_ALIASES = Object.freeze({
|
|
2160
|
+
$trackDependency: "$dependOn",
|
|
2161
|
+
$untrackDependency: "$untracked"
|
|
2162
|
+
});
|
|
1361
2163
|
function getWcsManifest() {
|
|
1362
2164
|
return {
|
|
1363
2165
|
version: WCS_MANIFEST_VERSION,
|
|
1364
2166
|
syntax: {
|
|
1365
2167
|
bindAttribute: config.bindAttributeName,
|
|
1366
2168
|
tagName: config.tagNames.state,
|
|
1367
|
-
pathDelimiter:
|
|
1368
|
-
wildcard:
|
|
2169
|
+
pathDelimiter: DELIMITER2,
|
|
2170
|
+
wildcard: WILDCARD2,
|
|
1369
2171
|
delimiters: {
|
|
1370
|
-
binding:
|
|
1371
|
-
propValue:
|
|
1372
|
-
modifier:
|
|
1373
|
-
filter:
|
|
2172
|
+
binding: BINDING_SEPARATOR2,
|
|
2173
|
+
propValue: PROP_VALUE_SEPARATOR2,
|
|
2174
|
+
modifier: MODIFIER_SEPARATOR2,
|
|
2175
|
+
filter: FILTER_SEPARATOR2
|
|
1374
2176
|
},
|
|
1375
2177
|
// 正本 STRUCTURAL_BINDING_TYPE_SET から導出(手書きの二重定義を排除)。
|
|
1376
|
-
structuralDirectives: Array.from(
|
|
2178
|
+
structuralDirectives: Array.from(STRUCTURAL_BINDING_TYPE_SET2),
|
|
1377
2179
|
modifiers: {
|
|
1378
2180
|
flags: MODIFIER_FLAGS,
|
|
1379
2181
|
keyValue: MODIFIER_KEYS,
|
|
1380
|
-
eventNamePrefix:
|
|
2182
|
+
eventNamePrefix: EVENT_PROP_PREFIX2
|
|
1381
2183
|
},
|
|
1382
2184
|
indexParam: {
|
|
1383
|
-
prefix:
|
|
1384
|
-
maxDepth:
|
|
2185
|
+
prefix: INDEX_PARAM_PREFIX2,
|
|
2186
|
+
maxDepth: MAX_WILDCARD_DEPTH2
|
|
1385
2187
|
},
|
|
1386
2188
|
bindingTypes: {
|
|
1387
|
-
elseKeyword:
|
|
1388
|
-
spread:
|
|
1389
|
-
eventPropertyPrefix:
|
|
1390
|
-
explicitPropertyPrefix:
|
|
2189
|
+
elseKeyword: ELSE_KEYWORD2,
|
|
2190
|
+
spread: SPREAD_PROP2,
|
|
2191
|
+
eventPropertyPrefix: EVENT_PROP_PREFIX2,
|
|
2192
|
+
explicitPropertyPrefix: DELIMITER2,
|
|
1391
2193
|
propNamespaces: {
|
|
1392
|
-
eventToken:
|
|
1393
|
-
command:
|
|
1394
|
-
class:
|
|
1395
|
-
attr:
|
|
1396
|
-
style:
|
|
2194
|
+
eventToken: EVENT_TOKEN_NAMESPACE2,
|
|
2195
|
+
command: COMMAND_NAMESPACE2,
|
|
2196
|
+
class: CLASS_NAMESPACE2,
|
|
2197
|
+
attr: ATTR_NAMESPACE2,
|
|
2198
|
+
style: STYLE_NAMESPACE2
|
|
1397
2199
|
}
|
|
1398
2200
|
}
|
|
1399
2201
|
},
|
|
@@ -1401,10 +2203,12 @@ function getWcsManifest() {
|
|
|
1401
2203
|
filters: Object.keys(outputBuiltinFilters),
|
|
1402
2204
|
filterMeta: builtinFilterMeta,
|
|
1403
2205
|
filterAliases: builtinFilterAliases,
|
|
2206
|
+
declarationAliases: DECLARATION_ALIASES,
|
|
2207
|
+
apiAliases: STATE_API_ALIASES,
|
|
1404
2208
|
reservedLifecycle: [
|
|
1405
2209
|
STATE_CONNECTED_CALLBACK_NAME,
|
|
1406
2210
|
STATE_DISCONNECTED_CALLBACK_NAME,
|
|
1407
|
-
|
|
2211
|
+
STATE_RENDERED_CALLBACK_NAME,
|
|
1408
2212
|
STATE_ERROR_CALLBACK_NAME,
|
|
1409
2213
|
WEBCOMPONENT_STATE_READY_CALLBACK_NAME
|
|
1410
2214
|
],
|
|
@@ -1415,7 +2219,7 @@ function getWcsManifest() {
|
|
|
1415
2219
|
STATE_COMMAND_NAMESPACE_NAME,
|
|
1416
2220
|
STATE_EVENT_TOKENS_NAME,
|
|
1417
2221
|
STATE_ON_NAME,
|
|
1418
|
-
|
|
2222
|
+
STATE_STREAM_NAME,
|
|
1419
2223
|
STATE_WATCH_NAME,
|
|
1420
2224
|
STATE_SCAN_NAME,
|
|
1421
2225
|
STATE_LIST_KEYS_NAME,
|
|
@@ -1439,16 +2243,16 @@ var STRUCTURAL_DIRECTIVE_INFO = {
|
|
|
1439
2243
|
elseif: { description: "else-if \u6761\u4EF6 (<template>)", insertColon: true },
|
|
1440
2244
|
else: { description: "else \u30D6\u30ED\u30C3\u30AF (<template>)", insertColon: false }
|
|
1441
2245
|
};
|
|
1442
|
-
var STRUCTURAL_DIRECTIVES = [...
|
|
2246
|
+
var STRUCTURAL_DIRECTIVES = [...STRUCTURAL_BINDING_TYPE_SET2].map((name) => ({
|
|
1443
2247
|
name,
|
|
1444
2248
|
...STRUCTURAL_DIRECTIVE_INFO[name]
|
|
1445
2249
|
}));
|
|
1446
2250
|
|
|
1447
2251
|
// src/service/recursionPaths.ts
|
|
1448
|
-
var
|
|
2252
|
+
var RECURSION_WILDCARD2 = "**";
|
|
1449
2253
|
var RECURSION_KEY = "$recursion";
|
|
1450
2254
|
function hasRecursionWildcard(path) {
|
|
1451
|
-
return path.indexOf(
|
|
2255
|
+
return path.indexOf(RECURSION_WILDCARD2) !== -1;
|
|
1452
2256
|
}
|
|
1453
2257
|
function checkNodePath(path) {
|
|
1454
2258
|
if (typeof path !== "string" || path.length === 0) return "empty";
|
|
@@ -1459,7 +2263,7 @@ function checkNodePath(path) {
|
|
|
1459
2263
|
if (path.indexOf("#") !== -1) return "reservedMount";
|
|
1460
2264
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
1461
2265
|
if (segments[i] === "*") return "midWildcard";
|
|
1462
|
-
if (segments[i] ===
|
|
2266
|
+
if (segments[i] === RECURSION_WILDCARD2) return "nestedRecursion";
|
|
1463
2267
|
if (!isNaN(Number(segments[i]))) return "indexSegment";
|
|
1464
2268
|
}
|
|
1465
2269
|
return null;
|
|
@@ -1468,7 +2272,7 @@ function makeRecursionSpec(anchor, repeat) {
|
|
|
1468
2272
|
return Object.freeze({
|
|
1469
2273
|
anchor,
|
|
1470
2274
|
repeat,
|
|
1471
|
-
recursiveAnchor: anchor.slice(0, anchor.lastIndexOf(".")) + "." +
|
|
2275
|
+
recursiveAnchor: anchor.slice(0, anchor.lastIndexOf(".")) + "." + RECURSION_WILDCARD2,
|
|
1472
2276
|
anchorList: anchor.slice(0, anchor.lastIndexOf(".")),
|
|
1473
2277
|
repeatList: repeat.slice(0, repeat.lastIndexOf("."))
|
|
1474
2278
|
});
|
|
@@ -1544,8 +2348,8 @@ function collectRecursionSpecs(candidates) {
|
|
|
1544
2348
|
const out = [];
|
|
1545
2349
|
for (const candidate of candidates) {
|
|
1546
2350
|
if (candidate.kind !== "recursionAnchor" || typeof candidate.repeat !== "string") continue;
|
|
1547
|
-
if (!candidate.path.endsWith("." +
|
|
1548
|
-
const anchor = candidate.path.slice(0, candidate.path.length -
|
|
2351
|
+
if (!candidate.path.endsWith("." + RECURSION_WILDCARD2)) continue;
|
|
2352
|
+
const anchor = candidate.path.slice(0, candidate.path.length - RECURSION_WILDCARD2.length) + "*";
|
|
1549
2353
|
if (out.some((spec) => spec.anchor === anchor && spec.repeat === candidate.repeat)) continue;
|
|
1550
2354
|
out.push(makeRecursionSpec(anchor, candidate.repeat));
|
|
1551
2355
|
}
|
|
@@ -1725,6 +2529,26 @@ function analyzeWatchEntries(scriptContent) {
|
|
|
1725
2529
|
definitelyNotFunction: entry.kind === "data" && isNonFunctionLiteral(entry.value)
|
|
1726
2530
|
}));
|
|
1727
2531
|
}
|
|
2532
|
+
function analyzeWatchHandlerSources(scriptContent) {
|
|
2533
|
+
const root = locateDefaultExportObject(scriptContent);
|
|
2534
|
+
if (!root) return [];
|
|
2535
|
+
const prop = parseTopLevelProperties(root.content).find((p) => p.name === RESERVED_WATCH_KEY);
|
|
2536
|
+
if (!prop || prop.kind !== "data" || !prop.value || !isObjectLiteral(prop.value) || prop.valueStart === void 0) {
|
|
2537
|
+
return [];
|
|
2538
|
+
}
|
|
2539
|
+
const leading = prop.value.length - prop.value.trimStart().length;
|
|
2540
|
+
const innerStart = root.start + prop.valueStart + leading + 1;
|
|
2541
|
+
const out = [];
|
|
2542
|
+
for (const entry of parseTopLevelProperties(extractObjectContent(prop.value))) {
|
|
2543
|
+
if (entry.value === void 0 || entry.valueStart === void 0) continue;
|
|
2544
|
+
out.push({
|
|
2545
|
+
kind: entry.kind === "data" ? "value" : "body",
|
|
2546
|
+
text: entry.value,
|
|
2547
|
+
start: innerStart + entry.valueStart
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
return out;
|
|
2551
|
+
}
|
|
1728
2552
|
function analyzeListKeyEntries(scriptContent) {
|
|
1729
2553
|
return analyzeObjectEntries(scriptContent, RESERVED_LIST_KEYS_KEY).map((entry) => ({ key: entry.key, start: entry.start, end: entry.end }));
|
|
1730
2554
|
}
|
|
@@ -2591,271 +3415,42 @@ function pushSchemaValuePaths(path, node, paths, defs, depth) {
|
|
|
2591
3415
|
const typeHint = schemaTypeHint(nodes);
|
|
2592
3416
|
paths.push(withHint({ path, kind: "data", fromSchema: true }, typeHint));
|
|
2593
3417
|
const items = nodes.map((n) => n.items).find((i) => i !== void 0 && i !== null && typeof i === "object");
|
|
2594
|
-
const isArray2 = items !== void 0 || (typeHint?.split("|").includes("array") ?? false);
|
|
2595
|
-
if (isArray2) {
|
|
2596
|
-
const itemNodes = items !== void 0 ? derefSchemaNodes(items, defs) : [];
|
|
2597
|
-
paths.push(withHint({ path: `${path}.*`, kind: "list", fromSchema: true }, schemaTypeHint(itemNodes)));
|
|
2598
|
-
paths.push({ path: `${path}.length`, kind: "data", typeHint: "number", fromSchema: true });
|
|
2599
|
-
if (depth >= MAX_OBJECT_NEST_DEPTH) return;
|
|
2600
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2601
|
-
for (const n of itemNodes) {
|
|
2602
|
-
for (const [childKey, childNode] of Object.entries(n.properties ?? {})) {
|
|
2603
|
-
if (seen.has(childKey)) continue;
|
|
2604
|
-
seen.add(childKey);
|
|
2605
|
-
pushSchemaValuePaths(`${path}.*.${childKey}`, childNode, paths, defs, depth + 1);
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
return;
|
|
2609
|
-
}
|
|
2610
|
-
if (nodes.some((n) => n.properties !== void 0)) {
|
|
2611
|
-
collectSchemaObjectPaths(node, path, paths, defs, depth + 1);
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
function withHint(candidate, typeHint) {
|
|
2615
|
-
return typeHint === void 0 ? candidate : { ...candidate, typeHint };
|
|
2616
|
-
}
|
|
2617
|
-
|
|
2618
|
-
// src/language/htmlParse.ts
|
|
2619
|
-
function parseWcsScriptBlocks(html, stateTagName = "wcs-state") {
|
|
2620
|
-
const blocks = [];
|
|
2621
|
-
let pos = 0;
|
|
2622
|
-
const len = html.length;
|
|
2623
|
-
while (pos < len) {
|
|
2624
|
-
if (html.startsWith("<!--", pos)) {
|
|
2625
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
2626
|
-
if (commentEnd === -1) break;
|
|
2627
|
-
pos = commentEnd + 3;
|
|
2628
|
-
continue;
|
|
2629
|
-
}
|
|
2630
|
-
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
2631
|
-
if (wcsMatch === null) {
|
|
2632
|
-
pos++;
|
|
2633
|
-
continue;
|
|
2634
|
-
}
|
|
2635
|
-
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
2636
|
-
pos = wcsMatch.end;
|
|
2637
|
-
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
2638
|
-
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
2639
|
-
while (pos < wcsEnd) {
|
|
2640
|
-
if (html.startsWith("<!--", pos)) {
|
|
2641
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
2642
|
-
if (commentEnd === -1) break;
|
|
2643
|
-
pos = commentEnd + 3;
|
|
2644
|
-
continue;
|
|
2645
|
-
}
|
|
2646
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
2647
|
-
if (scriptMatch === null) {
|
|
2648
|
-
pos++;
|
|
2649
|
-
continue;
|
|
2650
|
-
}
|
|
2651
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
2652
|
-
if (typeAttr?.toLowerCase() !== "module") {
|
|
2653
|
-
pos = scriptMatch.end;
|
|
2654
|
-
continue;
|
|
2655
|
-
}
|
|
2656
|
-
const contentStart = scriptMatch.end;
|
|
2657
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
2658
|
-
if (scriptCloseIdx === -1) {
|
|
2659
|
-
pos = contentStart;
|
|
2660
|
-
break;
|
|
2661
|
-
}
|
|
2662
|
-
const contentEnd = scriptCloseIdx;
|
|
2663
|
-
blocks.push({
|
|
2664
|
-
contentStart,
|
|
2665
|
-
contentEnd,
|
|
2666
|
-
content: html.slice(contentStart, contentEnd),
|
|
2667
|
-
mountPath
|
|
2668
|
-
});
|
|
2669
|
-
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
2670
|
-
if (pos === 0) break;
|
|
2671
|
-
}
|
|
2672
|
-
pos = wcsEnd;
|
|
2673
|
-
if (wcsCloseIdx !== -1) {
|
|
2674
|
-
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
2675
|
-
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
return blocks;
|
|
2679
|
-
}
|
|
2680
|
-
function parseWcsStateElements(html, stateTagName = "wcs-state") {
|
|
2681
|
-
const elements = [];
|
|
2682
|
-
let pos = 0;
|
|
2683
|
-
const len = html.length;
|
|
2684
|
-
while (pos < len) {
|
|
2685
|
-
if (html.startsWith("<!--", pos)) {
|
|
2686
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
2687
|
-
if (commentEnd === -1) break;
|
|
2688
|
-
pos = commentEnd + 3;
|
|
2689
|
-
continue;
|
|
2690
|
-
}
|
|
2691
|
-
const wcsMatch = matchOpenTag(html, pos, stateTagName);
|
|
2692
|
-
if (wcsMatch === null) {
|
|
2693
|
-
pos++;
|
|
2694
|
-
continue;
|
|
2695
|
-
}
|
|
2696
|
-
const mountPath = extractAttribute(wcsMatch.tagContent, "mount");
|
|
2697
|
-
const bindComponent = parseAttributeNames(wcsMatch.tagContent).has("bind-component");
|
|
2698
|
-
const jsonAttr = extractAttribute(wcsMatch.tagContent, "json") ?? void 0;
|
|
2699
|
-
const stateAttr = extractAttribute(wcsMatch.tagContent, "state") ?? void 0;
|
|
2700
|
-
const srcAttr = extractAttribute(wcsMatch.tagContent, "src") ?? void 0;
|
|
2701
|
-
const tagStart = pos;
|
|
2702
|
-
const tagEnd = wcsMatch.end;
|
|
2703
|
-
pos = wcsMatch.end;
|
|
2704
|
-
const scriptBlocks = [];
|
|
2705
|
-
const wcsCloseIdx = findCloseTag(html, pos, stateTagName);
|
|
2706
|
-
const wcsEnd = wcsCloseIdx === -1 ? len : wcsCloseIdx;
|
|
2707
|
-
while (pos < wcsEnd) {
|
|
2708
|
-
if (html.startsWith("<!--", pos)) {
|
|
2709
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
2710
|
-
if (commentEnd === -1) break;
|
|
2711
|
-
pos = commentEnd + 3;
|
|
2712
|
-
continue;
|
|
2713
|
-
}
|
|
2714
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
2715
|
-
if (scriptMatch === null) {
|
|
2716
|
-
pos++;
|
|
2717
|
-
continue;
|
|
2718
|
-
}
|
|
2719
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
2720
|
-
if (typeAttr?.toLowerCase() !== "module") {
|
|
2721
|
-
pos = scriptMatch.end;
|
|
2722
|
-
continue;
|
|
2723
|
-
}
|
|
2724
|
-
const contentStart = scriptMatch.end;
|
|
2725
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
2726
|
-
if (scriptCloseIdx === -1) {
|
|
2727
|
-
pos = contentStart;
|
|
2728
|
-
break;
|
|
2729
|
-
}
|
|
2730
|
-
scriptBlocks.push({
|
|
2731
|
-
contentStart,
|
|
2732
|
-
contentEnd: scriptCloseIdx,
|
|
2733
|
-
content: html.slice(contentStart, scriptCloseIdx),
|
|
2734
|
-
mountPath
|
|
2735
|
-
});
|
|
2736
|
-
pos = html.indexOf(">", scriptCloseIdx) + 1;
|
|
2737
|
-
if (pos === 0) break;
|
|
2738
|
-
}
|
|
2739
|
-
elements.push({ mountPath, bindComponent, jsonAttr, stateAttr, srcAttr, scriptBlocks, tagStart, tagEnd });
|
|
2740
|
-
pos = wcsEnd;
|
|
2741
|
-
if (wcsCloseIdx !== -1) {
|
|
2742
|
-
const closeEnd = html.indexOf(">", wcsCloseIdx);
|
|
2743
|
-
if (closeEnd !== -1) pos = closeEnd + 1;
|
|
2744
|
-
}
|
|
2745
|
-
}
|
|
2746
|
-
return elements;
|
|
2747
|
-
}
|
|
2748
|
-
function findScriptJsonById(html, id2) {
|
|
2749
|
-
let pos = 0;
|
|
2750
|
-
const len = html.length;
|
|
2751
|
-
while (pos < len) {
|
|
2752
|
-
if (html.startsWith("<!--", pos)) {
|
|
2753
|
-
const commentEnd = html.indexOf("-->", pos + 4);
|
|
2754
|
-
if (commentEnd === -1) break;
|
|
2755
|
-
pos = commentEnd + 3;
|
|
2756
|
-
continue;
|
|
2757
|
-
}
|
|
2758
|
-
const scriptMatch = matchOpenTag(html, pos, "script");
|
|
2759
|
-
if (scriptMatch === null) {
|
|
2760
|
-
pos++;
|
|
2761
|
-
continue;
|
|
2762
|
-
}
|
|
2763
|
-
const typeAttr = extractAttribute(scriptMatch.tagContent, "type");
|
|
2764
|
-
const idAttr = extractAttribute(scriptMatch.tagContent, "id");
|
|
2765
|
-
if (typeAttr?.toLowerCase() === "application/json" && idAttr === id2) {
|
|
2766
|
-
const contentStart = scriptMatch.end;
|
|
2767
|
-
const scriptCloseIdx = findCloseTag(html, contentStart, "script");
|
|
2768
|
-
if (scriptCloseIdx === -1) return null;
|
|
2769
|
-
return html.slice(contentStart, scriptCloseIdx);
|
|
2770
|
-
}
|
|
2771
|
-
pos = scriptMatch.end;
|
|
2772
|
-
}
|
|
2773
|
-
return null;
|
|
2774
|
-
}
|
|
2775
|
-
function matchOpenTag(html, pos, tagName) {
|
|
2776
|
-
if (html[pos] !== "<") return null;
|
|
2777
|
-
const nameStart = pos + 1;
|
|
2778
|
-
const nameEnd = nameStart + tagName.length;
|
|
2779
|
-
if (nameEnd > html.length) return null;
|
|
2780
|
-
const slice2 = html.slice(nameStart, nameEnd);
|
|
2781
|
-
if (slice2.toLowerCase() !== tagName.toLowerCase()) return null;
|
|
2782
|
-
const charAfter = html[nameEnd];
|
|
2783
|
-
if (charAfter !== ">" && charAfter !== " " && charAfter !== " " && charAfter !== "\n" && charAfter !== "\r" && charAfter !== "/") {
|
|
2784
|
-
return null;
|
|
2785
|
-
}
|
|
2786
|
-
let i = nameEnd;
|
|
2787
|
-
let inSingleQuote = false;
|
|
2788
|
-
let inDoubleQuote = false;
|
|
2789
|
-
while (i < html.length) {
|
|
2790
|
-
const ch = html[i];
|
|
2791
|
-
if (inSingleQuote) {
|
|
2792
|
-
if (ch === "'") inSingleQuote = false;
|
|
2793
|
-
} else if (inDoubleQuote) {
|
|
2794
|
-
if (ch === '"') inDoubleQuote = false;
|
|
2795
|
-
} else if (ch === "'") {
|
|
2796
|
-
inSingleQuote = true;
|
|
2797
|
-
} else if (ch === '"') {
|
|
2798
|
-
inDoubleQuote = true;
|
|
2799
|
-
} else if (ch === ">") {
|
|
2800
|
-
return {
|
|
2801
|
-
start: pos,
|
|
2802
|
-
end: i + 1,
|
|
2803
|
-
tagContent: html.slice(nameEnd, i)
|
|
2804
|
-
};
|
|
2805
|
-
}
|
|
2806
|
-
i++;
|
|
2807
|
-
}
|
|
2808
|
-
return null;
|
|
2809
|
-
}
|
|
2810
|
-
function findCloseTag(html, startPos, tagName) {
|
|
2811
|
-
const pattern = "</" + tagName;
|
|
2812
|
-
const patternLower = pattern.toLowerCase();
|
|
2813
|
-
const htmlLower = html.toLowerCase();
|
|
2814
|
-
let pos = startPos;
|
|
2815
|
-
while (pos < html.length) {
|
|
2816
|
-
const idx = htmlLower.indexOf(patternLower, pos);
|
|
2817
|
-
if (idx === -1) return -1;
|
|
2818
|
-
const afterIdx = idx + pattern.length;
|
|
2819
|
-
if (afterIdx < html.length) {
|
|
2820
|
-
const ch = html[afterIdx];
|
|
2821
|
-
if (ch === ">" || ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
2822
|
-
return idx;
|
|
3418
|
+
const isArray2 = items !== void 0 || (typeHint?.split("|").includes("array") ?? false);
|
|
3419
|
+
if (isArray2) {
|
|
3420
|
+
const itemNodes = items !== void 0 ? derefSchemaNodes(items, defs) : [];
|
|
3421
|
+
paths.push(withHint({ path: `${path}.*`, kind: "list", fromSchema: true }, schemaTypeHint(itemNodes)));
|
|
3422
|
+
paths.push({ path: `${path}.length`, kind: "data", typeHint: "number", fromSchema: true });
|
|
3423
|
+
if (depth >= MAX_OBJECT_NEST_DEPTH) return;
|
|
3424
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3425
|
+
for (const n of itemNodes) {
|
|
3426
|
+
for (const [childKey, childNode] of Object.entries(n.properties ?? {})) {
|
|
3427
|
+
if (seen.has(childKey)) continue;
|
|
3428
|
+
seen.add(childKey);
|
|
3429
|
+
pushSchemaValuePaths(`${path}.*.${childKey}`, childNode, paths, defs, depth + 1);
|
|
2823
3430
|
}
|
|
2824
3431
|
}
|
|
2825
|
-
|
|
3432
|
+
return;
|
|
2826
3433
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
function parseAttributeNames(tagContent) {
|
|
2830
|
-
const names = /* @__PURE__ */ new Set();
|
|
2831
|
-
const attribute = /([^\s"'<>/=]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'<>`=]+))?/g;
|
|
2832
|
-
let match;
|
|
2833
|
-
while ((match = attribute.exec(tagContent)) !== null) {
|
|
2834
|
-
names.add(match[1].toLowerCase());
|
|
3434
|
+
if (nodes.some((n) => n.properties !== void 0)) {
|
|
3435
|
+
collectSchemaObjectPaths(node, path, paths, defs, depth + 1);
|
|
2835
3436
|
}
|
|
2836
|
-
return names;
|
|
2837
3437
|
}
|
|
2838
|
-
function
|
|
2839
|
-
|
|
2840
|
-
`(?:^|\\s)${attrName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|(\\S+))`,
|
|
2841
|
-
"i"
|
|
2842
|
-
);
|
|
2843
|
-
const match = tagContent.match(regex);
|
|
2844
|
-
if (!match) return null;
|
|
2845
|
-
return match[1] ?? match[2] ?? match[3] ?? null;
|
|
3438
|
+
function withHint(candidate, typeHint) {
|
|
3439
|
+
return typeHint === void 0 ? candidate : { ...candidate, typeHint };
|
|
2846
3440
|
}
|
|
2847
3441
|
|
|
2848
3442
|
// src/service/statePathResolver.ts
|
|
2849
3443
|
function getStatePathsFromHtml(html, stateTagName = "wcs-state", fileReader) {
|
|
2850
3444
|
const elements = parseWcsStateElements(html, stateTagName);
|
|
2851
3445
|
const allPaths = [];
|
|
3446
|
+
const mountPoints = /* @__PURE__ */ new Set();
|
|
2852
3447
|
for (const element of elements) {
|
|
2853
|
-
const paths = resolveElementPaths(element, html, fileReader);
|
|
3448
|
+
const paths = resolveElementPaths(element, html, mountPoints, fileReader);
|
|
2854
3449
|
allPaths.push(...paths);
|
|
2855
3450
|
}
|
|
2856
3451
|
return allPaths;
|
|
2857
3452
|
}
|
|
2858
|
-
function resolveElementPaths(element, html, fileReader) {
|
|
3453
|
+
function resolveElementPaths(element, html, mountPoints, fileReader) {
|
|
2859
3454
|
const raw = resolveElementPathsRaw(element, html, fileReader);
|
|
2860
3455
|
if (element.mountPath === null) return raw;
|
|
2861
3456
|
const prefix = element.mountPath + ".";
|
|
@@ -2865,6 +3460,15 @@ function resolveElementPaths(element, html, fileReader) {
|
|
|
2865
3460
|
if (p.kind === "method" || p.kind === "eventToken") continue;
|
|
2866
3461
|
out.push({ ...p, path: prefix + p.path });
|
|
2867
3462
|
}
|
|
3463
|
+
if (out.length > 0) {
|
|
3464
|
+
const segments = element.mountPath.split(".");
|
|
3465
|
+
for (let i = 1; i <= segments.length; i++) {
|
|
3466
|
+
const path = segments.slice(0, i).join(".");
|
|
3467
|
+
if (mountPoints.has(path)) continue;
|
|
3468
|
+
mountPoints.add(path);
|
|
3469
|
+
out.push({ path, kind: "data", typeHint: "object" });
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
2868
3472
|
return out;
|
|
2869
3473
|
}
|
|
2870
3474
|
function resolveElementPathsRaw(element, html, fileReader) {
|
|
@@ -2976,7 +3580,7 @@ function countWildcardSegments(path) {
|
|
|
2976
3580
|
}
|
|
2977
3581
|
function forPathOf(raw) {
|
|
2978
3582
|
let path = raw.trim();
|
|
2979
|
-
const pipe = path
|
|
3583
|
+
const pipe = indexOfOutsideQuotes2(path, "|");
|
|
2980
3584
|
if (pipe !== -1) path = path.slice(0, pipe).trim();
|
|
2981
3585
|
const at2 = path.indexOf("@");
|
|
2982
3586
|
if (at2 !== -1) path = path.slice(0, at2).trim();
|
|
@@ -3107,7 +3711,10 @@ var ja = {
|
|
|
3107
3711
|
arrayIndexAssign: (sp) => `\u914D\u5217\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3078\u306E\u76F4\u63A5\u4EE3\u5165\u306F\u30EA\u30A2\u30AF\u30C6\u30A3\u30D6\u66F4\u65B0\u3092\u30C8\u30EA\u30AC\u30FC\u3057\u307E\u305B\u3093\u3002this["${sp}"] \u306E\u3088\u3046\u306A\u30C9\u30C3\u30C8\u30D1\u30B9\u4EE3\u5165\u3001\u307E\u305F\u306F with() \u3068\u518D\u4EE3\u5165\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
|
|
3108
3712
|
tagMemberUnknown: (prop, tag) => `"${prop}" \u306F <${tag}> \u306E wcBindable \u30E1\u30F3\u30D0\u30FC\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u672A\u77E5\u30E1\u30F3\u30D0\u30FC\u3078\u306E\u30D0\u30A4\u30F3\u30C9\u306F\u9ED9\u3063\u3066\u7121\u8996\u3055\u308C\u307E\u3059\uFF09`,
|
|
3109
3713
|
nameAlias: (written, canonical) => `"${written}" \u306F "${canonical}" \u306E\u65E7\u540D\u3067\u3059\u30023.x \u306E\u9593\u306F\u52D5\u304D\u307E\u3059\u304C 4.0 \u3067\u5916\u308C\u308B\u306E\u3067\u3001"${canonical}" \u3068\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.2\uFF09`,
|
|
3110
|
-
|
|
3714
|
+
declarationAliasRead: (alias, canonical) => `"${alias}" \u306E\u8AAD\u307F\u51FA\u3057\u306F 3.x \u3067\u3082\u52D5\u304D\u307E\u305B\u3093\u3002"${alias}" \u306F "${canonical}" \u306E\u65E7\u540D\u3067\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u307F\u8FBC\u307F\u6642\u306B "${canonical}" \u3078\u5199\u3057\u3001\u65E7\u540D\u306E\u81EA\u524D\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u524A\u9664\u3059\u308B\u305F\u3081\u3001this["${alias}"] \u306F undefined \u306B\u306A\u308A\u307E\u3059\u3002"${canonical}" \u3092\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.2\uFF09`,
|
|
3715
|
+
declarationAliasReadUncertain: (alias, canonical) => `"${alias}" \u306E\u8AAD\u307F\u51FA\u3057\u306F\u65E7\u540D\u306E\u307E\u307E\u306B\u3067\u304D\u307E\u305B\u3093\u3002"${alias}" \u306F "${canonical}" \u306E\u65E7\u540D\u3067\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u307F\u8FBC\u307F\u6642\u306B "${canonical}" \u3078\u5199\u3057\u307E\u3059\u3002\u65E7\u540D\u304C\u81EA\u524D\u30D7\u30ED\u30D1\u30C6\u30A3\u306A\u3089\u524A\u9664\u3055\u308C\u308B\u306E\u3067 this["${alias}"] \u306F undefined \u306B\u306A\u308A\u3001class \u306E\u30D7\u30ED\u30C8\u30BF\u30A4\u30D7\u306B\u7F6E\u3044\u305F\u30E1\u30BD\u30C3\u30C9 / \u30A2\u30AF\u30BB\u30B5\u306A\u3089\u4ECA\u306F\u8AAD\u3081\u307E\u3059\u304C 4.0 \u3067\u5916\u308C\u307E\u3059\u3002\u3069\u3061\u3089\u306E\u5F62\u3067\u3082 "${canonical}" \u3092\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.2\uFF09`,
|
|
3716
|
+
declarationAlias: (alias, canonical) => `\u3053\u306E state \u306F "${alias}" \u3068 "${canonical}" \u3092\u4E21\u65B9\u5BA3\u8A00\u3057\u3066\u3044\u307E\u3059\u3002"${alias}" \u306F "${canonical}" \u306E\u65E7\u540D\uFF083.x \u306E\u9593\u306F\u52D5\u304D\u307E\u3059\uFF09\u306A\u306E\u3067\u3001"${canonical}" \u3060\u3051\u3092\u6B8B\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u8AAD\u307F\u8FBC\u307F\u6642\u306B throw \u3057\u307E\u3059\uFF09`,
|
|
3717
|
+
onPrefixedMember: (member, tag, modifiers) => `"${member}" \u306F <${tag}> \u306E\u30E1\u30F3\u30D0\u30FC\u3067\u3059\u304C\u3001"on" \u3067\u59CB\u307E\u308B\u540D\u524D\u306F\u30A4\u30D9\u30F3\u30C8\u675F\u7E1B\u306B\u306A\u308A\uFF08"${member.slice(2)}" \u30A4\u30D9\u30F3\u30C8\u3092\u5F85\u3064\uFF09\u3001\u5024\u306F\u5C4A\u304D\u307E\u305B\u3093\u3002\u30D7\u30ED\u30D1\u30C6\u30A3\u3068\u3057\u3066\u675F\u7E1B\u3059\u308B\u306B\u306F ".${member}${modifiers ? `#${modifiers}` : ""}:" \u3068\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\uFF08@wcstack/state 3.1\uFF09`,
|
|
3111
3718
|
tagCommandUnknown: (name, tag, declared) => `"${name}" \u306F <${tag}> \u306E command \u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
|
|
3112
3719
|
spreadNoBindable: (tag) => `'...'\uFF08spread\uFF09\u306F <${tag}> \u306B\u6709\u52B9\u306A wcBindable \u5BA3\u8A00\u304C\u5FC5\u8981\u3067\u3059 \u2014 \u3053\u306E\u30BF\u30B0\u306F\u5BA3\u8A00\u3092\u6301\u305F\u306A\u3044\u305F\u3081\u3001\u30E9\u30F3\u30BF\u30A4\u30E0\u306F\u30A8\u30E9\u30FC\u3092\u9001\u51FA\u3057\u307E\u3059`,
|
|
3113
3720
|
tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken \u306E\u30AD\u30FC "${name}" \u306F <${tag}> \u306E wcBindable \u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u751F DOM \u30A4\u30D9\u30F3\u30C8\u540D\u306F\u767A\u706B\u3057\u307E\u305B\u3093 \u2014 \u30D7\u30ED\u30D1\u30C6\u30A3\u540D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u5BA3\u8A00\u6E08\u307F: ${declared}\uFF09`,
|
|
@@ -3145,6 +3752,8 @@ var ja = {
|
|
|
3145
3752
|
return `this["${p}"] \u3078\u306E\u4EE3\u5165\u306B "**" \u306F\u4F7F\u3048\u307E\u305B\u3093\uFF08\u518D\u5E30 setter \u306F\u521D\u7248\u3067\u306F\u6301\u3066\u305A\u3001\u4EE3\u5165\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\u306B\u3057\u304B\u6210\u7ACB\u3057\u307E\u305B\u3093\uFF09\u3002$setAll("${p}", [], value) \u3067\u5168\u6DF1\u3055\u3078\u30D6\u30ED\u30FC\u30C9\u30AD\u30E3\u30B9\u30C8\u3059\u308B\u304B\u3001\u5177\u4F53\u30D1\u30B9\u3078\u66F8\u3044\u3066\u304F\u3060\u3055\u3044`;
|
|
3146
3753
|
case "postUpdate":
|
|
3147
3754
|
return `$postUpdate("${p}") \u306B "**" \u306F\u6E21\u305B\u307E\u305B\u3093\u3002\u901A\u77E5\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\uFF08\u56FA\u5B9A\u672C\u6570\u306E "*"\uFF09\u306B\u5BFE\u3057\u3066\u306E\u307F\u6210\u7ACB\u3057\u307E\u3059`;
|
|
3755
|
+
case "dependOn":
|
|
3756
|
+
return `$dependOn("${p}") \u306B "**" \u306F\u6E21\u305B\u307E\u305B\u3093\u3002\u4F9D\u5B58\u306E\u767B\u9332\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\uFF08\u56FA\u5B9A\u672C\u6570\u306E "*"\uFF09\u306B\u5BFE\u3057\u3066\u306E\u307F\u6210\u7ACB\u3057\u307E\u3059`;
|
|
3148
3757
|
case "trackDependency":
|
|
3149
3758
|
return `$trackDependency("${p}") \u306B "**" \u306F\u6E21\u305B\u307E\u305B\u3093\u3002\u4F9D\u5B58\u306E\u767B\u9332\u306F\u5C55\u958B\u5F8C\u306E\u5177\u4F53\u30D1\u30B9\uFF08\u56FA\u5B9A\u672C\u6570\u306E "*"\uFF09\u306B\u5BFE\u3057\u3066\u306E\u307F\u6210\u7ACB\u3057\u307E\u3059`;
|
|
3150
3759
|
case "listKeys":
|
|
@@ -3298,7 +3907,10 @@ var en = {
|
|
|
3298
3907
|
arrayIndexAssign: (sp) => `Assigning directly to an array index does not trigger a reactive update. Use a dot-path assignment like this["${sp}"], or with() plus reassignment.`,
|
|
3299
3908
|
tagMemberUnknown: (prop, tag) => `"${prop}" is not a wcBindable member of <${tag}> (bindings to unknown members are silently ignored)`,
|
|
3300
3909
|
nameAlias: (written, canonical) => `"${written}" is the old name of "${canonical}". It works through 3.x and goes in 4.0 \u2014 write "${canonical}" (@wcstack/state 3.2)`,
|
|
3301
|
-
|
|
3910
|
+
declarationAliasRead: (alias, canonical) => `Reading "${alias}" does not work, not even in 3.x. "${alias}" is the old name of "${canonical}": the runtime maps it to "${canonical}" at load time and deletes the old own property, so this["${alias}"] is undefined. Read "${canonical}" instead (@wcstack/state 3.2)`,
|
|
3911
|
+
declarationAliasReadUncertain: (alias, canonical) => `Reading "${alias}" cannot stay on the old name. "${alias}" is the old name of "${canonical}": the runtime maps it to "${canonical}" at load time. If the old name is an own property it is deleted, so this["${alias}"] is undefined; if it sits on a class prototype (a method or accessor) it still reads today but goes in 4.0. Either way, read "${canonical}" (@wcstack/state 3.2)`,
|
|
3912
|
+
declarationAlias: (alias, canonical) => `The state declares both "${alias}" and "${canonical}". "${alias}" is the old name of "${canonical}" (it works through 3.x) \u2014 keep "${canonical}" (the runtime throws at load time)`,
|
|
3913
|
+
onPrefixedMember: (member, tag, modifiers) => `"${member}" is a member of <${tag}>, but a name starting with "on" makes an event binding (it listens for a "${member.slice(2)}" event) and the value never arrives. Write ".${member}${modifiers ? `#${modifiers}` : ""}:" to bind the property (@wcstack/state 3.1)`,
|
|
3302
3914
|
tagCommandUnknown: (name, tag, declared) => `"${name}" is not a command of <${tag}> (declared: ${declared})`,
|
|
3303
3915
|
spreadNoBindable: (tag) => `'...' (spread) requires <${tag}> to expose a valid wcBindable declaration \u2014 this tag declares none, so the runtime raises an error`,
|
|
3304
3916
|
tagEventTokenKeyUnknown: (name, tag, declared) => `eventToken key "${name}" is not a wcBindable property of <${tag}>. Raw DOM event names never fire \u2014 use the property name (declared: ${declared})`,
|
|
@@ -3336,6 +3948,8 @@ var en = {
|
|
|
3336
3948
|
return `this["${p}"] = \u2026 cannot use "**" (there are no recursive setters in this version; an assignment only resolves against an expanded concrete path). Broadcast with $setAll("${p}", [], value), or write a concrete path`;
|
|
3337
3949
|
case "postUpdate":
|
|
3338
3950
|
return `$postUpdate("${p}") cannot take "**" \u2014 a notification is defined against a concrete path (a fixed number of "*")`;
|
|
3951
|
+
case "dependOn":
|
|
3952
|
+
return `$dependOn("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
3339
3953
|
case "trackDependency":
|
|
3340
3954
|
return `$trackDependency("${p}") cannot take "**" \u2014 a dependency is registered against a concrete path (a fixed number of "*")`;
|
|
3341
3955
|
case "listKeys":
|
|
@@ -3437,9 +4051,9 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
|
|
|
3437
4051
|
if (nonEmptyCount > 1) {
|
|
3438
4052
|
let scanPos = 0;
|
|
3439
4053
|
for (const b of bindings) {
|
|
3440
|
-
const colon = b
|
|
4054
|
+
const colon = indexOfOutsideQuotes2(b, ":");
|
|
3441
4055
|
const prop = (colon === -1 ? b : b.slice(0, colon)).trim();
|
|
3442
|
-
if (
|
|
4056
|
+
if (STRUCTURAL_BINDING_TYPE_SET2.has(prop)) {
|
|
3443
4057
|
const leading = b.length - b.trimStart().length;
|
|
3444
4058
|
diagnostics.push({
|
|
3445
4059
|
code: WcsDiagnosticCode.TemplateSyntax,
|
|
@@ -3648,778 +4262,345 @@ function validateBindings(html, attrName, stateTagName = "wcs-state", locale2, f
|
|
|
3648
4262
|
const pathTrimmed = parsed.path.trim();
|
|
3649
4263
|
if (pathTrimmed && !pathTrimmed.startsWith(".") && !isLiteral(pathTrimmed)) {
|
|
3650
4264
|
const chainDiags = validateFilterChainTypes(
|
|
3651
|
-
pathTrimmed,
|
|
3652
|
-
parsed.filters,
|
|
3653
|
-
scopedPaths,
|
|
3654
|
-
bindingStart,
|
|
3655
|
-
msgs
|
|
3656
|
-
);
|
|
3657
|
-
diagnostics.push(...chainDiags);
|
|
3658
|
-
}
|
|
3659
|
-
}
|
|
3660
|
-
}
|
|
3661
|
-
for (const filter of parsed.inputFilters) {
|
|
3662
|
-
diagnostics.push(...validateFilterUsage(filter, bindingStart, msgs));
|
|
3663
|
-
}
|
|
3664
|
-
if (parsed.path && scopedPaths.length > 0) {
|
|
3665
|
-
const pathTrimmed = parsed.path.trim();
|
|
3666
|
-
if (pathTrimmed && !pathTrimmed.startsWith(".") && !isLiteral(pathTrimmed)) {
|
|
3667
|
-
const resultType = resolveResultType(pathTrimmed, parsed.filters, scopedPaths);
|
|
3668
|
-
if (resultType !== null) {
|
|
3669
|
-
const typeReq = getExpectedType(
|
|
3670
|
-
parsed.property,
|
|
3671
|
-
() => isNegatedByElseChain(getStructuralTemplates(), attr.valueStart)
|
|
3672
|
-
);
|
|
3673
|
-
if (typeReq && resultType !== typeReq.expected) {
|
|
3674
|
-
const pathOffset = binding.indexOf(parsed.path);
|
|
3675
|
-
const pathStart = bindingStart + pathOffset;
|
|
3676
|
-
const schemaDefinite = typeReq.expected === "array" && parsed.filters.length === 0 && applicationSchema !== void 0 && scopedPaths.some((p) => p.path === pathTrimmed && p.fromSchema === true);
|
|
3677
|
-
diagnostics.push({
|
|
3678
|
-
code: schemaDefinite ? WcsDiagnosticCode.PathTypeMismatch : WcsDiagnosticCode.BindingTypeExpectation,
|
|
3679
|
-
start: pathStart,
|
|
3680
|
-
end: pathStart + pathTrimmed.length,
|
|
3681
|
-
message: schemaDefinite ? msgs.pathTypeMismatch(pathTrimmed, typeReq.label, typeReq.expected, resultType) : msgs.typeExpectation(typeReq.label, typeReq.expected, resultType),
|
|
3682
|
-
severity: schemaDefinite ? "error" : typeReq.severity
|
|
3683
|
-
});
|
|
3684
|
-
}
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
}
|
|
3688
|
-
pos += binding.length + 1;
|
|
3689
|
-
}
|
|
3690
|
-
}
|
|
3691
|
-
return diagnostics;
|
|
3692
|
-
}
|
|
3693
|
-
function findAllBindAttributes(html, attrName) {
|
|
3694
|
-
const attrs = [];
|
|
3695
|
-
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3696
|
-
const regex = new RegExp(`${escaped}\\s*=\\s*(["'])`, "gi");
|
|
3697
|
-
let match;
|
|
3698
|
-
while ((match = regex.exec(html)) !== null) {
|
|
3699
|
-
const quote = match[1];
|
|
3700
|
-
const valueStart = match.index + match[0].length;
|
|
3701
|
-
const valueEnd = html.indexOf(quote, valueStart);
|
|
3702
|
-
if (valueEnd === -1) continue;
|
|
3703
|
-
attrs.push({
|
|
3704
|
-
value: html.slice(valueStart, valueEnd),
|
|
3705
|
-
valueStart
|
|
3706
|
-
});
|
|
3707
|
-
}
|
|
3708
|
-
return attrs;
|
|
3709
|
-
}
|
|
3710
|
-
function splitBindingExpressions(value) {
|
|
3711
|
-
const result = [];
|
|
3712
|
-
let current2 = "";
|
|
3713
|
-
let parenDepth = 0;
|
|
3714
|
-
for (const ch of value) {
|
|
3715
|
-
if (ch === "(") parenDepth++;
|
|
3716
|
-
else if (ch === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
3717
|
-
else if (ch === ";" && parenDepth === 0) {
|
|
3718
|
-
result.push(current2);
|
|
3719
|
-
current2 = "";
|
|
3720
|
-
continue;
|
|
3721
|
-
}
|
|
3722
|
-
current2 += ch;
|
|
3723
|
-
}
|
|
3724
|
-
result.push(current2);
|
|
3725
|
-
return result;
|
|
3726
|
-
}
|
|
3727
|
-
function parseBindingExpression(expr) {
|
|
3728
|
-
const colonIndex = expr.indexOf(":");
|
|
3729
|
-
if (colonIndex === -1) {
|
|
3730
|
-
return { property: expr.trim(), path: null, filters: [], inputFilters: [] };
|
|
3731
|
-
}
|
|
3732
|
-
const rawProp = expr.slice(0, colonIndex);
|
|
3733
|
-
const propSegments = splitByPipe(rawProp);
|
|
3734
|
-
const property = propSegments[0].trim();
|
|
3735
|
-
const inputFilters = parseFilterSegments(expr, propSegments.slice(1), propSegments[0].length + 1);
|
|
3736
|
-
const afterColon = expr.slice(colonIndex + 1);
|
|
3737
|
-
const segments = splitByPipe(afterColon);
|
|
3738
|
-
const pathSegment = segments[0] || "";
|
|
3739
|
-
const filterSegments = segments.slice(1);
|
|
3740
|
-
const atIndex = pathSegment.indexOf("@");
|
|
3741
|
-
const path = atIndex !== -1 ? pathSegment.slice(0, atIndex) : pathSegment;
|
|
3742
|
-
const filters = parseFilterSegments(expr, filterSegments, colonIndex + 1 + pathSegment.length + 1);
|
|
3743
|
-
return { property, path: path.trim() || null, filters, inputFilters };
|
|
3744
|
-
}
|
|
3745
|
-
function parseFilterSegments(expr, segments, searchStart) {
|
|
3746
|
-
const filters = [];
|
|
3747
|
-
let filterSearchStart = searchStart;
|
|
3748
|
-
for (const seg of segments) {
|
|
3749
|
-
const trimmed = seg.trim();
|
|
3750
|
-
const filterMatch = trimmed.match(/^(\w+)(?:\(([^)]*)\))?/);
|
|
3751
|
-
if (filterMatch) {
|
|
3752
|
-
const nameOffset = expr.indexOf(trimmed, filterSearchStart);
|
|
3753
|
-
const args = filterMatch[2] !== void 0 ? filterMatch[2].split(",").map((a) => a.trim()).filter((a) => a !== "") : [];
|
|
3754
|
-
filters.push({
|
|
3755
|
-
name: filterMatch[1],
|
|
3756
|
-
offset: nameOffset >= 0 ? nameOffset : filterSearchStart,
|
|
3757
|
-
args,
|
|
3758
|
-
argsOffset: nameOffset >= 0 ? nameOffset + filterMatch[1].length : filterSearchStart
|
|
3759
|
-
});
|
|
3760
|
-
}
|
|
3761
|
-
filterSearchStart += seg.length + 1;
|
|
3762
|
-
}
|
|
3763
|
-
return filters;
|
|
3764
|
-
}
|
|
3765
|
-
function validateFilterUsage(filter, bindingStart, msgs) {
|
|
3766
|
-
const diagnostics = [];
|
|
3767
|
-
const canonical = canonicalFilterName(filter.name);
|
|
3768
|
-
const info = filterMap.get(canonical);
|
|
3769
|
-
if (info && canonical !== filter.name) {
|
|
3770
|
-
diagnostics.push({
|
|
3771
|
-
code: WcsDiagnosticCode.NameAlias,
|
|
3772
|
-
start: bindingStart + filter.offset,
|
|
3773
|
-
end: bindingStart + filter.offset + filter.name.length,
|
|
3774
|
-
message: msgs.nameAlias(filter.name, canonical),
|
|
3775
|
-
severity: "info"
|
|
3776
|
-
});
|
|
3777
|
-
}
|
|
3778
|
-
if (!info) {
|
|
3779
|
-
diagnostics.push({
|
|
3780
|
-
code: WcsDiagnosticCode.FilterUnknown,
|
|
3781
|
-
start: bindingStart + filter.offset,
|
|
3782
|
-
end: bindingStart + filter.offset + filter.name.length,
|
|
3783
|
-
message: msgs.filterUnknown(filter.name),
|
|
3784
|
-
severity: "warning"
|
|
3785
|
-
});
|
|
3786
|
-
return diagnostics;
|
|
3787
|
-
}
|
|
3788
|
-
const argCount = filter.args.length;
|
|
3789
|
-
if (argCount < info.minArgs) {
|
|
3790
|
-
diagnostics.push({
|
|
3791
|
-
code: WcsDiagnosticCode.FilterArity,
|
|
3792
|
-
start: bindingStart + filter.offset,
|
|
3793
|
-
end: bindingStart + filter.offset + filter.name.length,
|
|
3794
|
-
message: msgs.filterMinArgs(filter.name, info.minArgs, argCount),
|
|
3795
|
-
severity: "error"
|
|
3796
|
-
});
|
|
3797
|
-
} else if (argCount > info.maxArgs) {
|
|
3798
|
-
diagnostics.push({
|
|
3799
|
-
code: WcsDiagnosticCode.FilterArity,
|
|
3800
|
-
start: bindingStart + filter.offset,
|
|
3801
|
-
end: bindingStart + filter.offset + filter.name.length,
|
|
3802
|
-
message: msgs.filterMaxArgs(filter.name, info.maxArgs, argCount),
|
|
3803
|
-
severity: "error"
|
|
3804
|
-
});
|
|
3805
|
-
}
|
|
3806
|
-
if (info.argTypes && argCount > 0) {
|
|
3807
|
-
for (let i = 0; i < Math.min(argCount, info.argTypes.length); i++) {
|
|
3808
|
-
const expectedArgType = info.argTypes[i];
|
|
3809
|
-
if (expectedArgType === "any") continue;
|
|
3810
|
-
const actualArgType = inferArgType(filter.args[i]);
|
|
3811
|
-
if (actualArgType !== expectedArgType) {
|
|
3812
|
-
diagnostics.push({
|
|
3813
|
-
code: WcsDiagnosticCode.FilterArgType,
|
|
3814
|
-
start: bindingStart + filter.argsOffset,
|
|
3815
|
-
end: bindingStart + filter.argsOffset + filter.name.length,
|
|
3816
|
-
message: msgs.filterArgType(filter.name, i + 1, expectedArgType, filter.args[i], actualArgType),
|
|
3817
|
-
severity: "warning"
|
|
3818
|
-
});
|
|
3819
|
-
}
|
|
3820
|
-
}
|
|
3821
|
-
}
|
|
3822
|
-
return diagnostics;
|
|
3823
|
-
}
|
|
3824
|
-
function splitByPipe(value) {
|
|
3825
|
-
const result = [];
|
|
3826
|
-
let current2 = "";
|
|
3827
|
-
let parenDepth = 0;
|
|
3828
|
-
for (const ch of value) {
|
|
3829
|
-
if (ch === "(") parenDepth++;
|
|
3830
|
-
else if (ch === ")") parenDepth = Math.max(0, parenDepth - 1);
|
|
3831
|
-
else if (ch === "|" && parenDepth === 0) {
|
|
3832
|
-
result.push(current2);
|
|
3833
|
-
current2 = "";
|
|
3834
|
-
continue;
|
|
3835
|
-
}
|
|
3836
|
-
current2 += ch;
|
|
3837
|
-
}
|
|
3838
|
-
result.push(current2);
|
|
3839
|
-
return result;
|
|
3840
|
-
}
|
|
3841
|
-
function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs) {
|
|
3842
|
-
if (/^\$\d+$/.test(checkPath)) return null;
|
|
3843
|
-
if (checkPath.startsWith("$command.")) {
|
|
3844
|
-
if (commandNames.size > 0 && !commandNames.has(checkPath)) {
|
|
3845
|
-
return msgs.commandTokenUndeclared(displayPath);
|
|
3846
|
-
}
|
|
3847
|
-
return null;
|
|
3848
|
-
}
|
|
3849
|
-
if (checkPath.startsWith("$streamStatus.") || checkPath.startsWith("$streamError.")) {
|
|
3850
|
-
const prefix = checkPath.startsWith("$streamStatus.") ? "$streamStatus." : "$streamError.";
|
|
3851
|
-
const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
|
|
3852
|
-
if (hasNamespace && !scopedPathSet.has(checkPath)) {
|
|
3853
|
-
return msgs.streamPathMissing(displayPath);
|
|
3854
|
-
}
|
|
3855
|
-
return null;
|
|
3856
|
-
}
|
|
3857
|
-
if (!scopedPathSet.has(checkPath) && !matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) {
|
|
3858
|
-
return msgs.pathMissing(displayPath);
|
|
3859
|
-
}
|
|
3860
|
-
return null;
|
|
3861
|
-
}
|
|
3862
|
-
function matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet) {
|
|
3863
|
-
const specs = collectRecursionSpecs(scopedPaths);
|
|
3864
|
-
if (specs.length === 0) return false;
|
|
3865
|
-
return matchesRecursion(specs, checkPath, (candidate) => scopedPathSet.has(candidate));
|
|
3866
|
-
}
|
|
3867
|
-
function toMissingVerdict(message) {
|
|
3868
|
-
return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
|
|
3869
|
-
}
|
|
3870
|
-
function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, schema, msgs) {
|
|
3871
|
-
if (checkPath.startsWith("$")) {
|
|
3872
|
-
return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
3873
|
-
}
|
|
3874
|
-
if (scopedPathSet.has(checkPath)) return null;
|
|
3875
|
-
if (matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) return null;
|
|
3876
|
-
const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
|
|
3877
|
-
if (resolution.kind === "nonexistent") {
|
|
3878
|
-
return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
|
|
3879
|
-
}
|
|
3880
|
-
return null;
|
|
3881
|
-
}
|
|
3882
|
-
function collectStructuralTemplates(html, attrName) {
|
|
3883
|
-
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3884
|
-
const attrRegex = new RegExp(`${escaped}\\s*=\\s*(["'])`, "i");
|
|
3885
|
-
const tagRegex = /<template(?:\s[^>]*)?>|<\/template\s*>/gi;
|
|
3886
|
-
const templates = [];
|
|
3887
|
-
let depth = 0;
|
|
3888
|
-
let match;
|
|
3889
|
-
while ((match = tagRegex.exec(html)) !== null) {
|
|
3890
|
-
if (match[0].startsWith("</")) {
|
|
3891
|
-
depth = Math.max(0, depth - 1);
|
|
3892
|
-
continue;
|
|
3893
|
-
}
|
|
3894
|
-
const attrMatch = attrRegex.exec(match[0]);
|
|
3895
|
-
if (attrMatch) {
|
|
3896
|
-
const quote = attrMatch[1];
|
|
3897
|
-
const valueStart = match.index + attrMatch.index + attrMatch[0].length;
|
|
3898
|
-
const valueEnd = html.indexOf(quote, valueStart);
|
|
3899
|
-
if (valueEnd !== -1) {
|
|
3900
|
-
const first = splitBindingExpressions(html.slice(valueStart, valueEnd))[0] ?? "";
|
|
3901
|
-
const prop = first.split(":")[0].replace(/#.*$/, "").trim();
|
|
3902
|
-
const type = prop === "if" || prop === "elseif" || prop === "else" ? prop : "other";
|
|
3903
|
-
templates.push({ valueStart, depth, type });
|
|
4265
|
+
pathTrimmed,
|
|
4266
|
+
parsed.filters,
|
|
4267
|
+
scopedPaths,
|
|
4268
|
+
bindingStart,
|
|
4269
|
+
msgs
|
|
4270
|
+
);
|
|
4271
|
+
diagnostics.push(...chainDiags);
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
3904
4274
|
}
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
}
|
|
3908
|
-
return templates;
|
|
3909
|
-
}
|
|
3910
|
-
function isNegatedByElseChain(templates, valueStart) {
|
|
3911
|
-
const index = templates.findIndex((t) => t.valueStart === valueStart);
|
|
3912
|
-
if (index === -1) return false;
|
|
3913
|
-
const selfDepth = templates[index].depth;
|
|
3914
|
-
for (let i = index + 1; i < templates.length; i++) {
|
|
3915
|
-
const next = templates[i];
|
|
3916
|
-
if (next.depth > selfDepth) continue;
|
|
3917
|
-
if (next.depth < selfDepth) return false;
|
|
3918
|
-
if (next.type === "elseif" || next.type === "else") return true;
|
|
3919
|
-
if (next.type === "if") return false;
|
|
3920
|
-
}
|
|
3921
|
-
return false;
|
|
3922
|
-
}
|
|
3923
|
-
function getExpectedType(property, isNegatedIf) {
|
|
3924
|
-
const prop = property.replace(/#.*$/, "");
|
|
3925
|
-
if (prop === "for") {
|
|
3926
|
-
return { label: "for", expected: "array", severity: "error" };
|
|
3927
|
-
}
|
|
3928
|
-
if (prop === "if" || prop === "elseif") {
|
|
3929
|
-
if (!isNegatedIf()) return null;
|
|
3930
|
-
return { label: prop, expected: "boolean", severity: "warning" };
|
|
3931
|
-
}
|
|
3932
|
-
if (prop.startsWith("class.")) {
|
|
3933
|
-
return { label: prop, expected: "boolean", severity: "warning" };
|
|
3934
|
-
}
|
|
3935
|
-
if (prop.startsWith("attr.")) {
|
|
3936
|
-
return { label: prop, expected: "string", severity: "warning" };
|
|
3937
|
-
}
|
|
3938
|
-
if (prop.startsWith("style.")) {
|
|
3939
|
-
return { label: prop, expected: "string", severity: "warning" };
|
|
3940
|
-
}
|
|
3941
|
-
return null;
|
|
3942
|
-
}
|
|
3943
|
-
function validateFilterChainTypes(path, filters, statePaths, bindingStart, msgs) {
|
|
3944
|
-
const diagnostics = [];
|
|
3945
|
-
const pathInfo = statePaths.find((p) => p.path === path);
|
|
3946
|
-
if (!pathInfo?.typeHint) return diagnostics;
|
|
3947
|
-
let currentType = pathInfo.typeHint;
|
|
3948
|
-
for (const filter of filters) {
|
|
3949
|
-
const info = filterMap.get(filter.name);
|
|
3950
|
-
if (!info) break;
|
|
3951
|
-
if (info.acceptTypes !== "any") {
|
|
3952
|
-
const currentTypes = currentType.split("|");
|
|
3953
|
-
const hasMatch = currentTypes.some((t) => info.acceptTypes.includes(t));
|
|
3954
|
-
if (!hasMatch) {
|
|
3955
|
-
diagnostics.push({
|
|
3956
|
-
code: WcsDiagnosticCode.FilterInputType,
|
|
3957
|
-
start: bindingStart + filter.offset,
|
|
3958
|
-
end: bindingStart + filter.offset + filter.name.length,
|
|
3959
|
-
message: msgs.filterInputType(filter.name, info.acceptTypes.join("|"), currentType),
|
|
3960
|
-
severity: "warning"
|
|
3961
|
-
});
|
|
4275
|
+
for (const filter of parsed.inputFilters) {
|
|
4276
|
+
diagnostics.push(...validateFilterUsage(filter, bindingStart, msgs));
|
|
3962
4277
|
}
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
4278
|
+
if (parsed.path && scopedPaths.length > 0) {
|
|
4279
|
+
const pathTrimmed = parsed.path.trim();
|
|
4280
|
+
if (pathTrimmed && !pathTrimmed.startsWith(".") && !isLiteral(pathTrimmed)) {
|
|
4281
|
+
const resultType = resolveResultType(pathTrimmed, parsed.filters, scopedPaths);
|
|
4282
|
+
if (resultType !== null) {
|
|
4283
|
+
const typeReq = getExpectedType(
|
|
4284
|
+
parsed.property,
|
|
4285
|
+
() => isNegatedByElseChain(getStructuralTemplates(), attr.valueStart)
|
|
4286
|
+
);
|
|
4287
|
+
if (typeReq && resultType !== typeReq.expected) {
|
|
4288
|
+
const pathOffset = binding.indexOf(parsed.path);
|
|
4289
|
+
const pathStart = bindingStart + pathOffset;
|
|
4290
|
+
const schemaDefinite = typeReq.expected === "array" && parsed.filters.length === 0 && applicationSchema !== void 0 && scopedPaths.some((p) => p.path === pathTrimmed && p.fromSchema === true);
|
|
4291
|
+
diagnostics.push({
|
|
4292
|
+
code: schemaDefinite ? WcsDiagnosticCode.PathTypeMismatch : WcsDiagnosticCode.BindingTypeExpectation,
|
|
4293
|
+
start: pathStart,
|
|
4294
|
+
end: pathStart + pathTrimmed.length,
|
|
4295
|
+
message: schemaDefinite ? msgs.pathTypeMismatch(pathTrimmed, typeReq.label, typeReq.expected, resultType) : msgs.typeExpectation(typeReq.label, typeReq.expected, resultType),
|
|
4296
|
+
severity: schemaDefinite ? "error" : typeReq.severity
|
|
4297
|
+
});
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
pos += binding.length + 1;
|
|
3966
4303
|
}
|
|
3967
4304
|
}
|
|
3968
4305
|
return diagnostics;
|
|
3969
4306
|
}
|
|
3970
|
-
function
|
|
3971
|
-
const
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
for (const
|
|
3975
|
-
const
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
4307
|
+
function findAllBindAttributes(html, attrName) {
|
|
4308
|
+
const attrs = [];
|
|
4309
|
+
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4310
|
+
const regex = new RegExp(`(?:^|[\\s/])${escaped}\\s*=\\s*(["'])`, "gi");
|
|
4311
|
+
for (const tag of findStartTagRegions(html)) {
|
|
4312
|
+
const attrsText = html.slice(tag.start, tag.end);
|
|
4313
|
+
regex.lastIndex = 0;
|
|
4314
|
+
let match;
|
|
4315
|
+
while ((match = regex.exec(attrsText)) !== null) {
|
|
4316
|
+
const quote = match[1];
|
|
4317
|
+
const valueStart = tag.start + match.index + match[0].length;
|
|
4318
|
+
const valueEnd = html.indexOf(quote, valueStart);
|
|
4319
|
+
if (valueEnd === -1 || valueEnd > tag.end) continue;
|
|
4320
|
+
attrs.push({ value: html.slice(valueStart, valueEnd), valueStart });
|
|
4321
|
+
regex.lastIndex = valueEnd - tag.start;
|
|
4322
|
+
}
|
|
3979
4323
|
}
|
|
3980
|
-
return
|
|
3981
|
-
}
|
|
3982
|
-
function inferArgType(arg) {
|
|
3983
|
-
const v = arg.trim();
|
|
3984
|
-
if (/^-?\d+(\.\d+)?$/.test(v)) return "number";
|
|
3985
|
-
return "string";
|
|
4324
|
+
return attrs;
|
|
3986
4325
|
}
|
|
3987
|
-
function
|
|
3988
|
-
return
|
|
4326
|
+
function splitBindingExpressions(value) {
|
|
4327
|
+
return splitBindTexts(value);
|
|
3989
4328
|
}
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
3995
|
-
const diagnostics = [];
|
|
3996
|
-
for (const block of blocks) {
|
|
3997
|
-
const props = findJsDocTypedProperties(block.content);
|
|
3998
|
-
for (const prop of props) {
|
|
3999
|
-
if (!isValueCompatible(prop.declaredTypes, prop.valueType)) {
|
|
4000
|
-
const absStart = block.contentStart + prop.valueOffset;
|
|
4001
|
-
const absEnd = absStart + prop.valueLength;
|
|
4002
|
-
diagnostics.push({
|
|
4003
|
-
start: absStart,
|
|
4004
|
-
end: absEnd,
|
|
4005
|
-
message: msgs.typeAnnotationIncompatible(prop.valueType, prop.rawType),
|
|
4006
|
-
severity: "warning"
|
|
4007
|
-
});
|
|
4008
|
-
}
|
|
4009
|
-
}
|
|
4329
|
+
function parseBindingExpression(expr) {
|
|
4330
|
+
const colonIndex = indexOfOutsideQuotes2(expr, ":");
|
|
4331
|
+
if (colonIndex === -1) {
|
|
4332
|
+
return { property: expr.trim(), path: null, filters: [], inputFilters: [] };
|
|
4010
4333
|
}
|
|
4011
|
-
|
|
4334
|
+
const rawProp = expr.slice(0, colonIndex);
|
|
4335
|
+
const propSegments = splitByPipe(rawProp);
|
|
4336
|
+
const property = propSegments[0].trim();
|
|
4337
|
+
const inputFilters = parseFilterSegments(expr, propSegments.slice(1), propSegments[0].length + 1);
|
|
4338
|
+
const afterColon = expr.slice(colonIndex + 1);
|
|
4339
|
+
const segments = splitByPipe(afterColon);
|
|
4340
|
+
const pathSegment = segments[0] || "";
|
|
4341
|
+
const filterSegments = segments.slice(1);
|
|
4342
|
+
const atIndex = pathSegment.indexOf("@");
|
|
4343
|
+
const path = atIndex !== -1 ? pathSegment.slice(0, atIndex) : pathSegment;
|
|
4344
|
+
const filters = parseFilterSegments(expr, filterSegments, colonIndex + 1 + pathSegment.length + 1);
|
|
4345
|
+
return { property, path: path.trim() || null, filters, inputFilters };
|
|
4012
4346
|
}
|
|
4013
|
-
function
|
|
4014
|
-
const
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
const
|
|
4019
|
-
const
|
|
4020
|
-
const
|
|
4021
|
-
const
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
const
|
|
4025
|
-
|
|
4347
|
+
function parseFilterSegments(expr, segments, searchStart) {
|
|
4348
|
+
const filters = [];
|
|
4349
|
+
let filterSearchStart = searchStart;
|
|
4350
|
+
for (const seg of segments) {
|
|
4351
|
+
const trimmed = seg.trim();
|
|
4352
|
+
const open = trimmed.indexOf("(");
|
|
4353
|
+
const close = trimmed.lastIndexOf(")");
|
|
4354
|
+
const namePart = open === -1 ? trimmed : trimmed.slice(0, open);
|
|
4355
|
+
const nameMatch = /^\w+/.exec(namePart.trim());
|
|
4356
|
+
if (nameMatch !== null) {
|
|
4357
|
+
const name = nameMatch[0];
|
|
4358
|
+
const nameOffset = expr.indexOf(trimmed, filterSearchStart);
|
|
4359
|
+
const args = open !== -1 && close > open ? parseFilterArgsText(trimmed.slice(open + 1, close)) : [];
|
|
4360
|
+
filters.push({
|
|
4026
4361
|
name,
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
valueOffset: valueStart,
|
|
4031
|
-
valueLength: valueText.length
|
|
4362
|
+
offset: nameOffset >= 0 ? nameOffset : filterSearchStart,
|
|
4363
|
+
args,
|
|
4364
|
+
argsOffset: nameOffset >= 0 ? nameOffset + name.length : filterSearchStart
|
|
4032
4365
|
});
|
|
4033
4366
|
}
|
|
4367
|
+
filterSearchStart += seg.length + 1;
|
|
4034
4368
|
}
|
|
4035
|
-
return
|
|
4036
|
-
}
|
|
4037
|
-
function extractValue(script, start) {
|
|
4038
|
-
let depth = 0;
|
|
4039
|
-
let inString = null;
|
|
4040
|
-
let i = start;
|
|
4041
|
-
while (i < script.length) {
|
|
4042
|
-
const ch = script[i];
|
|
4043
|
-
if (inString) {
|
|
4044
|
-
if (ch === inString && script[i - 1] !== "\\") inString = null;
|
|
4045
|
-
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
4046
|
-
inString = ch;
|
|
4047
|
-
} else if (ch === "{" || ch === "[" || ch === "(") {
|
|
4048
|
-
depth++;
|
|
4049
|
-
} else if (ch === "}" || ch === "]" || ch === ")") {
|
|
4050
|
-
if (depth === 0) break;
|
|
4051
|
-
depth--;
|
|
4052
|
-
} else if ((ch === "," || ch === "\n") && depth === 0) {
|
|
4053
|
-
break;
|
|
4054
|
-
}
|
|
4055
|
-
i++;
|
|
4056
|
-
}
|
|
4057
|
-
return script.slice(start, i).trim();
|
|
4058
|
-
}
|
|
4059
|
-
function inferValueType(value) {
|
|
4060
|
-
const v = value.replace(/,\s*$/, "").trim();
|
|
4061
|
-
if (v === "null") return "null";
|
|
4062
|
-
if (v === "undefined") return "null";
|
|
4063
|
-
if (v === "true" || v === "false") return "boolean";
|
|
4064
|
-
if (/^-?\d+\.\d/.test(v)) return "number";
|
|
4065
|
-
if (/^-?\d/.test(v)) return "number";
|
|
4066
|
-
if (/^["'`]/.test(v)) return "string";
|
|
4067
|
-
if (v.startsWith("[")) return "array";
|
|
4068
|
-
if (v.startsWith("{")) return "object";
|
|
4069
|
-
return null;
|
|
4070
|
-
}
|
|
4071
|
-
function normalizeType(type) {
|
|
4072
|
-
const lower = type.toLowerCase();
|
|
4073
|
-
if (lower === "null" || lower === "undefined") return "null";
|
|
4074
|
-
if (lower === "string") return "string";
|
|
4075
|
-
if (lower === "number") return "number";
|
|
4076
|
-
if (lower === "boolean") return "boolean";
|
|
4077
|
-
if (lower.endsWith("[]") || lower.startsWith("array")) return "array";
|
|
4078
|
-
if (lower === "object") return "object";
|
|
4079
|
-
return type;
|
|
4080
|
-
}
|
|
4081
|
-
function isValueCompatible(declaredTypes, valueType) {
|
|
4082
|
-
return declaredTypes.includes(valueType);
|
|
4083
|
-
}
|
|
4084
|
-
|
|
4085
|
-
// src/service/scriptPatterns.ts
|
|
4086
|
-
var ID = String.raw`[\w$]+`;
|
|
4087
|
-
var SUB = String.raw`\s*(?:\?\.)?\s*\[(?!\s*["'])[^\[\]]+\]`;
|
|
4088
|
-
var DOT_SEG = String.raw`\s*\??\.\s*${ID}`;
|
|
4089
|
-
var CHAIN = String.raw`(?:${DOT_SEG}|${SUB})*`;
|
|
4090
|
-
var BRACKETS_ONLY = String.raw`(?:${SUB})+`;
|
|
4091
|
-
var CHAIN_ONE_PLUS = String.raw`(?:${DOT_SEG}|${SUB})+`;
|
|
4092
|
-
var ROOT_DOT = String.raw`\bthis\s*\??\.\s*(${ID})`;
|
|
4093
|
-
var ROOT_BRACKET = String.raw`\bthis\s*(?:\?\.)?\s*\[\s*["']([^"']+)["']\s*\]`;
|
|
4094
|
-
var ASSIGN_TAIL = String.raw`\s*(?:(?:\*\*|<<|>>>|>>|&&|\|\||\?\?|[+\-*/%&|^])?=(?!=)|\+\+|--)`;
|
|
4095
|
-
var PRE_INCDEC = String.raw`(?:\+\+|--)\s*`;
|
|
4096
|
-
function chainToDotted(chain) {
|
|
4097
|
-
const token = new RegExp(String.raw`\s*(?:\??\.\s*(${ID})|(?:\?\.)?\s*\[([^\[\]]+)\])`, "g");
|
|
4098
|
-
let out = "";
|
|
4099
|
-
let match;
|
|
4100
|
-
while ((match = token.exec(chain)) !== null) {
|
|
4101
|
-
if (match[1] !== void 0) {
|
|
4102
|
-
out += `.${match[1]}`;
|
|
4103
|
-
} else {
|
|
4104
|
-
const key = match[2].trim();
|
|
4105
|
-
out += /^\d+$/.test(key) ? `.${key}` : `.<${key}>`;
|
|
4106
|
-
}
|
|
4107
|
-
}
|
|
4108
|
-
return out;
|
|
4109
|
-
}
|
|
4110
|
-
function hasDotSegment(chain) {
|
|
4111
|
-
return /[.]/.test(chain.replace(/\s*(?:\?\.)?\s*\[[^\[\]]+\]/g, ""));
|
|
4369
|
+
return filters;
|
|
4112
4370
|
}
|
|
4113
|
-
function
|
|
4114
|
-
|
|
4371
|
+
function parseFilterArgsText(argsText) {
|
|
4372
|
+
const args = splitOutsideQuotes2(argsText, ",").map((a) => a.trim());
|
|
4373
|
+
if (args[args.length - 1] === "") args.pop();
|
|
4374
|
+
return args;
|
|
4115
4375
|
}
|
|
4116
|
-
|
|
4117
|
-
// src/service/nestedAssignValidator.ts
|
|
4118
|
-
var NESTED_ASSIGN = new RegExp(`${ROOT_DOT}(${CHAIN_ONE_PLUS})${ASSIGN_TAIL}`, "g");
|
|
4119
|
-
var PRE_NESTED_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_DOT}(${CHAIN_ONE_PLUS})`, "g");
|
|
4120
|
-
function validateNestedAssigns(html, stateTagName = "wcs-state", locale2) {
|
|
4121
|
-
const msgs = getMessages(locale2);
|
|
4122
|
-
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
4376
|
+
function validateFilterUsage(filter, bindingStart, msgs) {
|
|
4123
4377
|
const diagnostics = [];
|
|
4124
|
-
|
|
4125
|
-
|
|
4378
|
+
const canonical = canonicalFilterName(filter.name);
|
|
4379
|
+
const info = filterMap.get(canonical);
|
|
4380
|
+
if (info && canonical !== filter.name) {
|
|
4381
|
+
diagnostics.push({
|
|
4382
|
+
code: WcsDiagnosticCode.NameAlias,
|
|
4383
|
+
start: bindingStart + filter.offset,
|
|
4384
|
+
end: bindingStart + filter.offset + filter.name.length,
|
|
4385
|
+
message: msgs.nameAlias(filter.name, canonical),
|
|
4386
|
+
severity: "info"
|
|
4387
|
+
});
|
|
4126
4388
|
}
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4389
|
+
if (!info) {
|
|
4390
|
+
diagnostics.push({
|
|
4391
|
+
code: WcsDiagnosticCode.FilterUnknown,
|
|
4392
|
+
start: bindingStart + filter.offset,
|
|
4393
|
+
end: bindingStart + filter.offset + filter.name.length,
|
|
4394
|
+
message: msgs.filterUnknown(filter.name),
|
|
4395
|
+
severity: "warning"
|
|
4396
|
+
});
|
|
4397
|
+
return diagnostics;
|
|
4398
|
+
}
|
|
4399
|
+
const argCount = filter.args.length;
|
|
4400
|
+
if (argCount < info.minArgs) {
|
|
4401
|
+
diagnostics.push({
|
|
4402
|
+
code: WcsDiagnosticCode.FilterArity,
|
|
4403
|
+
start: bindingStart + filter.offset,
|
|
4404
|
+
end: bindingStart + filter.offset + filter.name.length,
|
|
4405
|
+
message: msgs.filterMinArgs(filter.name, info.minArgs, argCount),
|
|
4406
|
+
severity: "error"
|
|
4407
|
+
});
|
|
4408
|
+
} else if (argCount > info.maxArgs) {
|
|
4409
|
+
diagnostics.push({
|
|
4410
|
+
code: WcsDiagnosticCode.FilterArity,
|
|
4411
|
+
start: bindingStart + filter.offset,
|
|
4412
|
+
end: bindingStart + filter.offset + filter.name.length,
|
|
4413
|
+
message: msgs.filterMaxArgs(filter.name, info.maxArgs, argCount),
|
|
4414
|
+
severity: "error"
|
|
4415
|
+
});
|
|
4416
|
+
}
|
|
4417
|
+
if (info.argTypes && argCount > 0) {
|
|
4418
|
+
for (let i = 0; i < Math.min(argCount, info.argTypes.length); i++) {
|
|
4419
|
+
const expectedArgType = info.argTypes[i];
|
|
4420
|
+
if (expectedArgType === "any") continue;
|
|
4421
|
+
const actualArgType = inferArgType(filter.args[i]);
|
|
4422
|
+
if (actualArgType !== expectedArgType) {
|
|
4423
|
+
diagnostics.push({
|
|
4424
|
+
code: WcsDiagnosticCode.FilterArgType,
|
|
4425
|
+
start: bindingStart + filter.argsOffset,
|
|
4426
|
+
end: bindingStart + filter.argsOffset + filter.name.length,
|
|
4427
|
+
message: msgs.filterArgType(filter.name, i + 1, expectedArgType, filter.args[i], actualArgType),
|
|
4428
|
+
severity: "warning"
|
|
4429
|
+
});
|
|
4430
|
+
}
|
|
4145
4431
|
}
|
|
4146
4432
|
}
|
|
4433
|
+
return diagnostics;
|
|
4147
4434
|
}
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
var DESTRUCTIVE_METHODS = "push|pop|shift|unshift|splice|sort|reverse|fill|copyWithin";
|
|
4151
|
-
var ALTERNATIVES = {
|
|
4152
|
-
push: (a) => `${a} = ${a}.concat(item)`,
|
|
4153
|
-
unshift: (a) => `${a} = [item, ...${a}]`,
|
|
4154
|
-
pop: (a) => `${a} = ${a}.slice(0, -1)`,
|
|
4155
|
-
shift: (a) => `${a} = ${a}.slice(1)`,
|
|
4156
|
-
splice: (a) => `${a} = ${a}.toSpliced(...)`,
|
|
4157
|
-
sort: (a) => `${a} = ${a}.toSorted(...)`,
|
|
4158
|
-
reverse: (a) => `${a} = ${a}.toReversed()`,
|
|
4159
|
-
fill: (a) => `${a} = ${a}.map(...)`,
|
|
4160
|
-
copyWithin: (a) => `${a} = ${a}.map(...)`
|
|
4161
|
-
};
|
|
4162
|
-
var METHOD_TAIL = String.raw`\s*\??\.\s*(${DESTRUCTIVE_METHODS})(?=\s*\()`;
|
|
4163
|
-
var DOT_ROOT_CALL = new RegExp(`${ROOT_DOT}(${CHAIN})${METHOD_TAIL}`, "g");
|
|
4164
|
-
var BRACKET_ROOT_CALL = new RegExp(`${ROOT_BRACKET}(${CHAIN})${METHOD_TAIL}`, "g");
|
|
4165
|
-
var DOT_INDEX_ASSIGN = new RegExp(`${ROOT_DOT}(${BRACKETS_ONLY})${ASSIGN_TAIL}`, "g");
|
|
4166
|
-
var BRACKET_INDEX_ASSIGN = new RegExp(`${ROOT_BRACKET}(${BRACKETS_ONLY})${ASSIGN_TAIL}`, "g");
|
|
4167
|
-
var PRE_DOT_INDEX = new RegExp(`${PRE_INCDEC}${ROOT_DOT}(${BRACKETS_ONLY})`, "g");
|
|
4168
|
-
var PRE_BRACKET_INDEX = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}(${BRACKETS_ONLY})`, "g");
|
|
4169
|
-
function toAccessor(path) {
|
|
4170
|
-
return /^[A-Za-z_]\w*$/.test(path) ? `this.${path}` : `this["${path}"]`;
|
|
4435
|
+
function splitByPipe(value) {
|
|
4436
|
+
return splitOutsideQuotes2(value, "|");
|
|
4171
4437
|
}
|
|
4172
|
-
function
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4438
|
+
function validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs) {
|
|
4439
|
+
if (/^\$\d+$/.test(checkPath)) return null;
|
|
4440
|
+
if (checkPath.startsWith("$command.")) {
|
|
4441
|
+
if (commandNames.size > 0 && !commandNames.has(checkPath)) {
|
|
4442
|
+
return msgs.commandTokenUndeclared(displayPath);
|
|
4443
|
+
}
|
|
4444
|
+
return null;
|
|
4179
4445
|
}
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
let match;
|
|
4186
|
-
while ((match = regex.exec(script)) !== null) {
|
|
4187
|
-
const [full, root, chain, method] = match;
|
|
4188
|
-
if (isApiRoot(root)) continue;
|
|
4189
|
-
const statePath = root + chainToDotted(chain);
|
|
4190
|
-
const start = baseOffset + match.index;
|
|
4191
|
-
out.push({
|
|
4192
|
-
code: WcsDiagnosticCode.ArrayMutation,
|
|
4193
|
-
start,
|
|
4194
|
-
end: start + full.length,
|
|
4195
|
-
message: msgs.arrayMutation(method, ALTERNATIVES[method](toAccessor(statePath))),
|
|
4196
|
-
severity: "error",
|
|
4197
|
-
statePath
|
|
4198
|
-
});
|
|
4446
|
+
if (checkPath.startsWith("$streamStatus.") || checkPath.startsWith("$streamError.")) {
|
|
4447
|
+
const prefix = checkPath.startsWith("$streamStatus.") ? "$streamStatus." : "$streamError.";
|
|
4448
|
+
const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
|
|
4449
|
+
if (hasNamespace && !scopedPathSet.has(checkPath)) {
|
|
4450
|
+
return msgs.streamPathMissing(displayPath);
|
|
4199
4451
|
}
|
|
4452
|
+
return null;
|
|
4200
4453
|
}
|
|
4454
|
+
if (!scopedPathSet.has(checkPath) && !matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) {
|
|
4455
|
+
return msgs.pathMissing(displayPath);
|
|
4456
|
+
}
|
|
4457
|
+
return null;
|
|
4201
4458
|
}
|
|
4202
|
-
function
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
}
|
|
4459
|
+
function matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet) {
|
|
4460
|
+
const specs = collectRecursionSpecs(scopedPaths);
|
|
4461
|
+
if (specs.length === 0) return false;
|
|
4462
|
+
return matchesRecursion(specs, checkPath, (candidate) => scopedPathSet.has(candidate));
|
|
4463
|
+
}
|
|
4464
|
+
function toMissingVerdict(message) {
|
|
4465
|
+
return message ? { code: WcsDiagnosticCode.BindingPathMissing, message, severity: "warning" } : null;
|
|
4466
|
+
}
|
|
4467
|
+
function validateSchemaPathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, schema, msgs) {
|
|
4468
|
+
if (checkPath.startsWith("$")) {
|
|
4469
|
+
return toMissingVerdict(validatePathExistence(checkPath, displayPath, scopedPaths, scopedPathSet, commandNames, msgs));
|
|
4470
|
+
}
|
|
4471
|
+
if (scopedPathSet.has(checkPath)) return null;
|
|
4472
|
+
if (matchesRecursionCandidates(scopedPaths, checkPath, scopedPathSet)) return null;
|
|
4473
|
+
const resolution = resolveSchemaPath(schema, schema.$defs ?? {}, checkPath.split("."));
|
|
4474
|
+
if (resolution.kind === "nonexistent") {
|
|
4475
|
+
return { code: WcsDiagnosticCode.PathNonexistent, message: msgs.pathNonexistent(displayPath), severity: "error" };
|
|
4220
4476
|
}
|
|
4477
|
+
return null;
|
|
4221
4478
|
}
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
const
|
|
4226
|
-
const
|
|
4479
|
+
function collectStructuralTemplates(html, attrName) {
|
|
4480
|
+
const escaped = attrName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4481
|
+
const attrRegex = new RegExp(`${escaped}\\s*=\\s*(["'])`, "i");
|
|
4482
|
+
const tagRegex = /<template(?:\s[^>]*)?>|<\/template\s*>/gi;
|
|
4483
|
+
const templates = [];
|
|
4484
|
+
let depth = 0;
|
|
4227
4485
|
let match;
|
|
4228
|
-
while ((match =
|
|
4229
|
-
if (
|
|
4486
|
+
while ((match = tagRegex.exec(html)) !== null) {
|
|
4487
|
+
if (match[0].startsWith("</")) {
|
|
4488
|
+
depth = Math.max(0, depth - 1);
|
|
4230
4489
|
continue;
|
|
4231
4490
|
}
|
|
4232
|
-
const
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4491
|
+
const attrMatch = attrRegex.exec(match[0]);
|
|
4492
|
+
if (attrMatch) {
|
|
4493
|
+
const quote = attrMatch[1];
|
|
4494
|
+
const valueStart = match.index + attrMatch.index + attrMatch[0].length;
|
|
4495
|
+
const valueEnd = html.indexOf(quote, valueStart);
|
|
4496
|
+
if (valueEnd !== -1) {
|
|
4497
|
+
const first = splitBindingExpressions(html.slice(valueStart, valueEnd))[0] ?? "";
|
|
4498
|
+
const firstColon = indexOfOutsideQuotes2(first, ":");
|
|
4499
|
+
const prop = (firstColon === -1 ? first : first.slice(0, firstColon)).replace(/#.*$/, "").trim();
|
|
4500
|
+
const type = prop === "if" || prop === "elseif" || prop === "else" ? prop : "other";
|
|
4501
|
+
templates.push({ valueStart, depth, type });
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
depth++;
|
|
4243
4505
|
}
|
|
4244
|
-
return
|
|
4506
|
+
return templates;
|
|
4245
4507
|
}
|
|
4246
|
-
function
|
|
4247
|
-
const
|
|
4248
|
-
|
|
4249
|
-
const
|
|
4250
|
-
let
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
if (
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4508
|
+
function isNegatedByElseChain(templates, valueStart) {
|
|
4509
|
+
const index = templates.findIndex((t) => t.valueStart === valueStart);
|
|
4510
|
+
if (index === -1) return false;
|
|
4511
|
+
const selfDepth = templates[index].depth;
|
|
4512
|
+
for (let i = index + 1; i < templates.length; i++) {
|
|
4513
|
+
const next = templates[i];
|
|
4514
|
+
if (next.depth > selfDepth) continue;
|
|
4515
|
+
if (next.depth < selfDepth) return false;
|
|
4516
|
+
if (next.type === "elseif" || next.type === "else") return true;
|
|
4517
|
+
if (next.type === "if") return false;
|
|
4518
|
+
}
|
|
4519
|
+
return false;
|
|
4520
|
+
}
|
|
4521
|
+
function getExpectedType(property, isNegatedIf) {
|
|
4522
|
+
const prop = property.replace(/#.*$/, "");
|
|
4523
|
+
if (prop === "for") {
|
|
4524
|
+
return { label: "for", expected: "array", severity: "error" };
|
|
4525
|
+
}
|
|
4526
|
+
if (prop === "if" || prop === "elseif") {
|
|
4527
|
+
if (!isNegatedIf()) return null;
|
|
4528
|
+
return { label: prop, expected: "boolean", severity: "warning" };
|
|
4529
|
+
}
|
|
4530
|
+
if (prop.startsWith("class.")) {
|
|
4531
|
+
return { label: prop, expected: "boolean", severity: "warning" };
|
|
4532
|
+
}
|
|
4533
|
+
if (prop.startsWith("attr.")) {
|
|
4534
|
+
return { label: prop, expected: "string", severity: "warning" };
|
|
4264
4535
|
}
|
|
4265
|
-
|
|
4266
|
-
}
|
|
4267
|
-
function isInsideTag(html, offset2, tagName) {
|
|
4268
|
-
const tagRegex = new RegExp(`<(/?)${tagName}[\\s>]`, "gi");
|
|
4269
|
-
let depth = 0;
|
|
4270
|
-
let match;
|
|
4271
|
-
while ((match = tagRegex.exec(html)) !== null) {
|
|
4272
|
-
if (match.index > offset2) break;
|
|
4273
|
-
if (match[1]) {
|
|
4274
|
-
depth = Math.max(0, depth - 1);
|
|
4275
|
-
} else {
|
|
4276
|
-
depth++;
|
|
4277
|
-
}
|
|
4536
|
+
if (prop.startsWith("style.")) {
|
|
4537
|
+
return { label: prop, expected: "string", severity: "warning" };
|
|
4278
4538
|
}
|
|
4279
|
-
return
|
|
4539
|
+
return null;
|
|
4280
4540
|
}
|
|
4281
|
-
|
|
4282
|
-
// src/service/templateSyntaxValidator.ts
|
|
4283
|
-
function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale2, fileReader, applicationSchema) {
|
|
4541
|
+
function validateFilterChainTypes(path, filters, statePaths, bindingStart, msgs) {
|
|
4284
4542
|
const diagnostics = [];
|
|
4285
|
-
const
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
}
|
|
4296
|
-
if (isValidTemplatePath(path, pathSet2, scoped)) return null;
|
|
4297
|
-
if (defaultSchema !== void 0 && !path.startsWith("$")) {
|
|
4298
|
-
const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
|
|
4299
|
-
return resolution.kind === "nonexistent" ? { code: WcsDiagnosticCode.PathNonexistent, severity: "error", message: msgs.pathNonexistent(displayPath) } : null;
|
|
4300
|
-
}
|
|
4301
|
-
return { code: WcsDiagnosticCode.BindingPathMissing, severity: "warning", message: msgs.pathMissing(displayPath) };
|
|
4302
|
-
};
|
|
4303
|
-
if (allPaths.length === 0) return diagnostics;
|
|
4304
|
-
const defaultPaths = allPaths;
|
|
4305
|
-
const pathSet = new Set(defaultPaths.map((p) => p.path));
|
|
4306
|
-
const filterNameSet = new Set(BUILTIN_FILTERS.map((f) => f.name));
|
|
4307
|
-
const mustaches = findAllMustacheSyntax(html);
|
|
4308
|
-
const comments = findAllCommentBindings(html);
|
|
4309
|
-
for (const item of [...mustaches, ...comments]) {
|
|
4310
|
-
if (item.kind === "comment") {
|
|
4311
|
-
diagnostics.push({
|
|
4312
|
-
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4313
|
-
start: item.matchStart,
|
|
4314
|
-
end: item.matchEnd,
|
|
4315
|
-
message: msgs.wcsTextInfo(item.expression),
|
|
4316
|
-
severity: "info"
|
|
4317
|
-
});
|
|
4318
|
-
}
|
|
4319
|
-
if (item.kind === "mustache" && !item.insideTemplate) {
|
|
4320
|
-
diagnostics.push({
|
|
4321
|
-
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4322
|
-
start: item.matchStart,
|
|
4323
|
-
end: item.matchEnd,
|
|
4324
|
-
message: msgs.moustacheFouc(item.expression),
|
|
4325
|
-
severity: "info"
|
|
4326
|
-
});
|
|
4327
|
-
}
|
|
4328
|
-
if (!item.expression) continue;
|
|
4329
|
-
const parts = item.expression.split("|");
|
|
4330
|
-
const pathPart = (parts[0] || "").trim();
|
|
4331
|
-
if (pathPart.includes("@")) continue;
|
|
4332
|
-
const insideFor = item.insideTemplate && isInsideForTemplate(html, item.matchStart, bindAttrName);
|
|
4333
|
-
if (pathPart && !/^-?\d|^["'`]|^true$|^false$|^null$/.test(pathPart)) {
|
|
4334
|
-
if (!insideFor && pathPart.includes("*")) {
|
|
4335
|
-
diagnostics.push({
|
|
4336
|
-
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4337
|
-
start: item.exprStart,
|
|
4338
|
-
end: item.exprStart + pathPart.length,
|
|
4339
|
-
message: msgs.patternPathOutsideFor(pathPart),
|
|
4340
|
-
severity: "warning"
|
|
4341
|
-
});
|
|
4342
|
-
}
|
|
4343
|
-
if (!insideFor && pathPart.startsWith(".")) {
|
|
4344
|
-
diagnostics.push({
|
|
4345
|
-
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4346
|
-
start: item.exprStart,
|
|
4347
|
-
end: item.exprStart + pathPart.length,
|
|
4348
|
-
message: msgs.omittedPathOutsideFor(pathPart),
|
|
4349
|
-
severity: "warning"
|
|
4350
|
-
});
|
|
4351
|
-
}
|
|
4352
|
-
if (insideFor && !pathPart.startsWith(".")) {
|
|
4353
|
-
const indexMatch = /^\$(\d+)$/.exec(pathPart);
|
|
4354
|
-
const needed = indexMatch !== null ? Number(indexMatch[1]) : pathPart.includes("*") ? countWildcardSegments(pathPart) : 0;
|
|
4355
|
-
if (needed > 0) {
|
|
4356
|
-
const available = getAvailableWildcardRank(html, item.matchStart, bindAttrName);
|
|
4357
|
-
if (available > 0 && needed > available) {
|
|
4358
|
-
diagnostics.push({
|
|
4359
|
-
code: WcsDiagnosticCode.WildcardRank,
|
|
4360
|
-
start: item.exprStart,
|
|
4361
|
-
end: item.exprStart + pathPart.length,
|
|
4362
|
-
message: msgs.wildcardRank(`"${pathPart}"`, needed, available),
|
|
4363
|
-
severity: "warning"
|
|
4364
|
-
});
|
|
4365
|
-
}
|
|
4366
|
-
}
|
|
4367
|
-
}
|
|
4368
|
-
if (/\.\d+\.|\.\d+$/.test(pathPart)) {
|
|
4543
|
+
const pathInfo = statePaths.find((p) => p.path === path);
|
|
4544
|
+
if (!pathInfo?.typeHint) return diagnostics;
|
|
4545
|
+
let currentType = pathInfo.typeHint;
|
|
4546
|
+
for (const filter of filters) {
|
|
4547
|
+
const info = filterMap.get(canonicalFilterName(filter.name));
|
|
4548
|
+
if (!info) break;
|
|
4549
|
+
if (info.acceptTypes !== "any") {
|
|
4550
|
+
const currentTypes = currentType.split("|");
|
|
4551
|
+
const hasMatch = currentTypes.some((t) => info.acceptTypes.includes(t));
|
|
4552
|
+
if (!hasMatch) {
|
|
4369
4553
|
diagnostics.push({
|
|
4370
|
-
code: WcsDiagnosticCode.
|
|
4371
|
-
start:
|
|
4372
|
-
end:
|
|
4373
|
-
message: msgs.
|
|
4554
|
+
code: WcsDiagnosticCode.FilterInputType,
|
|
4555
|
+
start: bindingStart + filter.offset,
|
|
4556
|
+
end: bindingStart + filter.offset + filter.name.length,
|
|
4557
|
+
message: msgs.filterInputType(filter.name, info.acceptTypes.join("|"), currentType),
|
|
4374
4558
|
severity: "warning"
|
|
4375
4559
|
});
|
|
4376
4560
|
}
|
|
4377
|
-
if (pathPart.startsWith(".")) {
|
|
4378
|
-
const forPath = insideFor ? getInnermostForPath(html, item.matchStart, bindAttrName) : null;
|
|
4379
|
-
if (forPath && !forPath.startsWith(".")) {
|
|
4380
|
-
const expandedPath = pathPart === "." ? `${forPath}.*` : `${forPath}.*.${pathPart.slice(1)}`;
|
|
4381
|
-
const verdict = missingVerdict(expandedPath, pathPart, pathSet, defaultPaths);
|
|
4382
|
-
if (verdict) {
|
|
4383
|
-
diagnostics.push({
|
|
4384
|
-
code: verdict.code,
|
|
4385
|
-
start: item.exprStart,
|
|
4386
|
-
end: item.exprStart + pathPart.length,
|
|
4387
|
-
message: verdict.message + msgs.expansionSuffix(expandedPath),
|
|
4388
|
-
severity: verdict.severity
|
|
4389
|
-
});
|
|
4390
|
-
}
|
|
4391
|
-
}
|
|
4392
|
-
} else {
|
|
4393
|
-
const verdict = missingVerdict(pathPart, pathPart, pathSet, defaultPaths);
|
|
4394
|
-
if (verdict) {
|
|
4395
|
-
diagnostics.push({
|
|
4396
|
-
code: verdict.code,
|
|
4397
|
-
start: item.exprStart,
|
|
4398
|
-
end: item.exprStart + pathPart.length,
|
|
4399
|
-
message: verdict.message,
|
|
4400
|
-
severity: verdict.severity
|
|
4401
|
-
});
|
|
4402
|
-
}
|
|
4403
|
-
}
|
|
4404
4561
|
}
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4562
|
+
if (info.resultType !== "passthrough") {
|
|
4563
|
+
currentType = info.resultType;
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
return diagnostics;
|
|
4567
|
+
}
|
|
4568
|
+
function resolveResultType(path, filters, statePaths) {
|
|
4569
|
+
const pathInfo = statePaths.find((p) => p.path === path);
|
|
4570
|
+
if (!pathInfo?.typeHint) return null;
|
|
4571
|
+
let currentType = pathInfo.typeHint;
|
|
4572
|
+
for (const filter of filters) {
|
|
4573
|
+
const info = filterMap.get(canonicalFilterName(filter.name));
|
|
4574
|
+
if (!info) return null;
|
|
4575
|
+
if (info.resultType === "passthrough") continue;
|
|
4576
|
+
currentType = info.resultType;
|
|
4577
|
+
}
|
|
4578
|
+
return currentType;
|
|
4579
|
+
}
|
|
4580
|
+
function inferArgType(arg) {
|
|
4581
|
+
const v = arg.trim();
|
|
4582
|
+
if (/^-?\d+(\.\d+)?$/.test(v)) return "number";
|
|
4583
|
+
return "string";
|
|
4584
|
+
}
|
|
4585
|
+
function isLiteral(value) {
|
|
4586
|
+
return /^-?\d/.test(value) || /^["'`]/.test(value) || value === "true" || value === "false" || value === "null";
|
|
4587
|
+
}
|
|
4588
|
+
|
|
4589
|
+
// src/service/stateTypeValidator.ts
|
|
4590
|
+
function validateStateTypes(html, stateTagName = "wcs-state", locale2) {
|
|
4591
|
+
const msgs = getMessages(locale2);
|
|
4592
|
+
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
4593
|
+
const diagnostics = [];
|
|
4594
|
+
for (const block of blocks) {
|
|
4595
|
+
const props = findJsDocTypedProperties(block.content);
|
|
4596
|
+
for (const prop of props) {
|
|
4597
|
+
if (!isValueCompatible(prop.declaredTypes, prop.valueType)) {
|
|
4598
|
+
const absStart = block.contentStart + prop.valueOffset;
|
|
4599
|
+
const absEnd = absStart + prop.valueLength;
|
|
4418
4600
|
diagnostics.push({
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
message: msgs.filterUnknown(filterName),
|
|
4601
|
+
start: absStart,
|
|
4602
|
+
end: absEnd,
|
|
4603
|
+
message: msgs.typeAnnotationIncompatible(prop.valueType, prop.rawType),
|
|
4423
4604
|
severity: "warning"
|
|
4424
4605
|
});
|
|
4425
4606
|
}
|
|
@@ -4427,490 +4608,446 @@ function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", l
|
|
|
4427
4608
|
}
|
|
4428
4609
|
return diagnostics;
|
|
4429
4610
|
}
|
|
4430
|
-
function
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4611
|
+
function findJsDocTypedProperties(script) {
|
|
4612
|
+
const results = [];
|
|
4613
|
+
const regex = /\/\*\*\s*@type\s*\{([^}]+)\}\s*\*\/\s*(?:"([^"]+)"|'([^']+)'|(\w+))\s*:\s*/g;
|
|
4614
|
+
let match;
|
|
4615
|
+
while ((match = regex.exec(script)) !== null) {
|
|
4616
|
+
const rawType = match[1].trim();
|
|
4617
|
+
const name = match[2] ?? match[3] ?? match[4];
|
|
4618
|
+
const valueStart = match.index + match[0].length;
|
|
4619
|
+
const valueText = extractValue(script, valueStart);
|
|
4620
|
+
const valueType = inferValueType(valueText);
|
|
4621
|
+
if (valueType) {
|
|
4622
|
+
const declaredTypes = rawType.split("|").map((t) => normalizeType(t.trim()));
|
|
4623
|
+
results.push({
|
|
4624
|
+
name,
|
|
4625
|
+
rawType,
|
|
4626
|
+
declaredTypes,
|
|
4627
|
+
valueType,
|
|
4628
|
+
valueOffset: valueStart,
|
|
4629
|
+
valueLength: valueText.length
|
|
4630
|
+
});
|
|
4631
|
+
}
|
|
4436
4632
|
}
|
|
4437
|
-
return
|
|
4633
|
+
return results;
|
|
4438
4634
|
}
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4635
|
+
function extractValue(script, start) {
|
|
4636
|
+
let depth = 0;
|
|
4637
|
+
let inString = null;
|
|
4638
|
+
let i = start;
|
|
4639
|
+
while (i < script.length) {
|
|
4640
|
+
const ch = script[i];
|
|
4641
|
+
if (inString) {
|
|
4642
|
+
if (ch === inString && script[i - 1] !== "\\") inString = null;
|
|
4643
|
+
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
4644
|
+
inString = ch;
|
|
4645
|
+
} else if (ch === "{" || ch === "[" || ch === "(") {
|
|
4646
|
+
depth++;
|
|
4647
|
+
} else if (ch === "}" || ch === "]" || ch === ")") {
|
|
4648
|
+
if (depth === 0) break;
|
|
4649
|
+
depth--;
|
|
4650
|
+
} else if ((ch === "," || ch === "\n") && depth === 0) {
|
|
4651
|
+
break;
|
|
4652
|
+
}
|
|
4653
|
+
i++;
|
|
4654
|
+
}
|
|
4655
|
+
return script.slice(start, i).trim();
|
|
4460
4656
|
}
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4657
|
+
function inferValueType(value) {
|
|
4658
|
+
const v = value.replace(/,\s*$/, "").trim();
|
|
4659
|
+
if (v === "null") return "null";
|
|
4660
|
+
if (v === "undefined") return "null";
|
|
4661
|
+
if (v === "true" || v === "false") return "boolean";
|
|
4662
|
+
if (/^-?\d+\.\d/.test(v)) return "number";
|
|
4663
|
+
if (/^-?\d/.test(v)) return "number";
|
|
4664
|
+
if (/^["'`]/.test(v)) return "string";
|
|
4665
|
+
if (v.startsWith("[")) return "array";
|
|
4666
|
+
if (v.startsWith("{")) return "object";
|
|
4667
|
+
return null;
|
|
4465
4668
|
}
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4669
|
+
function normalizeType(type) {
|
|
4670
|
+
const lower = type.toLowerCase();
|
|
4671
|
+
if (lower === "null" || lower === "undefined") return "null";
|
|
4672
|
+
if (lower === "string") return "string";
|
|
4673
|
+
if (lower === "number") return "number";
|
|
4674
|
+
if (lower === "boolean") return "boolean";
|
|
4675
|
+
if (lower.endsWith("[]") || lower.startsWith("array")) return "array";
|
|
4676
|
+
if (lower === "object") return "object";
|
|
4677
|
+
return type;
|
|
4469
4678
|
}
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
let pathInfo = _cache.get(path);
|
|
4473
|
-
if (typeof pathInfo !== "undefined") {
|
|
4474
|
-
return pathInfo;
|
|
4475
|
-
}
|
|
4476
|
-
if (path.indexOf(RECURSION_WILDCARD2) !== -1) {
|
|
4477
|
-
raiseError2(`[wcs/recursion-unsupported] "${path}" uses "${RECURSION_WILDCARD2}", which is not accepted here. It is only meaningful in a $recursion declaration, in a recursive getter key, and in the path argument of $getAll / $setAll \u2014 and only when the state declares a $recursion anchor.`);
|
|
4478
|
-
}
|
|
4479
|
-
pathInfo = Object.freeze(new PathInfo(path));
|
|
4480
|
-
_cache.set(path, pathInfo);
|
|
4481
|
-
return pathInfo;
|
|
4679
|
+
function isValueCompatible(declaredTypes, valueType) {
|
|
4680
|
+
return declaredTypes.includes(valueType);
|
|
4482
4681
|
}
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
wildcardCount;
|
|
4506
|
-
parentPathInfo;
|
|
4507
|
-
constructor(path) {
|
|
4508
|
-
const getPattern = (_path) => {
|
|
4509
|
-
return path === _path ? this : getPathInfo(_path);
|
|
4510
|
-
};
|
|
4511
|
-
const segments = path.split(".");
|
|
4512
|
-
const cumulativePaths = [];
|
|
4513
|
-
const cumulativePathInfos = [];
|
|
4514
|
-
const wildcardPaths = [];
|
|
4515
|
-
const indexByWildcardPath = {};
|
|
4516
|
-
const wildcardPathInfos = [];
|
|
4517
|
-
const wildcardParentPaths = [];
|
|
4518
|
-
const wildcardParentPathInfos = [];
|
|
4519
|
-
const wildcardPositions = [];
|
|
4520
|
-
let currentPatternPath = "", prevPatternPath = "";
|
|
4521
|
-
let wildcardCount = 0;
|
|
4522
|
-
for (let i = 0; i < segments.length; i++) {
|
|
4523
|
-
currentPatternPath += segments[i];
|
|
4524
|
-
if (segments[i] === WILDCARD2) {
|
|
4525
|
-
wildcardPaths.push(currentPatternPath);
|
|
4526
|
-
indexByWildcardPath[currentPatternPath] = wildcardCount;
|
|
4527
|
-
wildcardPathInfos.push(getPattern(currentPatternPath));
|
|
4528
|
-
wildcardParentPaths.push(prevPatternPath);
|
|
4529
|
-
wildcardParentPathInfos.push(getPattern(prevPatternPath));
|
|
4530
|
-
wildcardPositions.push(i);
|
|
4531
|
-
wildcardCount++;
|
|
4532
|
-
}
|
|
4533
|
-
cumulativePaths.push(currentPatternPath);
|
|
4534
|
-
cumulativePathInfos.push(getPattern(currentPatternPath));
|
|
4535
|
-
prevPatternPath = currentPatternPath;
|
|
4536
|
-
currentPatternPath += ".";
|
|
4682
|
+
|
|
4683
|
+
// src/service/scriptPatterns.ts
|
|
4684
|
+
var ID = String.raw`[\w$]+`;
|
|
4685
|
+
var SUB = String.raw`\s*(?:\?\.)?\s*\[(?!\s*["'])[^\[\]]+\]`;
|
|
4686
|
+
var DOT_SEG = String.raw`\s*\??\.\s*${ID}`;
|
|
4687
|
+
var CHAIN = String.raw`(?:${DOT_SEG}|${SUB})*`;
|
|
4688
|
+
var BRACKETS_ONLY = String.raw`(?:${SUB})+`;
|
|
4689
|
+
var CHAIN_ONE_PLUS = String.raw`(?:${DOT_SEG}|${SUB})+`;
|
|
4690
|
+
var ROOT_DOT = String.raw`\bthis\s*\??\.\s*(${ID})`;
|
|
4691
|
+
var ROOT_BRACKET = String.raw`\bthis\s*(?:\?\.)?\s*\[\s*["']([^"']+)["']\s*\]`;
|
|
4692
|
+
var ASSIGN_TAIL = String.raw`\s*(?:(?:\*\*|<<|>>>|>>|&&|\|\||\?\?|[+\-*/%&|^])?=(?!=)|\+\+|--)`;
|
|
4693
|
+
var PRE_INCDEC = String.raw`(?:\+\+|--)\s*`;
|
|
4694
|
+
function chainToDotted(chain) {
|
|
4695
|
+
const token = new RegExp(String.raw`\s*(?:\??\.\s*(${ID})|(?:\?\.)?\s*\[([^\[\]]+)\])`, "g");
|
|
4696
|
+
let out = "";
|
|
4697
|
+
let match;
|
|
4698
|
+
while ((match = token.exec(chain)) !== null) {
|
|
4699
|
+
if (match[1] !== void 0) {
|
|
4700
|
+
out += `.${match[1]}`;
|
|
4701
|
+
} else {
|
|
4702
|
+
const key = match[2].trim();
|
|
4703
|
+
out += /^\d+$/.test(key) ? `.${key}` : `.<${key}>`;
|
|
4537
4704
|
}
|
|
4538
|
-
const lastWildcardPath = wildcardPaths.length > 0 ? wildcardPaths[wildcardPaths.length - 1] : null;
|
|
4539
|
-
const parentPath = cumulativePaths.length > 1 ? cumulativePaths[cumulativePaths.length - 2] : null;
|
|
4540
|
-
this.path = path;
|
|
4541
|
-
this.segments = segments;
|
|
4542
|
-
this.lastSegment = segments[segments.length - 1];
|
|
4543
|
-
this.cumulativePaths = cumulativePaths;
|
|
4544
|
-
this.cumulativePathSet = new Set(cumulativePaths);
|
|
4545
|
-
this.cumulativePathInfos = cumulativePathInfos;
|
|
4546
|
-
this.cumulativePathInfoSet = new Set(cumulativePathInfos);
|
|
4547
|
-
this.wildcardPaths = wildcardPaths;
|
|
4548
|
-
this.wildcardPathSet = new Set(wildcardPaths);
|
|
4549
|
-
this.indexByWildcardPath = indexByWildcardPath;
|
|
4550
|
-
this.wildcardPathInfos = wildcardPathInfos;
|
|
4551
|
-
this.wildcardPathInfoSet = new Set(wildcardPathInfos);
|
|
4552
|
-
this.wildcardParentPaths = wildcardParentPaths;
|
|
4553
|
-
this.wildcardParentPathSet = new Set(wildcardParentPaths);
|
|
4554
|
-
this.wildcardParentPathInfos = wildcardParentPathInfos;
|
|
4555
|
-
this.wildcardParentPathInfoSet = new Set(wildcardParentPathInfos);
|
|
4556
|
-
this.wildcardPositions = wildcardPositions;
|
|
4557
|
-
this.lastWildcardPath = lastWildcardPath;
|
|
4558
|
-
this.lastWildcardInfo = lastWildcardPath ? getPattern(lastWildcardPath) : null;
|
|
4559
|
-
this.parentPath = parentPath;
|
|
4560
|
-
this.parentPathInfo = parentPath ? getPattern(parentPath) : null;
|
|
4561
|
-
this.wildcardCount = wildcardCount;
|
|
4562
4705
|
}
|
|
4563
|
-
|
|
4564
|
-
var LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
|
|
4565
|
-
var STRUCTURAL_BINDING_TYPE_SET2 = /* @__PURE__ */ new Set([
|
|
4566
|
-
"if",
|
|
4567
|
-
"elseif",
|
|
4568
|
-
"else",
|
|
4569
|
-
"for"
|
|
4570
|
-
]);
|
|
4571
|
-
var resolvedByKey = /* @__PURE__ */ new Map();
|
|
4572
|
-
function clearFilterResolutionCache() {
|
|
4573
|
-
resolvedByKey.clear();
|
|
4706
|
+
return out;
|
|
4574
4707
|
}
|
|
4575
|
-
function
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4708
|
+
function hasDotSegment(chain) {
|
|
4709
|
+
return /[.]/.test(chain.replace(/\s*(?:\?\.)?\s*\[[^\[\]]+\]/g, ""));
|
|
4710
|
+
}
|
|
4711
|
+
function isApiRoot(root) {
|
|
4712
|
+
return root.startsWith("$");
|
|
4713
|
+
}
|
|
4714
|
+
function execAllMasked(pattern, script) {
|
|
4715
|
+
const masked = maskCommentsAndStrings(script);
|
|
4716
|
+
const regex = new RegExp(pattern, "gd");
|
|
4717
|
+
const out = [];
|
|
4718
|
+
let match;
|
|
4719
|
+
while ((match = regex.exec(masked)) !== null) {
|
|
4720
|
+
const indices = match.indices;
|
|
4721
|
+
out.push({
|
|
4722
|
+
index: match.index,
|
|
4723
|
+
length: match[0].length,
|
|
4724
|
+
groups: indices.slice(1).map((pair) => pair === void 0 ? void 0 : script.slice(pair[0], pair[1]))
|
|
4725
|
+
});
|
|
4585
4726
|
}
|
|
4586
|
-
return
|
|
4727
|
+
return out;
|
|
4587
4728
|
}
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4729
|
+
|
|
4730
|
+
// src/service/nestedAssignValidator.ts
|
|
4731
|
+
var NESTED_ASSIGN = `${ROOT_DOT}(${CHAIN_ONE_PLUS})${ASSIGN_TAIL}`;
|
|
4732
|
+
var PRE_NESTED_INCDEC = `${PRE_INCDEC}${ROOT_DOT}(${CHAIN_ONE_PLUS})`;
|
|
4733
|
+
function validateNestedAssigns(html, stateTagName = "wcs-state", locale2) {
|
|
4734
|
+
const msgs = getMessages(locale2);
|
|
4735
|
+
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
4736
|
+
const diagnostics = [];
|
|
4737
|
+
for (const block of blocks) {
|
|
4738
|
+
findNestedAssigns(block.content, block.contentStart, msgs, diagnostics);
|
|
4739
|
+
}
|
|
4740
|
+
return diagnostics;
|
|
4599
4741
|
}
|
|
4600
|
-
function
|
|
4601
|
-
const
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
lastQuoteEnd = -1;
|
|
4616
|
-
};
|
|
4617
|
-
for (let i = 0; i < argsText.length; i++) {
|
|
4618
|
-
const char = argsText[i];
|
|
4619
|
-
if (inQuote) {
|
|
4620
|
-
if (char === inQuote) {
|
|
4621
|
-
inQuote = null;
|
|
4622
|
-
} else {
|
|
4623
|
-
if (firstQuoteStart === -1) {
|
|
4624
|
-
firstQuoteStart = current2.length;
|
|
4625
|
-
}
|
|
4626
|
-
current2 += char;
|
|
4627
|
-
lastQuoteEnd = current2.length;
|
|
4628
|
-
}
|
|
4629
|
-
} else if (char === '"' || char === "'") {
|
|
4630
|
-
inQuote = char;
|
|
4631
|
-
hasQuote = true;
|
|
4632
|
-
} else if (char === ",") {
|
|
4633
|
-
flush();
|
|
4634
|
-
} else {
|
|
4635
|
-
current2 += char;
|
|
4742
|
+
function findNestedAssigns(script, baseOffset, msgs, out) {
|
|
4743
|
+
for (const pattern of [NESTED_ASSIGN, PRE_NESTED_INCDEC]) {
|
|
4744
|
+
for (const match of execAllMasked(pattern, script)) {
|
|
4745
|
+
const [topProp, chainPart] = match.groups;
|
|
4746
|
+
if (topProp === void 0 || chainPart === void 0) continue;
|
|
4747
|
+
if (isApiRoot(topProp)) continue;
|
|
4748
|
+
if (!hasDotSegment(chainPart)) continue;
|
|
4749
|
+
const suggestedPath = topProp + chainToDotted(chainPart);
|
|
4750
|
+
const start = baseOffset + match.index;
|
|
4751
|
+
out.push({
|
|
4752
|
+
start,
|
|
4753
|
+
end: start + match.length,
|
|
4754
|
+
message: msgs.nestedAssign(suggestedPath),
|
|
4755
|
+
severity: "error"
|
|
4756
|
+
});
|
|
4636
4757
|
}
|
|
4637
4758
|
}
|
|
4638
|
-
if (inQuote !== null) {
|
|
4639
|
-
raiseError2(`[wcs/binding-syntax] unterminated ${inQuote} quote in the filter arguments "(${argsText})". Close the quote.${LINT_HINT}`);
|
|
4640
|
-
}
|
|
4641
|
-
const last = finalizeArg(current2, firstQuoteStart, lastQuoteEnd);
|
|
4642
|
-
if (last || hasQuote) {
|
|
4643
|
-
args.push(last);
|
|
4644
|
-
literals.push(toLiteral(last, hasQuote));
|
|
4645
|
-
}
|
|
4646
|
-
return { args, literals };
|
|
4647
4759
|
}
|
|
4648
|
-
|
|
4649
|
-
|
|
4760
|
+
|
|
4761
|
+
// src/service/arrayMutationValidator.ts
|
|
4762
|
+
var DESTRUCTIVE_METHODS = "push|pop|shift|unshift|splice|sort|reverse|fill|copyWithin";
|
|
4763
|
+
var ALTERNATIVES = {
|
|
4764
|
+
push: (a) => `${a} = ${a}.concat(item)`,
|
|
4765
|
+
unshift: (a) => `${a} = [item, ...${a}]`,
|
|
4766
|
+
pop: (a) => `${a} = ${a}.slice(0, -1)`,
|
|
4767
|
+
shift: (a) => `${a} = ${a}.slice(1)`,
|
|
4768
|
+
splice: (a) => `${a} = ${a}.toSpliced(...)`,
|
|
4769
|
+
sort: (a) => `${a} = ${a}.toSorted(...)`,
|
|
4770
|
+
reverse: (a) => `${a} = ${a}.toReversed()`,
|
|
4771
|
+
fill: (a) => `${a} = ${a}.map(...)`,
|
|
4772
|
+
copyWithin: (a) => `${a} = ${a}.map(...)`
|
|
4773
|
+
};
|
|
4774
|
+
var METHOD_TAIL = String.raw`\s*\??\.\s*(${DESTRUCTIVE_METHODS})(?=\s*\()`;
|
|
4775
|
+
var DOT_ROOT_CALL = `${ROOT_DOT}(${CHAIN})${METHOD_TAIL}`;
|
|
4776
|
+
var BRACKET_ROOT_CALL = `${ROOT_BRACKET}(${CHAIN})${METHOD_TAIL}`;
|
|
4777
|
+
var DOT_INDEX_ASSIGN = `${ROOT_DOT}(${BRACKETS_ONLY})${ASSIGN_TAIL}`;
|
|
4778
|
+
var BRACKET_INDEX_ASSIGN = `${ROOT_BRACKET}(${BRACKETS_ONLY})${ASSIGN_TAIL}`;
|
|
4779
|
+
var PRE_DOT_INDEX = `${PRE_INCDEC}${ROOT_DOT}(${BRACKETS_ONLY})`;
|
|
4780
|
+
var PRE_BRACKET_INDEX = `${PRE_INCDEC}${ROOT_BRACKET}(${BRACKETS_ONLY})`;
|
|
4781
|
+
function toAccessor(path) {
|
|
4782
|
+
return /^[A-Za-z_]\w*$/.test(path) ? `this.${path}` : `this["${path}"]`;
|
|
4650
4783
|
}
|
|
4651
|
-
function
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
}
|
|
4661
|
-
const filterName = (openParenIndex === -1 ? filterText : filterText.substring(0, openParenIndex)).trim();
|
|
4662
|
-
if (filterName.length === 0) {
|
|
4663
|
-
raiseError2(`[wcs/binding-syntax] an empty filter in "${filterTextList.join("|")}" \u2014 remove the extra "|" or name the filter.${LINT_HINT}`);
|
|
4664
|
-
}
|
|
4665
|
-
if (openParenIndex === -1) {
|
|
4666
|
-
return { filterName, args: [], literals: [] };
|
|
4667
|
-
}
|
|
4668
|
-
const argsText = filterText.substring(openParenIndex + 1, closeParenIndex);
|
|
4669
|
-
return { filterName, ...parseFilterArgsWithLiterals(argsText) };
|
|
4670
|
-
});
|
|
4784
|
+
function validateArrayMutations(html, stateTagName = "wcs-state", locale2) {
|
|
4785
|
+
const msgs = getMessages(locale2);
|
|
4786
|
+
const blocks = parseWcsScriptBlocks(html, stateTagName);
|
|
4787
|
+
const diagnostics = [];
|
|
4788
|
+
for (const block of blocks) {
|
|
4789
|
+
findDestructiveCalls(block.content, block.contentStart, msgs, diagnostics);
|
|
4790
|
+
findIndexAssigns(block.content, block.contentStart, msgs, diagnostics);
|
|
4791
|
+
}
|
|
4792
|
+
return diagnostics;
|
|
4671
4793
|
}
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4794
|
+
function findDestructiveCalls(script, baseOffset, msgs, out) {
|
|
4795
|
+
for (const pattern of [DOT_ROOT_CALL, BRACKET_ROOT_CALL]) {
|
|
4796
|
+
for (const match of execAllMasked(pattern, script)) {
|
|
4797
|
+
const [root, chain, method] = match.groups;
|
|
4798
|
+
if (root === void 0 || chain === void 0 || method === void 0) continue;
|
|
4799
|
+
if (isApiRoot(root)) continue;
|
|
4800
|
+
const statePath = root + chainToDotted(chain);
|
|
4801
|
+
const start = baseOffset + match.index;
|
|
4802
|
+
out.push({
|
|
4803
|
+
code: WcsDiagnosticCode.ArrayMutation,
|
|
4804
|
+
start,
|
|
4805
|
+
end: start + match.length,
|
|
4806
|
+
message: msgs.arrayMutation(method, ALTERNATIVES[method](toAccessor(statePath))),
|
|
4807
|
+
severity: "error",
|
|
4808
|
+
statePath
|
|
4809
|
+
});
|
|
4685
4810
|
}
|
|
4686
4811
|
}
|
|
4687
|
-
return -1;
|
|
4688
4812
|
}
|
|
4689
|
-
function
|
|
4690
|
-
const
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4813
|
+
function findIndexAssigns(script, baseOffset, msgs, out) {
|
|
4814
|
+
for (const pattern of [DOT_INDEX_ASSIGN, BRACKET_INDEX_ASSIGN, PRE_DOT_INDEX, PRE_BRACKET_INDEX]) {
|
|
4815
|
+
for (const match of execAllMasked(pattern, script)) {
|
|
4816
|
+
const [root, chain] = match.groups;
|
|
4817
|
+
if (root === void 0 || chain === void 0) continue;
|
|
4818
|
+
if (isApiRoot(root)) continue;
|
|
4819
|
+
const suggestedPath = root + chainToDotted(chain);
|
|
4820
|
+
const start = baseOffset + match.index;
|
|
4821
|
+
out.push({
|
|
4822
|
+
code: WcsDiagnosticCode.ArrayIndexAssign,
|
|
4823
|
+
start,
|
|
4824
|
+
end: start + match.length,
|
|
4825
|
+
message: msgs.arrayIndexAssign(suggestedPath),
|
|
4826
|
+
severity: "error",
|
|
4827
|
+
statePath: suggestedPath
|
|
4828
|
+
});
|
|
4703
4829
|
}
|
|
4704
4830
|
}
|
|
4705
|
-
parts.push(text.slice(start));
|
|
4706
|
-
return parts;
|
|
4707
|
-
}
|
|
4708
|
-
var cacheFilterInfos$1 = /* @__PURE__ */ new Map();
|
|
4709
|
-
function clearPropPartCacheForTooling() {
|
|
4710
|
-
cacheFilterInfos$1.clear();
|
|
4711
4831
|
}
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
let
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
if (cacheFilterInfos$1.has(filtersText)) {
|
|
4722
|
-
filters = cacheFilterInfos$1.get(filtersText);
|
|
4723
|
-
} else {
|
|
4724
|
-
filterTexts = splitOutsideQuotes(filtersText, FILTER_SEPARATOR2).map(trimFn);
|
|
4725
|
-
filters = parseFilters(filterTexts);
|
|
4726
|
-
cacheFilterInfos$1.set(filtersText, filters);
|
|
4832
|
+
|
|
4833
|
+
// src/service/templateSyntax.ts
|
|
4834
|
+
function findAllMustacheSyntax(html) {
|
|
4835
|
+
const results = [];
|
|
4836
|
+
const regex = /\{\{\s*(.+?)\s*\}\}/g;
|
|
4837
|
+
let match;
|
|
4838
|
+
while ((match = regex.exec(html)) !== null) {
|
|
4839
|
+
if (isInsideTag(html, match.index, "script") || isInsideTag(html, match.index, "style")) {
|
|
4840
|
+
continue;
|
|
4727
4841
|
}
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4842
|
+
const expr = match[1];
|
|
4843
|
+
const exprStart = match.index + match[0].indexOf(expr);
|
|
4844
|
+
results.push({
|
|
4845
|
+
kind: "mustache",
|
|
4846
|
+
expression: expr,
|
|
4847
|
+
exprStart,
|
|
4848
|
+
exprEnd: exprStart + expr.length,
|
|
4849
|
+
matchStart: match.index,
|
|
4850
|
+
matchEnd: match.index + match[0].length,
|
|
4851
|
+
insideTemplate: isInsideTag(html, match.index, "template")
|
|
4852
|
+
});
|
|
4734
4853
|
}
|
|
4735
|
-
|
|
4736
|
-
const propSegments = propName.split(DELIMITER2).map(trimFn);
|
|
4737
|
-
const propModifiers = propModifiersText ? propModifiersText.split(",").map(trimFn) : [];
|
|
4738
|
-
return {
|
|
4739
|
-
propName,
|
|
4740
|
-
propSegments,
|
|
4741
|
-
propModifiers,
|
|
4742
|
-
inFilters: filters
|
|
4743
|
-
};
|
|
4854
|
+
return results;
|
|
4744
4855
|
}
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4856
|
+
function findAllCommentBindings(html, commentTextPrefix = "wcs-text") {
|
|
4857
|
+
const results = [];
|
|
4858
|
+
const escaped = commentTextPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4859
|
+
const regex = new RegExp(`<!--\\s*@@\\s*(?:${escaped})?\\s*:\\s*(.+?)\\s*-->`, "g");
|
|
4860
|
+
let match;
|
|
4861
|
+
while ((match = regex.exec(html)) !== null) {
|
|
4862
|
+
const expr = match[1];
|
|
4863
|
+
if (!expr) continue;
|
|
4864
|
+
const exprStart = match.index + match[0].indexOf(expr);
|
|
4865
|
+
results.push({
|
|
4866
|
+
kind: "comment",
|
|
4867
|
+
expression: expr,
|
|
4868
|
+
exprStart,
|
|
4869
|
+
exprEnd: exprStart + expr.length,
|
|
4870
|
+
matchStart: match.index,
|
|
4871
|
+
matchEnd: match.index + match[0].length,
|
|
4872
|
+
insideTemplate: isInsideTag(html, match.index, "template")
|
|
4873
|
+
});
|
|
4874
|
+
}
|
|
4875
|
+
return results;
|
|
4748
4876
|
}
|
|
4749
|
-
function
|
|
4750
|
-
const
|
|
4751
|
-
let
|
|
4752
|
-
let
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
filtersText = statePart.slice(pos + 1).trim();
|
|
4758
|
-
if (cacheFilterInfos.has(filtersText)) {
|
|
4759
|
-
filters = cacheFilterInfos.get(filtersText);
|
|
4877
|
+
function isInsideTag(html, offset2, tagName) {
|
|
4878
|
+
const tagRegex = new RegExp(`<(/?)${tagName}[\\s>]`, "gi");
|
|
4879
|
+
let depth = 0;
|
|
4880
|
+
let match;
|
|
4881
|
+
while ((match = tagRegex.exec(html)) !== null) {
|
|
4882
|
+
if (match.index > offset2) break;
|
|
4883
|
+
if (match[1]) {
|
|
4884
|
+
depth = Math.max(0, depth - 1);
|
|
4760
4885
|
} else {
|
|
4761
|
-
|
|
4762
|
-
filters = parseFilters(filterTexts);
|
|
4763
|
-
cacheFilterInfos.set(filtersText, filters);
|
|
4886
|
+
depth++;
|
|
4764
4887
|
}
|
|
4765
|
-
} else {
|
|
4766
|
-
stateAndPath = statePart.trim();
|
|
4767
|
-
}
|
|
4768
|
-
if (stateAndPath.indexOf("@") !== -1) {
|
|
4769
|
-
raiseError2(`"${stateAndPath}": the "@name" selector was removed in v2 \u2014 there is a single state tree. Mount the named state onto the tree (<wcs-state mount="...">) and read it by its path prefix instead.`);
|
|
4770
4888
|
}
|
|
4771
|
-
|
|
4772
|
-
const pathInfo = getPathInfo(statePathName);
|
|
4773
|
-
return {
|
|
4774
|
-
statePathName,
|
|
4775
|
-
statePathInfo: pathInfo,
|
|
4776
|
-
outFilters: filters
|
|
4777
|
-
};
|
|
4778
|
-
}
|
|
4779
|
-
var KEYWORDS_WITHOUT_MODIFIERS = /* @__PURE__ */ new Set([ELSE_KEYWORD2, "if", "elseif", "for", SPREAD_PROP2]);
|
|
4780
|
-
var EXPLICIT_PROPERTY_REJECTED_HEADS = /* @__PURE__ */ new Set([
|
|
4781
|
-
CLASS_NAMESPACE2,
|
|
4782
|
-
ATTR_NAMESPACE2,
|
|
4783
|
-
STYLE_NAMESPACE2,
|
|
4784
|
-
COMMAND_NAMESPACE2,
|
|
4785
|
-
EVENT_TOKEN_NAMESPACE2
|
|
4786
|
-
]);
|
|
4787
|
-
function splitBindTexts(bindText) {
|
|
4788
|
-
return splitOutsideQuotes(bindText, BINDING_SEPARATOR2);
|
|
4889
|
+
return depth > 0;
|
|
4789
4890
|
}
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4891
|
+
|
|
4892
|
+
// src/service/templateSyntaxValidator.ts
|
|
4893
|
+
function validateTemplateSyntax(html, stateTagName, bindAttrName = "data-wcs", locale2, fileReader, applicationSchema) {
|
|
4894
|
+
const diagnostics = [];
|
|
4895
|
+
const msgs = getMessages(locale2);
|
|
4896
|
+
const allPaths = mergeSchemaCandidates(getStatePathsFromHtml(html, stateTagName, fileReader), applicationSchema);
|
|
4897
|
+
const defaultSchema = applicationSchema;
|
|
4898
|
+
const missingVerdict = (path, displayPath, pathSet2, scoped) => {
|
|
4899
|
+
if (hasRecursionWildcard(path)) {
|
|
4900
|
+
return {
|
|
4901
|
+
code: WcsDiagnosticCode.RecursionUnsupported,
|
|
4902
|
+
severity: "error",
|
|
4903
|
+
message: msgs.recursionUnsupported(path, "binding")
|
|
4904
|
+
};
|
|
4905
|
+
}
|
|
4906
|
+
if (isValidTemplatePath(path, pathSet2, scoped)) return null;
|
|
4907
|
+
if (defaultSchema !== void 0 && !path.startsWith("$")) {
|
|
4908
|
+
const resolution = resolveSchemaPath(defaultSchema, defaultSchema.$defs ?? {}, path.split("."));
|
|
4909
|
+
return resolution.kind === "nonexistent" ? { code: WcsDiagnosticCode.PathNonexistent, severity: "error", message: msgs.pathNonexistent(displayPath) } : null;
|
|
4910
|
+
}
|
|
4911
|
+
return { code: WcsDiagnosticCode.BindingPathMissing, severity: "warning", message: msgs.pathMissing(displayPath) };
|
|
4912
|
+
};
|
|
4913
|
+
if (allPaths.length === 0) return diagnostics;
|
|
4914
|
+
const defaultPaths = allPaths;
|
|
4915
|
+
const pathSet = new Set(defaultPaths.map((p) => p.path));
|
|
4916
|
+
const filterNameSet = new Set(BUILTIN_FILTERS.map((f) => f.name));
|
|
4917
|
+
const mustaches = findAllMustacheSyntax(html);
|
|
4918
|
+
const comments = findAllCommentBindings(html);
|
|
4919
|
+
for (const item of [...mustaches, ...comments]) {
|
|
4920
|
+
if (item.kind === "comment") {
|
|
4921
|
+
diagnostics.push({
|
|
4922
|
+
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4923
|
+
start: item.matchStart,
|
|
4924
|
+
end: item.matchEnd,
|
|
4925
|
+
message: msgs.wcsTextInfo(item.expression),
|
|
4926
|
+
severity: "info"
|
|
4927
|
+
});
|
|
4796
4928
|
}
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4929
|
+
if (item.kind === "mustache" && !item.insideTemplate) {
|
|
4930
|
+
diagnostics.push({
|
|
4931
|
+
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4932
|
+
start: item.matchStart,
|
|
4933
|
+
end: item.matchEnd,
|
|
4934
|
+
message: msgs.moustacheFouc(item.expression),
|
|
4935
|
+
severity: "info"
|
|
4936
|
+
});
|
|
4802
4937
|
}
|
|
4803
|
-
if (
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
};
|
|
4818
|
-
} else if (propPart === SPREAD_PROP2) {
|
|
4819
|
-
const stateResult = parseStatePart(statePart);
|
|
4820
|
-
if (stateResult.outFilters.length > 0) {
|
|
4821
|
-
raiseError2(`Invalid spread binding "${bindText2}": filters are not allowed on spread targets.`);
|
|
4938
|
+
if (!item.expression) continue;
|
|
4939
|
+
const parts = splitOutsideQuotes2(item.expression, "|");
|
|
4940
|
+
const pathPart = (parts[0] || "").trim();
|
|
4941
|
+
if (pathPart.includes("@")) continue;
|
|
4942
|
+
const insideFor = item.insideTemplate && isInsideForTemplate(html, item.matchStart, bindAttrName);
|
|
4943
|
+
if (pathPart && !/^-?\d|^["'`]|^true$|^false$|^null$/.test(pathPart)) {
|
|
4944
|
+
if (!insideFor && pathPart.includes("*")) {
|
|
4945
|
+
diagnostics.push({
|
|
4946
|
+
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4947
|
+
start: item.exprStart,
|
|
4948
|
+
end: item.exprStart + pathPart.length,
|
|
4949
|
+
message: msgs.patternPathOutsideFor(pathPart),
|
|
4950
|
+
severity: "warning"
|
|
4951
|
+
});
|
|
4822
4952
|
}
|
|
4823
|
-
if (
|
|
4824
|
-
|
|
4953
|
+
if (!insideFor && pathPart.startsWith(".")) {
|
|
4954
|
+
diagnostics.push({
|
|
4955
|
+
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4956
|
+
start: item.exprStart,
|
|
4957
|
+
end: item.exprStart + pathPart.length,
|
|
4958
|
+
message: msgs.omittedPathOutsideFor(pathPart),
|
|
4959
|
+
severity: "warning"
|
|
4960
|
+
});
|
|
4825
4961
|
}
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
inFilters: [],
|
|
4841
|
-
...stateResult,
|
|
4842
|
-
bindingType: propPart
|
|
4843
|
-
};
|
|
4844
|
-
} else if (keyword === "radio" || keyword === "checkbox") {
|
|
4845
|
-
const stateResult = parseStatePart(statePart);
|
|
4846
|
-
const propResult = parsePropPart(propPart);
|
|
4847
|
-
return {
|
|
4848
|
-
...propResult,
|
|
4849
|
-
...stateResult,
|
|
4850
|
-
bindingType: keyword
|
|
4851
|
-
};
|
|
4852
|
-
} else {
|
|
4853
|
-
const stateResult = parseStatePart(statePart);
|
|
4854
|
-
const propResult = parsePropPart(propPart);
|
|
4855
|
-
if (propResult.propSegments[0] === "" && propResult.propSegments.length > 1) {
|
|
4856
|
-
const propSegments = propResult.propSegments.slice(1);
|
|
4857
|
-
if (propSegments.includes("") || EXPLICIT_PROPERTY_REJECTED_HEADS.has(propSegments[0])) {
|
|
4858
|
-
raiseError2(`[wcs/binding-syntax] "${propPart}": a leading "." binds an element property by name \u2014 write a non-empty property that is not a namespace (${[...EXPLICIT_PROPERTY_REJECTED_HEADS].join(", ")}).${LINT_HINT}`);
|
|
4962
|
+
if (insideFor && !pathPart.startsWith(".")) {
|
|
4963
|
+
const indexMatch = /^\$(\d+)$/.exec(pathPart);
|
|
4964
|
+
const needed = indexMatch !== null ? Number(indexMatch[1]) : pathPart.includes("*") ? countWildcardSegments(pathPart) : 0;
|
|
4965
|
+
if (needed > 0) {
|
|
4966
|
+
const available = getAvailableWildcardRank(html, item.matchStart, bindAttrName);
|
|
4967
|
+
if (available > 0 && needed > available) {
|
|
4968
|
+
diagnostics.push({
|
|
4969
|
+
code: WcsDiagnosticCode.WildcardRank,
|
|
4970
|
+
start: item.exprStart,
|
|
4971
|
+
end: item.exprStart + pathPart.length,
|
|
4972
|
+
message: msgs.wildcardRank(`"${pathPart}"`, needed, available),
|
|
4973
|
+
severity: "warning"
|
|
4974
|
+
});
|
|
4975
|
+
}
|
|
4859
4976
|
}
|
|
4860
|
-
return {
|
|
4861
|
-
...propResult,
|
|
4862
|
-
propName: propSegments.join(DELIMITER2),
|
|
4863
|
-
propSegments,
|
|
4864
|
-
...stateResult,
|
|
4865
|
-
bindingType: "prop"
|
|
4866
|
-
};
|
|
4867
4977
|
}
|
|
4868
|
-
if (
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4978
|
+
if (/\.\d+\.|\.\d+$/.test(pathPart)) {
|
|
4979
|
+
diagnostics.push({
|
|
4980
|
+
code: WcsDiagnosticCode.TemplateSyntax,
|
|
4981
|
+
start: item.exprStart,
|
|
4982
|
+
end: item.exprStart + pathPart.length,
|
|
4983
|
+
message: msgs.resolvedPathInUi(pathPart),
|
|
4984
|
+
severity: "warning"
|
|
4985
|
+
});
|
|
4874
4986
|
}
|
|
4875
|
-
if (
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4987
|
+
if (pathPart.startsWith(".")) {
|
|
4988
|
+
const forPath = insideFor ? getInnermostForPath(html, item.matchStart, bindAttrName) : null;
|
|
4989
|
+
if (forPath && !forPath.startsWith(".")) {
|
|
4990
|
+
const expandedPath = pathPart === "." ? `${forPath}.*` : `${forPath}.*.${pathPart.slice(1)}`;
|
|
4991
|
+
const verdict = missingVerdict(expandedPath, pathPart, pathSet, defaultPaths);
|
|
4992
|
+
if (verdict) {
|
|
4993
|
+
diagnostics.push({
|
|
4994
|
+
code: verdict.code,
|
|
4995
|
+
start: item.exprStart,
|
|
4996
|
+
end: item.exprStart + pathPart.length,
|
|
4997
|
+
message: verdict.message + msgs.expansionSuffix(expandedPath),
|
|
4998
|
+
severity: verdict.severity
|
|
4999
|
+
});
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
4881
5002
|
} else {
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
5003
|
+
const verdict = missingVerdict(pathPart, pathPart, pathSet, defaultPaths);
|
|
5004
|
+
if (verdict) {
|
|
5005
|
+
diagnostics.push({
|
|
5006
|
+
code: verdict.code,
|
|
5007
|
+
start: item.exprStart,
|
|
5008
|
+
end: item.exprStart + pathPart.length,
|
|
5009
|
+
message: verdict.message,
|
|
5010
|
+
severity: verdict.severity
|
|
5011
|
+
});
|
|
5012
|
+
}
|
|
4887
5013
|
}
|
|
4888
5014
|
}
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
5015
|
+
let segmentStart = parts[0].length + 1;
|
|
5016
|
+
for (let i = 1; i < parts.length; i++) {
|
|
5017
|
+
const segment = parts[i];
|
|
5018
|
+
const filterName = segment.trim().replace(/\(.*$/, "");
|
|
5019
|
+
const filterOffset = segmentStart + (segment.length - segment.trimStart().length);
|
|
5020
|
+
segmentStart += segment.length + 1;
|
|
5021
|
+
const canonical = canonicalFilterName(filterName);
|
|
5022
|
+
if (filterName && canonical !== filterName && filterNameSet.has(canonical)) {
|
|
5023
|
+
diagnostics.push({
|
|
5024
|
+
code: WcsDiagnosticCode.NameAlias,
|
|
5025
|
+
start: item.exprStart + filterOffset,
|
|
5026
|
+
end: item.exprStart + filterOffset + filterName.length,
|
|
5027
|
+
message: msgs.nameAlias(filterName, canonical),
|
|
5028
|
+
severity: "info"
|
|
5029
|
+
});
|
|
5030
|
+
} else if (filterName && !filterNameSet.has(filterName)) {
|
|
5031
|
+
diagnostics.push({
|
|
5032
|
+
code: WcsDiagnosticCode.FilterUnknown,
|
|
5033
|
+
start: item.exprStart + filterOffset,
|
|
5034
|
+
end: item.exprStart + filterOffset + filterName.length,
|
|
5035
|
+
message: msgs.filterUnknown(filterName),
|
|
5036
|
+
severity: "warning"
|
|
5037
|
+
});
|
|
5038
|
+
}
|
|
4894
5039
|
}
|
|
4895
5040
|
}
|
|
4896
|
-
return
|
|
4897
|
-
}
|
|
4898
|
-
function parseBindTextForEmbeddedNode(bindText) {
|
|
4899
|
-
const stateResult = parseStatePart(bindText);
|
|
4900
|
-
return {
|
|
4901
|
-
propName: "textContent",
|
|
4902
|
-
propSegments: ["textContent"],
|
|
4903
|
-
propModifiers: [],
|
|
4904
|
-
inFilters: [],
|
|
4905
|
-
...stateResult,
|
|
4906
|
-
bindingType: "text"
|
|
4907
|
-
};
|
|
5041
|
+
return diagnostics;
|
|
4908
5042
|
}
|
|
4909
|
-
function
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
5043
|
+
function isValidTemplatePath(path, pathSet, scopedPaths) {
|
|
5044
|
+
if (/^\$\d+$/.test(path)) return true;
|
|
5045
|
+
if (path.startsWith("$streamStatus.") || path.startsWith("$streamError.")) {
|
|
5046
|
+
const prefix = path.startsWith("$streamStatus.") ? "$streamStatus." : "$streamError.";
|
|
5047
|
+
const hasNamespace = scopedPaths.some((p) => p.path.startsWith(prefix));
|
|
5048
|
+
return !hasNamespace || pathSet.has(path);
|
|
5049
|
+
}
|
|
5050
|
+
return pathSet.has(path) || matchesRecursionCandidates(scopedPaths, path, pathSet);
|
|
4914
5051
|
}
|
|
4915
5052
|
|
|
4916
5053
|
// src/core/parser/positionalParser.ts
|
|
@@ -4933,7 +5070,7 @@ function parseEmbeddedTextWithPositions(expression) {
|
|
|
4933
5070
|
if (parsed === null) {
|
|
4934
5071
|
return { exprRange, exprText: expression, parsed, error, propRange: null, pathRange: null };
|
|
4935
5072
|
}
|
|
4936
|
-
const firstPipe = expression
|
|
5073
|
+
const firstPipe = indexOfOutsideQuotes2(expression, delimiters.filter);
|
|
4937
5074
|
const pathScopeEnd = firstPipe === -1 ? expression.length : firstPipe;
|
|
4938
5075
|
const pathLocal = locate(expression, parsed.statePathName, 0, pathScopeEnd);
|
|
4939
5076
|
return {
|
|
@@ -4967,14 +5104,14 @@ function parseBindTextWithPositions(bindText) {
|
|
|
4967
5104
|
results.push({ exprRange, exprText: expr, parsed, error, propRange: null, pathRange: null });
|
|
4968
5105
|
continue;
|
|
4969
5106
|
}
|
|
4970
|
-
const colon = expr
|
|
5107
|
+
const colon = indexOfOutsideQuotes2(expr, delimiters.propValue);
|
|
4971
5108
|
const propEndLimit = colon === -1 ? expr.length : colon;
|
|
4972
5109
|
const propLocal = locate(expr, parsed.propName, 0, propEndLimit);
|
|
4973
5110
|
let pathLocal = null;
|
|
4974
5111
|
if (colon !== -1) {
|
|
4975
5112
|
const stateBase = colon + 1;
|
|
4976
|
-
const
|
|
4977
|
-
const pathScopeEnd =
|
|
5113
|
+
const rhsPipe = indexOfOutsideQuotes2(expr.slice(stateBase), delimiters.filter);
|
|
5114
|
+
const pathScopeEnd = rhsPipe === -1 ? expr.length : stateBase + rhsPipe;
|
|
4978
5115
|
pathLocal = locate(expr, parsed.statePathName, stateBase, pathScopeEnd);
|
|
4979
5116
|
}
|
|
4980
5117
|
const lift = (range) => range === null ? null : { start: exprStart + range.start, end: exprStart + range.end };
|
|
@@ -5016,13 +5153,13 @@ function validateBindingSyntax(html, bindAttrName = "data-wcs", locale2) {
|
|
|
5016
5153
|
});
|
|
5017
5154
|
}
|
|
5018
5155
|
}
|
|
5019
|
-
for (const
|
|
5020
|
-
const detail = bindingSyntaxDetail(parseEmbeddedTextWithPositions(
|
|
5156
|
+
for (const item of [...findAllMustacheSyntax(html), ...findAllCommentBindings(html)]) {
|
|
5157
|
+
const detail = bindingSyntaxDetail(parseEmbeddedTextWithPositions(item.expression).error);
|
|
5021
5158
|
if (detail === null) continue;
|
|
5022
5159
|
diagnostics.push({
|
|
5023
5160
|
code: WcsDiagnosticCode.BindingSyntax,
|
|
5024
|
-
start:
|
|
5025
|
-
end:
|
|
5161
|
+
start: item.exprStart,
|
|
5162
|
+
end: item.exprStart + item.expression.length,
|
|
5026
5163
|
message: msgs.bindingSyntax(detail),
|
|
5027
5164
|
severity: "error"
|
|
5028
5165
|
});
|
|
@@ -6317,6 +6454,13 @@ var DOM_COMMON_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
6317
6454
|
"className"
|
|
6318
6455
|
]);
|
|
6319
6456
|
var STRUCTURAL_DIRECTIVES2 = /* @__PURE__ */ new Set(["for", "if", "elseif", "else"]);
|
|
6457
|
+
var EXPLICIT_PROPERTY_REJECTED_HEADS2 = /* @__PURE__ */ new Set([
|
|
6458
|
+
...Object.values(getWcsManifest().syntax.bindingTypes.propNamespaces),
|
|
6459
|
+
"state"
|
|
6460
|
+
]);
|
|
6461
|
+
var EXPLICIT_PROPERTY_REJECTED_HEADS_RE = new RegExp(
|
|
6462
|
+
`^(?:${[...EXPLICIT_PROPERTY_REJECTED_HEADS2].map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(\\.|$)`
|
|
6463
|
+
);
|
|
6320
6464
|
var EMPTYISH_SEEDS = /* @__PURE__ */ new Set(["''", '""', "``", "null", "[]", "{}"]);
|
|
6321
6465
|
function validateIoNodes(html, bindAttribute = "data-wcs", stateTagName = "wcs-state", locale2, fileReader) {
|
|
6322
6466
|
const diagnostics = [];
|
|
@@ -6383,7 +6527,8 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
|
|
|
6383
6527
|
const explicit = property.startsWith(".") && property !== "...";
|
|
6384
6528
|
if (explicit) {
|
|
6385
6529
|
property = property.slice(1);
|
|
6386
|
-
if (
|
|
6530
|
+
if (property.length === 0 || property.startsWith(".")) return;
|
|
6531
|
+
if (EXPLICIT_PROPERTY_REJECTED_HEADS_RE.test(property)) return;
|
|
6387
6532
|
}
|
|
6388
6533
|
if (property === "...") return;
|
|
6389
6534
|
if (STRUCTURAL_DIRECTIVES2.has(property)) return;
|
|
@@ -6397,7 +6542,7 @@ function validateBindingAgainstContract(tagName, contract, parsed, property, sta
|
|
|
6397
6542
|
severity: "warning",
|
|
6398
6543
|
tag: tagName,
|
|
6399
6544
|
member: property,
|
|
6400
|
-
message: msgs.onPrefixedMember(property, tagName)
|
|
6545
|
+
message: msgs.onPrefixedMember(property, tagName, modifiers)
|
|
6401
6546
|
});
|
|
6402
6547
|
}
|
|
6403
6548
|
return;
|
|
@@ -7168,12 +7313,12 @@ function createApiCallRegex(apis) {
|
|
|
7168
7313
|
}
|
|
7169
7314
|
|
|
7170
7315
|
// src/service/recursionValidator.ts
|
|
7171
|
-
var RECURSION_APIS = ["getAll", "setAll", "resolve", "postUpdate", "trackDependency"];
|
|
7316
|
+
var RECURSION_APIS = ["getAll", "setAll", "resolve", "postUpdate", "dependOn", "trackDependency"];
|
|
7172
7317
|
var UNSUPPORTED_API_SITE = {
|
|
7173
7318
|
$resolve: "resolve",
|
|
7174
7319
|
$postUpdate: "postUpdate",
|
|
7175
|
-
$
|
|
7176
|
-
$
|
|
7320
|
+
$dependOn: "dependOn",
|
|
7321
|
+
$trackDependency: "trackDependency"
|
|
7177
7322
|
};
|
|
7178
7323
|
var BRACKET_ASSIGNMENT = new RegExp(`${ROOT_BRACKET}${ASSIGN_TAIL}`, "g");
|
|
7179
7324
|
var PRE_BRACKET_INCDEC = new RegExp(`${PRE_INCDEC}${ROOT_BRACKET}`, "g");
|
|
@@ -7431,12 +7576,12 @@ function validateListKeys(script, offset2, msgs, out) {
|
|
|
7431
7576
|
}
|
|
7432
7577
|
}
|
|
7433
7578
|
function validateApiCalls(script, offset2, spec, getterSuffixes, undeclared, msgs, out) {
|
|
7434
|
-
const scan =
|
|
7579
|
+
const scan = maskCommentsAndStrings(script);
|
|
7435
7580
|
const regex = createApiCallRegex(RECURSION_APIS);
|
|
7436
7581
|
let match;
|
|
7437
7582
|
while ((match = regex.exec(scan)) !== null) {
|
|
7438
7583
|
const api = `$${match[1]}`;
|
|
7439
|
-
const parsed = splitCallArgs(
|
|
7584
|
+
const parsed = splitCallArgs(script, match.index + match[0].length);
|
|
7440
7585
|
if (parsed === null) continue;
|
|
7441
7586
|
regex.lastIndex = parsed.end;
|
|
7442
7587
|
if (parsed.args.length === 0) continue;
|
|
@@ -7589,19 +7734,10 @@ function isFunctionLiteral(arg) {
|
|
|
7589
7734
|
|
|
7590
7735
|
// src/service/namedStateValidator.ts
|
|
7591
7736
|
function findStateSelector(expr, embedded = false) {
|
|
7592
|
-
const colon = embedded ? -1 : expr
|
|
7737
|
+
const colon = embedded ? -1 : indexOfOutsideQuotes2(expr, ":");
|
|
7593
7738
|
const from = colon + 1;
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
for (let i = from; i < expr.length; i++) {
|
|
7597
|
-
const ch = expr[i];
|
|
7598
|
-
if (ch === "(") depth++;
|
|
7599
|
-
else if (ch === ")") depth = Math.max(0, depth - 1);
|
|
7600
|
-
else if (ch === "|" && depth === 0) {
|
|
7601
|
-
end = i;
|
|
7602
|
-
break;
|
|
7603
|
-
}
|
|
7604
|
-
}
|
|
7739
|
+
const pipe = indexOfOutsideQuotes2(expr.slice(from), "|");
|
|
7740
|
+
const end = pipe === -1 ? expr.length : from + pipe;
|
|
7605
7741
|
const at2 = expr.indexOf("@", from);
|
|
7606
7742
|
if (at2 === -1 || at2 >= end) return null;
|
|
7607
7743
|
const raw = expr.slice(at2 + 1, end);
|
|
@@ -13703,6 +13839,64 @@ function visitDestructure(pattern, prefix, scope, out) {
|
|
|
13703
13839
|
emit(out, segments, { form: "destructure", callee: false, written: false }, property.start, property.end);
|
|
13704
13840
|
}
|
|
13705
13841
|
}
|
|
13842
|
+
function collectThisMemberRefs(body, names) {
|
|
13843
|
+
let program;
|
|
13844
|
+
try {
|
|
13845
|
+
program = parse3(PREFIX + body + SUFFIX, { ecmaVersion: "latest", sourceType: "module" });
|
|
13846
|
+
} catch {
|
|
13847
|
+
return null;
|
|
13848
|
+
}
|
|
13849
|
+
const fn = unwrapWrapper(program);
|
|
13850
|
+
if (fn === null) return null;
|
|
13851
|
+
return walkThisMembers(fn.body, names, PREFIX.length);
|
|
13852
|
+
}
|
|
13853
|
+
var VALUE_PREFIX = "(async function* () {\n(";
|
|
13854
|
+
var VALUE_SUFFIX = ")\n})";
|
|
13855
|
+
function collectThisMemberRefsInValue(value, names) {
|
|
13856
|
+
let program;
|
|
13857
|
+
try {
|
|
13858
|
+
program = parse3(VALUE_PREFIX + value + VALUE_SUFFIX, { ecmaVersion: "latest", sourceType: "module" });
|
|
13859
|
+
} catch {
|
|
13860
|
+
return null;
|
|
13861
|
+
}
|
|
13862
|
+
const wrapper = unwrapWrapper(program);
|
|
13863
|
+
if (wrapper === null || wrapper.body.body.length !== 1) return null;
|
|
13864
|
+
const statement = wrapper.body.body[0];
|
|
13865
|
+
if (statement.type !== "ExpressionStatement") return null;
|
|
13866
|
+
const expression = statement.expression;
|
|
13867
|
+
if (expression.type !== "FunctionExpression") return [];
|
|
13868
|
+
return walkThisMembers(expression.body, names, VALUE_PREFIX.length);
|
|
13869
|
+
}
|
|
13870
|
+
function walkThisMembers(root, names, offsetShift) {
|
|
13871
|
+
const out = [];
|
|
13872
|
+
const walk = (node, thisIsState) => {
|
|
13873
|
+
if (isClassNode(node)) return;
|
|
13874
|
+
if (isFunctionNode(node)) {
|
|
13875
|
+
const inner = node.type === "ArrowFunctionExpression" ? thisIsState : false;
|
|
13876
|
+
forEachChild(node, (child) => walk(child, inner));
|
|
13877
|
+
return;
|
|
13878
|
+
}
|
|
13879
|
+
if (thisIsState && node.type === "MemberExpression" && node.object.type === "ThisExpression") {
|
|
13880
|
+
const hit = thisMemberName(node);
|
|
13881
|
+
if (hit !== null && names.has(hit.name)) {
|
|
13882
|
+
out.push({ name: hit.name, start: hit.start - offsetShift, end: hit.end - offsetShift });
|
|
13883
|
+
}
|
|
13884
|
+
}
|
|
13885
|
+
forEachChild(node, (child) => walk(child, thisIsState));
|
|
13886
|
+
};
|
|
13887
|
+
walk(root, true);
|
|
13888
|
+
return out;
|
|
13889
|
+
}
|
|
13890
|
+
function thisMemberName(node) {
|
|
13891
|
+
const property = node.property;
|
|
13892
|
+
if (!node.computed && property.type === "Identifier") {
|
|
13893
|
+
return { name: property.name, start: property.start, end: property.end };
|
|
13894
|
+
}
|
|
13895
|
+
if (node.computed && property.type === "Literal" && typeof property.value === "string") {
|
|
13896
|
+
return { name: property.value, start: property.start + 1, end: property.end - 1 };
|
|
13897
|
+
}
|
|
13898
|
+
return null;
|
|
13899
|
+
}
|
|
13706
13900
|
|
|
13707
13901
|
// src/core/index/referenceIndex.ts
|
|
13708
13902
|
function buildReferenceIndex(html, options = {}) {
|
|
@@ -13815,10 +14009,30 @@ var STATE_UPDATED_CALLBACKS = /* @__PURE__ */ new Set(["$renderedCallback", "$up
|
|
|
13815
14009
|
var OLD_API_NAMES = { $trackDependency: "$dependOn", $untrackDependency: "$untracked" };
|
|
13816
14010
|
var OLD_DECLARATION_KEYS = { $streams: "$stream", $updatedCallback: "$renderedCallback" };
|
|
13817
14011
|
var OLD_API_CALL = /\.\s*(\$trackDependency|\$untrackDependency)\b/g;
|
|
13818
|
-
var
|
|
14012
|
+
var OLD_DECLARATION_MEMBER = /\.\s*(\$streams|\$updatedCallback)\b/g;
|
|
14013
|
+
var OLD_DECLARATION_MEMBER_NAMES = new Set(Object.keys(OLD_DECLARATION_KEYS));
|
|
14014
|
+
var OLD_DECLARATION_KEY = /(^|[{,;\s])(\$streams|\$updatedCallback)(?=\s*(?:[:(]|=(?!=)))/g;
|
|
14015
|
+
function collectDeclarationAliasReads(script) {
|
|
14016
|
+
if (!hasDefaultExportObject(script)) return null;
|
|
14017
|
+
const out = [];
|
|
14018
|
+
const take = (refs, base) => {
|
|
14019
|
+
if (refs === null) return false;
|
|
14020
|
+
for (const ref2 of refs) out.push({ name: ref2.name, start: base + ref2.start });
|
|
14021
|
+
return true;
|
|
14022
|
+
};
|
|
14023
|
+
for (const callable of analyzeCallableBodies(script)) {
|
|
14024
|
+
if (!take(collectThisMemberRefs(callable.body, OLD_DECLARATION_MEMBER_NAMES), callable.bodyStart)) return null;
|
|
14025
|
+
}
|
|
14026
|
+
for (const handler of analyzeWatchHandlerSources(script)) {
|
|
14027
|
+
const refs = handler.kind === "body" ? collectThisMemberRefs(handler.text, OLD_DECLARATION_MEMBER_NAMES) : collectThisMemberRefsInValue(handler.text, OLD_DECLARATION_MEMBER_NAMES);
|
|
14028
|
+
if (!take(refs, handler.start)) return null;
|
|
14029
|
+
}
|
|
14030
|
+
out.sort((a, b) => a.start - b.start);
|
|
14031
|
+
return out;
|
|
14032
|
+
}
|
|
13819
14033
|
function validateNameAliases(script, scriptStart, locale2) {
|
|
13820
14034
|
const msgs = getMessages(locale2);
|
|
13821
|
-
const scan =
|
|
14035
|
+
const scan = maskCommentsAndStrings(script);
|
|
13822
14036
|
const out = [];
|
|
13823
14037
|
const push2 = (name, canonical, offset2) => {
|
|
13824
14038
|
out.push({
|
|
@@ -13829,14 +14043,55 @@ function validateNameAliases(script, scriptStart, locale2) {
|
|
|
13829
14043
|
severity: "info"
|
|
13830
14044
|
});
|
|
13831
14045
|
};
|
|
14046
|
+
const pushRead = (name, offset2, shape) => {
|
|
14047
|
+
const canonical = OLD_DECLARATION_KEYS[name];
|
|
14048
|
+
out.push({
|
|
14049
|
+
code: WcsDiagnosticCode.DeclarationAliasRead,
|
|
14050
|
+
start: scriptStart + offset2,
|
|
14051
|
+
end: scriptStart + offset2 + name.length,
|
|
14052
|
+
message: shape === "own" ? msgs.declarationAliasRead(name, canonical) : msgs.declarationAliasReadUncertain(name, canonical),
|
|
14053
|
+
severity: shape === "own" ? "warning" : "info"
|
|
14054
|
+
});
|
|
14055
|
+
};
|
|
13832
14056
|
OLD_API_CALL.lastIndex = 0;
|
|
13833
14057
|
let match;
|
|
13834
14058
|
while ((match = OLD_API_CALL.exec(scan)) !== null) {
|
|
13835
|
-
|
|
14059
|
+
const written = match[1];
|
|
14060
|
+
push2(written, OLD_API_NAMES[written], match.index + match[0].length - written.length);
|
|
14061
|
+
}
|
|
14062
|
+
const astReads = collectDeclarationAliasReads(script);
|
|
14063
|
+
if (astReads !== null) {
|
|
14064
|
+
for (const read of astReads) pushRead(read.name, read.start, "own");
|
|
14065
|
+
} else {
|
|
14066
|
+
OLD_DECLARATION_MEMBER.lastIndex = 0;
|
|
14067
|
+
while ((match = OLD_DECLARATION_MEMBER.exec(scan)) !== null) {
|
|
14068
|
+
const written = match[1];
|
|
14069
|
+
pushRead(written, match.index + match[0].length - written.length, "unknown");
|
|
14070
|
+
}
|
|
14071
|
+
}
|
|
14072
|
+
const spans = analyzeDeclarationSpans(script);
|
|
14073
|
+
if (spans.length === 0) {
|
|
14074
|
+
OLD_DECLARATION_KEY.lastIndex = 0;
|
|
14075
|
+
while ((match = OLD_DECLARATION_KEY.exec(scan)) !== null) {
|
|
14076
|
+
push2(match[2], OLD_DECLARATION_KEYS[match[2]], match.index + match[1].length);
|
|
14077
|
+
}
|
|
14078
|
+
return out;
|
|
13836
14079
|
}
|
|
13837
|
-
|
|
13838
|
-
|
|
13839
|
-
|
|
14080
|
+
const declared = new Set(spans.map((span) => span.name));
|
|
14081
|
+
for (const span of spans) {
|
|
14082
|
+
const canonical = OLD_DECLARATION_KEYS[span.name];
|
|
14083
|
+
if (canonical === void 0) continue;
|
|
14084
|
+
if (declared.has(canonical)) {
|
|
14085
|
+
out.push({
|
|
14086
|
+
code: WcsDiagnosticCode.DeclarationAlias,
|
|
14087
|
+
start: scriptStart + span.start,
|
|
14088
|
+
end: scriptStart + span.end,
|
|
14089
|
+
message: msgs.declarationAlias(span.name, canonical),
|
|
14090
|
+
severity: "error"
|
|
14091
|
+
});
|
|
14092
|
+
continue;
|
|
14093
|
+
}
|
|
14094
|
+
push2(span.name, canonical, span.start);
|
|
13840
14095
|
}
|
|
13841
14096
|
return out;
|
|
13842
14097
|
}
|
|
@@ -13844,9 +14099,10 @@ var API_CALL = /\.\s*\$(getAll|setAll|resolve)\s*\(/g;
|
|
|
13844
14099
|
function validateIndexArity(script, scriptStart, locale2) {
|
|
13845
14100
|
const msgs = getMessages(locale2);
|
|
13846
14101
|
const out = [];
|
|
14102
|
+
const scan = maskCommentsAndStrings(script);
|
|
13847
14103
|
API_CALL.lastIndex = 0;
|
|
13848
14104
|
let match;
|
|
13849
|
-
while ((match = API_CALL.exec(
|
|
14105
|
+
while ((match = API_CALL.exec(scan)) !== null) {
|
|
13850
14106
|
const api = `$${match[1]}`;
|
|
13851
14107
|
const parsed = splitCallArgs(script, match.index + match[0].length);
|
|
13852
14108
|
if (parsed === null) continue;
|
|
@@ -13996,18 +14252,21 @@ function collectNestedWriteRoots(html, stateTagName, bindAttrName, blocks) {
|
|
|
13996
14252
|
}
|
|
13997
14253
|
for (const block of blocks) {
|
|
13998
14254
|
if (block.mountPath !== null) addPrefixes(block.mountPath, true);
|
|
13999
|
-
const scan =
|
|
14255
|
+
const scan = maskCommentsAndStrings(block.content);
|
|
14000
14256
|
for (const regex of [BRACKET_WRITE, PRE_BRACKET_INCDEC2]) {
|
|
14001
14257
|
regex.lastIndex = 0;
|
|
14002
14258
|
let match;
|
|
14003
|
-
while ((match = regex.exec(scan)) !== null)
|
|
14259
|
+
while ((match = regex.exec(scan)) !== null) {
|
|
14260
|
+
const keyStart = match.index + match[0].search(/["']/) + 1;
|
|
14261
|
+
addPrefixes(block.content.slice(keyStart, keyStart + match[1].length), false);
|
|
14262
|
+
}
|
|
14004
14263
|
}
|
|
14005
14264
|
API_CALL.lastIndex = 0;
|
|
14006
14265
|
let call;
|
|
14007
14266
|
while ((call = API_CALL.exec(scan)) !== null) {
|
|
14008
14267
|
const api = call[1];
|
|
14009
14268
|
if (api === "getAll") continue;
|
|
14010
|
-
const parsed = splitCallArgs(
|
|
14269
|
+
const parsed = splitCallArgs(block.content, call.index + call[0].length);
|
|
14011
14270
|
if (parsed === null) continue;
|
|
14012
14271
|
API_CALL.lastIndex = parsed.end;
|
|
14013
14272
|
const path = parsed.args.length > 0 ? literalString(parsed.args[0]) : null;
|