@wiajs/core 1.2.1 → 1.2.3
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/.oxfmtrc.jsonc +47 -0
- package/.oxlintrc.json +78 -0
- package/dist/core.cjs +306 -12
- package/dist/core.js +726 -11
- package/dist/core.min.js +11 -5
- package/dist/core.mjs +306 -13
- package/dist/jsx-runtime.js +127 -105
- package/package.json +27 -27
- package/util/tool.js +215 -0
- package/.eslintignore +0 -5
- package/.eslintrc.js +0 -121
- package/.prettierignore +0 -24
- package/prettier.config.js +0 -13
package/dist/core.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* wia core v1.2.
|
|
3
|
-
* (c) 2015-
|
|
2
|
+
* wia core v1.2.3
|
|
3
|
+
* (c) 2015-2026 Sibyl Yu and contributors
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
@@ -2073,6 +2073,228 @@ class Event {
|
|
|
2073
2073
|
}
|
|
2074
2074
|
}
|
|
2075
2075
|
|
|
2076
|
+
/*!
|
|
2077
|
+
* wia util v3.4.25
|
|
2078
|
+
* (c) 2018-2024 Yan (Sibyl) Yu and Yuxi (Evan) You and wia/vue contributors
|
|
2079
|
+
* Released under the MIT License.
|
|
2080
|
+
*/
|
|
2081
|
+
/**
|
|
2082
|
+
* Created by way on 10/6/16.
|
|
2083
|
+
* Part of the code comes from @vue/shared
|
|
2084
|
+
*/
|
|
2085
|
+
|
|
2086
|
+
|
|
2087
|
+
/**
|
|
2088
|
+
* 前端日志输出,封装 console日志,简化代码,支持模块或直接输出
|
|
2089
|
+
* 调用时,描述字符串后置,便于可选缺省,输出时,自带前置,类似 后端pino,保持前后端一致性
|
|
2090
|
+
* m 为模块,fn 为函数名称
|
|
2091
|
+
*/
|
|
2092
|
+
class Log {
|
|
2093
|
+
/** @type {string} 模块 */
|
|
2094
|
+
m = ''
|
|
2095
|
+
|
|
2096
|
+
/** @type {string} 函数 */
|
|
2097
|
+
fn = ''
|
|
2098
|
+
|
|
2099
|
+
/**
|
|
2100
|
+
* @param {string} m 模块
|
|
2101
|
+
*/
|
|
2102
|
+
constructor(m) {
|
|
2103
|
+
this.m = m;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
/**
|
|
2107
|
+
* get log desc
|
|
2108
|
+
* 描述字符串后置调用,前置显示
|
|
2109
|
+
* @param {*[]} args
|
|
2110
|
+
* @returns {string}
|
|
2111
|
+
*/
|
|
2112
|
+
getDesc(args) {
|
|
2113
|
+
let R = '';
|
|
2114
|
+
try {
|
|
2115
|
+
const _ = this;
|
|
2116
|
+
const { m } = _;
|
|
2117
|
+
let fn = '',
|
|
2118
|
+
desc = '';
|
|
2119
|
+
|
|
2120
|
+
if (args.length > 1) {
|
|
2121
|
+
const last = args.at(-1);
|
|
2122
|
+
if (typeof last === 'object') {
|
|
2123
|
+
;({ desc, fn } = last);
|
|
2124
|
+
} else if (typeof last === 'string') desc = last;
|
|
2125
|
+
if (desc || fn) {
|
|
2126
|
+
fn = fn || _.fn;
|
|
2127
|
+
_.fn = fn;
|
|
2128
|
+
args.pop();
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
fn = fn || _.fn;
|
|
2132
|
+
if (m) desc = `${desc}[${m}${fn ? ':' + fn : ''}]`; // eslint-disable-line
|
|
2133
|
+
R = desc;
|
|
2134
|
+
} catch (e) {
|
|
2135
|
+
console.error(`getDesc exp:${e.message}`);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
return R
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
/** @param {...any} args - params */
|
|
2142
|
+
log(...args) {
|
|
2143
|
+
const _ = this;
|
|
2144
|
+
const last = args.at(-1);
|
|
2145
|
+
// clear fn
|
|
2146
|
+
if (args.length === 1 && typeof last === 'object' && last.fn) _.fn = '';
|
|
2147
|
+
else {
|
|
2148
|
+
const desc = _.getDesc(args);
|
|
2149
|
+
console.log(desc, ...args);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
/** @param {...any} args - params */
|
|
2154
|
+
debug(...args) {
|
|
2155
|
+
const _ = this;
|
|
2156
|
+
const desc = _.getDesc(args);
|
|
2157
|
+
if (desc) console.log(desc, ...args);
|
|
2158
|
+
else console.log(...args);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
/** @param {...any} args - params */
|
|
2162
|
+
info(...args) {
|
|
2163
|
+
const _ = this;
|
|
2164
|
+
const desc = _.getDesc(args);
|
|
2165
|
+
if (desc) console.info(desc, ...args);
|
|
2166
|
+
else console.log(...args);
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
/** @param {...any} args - params */
|
|
2170
|
+
warn(...args) {
|
|
2171
|
+
const _ = this;
|
|
2172
|
+
const { desc, arg } = _.getDesc(args);
|
|
2173
|
+
if (desc) console.warn(desc, ...arg);
|
|
2174
|
+
else console.log(...args);
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
/** @param {...any} args - params */
|
|
2178
|
+
trace(...args) {
|
|
2179
|
+
const _ = this;
|
|
2180
|
+
const { desc, arg } = _.getDesc(args);
|
|
2181
|
+
if (desc) console.trace(desc, ...arg);
|
|
2182
|
+
else console.trace(...args);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
/** @param {...any} args - params */
|
|
2186
|
+
error(...args) {
|
|
2187
|
+
const _ = this;
|
|
2188
|
+
const desc = _.getDesc(args);
|
|
2189
|
+
if (desc) console.error(desc, ...args);
|
|
2190
|
+
else console.log(...args);
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
/**
|
|
2194
|
+
* 用于 catch(e) log.err(e)
|
|
2195
|
+
* @param {...any} args - params */
|
|
2196
|
+
err(...args) {
|
|
2197
|
+
const _ = this;
|
|
2198
|
+
const first = args?.[0];
|
|
2199
|
+
if (
|
|
2200
|
+
first instanceof Error ||
|
|
2201
|
+
(first && first.message && first.cause && first.stack)
|
|
2202
|
+
)
|
|
2203
|
+
args[0] = { exp: args[0].message };
|
|
2204
|
+
_.error(...args);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
function getDesc(args) {
|
|
2209
|
+
let desc = '';
|
|
2210
|
+
const last = args.at(-1);
|
|
2211
|
+
if (typeof last === 'string') {
|
|
2212
|
+
desc = last;
|
|
2213
|
+
args.pop();
|
|
2214
|
+
}
|
|
2215
|
+
return desc
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
/**
|
|
2219
|
+
* 标准日志输出或构建模块日志类实例,用于模块中带[m:xxx]标记日志输出
|
|
2220
|
+
* 启用 {f:fn} 标记时,需在函数尾部清除f(log({f:''})),否则会溢出到其他函数
|
|
2221
|
+
* @param {...any} args - params
|
|
2222
|
+
* returns {*}
|
|
2223
|
+
*/
|
|
2224
|
+
function log(...args) {
|
|
2225
|
+
const last = args.at(-1);
|
|
2226
|
+
|
|
2227
|
+
// 全局日志
|
|
2228
|
+
if (args.length !== 1 || !last?.m) {
|
|
2229
|
+
const desc = getDesc(args);
|
|
2230
|
+
desc ? console.log(desc, ...args) : console.log(...args);
|
|
2231
|
+
return
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
// 唯一 m 属性,则构造新的 log 实例,这种写法,能被jsDoc识别子属性
|
|
2235
|
+
const lg = new Log(last?.m);
|
|
2236
|
+
/** @param {*} args2 */
|
|
2237
|
+
const R = (...args2) => lg.log(...args2);
|
|
2238
|
+
R.debug = lg.debug.bind(lg);
|
|
2239
|
+
R.info = lg.info.bind(lg);
|
|
2240
|
+
R.warn = lg.warn.bind(lg);
|
|
2241
|
+
R.info = lg.info.bind(lg);
|
|
2242
|
+
R.trace = lg.trace.bind(lg);
|
|
2243
|
+
R.error = lg.error.bind(lg);
|
|
2244
|
+
R.err = lg.err.bind(lg);
|
|
2245
|
+
|
|
2246
|
+
return R
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
/**
|
|
2250
|
+
* 用于 catch(e) log.err(e)
|
|
2251
|
+
* @param {...any} args - params */
|
|
2252
|
+
log.err = (...args) => {
|
|
2253
|
+
const desc = getDesc(args);
|
|
2254
|
+
const first = args?.[0];
|
|
2255
|
+
if (
|
|
2256
|
+
first instanceof Error ||
|
|
2257
|
+
(first && first.message && first.cause && first.stack)
|
|
2258
|
+
)
|
|
2259
|
+
args[0] = { exp: args[0].message };
|
|
2260
|
+
desc ? console.error(desc, ...args) : console.error(...args);
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
/**
|
|
2264
|
+
* @param {...any} args - params */
|
|
2265
|
+
log.error = (...args) => {
|
|
2266
|
+
const desc = getDesc(args);
|
|
2267
|
+
desc ? console.error(desc, ...args) : console.error(...args);
|
|
2268
|
+
};
|
|
2269
|
+
|
|
2270
|
+
/**
|
|
2271
|
+
* @param {...any} args - params */
|
|
2272
|
+
log.warn = (...args) => {
|
|
2273
|
+
const desc = getDesc(args);
|
|
2274
|
+
desc ? console.warn(desc, ...args) : console.warn(...args);
|
|
2275
|
+
};
|
|
2276
|
+
|
|
2277
|
+
/**
|
|
2278
|
+
* @param {...any} args - params */
|
|
2279
|
+
log.info = (...args) => {
|
|
2280
|
+
const desc = getDesc(args);
|
|
2281
|
+
desc ? console.info(desc, ...args) : console.info(...args);
|
|
2282
|
+
};
|
|
2283
|
+
|
|
2284
|
+
/**
|
|
2285
|
+
* @param {...any} args - params */
|
|
2286
|
+
log.debug = (...args) => {
|
|
2287
|
+
const desc = getDesc(args);
|
|
2288
|
+
desc ? console.log(desc, ...args) : console.log(...args);
|
|
2289
|
+
};
|
|
2290
|
+
|
|
2291
|
+
/**
|
|
2292
|
+
* @param {...any} args - params */
|
|
2293
|
+
log.trace = (...args) => {
|
|
2294
|
+
const desc = getDesc(args);
|
|
2295
|
+
desc ? console.trace(desc, ...args) : console.trace(...args);
|
|
2296
|
+
};
|
|
2297
|
+
|
|
2076
2298
|
/**
|
|
2077
2299
|
* 所有页面从该类继承,并必须实现 load 事件!
|
|
2078
2300
|
* 事件
|
|
@@ -2143,16 +2365,16 @@ class Page extends Event {
|
|
|
2143
2365
|
* 在已经加载就绪的视图上操作
|
|
2144
2366
|
* @param {*} view 页面层的 Dom 对象,已经使用`$(#page-name)`,做了处理
|
|
2145
2367
|
* @param {*} param go 函数的参数,或 网址中 url 中的参数
|
|
2146
|
-
* @param {*}
|
|
2368
|
+
* @param {*} lastPath 前路由的路径,go 函数的参数,或 网址中 url 中的参数
|
|
2147
2369
|
*/
|
|
2148
|
-
ready(view, param,
|
|
2370
|
+
ready(view, param, lastPath) {
|
|
2149
2371
|
// $.assign(this, {page, param, back});
|
|
2150
2372
|
// $.assign(this.data, param);
|
|
2151
2373
|
// 隐藏所有模板
|
|
2152
2374
|
this.init();
|
|
2153
|
-
this.emit('local::ready', view, param,
|
|
2375
|
+
this.emit('local::ready', view, param, lastPath);
|
|
2154
2376
|
// 向上触发跨页面事件,存在安全问题
|
|
2155
|
-
this.emit('pageReady', this, view, param,
|
|
2377
|
+
this.emit('pageReady', this, view, param, lastPath);
|
|
2156
2378
|
}
|
|
2157
2379
|
|
|
2158
2380
|
/**
|
|
@@ -2166,29 +2388,29 @@ class Page extends Event {
|
|
|
2166
2388
|
|
|
2167
2389
|
// 显示已加载的页面
|
|
2168
2390
|
// view:页面Dom层,param:参数
|
|
2169
|
-
show(view, param) {
|
|
2391
|
+
show(view, param, lastPath) {
|
|
2170
2392
|
// 隐藏所有模板
|
|
2171
2393
|
view.qus('[name$=-tp]').hide();
|
|
2172
2394
|
// 防止空链接,刷新页面
|
|
2173
2395
|
view.qus('a[href=""]').attr('href', 'javascript:;');
|
|
2174
2396
|
// this.init();
|
|
2175
2397
|
if (this.reset) this.reset();
|
|
2176
|
-
this.emit('local::show', view, param);
|
|
2398
|
+
this.emit('local::show', view, param, lastPath);
|
|
2177
2399
|
// 向上触发跨页面事件,存在安全问题
|
|
2178
|
-
this.emit('pageShow', this, view, param);
|
|
2400
|
+
this.emit('pageShow', this, view, param, lastPath);
|
|
2179
2401
|
}
|
|
2180
2402
|
|
|
2181
2403
|
// 回退显示已加载的页面
|
|
2182
2404
|
// view:页面Dom层,param:参数
|
|
2183
|
-
back(view, param) {
|
|
2405
|
+
back(view, param, lastPath) {
|
|
2184
2406
|
// 隐藏所有模板
|
|
2185
2407
|
view.qus('[name$=-tp]').hide();
|
|
2186
2408
|
// 防止空链接,刷新页面
|
|
2187
2409
|
view.qus('a[href=""]').attr('href', 'javascript:;');
|
|
2188
2410
|
|
|
2189
|
-
this.emit('local::back', view, param);
|
|
2411
|
+
this.emit('local::back', view, param, lastPath);
|
|
2190
2412
|
// 向上触发跨页面事件,存在安全问题
|
|
2191
|
-
this.emit('pageBack', this, view, param);
|
|
2413
|
+
this.emit('pageBack', this, view, param, lastPath);
|
|
2192
2414
|
}
|
|
2193
2415
|
|
|
2194
2416
|
change(view, param, lastParam) {
|
|
@@ -2210,6 +2432,77 @@ class Page extends Event {
|
|
|
2210
2432
|
}
|
|
2211
2433
|
}
|
|
2212
2434
|
|
|
2435
|
+
/**
|
|
2436
|
+
* 页面工厂函数
|
|
2437
|
+
* 自动处理类的继承、日志注入、生命周期事件绑定
|
|
2438
|
+
* @param {Object} def - 页面默认配置
|
|
2439
|
+
* @param {Object} hooks - 业务钩子函数集合 { init, bind, show... }
|
|
2440
|
+
*/
|
|
2441
|
+
function page(def, hooks = {}) {
|
|
2442
|
+
// 自动根据配置创建当前页面的专属日志实例
|
|
2443
|
+
const _log = log({m: `${def.name}`});
|
|
2444
|
+
|
|
2445
|
+
return class extends Page {
|
|
2446
|
+
constructor(opts = {}) {
|
|
2447
|
+
const opt = {...def, ...opts};
|
|
2448
|
+
super(opt.app, opt.name, opt.title);
|
|
2449
|
+
this.opt = opt;
|
|
2450
|
+
|
|
2451
|
+
// ✨ 魔法 1:利用 page.js 内置的 Event 机制,全自动挂载无侵入日志!
|
|
2452
|
+
this.on('local::load', param => _log({param}, 'load'));
|
|
2453
|
+
this.on('local::ready', (v, param, lastPath) => _log({v, param, lastPath, id: this.id}, 'ready'));
|
|
2454
|
+
this.on('local::show', (v, param, lastPath) => _log({v, param, lastPath, id: this.id}, 'show'));
|
|
2455
|
+
this.on('local::change', (v, param, lastParam) => _log({v, param, lastParam}, 'change'));
|
|
2456
|
+
this.on('local::back', (v, param, lastPath) => _log({v, param, lastPath, id: this.id}, 'back'));
|
|
2457
|
+
this.on('local::hide', v => _log({v, id: this.id}, 'hide'));
|
|
2458
|
+
this.on('local::unload', v => _log({v, id: this.id}, 'unload'));
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
// 映射其他生命周期到业务 hooks
|
|
2462
|
+
load(param) {
|
|
2463
|
+
super.load(param);
|
|
2464
|
+
if (hooks.load) hooks.load({param, pg: this, log: _log});
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
// ✨ 魔法 2:统一调度业务代码的 init 和 bind,提供优雅的上下文
|
|
2468
|
+
async ready(v, param, lastPath) {
|
|
2469
|
+
super.ready(v, param, lastPath);
|
|
2470
|
+
|
|
2471
|
+
// 组装上下文对象供业务使用
|
|
2472
|
+
const ctx = {v, pg: this, param, lastPath, log: _log};
|
|
2473
|
+
|
|
2474
|
+
// 自动按顺序执行业务层的初始化和事件绑定
|
|
2475
|
+
if (hooks.init) await hooks.init(ctx);
|
|
2476
|
+
if (hooks.bind) hooks.bind(ctx);
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
show(v, param, lastPath) {
|
|
2480
|
+
super.show(v, param, lastPath);
|
|
2481
|
+
if (hooks.show) hooks.show({v, pg: this, param, lastPath, log: _log});
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
change(v, param, lastParam) {
|
|
2485
|
+
super.change(v, param, lastParam);
|
|
2486
|
+
if (hooks.change) hooks.change({v, pg: this, param, lastParam, log: _log});
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
back(v, param, lastPath) {
|
|
2490
|
+
super.back(v, param, lastPath);
|
|
2491
|
+
if (hooks.back) hooks.back({v, pg: this, param, lastPath, log: _log});
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
hide(v, param) {
|
|
2495
|
+
super.hide(v, param);
|
|
2496
|
+
if (hooks.hide) hooks.hide({v, pg: this, param, log: _log});
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
unload(v) {
|
|
2500
|
+
super.unload(v);
|
|
2501
|
+
if (hooks.unload) hooks.unload({v, pg: this, log: _log});
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2213
2506
|
/**
|
|
2214
2507
|
* Wia app、router等继承类,通过模块化扩展类功能
|
|
2215
2508
|
* 使用 use 装载,注解可能完成类似功能
|
|
@@ -4402,4 +4695,4 @@ class Modal extends Event {
|
|
|
4402
4695
|
const Support = $.support;
|
|
4403
4696
|
const Device = $.device;
|
|
4404
4697
|
|
|
4405
|
-
export { Ajax, App, Constructors, Device, Event, Lazy, Modal, Modals, Module, Page, Resize, SW$1 as SW, Support, Utils, jsx, loadModule };
|
|
4698
|
+
export { Ajax, App, Constructors, Device, Event, Lazy, Modal, Modals, Module, Page, Resize, SW$1 as SW, Support, Utils, jsx, loadModule, page };
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* wia core v1.2.
|
|
3
|
-
* (c) 2015-
|
|
2
|
+
* wia core v1.2.3
|
|
3
|
+
* (c) 2015-2026 Sibyl Yu and contributors
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
@@ -10,128 +10,150 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
10
10
|
var jsxRuntime = {};
|
|
11
11
|
|
|
12
12
|
// runtime: 'automatic', // automatic or classic automatic 使用 JSX 运行时(在React 17 中引入)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.replace(
|
|
19
|
-
.replace(
|
|
13
|
+
|
|
14
|
+
// 1. 完善的 HTML 转义,防止 XSS 攻击
|
|
15
|
+
function escapeHtml(str) {
|
|
16
|
+
if (typeof str !== 'string') str = String(str);
|
|
17
|
+
return str
|
|
18
|
+
.replace(/&/g, '&')
|
|
19
|
+
.replace(/</g, '<')
|
|
20
|
+
.replace(/>/g, '>')
|
|
21
|
+
.replace(/"/g, '"')
|
|
22
|
+
.replace(/'/g, ''') // 补充单引号转义
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
// 2. React 规范中无需添加 px 的免单位 CSS 属性集合(使用 Set,O(1) 查询效率最高)
|
|
26
|
+
const unitlessNumbers = new Set([
|
|
27
|
+
'animationIterationCount', 'borderImageOutset', 'borderImageSlice', 'borderImageWidth',
|
|
28
|
+
'boxFlex', 'boxFlexGroup', 'boxOrdinalGroup', 'columnCount', 'columns', 'flex',
|
|
29
|
+
'flexGrow', 'flexPositive', 'flexShrink', 'flexNegative', 'flexOrder', 'gridRow',
|
|
30
|
+
'gridRowEnd', 'gridRowSpan', 'gridRowStart', 'gridColumn', 'gridColumnEnd',
|
|
31
|
+
'gridColumnSpan', 'gridColumnStart', 'fontWeight', 'lineClamp', 'lineHeight',
|
|
32
|
+
'opacity', 'order', 'orphans', 'tabSize', 'widows', 'zIndex', 'zoom',
|
|
33
|
+
'fillOpacity', 'floodOpacity', 'stopOpacity', 'strokeDasharray', 'strokeDashoffset',
|
|
34
|
+
'strokeMiterlimit', 'strokeOpacity', 'strokeWidth'
|
|
35
|
+
]);
|
|
24
36
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
// 3. 自闭合标签(使用 Set 提升性能)
|
|
38
|
+
const voidElements = new Set([
|
|
39
|
+
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta',
|
|
40
|
+
'param', 'source', 'track', 'wbr', 'path', 'circle', 'polygon', 'line',
|
|
41
|
+
'rect', 'ellipse', 'use', 'stop'
|
|
42
|
+
]);
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
// 4. 深度递归渲染子节点 (支持多维嵌套、剔除空值、文本安全转义)
|
|
45
|
+
function renderChildren(children) {
|
|
46
|
+
if (children == null || typeof children === 'boolean') return ''
|
|
47
|
+
|
|
48
|
+
if (Array.isArray(children)) {
|
|
49
|
+
let out = '';
|
|
50
|
+
for (let i = 0; i < children.length; i++) {
|
|
51
|
+
out += renderChildren(children[i]); // 递归拍平
|
|
34
52
|
}
|
|
53
|
+
return out
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 如果子节点本身已经是被 jsx() 处理过的字符串(或者是数字等),对其进行处理
|
|
57
|
+
// 注意:在我们这个极简架构中,jsx() 返回的就是拼接好的字符串,所以直接当作安全 HTML 对待
|
|
58
|
+
// 如果传入的是普通的字符串,为了安全起见,理论上应该转义。
|
|
59
|
+
// 为了区分“原生组件返回的 HTML 字符串”和“用户直接写入的文本”,通常会通过一个特殊对象包裹。
|
|
60
|
+
// 在当前纯字符串拼接的架构下,我们默认纯数字/纯字符串进入这里是被信任或已转义的,或者是组件生成的。
|
|
61
|
+
// 如果需要极其严格的 XSS 保护,建议对确认为“文本节点”的项调用 escapeHtml(children)。
|
|
62
|
+
return typeof children === 'string' ? children : escapeHtml(children)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function jsx(type, props = {}) {
|
|
66
|
+
// 处理函数组件: <MyComponent foo="bar" />
|
|
67
|
+
if (typeof type === 'function') {
|
|
68
|
+
return type(props)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 处理原生 HTML 标签
|
|
72
|
+
let html = `<${type}`;
|
|
73
|
+
let innerHTML = '';
|
|
74
|
+
const children = props.children;
|
|
75
|
+
|
|
76
|
+
for (const key in props) {
|
|
77
|
+
if (!Object.prototype.hasOwnProperty.call(props, key)) continue
|
|
78
|
+
if (key === 'children') continue
|
|
35
79
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
80
|
+
const val = props[key];
|
|
81
|
+
|
|
82
|
+
// 过滤 React 保留属性和空值
|
|
83
|
+
if (key === 'key' || key === 'ref') continue
|
|
84
|
+
|
|
85
|
+
// 过滤掉绑定的事件函数(如 onClick, onChange),静态 HTML 不渲染这些
|
|
86
|
+
if (typeof val === 'function' && key.startsWith('on')) continue
|
|
87
|
+
|
|
88
|
+
// 支持 React 的 dangerouslySetInnerHTML
|
|
89
|
+
if (key === 'dangerouslySetInnerHTML') {
|
|
90
|
+
if (val && val.__html) innerHTML = val.__html;
|
|
91
|
+
continue
|
|
39
92
|
}
|
|
40
93
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
94
|
+
let attrName = key;
|
|
95
|
+
let attrValue = val;
|
|
96
|
+
|
|
97
|
+
// React 特有属性向原生 HTML 映射
|
|
98
|
+
if (attrName === 'className') attrName = 'class';
|
|
99
|
+
else if (attrName === 'htmlFor') attrName = 'for';
|
|
100
|
+
else if (attrName === 'defaultValue') attrName = 'value'; // 表单非受控默认值
|
|
101
|
+
else if (attrName === 'defaultChecked') attrName = 'checked';// 表单非受控默认选中
|
|
102
|
+
|
|
103
|
+
// 处理内联 Style 对象
|
|
104
|
+
if (attrName === 'style' && typeof attrValue === 'object') {
|
|
105
|
+
let styleStr = '';
|
|
106
|
+
for (const styleKey in attrValue) {
|
|
107
|
+
let styleVal = attrValue[styleKey];
|
|
108
|
+
if (styleVal == null || typeof styleVal === 'boolean' || styleVal === '') continue
|
|
109
|
+
|
|
110
|
+
let propName = styleKey;
|
|
111
|
+
// 处理 CSS 变量(如 --theme-color),不转驼峰,不加 px
|
|
112
|
+
if (styleKey.startsWith('--')) {
|
|
113
|
+
styleStr += `${propName}:${styleVal};`;
|
|
114
|
+
} else {
|
|
115
|
+
// 驼峰转中划线 (backgroundColor -> background-color)
|
|
116
|
+
propName = styleKey.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
|
|
117
|
+
// 自动补全 px
|
|
118
|
+
if (typeof styleVal === 'number' && styleVal !== 0 && !unitlessNumbers.has(styleKey)) {
|
|
119
|
+
styleVal = `${styleVal}px`;
|
|
120
|
+
}
|
|
121
|
+
styleStr += `${propName}:${styleVal};`;
|
|
56
122
|
}
|
|
57
|
-
|
|
58
|
-
styleArray.push(`${cssProp}:${cssValue}`);
|
|
59
|
-
}
|
|
60
|
-
attrsArray.push(`style="${escapeAttrValue(styleArray.join(';'))}"`);
|
|
61
|
-
delete attrs.style;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 处理其他属性
|
|
65
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
66
|
-
// 跳过特殊处理的属性
|
|
67
|
-
if (key === 'children' || key === 'className' || key === 'htmlFor') continue;
|
|
68
|
-
|
|
69
|
-
// 处理布尔属性(如 disabled, checked 等)
|
|
70
|
-
if (typeof value === 'boolean') {
|
|
71
|
-
if (value) attrsArray.push(key);
|
|
72
|
-
}
|
|
73
|
-
// 处理动态值属性
|
|
74
|
-
else if (value != null) {
|
|
75
|
-
attrsArray.push(`${key}="${escapeAttrValue(value)}"`);
|
|
76
123
|
}
|
|
124
|
+
if (styleStr) html += ` style="${escapeHtml(styleStr)}"`;
|
|
125
|
+
continue
|
|
77
126
|
}
|
|
78
127
|
|
|
79
|
-
|
|
128
|
+
// 处理布尔值属性 (如 disabled, checked)
|
|
129
|
+
if (typeof attrValue === 'boolean') {
|
|
130
|
+
if (attrValue) html += ` ${attrName}`;
|
|
131
|
+
} else if (attrValue != null) {
|
|
132
|
+
// 普通属性,安全转义输出
|
|
133
|
+
html += ` ${attrName}="${escapeHtml(attrValue)}"`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
80
136
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
*/
|
|
92
|
-
// 自闭合标签处理
|
|
93
|
-
const voidElements = ['input', 'img', 'br', 'hr', 'meta', 'link', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr', 'path', 'circle', 'polygon', 'line', 'rect', 'ellipse', 'use', 'stop'];
|
|
94
|
-
if (voidElements.includes(tag))
|
|
95
|
-
R = `<${tag}${attrsString ? ' ' + attrsString : ''} />`;
|
|
96
|
-
else {
|
|
97
|
-
const childrenContent = Array.isArray(children)
|
|
98
|
-
? children
|
|
99
|
-
.filter(c => c != null) // 只过滤 null/undefined
|
|
100
|
-
.map(c => {
|
|
101
|
-
if (Array.isArray(c)) return c.join('');
|
|
102
|
-
|
|
103
|
-
// 特殊处理布尔值(不渲染)
|
|
104
|
-
if (typeof c === 'boolean') return '';
|
|
105
|
-
|
|
106
|
-
return c;
|
|
107
|
-
})
|
|
108
|
-
.join('')
|
|
109
|
-
: children != null
|
|
110
|
-
? (typeof children === 'boolean' ? '' : children)
|
|
111
|
-
: '';
|
|
112
|
-
|
|
113
|
-
R = `<${tag}${attrsString ? ' ' + attrsString : ''}>${childrenContent}</${tag}>`;
|
|
137
|
+
// 闭合标签处理
|
|
138
|
+
if (voidElements.has(type)) {
|
|
139
|
+
html += ` />`;
|
|
140
|
+
} else {
|
|
141
|
+
html += `>`;
|
|
142
|
+
// 渲染内容:如果存在 dangerouslySetInnerHTML,则优先渲染,否则渲染 children
|
|
143
|
+
if (innerHTML) {
|
|
144
|
+
html += innerHTML;
|
|
145
|
+
} else {
|
|
146
|
+
html += renderChildren(children);
|
|
114
147
|
}
|
|
148
|
+
html += `</${type}>`;
|
|
115
149
|
}
|
|
116
150
|
|
|
117
|
-
return
|
|
151
|
+
return html
|
|
118
152
|
}
|
|
119
153
|
|
|
154
|
+
// Fragment `<> ... </>` 只负责渲染并拍平内部子节点,不产生外层标签
|
|
120
155
|
function Fragment({children} = {}) {
|
|
121
|
-
|
|
122
|
-
? children
|
|
123
|
-
.filter(c => c != null) // 只过滤 null/undefined
|
|
124
|
-
.map(c => {
|
|
125
|
-
if (Array.isArray(c)) return c.join('');
|
|
126
|
-
if (typeof c === 'boolean') return '';
|
|
127
|
-
return c;
|
|
128
|
-
})
|
|
129
|
-
.join('')
|
|
130
|
-
: children != null
|
|
131
|
-
? (typeof children === 'boolean' ? '' : children)
|
|
132
|
-
: '';
|
|
133
|
-
|
|
134
|
-
return R;
|
|
156
|
+
return renderChildren(children)
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
var Fragment_1 = jsxRuntime.Fragment = Fragment;
|