@dooboostore/simple-web-component 1.0.25 → 1.0.27

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 (82) hide show
  1. package/README.md +284 -68
  2. package/dist/cjs/decorators/attribute.js +32 -43
  3. package/dist/cjs/decorators/attribute.js.map +3 -3
  4. package/dist/cjs/decorators/elementDefine.js +33 -19
  5. package/dist/cjs/decorators/elementDefine.js.map +2 -2
  6. package/dist/cjs/decorators/index.js +1 -0
  7. package/dist/cjs/decorators/index.js.map +2 -2
  8. package/dist/cjs/decorators/lifecycles.js +5 -5
  9. package/dist/cjs/decorators/lifecycles.js.map +2 -2
  10. package/dist/cjs/decorators/query.js +4 -4
  11. package/dist/cjs/decorators/query.js.map +2 -2
  12. package/dist/cjs/decorators/queryAll.js +4 -4
  13. package/dist/cjs/decorators/queryAll.js.map +2 -2
  14. package/dist/cjs/decorators/subscribeSwcAppRouteChange.js +59 -0
  15. package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +7 -0
  16. package/dist/cjs/elements/SwcAppEngine.js +18 -63
  17. package/dist/cjs/elements/SwcAppEngine.js.map +3 -3
  18. package/dist/cjs/elements/SwcAppMixin.js +103 -24
  19. package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
  20. package/dist/cjs/elements/register.js +0 -1
  21. package/dist/cjs/elements/register.js.map +2 -2
  22. package/dist/cjs/utils/Utils.js +18 -0
  23. package/dist/cjs/utils/Utils.js.map +3 -3
  24. package/dist/esm/decorators/attribute.js +32 -43
  25. package/dist/esm/decorators/attribute.js.map +3 -3
  26. package/dist/esm/decorators/elementDefine.js +34 -20
  27. package/dist/esm/decorators/elementDefine.js.map +2 -2
  28. package/dist/esm/decorators/index.js +1 -0
  29. package/dist/esm/decorators/index.js.map +2 -2
  30. package/dist/esm/decorators/lifecycles.js +5 -5
  31. package/dist/esm/decorators/lifecycles.js.map +2 -2
  32. package/dist/esm/decorators/query.js +4 -4
  33. package/dist/esm/decorators/query.js.map +2 -2
  34. package/dist/esm/decorators/queryAll.js +4 -4
  35. package/dist/esm/decorators/queryAll.js.map +2 -2
  36. package/dist/esm/decorators/subscribeSwcAppRouteChange.js +40 -0
  37. package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +7 -0
  38. package/dist/esm/elements/SwcAppEngine.js +18 -63
  39. package/dist/esm/elements/SwcAppEngine.js.map +2 -2
  40. package/dist/esm/elements/SwcAppMixin.js +103 -24
  41. package/dist/esm/elements/SwcAppMixin.js.map +2 -2
  42. package/dist/esm/elements/register.js +0 -1
  43. package/dist/esm/elements/register.js.map +2 -2
  44. package/dist/esm/utils/Utils.js +18 -0
  45. package/dist/esm/utils/Utils.js.map +3 -3
  46. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +135 -2470
  47. package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
  48. package/dist/types/decorators/attribute.d.ts +0 -1
  49. package/dist/types/decorators/attribute.d.ts.map +1 -1
  50. package/dist/types/decorators/elementDefine.d.ts.map +1 -1
  51. package/dist/types/decorators/index.d.ts +1 -0
  52. package/dist/types/decorators/index.d.ts.map +1 -1
  53. package/dist/types/decorators/lifecycles.d.ts +2 -2
  54. package/dist/types/decorators/lifecycles.d.ts.map +1 -1
  55. package/dist/types/decorators/query.d.ts +1 -1
  56. package/dist/types/decorators/query.d.ts.map +1 -1
  57. package/dist/types/decorators/queryAll.d.ts +1 -1
  58. package/dist/types/decorators/queryAll.d.ts.map +1 -1
  59. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +18 -0
  60. package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +1 -0
  61. package/dist/types/elements/SwcAppEngine.d.ts +3 -5
  62. package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
  63. package/dist/types/elements/SwcAppMixin.d.ts +9 -0
  64. package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
  65. package/dist/types/elements/register.d.ts +1 -1
  66. package/dist/types/elements/register.d.ts.map +1 -1
  67. package/dist/types/utils/Utils.d.ts +3 -0
  68. package/dist/types/utils/Utils.d.ts.map +1 -1
  69. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +135 -2470
  70. package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
  71. package/package.json +3 -3
  72. package/src/decorators/attribute.ts +62 -53
  73. package/src/decorators/elementDefine.ts +79 -30
  74. package/src/decorators/index.ts +1 -0
  75. package/src/decorators/lifecycles.ts +2 -2
  76. package/src/decorators/query.ts +5 -5
  77. package/src/decorators/queryAll.ts +8 -7
  78. package/src/decorators/subscribeSwcAppRouteChange.ts +71 -0
  79. package/src/elements/SwcAppEngine.ts +25 -81
  80. package/src/elements/SwcAppMixin.ts +143 -28
  81. package/src/elements/register.ts +0 -2
  82. package/src/utils/Utils.ts +33 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dooboostore/simple-web-component",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -94,8 +94,8 @@
94
94
  "webpack-dev-server": "^5.2.0",
95
95
  "@dooboostore/core": "1.0.30",
96
96
  "@dooboostore/core-web": "1.0.10",
97
- "@dooboostore/simple-boot": "1.0.54",
98
- "@dooboostore/core-node": "1.0.12"
97
+ "@dooboostore/core-node": "1.0.12",
98
+ "@dooboostore/simple-boot": "1.0.54"
99
99
  },
100
100
  "scripts": {
101
101
  "clean": "rm -rf dist",
@@ -2,10 +2,9 @@ import {ReflectUtils} from '@dooboostore/core';
2
2
  import {ensureInit, getElementConfig} from './elementDefine';
3
3
  import {SwcUtils} from '../utils/Utils';
4
4
  import {SpecialSelector, SwcQueryOptions} from '../types';
5
-
6
5
  export interface AttributeFieldOptions extends SwcQueryOptions {
7
6
  name?: string;
8
- connectedInitialize?: boolean;
7
+ // connectedInitialize?: boolean;
9
8
  type?: typeof Number | typeof Boolean | typeof String;
10
9
  }
11
10
 
@@ -81,9 +80,9 @@ export function attribute(selector: string, options: AttributeFieldOptions = {})
81
80
  return (target: Object, propertyKey: string | symbol) => {
82
81
  const constructor = target.constructor;
83
82
  const privateKey = Symbol(String(propertyKey));
84
-
83
+ //
85
84
  const designType = (Reflect as any).getMetadata('design:type', target, propertyKey);
86
-
85
+ //
87
86
  let metaList = ReflectUtils.getOwnMetadata(ATTRIBUTE_FIELD_METADATA_KEY, constructor) as AttributeFieldMetadata[];
88
87
  if (!metaList) {
89
88
  metaList = [];
@@ -105,56 +104,65 @@ export function attribute(selector: string, options: AttributeFieldOptions = {})
105
104
  });
106
105
  };
107
106
 
108
- Object.defineProperty(target, propertyKey, {
109
- get(this: any) {
110
- const attrName = options.name || String(propertyKey);
111
- const conf = getElementConfig(this);
112
- const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
113
- const targetType = options.type || designType;
114
-
115
- if (this.__swc_syncing_init || (options.connectedInitialize && !this.__swc_connected)) {
116
- return convertValue(this[privateKey], targetType);
117
- }
118
-
119
- const targets = resolveTargetEls(this, selector, options, currentWin);
120
- const primaryTarget = targets[0];
121
-
122
- if (primaryTarget && typeof (primaryTarget as any).hasAttribute === 'function') {
123
- if ((primaryTarget as any).hasAttribute(attrName)) {
124
- const domVal = (primaryTarget as any).getAttribute(attrName);
125
- return convertValue(domVal, targetType);
126
- }
127
- return null;
128
- }
129
-
130
- return convertValue(this[privateKey], targetType);
131
- },
132
- set(this: any, nv: any) {
133
- const attrName = options.name || String(propertyKey);
134
- const conf = getElementConfig(this);
135
- const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
107
+ try {
108
+ // Own property를 삭제하여 getter가 작동하도록 함
109
+ // delete target[propertyKey];
110
+
111
+ // const defineTarget = target instanceof Function ? target : (target as any).constructor;
112
+
113
+ Object.defineProperty(target, propertyKey, {
114
+ get(this: any) {
115
+ // try {
116
+ const attrName = options.name || String(propertyKey);
117
+ const conf = getElementConfig(this);
118
+ const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
119
+ const targetType = options.type || designType;
120
+ //
121
+ // connectedInitialize: true이면 privateKey 값만 반환 (DOM 무시)
122
+ // if (options.connectedInitialize === true && (this as any).__swc_syncing_init) {
123
+ // console.log('cccccccc2');
124
+ // return this[privateKey];
125
+ // // return convertValue(this[privateKey], targetType);
126
+ // }
136
127
 
137
- if (this.__swc_syncing_init) {
138
- if (options.connectedInitialize === true) {
139
128
  const targets = resolveTargetEls(this, selector, options, currentWin);
140
- applySetAttribute(this, targets, attrName, nv);
141
- } else {
142
- this[privateKey] = nv;
143
- }
144
- return;
145
- }
146
-
147
- if (!this.__swc_connected) {
148
- this[privateKey] = nv;
149
- return;
150
- }
151
-
152
- const targets = resolveTargetEls(this, selector, options, currentWin);
153
- applySetAttribute(this, targets, attrName, nv);
154
- },
155
- enumerable: true,
156
- configurable: true
157
- });
129
+ const primaryTarget = targets[0];
130
+
131
+ if (primaryTarget && typeof (primaryTarget as any).hasAttribute === 'function') {
132
+ if ((primaryTarget as any).hasAttribute(attrName)) {
133
+ const domVal = (primaryTarget as any).getAttribute(attrName);
134
+ return convertValue(domVal, targetType);
135
+ }
136
+ return null;
137
+ }
138
+
139
+ return convertValue(this[privateKey], targetType);
140
+ // } catch (err) {
141
+ // console.error('Error in getter:', err);
142
+ // throw err;
143
+ // }
144
+ },
145
+ set(this: any, nv: any) {
146
+ // this[propertyKey] = nv;
147
+ const attrName = options.name || String(propertyKey);
148
+ const conf = getElementConfig(this);
149
+ const currentWin = this._resolveWindow?.(conf) || ((typeof window !== 'undefined' ? window : undefined) as any);
150
+
151
+
152
+ // if (!this.__swc_connected) {
153
+ // this[privateKey] = nv;
154
+ // return;
155
+ // }
156
+
157
+ const targets = resolveTargetEls(this, selector, options, currentWin);
158
+ applySetAttribute(this, targets, attrName, nv);
159
+ },
160
+ enumerable: true,
161
+ configurable: true
162
+ });
163
+ } catch (err) {
164
+ console.error('[SWC] defineProperty error:', err);
165
+ }
158
166
  };
159
167
  }
160
168
 
@@ -187,5 +195,6 @@ export function attributeHost(targetOrAttrOrOptions?: any, propertyKeyOrOptions?
187
195
  }
188
196
 
189
197
  export const findAllAttributeMetadata = (target: any): AttributeFieldMetadata[] => {
190
- return ReflectUtils.findAllMetadata<AttributeFieldMetadata[]>(ATTRIBUTE_FIELD_METADATA_KEY, target).flat();
198
+ const actualTarget = target instanceof Function ? target : target.constructor;
199
+ return ReflectUtils.findAllMetadata<AttributeFieldMetadata[]>(ATTRIBUTE_FIELD_METADATA_KEY, actualTarget).flat();
191
200
  };
@@ -1,11 +1,13 @@
1
1
  import { ReflectUtils } from '@dooboostore/core';
2
2
  import { FunctionUtils } from '@dooboostore/core';
3
3
  import { getAddEventListenerMetadata } from './addEventListener';
4
- import { ON_CONSTRUCTOR_METADATA_KEY, ON_BEFORE_CONNECTED_METADATA_KEY, ON_AFTER_CONNECTED_METADATA_KEY, ON_BEFORE_DISCONNECTED_METADATA_KEY, ON_AFTER_DISCONNECTED_METADATA_KEY, ON_BEFORE_ADOPTED_METADATA_KEY, ON_AFTER_ADOPTED_METADATA_KEY, findAllLifecycleMetadata, getOnConnectedInnerHtmlMetadata } from './lifecycles';
4
+ import { ON_INITIALIZE_METADATA_KEY, ON_BEFORE_CONNECTED_METADATA_KEY, ON_AFTER_CONNECTED_METADATA_KEY, ON_BEFORE_DISCONNECTED_METADATA_KEY, ON_AFTER_DISCONNECTED_METADATA_KEY, ON_BEFORE_ADOPTED_METADATA_KEY, ON_AFTER_ADOPTED_METADATA_KEY, findAllLifecycleMetadata, getOnConnectedInnerHtmlMetadata } from './lifecycles';
5
5
  import { getEmitCustomEventMetadataList } from './emitCustomEvent';
6
6
  import { findAllAttributeChangedMetadata } from './changedAttributeHost';
7
7
  import { findAllAttributeApplyMetadata } from './applyAttribute';
8
8
  import { findAllAttributeMetadata } from './attribute';
9
+ import { getQueryMetadata } from './query';
10
+ import { getQueryAllMetadata } from './queryAll';
9
11
  import { SwcUtils } from '../utils/Utils';
10
12
  import { HTML_TAG_ENTRIES, DOM_EVENT_NAMES } from '../config/config';
11
13
  import { SituationTypeContainer, SituationTypeContainers } from '@dooboostore/simple-boot/decorators/inject/Inject';
@@ -39,12 +41,60 @@ export const ensureInit = (inst: any) => {
39
41
  inst._boundListeners = [];
40
42
  inst.__swc_initialized = true;
41
43
 
44
+ // const attributeList = findAllQ(inst);
45
+ // attributeList.forEach(meta => {
46
+ // delete (inst as any)[meta.propertyKey];
47
+ // });
48
+
49
+ const target = inst instanceof Function ? inst : inst.constructor;
50
+
51
+ // ╔════════════════════════════════════════════════════════════════════════════════╗
52
+ // ║ decorator 필드의 own property 삭제 ║
53
+ // ║ ║
54
+ // ║ 이유: ║
55
+ // ║ 1. TypeScript는 초기값이 있는 필드를 constructor에서 own property로 생성 ║
56
+ // ║ @attributeHost('id') myId: string = "default" → this.myId = "default" ║
57
+ // ║ ║
58
+ // ║ 2. JavaScript의 property lookup은 own property를 먼저 찾음 ║
59
+ // ║ - this.myId 접근 ║
60
+ // ║ - 1순위: instance의 own property 있나? → 있으면 그것 반환 ║
61
+ // ║ - 2순위: prototype의 getter 있나? → 있으면 호출 ║
62
+ // ║ ║
63
+ // ║ 3. own property가 있으면 prototype의 getter는 절대 호출되지 않음 ║
64
+ // ║ 따라서 getter에서 정의된 DOM 동기화 로직이 작동하지 않음 ║
65
+ // ║ ║
66
+ // ║ 4. own property를 삭제하면 JavaScript가 prototype의 getter를 찾게 됨 ║
67
+ // ║ → getter 호출됨 → DOM 속성 값 동기화됨 ║
68
+ // ╚════════════════════════════════════════════════════════════════════════════════╝
69
+
70
+ // @attribute 필드의 own property 삭제 → getter/setter 작동
71
+ const attributeList = findAllAttributeMetadata(target);
72
+ attributeList.forEach(meta => {
73
+ delete (inst as any)[meta.propertyKey];
74
+ });
75
+
76
+ // @query 필드의 own property 삭제 → getter 작동
77
+ const queryList = getQueryMetadata(target);
78
+ if (queryList) {
79
+ queryList.forEach(meta => {
80
+ delete (inst as any)[meta.propertyKey];
81
+ });
82
+ }
83
+
84
+ // @queryAll 필드의 own property 삭제 → getter 작동
85
+ const queryAllList = getQueryAllMetadata(target);
86
+ if (queryAllList) {
87
+ queryAllList.forEach(meta => {
88
+ delete (inst as any)[meta.propertyKey];
89
+ });
90
+ }
91
+
42
92
  // Call constructor script if present
43
93
  const hostSet = SwcUtils.getHostSet(inst);
44
94
  inst._executeSwcScript?.('swc-on-constructor', hostSet);
45
95
 
46
- // Call @onConstructor lifecycle methods
47
- const cMethods = findAllLifecycleMetadata(inst, ON_CONSTRUCTOR_METADATA_KEY);
96
+ // Call @onInitialize lifecycle methods
97
+ const cMethods = findAllLifecycleMetadata(inst, ON_INITIALIZE_METADATA_KEY);
48
98
  if (cMethods) {
49
99
  for (const m of cMethods) (inst)._invokeLifecycleMethod(m, hostSet);
50
100
  }
@@ -79,9 +129,10 @@ function buildEnv(configWindow?: Window) {
79
129
  const doc: Document = win?.document;
80
130
  const builtInTagMap = new Map<any, string>();
81
131
  for (const [cls, tag] of HTML_TAG_ENTRIES) {
82
- if (win?.[cls]) builtInTagMap.set(win[cls], tag);
132
+ if ((win as any)?.[cls]) builtInTagMap.set((win as any)[cls], tag);
83
133
  }
84
134
  const domHelpers = SwcUtils.getHelperSet(win);
135
+
85
136
  return { win, doc, builtInTagMap, domHelpers };
86
137
  }
87
138
 
@@ -238,15 +289,16 @@ export const elementDefine =
238
289
 
239
290
  // Collect all observed attributes from all decorators
240
291
  const attributeApplyNames = Array.from(applyAttributeMap.values()).map(it => it.options.name).filter(Boolean) as string[];
241
- const attributeListNames = attributeList.map(it => it.options.name || String(it.propertyKey));
242
-
292
+ // const attributeListNames = attributeList.map(it => it.options.name || String(it.propertyKey));
293
+ const hostAttributes = attributeList.filter(it => it.selector === ':host').map(it=>it.options.name || String(it.propertyKey));
294
+
243
295
  const mergedObservedAttributes = [...new Set([
244
- ...(metadata.observedAttributes ?? []),
245
- ...attrChangeMap.keys(),
296
+ ...(metadata.observedAttributes ?? []),
297
+ ...attrChangeMap.keys(),
246
298
  ...attributeApplyNames,
247
- ...attributeListNames,
248
- ...emitHostCustomEventList.map(it => (it.options as any).attributeName).filter(Boolean),
249
- ...swcLifecycleAttributes,
299
+ ...hostAttributes,
300
+ ...emitHostCustomEventList.map(it => (it.options as any).attributeName).filter(Boolean),
301
+ ...swcLifecycleAttributes,
250
302
  ...swcOnEvents
251
303
  ])];
252
304
 
@@ -259,35 +311,21 @@ export const elementDefine =
259
311
  proto.connectedCallback = function () {
260
312
  ensureInit(this);
261
313
 
314
+ const hostSet = SwcUtils.getHostSet(this as any);
315
+ const appHost = hostSet.$appHost;
316
+ const conf = getElementConfig(this);
317
+ const currentWin = (this as any)._resolveWindow(conf);
318
+
262
319
  if (connectedInnerHtmlList.some(it => it.options.useShadow === true) && !this.shadowRoot) {
263
320
  this.attachShadow({ mode: 'open' });
264
321
  }
265
322
 
266
- const conf = getElementConfig(this);
267
- const currentWin = (this as any)._resolveWindow(conf);
268
- const hostSet = SwcUtils.getHostSet(this as any);
269
-
270
323
  (this as any)._executeSwcScript('swc-on-before-connected', hostSet);
271
324
  const bMethods = findAllLifecycleMetadata(this, ON_BEFORE_CONNECTED_METADATA_KEY);
272
325
  if (bMethods) {
273
326
  for (const m of bMethods) (this as any)._invokeLifecycleMethod(m, hostSet);
274
327
  }
275
328
 
276
- // Initialize @attribute fields
277
- attributeList.forEach(meta => {
278
- if (meta.options.connectedInitialize === true) {
279
- (this as any).__swc_syncing_init = true;
280
- try {
281
- const memoryVal = (this as any)[meta.privateKey];
282
- if (memoryVal !== undefined) {
283
- (this as any)[meta.propertyKey] = memoryVal;
284
- }
285
- } finally {
286
- (this as any).__swc_syncing_init = false;
287
- }
288
- }
289
- });
290
-
291
329
  if (connectedInnerHtmlList.length > 0) {
292
330
  let sContent = '',
293
331
  lContent = '';
@@ -439,6 +477,11 @@ export const elementDefine =
439
477
  (this as any)._executeSwcScript('swc-on-connected', hostSet);
440
478
  (this as any)._executeSwcScript('swc-on-after-connected', hostSet);
441
479
  (this as any).__swc_connected = true;
480
+
481
+ // Register to appHost LAST when all lifecycle completed
482
+ if (appHost && typeof (appHost as any)._connected === 'function') {
483
+ (appHost as any)._connected(this);
484
+ }
442
485
  };
443
486
 
444
487
  const originalDisconnected = proto.disconnectedCallback;
@@ -464,6 +507,12 @@ export const elementDefine =
464
507
  (this as any)._executeSwcScript('swc-on-disconnected', hostSet);
465
508
  (this as any)._executeSwcScript('swc-on-after-disconnected', hostSet);
466
509
  (this as any).__swc_connected = false;
510
+
511
+ // Remove from appHost LAST when disconnected
512
+ const appHost = hostSet.$appHost;
513
+ if (appHost && typeof (appHost as any)._disconnected === 'function') {
514
+ (appHost as any)._disconnected(this);
515
+ }
467
516
  };
468
517
 
469
518
  const originalAdopted = proto.adoptedCallback;
@@ -10,3 +10,4 @@ export * from './queryAll';
10
10
  export * from './applyClass';
11
11
  export * from './applyStyle';
12
12
  export * from './applyAttribute';
13
+ export * from './subscribeSwcAppRouteChange';
@@ -1,6 +1,6 @@
1
1
  import { ReflectUtils } from '@dooboostore/core';
2
2
 
3
- export const ON_CONSTRUCTOR_METADATA_KEY = Symbol.for('simple-web-component:on-constructor');
3
+ export const ON_INITIALIZE_METADATA_KEY = Symbol.for('simple-web-component:on-constructor');
4
4
  export const ON_BEFORE_CONNECTED_METADATA_KEY = Symbol.for('simple-web-component:on-before-connected');
5
5
  export const ON_AFTER_CONNECTED_METADATA_KEY = Symbol.for('simple-web-component:on-after-connected');
6
6
  export const ON_BEFORE_DISCONNECTED_METADATA_KEY = Symbol.for('simple-web-component:on-before-disconnected');
@@ -33,7 +33,7 @@ function createLifecycleDecorator(metadataKey: symbol): MethodDecorator {
33
33
  };
34
34
  }
35
35
 
36
- export const onConstructor = createLifecycleDecorator(ON_CONSTRUCTOR_METADATA_KEY);
36
+ export const onInitialize = createLifecycleDecorator(ON_INITIALIZE_METADATA_KEY);
37
37
  export const onBeforeConnected = createLifecycleDecorator(ON_BEFORE_CONNECTED_METADATA_KEY);
38
38
  export const onAfterConnected = createLifecycleDecorator(ON_AFTER_CONNECTED_METADATA_KEY);
39
39
  export const onConnected = onAfterConnected;
@@ -102,11 +102,6 @@ export function query(selectorOrTarget: string, options: any = {}): PropertyDeco
102
102
  };
103
103
  }
104
104
 
105
- export const getQueryMetadata = (target: any): QueryMetadata[] | undefined => {
106
- const constructor = target instanceof Function ? target : target.constructor;
107
- return ReflectUtils.getMetadata(QUERY_METADATA_KEY, constructor);
108
- };
109
-
110
105
  /**
111
106
  * @queryHost decorator - simplified version of @query for :host selector
112
107
  * Usage: @queryHost testzzs: HTMLElement | undefined;
@@ -257,3 +252,8 @@ export function lastAppHost(targetOrOptions?: any, propertyKey?: string | symbol
257
252
  return query(':lastAppHost', {});
258
253
  }
259
254
  }
255
+
256
+ export const getQueryMetadata = (target: any): QueryMetadata[] | undefined => {
257
+ const constructor = target instanceof Function ? target : target.constructor;
258
+ return ReflectUtils.getMetadata(QUERY_METADATA_KEY, constructor);
259
+ };
@@ -102,12 +102,6 @@ export function queryAll(selectorOrTarget: string, options: any = {}): PropertyD
102
102
  });
103
103
  };
104
104
  }
105
-
106
- export const getQueryAllMetadata = (target: any): QueryAllMetadata[] | undefined => {
107
- const constructor = target instanceof Function ? target : target.constructor;
108
- return ReflectUtils.getMetadata(QUERY_ALL_METADATA_KEY, constructor);
109
- };
110
-
111
105
  /**
112
106
  * @hostQueryAll decorator - simplified version of @queryAll for :host selector
113
107
  * Usage: @hostQueryAll()
@@ -261,4 +255,11 @@ export function allLastAppHost(targetOrOptions?: any, propertyKey?: string | sym
261
255
  } else {
262
256
  return queryAll(':lastAppHost', {});
263
257
  }
264
- }
258
+ }
259
+
260
+
261
+ export const getQueryAllMetadata = (target: any): QueryAllMetadata[] | undefined => {
262
+ const constructor = target instanceof Function ? target : target.constructor;
263
+ return ReflectUtils.getMetadata(QUERY_ALL_METADATA_KEY, constructor);
264
+ };
265
+
@@ -0,0 +1,71 @@
1
+ import { ReflectUtils } from '@dooboostore/core';
2
+ import { Router } from '@dooboostore/core-web';
3
+
4
+ export const SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY = Symbol.for('simple-web-component:subscribe-swc-app-route-change');
5
+
6
+ export interface SwcAppRouteChangeSubscriberMetadata {
7
+ propertyKey: string | symbol;
8
+ pathPattern?: string | string[];
9
+ filter?: (router: Router) => boolean;
10
+ }
11
+
12
+ export interface SwcAppRouteChangeOptions {
13
+ path?: string | string[];
14
+ filter?: (router: Router) => boolean;
15
+ }
16
+
17
+ function createSwcAppRouteChangeSubscriber(options?: SwcAppRouteChangeOptions): MethodDecorator {
18
+ return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
19
+ const pathPattern = options?.path;
20
+ const filter = options?.filter;
21
+
22
+ const constructor = target.constructor;
23
+ let list = ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, constructor) as SwcAppRouteChangeSubscriberMetadata[];
24
+ if (!list) {
25
+ list = [];
26
+ ReflectUtils.defineMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, list, constructor);
27
+ }
28
+ list.push({ propertyKey, pathPattern, filter });
29
+
30
+ return descriptor;
31
+ };
32
+ }
33
+
34
+ // 오버로드 시그니처 - 직접 사용 (괄호 없음)
35
+ export function subscribeSwcAppRouteChange(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
36
+ // 오버로드 시그니처 - 함수 호출 (괄호 있음)
37
+ export function subscribeSwcAppRouteChange(): MethodDecorator;
38
+ export function subscribeSwcAppRouteChange(pathPattern: string): MethodDecorator;
39
+ export function subscribeSwcAppRouteChange(pathPatterns: string[]): MethodDecorator;
40
+ export function subscribeSwcAppRouteChange(options: SwcAppRouteChangeOptions): MethodDecorator;
41
+
42
+ // 실제 구현 (이중 모드)
43
+ export function subscribeSwcAppRouteChange(targetOrOptions?: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor): any {
44
+ // 직접 데코레이터로 사용된 경우 (괄호 없음): @subscribeSwcAppRouteChange
45
+ // propertyKey가 string | symbol이고 descriptor가 있으면 직접 사용
46
+ if (propertyKey && (typeof propertyKey === 'string' || typeof propertyKey === 'symbol')) {
47
+ return createSwcAppRouteChangeSubscriber({})(targetOrOptions, propertyKey, descriptor);
48
+ }
49
+
50
+ // 함수로 호출된 경우 (괄호 있음): @subscribeSwcAppRouteChange() / @subscribeSwcAppRouteChange('/path')
51
+ let options: SwcAppRouteChangeOptions = {};
52
+
53
+ if (Array.isArray(targetOrOptions)) {
54
+ // 배열: @subscribeSwcAppRouteChange(['/path1', '/path2'])
55
+ options.path = targetOrOptions;
56
+ } else if (typeof targetOrOptions === 'string') {
57
+ // 문자열: @subscribeSwcAppRouteChange('/path')
58
+ options.path = targetOrOptions;
59
+ } else if (targetOrOptions && typeof targetOrOptions === 'object') {
60
+ // 객체: @subscribeSwcAppRouteChange({ path: '/path', filter: ... })
61
+ options = targetOrOptions;
62
+ }
63
+
64
+ return createSwcAppRouteChangeSubscriber(options);
65
+ }
66
+
67
+ // Helper function to retrieve route change subscribers metadata
68
+ export const getSubscribeSwcAppRouteChangeMetadata = (target: any): SwcAppRouteChangeSubscriberMetadata[] | undefined => {
69
+ const constructor = target instanceof Function ? target : target.constructor;
70
+ return ReflectUtils.getOwnMetadata(SUBSCRIBE_SWC_APP_ROUTE_CHANGE_METADATA_KEY, constructor);
71
+ };
@@ -13,13 +13,12 @@ import { SwcAppInterface } from '../types';
13
13
  import {Intent} from "@dooboostore/simple-boot";
14
14
 
15
15
  export type SwcConfigType = {
16
- rootRouter: ConstructorType<any> | symbol;
17
16
  routeType: 'path' | 'hash' | 'element';
18
17
  connectMode?: 'direct' | 'swap';
19
18
  container?: symbol;
20
19
  path?: string;
21
20
  window: Window;
22
- onStarted?: (app: SwcAppInterface) => void;
21
+ onEngineStarted?: (sp: SimpleApplication, app: SwcAppInterface) => void;
23
22
  onConnected?: (app: SwcAppInterface) => void;
24
23
  onDisconnected?: (app: SwcAppInterface) => void;
25
24
  };
@@ -30,7 +29,6 @@ export class SwcAppEngine {
30
29
  public simpleApplication?: SimpleApplication;
31
30
  public router?: Router;
32
31
  private routerSubscription?: Subscription;
33
- private _isInitialized = false;
34
32
  private _config?: SwcConfigType;
35
33
 
36
34
  constructor(private host: HTMLElement) {}
@@ -38,96 +36,42 @@ export class SwcAppEngine {
38
36
  get config() {
39
37
  return this._config;
40
38
  }
41
- async connect(config?: SwcAttributeConfigType) {
42
- if (this._isInitialized) return;
43
-
44
- let userConfig = config;
45
- const win = this.host.ownerDocument?.defaultView || window;
46
- const Node = (win as any).Node as typeof globalThis.Node;
47
- if (!userConfig) {
48
- const configStr = this.host.getAttribute('swc-get-application-config');
49
- if (configStr) {
50
- try {
51
- // console.log('vvvvvvvvvvvvvvvv->>>>', SwcUtils.findNearestSwcHost(this.host), SwcUtils.getHostSet(this.host))
52
- userConfig = FunctionUtils.executeReturn({
53
- script: configStr,
54
- context: this.host,
55
- args: SwcUtils.getHelperAndHostSet(win, SwcUtils.findNearestSwcHost(this.host))
56
- });
57
- } catch (e) {
58
- console.error('[SWC-APP-ENGINE] Script execution failed:', e);
59
- }
60
- }
61
- }
62
-
63
- if (!userConfig) return;
64
- this._isInitialized = true;
65
-
39
+ async connect(config: SwcConfigType) {
66
40
  try {
67
- if (userConfig.rootRouter) {
68
- this._config = {
69
- routeType: 'element',
70
- window: win,
71
- ...userConfig
72
- } as SwcConfigType;
73
- const _config = this._config;
74
-
75
- // Router Selection
76
- if (_config.routeType === 'path') {
77
- this.router = new PathRouter({ window: _config.window, firstUrl: _config.path });
78
- } else if (_config.routeType === 'hash') {
79
- this.router = new HashRouter({ window: _config.window, firstUrl: _config.path });
80
- } else if (_config.routeType === 'element') {
81
- this.router = new ElementRouter({ window: _config.window, firstUrl: _config.path });
82
- }
83
-
84
- // console.log('vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv', this.router)
85
-
86
- // Routing sync
87
- if (this.router) {
88
- this.routerSubscription = this.router.observable.subscribe((route: RouterEventType) => {
89
- if (route.triggerPoint === 'end') {
90
- const targetPath = (route.path || '/') + route.search;
91
- const intent = new Intent(targetPath);
92
- this.simpleApplication?.routing(intent);
93
- }
94
- });
95
- }
41
+ this._config = config;
42
+
43
+ // Router Selection
44
+ if (this._config.routeType === 'path') {
45
+ this.router = new PathRouter({ window: this._config.window, firstUrl: this._config.path });
46
+ } else if (this._config.routeType === 'hash') {
47
+ this.router = new HashRouter({ window: this._config.window, firstUrl: this._config.path });
48
+ } else if (this._config.routeType === 'element') {
49
+ this.router = new ElementRouter({ window: this._config.window, firstUrl: this._config.path });
50
+ }
96
51
 
52
+ // console.log('vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv', this.router)
97
53
 
98
- // App start
99
- const containerSymbol = _config.container || (this.host as any).getContainerSymbol?.();
100
- this.simpleApplication = new SimpleApplication(new SimOption({
101
- excludeProxys: [Node],
102
- rootRouter: _config.rootRouter,
54
+ // App start
55
+ const containerSymbol = this._config.container || (this.host as any).getContainerSymbol?.();
56
+ this.simpleApplication = new SimpleApplication(
57
+ new SimOption({
58
+ excludeProxys: [Node],
103
59
  container: containerSymbol
104
- }));
105
-
106
- const otherInstanceSim = new Map<any, any>();
107
- if (this.router) otherInstanceSim.set(Router, this.router);
108
- this.simpleApplication.run(otherInstanceSim);
60
+ })
61
+ );
109
62
 
63
+ const otherInstanceSim = new Map<any, any>();
64
+ if (this.router) otherInstanceSim.set(Router, this.router);
65
+ this.simpleApplication.run(otherInstanceSim);
110
66
 
111
-
112
- // Rendering
113
- const data = this.simpleApplication.sim(_config.rootRouter);
114
- if (data instanceof Node) {
115
- // console.warn('[SWC-APP-ENGINE] Rendering failed: Root element is not a DocumentFragment.', data);
116
- if (this.host.shadowRoot) this.host.shadowRoot.replaceChildren(data);
117
- else this.host.replaceChildren(data);
118
- }
119
-
120
- if (_config.path) this.router?.go(_config.path);
121
- if (_config.onStarted) {
122
- _config.onStarted(this.host as unknown as SwcAppInterface);
123
- }
67
+ if (this._config.onEngineStarted) {
68
+ this._config.onEngineStarted(this.simpleApplication, this.host as unknown as SwcAppInterface);
124
69
  }
125
70
  } catch (e) {
126
71
  console.error('[SWC-APP-ENGINE] Init failed:', e);
127
72
  }
128
73
  }
129
74
 
130
-
131
75
  disconnect() {
132
76
  this.routerSubscription?.unsubscribe();
133
77
  }