@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantou/gem",
3
- "version": "2.3.2",
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 { basePathStore, history } from '../../lib/history';
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, RouteOptions } from './route';
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
- type Params = Record<string, string>;
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
- // pattern 开头不含参数和特殊字符的部分
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 = {
@@ -1,19 +1,22 @@
1
- import * as decoratorsExports from './decorators';
2
1
  import { html, render } from './lit-html';
3
- import * as reactiveExports from './reactive';
4
- import { setEngine } from './reactive';
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
- type HookExports = typeof reactiveExports & typeof decoratorsExports & typeof storeExports & typeof versionExports;
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
  }
@@ -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
+ }
@@ -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;
@@ -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
@@ -1 +1 @@
1
- export const version = '2.3.2';
1
+ export const version = '2.3.3';