@mantou/gem 2.3.2 → 2.3.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/dist/gem.js +11 -11
- package/dist/gem.mjs +11 -11
- package/elements/base/link.d.ts +2 -1
- package/elements/base/link.d.ts.map +1 -1
- package/elements/base/link.js +1 -2
- package/elements/base/link.js.map +1 -1
- package/elements/base/route.d.ts +4 -21
- package/elements/base/route.d.ts.map +1 -1
- package/elements/base/route.js +2 -83
- package/elements/base/route.js.map +1 -1
- package/lib/element.d.ts +13 -5
- package/lib/element.d.ts.map +1 -1
- package/lib/element.js +8 -11
- package/lib/element.js.map +1 -1
- package/lib/history.d.ts +24 -0
- package/lib/history.d.ts.map +1 -1
- package/lib/history.js +82 -0
- package/lib/history.js.map +1 -1
- package/lib/keyed.d.ts +10 -0
- package/lib/keyed.d.ts.map +1 -0
- package/lib/keyed.js +26 -0
- package/lib/keyed.js.map +1 -0
- package/lib/lit-html.d.ts.map +1 -1
- package/lib/lit-html.js +3 -3
- package/lib/lit-html.js.map +1 -1
- package/lib/repeat.d.ts.map +1 -1
- package/lib/repeat.js +1 -1
- package/lib/repeat.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +9 -1
- package/src/elements/base/link.ts +3 -3
- package/src/elements/base/route.ts +4 -96
- package/src/lib/element.ts +10 -12
- package/src/lib/history.ts +96 -0
- package/src/lib/keyed.ts +30 -0
- package/src/lib/lit-html.ts +3 -3
- package/src/lib/repeat.ts +1 -1
- package/src/lib/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mantou/gem",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "💎 Build reusable custom elements and web apps on Web Components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontend",
|
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
"esm.sh": {
|
|
13
13
|
"bundle": false
|
|
14
14
|
},
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"./index.js",
|
|
17
|
+
"./lib/element.js",
|
|
18
|
+
"./elements/*.js",
|
|
19
|
+
"./helper/hmr.js",
|
|
20
|
+
"./helper/ssr-shim.js",
|
|
21
|
+
"./dist/*"
|
|
22
|
+
],
|
|
15
23
|
"main": "index.js",
|
|
16
24
|
"module": "index.js",
|
|
17
25
|
"exports": {
|
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
template,
|
|
14
14
|
} from '../../lib/decorators';
|
|
15
15
|
import { css, GemElement, html } from '../../lib/element';
|
|
16
|
-
import {
|
|
16
|
+
import type { RouteOptions } from '../../lib/history';
|
|
17
|
+
import { basePathStore, createHistoryParams, createPath, history, matchPath } from '../../lib/history';
|
|
17
18
|
import { isServer } from '../../lib/server';
|
|
18
19
|
import { absoluteLocation } from '../../lib/utils';
|
|
19
|
-
import type { RouteItem
|
|
20
|
-
import { createHistoryParams, createPath, matchPath } from './route';
|
|
20
|
+
import type { RouteItem } from './route';
|
|
21
21
|
|
|
22
22
|
function isExternal(path: string) {
|
|
23
23
|
return !path.startsWith('/');
|
|
@@ -1,80 +1,17 @@
|
|
|
1
1
|
import type { Emitter } from '../../lib/decorators';
|
|
2
2
|
import { adoptedStyle, boolattribute, effect, emitter, light, property, shadow, template } from '../../lib/decorators';
|
|
3
3
|
import { createState, css, GemElement, html } from '../../lib/element';
|
|
4
|
-
import type { UpdateHistoryParams } from '../../lib/history';
|
|
5
|
-
import { history, titleStore } from '../../lib/history';
|
|
4
|
+
import type { RouteOptions, UpdateHistoryParams } from '../../lib/history';
|
|
5
|
+
import { createPath, history, matchPath, titleStore } from '../../lib/history';
|
|
6
6
|
import type { TemplateResult } from '../../lib/lit-html';
|
|
7
7
|
import type { Store } from '../../lib/store';
|
|
8
8
|
import { connect, createStore } from '../../lib/store';
|
|
9
9
|
import type { QueryString } from '../../lib/utils';
|
|
10
10
|
import { addListener } from '../../lib/utils';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
type Matcher = (path: string) => Params | null | undefined;
|
|
14
|
-
|
|
15
|
-
const UNNAMED_PARAM_PREFIX = '_';
|
|
16
|
-
|
|
17
|
-
// 不支持 `URLPattern` 时转换成正则
|
|
18
|
-
// '/test/:a' /^\/test\/(?<a>[^/]+)$/
|
|
19
|
-
// '/test/:a/b' /^\/test\/(?<a>[^/]+)\/b$/
|
|
20
|
-
// '/test/*' /^\/test\/(?<_0>.*)$/
|
|
21
|
-
// '/test/:a/*' /^\/test\/(?<a>[^/]+)\/(?<_0>.*)$/
|
|
22
|
-
// '/test/*/:b' /^\/test\/(?<_0>.*)\/(?<b>[^/]+)$/
|
|
23
|
-
function createRegExpMatcher(pattern: string): Matcher {
|
|
24
|
-
let i = 0;
|
|
25
|
-
const source = pattern.replace(/:[^/$]+|\*/g, (name) =>
|
|
26
|
-
name === '*' ? `(?<${UNNAMED_PARAM_PREFIX}${i++}>.*)` : `(?<${name.slice(1)}>[^/]+)`,
|
|
27
|
-
);
|
|
28
|
-
const reg = new RegExp(`^${source}$`);
|
|
29
|
-
return (path) => {
|
|
30
|
-
const result = path.match(reg);
|
|
31
|
-
if (!result) return null;
|
|
32
|
-
const params: Params = {};
|
|
33
|
-
for (const key in result.groups) {
|
|
34
|
-
params[key.startsWith(UNNAMED_PARAM_PREFIX) ? key.slice(1) : key] = result.groups[key];
|
|
35
|
-
}
|
|
36
|
-
return params;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
12
|
+
export { createHistoryParams, createPath, matchPath, type RouteOptions } from '../../lib/history';
|
|
39
13
|
|
|
40
|
-
|
|
41
|
-
const LITERAL_PREFIX = /^[\w\-./~]*/;
|
|
42
|
-
|
|
43
|
-
// `URLPattern` 的创建和 `exec` 都比较慢,不含参数时直接比较,否则先检查固定的开头
|
|
44
|
-
function createMatcher(pattern: string): Matcher {
|
|
45
|
-
const literal = LITERAL_PREFIX.exec(pattern)![0];
|
|
46
|
-
if (literal === pattern) return (path) => (path === pattern ? {} : null);
|
|
47
|
-
// 根 layout 的 pattern
|
|
48
|
-
if (pattern === '*') return (path) => ({ 0: path });
|
|
49
|
-
if (!globalThis.URLPattern) return createRegExpMatcher(pattern);
|
|
50
|
-
const urlPattern = new URLPattern({ pathname: pattern });
|
|
51
|
-
// 参数前的 `/` 可能是可选的,例如 `/a/:b?` 匹配 `/a`
|
|
52
|
-
const prefix = literal.replace(/\/$/, '');
|
|
53
|
-
return (path) =>
|
|
54
|
-
path.startsWith(prefix) ? (urlPattern.exec({ pathname: path })?.pathname.groups as Params | undefined) : null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// pattern 来自路由定义,数量有限
|
|
58
|
-
const matchers = new Map<string, Matcher>();
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* - 大小写敏感
|
|
62
|
-
* - 匹配成功时返回 params
|
|
63
|
-
* - 以 `/` 结尾时能不带 `/` 结尾的路径
|
|
64
|
-
* - `*` 匹配到的 params 名称为数字
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```js
|
|
68
|
-
* matchPath(`/a/b/:c/:d`,`/a/b/1/2`);
|
|
69
|
-
* matchPath(`/a/b/:c/:d`,`/a/b/1/2/`);
|
|
70
|
-
* matchPath(`/a/b/*`,`/a/b/1/2/`);
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export function matchPath(pattern: string, path: string) {
|
|
74
|
-
let match = matchers.get(pattern);
|
|
75
|
-
if (!match) matchers.set(pattern, (match = createMatcher(pattern)));
|
|
76
|
-
return match(path) || match(`${path}/`) || null;
|
|
77
|
-
}
|
|
14
|
+
type Params = Record<string, string>;
|
|
78
15
|
|
|
79
16
|
export interface RouteItem<T = unknown> {
|
|
80
17
|
// 支持 *
|
|
@@ -96,35 +33,6 @@ export interface RouteItem<T = unknown> {
|
|
|
96
33
|
|
|
97
34
|
export type RoutesObject<T = unknown> = Record<string, RouteItem<T>>;
|
|
98
35
|
|
|
99
|
-
// params 中的成员不会验证
|
|
100
|
-
export interface RouteOptions extends Omit<UpdateHistoryParams, 'path'> {
|
|
101
|
-
params?: Params;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// 从路由创建路径
|
|
105
|
-
// 不包含 basePath
|
|
106
|
-
export function createPath(route: RouteItem, options?: RouteOptions) {
|
|
107
|
-
// /a/* /a/:0
|
|
108
|
-
// /a/*/:b/* /a/:0/:b/:1
|
|
109
|
-
let i = 0;
|
|
110
|
-
let path = route.pattern.replaceAll('*', () => `:${i++}`);
|
|
111
|
-
const params = options?.params;
|
|
112
|
-
if (params) {
|
|
113
|
-
Object.keys(params).forEach((key) => {
|
|
114
|
-
path = path.replace(new RegExp(`:${key}`, 'g'), params[key]);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
return path;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function createHistoryParams(route: RouteItem, options?: RouteOptions): UpdateHistoryParams {
|
|
121
|
-
const path = createPath(route, options);
|
|
122
|
-
return {
|
|
123
|
-
path,
|
|
124
|
-
...options,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
36
|
const INIT_CONTENT = html``;
|
|
129
37
|
|
|
130
38
|
type State = {
|
package/src/lib/element.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import * as decoratorsExports from './decorators';
|
|
2
1
|
import { html, render } from './lit-html';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import * as storeExports from './store';
|
|
6
|
-
import * as versionExports from './version';
|
|
2
|
+
import { createState, css, GemElement, setEngine } from './reactive';
|
|
3
|
+
import { version } from './version';
|
|
7
4
|
|
|
5
|
+
// 副作用,在 package.json 的 `sideEffects` 中声明;
|
|
6
|
+
// 同时声明 `index.js`,否则 esbuild 从入口直接解析到 reactive 的导出时会跳过这个模块
|
|
8
7
|
setEngine(html, render);
|
|
9
8
|
|
|
10
9
|
export * from './decorators';
|
|
11
10
|
export { directive } from './directive';
|
|
11
|
+
export { keyed } from './keyed';
|
|
12
12
|
export { createRef, mathml, svg, TemplateResult } from './lit-html';
|
|
13
13
|
export * from './reactive';
|
|
14
14
|
export { repeat } from './repeat';
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// 只提供 devtools 使用的成员,引用整个模块会导致没用到的导出不能被树抖
|
|
17
|
+
const hookExports = { GemElement, setEngine, html, render, css, createState, version };
|
|
18
|
+
|
|
19
|
+
type HookExports = typeof hookExports;
|
|
17
20
|
|
|
18
21
|
declare global {
|
|
19
22
|
/** devtools 注入的钩子,其他包可以扩展,例如 gem-ssr */
|
|
@@ -23,10 +26,5 @@ declare global {
|
|
|
23
26
|
|
|
24
27
|
// 只记录第一次定义,往往是最外层 App
|
|
25
28
|
if (globalThis.__GEM_DEVTOOLS__HOOK__ && !globalThis.__GEM_DEVTOOLS__HOOK__.GemElement) {
|
|
26
|
-
Object.assign(globalThis.__GEM_DEVTOOLS__HOOK__,
|
|
27
|
-
...reactiveExports,
|
|
28
|
-
...decoratorsExports,
|
|
29
|
-
...storeExports,
|
|
30
|
-
...versionExports,
|
|
31
|
-
});
|
|
29
|
+
Object.assign(globalThis.__GEM_DEVTOOLS__HOOK__, hookExports);
|
|
32
30
|
}
|
package/src/lib/history.ts
CHANGED
|
@@ -309,3 +309,99 @@ if (!globalThis._GEMHISTORY) {
|
|
|
309
309
|
const { history, titleStore, basePathStore } = globalThis._GEMHISTORY;
|
|
310
310
|
|
|
311
311
|
export { basePathStore, history, titleStore };
|
|
312
|
+
|
|
313
|
+
type Params = Record<string, string>;
|
|
314
|
+
type Matcher = (path: string) => Params | null | undefined;
|
|
315
|
+
|
|
316
|
+
const UNNAMED_PARAM_PREFIX = '_';
|
|
317
|
+
|
|
318
|
+
// 不支持 `URLPattern` 时转换成正则
|
|
319
|
+
// '/test/:a' /^\/test\/(?<a>[^/]+)$/
|
|
320
|
+
// '/test/:a/b' /^\/test\/(?<a>[^/]+)\/b$/
|
|
321
|
+
// '/test/*' /^\/test\/(?<_0>.*)$/
|
|
322
|
+
// '/test/:a/*' /^\/test\/(?<a>[^/]+)\/(?<_0>.*)$/
|
|
323
|
+
// '/test/*/:b' /^\/test\/(?<_0>.*)\/(?<b>[^/]+)$/
|
|
324
|
+
function createRegExpMatcher(pattern: string): Matcher {
|
|
325
|
+
let i = 0;
|
|
326
|
+
const source = pattern.replace(/:[^/$]+|\*/g, (name) =>
|
|
327
|
+
name === '*' ? `(?<${UNNAMED_PARAM_PREFIX}${i++}>.*)` : `(?<${name.slice(1)}>[^/]+)`,
|
|
328
|
+
);
|
|
329
|
+
const reg = new RegExp(`^${source}$`);
|
|
330
|
+
return (path) => {
|
|
331
|
+
const result = path.match(reg);
|
|
332
|
+
if (!result) return null;
|
|
333
|
+
const params: Params = {};
|
|
334
|
+
for (const key in result.groups) {
|
|
335
|
+
params[key.startsWith(UNNAMED_PARAM_PREFIX) ? key.slice(1) : key] = result.groups[key];
|
|
336
|
+
}
|
|
337
|
+
return params;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// pattern 开头不含参数和特殊字符的部分
|
|
342
|
+
const LITERAL_PREFIX = /^[\w\-./~]*/;
|
|
343
|
+
|
|
344
|
+
// `URLPattern` 的创建和 `exec` 都比较慢,不含参数时直接比较,否则先检查固定的开头
|
|
345
|
+
function createMatcher(pattern: string): Matcher {
|
|
346
|
+
const literal = LITERAL_PREFIX.exec(pattern)![0];
|
|
347
|
+
if (literal === pattern) return (path) => (path === pattern ? {} : null);
|
|
348
|
+
// 根 layout 的 pattern
|
|
349
|
+
if (pattern === '*') return (path) => ({ 0: path });
|
|
350
|
+
if (!globalThis.URLPattern) return createRegExpMatcher(pattern);
|
|
351
|
+
const urlPattern = new URLPattern({ pathname: pattern });
|
|
352
|
+
// 参数前的 `/` 可能是可选的,例如 `/a/:b?` 匹配 `/a`
|
|
353
|
+
const prefix = literal.replace(/\/$/, '');
|
|
354
|
+
return (path) =>
|
|
355
|
+
path.startsWith(prefix) ? (urlPattern.exec({ pathname: path })?.pathname.groups as Params | undefined) : null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// pattern 来自路由定义,数量有限
|
|
359
|
+
const matchers = new Map<string, Matcher>();
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* - 大小写敏感
|
|
363
|
+
* - 匹配成功时返回 params
|
|
364
|
+
* - 以 `/` 结尾时能不带 `/` 结尾的路径
|
|
365
|
+
* - `*` 匹配到的 params 名称为数字
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```js
|
|
369
|
+
* matchPath(`/a/b/:c/:d`,`/a/b/1/2`);
|
|
370
|
+
* matchPath(`/a/b/:c/:d`,`/a/b/1/2/`);
|
|
371
|
+
* matchPath(`/a/b/*`,`/a/b/1/2/`);
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
export function matchPath(pattern: string, path: string) {
|
|
375
|
+
let match = matchers.get(pattern);
|
|
376
|
+
if (!match) matchers.set(pattern, (match = createMatcher(pattern)));
|
|
377
|
+
return match(path) || match(`${path}/`) || null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// params 中的成员不会验证
|
|
381
|
+
export interface RouteOptions extends Omit<UpdateHistoryParams, 'path'> {
|
|
382
|
+
params?: Record<string, string>;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// 从路由创建路径
|
|
386
|
+
// 不包含 basePath
|
|
387
|
+
export function createPath(route: { pattern: string }, options?: RouteOptions) {
|
|
388
|
+
// /a/* /a/:0
|
|
389
|
+
// /a/*/:b/* /a/:0/:b/:1
|
|
390
|
+
let i = 0;
|
|
391
|
+
let path = route.pattern.replaceAll('*', () => `:${i++}`);
|
|
392
|
+
const params = options?.params;
|
|
393
|
+
if (params) {
|
|
394
|
+
Object.keys(params).forEach((key) => {
|
|
395
|
+
path = path.replace(new RegExp(`:${key}`, 'g'), params[key]);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
return path;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function createHistoryParams(route: { pattern: string }, options?: RouteOptions): UpdateHistoryParams {
|
|
402
|
+
const path = createPath(route, options);
|
|
403
|
+
return {
|
|
404
|
+
path,
|
|
405
|
+
...options,
|
|
406
|
+
};
|
|
407
|
+
}
|
package/src/lib/keyed.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Directive, directive } from './directive';
|
|
2
|
+
import type { ChildPart } from './lit-html';
|
|
3
|
+
import { setCommittedValue } from './repeat';
|
|
4
|
+
|
|
5
|
+
const INITIAL = Symbol();
|
|
6
|
+
|
|
7
|
+
class KeyedDirective extends Directive {
|
|
8
|
+
#key: unknown = INITIAL;
|
|
9
|
+
|
|
10
|
+
render(_key: unknown, value: unknown) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 第一次渲染时不清除,水合时复用 SSR 的内容
|
|
15
|
+
update(part: ChildPart, [key, value]: [unknown, unknown]) {
|
|
16
|
+
if (this.#key !== INITIAL && key !== this.#key) setCommittedValue(part);
|
|
17
|
+
this.#key = key;
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* `key` 改变时丢弃原来的 DOM,重新创建内容,即使模板相同,例如页面参数改变时重新创建页面元素
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* html`${keyed(this.id, html`<my-editor .id=${this.id}></my-editor>`)}`;
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const keyed = /*#__PURE__*/ directive(KeyedDirective) as (key: unknown, value: unknown) => unknown;
|
package/src/lib/lit-html.ts
CHANGED
|
@@ -167,9 +167,9 @@ const tag =
|
|
|
167
167
|
return new TemplateResult(type, strings, values);
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
export const html = tag(HTML_RESULT);
|
|
171
|
-
export const svg = tag(SVG_RESULT);
|
|
172
|
-
export const mathml = tag(MATHML_RESULT);
|
|
170
|
+
export const html = /*#__PURE__*/ tag(HTML_RESULT);
|
|
171
|
+
export const svg = /*#__PURE__*/ tag(SVG_RESULT);
|
|
172
|
+
export const mathml = /*#__PURE__*/ tag(MATHML_RESULT);
|
|
173
173
|
|
|
174
174
|
// for directive
|
|
175
175
|
export const noChange = Symbol.for('lit-noChange');
|
package/src/lib/repeat.ts
CHANGED
|
@@ -571,7 +571,7 @@ export interface RepeatDirectiveFn {
|
|
|
571
571
|
* If no `keyFn` is provided, this directive will perform similar to mapping
|
|
572
572
|
* items to values, and DOM will be reused against potentially different items.
|
|
573
573
|
*/
|
|
574
|
-
export const repeat = directive(RepeatDirective) as RepeatDirectiveFn;
|
|
574
|
+
export const repeat = /*#__PURE__*/ directive(RepeatDirective) as RepeatDirectiveFn;
|
|
575
575
|
|
|
576
576
|
/**
|
|
577
577
|
* The type of the class that powers this directive. Necessary for naming the
|
package/src/lib/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.3.
|
|
1
|
+
export const version = '2.3.3';
|