@mantou/gem 2.2.5 → 2.2.8

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/src/helper/hmr.ts CHANGED
@@ -5,7 +5,10 @@ import { Logger } from './logger';
5
5
 
6
6
  const logger = new Logger('HMR');
7
7
 
8
- const nativeDefineElement = window.customElements.define.bind(window.customElements);
8
+ const nativeDefineElement =
9
+ typeof window === 'undefined' || !window.customElements
10
+ ? undefined
11
+ : window.customElements.define.bind(window.customElements);
9
12
 
10
13
  const cache = new Map<string, Element[]>();
11
14
  function updateElement(name: string, fn: (ele: Element) => void) {
@@ -160,147 +163,149 @@ declare global {
160
163
  }
161
164
  }
162
165
 
163
- window._hmrClassRegistry = new Map();
164
- window._hmrRegisterClass =
165
- (name: string) =>
166
- (cls: HasFieldsRecordClass, { addInitializer }: ClassDecoratorContext) => {
167
- addInitializer(() => {
168
- const existed = window._hmrClassRegistry.get(name);
169
-
170
- if (!existed) {
171
- window._hmrClassRegistry.set(name, cls);
172
- return;
173
- }
174
-
175
- logger.info(`class <${name}> update,`, { existed, cls });
176
-
177
- let current: any[] = [existed, cls];
178
- while (current) {
179
- const [oldClass, newClass] = current;
180
- if (oldClass === newClass || current.some((e) => e === GemElement || e === Function.prototype)) break;
181
- if (current.some((e) => !e) || checkClassNeedReload(oldClass, newClass)) {
182
- location.reload();
183
- return;
184
- }
166
+ if (typeof window !== 'undefined' && nativeDefineElement && !window._hmrClassRegistry) {
167
+ window._hmrClassRegistry = new Map();
168
+ window._hmrRegisterClass =
169
+ (name: string) =>
170
+ (cls: HasFieldsRecordClass, { addInitializer }: ClassDecoratorContext) => {
171
+ addInitializer(() => {
172
+ const existed = window._hmrClassRegistry.get(name);
185
173
 
186
- // 修正 `instanceof`
187
- setProperty(newClass, Symbol.hasInstance, function hasInstance(instance: any) {
188
- const isOldInstance = instance instanceof oldClass;
189
- if (isOldInstance) return true;
190
- setProperty(newClass, Symbol.hasInstance, undefined);
191
- const isNew = instance instanceof newClass;
192
- setProperty(newClass, Symbol.hasInstance, hasInstance);
193
- return isNew;
194
- });
195
-
196
- const oldMetadata = getMetadata(oldClass);
197
- const newMetadata = getMetadata(newClass);
198
-
199
- if (checkMetadataNeedReload(oldMetadata, newMetadata)) {
200
- location.reload();
174
+ if (!existed) {
175
+ window._hmrClassRegistry.set(name, cls);
201
176
  return;
202
177
  }
203
178
 
204
- const fields = getFields(oldClass, newClass);
179
+ logger.info(`class <${name}> update,`, { existed, cls });
180
+
181
+ let current: any[] = [existed, cls];
182
+ while (current) {
183
+ const [oldClass, newClass] = current;
184
+ if (oldClass === newClass || current.some((e) => e === GemElement || e === Function.prototype)) break;
185
+ if (current.some((e) => !e) || checkClassNeedReload(oldClass, newClass)) {
186
+ location.reload();
187
+ return;
188
+ }
189
+
190
+ // 修正 `instanceof`
191
+ setProperty(newClass, Symbol.hasInstance, function hasInstance(instance: any) {
192
+ const isOldInstance = instance instanceof oldClass;
193
+ if (isOldInstance) return true;
194
+ setProperty(newClass, Symbol.hasInstance, undefined);
195
+ const isNew = instance instanceof newClass;
196
+ setProperty(newClass, Symbol.hasInstance, hasInstance);
197
+ return isNew;
198
+ });
205
199
 
206
- if (checkFieldsNeedReload(fields)) {
207
- location.reload();
208
- return;
209
- }
200
+ const oldMetadata = getMetadata(oldClass);
201
+ const newMetadata = getMetadata(newClass);
210
202
 
211
- Object.entries(fields.staticFields).forEach(([type, { remove, add }]) => {
212
- remove.forEach((prop) => {
213
- if (safeProp.has(type)) {
214
- deleteProperty(oldClass, prop.name);
215
- }
216
- });
217
- add.forEach((prop) => {
218
- if (safeProp.has(type)) {
219
- setProperty(oldClass, prop.name, getProperty(newClass, prop.name));
220
- }
221
- });
222
- });
223
-
224
- const instanceFieldsEntries = Object.entries(fields.instanceFields);
225
- // 删除的属性重新加回来要先清除占位值
226
- instanceFieldsEntries.forEach(([type, { add }]) => {
227
- add.forEach((prop) => {
228
- if (type in descMap) {
229
- deleteProperty(oldClass.prototype, prop.name);
230
- }
203
+ if (checkMetadataNeedReload(oldMetadata, newMetadata)) {
204
+ location.reload();
205
+ return;
206
+ }
207
+
208
+ const fields = getFields(oldClass, newClass);
209
+
210
+ if (checkFieldsNeedReload(fields)) {
211
+ location.reload();
212
+ return;
213
+ }
214
+
215
+ Object.entries(fields.staticFields).forEach(([type, { remove, add }]) => {
216
+ remove.forEach((prop) => {
217
+ if (safeProp.has(type)) {
218
+ deleteProperty(oldClass, prop.name);
219
+ }
220
+ });
221
+ add.forEach((prop) => {
222
+ if (safeProp.has(type)) {
223
+ setProperty(oldClass, prop.name, getProperty(newClass, prop.name));
224
+ }
225
+ });
231
226
  });
232
- });
233
- updateElement(name, (element) => {
234
- instanceFieldsEntries.forEach(([type, { add, remove }]) => {
227
+
228
+ const instanceFieldsEntries = Object.entries(fields.instanceFields);
229
+ // 删除的属性重新加回来要先清除占位值
230
+ instanceFieldsEntries.forEach(([type, { add }]) => {
235
231
  add.forEach((prop) => {
236
232
  if (type in descMap) {
237
- (descMap as any)[type](undefined, {
238
- name: prop.name,
239
- metadata: oldMetadata,
240
- addInitializer: (fn: () => void) => fn.apply(element),
241
- } as ClassMemberDecoratorContext);
233
+ deleteProperty(oldClass.prototype, prop.name);
242
234
  }
243
235
  });
236
+ });
237
+ updateElement(name, (element) => {
238
+ instanceFieldsEntries.forEach(([type, { add, remove }]) => {
239
+ add.forEach((prop) => {
240
+ if (type in descMap) {
241
+ (descMap as any)[type](undefined, {
242
+ name: prop.name,
243
+ metadata: oldMetadata,
244
+ addInitializer: (fn: () => void) => fn.apply(element),
245
+ } as ClassMemberDecoratorContext);
246
+ }
247
+ });
248
+ remove.forEach((prop) => {
249
+ setProperty(element, prop.name, undefined);
250
+ });
251
+ });
252
+ });
253
+ // 删除的属性在 proto 上添加占位符
254
+ instanceFieldsEntries.forEach(([type, { remove }]) => {
244
255
  remove.forEach((prop) => {
245
- setProperty(element, prop.name, undefined);
256
+ if (type in descMap) {
257
+ // attribute 映射 prop 没有移除
258
+ setProperty(oldClass.prototype, prop.name, undefined);
259
+ }
246
260
  });
247
261
  });
248
- });
249
- // 删除的属性在 proto 上添加占位符
250
- instanceFieldsEntries.forEach(([type, { remove }]) => {
251
- remove.forEach((prop) => {
252
- if (type in descMap) {
253
- // attribute 映射 prop 没有移除
254
- setProperty(oldClass.prototype, prop.name, undefined);
255
- }
262
+
263
+ setArrValue(oldMetadata, 'definedParts', newMetadata.definedParts);
264
+ setArrValue(oldMetadata, 'definedSlots', newMetadata.definedSlots);
265
+ setArrValue(oldMetadata, 'observedProperties', newMetadata.observedProperties);
266
+ setArrValue(oldMetadata, 'observedAttributes', newMetadata.observedAttributes);
267
+ setArrValue(oldMetadata, 'definedEvents', newMetadata.definedEvents);
268
+ setArrValue(oldMetadata, 'definedCSSStates', newMetadata.definedCSSStates);
269
+ setArrValue(oldMetadata, 'adoptedStyleSheets', newMetadata.adoptedStyleSheets);
270
+ setArrValue(oldClass, '_defined_fields_', newClass._defined_fields_);
271
+
272
+ [
273
+ [oldClass, newClass],
274
+ [oldClass.prototype, newClass.prototype],
275
+ ].forEach(([existedObj, newObj]) => {
276
+ Object.assign(
277
+ existedObj,
278
+ Object.fromEntries(
279
+ getHmrMethodKeys(newObj)
280
+ .filter((key) => existedObj[key].toString() !== newObj[key].toString())
281
+ .map((key) => [key, newObj[key]]),
282
+ ),
283
+ );
256
284
  });
257
- });
258
-
259
- setArrValue(oldMetadata, 'definedParts', newMetadata.definedParts);
260
- setArrValue(oldMetadata, 'definedSlots', newMetadata.definedSlots);
261
- setArrValue(oldMetadata, 'observedProperties', newMetadata.observedProperties);
262
- setArrValue(oldMetadata, 'observedAttributes', newMetadata.observedAttributes);
263
- setArrValue(oldMetadata, 'definedEvents', newMetadata.definedEvents);
264
- setArrValue(oldMetadata, 'definedCSSStates', newMetadata.definedCSSStates);
265
- setArrValue(oldMetadata, 'adoptedStyleSheets', newMetadata.adoptedStyleSheets);
266
- setArrValue(oldClass, '_defined_fields_', newClass._defined_fields_);
267
-
268
- [
269
- [oldClass, newClass],
270
- [oldClass.prototype, newClass.prototype],
271
- ].forEach(([existedObj, newObj]) => {
272
- Object.assign(
273
- existedObj,
274
- Object.fromEntries(
275
- getHmrMethodKeys(newObj)
276
- .filter((key) => existedObj[key].toString() !== newObj[key].toString())
277
- .map((key) => [key, newObj[key]]),
278
- ),
279
- );
280
- });
281
-
282
- current = [getPrototypeOf(oldClass), getPrototypeOf(newClass)];
283
- }
284
- });
285
- };
286
285
 
287
- window.customElements.define = (name: string, cls: CustomElementConstructor) => {
288
- const existed = customElements.get(name);
286
+ current = [getPrototypeOf(oldClass), getPrototypeOf(newClass)];
287
+ }
288
+ });
289
+ };
289
290
 
290
- if (!existed) {
291
- nativeDefineElement(name, cls);
292
- return;
293
- }
291
+ window.customElements.define = (name: string, cls: CustomElementConstructor) => {
292
+ const existed = customElements.get(name);
293
+
294
+ if (!existed) {
295
+ nativeDefineElement(name, cls);
296
+ return;
297
+ }
294
298
 
295
- // 等待类更新
296
- queueMicrotask(() => {
297
- logger.info(`<${name}> update,`, { cls });
299
+ // 等待类更新
300
+ queueMicrotask(() => {
301
+ logger.info(`<${name}> update,`, { cls });
298
302
 
299
- updateElement(name, (element) => {
300
- // 触发样式更新,支持 Light DOM
301
- element.after(element);
302
- // 重新渲染
303
- (element as any)[UpdateToken]?.();
303
+ updateElement(name, (element) => {
304
+ // 触发样式更新,支持 Light DOM
305
+ element.after(element);
306
+ // 重新渲染
307
+ (element as any)[UpdateToken]?.();
308
+ });
304
309
  });
305
- });
306
- };
310
+ };
311
+ }
@@ -213,7 +213,8 @@ if (!globalThis._GEMHISTORY) {
213
213
  // 初始化 historyState
214
214
  const { protocol, pathname, search, hash } = location;
215
215
  // 支持在 `data:` `blob:` 中 `import`
216
- if (protocol.startsWith('http')) gemHistory.replace({ path: pathname, query: search, hash });
216
+ if (protocol.startsWith('http') || protocol.startsWith('tauri'))
217
+ gemHistory.replace({ path: pathname, query: search, hash });
217
218
  } else if (nativeHistory.state.$hasCloseHandle) {
218
219
  store(nativeHistory.state);
219
220
  const params = normalizeParams({ title: document.title });
@@ -315,4 +316,5 @@ if (!globalThis._GEMHISTORY) {
315
316
  }
316
317
 
317
318
  const { history, titleStore, basePathStore } = globalThis._GEMHISTORY;
319
+
318
320
  export { basePathStore, history, titleStore };
@@ -615,9 +615,11 @@ export class TemplateInstance implements Disconnectable {
615
615
  }
616
616
  }
617
617
 
618
+ const conditionMap = new Map<Element, ConditionChain>();
618
619
  ifMap.forEach((idx, element) => {
619
620
  const chain = new ConditionChain(element, idx);
620
621
  this._$conditions.push(chain);
622
+ conditionMap.set(element, chain);
621
623
  let ele: Element | null = element;
622
624
  while ((ele = ele.nextElementSibling)) {
623
625
  const pos = elseIfMap.get(ele);
@@ -625,6 +627,13 @@ export class TemplateInstance implements Disconnectable {
625
627
  chain.push(ele, pos);
626
628
  }
627
629
  });
630
+ // A ChildPart can use the following conditional element as its end boundary.
631
+ // Point it to the persistent chain marker before v-if can remove that element.
632
+ for (const part of this._$parts) {
633
+ if (part?.type !== CHILD_PART) continue;
634
+ const chain = conditionMap.get(part._$endNode as Element);
635
+ if (chain) part._$endNode = chain.mark;
636
+ }
628
637
 
629
638
  // We need to set the currentNode away from the cloned tree so that we
630
639
  // don't hold onto the tree even if the tree is detached and should be
@@ -1 +1 @@
1
- export const version = '2.2.5';
1
+ export const version = '2.2.8';
@@ -0,0 +1,19 @@
1
+ import { html, render } from '../lib/lit-html';
2
+ import { expect } from './utils';
3
+
4
+ describe('lit-html', () => {
5
+ it('updates an iterable before a removed v-if element', () => {
6
+ const container = document.createElement('div');
7
+ const view = (messages: string[], permissionRequest: boolean) =>
8
+ html`${messages.map((message) => html`<p>${message}</p>`)}<div v-if=${permissionRequest}>Permission request</div>`;
9
+
10
+ render(view(['Message 1'], false), container);
11
+ expect(container.textContent?.trim()).to.equal('Message 1');
12
+
13
+ expect(() => render(view(['Message 1', 'Message 2'], false), container)).not.to.throw();
14
+ expect(container.textContent?.replace(/\s/g, '')).to.equal('Message1Message2');
15
+
16
+ render(view(['Message 1', 'Message 2'], true), container);
17
+ expect(container.textContent?.replace(/\s/g, '')).to.equal('Message1Message2Permissionrequest');
18
+ });
19
+ });