@wsxjs/wsx-router 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,409 @@
1
+ import { createLogger as y, autoRegister as E, WebComponent as S, jsx as f } from "@wsxjs/wsx-core";
2
+ const Y = ":host{display:block;width:100%;height:100%}.router-outlet{width:100%;height:100%}";
3
+ var Z = Object.create, R = Object.defineProperty, tt = Object.getOwnPropertyDescriptor, O = (e, t) => (t = Symbol[e]) ? t : Symbol.for("Symbol." + e), N = (e) => {
4
+ throw TypeError(e);
5
+ }, j = (e, t, r) => t in e ? R(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r, et = (e, t) => R(e, "name", { value: t, configurable: !0 }), rt = (e) => [, , , Z((e == null ? void 0 : e[O("metadata")]) ?? null)], ot = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"], z = (e) => e !== void 0 && typeof e != "function" ? N("Function expected") : e, it = (e, t, r, i, o) => ({ kind: ot[e], name: t, metadata: i, addInitializer: (n) => r._ ? N("Already initialized") : o.push(z(n || null)) }), nt = (e, t) => j(t, O("metadata"), e[3]), st = (e, t, r, i) => {
6
+ for (var o = 0, n = e[t >> 1], s = n && n.length; o < s; o++) n[o].call(r);
7
+ return i;
8
+ }, at = (e, t, r, i, o, n) => {
9
+ var s, l, c, a = t & 7, h = !1, v = 0, w = e[v] || (e[v] = []), d = a && (o = o.prototype, a < 5 && (a > 3 || !h) && tt(o, r));
10
+ et(o, r);
11
+ for (var u = i.length - 1; u >= 0; u--)
12
+ c = it(a, r, l = {}, e[3], w), s = (0, i[u])(o, c), l._ = 1, z(s) && (o = s);
13
+ return nt(e, o), d && R(o, r, d), h ? a ^ 4 ? n : d : o;
14
+ }, g = (e, t, r) => j(e, typeof t != "symbol" ? t + "" : t, r), D, C, F;
15
+ const p = y("WsxRouter");
16
+ D = [E({ tagName: "wsx-router" })];
17
+ class $ extends (F = S) {
18
+ constructor() {
19
+ super({ styles: Y }), g(this, "views", /* @__PURE__ */ new Map()), g(this, "currentView", null), g(this, "handleRouteChange", () => {
20
+ const t = window.location.pathname;
21
+ p.debug(`Route changed to: ${t}`), this.currentView && (this.currentView.style.display = "none", p.debug("Hiding previous view"));
22
+ const r = this.matchRoute(t);
23
+ if (r) {
24
+ r.style.display = "block", this.currentView = r, p.debug(`Showing view for route: ${r.getAttribute("route")}`);
25
+ const i = this.extractParams(r.getAttribute("route") || "/", t);
26
+ i && r.setAttribute("params", JSON.stringify(i));
27
+ } else
28
+ p.warn(`No view found for path: ${t}`);
29
+ this.dispatchEvent(
30
+ new CustomEvent("route-changed", {
31
+ detail: { path: t, view: r },
32
+ bubbles: !0,
33
+ composed: !0
34
+ })
35
+ );
36
+ }), g(this, "interceptLinks", (t) => {
37
+ const r = t.target.closest("a");
38
+ if (!r) return;
39
+ const i = r.getAttribute("href");
40
+ !i || i.startsWith("http") || i.startsWith("#") || (t.preventDefault(), this.navigate(i));
41
+ });
42
+ }
43
+ render() {
44
+ return /* @__PURE__ */ f("div", { class: "router-outlet" }, /* @__PURE__ */ f("slot", null));
45
+ }
46
+ onConnected() {
47
+ p.debug("WsxRouter connected to DOM"), this.collectViews(), p.debug("WsxRouter collected views:", this.views.size), window.addEventListener("popstate", this.handleRouteChange), this.addEventListener("click", this.interceptLinks), this.handleRouteChange();
48
+ }
49
+ onDisconnected() {
50
+ window.removeEventListener("popstate", this.handleRouteChange);
51
+ }
52
+ collectViews() {
53
+ var o;
54
+ const t = (o = this.shadowRoot) == null ? void 0 : o.querySelector("slot");
55
+ if (!t) {
56
+ p.error("WsxRouter: No slot found");
57
+ return;
58
+ }
59
+ const i = t.assignedElements().filter((n) => n.tagName.toLowerCase() === "wsx-view");
60
+ p.debug("WsxRouter found views:", i.length), i.forEach((n) => {
61
+ const s = n.getAttribute("route") || "/";
62
+ this.views.set(s, n), n.style.display = "none", p.debug(`WsxRouter hiding view for route: ${s}`);
63
+ });
64
+ }
65
+ matchRoute(t) {
66
+ if (this.views.has(t))
67
+ return this.views.get(t);
68
+ for (const [r, i] of this.views)
69
+ if (r.includes(":")) {
70
+ const o = r.replace(/:[^/]+/g, "([^/]+)");
71
+ if (new RegExp(`^${o}$`).test(t))
72
+ return i;
73
+ }
74
+ return this.views.get("*") || null;
75
+ }
76
+ extractParams(t, r) {
77
+ var c;
78
+ if (!t.includes(":")) return null;
79
+ const i = ((c = t.match(/:([^/]+)/g)) == null ? void 0 : c.map((a) => a.slice(1))) || [], o = t.replace(/:[^/]+/g, "([^/]+)"), n = new RegExp(`^${o}$`), s = r.match(n);
80
+ if (!s || !i.length) return null;
81
+ const l = {};
82
+ return i.forEach((a, h) => {
83
+ l[a] = s[h + 1];
84
+ }), l;
85
+ }
86
+ /**
87
+ * 编程式导航 - 使用原生 History API
88
+ */
89
+ navigate(t) {
90
+ window.history.pushState(null, "", t), this.handleRouteChange();
91
+ }
92
+ }
93
+ C = rt(F);
94
+ $ = at(C, 0, "WsxRouter", D, $);
95
+ st(C, 1, $);
96
+ const ct = ":host{display:block;width:100%;height:100%}.route-view{width:100%;height:100%;position:relative}:host([loading]) .route-view{opacity:.5;transition:opacity .3s ease}:host([error]) .route-view{border:1px solid var(--error-color, #ff0000);padding:1rem}.route-view.entering{animation:fadeIn .3s ease-out}.route-view.leaving{animation:fadeOut .3s ease-in}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}";
97
+ var lt = Object.create, A = Object.defineProperty, ht = Object.getOwnPropertyDescriptor, U = (e, t) => (t = Symbol[e]) ? t : Symbol.for("Symbol." + e), M = (e) => {
98
+ throw TypeError(e);
99
+ }, q = (e, t, r) => t in e ? A(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r, dt = (e, t) => A(e, "name", { value: t, configurable: !0 }), ut = (e) => [, , , lt((e == null ? void 0 : e[U("metadata")]) ?? null)], pt = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"], V = (e) => e !== void 0 && typeof e != "function" ? M("Function expected") : e, vt = (e, t, r, i, o) => ({ kind: pt[e], name: t, metadata: i, addInitializer: (n) => r._ ? M("Already initialized") : o.push(V(n || null)) }), mt = (e, t) => q(t, U("metadata"), e[3]), wt = (e, t, r, i) => {
100
+ for (var o = 0, n = e[t >> 1], s = n && n.length; o < s; o++) n[o].call(r);
101
+ return i;
102
+ }, ft = (e, t, r, i, o, n) => {
103
+ var s, l, c, a = t & 7, h = !1, v = 0, w = e[v] || (e[v] = []), d = a && (o = o.prototype, a < 5 && (a > 3 || !h) && ht(o, r));
104
+ dt(o, r);
105
+ for (var u = i.length - 1; u >= 0; u--)
106
+ c = vt(a, r, l = {}, e[3], w), s = (0, i[u])(o, c), l._ = 1, V(s) && (o = s);
107
+ return mt(e, o), d && A(o, r, d), h ? a ^ 4 ? n : d : o;
108
+ }, _ = (e, t, r) => q(e, typeof t != "symbol" ? t + "" : t, r), Q, P, T;
109
+ const k = y("WsxView");
110
+ Q = [E({ tagName: "wsx-view" })];
111
+ class b extends (T = S) {
112
+ constructor() {
113
+ super({
114
+ styles: ct,
115
+ styleName: "wsx-view"
116
+ }), _(this, "component", null), _(this, "params", {}), _(this, "componentInstance", null);
117
+ }
118
+ render() {
119
+ return /* @__PURE__ */ f("div", { class: "route-view", part: "view" });
120
+ }
121
+ onConnected() {
122
+ const t = this.getAttribute("component");
123
+ t && !this.componentInstance && this.loadComponent(t);
124
+ }
125
+ onAttributeChanged(t, r, i) {
126
+ if (t === "component" && i && !this.componentInstance)
127
+ this.loadComponent(i);
128
+ else if (t === "params" && this.componentInstance)
129
+ try {
130
+ this.params = JSON.parse(i), Object.entries(this.params).forEach(([o, n]) => {
131
+ this.componentInstance.setAttribute(o, n);
132
+ });
133
+ } catch (o) {
134
+ k.error("Failed to parse params:", o);
135
+ }
136
+ }
137
+ async loadComponent(t) {
138
+ var o;
139
+ if (this.componentInstance && (this.componentInstance.remove(), this.componentInstance = null), !customElements.get(t)) {
140
+ k.warn(`Component ${t} not found in customElements registry`);
141
+ return;
142
+ }
143
+ this.componentInstance = document.createElement(t), Object.keys(this.params).length > 0 && Object.entries(this.params).forEach(([n, s]) => {
144
+ this.componentInstance.setAttribute(n, s);
145
+ });
146
+ const i = (o = this.shadowRoot) == null ? void 0 : o.querySelector(".route-view");
147
+ i ? i.appendChild(this.componentInstance) : k.error("Route view container not found");
148
+ }
149
+ onDisconnected() {
150
+ this.componentInstance && (this.componentInstance.remove(), this.componentInstance = null);
151
+ }
152
+ }
153
+ P = ut(T);
154
+ b = ft(P, 0, "WsxView", Q, b);
155
+ _(b, "observedAttributes", ["route", "component", "params"]);
156
+ wt(P, 1, b);
157
+ const gt = ':host{display:inline-block;min-width:fit-content;min-height:fit-content;width:auto;height:auto}.wsx-link{color:var(--link-color, #007bff);text-decoration:var(--link-decoration, underline);cursor:pointer;transition:color .2s ease;display:inline-block;min-height:1.2em;line-height:1.2}.wsx-link:hover{color:var(--link-hover-color, #0056b3);text-decoration:var(--link-hover-decoration, underline)}.wsx-link:focus{outline:2px solid var(--link-focus-color, #007bff);outline-offset:2px}.wsx-link.active{color:var(--link-active-color, #6c757d);font-weight:var(--link-active-weight, bold)}:host([disabled]) .wsx-link{color:var(--link-disabled-color, #6c757d);cursor:not-allowed;pointer-events:none}:host([external]) .wsx-link:after{content:"↗";font-size:.8em;margin-left:.2em;opacity:.7}:host([variant="button"]) .wsx-link{background-color:var(--button-bg, #007bff);color:var(--button-color, white);padding:.5rem 1rem;border-radius:.25rem;text-decoration:none;display:inline-block}:host([variant="button"]) .wsx-link:hover{background-color:var(--button-hover-bg, #0056b3);color:var(--button-hover-color, white)}:host([variant="tab"]) .wsx-link{padding:.5rem 1rem;border-bottom:2px solid transparent;text-decoration:none}:host([variant="tab"]) .wsx-link.active{border-bottom-color:var(--tab-active-border, #007bff)}';
158
+ var _t = Object.create, L = Object.defineProperty, bt = Object.getOwnPropertyDescriptor, H = (e, t) => (t = Symbol[e]) ? t : Symbol.for("Symbol." + e), J = (e) => {
159
+ throw TypeError(e);
160
+ }, B = (e, t, r) => t in e ? L(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r, xt = (e, t) => L(e, "name", { value: t, configurable: !0 }), yt = (e) => [, , , _t((e == null ? void 0 : e[H("metadata")]) ?? null)], kt = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"], G = (e) => e !== void 0 && typeof e != "function" ? J("Function expected") : e, $t = (e, t, r, i, o) => ({ kind: kt[e], name: t, metadata: i, addInitializer: (n) => r._ ? J("Already initialized") : o.push(G(n || null)) }), Et = (e, t) => B(t, H("metadata"), e[3]), St = (e, t, r, i) => {
161
+ for (var o = 0, n = e[t >> 1], s = n && n.length; o < s; o++) n[o].call(r);
162
+ return i;
163
+ }, Rt = (e, t, r, i, o, n) => {
164
+ var s, l, c, a = t & 7, h = !1, v = 0, w = e[v] || (e[v] = []), d = a && (o = o.prototype, a < 5 && (a > 3 || !h) && bt(o, r));
165
+ xt(o, r);
166
+ for (var u = i.length - 1; u >= 0; u--)
167
+ c = $t(a, r, l = {}, e[3], w), s = (0, i[u])(o, c), l._ = 1, G(s) && (o = s);
168
+ return Et(e, o), d && L(o, r, d), h ? a ^ 4 ? n : d : o;
169
+ }, m = (e, t, r) => B(e, typeof t != "symbol" ? t + "" : t, r), K, W, X;
170
+ const I = y("WsxLink");
171
+ K = [E({ tagName: "wsx-link" })];
172
+ class x extends (X = S) {
173
+ constructor() {
174
+ super({
175
+ styles: gt,
176
+ styleName: "wsx-link"
177
+ }), m(this, "to", ""), m(this, "replace", !1), m(this, "activeClass", "active"), m(this, "exact", !1), m(this, "handleClick", (t) => {
178
+ if (t.preventDefault(), !this.to) {
179
+ I.warn("No 'to' attribute specified");
180
+ return;
181
+ }
182
+ if (this.isExternalLink(this.to)) {
183
+ window.open(this.to, "_blank");
184
+ return;
185
+ }
186
+ this.replace ? window.history.replaceState(null, "", this.to) : window.history.pushState(null, "", this.to), window.dispatchEvent(new PopStateEvent("popstate")), I.debug(`Navigated to: ${this.to}`);
187
+ }), m(this, "updateActiveState", () => {
188
+ var o;
189
+ const t = window.location.pathname, r = this.exact ? t === this.to : t.startsWith(this.to) && this.to !== "/", i = (o = this.shadowRoot) == null ? void 0 : o.querySelector("a");
190
+ i && (r ? (i.classList.add(this.activeClass), this.setAttribute("active", "")) : (i.classList.remove(this.activeClass), this.removeAttribute("active")));
191
+ });
192
+ }
193
+ render() {
194
+ return /* @__PURE__ */ f("a", { href: this.to, class: "wsx-link", onClick: this.handleClick, part: "link" }, /* @__PURE__ */ f("slot", null));
195
+ }
196
+ onConnected() {
197
+ this.to = this.getAttribute("to") || "", this.replace = this.hasAttribute("replace"), this.activeClass = this.getAttribute("active-class") || "active", this.exact = this.hasAttribute("exact");
198
+ const t = this.shadowRoot.querySelector(".wsx-link");
199
+ t && (t.href = this.to), window.addEventListener("popstate", this.updateActiveState), document.addEventListener("route-changed", this.updateActiveState), this.updateActiveState();
200
+ }
201
+ onDisconnected() {
202
+ window.removeEventListener("popstate", this.updateActiveState), document.removeEventListener("route-changed", this.updateActiveState);
203
+ }
204
+ onAttributeChanged(t, r, i) {
205
+ switch (t) {
206
+ case "to":
207
+ this.to = i || "", this.rerender(), this.updateActiveState();
208
+ break;
209
+ case "replace":
210
+ this.replace = i !== null && i !== "false";
211
+ break;
212
+ case "active-class":
213
+ this.activeClass = i || "active", this.updateActiveState();
214
+ break;
215
+ case "exact":
216
+ this.exact = i !== null && i !== "false", this.updateActiveState();
217
+ break;
218
+ }
219
+ }
220
+ isExternalLink(t) {
221
+ return t.startsWith("http://") || t.startsWith("https://") || t.startsWith("mailto:") || t.startsWith("tel:");
222
+ }
223
+ /**
224
+ * 编程式导航
225
+ */
226
+ navigate() {
227
+ this.to && this.handleClick(
228
+ new MouseEvent("click", {
229
+ bubbles: !0,
230
+ cancelable: !0
231
+ })
232
+ );
233
+ }
234
+ }
235
+ W = yt(X);
236
+ x = Rt(W, 0, "WsxLink", K, x);
237
+ m(x, "observedAttributes", ["to", "replace", "active-class", "exact"]);
238
+ St(W, 1, x);
239
+ const Ct = y("RouterUtils");
240
+ class Pt {
241
+ /**
242
+ * 编程式导航
243
+ */
244
+ static navigate(t, r = !1) {
245
+ r ? window.history.replaceState(null, "", t) : window.history.pushState(null, "", t), window.dispatchEvent(new PopStateEvent("popstate")), Ct.debug(`Navigated to: ${t} (replace: ${r})`);
246
+ }
247
+ /**
248
+ * 获取当前路由信息
249
+ */
250
+ static getCurrentRoute() {
251
+ const t = new URL(window.location.href);
252
+ return {
253
+ path: t.pathname,
254
+ params: {},
255
+ // 需要路由匹配后才能确定
256
+ query: Object.fromEntries(t.searchParams.entries()),
257
+ hash: t.hash.slice(1)
258
+ // 移除 # 号
259
+ };
260
+ }
261
+ /**
262
+ * 解析路由路径,提取参数
263
+ */
264
+ static parseRoute(t, r) {
265
+ var i;
266
+ if (t === r)
267
+ return {
268
+ route: t,
269
+ params: {},
270
+ exact: !0
271
+ };
272
+ if (t === "*")
273
+ return {
274
+ route: t,
275
+ params: {},
276
+ exact: !1
277
+ };
278
+ if (t.includes(":")) {
279
+ const o = ((i = t.match(/:([^/]+)/g)) == null ? void 0 : i.map((c) => c.slice(1))) || [], n = t.replace(/:[^/]+/g, "([^/]+)"), s = new RegExp(`^${n}$`), l = r.match(s);
280
+ if (l && o.length > 0) {
281
+ const c = {};
282
+ return o.forEach((a, h) => {
283
+ c[a] = l[h + 1];
284
+ }), {
285
+ route: t,
286
+ params: c,
287
+ exact: !0
288
+ };
289
+ }
290
+ }
291
+ if (t.endsWith("/*")) {
292
+ const o = t.slice(0, -2);
293
+ if (r.startsWith(o))
294
+ return {
295
+ route: t,
296
+ params: {},
297
+ exact: !1
298
+ };
299
+ }
300
+ return null;
301
+ }
302
+ /**
303
+ * 构建路由路径,替换参数
304
+ */
305
+ static buildPath(t, r = {}) {
306
+ let i = t;
307
+ return Object.entries(r).forEach(([o, n]) => {
308
+ i = i.replace(`:${o}`, encodeURIComponent(n));
309
+ }), i;
310
+ }
311
+ /**
312
+ * 检查路由是否匹配当前路径
313
+ */
314
+ static isRouteActive(t, r = !1) {
315
+ const i = window.location.pathname;
316
+ return r ? i === t : t === "/" ? i === "/" : i.startsWith(t);
317
+ }
318
+ /**
319
+ * 获取路由层级
320
+ */
321
+ static getRouteDepth(t) {
322
+ return t.split("/").filter((r) => r.length > 0).length;
323
+ }
324
+ /**
325
+ * 获取父级路由
326
+ */
327
+ static getParentRoute(t) {
328
+ const r = t.split("/").filter((i) => i.length > 0);
329
+ return r.length <= 1 ? "/" : (r.pop(), "/" + r.join("/"));
330
+ }
331
+ /**
332
+ * 合并路由路径
333
+ */
334
+ static joinPaths(...t) {
335
+ return t.map((r) => r.replace(/^\/+|\/+$/g, "")).filter((r) => r.length > 0).join("/").replace(/^/, "/");
336
+ }
337
+ /**
338
+ * 检查是否为外部链接
339
+ */
340
+ static isExternalUrl(t) {
341
+ return /^https?:\/\//.test(t) || /^mailto:/.test(t) || /^tel:/.test(t);
342
+ }
343
+ /**
344
+ * 获取查询参数
345
+ */
346
+ static getQueryParam(t) {
347
+ return new URL(window.location.href).searchParams.get(t);
348
+ }
349
+ /**
350
+ * 设置查询参数
351
+ */
352
+ static setQueryParam(t, r, i = !1) {
353
+ const o = new URL(window.location.href);
354
+ o.searchParams.set(t, r);
355
+ const n = o.pathname + o.search + o.hash;
356
+ this.navigate(n, i);
357
+ }
358
+ /**
359
+ * 删除查询参数
360
+ */
361
+ static removeQueryParam(t, r = !1) {
362
+ const i = new URL(window.location.href);
363
+ i.searchParams.delete(t);
364
+ const o = i.pathname + i.search + i.hash;
365
+ this.navigate(o, r);
366
+ }
367
+ /**
368
+ * 返回上一页
369
+ */
370
+ static goBack() {
371
+ window.history.back();
372
+ }
373
+ /**
374
+ * 前进一页
375
+ */
376
+ static goForward() {
377
+ window.history.forward();
378
+ }
379
+ /**
380
+ * 替换当前页面
381
+ */
382
+ static replace(t) {
383
+ this.navigate(t, !0);
384
+ }
385
+ /**
386
+ * 获取历史记录长度
387
+ */
388
+ static getHistoryLength() {
389
+ return window.history.length;
390
+ }
391
+ /**
392
+ * 监听路由变化
393
+ */
394
+ static onRouteChange(t) {
395
+ const r = () => {
396
+ const i = this.getCurrentRoute();
397
+ t(i);
398
+ };
399
+ return window.addEventListener("popstate", r), document.addEventListener("route-changed", r), () => {
400
+ window.removeEventListener("popstate", r), document.removeEventListener("route-changed", r);
401
+ };
402
+ }
403
+ }
404
+ export {
405
+ Pt as RouterUtils,
406
+ x as WsxLink,
407
+ $ as WsxRouter,
408
+ b as WsxView
409
+ };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@wsxjs/wsx-router",
3
+ "version": "0.0.5",
4
+ "description": "WSX Router - Native History API-based routing for WSX Framework",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.cjs"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src",
17
+ "!**/__tests__",
18
+ "!**/test"
19
+ ],
20
+ "dependencies": {
21
+ "@wsxjs/wsx-core": "0.0.5"
22
+ },
23
+ "devDependencies": {
24
+ "@typescript-eslint/eslint-plugin": "^8.37.0",
25
+ "@typescript-eslint/parser": "^8.37.0",
26
+ "@vitest/ui": "^2.1.8",
27
+ "@vitest/coverage-v8": "^2.1.8",
28
+ "eslint": "^9.31.0",
29
+ "jsdom": "^26.0.0",
30
+ "typescript": "^5.0.0",
31
+ "vite": "^5.4.19",
32
+ "vitest": "^2.1.8",
33
+ "@wsxjs/wsx-vite-plugin": "0.0.5",
34
+ "@wsxjs/eslint-plugin-wsx": "0.0.5"
35
+ },
36
+ "keywords": [
37
+ "wsx",
38
+ "router",
39
+ "web-components",
40
+ "history-api",
41
+ "navigation",
42
+ "spa"
43
+ ],
44
+ "author": "WSX Framework Team",
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/wsxjs/wsxjs.git",
49
+ "directory": "packages/wsx-router"
50
+ },
51
+ "scripts": {
52
+ "build": "vite build",
53
+ "build:dev": "NODE_ENV=development vite build",
54
+ "dev": "NODE_ENV=development vite build --watch",
55
+ "clean": "rm -rf dist",
56
+ "test": "vitest",
57
+ "test:ui": "vitest --ui",
58
+ "test:coverage": "vitest --coverage",
59
+ "typecheck": "tsc --noEmit",
60
+ "lint": "eslint .",
61
+ "lint:fix": "eslint . --fix"
62
+ }
63
+ }