@mantou/gem 2.2.1 → 2.2.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.
Files changed (87) hide show
  1. package/dist/gem.es.js +1015 -1014
  2. package/dist/gem.umd.js +12 -15
  3. package/elements/base/gesture.js +1 -1
  4. package/elements/base/link.d.ts +1 -1
  5. package/elements/base/link.d.ts.map +1 -1
  6. package/elements/base/link.js +13 -9
  7. package/elements/base/link.js.map +1 -1
  8. package/elements/base/modal-factory.d.ts +73 -48
  9. package/elements/base/modal-factory.d.ts.map +1 -1
  10. package/elements/base/modal-factory.js +12 -11
  11. package/elements/base/modal-factory.js.map +1 -1
  12. package/elements/base/route.d.ts +1 -1
  13. package/elements/base/route.d.ts.map +1 -1
  14. package/elements/base/route.js +11 -15
  15. package/elements/base/route.js.map +1 -1
  16. package/elements/base/title.d.ts.map +1 -1
  17. package/elements/base/title.js +2 -4
  18. package/elements/base/title.js.map +1 -1
  19. package/elements/base/unsafe.d.ts +3 -2
  20. package/elements/base/unsafe.d.ts.map +1 -1
  21. package/elements/base/unsafe.js +45 -24
  22. package/elements/base/unsafe.js.map +1 -1
  23. package/helper/i18n.d.ts.map +1 -1
  24. package/helper/logger.d.ts.map +1 -1
  25. package/helper/react-utils.d.ts.map +1 -1
  26. package/helper/react-utils.js +2 -6
  27. package/helper/react-utils.js.map +1 -1
  28. package/helper/ssr-shim.d.ts.map +1 -1
  29. package/helper/ssr-shim.js +0 -1
  30. package/helper/ssr-shim.js.map +1 -1
  31. package/helper/theme.d.ts +15 -3
  32. package/helper/theme.d.ts.map +1 -1
  33. package/helper/theme.js +22 -7
  34. package/helper/theme.js.map +1 -1
  35. package/lib/decorators.d.ts +49 -49
  36. package/lib/decorators.d.ts.map +1 -1
  37. package/lib/decorators.js +50 -49
  38. package/lib/decorators.js.map +1 -1
  39. package/lib/directive.d.ts.map +1 -1
  40. package/lib/element.d.ts.map +1 -1
  41. package/lib/element.js.map +1 -1
  42. package/lib/history.d.ts +1 -1
  43. package/lib/history.d.ts.map +1 -1
  44. package/lib/history.js +3 -3
  45. package/lib/history.js.map +1 -1
  46. package/lib/lit-html.d.ts +2 -5
  47. package/lib/lit-html.d.ts.map +1 -1
  48. package/lib/lit-html.js +2 -5
  49. package/lib/lit-html.js.map +1 -1
  50. package/lib/reactive.d.ts +12 -5
  51. package/lib/reactive.d.ts.map +1 -1
  52. package/lib/reactive.js +14 -18
  53. package/lib/reactive.js.map +1 -1
  54. package/lib/repeat.d.ts.map +1 -1
  55. package/lib/store.d.ts +2 -2
  56. package/lib/store.d.ts.map +1 -1
  57. package/lib/store.js +1 -0
  58. package/lib/store.js.map +1 -1
  59. package/lib/utils.d.ts +4 -1
  60. package/lib/utils.d.ts.map +1 -1
  61. package/lib/utils.js +11 -9
  62. package/lib/utils.js.map +1 -1
  63. package/lib/version.d.ts +1 -1
  64. package/lib/version.d.ts.map +1 -1
  65. package/lib/version.js +2 -1
  66. package/lib/version.js.map +1 -1
  67. package/package.json +2 -2
  68. package/src/elements/base/gesture.ts +1 -1
  69. package/src/elements/base/link.ts +3 -3
  70. package/src/elements/base/modal-factory.ts +12 -11
  71. package/src/elements/base/route.ts +9 -15
  72. package/src/elements/base/title.ts +2 -4
  73. package/src/elements/base/unsafe.ts +33 -14
  74. package/src/helper/react-utils.ts +4 -12
  75. package/src/helper/ssr-shim.ts +0 -1
  76. package/src/helper/theme.ts +25 -10
  77. package/src/lib/decorators.ts +51 -49
  78. package/src/lib/element.ts +0 -1
  79. package/src/lib/history.ts +3 -4
  80. package/src/lib/lit-html.ts +3 -0
  81. package/src/lib/reactive.ts +45 -21
  82. package/src/lib/store.ts +3 -2
  83. package/src/lib/utils.ts +15 -8
  84. package/src/lib/version.ts +2 -1
  85. package/src/test/gem-element/advance.test.ts +1 -1
  86. package/src/test/gem-element/basic.test.ts +1 -2
  87. package/src/test/utils.test.ts +1 -1
@@ -1,7 +1,6 @@
1
1
  /// 简单的垫片,让 GemElement 服务端渲染不报错
2
2
  /// 渲染时将忽略 GemElement 内容
3
3
 
4
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
5
4
  // @ts-nocheck
6
5
  if (typeof window === 'undefined') {
7
6
  const getAny = () => {
@@ -2,9 +2,10 @@ import type { GemElement, Sheet } from '../lib/reactive';
2
2
  import { css, SheetToken } from '../lib/reactive';
3
3
  import type { Store } from '../lib/store';
4
4
  import { connect, createStore } from '../lib/store';
5
+ import type { Updater } from '../lib/utils';
5
6
  import { camelToKebabCase, createUpdater, randomStr } from '../lib/utils';
6
7
 
7
- export type Theme<T> = ReturnType<typeof createUpdater<T>> &
8
+ export type Theme<T> = Updater<T> &
8
9
  Sheet<unknown> & {
9
10
  [K in keyof T as K extends `${string}Color`
10
11
  ? `${string & K}${'' | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900}`
@@ -52,7 +53,7 @@ function createThemeFromProps<T extends Record<string, unknown>>(themeObj: T, pr
52
53
  }
53
54
  rules += `${props[key]}:${store[key]};`;
54
55
  });
55
- styleSheet.setContent(`:scope,:host{${rules}}`);
56
+ styleSheet.setContent(`&,:host{${rules}}`);
56
57
  };
57
58
  updateContent();
58
59
  connect(store, () => {
@@ -81,19 +82,33 @@ export function createOverrideTheme<T extends Record<string, unknown>>(theme: Th
81
82
  return createThemeFromProps(themeObj, getThemeProps(theme));
82
83
  }
83
84
 
85
+ /**
86
+ * 返回一个主题对象,但同时又是一个装饰器函数,用来装饰一个主题更新函数
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const elementTheme = createDecoratorTheme({ color: '' });
91
+ * class MyElement {
92
+ * @​elementTheme()
93
+ * #theme = () => ({ color: this.color });
94
+ * }
95
+ * ```
96
+ * */
84
97
  export function createDecoratorTheme<T extends Record<string, unknown>>(themeObj: T) {
85
- const themeAsKeys = createThemeFromProps<T>(themeObj);
98
+ const themeAsKeys = createThemeFromProps(themeObj);
86
99
  const props = getThemeProps(themeAsKeys);
87
- const update =
88
- <E extends GemElement, V extends () => T, K = any[] | undefined>(
89
- getDep?: K extends readonly any[] ? (instance: E) => K : undefined,
90
- ) =>
91
- (_: any, { addInitializer, access }: ClassFieldDecoratorContext<E, V> | ClassMethodDecoratorContext<E, V>) => {
100
+ const update = <E extends GemElement, V extends () => T, K = any[] | undefined>(
101
+ getDep?: K extends readonly any[] ? (instance: E) => K : undefined,
102
+ ) => {
103
+ type Ctx = ClassFieldDecoratorContext<E, V> | ClassMethodDecoratorContext<E, V>;
104
+ return (_: any, { addInitializer, access }: Ctx) => {
92
105
  addInitializer(function (this: E) {
93
106
  const theme = createThemeFromProps({ ...themeObj }, props);
94
- this.internals.sheets.push(theme[SheetToken].getStyle(this));
107
+ this.internals.sheets.push(theme[SheetToken].getStyle(this, true));
95
108
  this.memo(() => theme(access.get(this).apply(this)), getDep && (() => getDep(this) as any));
96
109
  });
97
110
  };
98
- return createUpdater(themeAsKeys, update) as ReturnType<typeof createUpdater<typeof themeAsKeys, typeof update>>;
111
+ };
112
+ // 不知道为什么 `as` 能解决外部 `SheetToken` 不能命名的问题
113
+ return createUpdater(themeAsKeys, update) as Updater<Theme<T>, typeof update>;
99
114
  }
@@ -1,3 +1,5 @@
1
+ // biome-ignore-all lint/plugin/no_this_effect: 定义装饰器
2
+
1
3
  import type { TemplateResult } from './lit-html';
2
4
  import type { Metadata, Sheet } from './reactive';
3
5
  import { _createTemplate, _RenderErrorEvent, GemElement, render, UpdateToken } from './reactive';
@@ -150,11 +152,11 @@ function decoratorAttr<T extends GemElement>(context: ClassFieldDecoratorContext
150
152
  /**
151
153
  * 定义一个响应式的 attribute,驼峰字段名将自动映射到烤串 attribute,默认值为空字符串;不要覆盖定义
152
154
  *
153
- * For example
155
+ * @example
154
156
  * ```ts
155
- * class App extends GemElement {
156
- * @attribute attr: string;
157
- * }
157
+ * class App extends GemElement {
158
+ * @​attribute attr: string;
159
+ * }
158
160
  * ```
159
161
  */
160
162
  export function attribute<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, string>) {
@@ -170,11 +172,11 @@ export function numattribute<T extends GemElement>(_: undefined, context: ClassF
170
172
  /**
171
173
  * 定义一个响应式的 property,注意值可能为 `undefined`;不要覆盖定义
172
174
  *
173
- * For example
175
+ * @example
174
176
  * ```ts
175
- * class App extends GemElement {
176
- * @property prop: Data | undefined;
177
- * }
177
+ * class App extends GemElement {
178
+ * @​property prop: Data | undefined;
179
+ * }
178
180
  * ```
179
181
  */
180
182
  export function property<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T>) {
@@ -196,14 +198,14 @@ export function property<T extends GemElement>(_: undefined, context: ClassField
196
198
  /**
197
199
  * 依赖 `GemElement.memo`
198
200
  *
199
- * For example
201
+ * @example
200
202
  * ```ts
201
- * class App extends GemElement {
202
- * @memo(() => [])
203
- * get data() {
204
- * return 1;
205
- * }
206
- * }
203
+ * class App extends GemElement {
204
+ * @​memo(() => [])
205
+ * get data() {
206
+ * return 1;
207
+ * }
208
+ * }
207
209
  * ```
208
210
  */
209
211
  export function memo<T extends GemElement, V = any, K = any[] | undefined>(
@@ -240,14 +242,14 @@ export function memo<T extends GemElement, V = any, K = any[] | undefined>(
240
242
  * - 方法执行在字段前面
241
243
  * - 如果没有 oldValue,在为首次执行
242
244
  *
243
- * For example
245
+ * @example
244
246
  * ```ts
245
- * class App extends GemElement {
246
- * @effect(() => [])
247
- * #fetchData(newValue, oldValue) {
248
- * console.log('fetch')
249
- * }
250
- * }
247
+ * class App extends GemElement {
248
+ * @​effect(() => [])
249
+ * #fetchData(newValue, oldValue) {
250
+ * console.log('fetch')
251
+ * }
252
+ * }
251
253
  * ```
252
254
  */
253
255
  export function effect<T extends GemElement, V extends (depValues: K, oldDepValues?: K) => any, K = any[] | undefined>(
@@ -331,11 +333,11 @@ function defineCSSState(target: GemElementPrototype, prop: string, stateStr: str
331
333
  * 重新赋值转换 css 伪类
332
334
  * 将来也可能用于 IDE 识别
333
335
  *
334
- * For example
336
+ * @example
335
337
  * ```ts
336
- * class App extends GemElement {
337
- * @state state: boolean;
338
- * }
338
+ * class App extends GemElement {
339
+ * @​state state: boolean;
340
+ * }
339
341
  * ```
340
342
  */
341
343
  export function state<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, boolean>) {
@@ -374,12 +376,12 @@ function defineStaticField(
374
376
  /**
375
377
  * 定义一个内部 slot,默认值即为字段名,不能使用全局属性 `slot`
376
378
  *
377
- * For example
379
+ * @example
378
380
  * ```ts
379
- * class App extends GemElement {
380
- * @slot slot: string;
381
- * @slot static slot: string;
382
- * }
381
+ * class App extends GemElement {
382
+ * @​slot slot: string;
383
+ * @​slot static slot: string;
384
+ * }
383
385
  * ```
384
386
  */
385
387
  export function slot(_: undefined, context: ClassFieldDecoratorContext<any, string>) {
@@ -391,12 +393,12 @@ export function slot(_: undefined, context: ClassFieldDecoratorContext<any, stri
391
393
  /**
392
394
  * 定义一个内部 part,默认值即为字段名,不能使用全局属性 `part`
393
395
  *
394
- * For example
396
+ * @example
395
397
  * ```ts
396
- * class App extends GemElement {
397
- * @part part: string;
398
- * @part static part: string;
399
- * }
398
+ * class App extends GemElement {
399
+ * @​part part: string;
400
+ * @​part static part: string;
401
+ * }
400
402
  * ```
401
403
  */
402
404
  export function part(_: undefined, context: ClassFieldDecoratorContext<any, string>) {
@@ -421,11 +423,11 @@ export type Emitter<T = any> = ((
421
423
  * 定义一个事件发射器,类似 `HTMLElement.click`,
422
424
  * 调用时将同步发送一个和字段名称的烤串式格式的自定义事件
423
425
  *
424
- * For example
426
+ * @example
425
427
  * ```ts
426
- * class App extends GemElement {
427
- * @emitter load: Function;
428
- * }
428
+ * class App extends GemElement {
429
+ * @​emitter load: Function;
430
+ * }
429
431
  * ```
430
432
  */
431
433
  export function emitter<T extends GemElement>(_: undefined, context: ClassFieldDecoratorContext<T, Emitter>) {
@@ -453,10 +455,10 @@ function defineEmitter<T extends GemElement>(
453
455
  /**
454
456
  * 分配一个构造的样式表,前面的优先级越高(因为装饰器是越近越早执行)
455
457
  *
456
- * For example
458
+ * @example
457
459
  * ```ts
458
- * @adoptedStyle(stylesheet)
459
- * class App extends GemElement {}
460
+ * ​@​adoptedStyle(stylesheet)
461
+ * class App extends GemElement {}
460
462
  * ```
461
463
  */
462
464
  export function adoptedStyle(sheet: Sheet<unknown>) {
@@ -468,10 +470,10 @@ export function adoptedStyle(sheet: Sheet<unknown>) {
468
470
  /**
469
471
  * 链接一个 store,当 store 更新时更新元素
470
472
  *
471
- * For example
473
+ * @example
472
474
  * ```ts
473
- * @connectStore(store)
474
- * class App extends GemElement {}
475
+ * ​@​connectStore(store)
476
+ * class App extends GemElement {}
475
477
  * ```
476
478
  */
477
479
  export function connectStore(store: Store<any>) {
@@ -523,10 +525,10 @@ export function aria(info: Metadata['aria']) {
523
525
  /**
524
526
  * 定义自定义元素
525
527
  *
526
- * For example
528
+ * @example
527
529
  * ```ts
528
- * @customElement('my-element')
529
- * class App extends GemElement {}
530
+ * ​@​customElement('my-element')
531
+ * class App extends GemElement {}
530
532
  * ```
531
533
  */
532
534
  export function customElement(name: string) {
@@ -14,7 +14,6 @@ export * from './reactive';
14
14
  export { repeat } from './repeat';
15
15
 
16
16
  declare global {
17
- // eslint-disable-next-line no-var
18
17
  var __GEM_DEVTOOLS__HOOK__:
19
18
  | (typeof reactiveExports & typeof decoratorsExports & typeof storeExports & typeof versionExports)
20
19
  | Record<string, never>
@@ -188,7 +188,6 @@ const gemTitleStore = createStore({ defaultTitle: document.title, url: '', title
188
188
  const HISTORY = { history: gemHistory, titleStore: gemTitleStore, basePathStore: gemBasePathStore };
189
189
 
190
190
  declare global {
191
- // eslint-disable-next-line no-var
192
191
  var _GEMHISTORY: typeof HISTORY | undefined;
193
192
  }
194
193
 
@@ -269,10 +268,10 @@ if (!globalThis._GEMHISTORY) {
269
268
  if (!paramsMap.has(newState.$key)) {
270
269
  const { pathname, search, hash } = location;
271
270
  paramsMap.set(newState.$key, {
272
- path: pathname,
271
+ path: getInternalPath(pathname),
273
272
  query: new QueryString(search),
274
273
  hash: decodeURIComponent(hash),
275
- title: newState.$title, // document.title 是导航前的
274
+ title: gemTitleStore.title || newState.$title, // document.title 是导航前的
276
275
  data: newState,
277
276
  });
278
277
  }
@@ -316,4 +315,4 @@ if (!globalThis._GEMHISTORY) {
316
315
  }
317
316
 
318
317
  const { history, titleStore, basePathStore } = globalThis._GEMHISTORY;
319
- export { history, titleStore, basePathStore };
318
+ export { basePathStore, history, titleStore };
@@ -1,3 +1,6 @@
1
+ /** biome-ignore-all lint/complexity/noCommaOperator: lit */
2
+ /** biome-ignore-all lint/style/useTemplate: lit */
3
+
1
4
  /**
2
5
  * @license
3
6
  * Copyright 2017 Google LLC
@@ -1,7 +1,19 @@
1
+ // biome-ignore-all lint/plugin/no_this_effect: 兼容生命周期
2
+
1
3
  import type * as Lit from './lit-html';
2
4
  import type { Store } from './store';
3
5
  import { connect } from './store';
4
- import { addListener, addMicrotask, createUpdater, GemError, isArrayChange, LinkedList, randomStr, raw } from './utils';
6
+ import {
7
+ addListener,
8
+ addMicrotask,
9
+ createUpdater,
10
+ GemError,
11
+ isArrayChange,
12
+ LinkedList,
13
+ randomStr,
14
+ raw,
15
+ type Updater,
16
+ } from './utils';
5
17
 
6
18
  export let html = ((arr: TemplateStringsArray, ...args: any[]) =>
7
19
  raw(arr, ...args.map((e) => (e instanceof Element ? e.outerHTML : e)))) as unknown as typeof Lit.html;
@@ -20,8 +32,11 @@ defineProperty(Symbol, 'metadata', { value: Symbol.for('Symbol.metadata') });
20
32
 
21
33
  const execCallback = (fun: any) => typeof fun === 'function' && fun();
22
34
 
35
+ const ssrNativeQueueMicrotask = (globalThis as any).nQM;
36
+
23
37
  // 读取构造样式表,跨多个 gem 工作
24
38
  export const SheetToken = Symbol.for('gem@sheetToken');
39
+ export type SheetTokenType = typeof SheetToken;
25
40
  // 更新元素,跨多个 gem 工作
26
41
  export const UpdateToken = Symbol.for('gem@update');
27
42
 
@@ -67,7 +82,7 @@ class GemCSSSheet {
67
82
  // 不需要 GC
68
83
  #record = new Map<any, GemCSSStyleSheet>();
69
84
  #used = new Map<GemCSSStyleSheet, string>();
70
- getStyle(host?: HTMLElement) {
85
+ getStyle(host?: HTMLElement, isEleTheme?: boolean) {
71
86
  const isLight = host && !(host as GemElement).internals?.shadowRoot;
72
87
 
73
88
  // 对同一类 dom 只使用同一个样式表
@@ -86,7 +101,7 @@ class GemCSSSheet {
86
101
  if (isLight) {
87
102
  // light dom 嵌套时需要选择子元素
88
103
  // `> *` 实际上是多范围?是否存在性能问题
89
- scope = `@scope (${host.tagName}) to (:state(${BoundaryCSSState}) > *)`;
104
+ scope = isEleTheme ? '@scope' : `@scope (${host.tagName}) to (:state(${BoundaryCSSState}) > *)`;
90
105
  // 不能使用 @layer,两个 @layer 不能覆盖,只有顺序起作用
91
106
  // 所以外部不能通过元素名称选择器来覆盖样式,除非样式在之前插入(会自动反转应用到尾部, see `appleCSSStyleSheet`)
92
107
  style = `${scope}{ ${style} }`;
@@ -143,16 +158,13 @@ export function css<T extends Record<string, string>>(
143
158
  // 对于已经有 `-` 的保留原始 key,支持覆盖修改
144
159
  // :scope 下可以写嵌套样式 &:xxx,:host() 下不行(子内容可以)
145
160
  sheet[key] = isScope || key.includes('-') ? key : `${key}-${randomStr()}`;
146
- style += `${isScope ? ':where(:scope:not([hidden])),:host(:where(:not([hidden])))' : `.${sheet[key]}`} {${rules[key]}}`;
161
+ style += `${isScope ? ':where(&:not([hidden])),:host(:where(:not([hidden])))' : `.${sheet[key]}`} {${rules[key]}}`;
147
162
  });
148
163
  }
149
164
  styleSheet.setContent(style);
150
165
  return sheet as Sheet<T>;
151
166
  }
152
167
 
153
- // TODO: move to transform?
154
- export const isSSR = typeof process !== 'undefined';
155
-
156
168
  const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleSheet[], value: number) => {
157
169
  let needUpdate = false;
158
170
  sheets.forEach((e) => {
@@ -164,7 +176,8 @@ const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleShee
164
176
  });
165
177
  if (needUpdate) {
166
178
  // 避免重复更新,例如 list 元素增删,全 light dom 时 document 更新
167
- addMicrotask(rootUpdateFnMap.get(map)!, isSSR);
179
+ // 在 SSR 中应该区别 effect 强制执行
180
+ addMicrotask(rootUpdateFnMap.get(map)!, ssrNativeQueueMicrotask);
168
181
  }
169
182
  };
170
183
 
@@ -189,7 +202,7 @@ const appleCSSStyleSheet = (ele: HTMLElement, sheets: CSSStyleSheet[]) => {
189
202
  };
190
203
 
191
204
  /**必须使用在字段中,否则会读取到错误的实例 */
192
- export let createState: <T>(initState: T) => ReturnType<typeof createUpdater<T>>;
205
+ export let createState: <T>(initState: T) => Updater<T>;
193
206
 
194
207
  type GetDepFun<T> = () => T;
195
208
  type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
@@ -269,12 +282,28 @@ noBlockingTaskList.addEventListener('start', () => addMicrotask(tick));
269
282
 
270
283
  let currentConstructGemElement: GemElement;
271
284
 
285
+ type KebabCase<S extends string> = S extends `${infer Head}${infer Tail}`
286
+ ? Tail extends Uncapitalize<Tail>
287
+ ? `${Lowercase<Head>}${KebabCase<Tail>}`
288
+ : `${Lowercase<Head>}-${KebabCase<Tail>}`
289
+ : S;
290
+
272
291
  export abstract class GemElement extends HTMLElement {
292
+ /** 用来判断 attribute 是否存在,但是 length/item 将读取到 NamedNodeMap */
293
+ declare readonly attributes: NamedNodeMap & {
294
+ [K in keyof this as K extends keyof NamedNodeMap
295
+ ? never
296
+ : K extends keyof GemElement
297
+ ? never
298
+ : K extends Uppercase<string & K>
299
+ ? never
300
+ : `${KebabCase<string & K>}`]?: Attr;
301
+ };
273
302
  #renderRoot: HTMLElement | ShadowRoot;
274
303
  #internals: ElementInternals & {
275
304
  // 有别于 adoptedStyleSheets,用来定义单个实例样式
276
305
  sheets: GemCSSStyleSheet[];
277
- stateList: ReturnType<typeof createState<any>>[];
306
+ stateList: Updater<any>[];
278
307
  };
279
308
  #effectList: EffectItem<any>[] = [];
280
309
  #memoList: EffectItem<any>[] = [];
@@ -297,13 +326,15 @@ export abstract class GemElement extends HTMLElement {
297
326
  const state = createUpdater(initState, (payload) => {
298
327
  const effect = ele.#effectList.at(0);
299
328
  // https://github.com/mantou132/gem/issues/203
300
- if (ele.#isMounted && effect && !effect.initialized) {
301
- throw new GemError(`Do't set state sync before insert the DOM`);
329
+ // SSR 不会执行 initEffect,应该跳过
330
+ if (!ssrNativeQueueMicrotask && ele.#isMounted && effect && !effect.initialized) {
331
+ throw new GemError(`Don't set state sync before insert the DOM`);
302
332
  }
303
333
  assign(state, payload);
304
334
  // 避免无限刷新
305
335
  // 挂载前 set state 不应该触发更新
306
- if (!ele.#rendering && ele.#isMounted) addMicrotask(ele.#update);
336
+ // SSR 中渲染 <gem-route>,其他情况的 SSR 应该也可以执行
337
+ if (!ele.#rendering && ele.#isMounted) addMicrotask(ele.#update, ssrNativeQueueMicrotask);
307
338
  });
308
339
  ele.#internals.stateList.push(state);
309
340
  return state;
@@ -314,7 +345,6 @@ export abstract class GemElement extends HTMLElement {
314
345
  constructor() {
315
346
  super();
316
347
 
317
- // eslint-disable-next-line @typescript-eslint/no-this-alias
318
348
  currentConstructGemElement = this;
319
349
 
320
350
  const { mode, serializable, delegatesFocus, slotAssignment, aria } = this.#metadata;
@@ -386,13 +416,7 @@ export abstract class GemElement extends HTMLElement {
386
416
  const temp = item ? item.render() : isLight ? undefined : html`<slot></slot>`;
387
417
  if (temp === undefined) return;
388
418
  render(
389
- temp !== null
390
- ? temp
391
- : isLight
392
- ? // prettier-ignore
393
- html`<style>@scope{:scope{display:none !important}}</style>`
394
- : // prettier-ignore
395
- html`<style>:host{display:none !important}</style>`,
419
+ temp !== null ? temp : html`<style>${isLight ? '@scope' : ':host'}{&{display:none!important;}}</style>`,
396
420
  this.#renderRoot,
397
421
  );
398
422
  } catch (err) {
package/src/lib/store.ts CHANGED
@@ -1,12 +1,13 @@
1
- import { addMicrotask, createUpdater } from './utils';
1
+ import { addMicrotask, createUpdater, type Updater } from './utils';
2
2
 
3
- export type Store<T = any> = ReturnType<typeof createUpdater<T>>;
3
+ export type Store<T = any> = Updater<T>;
4
4
 
5
5
  /**@internal */
6
6
  export const _StoreListenerMap = new WeakMap<Store<any>, Set<() => void>>();
7
7
 
8
8
  export function createStore<T = any>(originStore: T) {
9
9
  const store = createUpdater(originStore, (value?: Partial<T>) => {
10
+ // biome-ignore lint/plugin/assign: 未知类型
10
11
  Object.assign(store, value);
11
12
  _StoreListenerMap.get(store)?.forEach((func) => addMicrotask(func));
12
13
  });
package/src/lib/utils.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  const { assign, fromEntries, entries, keys } = Object;
2
2
 
3
+ // 用原生 Chrome 找不到调用栈
4
+ function queueMicrotask(cb: () => void) {
5
+ Promise.resolve().then(cb);
6
+ }
7
+
3
8
  const microtaskSet = new Set<() => void>();
4
- export function addMicrotask(func: () => void, isNodeForceMicrotask?: boolean) {
9
+ export function addMicrotask(func: () => void, method = queueMicrotask) {
5
10
  if (microtaskSet.has(func)) return;
6
- const next = () => {
11
+ method(() => {
7
12
  microtaskSet.delete(func);
8
13
  func();
9
- };
10
- if (isNodeForceMicrotask) {
11
- process.nextTick(next);
12
- } else {
13
- Promise.resolve().then(next);
14
- }
14
+ });
15
15
  microtaskSet.add(func);
16
16
  }
17
17
 
@@ -42,6 +42,8 @@ export function createUpdater<T, Fn = (payload?: Partial<T>) => any>(initState:
42
42
  return state as Fn & Omit<OverrideFn, keyof T> & T;
43
43
  }
44
44
 
45
+ export type Updater<T, Fn = (payload?: Partial<T>) => any> = ReturnType<typeof createUpdater<T, Fn>>;
46
+
45
47
  // 不编码 hash 用于比较
46
48
  export function absoluteLocation(currentPath = '', relativePath = '') {
47
49
  const { pathname, search, hash } = new URL(relativePath, location.origin + currentPath);
@@ -157,6 +159,11 @@ export class LinkedList<T = any> extends EventTarget {
157
159
  return deleteItem;
158
160
  }
159
161
 
162
+ clear() {
163
+ this.#map.clear();
164
+ this.#firstItem = this.#lastItem = undefined;
165
+ }
166
+
160
167
  /** 获取头部元素,会从链表删除 */
161
168
  get(): T | undefined {
162
169
  const firstItem = this.#firstItem;
@@ -1 +1,2 @@
1
- export const version = '2.2.1';
1
+ // Do not modify manually
2
+ export const version = '0.0.0';
@@ -341,7 +341,7 @@ describe('gem element 继承', () => {
341
341
  });
342
342
  el.sayHi();
343
343
  await Promise.resolve();
344
- expect(window.name).to.equal(name + '21');
344
+ expect(window.name).to.equal(`${name}21`);
345
345
  });
346
346
  });
347
347
  @customElement('render-empty')
@@ -108,8 +108,7 @@ describe('基本 gem element 测试', () => {
108
108
  expect(el.disabled).to.equal(true);
109
109
  expect(el.count).to.equal(2);
110
110
  expect(el.shadowRoot?.textContent).to.equal('attr: value, disabled: true, count: 2, prop: , state: ');
111
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
112
- // @ts-ignore
111
+ // @ts-expect-error
113
112
  el.attr = null;
114
113
  await Promise.resolve();
115
114
  expect(el.hasAttribute('attr')).to.equal(false);
@@ -110,7 +110,7 @@ describe('utils 测试', () => {
110
110
  });
111
111
  expect(cssSheet.scroll.startsWith('scroll')).to.true;
112
112
  const rules = cssSheet[SheetToken].getStyle().cssRules;
113
- expect(rules.item(0).selectorText).to.equal(':where(:scope:not([hidden])), :host(:where(:not([hidden])))');
113
+ expect(rules.item(0).selectorText).to.equal(':where(&:not([hidden])), :host(:where(:not([hidden])))');
114
114
  expect(rules.item(1).selectorText.startsWith('.scroll')).to.true;
115
115
  expect(rules.item(1).style.background).to.equal('red');
116
116
  expect(rules.item(1).cssRules.item(0).selectorText).to.equal('&:hover *');