@dooboostore/simple-web-component 1.0.26 → 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.
- package/README.md +186 -3441
- package/dist/cjs/decorators/attribute.js +32 -43
- package/dist/cjs/decorators/attribute.js.map +3 -3
- package/dist/cjs/decorators/elementDefine.js +33 -19
- package/dist/cjs/decorators/elementDefine.js.map +2 -2
- package/dist/cjs/decorators/index.js +1 -0
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/lifecycles.js +5 -5
- package/dist/cjs/decorators/lifecycles.js.map +2 -2
- package/dist/cjs/decorators/query.js +4 -4
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/queryAll.js +4 -4
- package/dist/cjs/decorators/queryAll.js.map +2 -2
- package/dist/cjs/decorators/subscribeSwcAppRouteChange.js +59 -0
- package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/cjs/elements/SwcAppEngine.js +18 -63
- package/dist/cjs/elements/SwcAppEngine.js.map +3 -3
- package/dist/cjs/elements/SwcAppMixin.js +103 -24
- package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
- package/dist/cjs/elements/register.js +0 -1
- package/dist/cjs/elements/register.js.map +2 -2
- package/dist/cjs/utils/Utils.js +18 -0
- package/dist/cjs/utils/Utils.js.map +3 -3
- package/dist/esm/decorators/attribute.js +32 -43
- package/dist/esm/decorators/attribute.js.map +3 -3
- package/dist/esm/decorators/elementDefine.js +34 -20
- package/dist/esm/decorators/elementDefine.js.map +2 -2
- package/dist/esm/decorators/index.js +1 -0
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/lifecycles.js +5 -5
- package/dist/esm/decorators/lifecycles.js.map +2 -2
- package/dist/esm/decorators/query.js +4 -4
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/queryAll.js +4 -4
- package/dist/esm/decorators/queryAll.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppRouteChange.js +40 -0
- package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/esm/elements/SwcAppEngine.js +18 -63
- package/dist/esm/elements/SwcAppEngine.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +103 -24
- package/dist/esm/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm/elements/register.js +0 -1
- package/dist/esm/elements/register.js.map +2 -2
- package/dist/esm/utils/Utils.js +18 -0
- package/dist/esm/utils/Utils.js.map +3 -3
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +164 -2480
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/decorators/attribute.d.ts +0 -1
- package/dist/types/decorators/attribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/index.d.ts +1 -0
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/lifecycles.d.ts +2 -2
- package/dist/types/decorators/lifecycles.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +1 -1
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/queryAll.d.ts +1 -1
- package/dist/types/decorators/queryAll.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +18 -0
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +1 -0
- package/dist/types/elements/SwcAppEngine.d.ts +3 -5
- package/dist/types/elements/SwcAppEngine.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts +9 -0
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/elements/register.d.ts +1 -1
- package/dist/types/elements/register.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +3 -0
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +164 -2480
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +2 -2
- package/src/decorators/attribute.ts +62 -53
- package/src/decorators/elementDefine.ts +79 -30
- package/src/decorators/index.ts +1 -0
- package/src/decorators/lifecycles.ts +2 -2
- package/src/decorators/query.ts +5 -5
- package/src/decorators/queryAll.ts +8 -7
- package/src/decorators/subscribeSwcAppRouteChange.ts +71 -0
- package/src/elements/SwcAppEngine.ts +25 -81
- package/src/elements/SwcAppMixin.ts +143 -28
- package/src/elements/register.ts +0 -2
- 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.
|
|
3
|
+
"version": "1.0.27",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"webpack-cli": "^5.1.4",
|
|
94
94
|
"webpack-dev-server": "^5.2.0",
|
|
95
95
|
"@dooboostore/core": "1.0.30",
|
|
96
|
-
"@dooboostore/core-node": "1.0.12",
|
|
97
96
|
"@dooboostore/core-web": "1.0.10",
|
|
97
|
+
"@dooboostore/core-node": "1.0.12",
|
|
98
98
|
"@dooboostore/simple-boot": "1.0.54"
|
|
99
99
|
},
|
|
100
100
|
"scripts": {
|
|
@@ -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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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 {
|
|
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 @
|
|
47
|
-
const cMethods = findAllLifecycleMetadata(inst,
|
|
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
|
-
...
|
|
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;
|
package/src/decorators/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
2
|
|
|
3
|
-
export const
|
|
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
|
|
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;
|
package/src/decorators/query.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
}
|