@dooboostore/simple-web-component 1.0.49 → 1.0.51
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 +790 -337
- package/dist/cjs/SwcAppEngine.js.map +2 -2
- package/dist/cjs/decorators/addEventListener.js +1925 -22
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/applyAttribute.js +41 -47
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +14 -11
- package/dist/cjs/decorators/applyClass.js.map +2 -2
- package/dist/cjs/decorators/applyNode.js +19 -21
- package/dist/cjs/decorators/applyNode.js.map +2 -2
- package/dist/cjs/decorators/applyProperty.js +24 -48
- package/dist/cjs/decorators/applyProperty.js.map +2 -2
- package/dist/cjs/decorators/applySlot.js +9 -8
- package/dist/cjs/decorators/applySlot.js.map +2 -2
- package/dist/cjs/decorators/applyStyle.js +14 -11
- package/dist/cjs/decorators/applyStyle.js.map +2 -2
- package/dist/cjs/decorators/around.js +124 -0
- package/dist/cjs/decorators/around.js.map +7 -0
- package/dist/cjs/decorators/changedAttribute.js +61 -24
- package/dist/cjs/decorators/changedAttribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +240 -196
- package/dist/cjs/decorators/elementDefine.js.map +3 -3
- package/dist/cjs/decorators/emitCustomEvent.js +14 -12
- package/dist/cjs/decorators/emitCustomEvent.js.map +2 -2
- package/dist/cjs/decorators/eventMedia.js +112 -0
- package/dist/cjs/decorators/eventMedia.js.map +7 -0
- package/dist/cjs/decorators/fetch.js +278 -0
- package/dist/cjs/decorators/fetch.js.map +7 -0
- package/dist/cjs/decorators/index.js +8 -2
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/intersectionObserver.js +76 -65
- package/dist/cjs/decorators/intersectionObserver.js.map +2 -2
- package/dist/cjs/decorators/lifecycles.js +11 -11
- package/dist/cjs/decorators/lifecycles.js.map +2 -2
- package/dist/cjs/decorators/mutationObserver.js +97 -90
- package/dist/cjs/decorators/mutationObserver.js.map +3 -3
- package/dist/cjs/decorators/parameter.js +156 -0
- package/dist/cjs/decorators/parameter.js.map +7 -0
- package/dist/cjs/decorators/publishSwcAppMessage.js +2 -2
- package/dist/cjs/decorators/publishSwcAppMessage.js.map +2 -2
- package/dist/cjs/decorators/query.js +11 -5
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/requestAnimationFrame.js +115 -0
- package/dist/cjs/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/cjs/decorators/resizeObserver.js +84 -69
- package/dist/cjs/decorators/resizeObserver.js.map +3 -3
- package/dist/cjs/decorators/setInterval.js +65 -22
- package/dist/cjs/decorators/setInterval.js.map +2 -2
- package/dist/cjs/decorators/setTimeout.js +68 -22
- package/dist/cjs/decorators/setTimeout.js.map +2 -2
- package/dist/cjs/decorators/state.js +13 -10
- package/dist/cjs/decorators/state.js.map +2 -2
- package/dist/cjs/decorators/{subscribeSwcAppMessageWhileConnected.js → subscribeSwcAppMessage.js} +20 -21
- package/dist/cjs/decorators/subscribeSwcAppMessage.js.map +7 -0
- package/dist/cjs/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +23 -19
- package/dist/cjs/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/cjs/decorators/swcAppRoute.js +149 -0
- package/dist/cjs/decorators/swcAppRoute.js.map +7 -0
- package/dist/cjs/elements/SwcApp.js +70 -0
- package/dist/cjs/elements/SwcApp.js.map +2 -2
- package/dist/cjs/elements/SwcAppMixin.js +256 -67
- package/dist/cjs/elements/SwcAppMixin.js.map +3 -3
- package/dist/cjs/types/index.js +5 -1
- package/dist/cjs/types/index.js.map +2 -2
- package/dist/cjs/types/window.d.js.map +1 -1
- package/dist/cjs/utils/Utils.js +36 -15
- package/dist/cjs/utils/Utils.js.map +2 -2
- package/dist/esm/SwcAppEngine.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +1925 -22
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/applyAttribute.js +41 -47
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +14 -11
- package/dist/esm/decorators/applyClass.js.map +2 -2
- package/dist/esm/decorators/applyNode.js +20 -22
- package/dist/esm/decorators/applyNode.js.map +2 -2
- package/dist/esm/decorators/applyProperty.js +24 -48
- package/dist/esm/decorators/applyProperty.js.map +2 -2
- package/dist/esm/decorators/applySlot.js +9 -8
- package/dist/esm/decorators/applySlot.js.map +2 -2
- package/dist/esm/decorators/applyStyle.js +14 -11
- package/dist/esm/decorators/applyStyle.js.map +2 -2
- package/dist/esm/decorators/around.js +105 -0
- package/dist/esm/decorators/around.js.map +7 -0
- package/dist/esm/decorators/changedAttribute.js +61 -24
- package/dist/esm/decorators/changedAttribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +238 -194
- package/dist/esm/decorators/elementDefine.js.map +3 -3
- package/dist/esm/decorators/emitCustomEvent.js +15 -13
- package/dist/esm/decorators/emitCustomEvent.js.map +2 -2
- package/dist/esm/decorators/eventMedia.js +94 -0
- package/dist/esm/decorators/eventMedia.js.map +7 -0
- package/dist/esm/decorators/fetch.js +259 -0
- package/dist/esm/decorators/fetch.js.map +7 -0
- package/dist/esm/decorators/index.js +8 -2
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/intersectionObserver.js +76 -65
- package/dist/esm/decorators/intersectionObserver.js.map +2 -2
- package/dist/esm/decorators/lifecycles.js +11 -11
- package/dist/esm/decorators/lifecycles.js.map +2 -2
- package/dist/esm/decorators/mutationObserver.js +97 -90
- package/dist/esm/decorators/mutationObserver.js.map +3 -3
- package/dist/esm/decorators/parameter.js +137 -0
- package/dist/esm/decorators/parameter.js.map +7 -0
- package/dist/esm/decorators/publishSwcAppMessage.js +2 -2
- package/dist/esm/decorators/publishSwcAppMessage.js.map +2 -2
- package/dist/esm/decorators/query.js +11 -5
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/requestAnimationFrame.js +96 -0
- package/dist/esm/decorators/requestAnimationFrame.js.map +7 -0
- package/dist/esm/decorators/resizeObserver.js +84 -69
- package/dist/esm/decorators/resizeObserver.js.map +2 -2
- package/dist/esm/decorators/setInterval.js +65 -23
- package/dist/esm/decorators/setInterval.js.map +2 -2
- package/dist/esm/decorators/setTimeout.js +68 -23
- package/dist/esm/decorators/setTimeout.js.map +2 -2
- package/dist/esm/decorators/state.js +13 -10
- package/dist/esm/decorators/state.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppMessage.js +53 -0
- package/dist/esm/decorators/subscribeSwcAppMessage.js.map +7 -0
- package/dist/esm/decorators/{subscribeSwcAppRouteChangeWhileConnected.js → subscribeSwcAppRouteChange.js} +20 -16
- package/dist/esm/decorators/subscribeSwcAppRouteChange.js.map +7 -0
- package/dist/esm/decorators/swcAppRoute.js +130 -0
- package/dist/esm/decorators/swcAppRoute.js.map +7 -0
- package/dist/esm/elements/SwcApp.js +70 -0
- package/dist/esm/elements/SwcApp.js.map +2 -2
- package/dist/esm/elements/SwcAppMixin.js +258 -69
- package/dist/esm/elements/SwcAppMixin.js.map +3 -3
- package/dist/esm/types/index.js +5 -1
- package/dist/esm/types/index.js.map +2 -2
- package/dist/esm/utils/Utils.js +36 -15
- package/dist/esm/utils/Utils.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +4395 -1266
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/SwcAppEngine.d.ts +1 -0
- package/dist/types/SwcAppEngine.d.ts.map +1 -1
- package/dist/types/decorators/addEventListener.d.ts +936 -11
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +27 -37
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts +2 -2
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +16 -16
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/applyProperty.d.ts +28 -44
- package/dist/types/decorators/applyProperty.d.ts.map +1 -1
- package/dist/types/decorators/applySlot.d.ts.map +1 -1
- package/dist/types/decorators/applyStyle.d.ts +2 -2
- package/dist/types/decorators/applyStyle.d.ts.map +1 -1
- package/dist/types/decorators/around.d.ts +87 -0
- package/dist/types/decorators/around.d.ts.map +1 -0
- package/dist/types/decorators/changedAttribute.d.ts +22 -4
- package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/emitCustomEvent.d.ts +0 -1
- package/dist/types/decorators/emitCustomEvent.d.ts.map +1 -1
- package/dist/types/decorators/eventMedia.d.ts +48 -0
- package/dist/types/decorators/eventMedia.d.ts.map +1 -0
- package/dist/types/decorators/fetch.d.ts +139 -0
- package/dist/types/decorators/fetch.d.ts.map +1 -0
- package/dist/types/decorators/index.d.ts +8 -2
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/intersectionObserver.d.ts +32 -10
- package/dist/types/decorators/intersectionObserver.d.ts.map +1 -1
- package/dist/types/decorators/lifecycles.d.ts +8 -7
- package/dist/types/decorators/lifecycles.d.ts.map +1 -1
- package/dist/types/decorators/mutationObserver.d.ts +34 -12
- package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
- package/dist/types/decorators/parameter.d.ts +113 -0
- package/dist/types/decorators/parameter.d.ts.map +1 -0
- package/dist/types/decorators/publishSwcAppMessage.d.ts +0 -1
- package/dist/types/decorators/publishSwcAppMessage.d.ts.map +1 -1
- package/dist/types/decorators/query.d.ts +7 -7
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/requestAnimationFrame.d.ts +47 -0
- package/dist/types/decorators/requestAnimationFrame.d.ts.map +1 -0
- package/dist/types/decorators/resizeObserver.d.ts +34 -12
- package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
- package/dist/types/decorators/setInterval.d.ts +27 -5
- package/dist/types/decorators/setInterval.d.ts.map +1 -1
- package/dist/types/decorators/setTimeout.d.ts +27 -5
- package/dist/types/decorators/setTimeout.d.ts.map +1 -1
- package/dist/types/decorators/state.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts +44 -0
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts.map +1 -0
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts +60 -0
- package/dist/types/decorators/subscribeSwcAppRouteChange.d.ts.map +1 -0
- package/dist/types/decorators/swcAppRoute.d.ts +94 -0
- package/dist/types/decorators/swcAppRoute.d.ts.map +1 -0
- package/dist/types/elements/SwcApp.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts +423 -388
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +43 -4
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/utils/Utils.d.ts +15 -5
- package/dist/types/utils/Utils.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +4395 -1266
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +8 -6
- package/src/SwcAppEngine.ts +1 -0
- package/src/decorators/addEventListener.ts +1086 -28
- package/src/decorators/applyAttribute.ts +82 -134
- package/src/decorators/applyClass.ts +14 -10
- package/src/decorators/applyNode.ts +39 -37
- package/src/decorators/applyProperty.ts +72 -100
- package/src/decorators/applySlot.ts +9 -8
- package/src/decorators/applyStyle.ts +14 -10
- package/src/decorators/around.ts +196 -0
- package/src/decorators/changedAttribute.ts +65 -26
- package/src/decorators/elementDefine.ts +298 -220
- package/src/decorators/emitCustomEvent.ts +17 -13
- package/src/decorators/eventMedia.ts +119 -0
- package/src/decorators/fetch.ts +454 -0
- package/src/decorators/index.ts +8 -2
- package/src/decorators/intersectionObserver.ts +108 -78
- package/src/decorators/lifecycles.ts +24 -22
- package/src/decorators/mutationObserver.ts +140 -104
- package/src/decorators/parameter.ts +212 -0
- package/src/decorators/publishSwcAppMessage.ts +3 -4
- package/src/decorators/query.ts +22 -14
- package/src/decorators/requestAnimationFrame.ts +149 -0
- package/src/decorators/resizeObserver.ts +119 -82
- package/src/decorators/setInterval.ts +90 -23
- package/src/decorators/setTimeout.ts +93 -23
- package/src/decorators/state.ts +19 -13
- package/src/decorators/subscribeSwcAppMessage.ts +106 -0
- package/src/decorators/subscribeSwcAppRouteChange.ts +131 -0
- package/src/decorators/swcAppRoute.ts +231 -0
- package/src/elements/SwcApp.ts +40 -10
- package/src/elements/SwcAppMixin.ts +329 -85
- package/src/types/index.ts +38 -5
- package/src/types/window.d.ts +2 -18
- package/src/utils/Utils.ts +50 -18
- package/dist/cjs/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
- package/dist/cjs/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
- package/dist/cjs/examples/RxJSOperatorsExample.js +0 -285
- package/dist/cjs/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js +0 -54
- package/dist/esm/decorators/subscribeSwcAppMessageWhileConnected.js.map +0 -7
- package/dist/esm/decorators/subscribeSwcAppRouteChangeWhileConnected.js.map +0 -7
- package/dist/esm/examples/RxJSOperatorsExample.js +0 -266
- package/dist/esm/examples/RxJSOperatorsExample.js.map +0 -7
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts +0 -23
- package/dist/types/decorators/subscribeSwcAppMessageWhileConnected.d.ts.map +0 -1
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts +0 -31
- package/dist/types/decorators/subscribeSwcAppRouteChangeWhileConnected.d.ts.map +0 -1
- package/dist/types/examples/RxJSOperatorsExample.d.ts +0 -94
- package/dist/types/examples/RxJSOperatorsExample.d.ts.map +0 -1
- package/src/decorators/subscribeSwcAppMessageWhileConnected.ts +0 -91
- package/src/decorators/subscribeSwcAppRouteChangeWhileConnected.ts +0 -107
- package/src/examples/RxJSOperatorsExample.ts +0 -279
|
@@ -55,7 +55,7 @@ export const resolvePropertyTargets = (inst: any, selector: PropertySelector, op
|
|
|
55
55
|
// Resolve selector if it's a function
|
|
56
56
|
let resolvedSelector: string | Node | Element | NodeList | Element[] | null = selector as any;
|
|
57
57
|
if (typeof selector === 'function') {
|
|
58
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
58
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
59
59
|
resolvedSelector = selector(inst, hostSet);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -182,9 +182,7 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
182
182
|
if (resolvedValue === undefined) return resolvedValue;
|
|
183
183
|
|
|
184
184
|
const targetEls = resolvePropertyTargets(inst, selector, finalOptions);
|
|
185
|
-
const
|
|
186
|
-
const currentWin = conf.window;
|
|
187
|
-
const hostSet = SwcUtils.getHelperAndHostSet(currentWin, inst);
|
|
185
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
188
186
|
|
|
189
187
|
targetEls.forEach(targetEl => {
|
|
190
188
|
// Apply filter if provided
|
|
@@ -219,7 +217,7 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
219
217
|
* use that value. Otherwise use the entire return value.
|
|
220
218
|
*
|
|
221
219
|
* Example:
|
|
222
|
-
* @
|
|
220
|
+
* @property('selector')
|
|
223
221
|
* myMethod() {
|
|
224
222
|
* return {
|
|
225
223
|
* [PROPERTY_METADATA_KEY]: 'property-value',
|
|
@@ -251,7 +249,13 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
251
249
|
return;
|
|
252
250
|
}
|
|
253
251
|
|
|
254
|
-
// Property decorator: define getter/setter to proxy to element properties
|
|
252
|
+
// Property decorator: define getter/setter to proxy to element properties.
|
|
253
|
+
// selector가 '$this' + 타겟 키가 자기 필드면 getter/setter 만들지 않고 리턴.
|
|
254
|
+
// (메타데이터는 위에서 이미 등록됨 → 하이드레이션 수집은 됨.)
|
|
255
|
+
// 순수 필드로 두는 게 업그레이드 때 값 유지에 안전 (setter 경로 타면 생성자·폴리필에 날아감).
|
|
256
|
+
if (selector === '$this' && targetPropertyKey === propertyKey) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
255
259
|
Object.defineProperty(target, propertyKey, {
|
|
256
260
|
configurable: true,
|
|
257
261
|
enumerable: true,
|
|
@@ -259,9 +263,7 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
259
263
|
ensureInit(this);
|
|
260
264
|
// Read from matched elements
|
|
261
265
|
const targetEls = resolvePropertyTargets(this, selector, finalOptions);
|
|
262
|
-
const
|
|
263
|
-
const currentWin = conf.window;
|
|
264
|
-
const hostSet = SwcUtils.getHelperAndHostSet(currentWin, this);
|
|
266
|
+
const hostSet = SwcUtils.getHelperAndHostSet(this);
|
|
265
267
|
|
|
266
268
|
// Apply filter
|
|
267
269
|
const filtered = targetEls.filter(el => {
|
|
@@ -292,76 +294,42 @@ export function applyProperty(selector: PropertySelector, targetPropertyKeyOrOpt
|
|
|
292
294
|
// ============================================
|
|
293
295
|
|
|
294
296
|
/**
|
|
295
|
-
* @property -
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* - @property
|
|
301
|
-
* - @property(
|
|
302
|
-
* - @property
|
|
303
|
-
*
|
|
304
|
-
*
|
|
297
|
+
* @property - 필드/메서드 공용. 붙은 곳에 따라 동작이 갈린다.
|
|
298
|
+
* - 필드: 대상 엘리먼트 프로퍼티를 읽고 쓰는 getter/setter ($this + 같은 이름이면 순수 필드로 두고 하이드레이션만 등록)
|
|
299
|
+
* - 메서드: 리턴값을 대상 엘리먼트 프로퍼티에 대입 (undefined 면 무시)
|
|
300
|
+
*
|
|
301
|
+
* 첫 문자열은 항상 셀렉터. 자기 자신의 프로퍼티는 필드 그 자체라 이름만 받는 형태는 의미가 없다.
|
|
302
|
+
* - @property - $this, 이름은 필드/메서드 이름
|
|
303
|
+
* - @property('selector') / @property('selector', options) - 대상의 같은 이름 프로퍼티
|
|
304
|
+
* - @property('selector', 'propertyName', options?) - 대상의 propertyName
|
|
305
|
+
* - @property((this, helper) => el, 'propertyName') - 함수 셀렉터
|
|
306
|
+
* - @property(options) - $this, 이름은 options.name 또는 필드/메서드 이름
|
|
307
|
+
*
|
|
308
|
+
* Example:
|
|
309
|
+
* @property('#chart', 'data') chartData: number[];
|
|
310
|
+
*
|
|
311
|
+
* @property('button', 'disabled')
|
|
312
|
+
* lock() { return true; }
|
|
305
313
|
*/
|
|
306
|
-
export function property(selector: string, targetPropertyKey: string | symbol, options?: PropertyQueryOptions): PropertyDecorator;
|
|
307
|
-
export function property(selector: PropertyFnSelector, targetPropertyKey: string | symbol, options?: PropertyNonQueryOptions): PropertyDecorator;
|
|
308
|
-
export function property(selector: string, options?: PropertyQueryOptions): PropertyDecorator;
|
|
309
|
-
export function property(selector: PropertyFnSelector, options?: PropertyNonQueryOptions): PropertyDecorator;
|
|
314
|
+
export function property(selector: string, targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator & PropertyDecorator;
|
|
315
|
+
export function property(selector: PropertyFnSelector, targetPropertyKey: string | symbol, options?: PropertyNonQueryOptions): MethodDecorator & PropertyDecorator;
|
|
316
|
+
export function property(selector: string, options?: PropertyQueryOptions): MethodDecorator & PropertyDecorator;
|
|
317
|
+
export function property(selector: PropertyFnSelector, options?: PropertyNonQueryOptions): MethodDecorator & PropertyDecorator;
|
|
318
|
+
export function property(options: PropertyOptions): MethodDecorator & PropertyDecorator;
|
|
319
|
+
// bare(@property) 시그니처는 맨 뒤 — 앞에 있으면 문자열 두 개짜리 호출이 (target, key) 로 먼저 잡혀 TS1240
|
|
310
320
|
export function property(target: Object, propertyKey: string | symbol): void;
|
|
311
321
|
export function property(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
312
|
-
export function property(selectorOrTarget?: PropertySelector | Object, targetPropertyKeyOrOptions?: any, optionsOrDescriptor?: any): any {
|
|
313
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
&& typeof selectorOrTarget !== 'string' && typeof selectorOrTarget !== 'function'
|
|
317
|
-
&& (typeof targetPropertyKeyOrOptions === 'string' || typeof targetPropertyKeyOrOptions === 'symbol')) {
|
|
318
|
-
return applyProperty('$this', undefined as any, {})(selectorOrTarget as Object, targetPropertyKeyOrOptions, optionsOrDescriptor as PropertyDescriptor);
|
|
319
|
-
}
|
|
320
|
-
// With selector
|
|
321
|
-
if (typeof selectorOrTarget === 'string') {
|
|
322
|
-
return applyProperty(selectorOrTarget, targetPropertyKeyOrOptions as any, optionsOrDescriptor as PropertyOptions);
|
|
323
|
-
}
|
|
324
|
-
// Function selector
|
|
325
|
-
if (typeof selectorOrTarget === 'function') {
|
|
326
|
-
return applyProperty(selectorOrTarget as PropertyFnSelector, targetPropertyKeyOrOptions as any, optionsOrDescriptor as PropertyOptions);
|
|
327
|
-
}
|
|
328
|
-
// Without selector (defaults to $this)
|
|
329
|
-
return applyProperty('$this', undefined as any, selectorOrTarget as PropertyOptions);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* @setProperty - Method decorator for setting element properties from method return value
|
|
334
|
-
*
|
|
335
|
-
* Usage:
|
|
336
|
-
* - @setProperty('selector', 'propertyName') - Set specific property
|
|
337
|
-
* - @setProperty('selector') - Use method name as property name
|
|
338
|
-
* - @setProperty('selector', options) - Set with options
|
|
339
|
-
* - @setProperty((this, helper) => 'selector', 'propertyName') - Function-based selector
|
|
340
|
-
* - @setProperty - Bare decorator (sets on $this with method name)
|
|
341
|
-
*
|
|
342
|
-
* Note: This decorator is for METHODS ONLY. For fields, use @property.
|
|
343
|
-
*
|
|
344
|
-
* Example:
|
|
345
|
-
* @setProperty('selector', 'disabled')
|
|
346
|
-
* disableElement() {
|
|
347
|
-
* return true;
|
|
348
|
-
* }
|
|
349
|
-
*/
|
|
350
|
-
export function setProperty(selector: string, targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
351
|
-
export function setProperty(selector: PropertyFnSelector, targetPropertyKey: string | symbol, options?: PropertyNonQueryOptions): MethodDecorator;
|
|
352
|
-
export function setProperty(selector: string, options?: PropertyQueryOptions): MethodDecorator;
|
|
353
|
-
export function setProperty(selector: PropertyFnSelector, options?: PropertyNonQueryOptions): MethodDecorator;
|
|
354
|
-
export function setProperty(selectorOrOptions?: PropertySelector | PropertyOptions, targetPropertyKeyOrOptions?: any, optionsOrUndefined?: PropertyOptions): MethodDecorator {
|
|
355
|
-
// With selector
|
|
356
|
-
if (typeof selectorOrOptions === 'string') {
|
|
357
|
-
return applyProperty(selectorOrOptions, targetPropertyKeyOrOptions as any, optionsOrUndefined as PropertyOptions) as MethodDecorator;
|
|
322
|
+
export function property(selectorOrTarget?: PropertySelector | PropertyOptions | Object, targetPropertyKeyOrOptions?: any, optionsOrDescriptor?: any): any {
|
|
323
|
+
// 셀렉터 (문자열 / 함수)
|
|
324
|
+
if (typeof selectorOrTarget === 'string' || typeof selectorOrTarget === 'function') {
|
|
325
|
+
return applyProperty(selectorOrTarget as PropertySelector, targetPropertyKeyOrOptions as any, optionsOrDescriptor as PropertyOptions);
|
|
358
326
|
}
|
|
359
|
-
//
|
|
360
|
-
if (typeof
|
|
361
|
-
return applyProperty(
|
|
327
|
+
// bare: @property — 데코레이터로 바로 호출됨 (target=prototype, key[, descriptor])
|
|
328
|
+
if (selectorOrTarget && (typeof targetPropertyKeyOrOptions === 'string' || typeof targetPropertyKeyOrOptions === 'symbol')) {
|
|
329
|
+
return applyProperty('$this', {})(selectorOrTarget as Object, targetPropertyKeyOrOptions, optionsOrDescriptor as PropertyDescriptor);
|
|
362
330
|
}
|
|
363
|
-
//
|
|
364
|
-
return applyProperty('$this',
|
|
331
|
+
// 옵션만: $this
|
|
332
|
+
return applyProperty('$this', selectorOrTarget as PropertyOptions);
|
|
365
333
|
}
|
|
366
334
|
|
|
367
335
|
/**
|
|
@@ -415,7 +383,7 @@ export function callPropertyAll(selector: string, targetMethodKey?: string | sym
|
|
|
415
383
|
// ============================================
|
|
416
384
|
|
|
417
385
|
export const findAllPropertyMetadata = (target: any): PropertyMetadata[] => {
|
|
418
|
-
const actualTarget = target
|
|
386
|
+
const actualTarget = typeof target === 'function' ? target : target.constructor;
|
|
419
387
|
return ReflectUtils.findAllMetadata<PropertyMetadata[]>(PROPERTY_METADATA_KEY, actualTarget).flat();
|
|
420
388
|
};
|
|
421
389
|
|
|
@@ -429,22 +397,37 @@ export const findAllPropertyApplyMetadata = (target: any): Map<string | symbol,
|
|
|
429
397
|
};
|
|
430
398
|
|
|
431
399
|
// ─── Aliases ───
|
|
432
|
-
//
|
|
400
|
+
// applyProperty 단축명
|
|
433
401
|
export const prop = applyProperty;
|
|
434
402
|
|
|
435
403
|
// ─── 편의 헬퍼 (selector/root 생략) ───
|
|
436
404
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
405
|
+
const dispatchPropConvenience = (
|
|
406
|
+
selector: '$this' | '$appHost' | '$window' | '$document',
|
|
407
|
+
targetPropertyKeyOrTarget?: string | symbol | Object,
|
|
408
|
+
optionsOrPropertyKey?: PropertyQueryOptions | string | symbol,
|
|
409
|
+
descriptor?: PropertyDescriptor
|
|
410
|
+
): any => {
|
|
411
|
+
if ((typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol') && descriptor !== undefined) {
|
|
412
|
+
return (applyProperty(selector, undefined as any, {}) as any)(targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
413
|
+
}
|
|
414
|
+
return applyProperty(selector, targetPropertyKeyOrTarget as any, optionsOrPropertyKey as PropertyQueryOptions);
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
export function propAppHost(targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
418
|
+
export function propAppHost(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
419
|
+
export function propAppHost(targetPropertyKeyOrTarget?: string | symbol | Object, optionsOrPropertyKey?: PropertyQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
420
|
+
return dispatchPropConvenience('$appHost', targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
442
421
|
}
|
|
443
|
-
export function propWindow(targetPropertyKey
|
|
444
|
-
|
|
422
|
+
export function propWindow(targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
423
|
+
export function propWindow(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
424
|
+
export function propWindow(targetPropertyKeyOrTarget?: string | symbol | Object, optionsOrPropertyKey?: PropertyQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
425
|
+
return dispatchPropConvenience('$window', targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
445
426
|
}
|
|
446
|
-
export function propDocument(targetPropertyKey
|
|
447
|
-
|
|
427
|
+
export function propDocument(targetPropertyKey: string | symbol, options?: PropertyQueryOptions): MethodDecorator;
|
|
428
|
+
export function propDocument(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
429
|
+
export function propDocument(targetPropertyKeyOrTarget?: string | symbol | Object, optionsOrPropertyKey?: PropertyQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
430
|
+
return dispatchPropConvenience('$document', targetPropertyKeyOrTarget, optionsOrPropertyKey, descriptor);
|
|
448
431
|
}
|
|
449
432
|
export function propLight(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator {
|
|
450
433
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'light'});
|
|
@@ -456,25 +439,14 @@ export function propAll(selector: string, targetPropertyKey?: string | symbol, o
|
|
|
456
439
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'all'});
|
|
457
440
|
}
|
|
458
441
|
|
|
459
|
-
// ───
|
|
460
|
-
export function
|
|
461
|
-
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'light'});
|
|
462
|
-
}
|
|
463
|
-
export function setPropertyShadow(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator {
|
|
464
|
-
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'shadow'});
|
|
465
|
-
}
|
|
466
|
-
export function setPropertyAll(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator {
|
|
467
|
-
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'all'});
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// ─── property root 별칭 (필드 프록시) ───
|
|
471
|
-
export function propertyLight(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): PropertyDecorator {
|
|
442
|
+
// ─── property root 별칭 ───
|
|
443
|
+
export function propertyLight(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator & PropertyDecorator {
|
|
472
444
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'light'});
|
|
473
445
|
}
|
|
474
|
-
export function propertyShadow(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): PropertyDecorator {
|
|
446
|
+
export function propertyShadow(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator & PropertyDecorator {
|
|
475
447
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'shadow'});
|
|
476
448
|
}
|
|
477
|
-
export function propertyAll(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): PropertyDecorator {
|
|
449
|
+
export function propertyAll(selector: string, targetPropertyKey?: string | symbol, options?: Omit<PropertyQueryOptions, 'root'>): MethodDecorator & PropertyDecorator {
|
|
478
450
|
return applyProperty(selector, targetPropertyKey, {...options ?? {}, root: 'all'});
|
|
479
451
|
}
|
|
480
452
|
|
|
@@ -73,8 +73,8 @@ export const APPLY_SLOT_METADATA_KEY = Symbol.for('simple-web-component:apply-sl
|
|
|
73
73
|
// }
|
|
74
74
|
// };
|
|
75
75
|
|
|
76
|
-
const applyToDom = (swcId: string, id: string, pos: ApplySlotPosition, nodes: Node[]) => {
|
|
77
|
-
const nodeSlot = new NodeSlot(
|
|
76
|
+
const applyToDom = (host: HTMLElement, swcId: string, id: string, pos: ApplySlotPosition, nodes: Node[]) => {
|
|
77
|
+
const nodeSlot = new NodeSlot(host, swcId);
|
|
78
78
|
if (pos === 'replaceChildrenText' || pos === 'replaceChildren' || pos === 'replaceChildrenHtml') {
|
|
79
79
|
nodeSlot.replaceChildren(id, ...nodes);
|
|
80
80
|
} else if (pos === 'appendHtml' || pos === 'append' || pos === 'appendText') {
|
|
@@ -109,11 +109,12 @@ export function applySlot(targetId: string, opt: OptionalType<ApplySlotOptions,
|
|
|
109
109
|
descriptor.value = function (...args: any[]) {
|
|
110
110
|
ensureInit(this);
|
|
111
111
|
|
|
112
|
+
const host = this;
|
|
112
113
|
const conf = getElementConfig(this);
|
|
113
114
|
const currentWin = conf.window;
|
|
114
115
|
const swcId = this._swcId;
|
|
115
116
|
const currentDoc = currentWin.document;
|
|
116
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
117
|
+
const hostSet = SwcUtils.getHelperAndHostSet(this);
|
|
117
118
|
const pos = options.position;
|
|
118
119
|
|
|
119
120
|
const resToNodes = (res: string | Node) => {
|
|
@@ -137,7 +138,7 @@ export function applySlot(targetId: string, opt: OptionalType<ApplySlotOptions,
|
|
|
137
138
|
const res: string | Node = options.fallback.call(this, hostSet);
|
|
138
139
|
const nodes: Node[] = resToNodes(res);
|
|
139
140
|
fallbackNodes.push(...nodes);
|
|
140
|
-
applyToDom(swcId, targetId, options.position, nodes);
|
|
141
|
+
applyToDom(host, swcId, targetId, options.position, nodes);
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
/**
|
|
@@ -162,12 +163,12 @@ export function applySlot(targetId: string, opt: OptionalType<ApplySlotOptions,
|
|
|
162
163
|
const extracted = extractValue(asyncRes);
|
|
163
164
|
const nodes = resToNodes(extracted)
|
|
164
165
|
fallbackNodes.forEach((it: any) => it.remove());
|
|
165
|
-
applyToDom(swcId, targetId, options.position, nodes);
|
|
166
|
+
applyToDom(host, swcId, targetId, options.position, nodes);
|
|
166
167
|
})
|
|
167
168
|
} else {
|
|
168
169
|
const extracted = extractValue(res);
|
|
169
170
|
const nodes: Node[] = resToNodes(extracted);
|
|
170
|
-
applyToDom(swcId, targetId, options.position, nodes);
|
|
171
|
+
applyToDom(host, swcId, targetId, options.position, nodes);
|
|
171
172
|
}
|
|
172
173
|
};
|
|
173
174
|
};
|
|
@@ -215,6 +216,6 @@ export function replaceChildrenTextSlot(targetId: string): MethodDecorator {
|
|
|
215
216
|
|
|
216
217
|
// export const findAllApplySlotMetadata = (target: any): Map<string | symbol, ApplySlotMetadata> => {
|
|
217
218
|
export const findAllApplySlotMetadata = (target: any): ApplySlotMetadata[] => {
|
|
218
|
-
const constructor = target
|
|
219
|
-
return ReflectUtils.
|
|
219
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
220
|
+
return ReflectUtils.findAllMetadata<any[]>(APPLY_SLOT_METADATA_KEY, constructor).flat();
|
|
220
221
|
};
|
|
@@ -39,7 +39,7 @@ export const STYLE_METADATA_KEY = Symbol.for('simple-web-component:style');
|
|
|
39
39
|
*/
|
|
40
40
|
const resolveSelector = (selector: StyleSelector, inst: any, win: Window): string | HTMLElement[] => {
|
|
41
41
|
if (typeof selector === 'function') {
|
|
42
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
42
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
43
43
|
const result = selector(inst, hostSet);
|
|
44
44
|
|
|
45
45
|
if (result instanceof win.Element) {
|
|
@@ -75,7 +75,7 @@ const getTargetElements = (selector: StyleSelector, inst: any, win: Window, root
|
|
|
75
75
|
const stringSelector = resolved as string;
|
|
76
76
|
const targetEls: HTMLElement[] = [];
|
|
77
77
|
const host = inst as unknown as HTMLElement;
|
|
78
|
-
const hostSet = SwcUtils.getHelperAndHostSet(
|
|
78
|
+
const hostSet = SwcUtils.getHelperAndHostSet(inst);
|
|
79
79
|
|
|
80
80
|
const applyRoot = (t: any) => {
|
|
81
81
|
if (!t || !(t instanceof win.HTMLElement)) return;
|
|
@@ -139,7 +139,7 @@ function createStyleDecorator(action: StyleAction) {
|
|
|
139
139
|
if (resolvedValue !== undefined) {
|
|
140
140
|
const win = (this as any).ownerDocument?.defaultView || window;
|
|
141
141
|
const host = this as unknown as HTMLElement;
|
|
142
|
-
const hostSet = { ...SwcUtils.getHelperAndHostSet(
|
|
142
|
+
const hostSet = { ...SwcUtils.getHelperAndHostSet(host), $this: this };
|
|
143
143
|
const r = options.root || 'auto';
|
|
144
144
|
const targetEls = getTargetElements(selector, this, win, r);
|
|
145
145
|
|
|
@@ -183,9 +183,11 @@ function createStyleDecorator(action: StyleAction) {
|
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
if (res instanceof Promise) {
|
|
186
|
-
|
|
186
|
+
// 자기 몫(valueKey)만 적용하고 원래 반환값은 그대로 위로 넘긴다 — 위에 쌓인 다른 아웃풋도 자기 키를 찾게
|
|
187
|
+
return res.then((v: any) => { handleResult(extractValue(v)); return v; });
|
|
187
188
|
} else {
|
|
188
|
-
|
|
189
|
+
handleResult(extractValue(res));
|
|
190
|
+
return res;
|
|
189
191
|
}
|
|
190
192
|
};
|
|
191
193
|
return descriptor;
|
|
@@ -266,11 +268,13 @@ export const style = applyStyle;
|
|
|
266
268
|
|
|
267
269
|
// ─── 편의 헬퍼 (selector/root 생략) ───
|
|
268
270
|
|
|
269
|
-
export function
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
export function styleAppHost(action: StyleAction, options?: StyleQueryOptions): MethodDecorator;
|
|
272
|
+
export function styleAppHost(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor | void;
|
|
273
|
+
export function styleAppHost(actionOrTarget?: StyleAction | Object, optionsOrPropertyKey?: StyleQueryOptions | string | symbol, descriptor?: PropertyDescriptor): any {
|
|
274
|
+
if ((typeof optionsOrPropertyKey === 'string' || typeof optionsOrPropertyKey === 'symbol') && descriptor !== undefined) {
|
|
275
|
+
return (applyStyle('$appHost', undefined as any, {}) as any)(actionOrTarget, optionsOrPropertyKey, descriptor);
|
|
276
|
+
}
|
|
277
|
+
return applyStyle('$appHost', actionOrTarget as any, optionsOrPropertyKey as StyleQueryOptions);
|
|
274
278
|
}
|
|
275
279
|
export function styleLight(selector: string, action?: StyleAction, options?: Omit<StyleQueryOptions, 'root'>): MethodDecorator;
|
|
276
280
|
export function styleLight(selector: string, options?: Omit<StyleQueryOptions, 'root'>): MethodDecorator;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ReflectUtils } from '@dooboostore/core';
|
|
2
|
+
import { ensureInit } from './elementDefine';
|
|
3
|
+
import { SwcUtils } from '../utils/Utils';
|
|
4
|
+
import { HelperHostSet } from '../types';
|
|
5
|
+
|
|
6
|
+
export const AROUND_METADATA_KEY = Symbol.for('simple-web-component:around');
|
|
7
|
+
|
|
8
|
+
export interface AroundMetadata {
|
|
9
|
+
propertyKey: string | symbol;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AroundPropertyOptions<Value = any> {
|
|
13
|
+
/**
|
|
14
|
+
* get할 때 (helperHostSet, 저장된 값)을 받아 실제로 읽는 쪽에 돌려줄 값을 리턴한다.
|
|
15
|
+
* undefined 리턴이면 저장값 그대로 통과. 안 주면 그대로 통과.
|
|
16
|
+
*/
|
|
17
|
+
get?: (helperHostSet: HelperHostSet, stored: Value) => Value | void;
|
|
18
|
+
/**
|
|
19
|
+
* set할 때 (helperHostSet, 대입되는 값)을 받아 실제로 저장할 값을 리턴한다.
|
|
20
|
+
* 안 주면 그대로 통과.
|
|
21
|
+
*/
|
|
22
|
+
set?: (helperHostSet: HelperHostSet, incoming: Value) => Value | void;
|
|
23
|
+
}
|
|
24
|
+
export interface AroundOptions<Args extends any[] = any[], Return = any> {
|
|
25
|
+
/**
|
|
26
|
+
* 원본 실행 전에, (helperHostSet, 호출자가 넘긴 인자 배열)을 받아
|
|
27
|
+
* 실제로 원본에 넘길 인자 배열을 리턴한다. undefined 리턴이면 인자 그대로 통과.
|
|
28
|
+
* 원본이 Promise를 리턴하면 before도 Promise를 리턴할 수 있다 —
|
|
29
|
+
* resolve된 값으로 원본이 호출된다 (이 경우 래퍼도 Promise 반환).
|
|
30
|
+
* 원본이 sync면 before도 sync여야 한다 (async를 달면 호출 측이 모르는 Promise가 새어나감).
|
|
31
|
+
* 안 주면 그대로 통과.
|
|
32
|
+
*
|
|
33
|
+
* 제네릭 생략 시(Return=any) 양쪽 다 허용이라 느슨하게 동작하고,
|
|
34
|
+
* `@around<[number], number>`처럼 명시하면 위 규칙이 검사된다.
|
|
35
|
+
*/
|
|
36
|
+
before?: (helperHostSet: HelperHostSet, args: Args) => Return extends Promise<any> ? Args | void | Promise<Args | void> : Args | void;
|
|
37
|
+
/**
|
|
38
|
+
* 원본 실행 후에, (helperHostSet, 리턴값)을 받아 실제로 호출자에게
|
|
39
|
+
* 돌려줄 값을 리턴한다.
|
|
40
|
+
* 원본이 Promise를 리턴하면 resolve된 값(Awaited<Return>)을 넘겨받고,
|
|
41
|
+
* after의 리턴값이 그대로 최종 Promise의 resolve 값이 된다.
|
|
42
|
+
* after가 Promise를 리턴해도 된다 — resolve된 값이 최종값이 된다.
|
|
43
|
+
* 안 주면 그대로 통과.
|
|
44
|
+
*
|
|
45
|
+
* 제네릭을 명시하면 훅 입출력이 검사된다:
|
|
46
|
+
* `@around<[string], Promise<string>>({ after: (h, r) => ... })`
|
|
47
|
+
* (r은 string으로 추론). 생략하면 any로 느슨하게 동작한다.
|
|
48
|
+
* 단, legacy 데코레이터 한계상 래핑된 메서드의 겉보기 시그니처는 안 바뀌므로
|
|
49
|
+
* async 훅을 단 sync 메서드는 호출 측에서 Promise로 취급해야 한다.
|
|
50
|
+
*/
|
|
51
|
+
after?: (helperHostSet: HelperHostSet, result: Awaited<Return>) => Return | Promise<Awaited<Return>>;
|
|
52
|
+
/**
|
|
53
|
+
* before/원본/after 실행이 끝나면 성공·실패와 무관하게 항상 실행되는 정리 훅 (try/finally의 finally).
|
|
54
|
+
* 두 번째 인자로 컨텍스트를 받는다:
|
|
55
|
+
* - args: 호출자가 넘긴 원본 인자 배열(before 가공 전)
|
|
56
|
+
* - result: 성공 시 최종 리턴값(after 적용 후). 실패 시 없음.
|
|
57
|
+
* - error: throw/reject 시 그 에러. 성공 시 없음.
|
|
58
|
+
* 에러를 삼키지 않는다 — finally를 먼저 돌린 뒤 원래 에러/리턴값이 그대로 전파된다.
|
|
59
|
+
* sync 원본 + sync finally면 sync로 유지되지만, finally가 Promise를 리턴하면
|
|
60
|
+
* 래퍼는 Promise를 반환한다(async before/after와 동일 규칙).
|
|
61
|
+
*/
|
|
62
|
+
finally?: (helperHostSet: HelperHostSet, ctx: { args: Args; result?: Awaited<Return>; error?: any }) => void | Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 인스턴스에서 HelperHostSet을 해석한다 — 실패(SSR/미초기화)하면 최소 폴백을 돌려준다. */
|
|
66
|
+
const resolveHelperHostSet = (inst: any): HelperHostSet => {
|
|
67
|
+
try {
|
|
68
|
+
ensureInit(inst);
|
|
69
|
+
return SwcUtils.getHelperAndHostSet(inst);
|
|
70
|
+
} catch {
|
|
71
|
+
return { $this: inst } as HelperHostSet;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 메서드에 걸면 호출을 감싸서 호출 전(인자 가공)/후(리턴값 가공) 훅을 건다 - AOP의 "around advice".
|
|
77
|
+
*
|
|
78
|
+
* @around({
|
|
79
|
+
* before: (h, args: [number, string]) => [args[0], args[1].toUpperCase()],
|
|
80
|
+
* after: (h, r: string) => r + '!'
|
|
81
|
+
* })
|
|
82
|
+
* greet(a: number, b: string) { return `${a}:${b}`; }
|
|
83
|
+
*
|
|
84
|
+
* 프로퍼티(필드)에 걸면 get/set을 가로챈다 - get은 get, set은 set이 호출된다.
|
|
85
|
+
* (프로퍼티 경로에서는 값이 배열이 아니라 단일값으로 넘어간다)
|
|
86
|
+
* 필드 이니셜라이저(`wow = 'x'`)가 있어도 된다 — ensureInit이 state처럼
|
|
87
|
+
* 초기값을 살려 setter 경유로 정리하므로 초기값에도 set()이 적용된다.
|
|
88
|
+
* (단, custom element가 아닌 일반 클래스에서는 ensureInit이 안 돌므로
|
|
89
|
+
* `declare` + 생성자 대입으로 쓸 것)
|
|
90
|
+
*
|
|
91
|
+
* @around({
|
|
92
|
+
* get: (h, stored: string) => stored?.toUpperCase(),
|
|
93
|
+
* set: (h, incoming: string) => incoming?.trim()
|
|
94
|
+
* })
|
|
95
|
+
* wow: string = ' hi ';
|
|
96
|
+
*/
|
|
97
|
+
export function around<Args extends any[] = any[], Return = any>(options: AroundOptions<NoInfer<Args>, NoInfer<Return>>): MethodDecorator;
|
|
98
|
+
export function around<Value = any>(options: AroundPropertyOptions<NoInfer<Value>>): PropertyDecorator;
|
|
99
|
+
export function around(options: any = {}): MethodDecorator & PropertyDecorator {
|
|
100
|
+
return ((target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
101
|
+
// 메서드: descriptor.value가 실제 함수일 때만 (이미 정의된 접근자를 감싸는 건 지원 범위 밖)
|
|
102
|
+
if (descriptor && typeof descriptor.value === 'function') {
|
|
103
|
+
const original = descriptor.value;
|
|
104
|
+
const runAfter = function (this: any, hhs: HelperHostSet, res: any) {
|
|
105
|
+
if (!options.after) return res;
|
|
106
|
+
const run = (v: any) => (options.after as any).call(this, hhs, v);
|
|
107
|
+
// after가 Promise를 리턴하면 resolve된 값이 최종값이 된다 (then 플래트닝).
|
|
108
|
+
return res && typeof res.then === 'function' ? res.then(run) : run(res);
|
|
109
|
+
};
|
|
110
|
+
descriptor.value = function (this: any, ...args: any[]) {
|
|
111
|
+
const hhs = resolveHelperHostSet(this);
|
|
112
|
+
const self = this;
|
|
113
|
+
const callOriginal = (finalArgs: any[]) => runAfter.call(self, hhs, original.apply(self, finalArgs));
|
|
114
|
+
const runBody = () => {
|
|
115
|
+
if (options.before) {
|
|
116
|
+
const mapped = (options.before as any).call(self, hhs, args);
|
|
117
|
+
if (mapped && typeof mapped.then === 'function') {
|
|
118
|
+
// async before — resolve된 인자로 원본 호출, 래퍼는 Promise 반환.
|
|
119
|
+
// (sync 원본 + sync 훅일 때만 sync 반환이 유지된다)
|
|
120
|
+
return mapped.then((m: any) => {
|
|
121
|
+
if (Array.isArray(m)) return callOriginal(m);
|
|
122
|
+
if (m !== undefined) return callOriginal([m]);
|
|
123
|
+
return callOriginal(args);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(mapped)) return callOriginal(mapped);
|
|
127
|
+
if (mapped !== undefined) return callOriginal([mapped]);
|
|
128
|
+
}
|
|
129
|
+
return callOriginal(args);
|
|
130
|
+
};
|
|
131
|
+
if (!options.finally) return runBody();
|
|
132
|
+
// sync/async 투명성을 지키는 try/finally — 에러는 삼키지 않고 finally 후 전파.
|
|
133
|
+
const afterFin = (settle: () => any, ctx: { args: any[]; result?: any; error?: any }) => {
|
|
134
|
+
const f = (options.finally as any).call(self, hhs, ctx);
|
|
135
|
+
return f && typeof f.then === 'function' ? f.then(settle) : settle();
|
|
136
|
+
};
|
|
137
|
+
let out: any;
|
|
138
|
+
try {
|
|
139
|
+
out = runBody();
|
|
140
|
+
} catch (e) {
|
|
141
|
+
return afterFin(() => { throw e; }, { args, error: e });
|
|
142
|
+
}
|
|
143
|
+
if (out && typeof out.then === 'function') {
|
|
144
|
+
return out.then(
|
|
145
|
+
(v: any) => afterFin(() => v, { args, result: v }),
|
|
146
|
+
(e: any) => afterFin(() => { throw e; }, { args, error: e })
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return afterFin(() => out, { args, result: out });
|
|
150
|
+
};
|
|
151
|
+
return descriptor;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 프로퍼티: ensureInit에서 own property를 정리할 수 있도록 메타데이터 등록 (state.ts와 동일 패턴).
|
|
155
|
+
// 필드 이니셜라이저가 있으면 own property가 accessor를 가리므로 getter가 영원히 안 불림 —
|
|
156
|
+
// getter 안의 자체이주는 마지막 안전망일 뿐, 실질적인 살림은 ensureInit이 한다.
|
|
157
|
+
const constructor = (target as any).constructor;
|
|
158
|
+
let aroundList = ReflectUtils.getOwnMetadata(AROUND_METADATA_KEY, constructor) as AroundMetadata[] | undefined;
|
|
159
|
+
if (!aroundList) {
|
|
160
|
+
aroundList = [];
|
|
161
|
+
ReflectUtils.defineMetadata(AROUND_METADATA_KEY, aroundList, constructor);
|
|
162
|
+
}
|
|
163
|
+
aroundList.push({ propertyKey });
|
|
164
|
+
|
|
165
|
+
// target은 prototype이라 여기 값을 직접 두면 모든 인스턴스가 공유해버린다.
|
|
166
|
+
// 인스턴스별로 따로 저장되도록 인스턴스 전용 Symbol 슬롯(this[storageKey])에 실제 값을 둔다.
|
|
167
|
+
const storageKey = Symbol(`around:${String(propertyKey)}`);
|
|
168
|
+
const getFn = (options as AroundPropertyOptions).get as unknown as ((hhs: HelperHostSet, value: any) => any) | undefined;
|
|
169
|
+
const setFn = (options as AroundPropertyOptions).set as unknown as ((hhs: HelperHostSet, value: any) => any) | undefined;
|
|
170
|
+
Object.defineProperty(target, propertyKey, {
|
|
171
|
+
get(this: any) {
|
|
172
|
+
// 안전망: 누군가 plain delete로 own을 걷어낸 뒤라면 여기서 슬롯으로 회수한다.
|
|
173
|
+
// (정상 경로에서는 ensureInit이 이미 setter 경유로 정리했으므로 own이 없음)
|
|
174
|
+
const own = Object.getOwnPropertyDescriptor(this, propertyKey);
|
|
175
|
+
if (own && 'value' in own) {
|
|
176
|
+
delete this[propertyKey];
|
|
177
|
+
this[storageKey] = setFn ? setFn.call(this, resolveHelperHostSet(this), own.value) : own.value;
|
|
178
|
+
}
|
|
179
|
+
const raw = this[storageKey];
|
|
180
|
+
if (!getFn) return raw;
|
|
181
|
+
const mapped = getFn.call(this, resolveHelperHostSet(this), raw);
|
|
182
|
+
return mapped === undefined ? raw : mapped;
|
|
183
|
+
},
|
|
184
|
+
set(this: any, value: any) {
|
|
185
|
+
this[storageKey] = setFn ? setFn.call(this, resolveHelperHostSet(this), value) : value;
|
|
186
|
+
},
|
|
187
|
+
enumerable: true,
|
|
188
|
+
configurable: true
|
|
189
|
+
});
|
|
190
|
+
}) as MethodDecorator & PropertyDecorator;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const findAllAroundMetadata = (target: any): AroundMetadata[] => {
|
|
194
|
+
const constructor = typeof target === 'function' ? target : target.constructor;
|
|
195
|
+
return ReflectUtils.findAllMetadata<any[]>(AROUND_METADATA_KEY, constructor).flat();
|
|
196
|
+
};
|