@wcstack/state 3.5.4 → 4.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +669 -428
- package/README.md +667 -426
- package/dist/auto.min.js +1 -2
- package/dist/define.d.ts +4 -8
- package/dist/define.js +1 -1
- package/dist/index.d.ts +139 -1483
- package/dist/index.esm.js +1 -2
- package/dist/manifest.d.ts +18 -59
- package/dist/manifest.esm.js +542 -1202
- package/dist/parser.d.ts +49 -103
- package/dist/parser.esm.js +662 -831
- package/dist/split/auto.js +1 -0
- package/dist/split/chunks/chunk-732CB4QN.js +1 -0
- package/dist/split/chunks/chunk-7PN5YNZZ.js +1 -0
- package/dist/split/chunks/chunk-FF6KHTYS.js +1 -0
- package/dist/split/chunks/chunk-HFKGUDDJ.js +1 -0
- package/dist/split/chunks/chunk-PKDBVBAP.js +1 -0
- package/dist/split/chunks/chunk-QI7P7YPP.js +1 -0
- package/dist/split/chunks/chunk-QQH6N5CJ.js +1 -0
- package/dist/split/chunks/chunk-UM3O6QXD.js +1 -0
- package/dist/split/chunks/chunk-WEL3YSG7.js +1 -0
- package/dist/split/chunks/chunk-WWJNAZVP.js +1 -0
- package/dist/split/chunks/chunk-YOPPVWPQ.js +1 -0
- package/dist/split/core.d.ts +114 -1168
- package/dist/split/core.js +1 -2
- package/dist/split/features/devtools.d.ts +23 -3
- package/dist/split/features/devtools.js +1 -2
- package/dist/split/features/diagnostics.d.ts +27 -5
- package/dist/split/features/diagnostics.js +1 -2
- package/dist/split/features/formats.d.ts +21 -5
- package/dist/split/features/formats.js +1 -2
- package/dist/split/features/list-keys.d.ts +24 -0
- package/dist/split/features/list-keys.js +1 -0
- package/dist/split/features/recursion.d.ts +23 -3
- package/dist/split/features/recursion.js +1 -2
- package/dist/split/features/scopes.d.ts +25 -4
- package/dist/split/features/scopes.js +1 -2
- package/dist/split/features/ssr.d.ts +23 -3
- package/dist/split/features/ssr.js +1 -2
- package/dist/split/features/temporal.d.ts +19 -4
- package/dist/split/features/temporal.js +1 -2
- package/dist/wcs-manifest.json +43 -49
- package/package.json +15 -17
- package/dist/auto.min.js.map +0 -1
- package/dist/index.esm.js.map +0 -1
- package/dist/split/chunks/binder.js +0 -2
- package/dist/split/chunks/binder.js.map +0 -1
- package/dist/split/chunks/checkDependency.js +0 -2
- package/dist/split/chunks/checkDependency.js.map +0 -1
- package/dist/split/chunks/declarationAliases.js +0 -2
- package/dist/split/chunks/declarationAliases.js.map +0 -1
- package/dist/split/chunks/declarationHooks.js +0 -2
- package/dist/split/chunks/declarationHooks.js.map +0 -1
- package/dist/split/chunks/diagnosticsHooks.js +0 -2
- package/dist/split/chunks/diagnosticsHooks.js.map +0 -1
- package/dist/split/chunks/errorGuidance.js +0 -2
- package/dist/split/chunks/errorGuidance.js.map +0 -1
- package/dist/split/chunks/featureBridge.js +0 -2
- package/dist/split/chunks/featureBridge.js.map +0 -1
- package/dist/split/chunks/features.d.ts +0 -29
- package/dist/split/chunks/filterRegistry.js +0 -2
- package/dist/split/chunks/filterRegistry.js.map +0 -1
- package/dist/split/chunks/getAllPropertyDescriptors.js +0 -2
- package/dist/split/chunks/getAllPropertyDescriptors.js.map +0 -1
- package/dist/split/chunks/lifecycleHooks.js +0 -2
- package/dist/split/chunks/lifecycleHooks.js.map +0 -1
- package/dist/split/chunks/pathDiagnostics.js +0 -2
- package/dist/split/chunks/pathDiagnostics.js.map +0 -1
- package/dist/split/chunks/registerComponents.js +0 -2
- package/dist/split/chunks/registerComponents.js.map +0 -1
- package/dist/split/chunks/setAll.js +0 -2
- package/dist/split/chunks/setAll.js.map +0 -1
- package/dist/split/chunks/version.js +0 -2
- package/dist/split/chunks/version.js.map +0 -1
- package/dist/split/core.js.map +0 -1
- package/dist/split/features/devtools.js.map +0 -1
- package/dist/split/features/diagnostics.js.map +0 -1
- package/dist/split/features/formats.js.map +0 -1
- package/dist/split/features/recursion.js.map +0 -1
- package/dist/split/features/scopes.js.map +0 -1
- package/dist/split/features/ssr.js.map +0 -1
- package/dist/split/features/temporal.js.map +0 -1
package/dist/manifest.esm.js
CHANGED
|
@@ -1,1240 +1,580 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
tagNames: {
|
|
4
|
-
state: 'wcs-state'},
|
|
5
|
-
locale: 'en'};
|
|
6
|
-
// backward compatible export (read-only usage)
|
|
7
|
-
const config = _config;
|
|
1
|
+
// src/hooks.ts
|
|
2
|
+
var hooks = {};
|
|
8
3
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// data-wcs バインディング構文 `[prop][#mod]: [path][|filter...]` の区切り文字(単一正本・`@state` は v2 で撤去)。
|
|
13
|
-
// これらは「死守の壁(構文契約)」であり値は不変。manifest.syntax.delimiters で公開される。
|
|
14
|
-
const BINDING_SEPARATOR = ';'; // 複数バインディングの区切り
|
|
15
|
-
const PROP_VALUE_SEPARATOR = ':'; // 左辺(prop)と右辺(path)の区切り
|
|
16
|
-
const MODIFIER_SEPARATOR = '#'; // prop と修飾子の区切り
|
|
17
|
-
const FILTER_SEPARATOR = '|'; // フィルタパイプの区切り
|
|
18
|
-
// 修飾子(`#` 後)の語彙(単一正本)。manifest.syntax.modifiers で公開される。
|
|
19
|
-
// フラグ形(`#prevent` — 値を取らない)とキー値形(`#init=element` — `=` で値を取る)。
|
|
20
|
-
// 消費箇所(event/handler・BindingSession・twowayHandler・bindings/initialSync)は
|
|
21
|
-
// この定数を参照する — 文字列リテラルの散在は tooling への収載漏れの温床だった
|
|
22
|
-
// (docs/static-wiring-dx-design.md §2-2)。
|
|
23
|
-
const MODIFIER_PREVENT = 'prevent';
|
|
24
|
-
const MODIFIER_STOP = 'stop';
|
|
25
|
-
const MODIFIER_READONLY = 'ro';
|
|
26
|
-
const MODIFIER_FLAGS = Object.freeze([
|
|
27
|
-
MODIFIER_PREVENT, MODIFIER_STOP, MODIFIER_READONLY,
|
|
28
|
-
]);
|
|
29
|
-
const MODIFIER_KEY_INIT = 'init';
|
|
30
|
-
const MODIFIER_KEY_SYNC = 'sync';
|
|
31
|
-
const MODIFIER_KEYS = Object.freeze([
|
|
32
|
-
MODIFIER_KEY_INIT, MODIFIER_KEY_SYNC,
|
|
33
|
-
]);
|
|
34
|
-
// bindingType 判別と左辺 namespace の語彙(単一正本)。manifest.syntax.bindingTypes で
|
|
35
|
-
// 公開される。パーサ(parseBindTextsForElement)とイベント層はこの定数に分岐する。
|
|
36
|
-
// apply 層のディスパッチマップ(apply/applyChange.ts の applyChangeByFirstSegment)の
|
|
37
|
-
// キー集合との一致は __tests__/manifest.test.ts の drift テストが強制する —
|
|
38
|
-
// manifest エントリ(DOM 非依存)から apply 層を import しないための分離。
|
|
39
|
-
const ELSE_KEYWORD = 'else';
|
|
40
|
-
const SPREAD_PROP = '...';
|
|
41
|
-
const EVENT_PROP_PREFIX = 'on';
|
|
42
|
-
const EVENT_TOKEN_NAMESPACE = 'eventToken';
|
|
43
|
-
const COMMAND_NAMESPACE = 'command';
|
|
44
|
-
const CLASS_NAMESPACE = 'class';
|
|
45
|
-
const ATTR_NAMESPACE = 'attr';
|
|
46
|
-
const STYLE_NAMESPACE = 'style';
|
|
47
|
-
// リストインデックス参照名(`$1`..`$N`)の接頭辞(単一正本)。
|
|
48
|
-
// manifest.syntax.indexParam で公開される。
|
|
49
|
-
const INDEX_PARAM_PREFIX = '$';
|
|
50
|
-
/**
|
|
51
|
-
* stackIndexByIndexName
|
|
52
|
-
* インデックス名からスタックインデックスへのマッピング
|
|
53
|
-
* $1 => 0
|
|
54
|
-
* $2 => 1
|
|
55
|
-
* :
|
|
56
|
-
* ${i + 1} => i
|
|
57
|
-
* i < MAX_WILDCARD_DEPTH
|
|
58
|
-
*/
|
|
59
|
-
const tmpIndexByIndexName = {};
|
|
60
|
-
for (let i = 0; i < MAX_WILDCARD_DEPTH; i++) {
|
|
61
|
-
tmpIndexByIndexName[`${INDEX_PARAM_PREFIX}${i + 1}`] = i;
|
|
4
|
+
// src/parser/raiseError.ts
|
|
5
|
+
function raiseError(message, subject, candidates) {
|
|
6
|
+
throw new Error(`[@wcstack/state] ${message}${hooks.explain?.(message, subject, candidates) ?? ""}`);
|
|
62
7
|
}
|
|
63
|
-
Object.freeze(tmpIndexByIndexName);
|
|
64
|
-
const STATE_CONNECTED_CALLBACK_NAME = "$connectedCallback";
|
|
65
|
-
const STATE_DISCONNECTED_CALLBACK_NAME = "$disconnectedCallback";
|
|
66
|
-
/** 旧名 `$updatedCallback` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
|
|
67
|
-
const STATE_RENDERED_CALLBACK_NAME = "$renderedCallback";
|
|
68
|
-
const STATE_ERROR_CALLBACK_NAME = "$errorCallback";
|
|
69
|
-
const WEBCOMPONENT_STATE_READY_CALLBACK_NAME = "$stateReadyCallback";
|
|
70
|
-
const STATE_BINDABLES_NAME = "$bindables";
|
|
71
|
-
const STATE_COMMANDS_NAME = "$commands";
|
|
72
|
-
const STATE_COMMAND_TOKENS_NAME = "$commandTokens";
|
|
73
|
-
const STATE_COMMAND_NAMESPACE_NAME = "$command";
|
|
74
|
-
const STATE_EVENT_TOKENS_NAME = "$eventTokens";
|
|
75
|
-
const STATE_ON_NAME = "$on";
|
|
76
|
-
/** 旧名 `$streams` は 3.x の間のエイリアス(要件 B12・declarationAliases.ts) */
|
|
77
|
-
const STATE_STREAM_NAME = "$stream";
|
|
78
|
-
const STATE_WATCH_NAME = "$watch";
|
|
79
|
-
const STATE_SCAN_NAME = "$scan";
|
|
80
|
-
const STATE_RECURSION_NAME = "$recursion";
|
|
81
|
-
const STATE_LIST_KEYS_NAME = "$listKeys";
|
|
82
|
-
const STATE_STREAM_STATUS_NAMESPACE_NAME = "$streamStatus";
|
|
83
|
-
const STATE_STREAM_ERROR_NAMESPACE_NAME = "$streamError";
|
|
84
8
|
|
|
85
|
-
|
|
86
|
-
|
|
9
|
+
// src/messages.ts
|
|
10
|
+
var CODES = [
|
|
11
|
+
"",
|
|
12
|
+
"binding-syntax",
|
|
13
|
+
"template-syntax",
|
|
14
|
+
"binding-path-missing",
|
|
15
|
+
"binding-type-expectation",
|
|
16
|
+
"filter-unknown",
|
|
17
|
+
"filter-arity",
|
|
18
|
+
"getter-cycle",
|
|
19
|
+
"getter-depth-exceeded",
|
|
20
|
+
"index-arity",
|
|
21
|
+
"index-param-range",
|
|
22
|
+
"recursion-unsupported",
|
|
23
|
+
"token-misconfigured",
|
|
24
|
+
"token-undeclared",
|
|
25
|
+
"wildcard-rank",
|
|
26
|
+
"spread-no-bindable",
|
|
27
|
+
"declaration-alias",
|
|
28
|
+
"name-alias"
|
|
29
|
+
];
|
|
30
|
+
var codeOf = (id) => {
|
|
31
|
+
const c = CODES[id / 100 | 0];
|
|
32
|
+
return c ? `[wcs/${c}] ` : "";
|
|
33
|
+
};
|
|
34
|
+
function text(id, args = []) {
|
|
35
|
+
return hooks.render?.(id, args) ?? `${codeOf(id)}#${id}${args.map((a) => ` ${typeof a === "string" ? JSON.stringify(a) : String(a)}`).join("")}`;
|
|
36
|
+
}
|
|
37
|
+
function raise(id, args, subject, candidates) {
|
|
38
|
+
raiseError(text(id, args), subject, candidates);
|
|
87
39
|
}
|
|
88
40
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
$streams: STATE_STREAM_NAME,
|
|
41
|
+
// src/config.ts
|
|
42
|
+
var config = {
|
|
43
|
+
bindAttributeName: "data-wcs",
|
|
44
|
+
commentForPrefix: "wcs-for",
|
|
45
|
+
commentIfPrefix: "wcs-if",
|
|
46
|
+
commentElseIfPrefix: "wcs-elseif",
|
|
47
|
+
commentElsePrefix: "wcs-else",
|
|
48
|
+
tagNames: { state: "wcs-state", ssr: "wcs-ssr" },
|
|
49
|
+
locale: typeof document !== "undefined" ? document.documentElement?.lang || "en" : "en",
|
|
50
|
+
enableContractAnalyzer: false
|
|
100
51
|
};
|
|
101
52
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
*
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
* - Takes function name as argument to specify which filter caused the error
|
|
110
|
-
*
|
|
111
|
-
* Design points:
|
|
112
|
-
* - optionsRequired: Error when required option is not specified
|
|
113
|
-
* - optionMustBeNumber: Error when option value is not a number
|
|
114
|
-
* - valueMustBeNumber: Error when value is not a number
|
|
115
|
-
* - valueMustBeDate: Error when value is not a Date
|
|
116
|
-
* - valueMustBeArray: Error when value is not an array
|
|
117
|
-
*/
|
|
118
|
-
/**
|
|
119
|
-
* Throws error when filter requires at least one option but none provided.
|
|
120
|
-
*
|
|
121
|
-
* @param fnName - Name of the filter function
|
|
122
|
-
* @returns Never returns (always throws)
|
|
123
|
-
*/
|
|
53
|
+
// src/load.ts
|
|
54
|
+
var FEATURE_NAMES = ["formats", "diagnostics", "temporal", "list-keys", "scopes", "recursion", "ssr", "devtools"];
|
|
55
|
+
|
|
56
|
+
// src/pattern.ts
|
|
57
|
+
var WILDCARD = "*";
|
|
58
|
+
|
|
59
|
+
// src/filters/errorMessages.ts
|
|
124
60
|
function optionsRequired(fnName) {
|
|
125
|
-
|
|
61
|
+
raise(26 /* FilterOptionsRequired */, [fnName]);
|
|
126
62
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Throws error when filter option must be a number but invalid value provided.
|
|
129
|
-
*
|
|
130
|
-
* @param fnName - Name of the filter function
|
|
131
|
-
* @returns Never returns (always throws)
|
|
132
|
-
*/
|
|
133
63
|
function optionMustBeNumber(fnName) {
|
|
134
|
-
|
|
64
|
+
raise(27 /* FilterOptionNotNumber */, [fnName]);
|
|
135
65
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Throws error when filter requires numeric value but non-number provided.
|
|
138
|
-
*
|
|
139
|
-
* @param fnName - Name of the filter function
|
|
140
|
-
* @returns Never returns (always throws)
|
|
141
|
-
*/
|
|
142
66
|
function valueMustBeNumber(fnName) {
|
|
143
|
-
|
|
67
|
+
raise(28 /* FilterValueNotNumber */, [fnName]);
|
|
144
68
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Throws error when filter requires Date value but non-Date provided.
|
|
147
|
-
*
|
|
148
|
-
* @param fnName - Name of the filter function
|
|
149
|
-
* @returns Never returns (always throws)
|
|
150
|
-
*/
|
|
151
69
|
function valueMustBeDate(fnName) {
|
|
152
|
-
|
|
70
|
+
raise(29 /* FilterValueNotDate */, [fnName]);
|
|
153
71
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Throws error when filter requires array value but non-array provided.
|
|
156
|
-
*
|
|
157
|
-
* @param fnName - Name of the filter function
|
|
158
|
-
* @returns Never returns (always throws)
|
|
159
|
-
*/
|
|
160
72
|
function valueMustBeArray(fnName) {
|
|
161
|
-
|
|
73
|
+
raise(30 /* FilterValueNotArray */, [fnName]);
|
|
162
74
|
}
|
|
163
75
|
|
|
164
|
-
|
|
165
|
-
* filters/filterAliases.ts — 組み込みフィルタの旧名 → 正式名(要件 B12・docs/state-3x-naming.ja.md V1〜V9)。
|
|
166
|
-
*
|
|
167
|
-
* 旧名は 3.x の間エイリアスとして残り、4.0 で外す(D4)。解決は登録簿(core/filterRegistry)が行い、
|
|
168
|
-
* 実装・引数の個数・メタデータは正式名だけが持つ。formats の install と manifest(tooling)の両方が読むので、
|
|
169
|
-
* 実装にもメタデータにも依存しない小さな表として独立させている。
|
|
170
|
-
*/
|
|
171
|
-
const builtinFilterAliases = {
|
|
172
|
-
inc: "add",
|
|
173
|
-
dec: "sub",
|
|
174
|
-
fix: "toFixed",
|
|
175
|
-
uc: "upper",
|
|
176
|
-
lc: "lower",
|
|
177
|
-
cap: "capitalize",
|
|
178
|
-
rep: "repeat",
|
|
179
|
-
rev: "reverse",
|
|
180
|
-
pad: "padStart",
|
|
181
|
-
null: "nullIfEmpty",
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* builtinFilters.ts
|
|
186
|
-
*
|
|
187
|
-
* Implementation file for built-in filter functions available in Structive.
|
|
188
|
-
*
|
|
189
|
-
* Main responsibilities:
|
|
190
|
-
* - Provides filters for conversion, comparison, formatting, and validation of numbers, strings, dates, booleans, etc.
|
|
191
|
-
* - Defines functions with options for each filter name, enabling flexible use during binding
|
|
192
|
-
* - Designed for common use as both input and output filters
|
|
193
|
-
*
|
|
194
|
-
* Design points:
|
|
195
|
-
* - Comprehensive coverage of diverse filters: eq, ne, lt, gt, inc, abs, clamp, fix, locale, uc, lc, cap, trim, slice, pad, truncate, join, int, float, round, percent, unit, date, time, ymd, hms, falsy, truthy, defaults, boolean, number, string, null, etc.
|
|
196
|
-
* - Rich type checking and error handling for option values
|
|
197
|
-
* - Centralized management of filter functions with FilterWithOptions type, easy to extend
|
|
198
|
-
* - Dynamic retrieval of filter functions from filter names and options via builtinFilterFn
|
|
199
|
-
*/
|
|
76
|
+
// src/filters/options.ts
|
|
200
77
|
function validateNumberString(value) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
78
|
+
if (!value || isNaN(Number(value))) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
205
82
|
}
|
|
206
|
-
/**
|
|
207
|
-
* Reads one numeric option **at construction time** and returns the number itself.
|
|
208
|
-
*
|
|
209
|
-
* Every numeric filter used to write out the same three lines (`options?.[i] ?? optionsRequired`,
|
|
210
|
-
* `validateNumberString`, `optionMustBeNumber`) and then call `Number(opt)` again on **every
|
|
211
|
-
* apply**. Folding it here keeps the option checks in one place and takes the conversion off the
|
|
212
|
-
* hot path.
|
|
213
|
-
*/
|
|
214
83
|
function numberOption(value, fnName) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
84
|
+
if (!validateNumberString(value)) {
|
|
85
|
+
optionMustBeNumber(fnName);
|
|
86
|
+
}
|
|
87
|
+
return Number(value);
|
|
219
88
|
}
|
|
220
|
-
/** A required numeric option: missing → `optionsRequired`, non-numeric → `optionMustBeNumber`. */
|
|
221
89
|
function requiredNumberOption(options, index, fnName) {
|
|
222
|
-
|
|
90
|
+
return numberOption(options?.[index] ?? optionsRequired(fnName), fnName);
|
|
223
91
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
* `boolean` / `truthy` / `falsy` / `not` / `eq` / `ne`;
|
|
236
|
-
* - the conversions, whose whole job is to convert: `int` / `float` / `number` / `string`;
|
|
237
|
-
* - the filters that reject a value of the wrong type (the number, date and array families).
|
|
238
|
-
* Their throw is confined to the one binding and reported through `$errorCallback`
|
|
239
|
-
* (`apply/applyChangeFromBindings.ts`), which is a different quality of failure from silently
|
|
240
|
-
* painting the word "undefined" into the page.
|
|
241
|
-
*/
|
|
242
|
-
const nullishPassthrough = (factory) => (options) => {
|
|
243
|
-
const filterFn = factory(options);
|
|
244
|
-
// `== null` は null と undefined の両方(意図的な緩い比較)
|
|
245
|
-
return (value) => (value == null ? value : filterFn(value));
|
|
246
|
-
};
|
|
247
|
-
/**
|
|
248
|
-
* Equality filter - compares value with option.
|
|
249
|
-
*
|
|
250
|
-
* @param options - Array with comparison value as first element
|
|
251
|
-
* @returns Filter function that returns boolean
|
|
252
|
-
*/
|
|
253
|
-
const eq = (options, literals) => {
|
|
254
|
-
const opt = options?.[0] ?? optionsRequired('eq');
|
|
255
|
-
// The typed literal (B9): unquoted true / false / null / numbers are typed, quoted arguments are strings
|
|
256
|
-
const literal = literals !== undefined && literals.length > 0 ? literals[0] : opt;
|
|
257
|
-
return (value) => {
|
|
258
|
-
// Align types for comparison
|
|
259
|
-
if (typeof value === 'number') {
|
|
260
|
-
// A typed literal that is not a string compares as itself (B9). A number is never equal to
|
|
261
|
-
// `true` / `false` / `null`, so `selectedId|eq(null)` is simply false once the id is a
|
|
262
|
-
// number — it used to throw `optionMustBeNumber` on **every apply**, which broke every
|
|
263
|
-
// binding on a path that is sometimes null and sometimes numeric. Only an unquoted
|
|
264
|
-
// non-number (a bare string such as `eq(abc)`) still reports the type mismatch, and it has
|
|
265
|
-
// to stay inside the closure: `eq` accepts values of any type, so `status|eq(active)` is
|
|
266
|
-
// perfectly valid and cannot be rejected at construction the way `lt` / `add` can.
|
|
267
|
-
if (typeof literal !== 'string') {
|
|
268
|
-
return value === literal;
|
|
269
|
-
}
|
|
270
|
-
if (!validateNumberString(opt)) {
|
|
271
|
-
optionMustBeNumber('eq');
|
|
272
|
-
}
|
|
273
|
-
return value === Number(opt);
|
|
274
|
-
}
|
|
275
|
-
if (typeof value === 'string') {
|
|
276
|
-
return value === opt;
|
|
277
|
-
}
|
|
278
|
-
// Booleans, null and the rest compare with the typed literal: eq(true) matches true (B9)
|
|
92
|
+
function firstLiteral(opt, literals) {
|
|
93
|
+
return literals !== void 0 && literals.length > 0 ? literals[0] : opt;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/filters/core.ts
|
|
97
|
+
var eq = (options, literals) => {
|
|
98
|
+
const opt = options?.[0] ?? optionsRequired("eq");
|
|
99
|
+
const literal = firstLiteral(opt, literals);
|
|
100
|
+
return (value) => {
|
|
101
|
+
if (typeof value === "number") {
|
|
102
|
+
if (typeof literal !== "string") {
|
|
279
103
|
return value === literal;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
if (!validateNumberString(opt)) {
|
|
300
|
-
optionMustBeNumber('ne');
|
|
301
|
-
}
|
|
302
|
-
return value !== Number(opt);
|
|
303
|
-
}
|
|
304
|
-
if (typeof value === 'string') {
|
|
305
|
-
return value !== opt;
|
|
306
|
-
}
|
|
307
|
-
// Booleans, null and the rest compare with the typed literal (B9)
|
|
104
|
+
}
|
|
105
|
+
if (!validateNumberString(opt)) {
|
|
106
|
+
optionMustBeNumber("eq");
|
|
107
|
+
}
|
|
108
|
+
return value === Number(opt);
|
|
109
|
+
}
|
|
110
|
+
if (typeof value === "string") {
|
|
111
|
+
return value === opt;
|
|
112
|
+
}
|
|
113
|
+
return value === literal;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
var ne = (options, literals) => {
|
|
117
|
+
const opt = options?.[0] ?? optionsRequired("ne");
|
|
118
|
+
const literal = firstLiteral(opt, literals);
|
|
119
|
+
return (value) => {
|
|
120
|
+
if (typeof value === "number") {
|
|
121
|
+
if (typeof literal !== "string") {
|
|
308
122
|
return value !== literal;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
* Deliberately the same lenient `!value` as the engine-owned copy in
|
|
315
|
-
* `core/filterRegistry.ts` (`CORE_FILTERS.not`). `if:` / `else:` are built as one
|
|
316
|
-
* `if` parse result plus an engine-injected `not` (`structural/createNotFilter.ts`),
|
|
317
|
-
* and `apply/applyChangeToIf.ts` coerces the `if` side with `Boolean()`. A strict
|
|
318
|
-
* `not` therefore made `else:` throw — and render neither branch — whenever the
|
|
319
|
-
* condition was a falsy non-boolean (`0` / `""` / `undefined` / `null`). Keeping the
|
|
320
|
-
* two implementations identical also means a page behaves the same whether or not
|
|
321
|
-
* `features/formats` is installed (the split core entry registers no `not`).
|
|
322
|
-
*
|
|
323
|
-
* @param options - Unused
|
|
324
|
-
* @returns Filter function that returns the inverted truthiness
|
|
325
|
-
*/
|
|
326
|
-
const not = (_options) => {
|
|
327
|
-
return (value) => !value;
|
|
328
|
-
};
|
|
329
|
-
/**
|
|
330
|
-
* Less than filter - checks if value is less than option.
|
|
331
|
-
*
|
|
332
|
-
* @param options - Array with comparison number as first element
|
|
333
|
-
* @returns Filter function that returns boolean
|
|
334
|
-
*/
|
|
335
|
-
const lt = (options) => {
|
|
336
|
-
const opt = requiredNumberOption(options, 0, 'lt');
|
|
337
|
-
return (value) => {
|
|
338
|
-
if (typeof value !== 'number') {
|
|
339
|
-
valueMustBeNumber('lt');
|
|
340
|
-
}
|
|
341
|
-
return value < opt;
|
|
342
|
-
};
|
|
343
|
-
};
|
|
344
|
-
/**
|
|
345
|
-
* Less than or equal filter - checks if value is less than or equal to option.
|
|
346
|
-
*
|
|
347
|
-
* @param options - Array with comparison number as first element
|
|
348
|
-
* @returns Filter function that returns boolean
|
|
349
|
-
*/
|
|
350
|
-
const le = (options) => {
|
|
351
|
-
const opt = requiredNumberOption(options, 0, 'le');
|
|
352
|
-
return (value) => {
|
|
353
|
-
if (typeof value !== 'number') {
|
|
354
|
-
valueMustBeNumber('le');
|
|
355
|
-
}
|
|
356
|
-
return value <= opt;
|
|
357
|
-
};
|
|
358
|
-
};
|
|
359
|
-
/**
|
|
360
|
-
* Greater than filter - checks if value is greater than option.
|
|
361
|
-
*
|
|
362
|
-
* @param options - Array with comparison number as first element
|
|
363
|
-
* @returns Filter function that returns boolean
|
|
364
|
-
*/
|
|
365
|
-
const gt = (options) => {
|
|
366
|
-
const opt = requiredNumberOption(options, 0, 'gt');
|
|
367
|
-
return (value) => {
|
|
368
|
-
if (typeof value !== 'number') {
|
|
369
|
-
valueMustBeNumber('gt');
|
|
370
|
-
}
|
|
371
|
-
return value > opt;
|
|
372
|
-
};
|
|
373
|
-
};
|
|
374
|
-
/**
|
|
375
|
-
* Greater than or equal filter - checks if value is greater than or equal to option.
|
|
376
|
-
*
|
|
377
|
-
* @param options - Array with comparison number as first element
|
|
378
|
-
* @returns Filter function that returns boolean
|
|
379
|
-
*/
|
|
380
|
-
const ge = (options) => {
|
|
381
|
-
const opt = requiredNumberOption(options, 0, 'ge');
|
|
382
|
-
return (value) => {
|
|
383
|
-
if (typeof value !== 'number') {
|
|
384
|
-
valueMustBeNumber('ge');
|
|
385
|
-
}
|
|
386
|
-
return value >= opt;
|
|
387
|
-
};
|
|
388
|
-
};
|
|
389
|
-
/**
|
|
390
|
-
* Increment filter - adds option value to input value.
|
|
391
|
-
*
|
|
392
|
-
* @param options - Array with increment number as first element
|
|
393
|
-
* @returns Filter function that returns incremented number
|
|
394
|
-
*/
|
|
395
|
-
const inc = (options) => {
|
|
396
|
-
const opt = requiredNumberOption(options, 0, 'add');
|
|
397
|
-
return (value) => {
|
|
398
|
-
if (typeof value !== 'number') {
|
|
399
|
-
valueMustBeNumber('add');
|
|
400
|
-
}
|
|
401
|
-
return value + opt;
|
|
402
|
-
};
|
|
403
|
-
};
|
|
404
|
-
/**
|
|
405
|
-
* Decrement filter - subtracts option value from input value.
|
|
406
|
-
*
|
|
407
|
-
* @param options - Array with decrement number as first element
|
|
408
|
-
* @returns Filter function that returns decremented number
|
|
409
|
-
*/
|
|
410
|
-
const dec = (options) => {
|
|
411
|
-
const opt = requiredNumberOption(options, 0, 'sub');
|
|
412
|
-
return (value) => {
|
|
413
|
-
if (typeof value !== 'number') {
|
|
414
|
-
valueMustBeNumber('sub');
|
|
415
|
-
}
|
|
416
|
-
return value - opt;
|
|
417
|
-
};
|
|
418
|
-
};
|
|
419
|
-
/**
|
|
420
|
-
* Multiply filter - multiplies value by option.
|
|
421
|
-
*
|
|
422
|
-
* @param options - Array with multiplier number as first element
|
|
423
|
-
* @returns Filter function that returns multiplied number
|
|
424
|
-
*/
|
|
425
|
-
const mul = (options) => {
|
|
426
|
-
const opt = requiredNumberOption(options, 0, 'mul');
|
|
427
|
-
return (value) => {
|
|
428
|
-
if (typeof value !== 'number') {
|
|
429
|
-
valueMustBeNumber('mul');
|
|
430
|
-
}
|
|
431
|
-
return value * opt;
|
|
432
|
-
};
|
|
433
|
-
};
|
|
434
|
-
/**
|
|
435
|
-
* Divide filter - divides value by option.
|
|
436
|
-
*
|
|
437
|
-
* @param options - Array with divisor number as first element
|
|
438
|
-
* @returns Filter function that returns divided number
|
|
439
|
-
*/
|
|
440
|
-
const div = (options) => {
|
|
441
|
-
const opt = requiredNumberOption(options, 0, 'div');
|
|
442
|
-
return (value) => {
|
|
443
|
-
if (typeof value !== 'number') {
|
|
444
|
-
valueMustBeNumber('div');
|
|
445
|
-
}
|
|
446
|
-
return value / opt;
|
|
447
|
-
};
|
|
448
|
-
};
|
|
449
|
-
/**
|
|
450
|
-
* Modulo filter - returns remainder of division.
|
|
451
|
-
*
|
|
452
|
-
* @param options - Array with divisor number as first element
|
|
453
|
-
* @returns Filter function that returns remainder
|
|
454
|
-
*/
|
|
455
|
-
const mod = (options) => {
|
|
456
|
-
const opt = requiredNumberOption(options, 0, 'mod');
|
|
457
|
-
return (value) => {
|
|
458
|
-
if (typeof value !== 'number') {
|
|
459
|
-
valueMustBeNumber('mod');
|
|
460
|
-
}
|
|
461
|
-
return value % opt;
|
|
462
|
-
};
|
|
463
|
-
};
|
|
464
|
-
/**
|
|
465
|
-
* Absolute value filter - returns the magnitude of a number.
|
|
466
|
-
*
|
|
467
|
-
* @param options - Unused
|
|
468
|
-
* @returns Filter function that returns the absolute value
|
|
469
|
-
*/
|
|
470
|
-
const abs = (_options) => {
|
|
471
|
-
return (value) => {
|
|
472
|
-
if (typeof value !== 'number') {
|
|
473
|
-
valueMustBeNumber('abs');
|
|
474
|
-
}
|
|
475
|
-
return Math.abs(value);
|
|
476
|
-
};
|
|
477
|
-
};
|
|
478
|
-
/**
|
|
479
|
-
* Clamp filter - constrains a number to the inclusive range [min, max].
|
|
480
|
-
*
|
|
481
|
-
* Saturating conversion in the same family as round/floor/ceil, so it stays on
|
|
482
|
-
* the wire rather than in state. Pairs with `unit` for style bindings:
|
|
483
|
-
* `style.width: ratio|clamp(0,1)|percent(0)`.
|
|
484
|
-
*
|
|
485
|
-
* @param options - Array with minimum as first element and maximum as second (both required)
|
|
486
|
-
* @returns Filter function that returns the clamped number
|
|
487
|
-
*/
|
|
488
|
-
const clamp = (options) => {
|
|
489
|
-
const min = requiredNumberOption(options, 0, 'clamp');
|
|
490
|
-
const max = requiredNumberOption(options, 1, 'clamp');
|
|
491
|
-
return (value) => {
|
|
492
|
-
if (typeof value !== 'number') {
|
|
493
|
-
valueMustBeNumber('clamp');
|
|
494
|
-
}
|
|
495
|
-
return Math.min(Math.max(value, min), max);
|
|
496
|
-
};
|
|
497
|
-
};
|
|
498
|
-
/**
|
|
499
|
-
* Fixed decimal filter - formats number to fixed decimal places.
|
|
500
|
-
*
|
|
501
|
-
* @param options - Array with decimal places as first element (default: 0)
|
|
502
|
-
* @returns Filter function that returns formatted string
|
|
503
|
-
*/
|
|
504
|
-
const fix = (options) => {
|
|
505
|
-
const opt = numberOption(options?.[0] ?? "0", 'toFixed');
|
|
506
|
-
return (value) => {
|
|
507
|
-
if (typeof value !== 'number') {
|
|
508
|
-
valueMustBeNumber('toFixed');
|
|
509
|
-
}
|
|
510
|
-
return value.toFixed(opt);
|
|
511
|
-
};
|
|
512
|
-
};
|
|
513
|
-
/**
|
|
514
|
-
* Locale number filter - formats number according to locale.
|
|
515
|
-
*
|
|
516
|
-
* ロケール依存フィルタ(`locale` / `date` / `time` / `datetime`)は
|
|
517
|
-
* **明示引数だけを構築時に確定し、既定の `config.locale` は適用のたびに読む**。
|
|
518
|
-
*
|
|
519
|
-
* 以前は `options?.[0] ?? config.locale` を返り値の関数の**外**で解決していた。
|
|
520
|
-
* フィルタ関数はバインド構築時に一度だけ作られるので、これはロケールを
|
|
521
|
-
* クロージャに焼き込むことを意味する。`config.locale` の確定がバインド構築より
|
|
522
|
-
* 遅れると、それ以降どう直しても「同じページの中で日付だけ既定ロケール」が
|
|
523
|
-
* 永続し、しかも `config.locale` は依存グラフに載らないので再描画で回復もしない。
|
|
524
|
-
* 症状(日付だけ英語)は原因(起動順序)から遠く、追いにくい。
|
|
525
|
-
*
|
|
526
|
-
* 適用のたびに読めば、少なくとも**再適用されたバインドは回復する**。ロケールは
|
|
527
|
-
* 起動時に確定する前提(docs/i18n-design.md D1)なので通常この差は現れず、
|
|
528
|
-
* これは順序事故から復帰できるようにするための保険である。
|
|
529
|
-
*
|
|
530
|
-
* 明示引数(`|date(ja-JP)`)は構築時に固定でよい — バインド式の一部であり、
|
|
531
|
-
* 実行中に変わらない。
|
|
532
|
-
*
|
|
533
|
-
* @param options - Array with locale string as first element (default: config.locale)
|
|
534
|
-
* @returns Filter function that returns localized number string
|
|
535
|
-
*/
|
|
536
|
-
const locale = (options) => {
|
|
537
|
-
const explicit = options?.[0];
|
|
538
|
-
return (value) => {
|
|
539
|
-
if (typeof value !== 'number') {
|
|
540
|
-
valueMustBeNumber('locale');
|
|
541
|
-
}
|
|
542
|
-
return value.toLocaleString(explicit ?? config.locale);
|
|
543
|
-
};
|
|
544
|
-
};
|
|
545
|
-
/**
|
|
546
|
-
* Uppercase filter - converts string to uppercase.
|
|
547
|
-
*
|
|
548
|
-
* @param options - Unused
|
|
549
|
-
* @returns Filter function that returns uppercase string
|
|
550
|
-
*/
|
|
551
|
-
const uc = (_options) => {
|
|
552
|
-
return (value) => {
|
|
553
|
-
return String(value).toUpperCase();
|
|
554
|
-
};
|
|
555
|
-
};
|
|
556
|
-
/**
|
|
557
|
-
* Lowercase filter - converts string to lowercase.
|
|
558
|
-
*
|
|
559
|
-
* @param options - Unused
|
|
560
|
-
* @returns Filter function that returns lowercase string
|
|
561
|
-
*/
|
|
562
|
-
const lc = (_options) => {
|
|
563
|
-
return (value) => {
|
|
564
|
-
return String(value).toLowerCase();
|
|
565
|
-
};
|
|
566
|
-
};
|
|
567
|
-
/**
|
|
568
|
-
* Capitalize filter - capitalizes first character of string.
|
|
569
|
-
*
|
|
570
|
-
* @param options - Unused
|
|
571
|
-
* @returns Filter function that returns capitalized string
|
|
572
|
-
*/
|
|
573
|
-
const cap = (_options) => {
|
|
574
|
-
return (value) => {
|
|
575
|
-
const v = String(value);
|
|
576
|
-
if (v.length === 0) {
|
|
577
|
-
return v;
|
|
578
|
-
}
|
|
579
|
-
if (v.length === 1) {
|
|
580
|
-
return v.toUpperCase();
|
|
581
|
-
}
|
|
582
|
-
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
583
|
-
};
|
|
584
|
-
};
|
|
585
|
-
/**
|
|
586
|
-
* Trim filter - removes whitespace from both ends of string.
|
|
587
|
-
*
|
|
588
|
-
* @param options - Unused
|
|
589
|
-
* @returns Filter function that returns trimmed string
|
|
590
|
-
*/
|
|
591
|
-
const trim = (_options) => {
|
|
592
|
-
return (value) => {
|
|
593
|
-
return String(value).trim();
|
|
594
|
-
};
|
|
595
|
-
};
|
|
596
|
-
/**
|
|
597
|
-
* Slice filter - extracts portion of string from specified index.
|
|
598
|
-
*
|
|
599
|
-
* @param options - Array with start index and optional end index
|
|
600
|
-
* @returns Filter function that returns sliced string
|
|
601
|
-
*/
|
|
602
|
-
const slice = (options) => {
|
|
603
|
-
const numberedOpts = [requiredNumberOption(options, 0, 'slice')];
|
|
604
|
-
const opt2 = options?.[1];
|
|
605
|
-
if (typeof opt2 !== 'undefined') {
|
|
606
|
-
numberedOpts.push(numberOption(opt2, 'slice'));
|
|
123
|
+
}
|
|
124
|
+
if (!validateNumberString(opt)) {
|
|
125
|
+
optionMustBeNumber("ne");
|
|
126
|
+
}
|
|
127
|
+
return value !== Number(opt);
|
|
607
128
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
* Substring filter - extracts substring from specified position and length.
|
|
614
|
-
*
|
|
615
|
-
* @param options - Array with start index and length
|
|
616
|
-
* @returns Filter function that returns substring
|
|
617
|
-
*/
|
|
618
|
-
const substr = (options) => {
|
|
619
|
-
const opt1 = requiredNumberOption(options, 0, 'substr');
|
|
620
|
-
const opt2 = requiredNumberOption(options, 1, 'substr');
|
|
621
|
-
return (value) => {
|
|
622
|
-
return String(value).substr(opt1, opt2);
|
|
623
|
-
};
|
|
624
|
-
};
|
|
625
|
-
/**
|
|
626
|
-
* Pad filter - pads string to specified length from start.
|
|
627
|
-
*
|
|
628
|
-
* @param options - Array with target length and pad string (default: '0')
|
|
629
|
-
* @returns Filter function that returns padded string
|
|
630
|
-
*/
|
|
631
|
-
const pad = (options) => {
|
|
632
|
-
const opt1 = requiredNumberOption(options, 0, 'padStart');
|
|
633
|
-
const opt2 = options?.[1] ?? '0';
|
|
634
|
-
return (value) => {
|
|
635
|
-
return String(value).padStart(opt1, opt2);
|
|
636
|
-
};
|
|
637
|
-
};
|
|
638
|
-
/**
|
|
639
|
-
* padEnd filter - pads string to specified length from the end (the pair of `padStart`, requirement B12).
|
|
640
|
-
*
|
|
641
|
-
* @param options - Array with target length and pad string (default: ' ')
|
|
642
|
-
* @returns Filter function that returns padded string
|
|
643
|
-
*/
|
|
644
|
-
const padEnd = (options) => {
|
|
645
|
-
const opt1 = requiredNumberOption(options, 0, 'padEnd');
|
|
646
|
-
const opt2 = options?.[1] ?? ' ';
|
|
647
|
-
return (value) => {
|
|
648
|
-
return String(value).padEnd(opt1, opt2);
|
|
649
|
-
};
|
|
650
|
-
};
|
|
651
|
-
/**
|
|
652
|
-
* Repeat filter - repeats string specified number of times.
|
|
653
|
-
*
|
|
654
|
-
* @param options - Array with repeat count as first element
|
|
655
|
-
* @returns Filter function that returns repeated string
|
|
656
|
-
*/
|
|
657
|
-
const rep = (options) => {
|
|
658
|
-
const opt = requiredNumberOption(options, 0, 'repeat');
|
|
659
|
-
return (value) => {
|
|
660
|
-
return String(value).repeat(opt);
|
|
661
|
-
};
|
|
662
|
-
};
|
|
663
|
-
/**
|
|
664
|
-
* Reverse filter - reverses character order in string.
|
|
665
|
-
*
|
|
666
|
-
* @param options - Unused
|
|
667
|
-
* @returns Filter function that returns reversed string
|
|
668
|
-
*/
|
|
669
|
-
const rev = (_options) => {
|
|
670
|
-
return (value) => {
|
|
671
|
-
return String(value).split('').reverse().join('');
|
|
672
|
-
};
|
|
673
|
-
};
|
|
674
|
-
/**
|
|
675
|
-
* Integer filter - parses value to integer.
|
|
676
|
-
*
|
|
677
|
-
* @param options - Unused
|
|
678
|
-
* @returns Filter function that returns integer
|
|
679
|
-
*/
|
|
680
|
-
const int = (_options) => {
|
|
681
|
-
return (value) => {
|
|
682
|
-
return parseInt(String(value), 10);
|
|
683
|
-
};
|
|
684
|
-
};
|
|
685
|
-
/**
|
|
686
|
-
* Float filter - parses value to floating point number.
|
|
687
|
-
*
|
|
688
|
-
* @param options - Unused
|
|
689
|
-
* @returns Filter function that returns float
|
|
690
|
-
*/
|
|
691
|
-
const float = (_options) => {
|
|
692
|
-
return (value) => {
|
|
693
|
-
return parseFloat(String(value));
|
|
694
|
-
};
|
|
695
|
-
};
|
|
696
|
-
/**
|
|
697
|
-
* Round filter - rounds number to specified decimal places.
|
|
698
|
-
*
|
|
699
|
-
* @param options - Array with decimal places as first element (default: 0)
|
|
700
|
-
* @returns Filter function that returns rounded number
|
|
701
|
-
*/
|
|
702
|
-
const round = (options) => {
|
|
703
|
-
const optValue = Math.pow(10, numberOption(options?.[0] ?? '0', 'round'));
|
|
704
|
-
return (value) => {
|
|
705
|
-
if (typeof value !== 'number') {
|
|
706
|
-
valueMustBeNumber('round');
|
|
707
|
-
}
|
|
708
|
-
return Math.round(value * optValue) / optValue;
|
|
709
|
-
};
|
|
710
|
-
};
|
|
711
|
-
/**
|
|
712
|
-
* Floor filter - rounds number down to specified decimal places.
|
|
713
|
-
*
|
|
714
|
-
* @param options - Array with decimal places as first element (default: 0)
|
|
715
|
-
* @returns Filter function that returns floored number
|
|
716
|
-
*/
|
|
717
|
-
const floor = (options) => {
|
|
718
|
-
const optValue = Math.pow(10, numberOption(options?.[0] ?? '0', 'floor'));
|
|
719
|
-
return (value) => {
|
|
720
|
-
if (typeof value !== 'number') {
|
|
721
|
-
valueMustBeNumber('floor');
|
|
722
|
-
}
|
|
723
|
-
return Math.floor(value * optValue) / optValue;
|
|
724
|
-
};
|
|
725
|
-
};
|
|
726
|
-
/**
|
|
727
|
-
* Ceiling filter - rounds number up to specified decimal places.
|
|
728
|
-
*
|
|
729
|
-
* @param options - Array with decimal places as first element (default: 0)
|
|
730
|
-
* @returns Filter function that returns ceiled number
|
|
731
|
-
*/
|
|
732
|
-
const ceil = (options) => {
|
|
733
|
-
const optValue = Math.pow(10, numberOption(options?.[0] ?? '0', 'ceil'));
|
|
734
|
-
return (value) => {
|
|
735
|
-
if (typeof value !== 'number') {
|
|
736
|
-
valueMustBeNumber('ceil');
|
|
737
|
-
}
|
|
738
|
-
return Math.ceil(value * optValue) / optValue;
|
|
739
|
-
};
|
|
740
|
-
};
|
|
741
|
-
/**
|
|
742
|
-
* Percent filter - formats number as percentage string.
|
|
743
|
-
*
|
|
744
|
-
* @param options - Array with decimal places as first element (default: 0)
|
|
745
|
-
* @returns Filter function that returns percentage string with '%'
|
|
746
|
-
*/
|
|
747
|
-
const percent = (options) => {
|
|
748
|
-
const opt = numberOption(options?.[0] ?? '0', 'percent');
|
|
749
|
-
return (value) => {
|
|
750
|
-
if (typeof value !== 'number') {
|
|
751
|
-
valueMustBeNumber('percent');
|
|
752
|
-
}
|
|
753
|
-
return `${(value * 100).toFixed(opt)}%`;
|
|
754
|
-
};
|
|
755
|
-
};
|
|
756
|
-
/**
|
|
757
|
-
* Unit filter - appends a CSS unit (or any suffix) to the value.
|
|
758
|
-
*
|
|
759
|
-
* A number alone does nothing in CSS, so without this the unit has to be built in
|
|
760
|
-
* state — which drags presentation into the source of truth, and in the worst case
|
|
761
|
-
* forces a whole derived array just to carry `"42%"` strings.
|
|
762
|
-
* `style.height: samples.*.cpu|clamp(0,100)|fix(0)|unit(%)` keeps it on the wire.
|
|
763
|
-
*
|
|
764
|
-
* Accepts strings as well as numbers **on purpose**: the useful chains run through
|
|
765
|
-
* `fix` / `percent`, which already return strings. Rejecting non-numbers here would
|
|
766
|
-
* break exactly the combination this filter exists for.
|
|
767
|
-
*
|
|
768
|
-
* `null` / `undefined` pass through untouched rather than becoming `"undefinedpx"`, so the
|
|
769
|
-
* binding layer's "undefined skips the write, null clears" semantics survive. That guard is no
|
|
770
|
-
* longer written here: it is `nullishPassthrough`, shared with the rest of the `String(value)`
|
|
771
|
-
* family, which used to paint `"undefined"` into the page for exactly the same reason.
|
|
772
|
-
*
|
|
773
|
-
* @param options - Array with the unit/suffix as first element (required)
|
|
774
|
-
* @returns Filter function that returns the value with the unit appended
|
|
775
|
-
*/
|
|
776
|
-
const unit = (options) => {
|
|
777
|
-
const opt = options?.[0] ?? optionsRequired('unit');
|
|
778
|
-
return (value) => {
|
|
779
|
-
return String(value) + opt;
|
|
780
|
-
};
|
|
781
|
-
};
|
|
782
|
-
/**
|
|
783
|
-
* Join filter - joins array elements into a string.
|
|
784
|
-
*
|
|
785
|
-
* The default separator is `", "` rather than `","`: a bare comma is what `String()`
|
|
786
|
-
* already produces without any filter, so defaulting to it would make `|join` a no-op.
|
|
787
|
-
*
|
|
788
|
-
* @param options - Array with separator as first element (default: ', ')
|
|
789
|
-
* @returns Filter function that returns the joined string
|
|
790
|
-
*/
|
|
791
|
-
const join = (options) => {
|
|
792
|
-
const opt = options?.[0] ?? ', ';
|
|
793
|
-
return (value) => {
|
|
794
|
-
if (!Array.isArray(value)) {
|
|
795
|
-
valueMustBeArray('join');
|
|
796
|
-
}
|
|
797
|
-
return value.join(opt);
|
|
798
|
-
};
|
|
799
|
-
};
|
|
800
|
-
/**
|
|
801
|
-
* Truncate filter - shortens a string and appends an ellipsis.
|
|
802
|
-
*
|
|
803
|
-
* The length option counts **kept characters**, not the total including the suffix,
|
|
804
|
-
* matching the existing `slice(0, n)` reading. A string at or below the limit is
|
|
805
|
-
* returned untouched (no suffix).
|
|
806
|
-
*
|
|
807
|
-
* @param options - Array with max kept length as first element and suffix as second (default: '…')
|
|
808
|
-
* @returns Filter function that returns the truncated string
|
|
809
|
-
*/
|
|
810
|
-
const truncate = (options) => {
|
|
811
|
-
const maxLength = requiredNumberOption(options, 0, 'truncate');
|
|
812
|
-
const suffix = options?.[1] ?? '…';
|
|
813
|
-
return (value) => {
|
|
814
|
-
const v = String(value);
|
|
815
|
-
if (v.length <= maxLength) {
|
|
816
|
-
return v;
|
|
817
|
-
}
|
|
818
|
-
return v.slice(0, maxLength) + suffix;
|
|
819
|
-
};
|
|
820
|
-
};
|
|
821
|
-
/**
|
|
822
|
-
* Date filter - formats Date object as localized date string.
|
|
823
|
-
*
|
|
824
|
-
* @param options - Array with locale string as first element (default: config.locale)
|
|
825
|
-
* @returns Filter function that returns date string
|
|
826
|
-
*/
|
|
827
|
-
const date = (options) => {
|
|
828
|
-
// 既定ロケールは適用のたびに読む(`locale` フィルタの注記を参照)
|
|
829
|
-
const explicit = options?.[0];
|
|
830
|
-
return (value) => {
|
|
831
|
-
if (!(value instanceof Date)) {
|
|
832
|
-
valueMustBeDate('date');
|
|
833
|
-
}
|
|
834
|
-
return value.toLocaleDateString(explicit ?? config.locale);
|
|
835
|
-
};
|
|
836
|
-
};
|
|
837
|
-
/**
|
|
838
|
-
* Time filter - formats Date object as localized time string.
|
|
839
|
-
*
|
|
840
|
-
* @param options - Array with locale string as first element (default: config.locale)
|
|
841
|
-
* @returns Filter function that returns time string
|
|
842
|
-
*/
|
|
843
|
-
const time = (options) => {
|
|
844
|
-
// 既定ロケールは適用のたびに読む(`locale` フィルタの注記を参照)
|
|
845
|
-
const explicit = options?.[0];
|
|
846
|
-
return (value) => {
|
|
847
|
-
if (!(value instanceof Date)) {
|
|
848
|
-
valueMustBeDate('time');
|
|
849
|
-
}
|
|
850
|
-
return value.toLocaleTimeString(explicit ?? config.locale);
|
|
851
|
-
};
|
|
852
|
-
};
|
|
853
|
-
/**
|
|
854
|
-
* DateTime filter - formats Date object as localized date and time string.
|
|
855
|
-
*
|
|
856
|
-
* @param options - Array with locale string as first element (default: config.locale)
|
|
857
|
-
* @returns Filter function that returns datetime string
|
|
858
|
-
*/
|
|
859
|
-
const datetime = (options) => {
|
|
860
|
-
// 既定ロケールは適用のたびに読む(`locale` フィルタの注記を参照)
|
|
861
|
-
const explicit = options?.[0];
|
|
862
|
-
return (value) => {
|
|
863
|
-
if (!(value instanceof Date)) {
|
|
864
|
-
valueMustBeDate('datetime');
|
|
865
|
-
}
|
|
866
|
-
return value.toLocaleString(explicit ?? config.locale);
|
|
867
|
-
};
|
|
868
|
-
};
|
|
869
|
-
/**
|
|
870
|
-
* Year-Month-Day filter - formats Date object as YYYY-MM-DD string.
|
|
871
|
-
*
|
|
872
|
-
* @param options - Array with separator string as first element (default: '-')
|
|
873
|
-
* @returns Filter function that returns formatted date string
|
|
874
|
-
*/
|
|
875
|
-
const ymd = (options) => {
|
|
876
|
-
const opt = options?.[0] ?? '-';
|
|
877
|
-
return (value) => {
|
|
878
|
-
if (!(value instanceof Date)) {
|
|
879
|
-
valueMustBeDate('ymd');
|
|
880
|
-
}
|
|
881
|
-
const year = value.getFullYear().toString();
|
|
882
|
-
const month = (value.getMonth() + 1).toString().padStart(2, '0');
|
|
883
|
-
const day = value.getDate().toString().padStart(2, '0');
|
|
884
|
-
return `${year}${opt}${month}${opt}${day}`;
|
|
885
|
-
};
|
|
886
|
-
};
|
|
887
|
-
/**
|
|
888
|
-
* Hour-Minute-Second filter - formats Date object as HH:MM:SS string.
|
|
889
|
-
*
|
|
890
|
-
* The counterpart of `ymd`: a fixed, zero-padded, locale-independent rendering with a
|
|
891
|
-
* configurable separator, for when `time` (locale-formatted) is not stable enough.
|
|
892
|
-
*
|
|
893
|
-
* @param options - Array with separator string as first element (default: ':')
|
|
894
|
-
* @returns Filter function that returns formatted time string
|
|
895
|
-
*/
|
|
896
|
-
const hms = (options) => {
|
|
897
|
-
const opt = options?.[0] ?? ':';
|
|
898
|
-
return (value) => {
|
|
899
|
-
if (!(value instanceof Date)) {
|
|
900
|
-
valueMustBeDate('hms');
|
|
901
|
-
}
|
|
902
|
-
const hours = value.getHours().toString().padStart(2, '0');
|
|
903
|
-
const minutes = value.getMinutes().toString().padStart(2, '0');
|
|
904
|
-
const seconds = value.getSeconds().toString().padStart(2, '0');
|
|
905
|
-
return `${hours}${opt}${minutes}${opt}${seconds}`;
|
|
906
|
-
};
|
|
907
|
-
};
|
|
908
|
-
/**
|
|
909
|
-
* Falsy filter - checks if value is falsy, by JavaScript's own truthiness (`!value`: false, null,
|
|
910
|
-
* undefined, 0, -0, 0n, '' and NaN). Before 3.0 the list was spelled out and missed 0n (B10).
|
|
911
|
-
*
|
|
912
|
-
* @param options - Unused
|
|
913
|
-
* @returns Filter function that returns true for falsy values
|
|
914
|
-
*/
|
|
915
|
-
const falsy = (_options) => {
|
|
916
|
-
return (value) => !value;
|
|
917
|
-
};
|
|
918
|
-
/**
|
|
919
|
-
* Truthy filter - checks if value is truthy.
|
|
920
|
-
*
|
|
921
|
-
* @param options - Unused
|
|
922
|
-
* @returns Filter function that returns true for non-falsy values
|
|
923
|
-
*/
|
|
924
|
-
const truthy = (_options) => {
|
|
925
|
-
// JavaScript's truthiness, the same as the `boolean` filter (B10)
|
|
926
|
-
return (value) => !!value;
|
|
927
|
-
};
|
|
928
|
-
/**
|
|
929
|
-
* Default filter - returns default value if input is falsy (JavaScript's truthiness, as `falsy`).
|
|
930
|
-
*
|
|
931
|
-
* @param options - Array with default value as first element
|
|
932
|
-
* @returns Filter function that returns value or default
|
|
933
|
-
*/
|
|
934
|
-
const defaults = (options, literals) => {
|
|
935
|
-
const opt = options?.[0] ?? optionsRequired('defaults');
|
|
936
|
-
// The fallback is the typed literal (B9): defaults(0) gives 0, defaults(null) gives null, defaults('0') gives "0"
|
|
937
|
-
const fallback = literals !== undefined && literals.length > 0 ? literals[0] : opt;
|
|
938
|
-
return (value) => {
|
|
939
|
-
if (!value) {
|
|
940
|
-
return fallback;
|
|
941
|
-
}
|
|
942
|
-
return value;
|
|
943
|
-
};
|
|
944
|
-
};
|
|
945
|
-
/**
|
|
946
|
-
* coalesce filter - returns the default only for null / undefined (`defaults` also replaces 0, false and "").
|
|
947
|
-
* Requirement B12: the nullish counterpart of `defaults`, read like SQL COALESCE.
|
|
948
|
-
*
|
|
949
|
-
* @param options - Array with default value as first element
|
|
950
|
-
* @returns Filter function that returns value or default
|
|
951
|
-
*/
|
|
952
|
-
const coalesce = (options, literals) => {
|
|
953
|
-
const opt = options?.[0] ?? optionsRequired('coalesce');
|
|
954
|
-
const fallback = literals !== undefined && literals.length > 0 ? literals[0] : opt;
|
|
955
|
-
return (value) => value ?? fallback;
|
|
956
|
-
};
|
|
957
|
-
/**
|
|
958
|
-
* Boolean filter - converts value to boolean.
|
|
959
|
-
*
|
|
960
|
-
* @param options - Unused
|
|
961
|
-
* @returns Filter function that returns boolean
|
|
962
|
-
*/
|
|
963
|
-
const boolean = (_options) => {
|
|
964
|
-
return (value) => {
|
|
965
|
-
return Boolean(value);
|
|
966
|
-
};
|
|
967
|
-
};
|
|
968
|
-
/**
|
|
969
|
-
* Number filter - converts value to number.
|
|
970
|
-
*
|
|
971
|
-
* @param options - Unused
|
|
972
|
-
* @returns Filter function that returns number
|
|
973
|
-
*/
|
|
974
|
-
const number = (_options) => {
|
|
975
|
-
return (value) => {
|
|
976
|
-
return Number(value);
|
|
977
|
-
};
|
|
978
|
-
};
|
|
979
|
-
/**
|
|
980
|
-
* String filter - converts value to string.
|
|
981
|
-
*
|
|
982
|
-
* @param options - Unused
|
|
983
|
-
* @returns Filter function that returns string
|
|
984
|
-
*/
|
|
985
|
-
const string = (_options) => {
|
|
986
|
-
return (value) => {
|
|
987
|
-
return String(value);
|
|
988
|
-
};
|
|
129
|
+
if (typeof value === "string") {
|
|
130
|
+
return value !== opt;
|
|
131
|
+
}
|
|
132
|
+
return value !== literal;
|
|
133
|
+
};
|
|
989
134
|
};
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
* @returns Filter function that returns null for empty string, otherwise original value
|
|
995
|
-
*/
|
|
996
|
-
const _null = (_options) => {
|
|
135
|
+
var not = () => (value) => !value;
|
|
136
|
+
var numeric = (name, op) => ({
|
|
137
|
+
factory: (options) => {
|
|
138
|
+
const opt = requiredNumberOption(options, 0, name);
|
|
997
139
|
return (value) => {
|
|
998
|
-
|
|
140
|
+
if (typeof value !== "number") {
|
|
141
|
+
valueMustBeNumber(name);
|
|
142
|
+
}
|
|
143
|
+
return op(value, opt);
|
|
999
144
|
};
|
|
145
|
+
},
|
|
146
|
+
arity: [1, 1]
|
|
147
|
+
});
|
|
148
|
+
var truthy = () => (value) => !!value;
|
|
149
|
+
var falsy = () => (value) => !value;
|
|
150
|
+
var boolean = () => (value) => Boolean(value);
|
|
151
|
+
var abs = () => (value) => {
|
|
152
|
+
if (typeof value !== "number") {
|
|
153
|
+
valueMustBeNumber("abs");
|
|
154
|
+
}
|
|
155
|
+
return Math.abs(value);
|
|
156
|
+
};
|
|
157
|
+
var clamp = (options) => {
|
|
158
|
+
const min = requiredNumberOption(options, 0, "clamp");
|
|
159
|
+
const max = requiredNumberOption(options, 1, "clamp");
|
|
160
|
+
return (value) => {
|
|
161
|
+
if (typeof value !== "number") {
|
|
162
|
+
valueMustBeNumber("clamp");
|
|
163
|
+
}
|
|
164
|
+
return Math.min(Math.max(value, min), max);
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
var number = () => (value) => Number(value);
|
|
168
|
+
var string = () => (value) => String(value);
|
|
169
|
+
var int = () => (value) => parseInt(String(value), 10);
|
|
170
|
+
var float = () => (value) => parseFloat(String(value));
|
|
171
|
+
var defaults = (options, literals) => {
|
|
172
|
+
const opt = options?.[0] ?? optionsRequired("defaults");
|
|
173
|
+
const fallback = firstLiteral(opt, literals);
|
|
174
|
+
return (value) => {
|
|
175
|
+
if (!value) {
|
|
176
|
+
return fallback;
|
|
177
|
+
}
|
|
178
|
+
return value;
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
var coalesce = (options, literals) => {
|
|
182
|
+
const opt = options?.[0] ?? optionsRequired("coalesce");
|
|
183
|
+
const fallback = firstLiteral(opt, literals);
|
|
184
|
+
return (value) => value ?? fallback;
|
|
185
|
+
};
|
|
186
|
+
var nullIfEmpty = () => (value) => value === "" ? null : value;
|
|
187
|
+
var coreFilters = {
|
|
188
|
+
eq: { factory: eq, arity: [1, 1] },
|
|
189
|
+
ne: { factory: ne, arity: [1, 1] },
|
|
190
|
+
not: { factory: not, arity: [0, 0] },
|
|
191
|
+
lt: numeric("lt", (value, opt) => value < opt),
|
|
192
|
+
le: numeric("le", (value, opt) => value <= opt),
|
|
193
|
+
gt: numeric("gt", (value, opt) => value > opt),
|
|
194
|
+
ge: numeric("ge", (value, opt) => value >= opt),
|
|
195
|
+
add: numeric("add", (value, opt) => value + opt),
|
|
196
|
+
sub: numeric("sub", (value, opt) => value - opt),
|
|
197
|
+
mul: numeric("mul", (value, opt) => value * opt),
|
|
198
|
+
div: numeric("div", (value, opt) => value / opt),
|
|
199
|
+
mod: numeric("mod", (value, opt) => value % opt),
|
|
200
|
+
abs: { factory: abs, arity: [0, 0] },
|
|
201
|
+
clamp: { factory: clamp, arity: [2, 2] },
|
|
202
|
+
int: { factory: int, arity: [0, 0] },
|
|
203
|
+
float: { factory: float, arity: [0, 0] },
|
|
204
|
+
falsy: { factory: falsy, arity: [0, 0] },
|
|
205
|
+
truthy: { factory: truthy, arity: [0, 0] },
|
|
206
|
+
defaults: { factory: defaults, arity: [1, 1] },
|
|
207
|
+
coalesce: { factory: coalesce, arity: [1, 1] },
|
|
208
|
+
boolean: { factory: boolean, arity: [0, 0] },
|
|
209
|
+
number: { factory: number, arity: [0, 0] },
|
|
210
|
+
string: { factory: string, arity: [0, 0] },
|
|
211
|
+
nullIfEmpty: { factory: nullIfEmpty, arity: [0, 0] }
|
|
1000
212
|
};
|
|
1001
|
-
const builtinFilters = {
|
|
1002
|
-
"eq": eq,
|
|
1003
|
-
"ne": ne,
|
|
1004
|
-
"not": not,
|
|
1005
|
-
"lt": lt,
|
|
1006
|
-
"le": le,
|
|
1007
|
-
"gt": gt,
|
|
1008
|
-
"ge": ge,
|
|
1009
|
-
"add": inc,
|
|
1010
|
-
"sub": dec,
|
|
1011
|
-
"mul": mul,
|
|
1012
|
-
"div": div,
|
|
1013
|
-
"mod": mod,
|
|
1014
|
-
"abs": abs,
|
|
1015
|
-
"clamp": clamp,
|
|
1016
|
-
"toFixed": fix,
|
|
1017
|
-
"locale": locale,
|
|
1018
|
-
// `String(value)` で組み立てる族は、値が無いときに素通しする(要件 B8 — nullishPassthrough を参照)
|
|
1019
|
-
"upper": nullishPassthrough(uc),
|
|
1020
|
-
"lower": nullishPassthrough(lc),
|
|
1021
|
-
"capitalize": nullishPassthrough(cap),
|
|
1022
|
-
"trim": nullishPassthrough(trim),
|
|
1023
|
-
"slice": nullishPassthrough(slice),
|
|
1024
|
-
"substr": nullishPassthrough(substr),
|
|
1025
|
-
"padStart": nullishPassthrough(pad),
|
|
1026
|
-
"padEnd": nullishPassthrough(padEnd),
|
|
1027
|
-
"repeat": nullishPassthrough(rep),
|
|
1028
|
-
"reverse": nullishPassthrough(rev),
|
|
1029
|
-
"truncate": nullishPassthrough(truncate),
|
|
1030
|
-
"join": join,
|
|
1031
|
-
"int": int,
|
|
1032
|
-
"float": float,
|
|
1033
|
-
"round": round,
|
|
1034
|
-
"floor": floor,
|
|
1035
|
-
"ceil": ceil,
|
|
1036
|
-
"percent": percent,
|
|
1037
|
-
"unit": nullishPassthrough(unit),
|
|
1038
|
-
"date": date,
|
|
1039
|
-
"time": time,
|
|
1040
|
-
"datetime": datetime,
|
|
1041
|
-
"ymd": ymd,
|
|
1042
|
-
"hms": hms,
|
|
1043
|
-
"falsy": falsy,
|
|
1044
|
-
"truthy": truthy,
|
|
1045
|
-
"defaults": defaults,
|
|
1046
|
-
"coalesce": coalesce,
|
|
1047
|
-
"boolean": boolean,
|
|
1048
|
-
"number": number,
|
|
1049
|
-
"string": string,
|
|
1050
|
-
"nullIfEmpty": _null,
|
|
1051
|
-
};
|
|
1052
|
-
const outputBuiltinFilters = builtinFilters;
|
|
1053
213
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
214
|
+
// src/filters/formats.ts
|
|
215
|
+
var nullishPassthrough = (factory) => (options) => {
|
|
216
|
+
const filterFn = factory(options);
|
|
217
|
+
return (value) => value == null ? value : filterFn(value);
|
|
218
|
+
};
|
|
219
|
+
var toFixed = (options) => {
|
|
220
|
+
const opt = numberOption(options?.[0] ?? "0", "toFixed");
|
|
221
|
+
return (value) => {
|
|
222
|
+
if (typeof value !== "number") {
|
|
223
|
+
valueMustBeNumber("toFixed");
|
|
224
|
+
}
|
|
225
|
+
return value.toFixed(opt);
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
var round = (options) => {
|
|
229
|
+
const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "round"));
|
|
230
|
+
return (value) => {
|
|
231
|
+
if (typeof value !== "number") {
|
|
232
|
+
valueMustBeNumber("round");
|
|
233
|
+
}
|
|
234
|
+
return Math.round(value * optValue) / optValue;
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
var floor = (options) => {
|
|
238
|
+
const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "floor"));
|
|
239
|
+
return (value) => {
|
|
240
|
+
if (typeof value !== "number") {
|
|
241
|
+
valueMustBeNumber("floor");
|
|
242
|
+
}
|
|
243
|
+
return Math.floor(value * optValue) / optValue;
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
var ceil = (options) => {
|
|
247
|
+
const optValue = Math.pow(10, numberOption(options?.[0] ?? "0", "ceil"));
|
|
248
|
+
return (value) => {
|
|
249
|
+
if (typeof value !== "number") {
|
|
250
|
+
valueMustBeNumber("ceil");
|
|
251
|
+
}
|
|
252
|
+
return Math.ceil(value * optValue) / optValue;
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
var percent = (options) => {
|
|
256
|
+
const opt = numberOption(options?.[0] ?? "0", "percent");
|
|
257
|
+
return (value) => {
|
|
258
|
+
if (typeof value !== "number") {
|
|
259
|
+
valueMustBeNumber("percent");
|
|
260
|
+
}
|
|
261
|
+
return `${(value * 100).toFixed(opt)}%`;
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
var unit = (options) => {
|
|
265
|
+
const opt = options?.[0] ?? optionsRequired("unit");
|
|
266
|
+
return (value) => String(value) + opt;
|
|
267
|
+
};
|
|
268
|
+
var seen;
|
|
269
|
+
var valid = "en";
|
|
270
|
+
function defaultLocale() {
|
|
271
|
+
if (config.locale !== seen) {
|
|
272
|
+
seen = config.locale;
|
|
273
|
+
try {
|
|
274
|
+
valid = Intl.getCanonicalLocales(seen)[0];
|
|
275
|
+
} catch {
|
|
276
|
+
console.warn(`[@wcstack/state] ${text(48 /* LocaleInvalid */, [seen])}`);
|
|
277
|
+
valid = "en";
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return valid;
|
|
281
|
+
}
|
|
282
|
+
var locale = (options) => {
|
|
283
|
+
const explicit = options?.[0];
|
|
284
|
+
return (value) => {
|
|
285
|
+
if (typeof value !== "number") {
|
|
286
|
+
valueMustBeNumber("locale");
|
|
287
|
+
}
|
|
288
|
+
return value.toLocaleString(explicit ?? defaultLocale());
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
var upper = () => (value) => String(value).toUpperCase();
|
|
292
|
+
var lower = () => (value) => String(value).toLowerCase();
|
|
293
|
+
var capitalize = () => (value) => {
|
|
294
|
+
const v = String(value);
|
|
295
|
+
if (v.length === 0) {
|
|
296
|
+
return v;
|
|
297
|
+
}
|
|
298
|
+
if (v.length === 1) {
|
|
299
|
+
return v.toUpperCase();
|
|
300
|
+
}
|
|
301
|
+
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
302
|
+
};
|
|
303
|
+
var trim = () => (value) => String(value).trim();
|
|
304
|
+
var slice = (options) => {
|
|
305
|
+
const numberedOpts = [requiredNumberOption(options, 0, "slice")];
|
|
306
|
+
const opt2 = options?.[1];
|
|
307
|
+
if (typeof opt2 !== "undefined") {
|
|
308
|
+
numberedOpts.push(numberOption(opt2, "slice"));
|
|
309
|
+
}
|
|
310
|
+
return (value) => String(value).slice(...numberedOpts);
|
|
311
|
+
};
|
|
312
|
+
var padStart = (options) => {
|
|
313
|
+
const opt1 = requiredNumberOption(options, 0, "padStart");
|
|
314
|
+
const opt2 = options?.[1] ?? "0";
|
|
315
|
+
return (value) => String(value).padStart(opt1, opt2);
|
|
316
|
+
};
|
|
317
|
+
var padEnd = (options) => {
|
|
318
|
+
const opt1 = requiredNumberOption(options, 0, "padEnd");
|
|
319
|
+
const opt2 = options?.[1] ?? " ";
|
|
320
|
+
return (value) => String(value).padEnd(opt1, opt2);
|
|
321
|
+
};
|
|
322
|
+
var repeat = (options) => {
|
|
323
|
+
const opt = requiredNumberOption(options, 0, "repeat");
|
|
324
|
+
return (value) => String(value).repeat(opt);
|
|
325
|
+
};
|
|
326
|
+
var reverse = () => (value) => String(value).split("").reverse().join("");
|
|
327
|
+
var truncate = (options) => {
|
|
328
|
+
const maxLength = requiredNumberOption(options, 0, "truncate");
|
|
329
|
+
const suffix = options?.[1] ?? "\u2026";
|
|
330
|
+
return (value) => {
|
|
331
|
+
const v = String(value);
|
|
332
|
+
if (v.length <= maxLength) {
|
|
333
|
+
return v;
|
|
334
|
+
}
|
|
335
|
+
return v.slice(0, maxLength) + suffix;
|
|
336
|
+
};
|
|
337
|
+
};
|
|
338
|
+
var join = (options) => {
|
|
339
|
+
const opt = options?.[0] ?? ", ";
|
|
340
|
+
return (value) => {
|
|
341
|
+
if (!Array.isArray(value)) {
|
|
342
|
+
valueMustBeArray("join");
|
|
343
|
+
}
|
|
344
|
+
return value.join(opt);
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
var date = (options) => {
|
|
348
|
+
const explicit = options?.[0];
|
|
349
|
+
return (value) => {
|
|
350
|
+
if (!(value instanceof Date)) {
|
|
351
|
+
valueMustBeDate("date");
|
|
352
|
+
}
|
|
353
|
+
return value.toLocaleDateString(explicit ?? defaultLocale());
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
var time = (options) => {
|
|
357
|
+
const explicit = options?.[0];
|
|
358
|
+
return (value) => {
|
|
359
|
+
if (!(value instanceof Date)) {
|
|
360
|
+
valueMustBeDate("time");
|
|
361
|
+
}
|
|
362
|
+
return value.toLocaleTimeString(explicit ?? defaultLocale());
|
|
363
|
+
};
|
|
364
|
+
};
|
|
365
|
+
var datetime = (options) => {
|
|
366
|
+
const explicit = options?.[0];
|
|
367
|
+
return (value) => {
|
|
368
|
+
if (!(value instanceof Date)) {
|
|
369
|
+
valueMustBeDate("datetime");
|
|
370
|
+
}
|
|
371
|
+
return value.toLocaleString(explicit ?? defaultLocale());
|
|
372
|
+
};
|
|
373
|
+
};
|
|
374
|
+
var ymd = (options) => {
|
|
375
|
+
const opt = options?.[0] ?? "-";
|
|
376
|
+
return (value) => {
|
|
377
|
+
if (!(value instanceof Date)) {
|
|
378
|
+
valueMustBeDate("ymd");
|
|
379
|
+
}
|
|
380
|
+
const year = value.getFullYear().toString();
|
|
381
|
+
const month = (value.getMonth() + 1).toString().padStart(2, "0");
|
|
382
|
+
const day = value.getDate().toString().padStart(2, "0");
|
|
383
|
+
return `${year}${opt}${month}${opt}${day}`;
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
var hms = (options) => {
|
|
387
|
+
const opt = options?.[0] ?? ":";
|
|
388
|
+
return (value) => {
|
|
389
|
+
if (!(value instanceof Date)) {
|
|
390
|
+
valueMustBeDate("hms");
|
|
391
|
+
}
|
|
392
|
+
const hours = value.getHours().toString().padStart(2, "0");
|
|
393
|
+
const minutes = value.getMinutes().toString().padStart(2, "0");
|
|
394
|
+
const seconds = value.getSeconds().toString().padStart(2, "0");
|
|
395
|
+
return `${hours}${opt}${minutes}${opt}${seconds}`;
|
|
396
|
+
};
|
|
397
|
+
};
|
|
398
|
+
var formatFilters = {
|
|
399
|
+
toFixed: { factory: toFixed, arity: [0, 1] },
|
|
400
|
+
locale: { factory: locale, arity: [0, 1] },
|
|
401
|
+
// The `String(value)` family passes an absent value through (B8 — see nullishPassthrough)
|
|
402
|
+
upper: { factory: nullishPassthrough(upper), arity: [0, 0] },
|
|
403
|
+
lower: { factory: nullishPassthrough(lower), arity: [0, 0] },
|
|
404
|
+
capitalize: { factory: nullishPassthrough(capitalize), arity: [0, 0] },
|
|
405
|
+
trim: { factory: nullishPassthrough(trim), arity: [0, 0] },
|
|
406
|
+
slice: { factory: nullishPassthrough(slice), arity: [1, 2] },
|
|
407
|
+
// The length is required too (the implementation reads both)
|
|
408
|
+
padStart: { factory: nullishPassthrough(padStart), arity: [1, 2] },
|
|
409
|
+
padEnd: { factory: nullishPassthrough(padEnd), arity: [1, 2] },
|
|
410
|
+
repeat: { factory: nullishPassthrough(repeat), arity: [1, 1] },
|
|
411
|
+
reverse: { factory: nullishPassthrough(reverse), arity: [0, 0] },
|
|
412
|
+
truncate: { factory: nullishPassthrough(truncate), arity: [1, 2] },
|
|
413
|
+
join: { factory: join, arity: [0, 1] },
|
|
414
|
+
round: { factory: round, arity: [0, 1] },
|
|
415
|
+
floor: { factory: floor, arity: [0, 1] },
|
|
416
|
+
ceil: { factory: ceil, arity: [0, 1] },
|
|
417
|
+
percent: { factory: percent, arity: [0, 1] },
|
|
418
|
+
unit: { factory: nullishPassthrough(unit), arity: [1, 1] },
|
|
419
|
+
// The locale-dependent three take a locale like `locale` does (`date(ja-JP)`)
|
|
420
|
+
date: { factory: date, arity: [0, 1] },
|
|
421
|
+
time: { factory: time, arity: [0, 1] },
|
|
422
|
+
datetime: { factory: datetime, arity: [0, 1] },
|
|
423
|
+
ymd: { factory: ymd, arity: [0, 1] },
|
|
424
|
+
hms: { factory: hms, arity: [0, 1] }
|
|
1130
425
|
};
|
|
1131
426
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
427
|
+
// src/parser/types.ts
|
|
428
|
+
var STRUCTURAL_BINDING_TYPE_SET = /* @__PURE__ */ new Set([
|
|
429
|
+
"if",
|
|
430
|
+
"elseif",
|
|
431
|
+
"else",
|
|
432
|
+
"for"
|
|
1137
433
|
]);
|
|
1138
434
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
}
|
|
1169
|
-
|
|
435
|
+
// src/parser/define.ts
|
|
436
|
+
var DELIMITER = ".";
|
|
437
|
+
var BINDING_SEPARATOR = ";";
|
|
438
|
+
var PROP_VALUE_SEPARATOR = ":";
|
|
439
|
+
var MODIFIER_SEPARATOR = "#";
|
|
440
|
+
var FILTER_SEPARATOR = "|";
|
|
441
|
+
var ELSE_KEYWORD = "else";
|
|
442
|
+
var SPREAD_PROP = "...";
|
|
443
|
+
var EVENT_PROP_PREFIX = "on";
|
|
444
|
+
var EVENT_TOKEN_NAMESPACE = "eventToken";
|
|
445
|
+
var COMMAND_NAMESPACE = "command";
|
|
446
|
+
var CLASS_NAMESPACE = "class";
|
|
447
|
+
var ATTR_NAMESPACE = "attr";
|
|
448
|
+
var STYLE_NAMESPACE = "style";
|
|
449
|
+
var MAX_INDEX_PARAM = 128;
|
|
450
|
+
|
|
451
|
+
// src/public/filterMeta.ts
|
|
452
|
+
var builtinFilterMeta = {
|
|
453
|
+
// 比較・論理
|
|
454
|
+
eq: { description: "\u7B49\u3057\u3044\u304B\u6BD4\u8F03", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
|
|
455
|
+
ne: { description: "\u7570\u306A\u308B\u304B\u6BD4\u8F03", hasArgs: true, resultType: "boolean", acceptTypes: "any", minArgs: 1, maxArgs: 1, argTypes: ["any"] },
|
|
456
|
+
// `not` は真偽性(truthy / falsy)の反転。`if:` が `Boolean()` で寄せるのと同じ規則で、
|
|
457
|
+
// `else:` はこのフィルタを足した束縛として組み立てられる(structural/createNotFilter.ts)
|
|
458
|
+
not: { description: "\u771F\u507D\u6027\u3092\u53CD\u8EE2\uFF08falsy \u2192 true\uFF09", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
459
|
+
lt: { description: "\u3088\u308A\u5C0F\u3055\u3044\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
460
|
+
le: { description: "\u4EE5\u4E0B\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
461
|
+
gt: { description: "\u3088\u308A\u5927\u304D\u3044\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
462
|
+
ge: { description: "\u4EE5\u4E0A\u304B", hasArgs: true, resultType: "boolean", acceptTypes: ["number", "string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
463
|
+
// 算術
|
|
464
|
+
add: { description: "\u52A0\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
465
|
+
sub: { description: "\u6E1B\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
466
|
+
mul: { description: "\u4E57\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
467
|
+
div: { description: "\u9664\u7B97", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
468
|
+
mod: { description: "\u5270\u4F59", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
469
|
+
abs: { description: "\u7D76\u5BFE\u5024", hasArgs: false, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 0 },
|
|
470
|
+
clamp: { description: "\u7BC4\u56F2\u5185\u306B\u4E38\u3081\u308B (min,max)", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 2, maxArgs: 2, argTypes: ["number", "number"] },
|
|
471
|
+
// 数値フォーマット
|
|
472
|
+
toFixed: { description: "\u56FA\u5B9A\u5C0F\u6570\u70B9\u8868\u8A18", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
|
|
473
|
+
locale: { description: "\u30ED\u30B1\u30FC\u30EB\u5F62\u5F0F\u3067\u6570\u5024\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
474
|
+
// 文字列
|
|
475
|
+
upper: { description: "\u5927\u6587\u5B57\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
476
|
+
lower: { description: "\u5C0F\u6587\u5B57\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
477
|
+
capitalize: { description: "\u5148\u982D\u6587\u5B57\u3092\u5927\u6587\u5B57\u306B", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
478
|
+
trim: { description: "\u524D\u5F8C\u306E\u7A7A\u767D\u3092\u524A\u9664", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
479
|
+
slice: { description: "\u90E8\u5206\u6587\u5B57\u5217 (start[,end])", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 2, argTypes: ["number", "number"] },
|
|
480
|
+
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"] },
|
|
481
|
+
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"] },
|
|
482
|
+
repeat: { description: "\u7E70\u308A\u8FD4\u3057 (count)", hasArgs: true, resultType: "string", acceptTypes: ["string"], minArgs: 1, maxArgs: 1, argTypes: ["number"] },
|
|
483
|
+
reverse: { description: "\u6587\u5B57\u9806\u3092\u53CD\u8EE2", hasArgs: false, resultType: "string", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 },
|
|
484
|
+
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"] },
|
|
485
|
+
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"] },
|
|
486
|
+
// 数値パース・丸め
|
|
487
|
+
int: { description: "\u6574\u6570\u306B\u30D1\u30FC\u30B9", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
|
|
488
|
+
float: { description: "\u6D6E\u52D5\u5C0F\u6570\u70B9\u6570\u306B\u30D1\u30FC\u30B9", hasArgs: false, resultType: "number", acceptTypes: ["string", "number"], minArgs: 0, maxArgs: 0 },
|
|
489
|
+
round: { description: "\u56DB\u6368\u4E94\u5165", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
|
|
490
|
+
floor: { description: "\u5207\u308A\u4E0B\u3052", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
|
|
491
|
+
ceil: { description: "\u5207\u308A\u4E0A\u3052", hasArgs: true, resultType: "number", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
|
|
492
|
+
percent: { description: "\u30D1\u30FC\u30BB\u30F3\u30C6\u30FC\u30B8\u5F62\u5F0F", hasArgs: true, resultType: "string", acceptTypes: ["number"], minArgs: 0, maxArgs: 1, argTypes: ["number"] },
|
|
493
|
+
// number だけでなく string も受ける。実用チェーンは fix / percent の後ろに繋がり、
|
|
494
|
+
// それらは既に string を返すため(builtinFilters.ts の unit を参照)
|
|
495
|
+
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"] },
|
|
496
|
+
// 日付・時刻
|
|
497
|
+
// `locale` と同じくロケールを 1 つ受ける(`date(ja-JP)`)。実装は最初からこれを読んでいたが、
|
|
498
|
+
// メタデータ側が maxArgs: 0 だったため lint と補完が正しい書き方を誤りとして報告していた
|
|
499
|
+
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"] },
|
|
500
|
+
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"] },
|
|
501
|
+
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"] },
|
|
502
|
+
ymd: { description: "YYYY-MM-DD \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F -)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
503
|
+
hms: { description: "HH:MM:SS \u5F62\u5F0F ([separator]\u3002\u65E2\u5B9A\u306F :)", hasArgs: true, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 1, argTypes: ["string"] },
|
|
504
|
+
// 真偽値・変換
|
|
505
|
+
falsy: { description: "\u507D\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
506
|
+
truthy: { description: "\u771F\u5024\u304B\u5224\u5B9A", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
507
|
+
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"] },
|
|
508
|
+
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"] },
|
|
509
|
+
boolean: { description: "\u30D6\u30FC\u30EB\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "boolean", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
510
|
+
number: { description: "\u6570\u5024\u306B\u5909\u63DB", hasArgs: false, resultType: "number", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
511
|
+
string: { description: "\u6587\u5B57\u5217\u306B\u5909\u63DB", hasArgs: false, resultType: "string", acceptTypes: "any", minArgs: 0, maxArgs: 0 },
|
|
512
|
+
nullIfEmpty: { description: "\u7A7A\u6587\u5B57\u5217\u3092null\u306B\u5909\u63DB", hasArgs: false, resultType: "passthrough", acceptTypes: ["string"], minArgs: 0, maxArgs: 0 }
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// src/public/manifest.ts
|
|
516
|
+
var WCS_MANIFEST_VERSION = 2;
|
|
517
|
+
var builtinFilterAliases = Object.freeze({});
|
|
518
|
+
var DECLARATION_ALIASES = Object.freeze({});
|
|
519
|
+
var STATE_API_ALIASES = Object.freeze({});
|
|
520
|
+
var MODIFIER_FLAGS = Object.freeze(["prevent", "stop", "ro", "direct"]);
|
|
521
|
+
var MODIFIER_KEYS = Object.freeze(["init", "sync"]);
|
|
522
|
+
var INDEX_PARAM_PREFIX = "$";
|
|
523
|
+
var BEHAVIOR_OPTION_KEYS = ["enableMustache", "sameValueGuard", "enableDirectionalInitialSync"];
|
|
1170
524
|
function getWcsManifest() {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
STATE_DISCONNECTED_CALLBACK_NAME,
|
|
1218
|
-
STATE_RENDERED_CALLBACK_NAME,
|
|
1219
|
-
STATE_ERROR_CALLBACK_NAME,
|
|
1220
|
-
WEBCOMPONENT_STATE_READY_CALLBACK_NAME,
|
|
1221
|
-
],
|
|
1222
|
-
reservedStateApi: [
|
|
1223
|
-
STATE_BINDABLES_NAME,
|
|
1224
|
-
STATE_COMMANDS_NAME,
|
|
1225
|
-
STATE_COMMAND_TOKENS_NAME,
|
|
1226
|
-
STATE_COMMAND_NAMESPACE_NAME,
|
|
1227
|
-
STATE_EVENT_TOKENS_NAME,
|
|
1228
|
-
STATE_ON_NAME,
|
|
1229
|
-
STATE_STREAM_NAME,
|
|
1230
|
-
STATE_WATCH_NAME,
|
|
1231
|
-
STATE_SCAN_NAME,
|
|
1232
|
-
STATE_LIST_KEYS_NAME,
|
|
1233
|
-
STATE_RECURSION_NAME,
|
|
1234
|
-
STATE_STREAM_STATUS_NAMESPACE_NAME,
|
|
1235
|
-
STATE_STREAM_ERROR_NAMESPACE_NAME,
|
|
1236
|
-
],
|
|
1237
|
-
};
|
|
525
|
+
return {
|
|
526
|
+
version: WCS_MANIFEST_VERSION,
|
|
527
|
+
syntax: {
|
|
528
|
+
bindAttribute: config.bindAttributeName,
|
|
529
|
+
tagName: config.tagNames.state,
|
|
530
|
+
pathDelimiter: DELIMITER,
|
|
531
|
+
wildcard: WILDCARD,
|
|
532
|
+
delimiters: { binding: BINDING_SEPARATOR, propValue: PROP_VALUE_SEPARATOR, modifier: MODIFIER_SEPARATOR, filter: FILTER_SEPARATOR },
|
|
533
|
+
structuralDirectives: Array.from(STRUCTURAL_BINDING_TYPE_SET),
|
|
534
|
+
modifiers: { flags: MODIFIER_FLAGS, keyValue: MODIFIER_KEYS, eventNamePrefix: EVENT_PROP_PREFIX },
|
|
535
|
+
indexParam: { prefix: INDEX_PARAM_PREFIX, maxDepth: MAX_INDEX_PARAM },
|
|
536
|
+
bindingTypes: {
|
|
537
|
+
elseKeyword: ELSE_KEYWORD,
|
|
538
|
+
spread: SPREAD_PROP,
|
|
539
|
+
eventPropertyPrefix: EVENT_PROP_PREFIX,
|
|
540
|
+
explicitPropertyPrefix: DELIMITER,
|
|
541
|
+
propNamespaces: { eventToken: EVENT_TOKEN_NAMESPACE, command: COMMAND_NAMESPACE, class: CLASS_NAMESPACE, attr: ATTR_NAMESPACE, style: STYLE_NAMESPACE }
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
// quoted: `filters` is an internal name the bundles shorten (mangle.mjs)
|
|
545
|
+
"filters": [...Object.keys(coreFilters), ...Object.keys(formatFilters)],
|
|
546
|
+
filterMeta: builtinFilterMeta,
|
|
547
|
+
filterAliases: builtinFilterAliases,
|
|
548
|
+
declarationAliases: DECLARATION_ALIASES,
|
|
549
|
+
apiAliases: STATE_API_ALIASES,
|
|
550
|
+
reservedLifecycle: ["$connectedCallback", "$disconnectedCallback", "$renderedCallback", "$errorCallback", "$stateReadyCallback"],
|
|
551
|
+
reservedStateApi: [
|
|
552
|
+
"$bindables",
|
|
553
|
+
"$commands",
|
|
554
|
+
"$commandTokens",
|
|
555
|
+
"$command",
|
|
556
|
+
"$eventTokens",
|
|
557
|
+
"$on",
|
|
558
|
+
"$stream",
|
|
559
|
+
"$watch",
|
|
560
|
+
"$listKeys",
|
|
561
|
+
"$recursion",
|
|
562
|
+
"$streamStatus",
|
|
563
|
+
"$streamError",
|
|
564
|
+
"$behavior",
|
|
565
|
+
"$features"
|
|
566
|
+
],
|
|
567
|
+
// every option is a boolean, true when left out (engine.ts `loadTarget`: `typeof … === "boolean"`, `?? true`)
|
|
568
|
+
behaviorOptions: Object.fromEntries(BEHAVIOR_OPTION_KEYS.map((key) => [key, { type: "boolean", default: true }])),
|
|
569
|
+
features: [...FEATURE_NAMES]
|
|
570
|
+
};
|
|
1238
571
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
572
|
+
export {
|
|
573
|
+
DECLARATION_ALIASES,
|
|
574
|
+
STATE_API_ALIASES,
|
|
575
|
+
STRUCTURAL_BINDING_TYPE_SET,
|
|
576
|
+
WCS_MANIFEST_VERSION,
|
|
577
|
+
builtinFilterAliases,
|
|
578
|
+
builtinFilterMeta,
|
|
579
|
+
getWcsManifest
|
|
580
|
+
};
|