@dooboostore/simple-web-component 1.0.53 → 1.0.55
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 +308 -56
- package/dist/cjs/decorators/addEventListener.js +37 -14
- package/dist/cjs/decorators/addEventListener.js.map +2 -2
- package/dist/cjs/decorators/applyAttribute.js +6 -3
- package/dist/cjs/decorators/applyAttribute.js.map +2 -2
- package/dist/cjs/decorators/applyClass.js +18 -2
- package/dist/cjs/decorators/applyClass.js.map +2 -2
- package/dist/cjs/decorators/applyNode.js +24 -2
- package/dist/cjs/decorators/applyNode.js.map +2 -2
- package/dist/cjs/decorators/changedAttribute.js +2 -5
- package/dist/cjs/decorators/changedAttribute.js.map +2 -2
- package/dist/cjs/decorators/elementDefine.js +13 -2
- package/dist/cjs/decorators/elementDefine.js.map +2 -2
- package/dist/cjs/decorators/eventMedia.js +2 -5
- package/dist/cjs/decorators/eventMedia.js.map +2 -2
- package/dist/cjs/decorators/fetch.js +216 -82
- package/dist/cjs/decorators/fetch.js.map +3 -3
- package/dist/cjs/decorators/index.js +1 -0
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/intersectionObserver.js +3 -6
- package/dist/cjs/decorators/intersectionObserver.js.map +2 -2
- package/dist/cjs/decorators/mutationObserver.js +3 -6
- package/dist/cjs/decorators/mutationObserver.js.map +2 -2
- package/dist/cjs/decorators/parameter.js +197 -33
- package/dist/cjs/decorators/parameter.js.map +3 -3
- package/dist/cjs/decorators/persistState.js +474 -0
- package/dist/cjs/decorators/persistState.js.map +7 -0
- package/dist/cjs/decorators/resizeObserver.js +3 -6
- package/dist/cjs/decorators/resizeObserver.js.map +2 -2
- package/dist/cjs/decorators/setInterval.js +2 -5
- package/dist/cjs/decorators/setInterval.js.map +2 -2
- package/dist/cjs/decorators/setTimeout.js +2 -5
- package/dist/cjs/decorators/setTimeout.js.map +2 -2
- package/dist/cjs/decorators/subscribeSwcAppMessage.js.map +1 -1
- package/dist/cjs/elements/SwcAppMixin.js +50 -2
- package/dist/cjs/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm/decorators/addEventListener.js +38 -15
- package/dist/esm/decorators/addEventListener.js.map +2 -2
- package/dist/esm/decorators/applyAttribute.js +6 -3
- package/dist/esm/decorators/applyAttribute.js.map +2 -2
- package/dist/esm/decorators/applyClass.js +18 -2
- package/dist/esm/decorators/applyClass.js.map +2 -2
- package/dist/esm/decorators/applyNode.js +24 -2
- package/dist/esm/decorators/applyNode.js.map +2 -2
- package/dist/esm/decorators/changedAttribute.js +3 -6
- package/dist/esm/decorators/changedAttribute.js.map +2 -2
- package/dist/esm/decorators/elementDefine.js +15 -4
- package/dist/esm/decorators/elementDefine.js.map +2 -2
- package/dist/esm/decorators/eventMedia.js +3 -6
- package/dist/esm/decorators/eventMedia.js.map +2 -2
- package/dist/esm/decorators/fetch.js +216 -82
- package/dist/esm/decorators/fetch.js.map +3 -3
- package/dist/esm/decorators/index.js +1 -0
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/intersectionObserver.js +4 -7
- package/dist/esm/decorators/intersectionObserver.js.map +2 -2
- package/dist/esm/decorators/mutationObserver.js +4 -7
- package/dist/esm/decorators/mutationObserver.js.map +2 -2
- package/dist/esm/decorators/parameter.js +197 -33
- package/dist/esm/decorators/parameter.js.map +3 -3
- package/dist/esm/decorators/persistState.js +455 -0
- package/dist/esm/decorators/persistState.js.map +7 -0
- package/dist/esm/decorators/resizeObserver.js +4 -7
- package/dist/esm/decorators/resizeObserver.js.map +2 -2
- package/dist/esm/decorators/setInterval.js +3 -6
- package/dist/esm/decorators/setInterval.js.map +2 -2
- package/dist/esm/decorators/setTimeout.js +3 -6
- package/dist/esm/decorators/setTimeout.js.map +2 -2
- package/dist/esm/decorators/subscribeSwcAppMessage.js.map +1 -1
- package/dist/esm/elements/SwcAppMixin.js +51 -3
- package/dist/esm/elements/SwcAppMixin.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +4470 -3620
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +4 -4
- package/dist/types/decorators/addEventListener.d.ts +15 -0
- package/dist/types/decorators/addEventListener.d.ts.map +1 -1
- package/dist/types/decorators/applyAttribute.d.ts +1 -0
- package/dist/types/decorators/applyAttribute.d.ts.map +1 -1
- package/dist/types/decorators/applyClass.d.ts.map +1 -1
- package/dist/types/decorators/applyNode.d.ts +14 -0
- package/dist/types/decorators/applyNode.d.ts.map +1 -1
- package/dist/types/decorators/changedAttribute.d.ts.map +1 -1
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/eventMedia.d.ts.map +1 -1
- package/dist/types/decorators/fetch.d.ts +52 -31
- package/dist/types/decorators/fetch.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/intersectionObserver.d.ts.map +1 -1
- package/dist/types/decorators/mutationObserver.d.ts.map +1 -1
- package/dist/types/decorators/parameter.d.ts +169 -31
- package/dist/types/decorators/parameter.d.ts.map +1 -1
- package/dist/types/decorators/persistState.d.ts +66 -0
- package/dist/types/decorators/persistState.d.ts.map +1 -0
- package/dist/types/decorators/resizeObserver.d.ts.map +1 -1
- package/dist/types/decorators/setInterval.d.ts.map +1 -1
- package/dist/types/decorators/setTimeout.d.ts.map +1 -1
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts +1 -1
- package/dist/types/decorators/subscribeSwcAppMessage.d.ts.map +1 -1
- package/dist/types/elements/SwcAppMixin.d.ts +12 -0
- package/dist/types/elements/SwcAppMixin.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +4470 -3620
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +4 -4
- package/package.json +1 -1
- package/src/decorators/addEventListener.ts +23 -15
- package/src/decorators/applyAttribute.ts +8 -3
- package/src/decorators/applyClass.ts +22 -2
- package/src/decorators/applyNode.ts +44 -2
- package/src/decorators/changedAttribute.ts +3 -4
- package/src/decorators/elementDefine.ts +19 -7
- package/src/decorators/eventMedia.ts +3 -6
- package/src/decorators/fetch.ts +270 -140
- package/src/decorators/index.ts +1 -0
- package/src/decorators/intersectionObserver.ts +4 -5
- package/src/decorators/mutationObserver.ts +4 -5
- package/src/decorators/parameter.ts +326 -42
- package/src/decorators/persistState.ts +408 -0
- package/src/decorators/resizeObserver.ts +4 -5
- package/src/decorators/setInterval.ts +3 -4
- package/src/decorators/setTimeout.ts +3 -4
- package/src/decorators/subscribeSwcAppMessage.ts +1 -1
- package/src/elements/SwcAppMixin.ts +64 -5
package/src/decorators/fetch.ts
CHANGED
|
@@ -25,45 +25,24 @@ export type FetchManualFn = (
|
|
|
25
25
|
returnValue?: any
|
|
26
26
|
) => any | Promise<any>;
|
|
27
27
|
|
|
28
|
-
/**
|
|
28
|
+
/** filter 가 받는 것 */
|
|
29
|
+
export type FetchFilterContext = { self: any; helper: HelperHostSet; args: any[] };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 모드(선언형/수동형, before/after)와 무관한 공통 옵션.
|
|
33
|
+
* 결과 처리(then/catch/finally)는 @fetchPromise 로 받은 Promise 로 사용자가 직접 한다 — 그래서 훅이 없다.
|
|
34
|
+
*/
|
|
29
35
|
export type FetchType = {
|
|
30
36
|
/**
|
|
31
37
|
* 같은 메서드를 다시 부르면 진행 중인 이전 요청을 abort 한다 (검색 자동완성 등 마지막 요청만 살림).
|
|
32
|
-
* 끊긴 이전
|
|
38
|
+
* 끊긴 이전 호출의 Promise 는 끝나지 않는다 (떨어진 DOM 에 그리지 않고 조용히 끝남).
|
|
33
39
|
*/
|
|
34
40
|
abortPrevious?: boolean;
|
|
35
|
-
/** false 를 리턴하면 이번 호출을 통째로 건너뛴다 (메서드도 fetch 도 실행 안 함, undefined 리턴). */
|
|
36
|
-
filter?: (
|
|
37
|
-
currentThis: any,
|
|
38
|
-
helper: HelperHostSet,
|
|
39
|
-
parameter: any[]
|
|
40
|
-
) => boolean | Promise<boolean>;
|
|
41
|
-
/** filter 통과 후 가장 먼저 실행 (await). 리턴값은 무시. init 조립은 request 팩토리 담당. */
|
|
42
|
-
before?: (
|
|
43
|
-
currentThis: any,
|
|
44
|
-
helper: HelperHostSet,
|
|
45
|
-
parameter: any[]
|
|
46
|
-
) => void | Promise<void>;
|
|
47
41
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
42
|
+
* false 를 리턴하면 이번 호출을 통째로 건너뛴다 (요청도 메서드도 실행 안 함, undefined 리턴).
|
|
43
|
+
* 요청이 메서드보다 먼저 나가므로 "보낼지 말지"는 여기서만 정할 수 있다. 메서드를 동기로 유지하려고 동기만 받는다.
|
|
50
44
|
*/
|
|
51
|
-
|
|
52
|
-
currentThis: any,
|
|
53
|
-
helper: HelperHostSet,
|
|
54
|
-
parameter: any[],
|
|
55
|
-
settled: PromiseSettledResult<any>
|
|
56
|
-
) => void | Promise<void>;
|
|
57
|
-
/**
|
|
58
|
-
* 성공/실패 무관 항상 실행 (다른 데코레이터의 finally 와 같은 규칙). 에러는 삼키지 않고 전파.
|
|
59
|
-
* args: 메서드에 넘긴 인자, result: 이번 호출의 리턴값(before 모드 = 메서드 리턴, after 모드 = fetch 결과)
|
|
60
|
-
*/
|
|
61
|
-
finally?: (
|
|
62
|
-
currentThis: any,
|
|
63
|
-
helper: HelperHostSet,
|
|
64
|
-
parameter: any[],
|
|
65
|
-
ctx: { args: any[]; result?: any; error?: any }
|
|
66
|
-
) => any | Promise<any>;
|
|
45
|
+
filter?: (ctx: FetchFilterContext) => boolean;
|
|
67
46
|
};
|
|
68
47
|
|
|
69
48
|
/** before: 인자 기반 RequestInit 조립. 없으면 GET. */
|
|
@@ -84,8 +63,13 @@ export type ReturnRequestFactory = (
|
|
|
84
63
|
export interface FetchMetadata {
|
|
85
64
|
propertyKey: string | symbol;
|
|
86
65
|
options: FetchOptions;
|
|
66
|
+
/** 필드(declare)에 붙었으면 true — 프레임워크가 요청 함수를 만든다 */
|
|
67
|
+
field?: boolean;
|
|
87
68
|
}
|
|
88
69
|
|
|
70
|
+
// 프레임워크가 만든 필드 요청 함수 표시 (덮어써졌는지 확인용)
|
|
71
|
+
const FETCH_FIELD_FN = Symbol.for('simple-web-component:fetch-field-fn');
|
|
72
|
+
|
|
89
73
|
/** 선언형: url/request/process 로 기술하면 프레임워크가 fetch 한다 */
|
|
90
74
|
export type FetchDeclarative<R> = {
|
|
91
75
|
url: RequestInfo | URL | FetchUrlFactory;
|
|
@@ -110,8 +94,8 @@ export type FetchManual = {
|
|
|
110
94
|
};
|
|
111
95
|
|
|
112
96
|
export type FetchOptions =
|
|
113
|
-
// before(기본):
|
|
114
|
-
//
|
|
97
|
+
// before(기본): 요청을 시작하고 그 Promise 를 @fetchPromise 자리에 넣어 메서드를 바로 호출한다.
|
|
98
|
+
// 메서드 반환값을 그대로 리턴 (동기 메서드는 동기 그대로). request 없으면 GET. valueKey 는 after 전용이라 금지.
|
|
115
99
|
| (FetchType & { trigger?: 'before'; valueKey?: never } & (FetchDeclarative<RequestFactory> | FetchManual))
|
|
116
100
|
// after: 메서드 실행 → 리턴값(valueKey 가 있으면 returnValue[valueKey])을 body(수동형이면 returnValue)로 → fetch. fetch 결과 리턴.
|
|
117
101
|
// (valueKey 기본값 = FETCH_METADATA_KEY: Symbol 키 우선 조회, 없으면 통째 값)
|
|
@@ -141,19 +125,44 @@ const combineSignals = (user: AbortSignal | null | undefined, own: AbortSignal):
|
|
|
141
125
|
return merged.signal;
|
|
142
126
|
};
|
|
143
127
|
|
|
144
|
-
|
|
128
|
+
/**
|
|
129
|
+
* @fetch 계열 데코레이터는 메서드와 필드 양쪽에 붙는다.
|
|
130
|
+
* - 메서드: 트리거가 부르는 핸들러용. 요청 Promise 를 @fetchPromise 로 주입하고 메서드를 쌓는다 (기존 방식)
|
|
131
|
+
* - 필드(declare): 직접 부르는 요청 함수용. `declare getPost: (id: number) => Promise<Post>` 의 몸통을 프레임워크가 만든다
|
|
132
|
+
*/
|
|
133
|
+
export type FetchDecorator = {
|
|
134
|
+
/**
|
|
135
|
+
* 필드: 선언 타입이 `(...args) => Promise<…>`(요청 함수) 또는 `Promise<…>`(값) 가 아니면 컴파일 에러.
|
|
136
|
+
* 옵셔널(`declare me?: Promise<T>`)도 된다 — 값 필드를 `this.me = undefined` 로 비우려면 strict 에선 옵셔널로 선언한다.
|
|
137
|
+
* (private / protected 필드는 TS 가 바깥 타입에서 못 봐서 검사 없이 통과 — 런타임 design:type 검사만 적용)
|
|
138
|
+
*/
|
|
139
|
+
<T, K extends string | symbol>(target: T & (K extends keyof T ? { [P in K]?: ((...args: any[]) => Promise<any>) | Promise<any> } : unknown), propertyKey: K): void;
|
|
140
|
+
/** 메서드: 트리거 핸들러 (요청 Promise 는 @fetchPromise 로) */
|
|
141
|
+
(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor): any;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// 건너뛰거나 끊긴 필드 호출이 돌려주는 Promise — 끝나지 않는다 (떨어진 DOM 에 그리지 않고 에러도 없음)
|
|
145
|
+
const never = () => new Promise<never>(() => {});
|
|
146
|
+
|
|
147
|
+
// 필드를 Promise 로 쓸 때 기억한 요청 — 엘리먼트 → (필드 이름 → Promise). 엘리먼트에 속성을 붙이지 않는다.
|
|
148
|
+
const valueCache = new WeakMap<object, Map<string | symbol, Promise<any>>>();
|
|
149
|
+
const cacheOf = (inst: object) => {
|
|
150
|
+
let m = valueCache.get(inst);
|
|
151
|
+
if (!m) valueCache.set(inst, (m = new Map()));
|
|
152
|
+
return m;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
function createFetch(optionsOrUrl: FetchOptions | string): FetchDecorator {
|
|
145
156
|
const options: FetchOptions = typeof optionsOrUrl === 'string' ? { url: optionsOrUrl } : optionsOrUrl;
|
|
146
|
-
return (target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
157
|
+
return ((target: Object, propertyKey: string | symbol, descriptor?: PropertyDescriptor) => {
|
|
147
158
|
const constructor = target.constructor;
|
|
159
|
+
const isField = !descriptor;
|
|
148
160
|
const list = (ReflectUtils.getOwnMetadata(FETCH_METADATA_KEY, constructor) as FetchMetadata[] | undefined) ?? [];
|
|
149
|
-
list.push({ propertyKey, options });
|
|
161
|
+
list.push({ propertyKey, options, field: isField });
|
|
150
162
|
ReflectUtils.defineMetadata(FETCH_METADATA_KEY, list, constructor);
|
|
151
163
|
|
|
152
|
-
if (!descriptor) return descriptor;
|
|
153
|
-
const originalMethod = descriptor.value;
|
|
154
|
-
|
|
155
164
|
const extractBody = (v: any) => {
|
|
156
|
-
|
|
165
|
+
const keyToUse = (options as { valueKey?: symbol | string }).valueKey ?? FETCH_METADATA_KEY;
|
|
157
166
|
if (v && typeof v === 'object' && keyToUse in v) {
|
|
158
167
|
return v[keyToUse];
|
|
159
168
|
}
|
|
@@ -224,37 +233,26 @@ function createFetch(optionsOrUrl: FetchOptions | string): MethodDecorator {
|
|
|
224
233
|
return readBody(res);
|
|
225
234
|
};
|
|
226
235
|
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
if (!saves.length) return args;
|
|
231
|
-
const out = [...args];
|
|
232
|
-
for (const s of saves) {
|
|
233
|
-
if (s.kind === 'fetchSettled') out[s.index] = settled;
|
|
234
|
-
}
|
|
235
|
-
return out;
|
|
236
|
-
};
|
|
236
|
+
const trigger = (options as { trigger?: string }).trigger;
|
|
237
|
+
const isAfter = trigger === 'after' || (trigger !== 'before' && 'valueKey' in options);
|
|
238
|
+
const manual = (options as FetchManual).manual;
|
|
237
239
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
+
/**
|
|
241
|
+
* 호출 한 번을 시작한다. 건너뛸 호출(떨어진 뒤 시작 / filter false)이면 null.
|
|
242
|
+
* 프레임워크가 끊는 경우(disconnect / abortPrevious)는 에러가 아니라 "할 일 없음" — frameworkAborted() 로 구분한다.
|
|
243
|
+
*/
|
|
244
|
+
const begin = (inst: any, args: any[]) => {
|
|
240
245
|
let helper: HelperHostSet | undefined;
|
|
241
246
|
try {
|
|
242
247
|
helper = SwcUtils.getHelperAndHostSet(inst);
|
|
243
248
|
} catch {
|
|
244
249
|
helper = undefined;
|
|
245
250
|
}
|
|
246
|
-
|
|
247
|
-
// disconnect 된 뒤에 새로 시작하는 호출(예: 떠나기 직전 걸어둔 setTimeout)도 조용히 건너뛴다.
|
|
251
|
+
// disconnect 된 뒤에 새로 시작하는 호출(예: 떠나기 직전 걸어둔 setTimeout)은 건너뛴다.
|
|
248
252
|
// 한 번도 connect 안 된 요소는 해당 없음 (FetchLifeCycler 가 disconnect 때만 표시)
|
|
249
|
-
if (inst?.__swc_fetchDetached && inst.isConnected === false) return
|
|
253
|
+
if (inst?.__swc_fetchDetached && inst.isConnected === false) return null;
|
|
254
|
+
if (options.filter && !options.filter({ self: inst, helper: helper!, args })) return null;
|
|
250
255
|
|
|
251
|
-
// filter → before → (before 모드: fetch → 메서드 / after 모드: 메서드 → fetch) → finally
|
|
252
|
-
if (options.filter && !(await options.filter(inst, helper!, args))) return undefined;
|
|
253
|
-
if (options.before) await options.before(inst, helper!, args);
|
|
254
|
-
|
|
255
|
-
const manual = (options as FetchManual).manual;
|
|
256
|
-
|
|
257
|
-
// 프레임워크가 끊는 경우(disconnect / abortPrevious)는 에러가 아니라 "할 일 없음" → 조용히 undefined
|
|
258
256
|
const controller = new AbortController();
|
|
259
257
|
const active = getActiveControllers(inst);
|
|
260
258
|
active.add(controller);
|
|
@@ -264,8 +262,12 @@ function createFetch(optionsOrUrl: FetchOptions | string): MethodDecorator {
|
|
|
264
262
|
latest.set(propertyKey, controller);
|
|
265
263
|
}
|
|
266
264
|
const frameworkAborted = () => controller.signal.aborted;
|
|
265
|
+
const cleanup = () => {
|
|
266
|
+
active.delete(controller);
|
|
267
|
+
if (options.abortPrevious && getLatestControllers(inst).get(propertyKey) === controller) getLatestControllers(inst).delete(propertyKey);
|
|
268
|
+
};
|
|
267
269
|
|
|
268
|
-
// 사용자 manual 이 signal 을 무시해도 abort 즉시
|
|
270
|
+
// 사용자 manual 이 signal 을 무시해도 abort 즉시 끝나도록 abort 와 경쟁시킨다
|
|
269
271
|
const aborted = new Promise<never>((_, reject) => {
|
|
270
272
|
if (controller.signal.aborted) reject(controller.signal.reason);
|
|
271
273
|
else controller.signal.addEventListener('abort', () => reject(controller.signal.reason), { once: true });
|
|
@@ -273,80 +275,163 @@ function createFetch(optionsOrUrl: FetchOptions | string): MethodDecorator {
|
|
|
273
275
|
aborted.catch(() => {}); // race 에 안 쓰이고 끝나도 unhandled rejection 이 안 나게
|
|
274
276
|
|
|
275
277
|
// 수동형이면 manual(…, signal, returnValue), 선언형이면 url/init 으로 기본 fetch
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
pending.catch(() => {}); // abort 로 race 에서 진 뒤 늦게 reject 돼도 unhandled rejection 이 안 나게
|
|
283
|
-
settled = { status: 'fulfilled', value: await Promise.race([pending, aborted]) };
|
|
284
|
-
} catch (reason) {
|
|
285
|
-
settled = { status: 'rejected', reason };
|
|
286
|
-
}
|
|
287
|
-
// 떨어진 요소의 DOM 을 건드리지 않게, 프레임워크 abort 면 after 훅은 건너뛴다
|
|
288
|
-
if (options.after && !frameworkAborted()) await options.after(inst, helper!, args, settled);
|
|
289
|
-
return settled;
|
|
278
|
+
const doFetch = (init?: RequestInit, returnValue?: any): Promise<any> => {
|
|
279
|
+
const pending = Promise.resolve().then(() => manual
|
|
280
|
+
? manual(inst, helper!, args, controller.signal, returnValue)
|
|
281
|
+
: defaultFetching(helper, resolveUrl(inst, helper!, args), { ...init, signal: combineSignals(init?.signal, controller.signal) }));
|
|
282
|
+
pending.catch(() => {}); // abort 로 race 에서 진 뒤 늦게 reject 돼도 unhandled rejection 이 안 나게
|
|
283
|
+
return Promise.race([pending, aborted]);
|
|
290
284
|
};
|
|
291
285
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (frameworkAborted()) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
286
|
+
// before: request 조립(인자 기반) → fetch
|
|
287
|
+
const fetchBefore = () => {
|
|
288
|
+
const reqOpt = manual ? undefined : (options as FetchDeclarative<RequestFactory>).request;
|
|
289
|
+
return Promise.resolve()
|
|
290
|
+
.then(() => (typeof reqOpt === 'string' ? { method: reqOpt } : typeof reqOpt === 'function' ? reqOpt(inst, helper!, args) : undefined))
|
|
291
|
+
.then(init => doFetch(init));
|
|
292
|
+
};
|
|
293
|
+
// after: returned(메서드 리턴 / 필드면 마지막 인자)에서 body 를 뽑아 request 와 합쳐 fetch
|
|
294
|
+
const fetchAfter = async (returned: any) => {
|
|
295
|
+
const body = extractBody(returned);
|
|
296
|
+
if (manual) return doFetch(undefined, body);
|
|
297
|
+
const reqOpt = (options as FetchDeclarative<ReturnRequestFactory>).request;
|
|
298
|
+
const reqInit: RequestInit | undefined = typeof reqOpt === 'string'
|
|
299
|
+
? { method: reqOpt }
|
|
300
|
+
: typeof reqOpt === 'function' ? await reqOpt(inst, helper!, args, returned) : undefined;
|
|
301
|
+
return doFetch({ ...reqInit, body: reqInit?.body ?? body });
|
|
302
|
+
};
|
|
303
|
+
// 결과 Promise: 프레임워크가 끊으면 끝나지 않는다. 사용자가 안 받아도 unhandled rejection 이 안 나게
|
|
304
|
+
const quiet = (run: Promise<any>): Promise<any> => {
|
|
305
|
+
const promise = new Promise<any>((resolve, reject) => {
|
|
306
|
+
run.then(v => { if (!frameworkAborted()) resolve(v); }, e => { if (!frameworkAborted()) reject(e); }).finally(cleanup);
|
|
307
|
+
});
|
|
308
|
+
promise.catch(() => {});
|
|
309
|
+
return promise;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
return { frameworkAborted, cleanup, fetchBefore, fetchAfter, quiet };
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// ── 필드: declare 필드의 값을 프레임워크가 만든다 ──
|
|
316
|
+
// 선언이 함수(`(…) => Promise<T>`)든 값(`Promise<T>`)든 데코레이터가 받는 건 (prototype, 이름) 뿐이고
|
|
317
|
+
// 타입은 런타임에 없다(빌드 도구에 따라 design:type 도 없음). 그래서 필드 값은 "부를 수 있는 함수이면서 Promise" 하나로 만든다:
|
|
318
|
+
// - 부르면 (함수로 선언한 경우): 부를 때마다 새 요청. 인자 = url/request 재료, after(POST…)면 마지막 인자가 body
|
|
319
|
+
// - then/catch/finally/await (Promise 로 선언한 경우): 기억된 요청. 처음이면 요청, 성공하면 기억, 실패하면 잊어서 다음에 다시
|
|
320
|
+
// - 대입: undefined / null 이면 잊음, 다른 값/Promise 면 그걸 기억 (예: SSR 로 받은 값)
|
|
321
|
+
// 사용자는 선언한 타입대로만 쓴다 (TS 가 Promise 로 선언한 필드의 호출을 막는다).
|
|
322
|
+
if (isField) {
|
|
323
|
+
// 타입 정보가 있는 빌드(emitDecoratorMetadata)면 함수/Promise 가 아닌 선언을 바로 알린다 (없으면 컴파일 검사만)
|
|
324
|
+
const designType = ReflectUtils.getMetadata('design:type', target, propertyKey);
|
|
325
|
+
if (designType !== undefined && designType !== Function && designType !== Promise && designType !== Object) {
|
|
326
|
+
throw new Error(`[SWC] @fetch field "${String(propertyKey)}" must be a function returning a Promise (\`declare ${String(propertyKey)}: (…) => Promise<T>\`) or a Promise (\`declare ${String(propertyKey)}: Promise<T>\`), not ${designType?.name ?? String(designType)}.`);
|
|
327
|
+
}
|
|
328
|
+
// 같은 이름을 다른 데코레이터가 이미 차지했으면(접근자 등) 정의 시점에 바로 알린다
|
|
329
|
+
if (Object.prototype.hasOwnProperty.call(target, propertyKey)) {
|
|
330
|
+
throw new Error(`[SWC] "${String(propertyKey)}" is a @fetch field but something else already defines it. Put triggers/outputs on a method that calls this.${String(propertyKey)}().`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 기억된 요청 (Promise 로 쓸 때). body 는 request 팩토리로 (인자 없음 → args = [], returnValue = undefined)
|
|
334
|
+
const remembered = (inst: any): Promise<any> => {
|
|
335
|
+
const cache = cacheOf(inst);
|
|
336
|
+
const hit = cache.get(propertyKey);
|
|
337
|
+
if (hit) return hit;
|
|
338
|
+
const call = begin(inst, []);
|
|
339
|
+
if (!call) return never(); // 건너뛴 호출은 기억하지 않는다
|
|
340
|
+
const promise = call.quiet(isAfter ? call.fetchAfter(undefined) : call.fetchBefore());
|
|
341
|
+
cache.set(propertyKey, promise);
|
|
342
|
+
promise.catch(() => { if (cache.get(propertyKey) === promise) cache.delete(propertyKey); });
|
|
343
|
+
return promise;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// 인스턴스마다 필드 값(함수 겸 Promise) 하나 — 읽을 때마다 같은 값
|
|
347
|
+
const values = new WeakMap<object, any>();
|
|
348
|
+
const get = function (this: any) {
|
|
349
|
+
const inst = this;
|
|
350
|
+
let value = values.get(inst);
|
|
351
|
+
if (value) return value;
|
|
352
|
+
value = function (...args: any[]) {
|
|
353
|
+
const call = begin(inst, args);
|
|
354
|
+
if (!call) return never();
|
|
355
|
+
return call.quiet(isAfter ? call.fetchAfter(args[args.length - 1]) : call.fetchBefore());
|
|
356
|
+
};
|
|
357
|
+
value.then = (ok?: any, fail?: any) => remembered(inst).then(ok, fail);
|
|
358
|
+
value.catch = (fail?: any) => remembered(inst).catch(fail);
|
|
359
|
+
value.finally = (done?: any) => remembered(inst).finally(done);
|
|
360
|
+
// instanceof Promise 로 Promise 를 알아보는 코드(출력 데코레이터 등)도 Promise 로 보게 한다.
|
|
361
|
+
// then/catch/finally 는 위에서 직접 달았으니 Promise.prototype 의 것(내부 슬롯 필요)은 쓰이지 않는다.
|
|
362
|
+
Object.setPrototypeOf(value, Promise.prototype);
|
|
363
|
+
values.set(inst, value);
|
|
364
|
+
return value;
|
|
365
|
+
};
|
|
366
|
+
(get as any)[FETCH_FIELD_FN] = true;
|
|
367
|
+
Object.defineProperty(target, propertyKey, {
|
|
368
|
+
get,
|
|
369
|
+
set(this: any, v: any) {
|
|
370
|
+
const cache = cacheOf(this);
|
|
371
|
+
if (v === undefined || v === null) cache.delete(propertyKey);
|
|
372
|
+
else cache.set(propertyKey, Promise.resolve(v));
|
|
373
|
+
},
|
|
374
|
+
configurable: true,
|
|
375
|
+
});
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ── 메서드: 트리거 핸들러용 ──
|
|
380
|
+
const originalMethod = descriptor!.value;
|
|
381
|
+
|
|
382
|
+
// after 모드는 fetch 결과를 리턴하므로 래퍼가 항상 Promise 를 돌려준다 — 동기 메서드면 선언과 어긋나니 알려준다
|
|
383
|
+
let warnedSync = false;
|
|
384
|
+
const isPromiseLike = (v: any) => v instanceof Promise || (v && typeof v.then === 'function');
|
|
385
|
+
const guardSync = (raw: any) => {
|
|
386
|
+
if (!warnedSync && originalMethod?.constructor?.name !== 'AsyncFunction' && !isPromiseLike(raw)) {
|
|
387
|
+
warnedSync = true;
|
|
388
|
+
console.warn(`[SWC] @fetch (after) on "${String(propertyKey)}" always returns a Promise: declare the method async (or with an explicit Promise return type) so the signature matches.`);
|
|
340
389
|
}
|
|
341
390
|
};
|
|
342
391
|
|
|
392
|
+
// 호출 인자는 유지한 채, @fetchPromise 자리만 채운다
|
|
393
|
+
const injectPromise = (inst: any, methodName: string | symbol, args: any[], promise: Promise<any>): any[] => {
|
|
394
|
+
const saves = getParameterMetadata(inst, methodName);
|
|
395
|
+
if (!saves.length) return args;
|
|
396
|
+
const out = [...args];
|
|
397
|
+
for (const s of saves) {
|
|
398
|
+
if (s.kind === 'fetchPromise') out[s.index] = promise;
|
|
399
|
+
}
|
|
400
|
+
return out;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
descriptor!.value = function (...args: any[]) {
|
|
404
|
+
const inst = this as any;
|
|
405
|
+
const call = begin(inst, args);
|
|
406
|
+
if (!call) return undefined;
|
|
407
|
+
|
|
408
|
+
if (!isAfter) {
|
|
409
|
+
// before: 요청을 시작하고, 그 Promise 를 @fetchPromise 자리에 넣어 메서드를 바로 부른다. 메서드 반환값을 그대로 리턴.
|
|
410
|
+
return originalMethod.apply(inst, injectPromise(inst, propertyKey, args, call.quiet(call.fetchBefore())));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// after: 메서드 실행 → 리턴값에서 body → fetch → fetch 결과 리턴
|
|
414
|
+
return (async () => {
|
|
415
|
+
try {
|
|
416
|
+
const raw = originalMethod.apply(inst, args);
|
|
417
|
+
guardSync(raw);
|
|
418
|
+
return await call.fetchAfter(await raw);
|
|
419
|
+
} catch (e) {
|
|
420
|
+
if (call.frameworkAborted()) return undefined; // 끊긴 호출은 조용히
|
|
421
|
+
throw e;
|
|
422
|
+
} finally {
|
|
423
|
+
call.cleanup();
|
|
424
|
+
}
|
|
425
|
+
})();
|
|
426
|
+
};
|
|
427
|
+
|
|
343
428
|
return descriptor;
|
|
344
|
-
};
|
|
429
|
+
}) as FetchDecorator;
|
|
345
430
|
}
|
|
346
431
|
|
|
347
|
-
export function fetch(url: string):
|
|
348
|
-
export function fetch(options: FetchOptions):
|
|
349
|
-
export function fetch(optionsOrUrl: FetchOptions | string):
|
|
432
|
+
export function fetch(url: string): FetchDecorator;
|
|
433
|
+
export function fetch(options: FetchOptions): FetchDecorator;
|
|
434
|
+
export function fetch(optionsOrUrl: FetchOptions | string): FetchDecorator {
|
|
350
435
|
return createFetch(optionsOrUrl);
|
|
351
436
|
}
|
|
352
437
|
|
|
@@ -365,7 +450,7 @@ export type FetchManualOptions = FetchType & FetchTriggerOptions;
|
|
|
365
450
|
* @fetchManual(fn, options?) = @fetch({ manual: fn, ...options })
|
|
366
451
|
* 서비스 호출 등 요청을 직접 만들 때. 예: @fetchManual((self) => self.myService.getProfile({}))
|
|
367
452
|
*/
|
|
368
|
-
export function fetchManual(manual: FetchManualFn, options?: FetchManualOptions):
|
|
453
|
+
export function fetchManual(manual: FetchManualFn, options?: FetchManualOptions): FetchDecorator {
|
|
369
454
|
return createFetch({ ...(options ?? {}), manual } as FetchOptions);
|
|
370
455
|
}
|
|
371
456
|
|
|
@@ -376,7 +461,7 @@ export type FetchAfterMethodOptions = FetchType & Omit<FetchDeclarative<ReturnRe
|
|
|
376
461
|
|
|
377
462
|
type FetchUrl = RequestInfo | URL | FetchUrlFactory;
|
|
378
463
|
|
|
379
|
-
const beforeMethod = (method: string) => (url: FetchUrl, options?: FetchBeforeMethodOptions):
|
|
464
|
+
const beforeMethod = (method: string) => (url: FetchUrl, options?: FetchBeforeMethodOptions): FetchDecorator => {
|
|
380
465
|
const { request, ...rest } = options ?? {};
|
|
381
466
|
return createFetch({
|
|
382
467
|
...rest,
|
|
@@ -386,7 +471,7 @@ const beforeMethod = (method: string) => (url: FetchUrl, options?: FetchBeforeMe
|
|
|
386
471
|
} as FetchOptions);
|
|
387
472
|
};
|
|
388
473
|
|
|
389
|
-
const afterMethod = (method: string) => (url: FetchUrl, options?: FetchAfterMethodOptions):
|
|
474
|
+
const afterMethod = (method: string) => (url: FetchUrl, options?: FetchAfterMethodOptions): FetchDecorator => {
|
|
390
475
|
const { request, ...rest } = options ?? {};
|
|
391
476
|
return createFetch({
|
|
392
477
|
...rest,
|
|
@@ -396,7 +481,7 @@ const afterMethod = (method: string) => (url: FetchUrl, options?: FetchAfterMeth
|
|
|
396
481
|
} as FetchOptions);
|
|
397
482
|
};
|
|
398
483
|
|
|
399
|
-
/** @fetchGet(url, options?) — before 모드 GET.
|
|
484
|
+
/** @fetchGet(url, options?) — before 모드 GET. 요청 Promise 는 @fetchPromise 로 */
|
|
400
485
|
export const fetchGet = beforeMethod('GET');
|
|
401
486
|
/** @fetchDelete(url, options?) — before 모드 DELETE (body 없음) */
|
|
402
487
|
export const fetchDelete = beforeMethod('DELETE');
|
|
@@ -411,20 +496,20 @@ export const fetchPatch = afterMethod('PATCH');
|
|
|
411
496
|
* @fetchLatest(url | options) = @fetch({ ...options, abortPrevious: true })
|
|
412
497
|
* 연달아 부르면 이전 요청을 끊고 마지막 것만 살린다 (검색 자동완성 등)
|
|
413
498
|
*/
|
|
414
|
-
export function fetchLatest(url: string):
|
|
415
|
-
export function fetchLatest(options: FetchOptions):
|
|
416
|
-
export function fetchLatest(optionsOrUrl: FetchOptions | string):
|
|
499
|
+
export function fetchLatest(url: string): FetchDecorator;
|
|
500
|
+
export function fetchLatest(options: FetchOptions): FetchDecorator;
|
|
501
|
+
export function fetchLatest(optionsOrUrl: FetchOptions | string): FetchDecorator {
|
|
417
502
|
const options: FetchOptions = typeof optionsOrUrl === 'string' ? { url: optionsOrUrl } : optionsOrUrl;
|
|
418
503
|
return createFetch({ ...options, abortPrevious: true } as FetchOptions);
|
|
419
504
|
}
|
|
420
505
|
|
|
421
506
|
/** @fetchBefore(options) = @fetch({ ...options, trigger: 'before' }) */
|
|
422
|
-
export function fetchBefore(options: FetchType & (FetchDeclarative<RequestFactory> | FetchManual)):
|
|
507
|
+
export function fetchBefore(options: FetchType & (FetchDeclarative<RequestFactory> | FetchManual)): FetchDecorator {
|
|
423
508
|
return createFetch({ ...options, trigger: 'before' } as FetchOptions);
|
|
424
509
|
}
|
|
425
510
|
|
|
426
511
|
/** @fetchAfter(options) = @fetch({ ...options, trigger: 'after' }) */
|
|
427
|
-
export function fetchAfter(options: FetchType & { valueKey?: string } & (FetchDeclarative<ReturnRequestFactory> | FetchManual)):
|
|
512
|
+
export function fetchAfter(options: FetchType & { valueKey?: string } & (FetchDeclarative<ReturnRequestFactory> | FetchManual)): FetchDecorator {
|
|
428
513
|
return createFetch({ ...options, trigger: 'after' } as FetchOptions);
|
|
429
514
|
}
|
|
430
515
|
|
|
@@ -434,18 +519,63 @@ export const getFetchMetadata = (target: any): FetchMetadata[] | undefined => {
|
|
|
434
519
|
return ReflectUtils.findAllMetadata<any[]>(FETCH_METADATA_KEY, constructor).flat();
|
|
435
520
|
};
|
|
436
521
|
|
|
522
|
+
// 메타데이터 값 안 어딘가가 이 이름(propertyKey)을 가리키는지 (Map 키 / 배열 / { propertyKey })
|
|
523
|
+
const refersTo = (v: any, key: string | symbol): boolean => {
|
|
524
|
+
if (v instanceof Map) return v.has(key) || [...v.values()].some(x => refersTo(x, key));
|
|
525
|
+
if (Array.isArray(v)) return v.some(x => refersTo(x, key));
|
|
526
|
+
return !!v && typeof v === 'object' && v.propertyKey === key;
|
|
527
|
+
};
|
|
528
|
+
const findDescriptor = (proto: any, key: string | symbol): PropertyDescriptor | undefined => {
|
|
529
|
+
for (let p = proto; p && p !== Object.prototype; p = Object.getPrototypeOf(p)) {
|
|
530
|
+
const d = Object.getOwnPropertyDescriptor(p, key);
|
|
531
|
+
if (d) return d;
|
|
532
|
+
}
|
|
533
|
+
return undefined;
|
|
534
|
+
};
|
|
535
|
+
const fieldHint = (key: string | symbol) => `Put triggers/outputs on a method that calls this.${String(key)}().`;
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* @elementDefine 이 클래스 정의 시점에 부른다 (멤버 데코레이터가 다 붙은 뒤라 붙인 순서와 무관).
|
|
539
|
+
* 필드 @fetch 와 같은 이름에 다른 SWC 데코레이터가 붙었으면 에러 — 메서드 데코레이터는 TS 가 이미 막지만
|
|
540
|
+
* JS / `as any` 로 우회한 경우, 필드용 데코레이터(@attribute, @query, @property …)까지 잡는다.
|
|
541
|
+
*/
|
|
542
|
+
export const assertFetchFields = (constructor: any): void => {
|
|
543
|
+
const fields = (getFetchMetadata(constructor) ?? []).filter(m => m.field).map(m => m.propertyKey);
|
|
544
|
+
for (const key of fields) {
|
|
545
|
+
const d = findDescriptor(constructor.prototype, key);
|
|
546
|
+
if (!d?.value?.[FETCH_FIELD_FN] && !(d?.get as any)?.[FETCH_FIELD_FN]) {
|
|
547
|
+
throw new Error(`[SWC] "${String(key)}" is a @fetch field but another decorator replaced it. ${fieldHint(key)}`);
|
|
548
|
+
}
|
|
549
|
+
for (const mk of ReflectUtils.getMetadataKeys(constructor)) {
|
|
550
|
+
if (mk === FETCH_METADATA_KEY || typeof mk === 'string') continue; // design:* 등 TS 메타데이터는 제외
|
|
551
|
+
if (refersTo(ReflectUtils.getMetadata(mk, constructor), key)) {
|
|
552
|
+
const name = typeof mk === 'symbol' ? mk.description?.replace('simple-web-component:', '@') : String(mk);
|
|
553
|
+
throw new Error(`[SWC] "${String(key)}" is a @fetch field — it can't also have ${name}. ${fieldHint(key)}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
|
|
437
559
|
/**
|
|
438
560
|
* disconnect 시 진행 중인 @fetch 요청을 전부 abort (메서드/after 는 실행되지 않고 조용히 끝난다).
|
|
439
561
|
* 떨어진 동안 새로 시작하는 호출도 건너뛰도록 표시하고, 다시 connect 되면 표시를 지운다.
|
|
440
562
|
*/
|
|
441
563
|
export class FetchLifeCycler implements ElementDefineLifeCycler {
|
|
442
564
|
onConnected(helperHostSet: HelperHostSet): void {
|
|
443
|
-
helperHostSet.$this
|
|
565
|
+
const inst = helperHostSet.$this;
|
|
566
|
+
inst.__swc_fetchDetached = false;
|
|
567
|
+
// declare 를 빠뜨리면 인스턴스 필드(undefined)가 프로토타입의 요청 함수를 가린다
|
|
568
|
+
for (const m of getFetchMetadata(inst) ?? []) {
|
|
569
|
+
if (m.field && Object.prototype.hasOwnProperty.call(inst, m.propertyKey)) {
|
|
570
|
+
throw new Error(`[SWC] @fetch field "${String(m.propertyKey)}" must be declared with \`declare\` (e.g. \`declare ${String(m.propertyKey)}: (…) => Promise<T>\`) — a plain field shadows the request function.`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
444
573
|
}
|
|
445
574
|
|
|
446
575
|
onDisconnected(helperHostSet: HelperHostSet): void {
|
|
447
576
|
const inst = helperHostSet.$this;
|
|
448
577
|
inst.__swc_fetchDetached = true;
|
|
578
|
+
valueCache.delete(inst); // 끊긴 요청은 끝나지 않으니, 다시 붙은 뒤 읽으면 새로 요청하도록 기억을 지운다
|
|
449
579
|
const active: Set<AbortController> | undefined = inst.__swc_fetchControllers;
|
|
450
580
|
if (!active?.size) return;
|
|
451
581
|
for (const c of active) c.abort(abortError('element disconnected'));
|
package/src/decorators/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
2
|
import { SpecialSelector, SwcQueryOptions, SwcFnSelector, SwcSelector, HelperHostSet } from '../types';
|
|
3
|
-
import { buildSwcParameterArgs } from './parameter';
|
|
3
|
+
import { buildSwcParameterArgs, buildCommonKindValues } from './parameter';
|
|
4
4
|
|
|
5
5
|
// 공통 옵션 — IntersectionObserverInit + filter/removeObserver. root·delegate 제외.
|
|
6
6
|
// 주의: 데코레이터의 root(light/shadow/all/auto) 는 셀렉터 스코프 선택용이다.
|
|
@@ -205,11 +205,10 @@ export class IntersectionObserverLifeCycler implements ElementDefineLifeCycler {
|
|
|
205
205
|
void (async () => {
|
|
206
206
|
const helper = SwcUtils.getHelperAndHostSet(inst);
|
|
207
207
|
if (opts.filter && !(await opts.filter(matchedEls, { currentThis: inst, helper }))) return;
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
const legacyArgs = [matchedEls, entries, obs, { ...hostSet, $root: root }];
|
|
208
|
+
const common = buildCommonKindValues(inst, helperHostSet.$w);
|
|
209
|
+
const legacyArgs = [matchedEls, entries, obs, { ...common.hostSet, $root: root }];
|
|
211
210
|
const buildArgs = (beforeReturn: any) => buildSwcParameterArgs(inst, m.propertyKey, {
|
|
212
|
-
|
|
211
|
+
...common, intersectionObserverBeforeReturn: beforeReturn
|
|
213
212
|
}, [...legacyArgs, beforeReturn]);
|
|
214
213
|
let args = buildArgs(undefined);
|
|
215
214
|
if (opts.before) { const br = await opts.before(matchedEls, { currentThis: inst, helper }); args = buildArgs(br); }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReflectUtils } from '@dooboostore/core';
|
|
2
2
|
import { SpecialSelector, SwcQueryOptions, SwcSelector, SwcFnSelector, HelperHostSet } from '../types';
|
|
3
|
-
import { buildSwcParameterArgs } from './parameter';
|
|
3
|
+
import { buildSwcParameterArgs, buildCommonKindValues } from './parameter';
|
|
4
4
|
|
|
5
5
|
// 공통 옵션 — root·delegate 없음 (MutationObserverInit + filter/removeObserver)
|
|
6
6
|
export interface MutationObserverBaseOptions extends MutationObserverInit {
|
|
@@ -263,11 +263,10 @@ export class MutationObserverLifeCycler implements ElementDefineLifeCycler {
|
|
|
263
263
|
void (async () => {
|
|
264
264
|
const helper = SwcUtils.getHelperAndHostSet(inst);
|
|
265
265
|
if (options.filter && !(await options.filter(matchedEls, { currentThis: inst, helper }))) return;
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
const legacyArgs = [matchedEls, records, obs, { ...hostSet, $root: root }];
|
|
266
|
+
const common = buildCommonKindValues(inst, helperHostSet.$w);
|
|
267
|
+
const legacyArgs = [matchedEls, records, obs, { ...common.hostSet, $root: root }];
|
|
269
268
|
const buildArgs = (beforeReturn: any) => buildSwcParameterArgs(inst, meta.propertyKey, {
|
|
270
|
-
|
|
269
|
+
...common, mutationObserverBeforeReturn: beforeReturn
|
|
271
270
|
}, [...legacyArgs, beforeReturn]);
|
|
272
271
|
let args = buildArgs(undefined);
|
|
273
272
|
if (options.before) { const br = await options.before(matchedEls, { currentThis: inst, helper }); args = buildArgs(br); }
|