@decaf-ts/decoration 0.0.6 → 0.0.7
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 +200 -108
- package/dist/decoration.cjs +35 -13
- package/dist/decoration.esm.cjs +35 -13
- package/lib/constants.cjs +3 -10
- package/lib/constants.d.ts +2 -9
- package/lib/decoration/index.cjs +5 -1
- package/lib/decoration/index.d.ts +4 -0
- package/lib/decorators.cjs +9 -2
- package/lib/decorators.d.ts +9 -2
- package/lib/esm/constants.d.ts +2 -9
- package/lib/esm/constants.js +3 -10
- package/lib/esm/decoration/index.d.ts +4 -0
- package/lib/esm/decoration/index.js +5 -1
- package/lib/esm/decorators.d.ts +9 -2
- package/lib/esm/decorators.js +9 -2
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/metadata/Metadata.d.ts +17 -1
- package/lib/esm/metadata/Metadata.js +24 -2
- package/lib/esm/metadata/index.d.ts +4 -0
- package/lib/esm/metadata/index.js +5 -1
- package/lib/index.cjs +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/metadata/Metadata.cjs +24 -2
- package/lib/metadata/Metadata.d.ts +17 -1
- package/lib/metadata/index.cjs +5 -1
- package/lib/metadata/index.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,175 +79,186 @@ Design highlights
|
|
|
79
79
|
|
|
80
80
|
# How to Use
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
Practical examples for every exported surface of **@decaf-ts/decoration**. All snippets are TypeScript and mirror the behaviour covered by the unit and integration tests.
|
|
83
83
|
|
|
84
|
-
Prerequisites
|
|
84
|
+
## Prerequisites
|
|
85
85
|
|
|
86
|
-
- Enable experimental decorators and
|
|
86
|
+
- Enable experimental decorators and decorator metadata in `tsconfig.json`:
|
|
87
87
|
|
|
88
|
-
```json
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"compilerOptions": {
|
|
91
|
+
"experimentalDecorators": true,
|
|
92
|
+
"emitDecoratorMetadata": true
|
|
93
|
+
}
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
-
```
|
|
95
|
+
```
|
|
96
96
|
|
|
97
|
-
- Import reflect-metadata once
|
|
97
|
+
- Import `reflect-metadata` once (before decorators execute):
|
|
98
98
|
|
|
99
|
-
```ts
|
|
100
|
-
import "reflect-metadata";
|
|
101
|
-
```
|
|
99
|
+
```ts
|
|
100
|
+
import "reflect-metadata";
|
|
101
|
+
```
|
|
102
102
|
|
|
103
|
-
Decoration
|
|
103
|
+
## Decoration Builder
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
The `Decoration` class exposes a fluent builder that lets you define base decorators, add flavour-specific extras, or override behaviour entirely.
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
### 1. Register base decorators for the default flavour
|
|
108
108
|
|
|
109
109
|
```ts
|
|
110
110
|
import { Decoration } from "@decaf-ts/decoration";
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
console.log(`[${tag}]`, (target as any).name);
|
|
112
|
+
const markAsComponent: ClassDecorator = (target) => {
|
|
113
|
+
(target as any).__isComponent = true;
|
|
115
114
|
};
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
|
|
116
|
+
const tagFactory = (tag: string): ClassDecorator => (target) => {
|
|
117
|
+
(target as any).__tag = tag;
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
const component = () =>
|
|
121
|
+
Decoration.for("component")
|
|
122
|
+
.define({ decorator: tagFactory, args: ["base"] }, markAsComponent)
|
|
123
|
+
.apply();
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class MyComponent {}
|
|
128
|
-
```
|
|
125
|
+
@component()
|
|
126
|
+
class DefaultComponent {}
|
|
129
127
|
|
|
130
|
-
|
|
128
|
+
(DefaultComponent as any).__isComponent; // true
|
|
129
|
+
(DefaultComponent as any).__tag; // "base"
|
|
130
|
+
```
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
### 2. Extend base decorators with flavour-specific extras
|
|
133
133
|
|
|
134
134
|
```ts
|
|
135
|
-
|
|
135
|
+
// Register the same base behaviour as above.
|
|
136
|
+
const baseComponent = () =>
|
|
137
|
+
Decoration.for("component")
|
|
138
|
+
.define(((target: any) => target) as ClassDecorator)
|
|
139
|
+
.apply();
|
|
136
140
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
.define(((t: any) => t) as ClassDecorator)
|
|
140
|
-
.apply();
|
|
141
|
-
|
|
142
|
-
// Flavour-specific extras
|
|
143
|
-
Decoration.flavouredAs("web")
|
|
144
|
-
.for("component")
|
|
145
|
-
.extend({
|
|
146
|
-
decorator: (tag: string): ClassDecorator => (target) => {
|
|
147
|
-
(target as any).__platform = tag;
|
|
148
|
-
},
|
|
149
|
-
args: ["web"],
|
|
150
|
-
})
|
|
151
|
-
.apply();
|
|
152
|
-
|
|
153
|
-
// Choose flavour at runtime
|
|
154
|
-
Decoration.setFlavourResolver(() => "web");
|
|
141
|
+
@baseComponent()
|
|
142
|
+
class BaseComponent {}
|
|
155
143
|
|
|
156
|
-
|
|
144
|
+
Decoration.setFlavourResolver(() => "web");
|
|
157
145
|
|
|
158
|
-
|
|
146
|
+
const decorate = () =>
|
|
147
|
+
Decoration.flavouredAs("web")
|
|
148
|
+
.for("component")
|
|
149
|
+
.extend({
|
|
150
|
+
decorator: (platform: string): ClassDecorator => (target) => {
|
|
151
|
+
(target as any).__platform = platform;
|
|
152
|
+
},
|
|
153
|
+
args: ["web"],
|
|
154
|
+
})
|
|
155
|
+
.apply();
|
|
156
|
+
|
|
157
|
+
@decorate()
|
|
159
158
|
class WebComponent {}
|
|
160
159
|
|
|
161
|
-
|
|
160
|
+
(WebComponent as any).__platform; // "web"
|
|
162
161
|
```
|
|
163
162
|
|
|
164
|
-
3
|
|
165
|
-
|
|
166
|
-
Description: Replace default decorators entirely for flavour "mobile".
|
|
163
|
+
### 3. Override decorators for an alternate flavour
|
|
167
164
|
|
|
168
165
|
```ts
|
|
169
|
-
|
|
166
|
+
const base = () =>
|
|
167
|
+
Decoration.for("component")
|
|
168
|
+
.define(((target: any) => {
|
|
169
|
+
(target as any).__base = true;
|
|
170
|
+
}) as ClassDecorator)
|
|
171
|
+
.apply();
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
.define(((t: any) => { (t as any).__base = true; }) as ClassDecorator)
|
|
174
|
-
.apply();
|
|
175
|
-
|
|
176
|
-
// Flavour override (no extras needed)
|
|
177
|
-
Decoration.flavouredAs("mobile")
|
|
178
|
-
.for("component")
|
|
179
|
-
.define(((t: any) => { (t as any).__mobile = true; }) as ClassDecorator)
|
|
180
|
-
.apply();
|
|
173
|
+
@base()
|
|
174
|
+
class BaseBehaviour {}
|
|
181
175
|
|
|
182
176
|
Decoration.setFlavourResolver(() => "mobile");
|
|
183
|
-
const dec = Decoration.flavouredAs("mobile").for("component").apply();
|
|
184
177
|
|
|
185
|
-
|
|
178
|
+
const mobileComponent = () =>
|
|
179
|
+
Decoration.flavouredAs("mobile")
|
|
180
|
+
.for("component")
|
|
181
|
+
.define(((target: any) => {
|
|
182
|
+
(target as any).__mobile = true;
|
|
183
|
+
}) as ClassDecorator)
|
|
184
|
+
.apply();
|
|
185
|
+
|
|
186
|
+
@mobileComponent()
|
|
186
187
|
class MobileComponent {}
|
|
187
188
|
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
(MobileComponent as any).__base; // undefined – overridden
|
|
190
|
+
(MobileComponent as any).__mobile; // true
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 4. Enforce builder guard rails
|
|
194
|
+
|
|
195
|
+
The builder throws when misused; tests assert these guards and you can rely on them in your own code.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
const base = Decoration.for("guarded");
|
|
199
|
+
|
|
200
|
+
// Missing key before define/extend
|
|
201
|
+
expect(() => (new Decoration() as any).define(() => () => undefined)).toThrow();
|
|
202
|
+
|
|
203
|
+
// Multiple overridable decorators are rejected
|
|
204
|
+
const overridable = {
|
|
205
|
+
decorator: (() => ((target: any) => target)) as any,
|
|
206
|
+
args: [],
|
|
207
|
+
};
|
|
208
|
+
expect(() => base.define(overridable as any, overridable as any)).toThrow();
|
|
209
|
+
|
|
210
|
+
// Extending the default flavour is blocked
|
|
211
|
+
expect(() => Decoration.for("guarded").extend(((t: any) => t) as any)).toThrow();
|
|
190
212
|
```
|
|
191
213
|
|
|
192
|
-
Decorator
|
|
214
|
+
## Decorator Utilities
|
|
193
215
|
|
|
194
|
-
|
|
216
|
+
Helper factories under `@decaf-ts/decoration` push metadata into the shared store.
|
|
195
217
|
|
|
196
|
-
|
|
218
|
+
### metadata(key, value)
|
|
197
219
|
|
|
198
220
|
```ts
|
|
199
221
|
import { metadata, Metadata } from "@decaf-ts/decoration";
|
|
200
222
|
|
|
201
223
|
@metadata("role", "entity")
|
|
202
|
-
class User {
|
|
203
|
-
@((metadata("format", "email") as unknown) as PropertyDecorator)
|
|
204
|
-
email!: string;
|
|
205
|
-
}
|
|
224
|
+
class User {}
|
|
206
225
|
|
|
207
|
-
|
|
208
|
-
console.log(Metadata.get(User, "format")); // "email"
|
|
226
|
+
Metadata.get(User, "role"); // "entity"
|
|
209
227
|
```
|
|
210
228
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Description: Record the reflected design type for a property.
|
|
229
|
+
### prop()
|
|
214
230
|
|
|
215
231
|
```ts
|
|
216
|
-
import "reflect-metadata";
|
|
217
232
|
import { prop, Metadata } from "@decaf-ts/decoration";
|
|
218
233
|
|
|
219
234
|
class Article {
|
|
220
|
-
@
|
|
235
|
+
@prop()
|
|
221
236
|
title!: string;
|
|
222
237
|
}
|
|
223
238
|
|
|
224
|
-
|
|
239
|
+
Metadata.type(Article, "title") === String; // true
|
|
225
240
|
```
|
|
226
241
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
Description: Compose multiple decorators of different kinds.
|
|
242
|
+
### apply(...decorators)
|
|
230
243
|
|
|
231
244
|
```ts
|
|
232
245
|
import { apply } from "@decaf-ts/decoration";
|
|
233
246
|
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
247
|
+
const logClass: ClassDecorator = (target) => {
|
|
248
|
+
console.log("class", (target as any).name);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const withLogging = () => apply(logClass);
|
|
252
|
+
const logProperty = () => apply((_, key) => console.log("prop", String(key)));
|
|
237
253
|
|
|
238
|
-
@
|
|
254
|
+
@withLogging()
|
|
239
255
|
class Box {
|
|
240
|
-
@(
|
|
256
|
+
@logProperty()
|
|
241
257
|
size!: number;
|
|
242
|
-
|
|
243
|
-
@((apply(dMethod) as unknown) as MethodDecorator)
|
|
244
|
-
open() {}
|
|
245
258
|
}
|
|
246
259
|
```
|
|
247
260
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
Description: Combine setting arbitrary metadata and capturing the property's design type.
|
|
261
|
+
### propMetadata(key, value)
|
|
251
262
|
|
|
252
263
|
```ts
|
|
253
264
|
import { propMetadata, Metadata } from "@decaf-ts/decoration";
|
|
@@ -257,13 +268,11 @@ class Product {
|
|
|
257
268
|
price!: number;
|
|
258
269
|
}
|
|
259
270
|
|
|
260
|
-
|
|
261
|
-
|
|
271
|
+
Metadata.get(Product, "column"); // "price"
|
|
272
|
+
Metadata.type(Product, "price") === Number; // true
|
|
262
273
|
```
|
|
263
274
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
Description: Store human-readable documentation for class and property.
|
|
275
|
+
### description(text)
|
|
267
276
|
|
|
268
277
|
```ts
|
|
269
278
|
import { description, Metadata } from "@decaf-ts/decoration";
|
|
@@ -274,10 +283,93 @@ class User {
|
|
|
274
283
|
email!: string;
|
|
275
284
|
}
|
|
276
285
|
|
|
277
|
-
|
|
278
|
-
|
|
286
|
+
Metadata.description(User); // "User entity"
|
|
287
|
+
Metadata.description<User>(User, "email" as keyof User); // "Primary email address"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Metadata Runtime Helpers
|
|
291
|
+
|
|
292
|
+
`Metadata` centralises all recorded information. The snippets below exercise the same flows as `metadata.test.ts` and the integration suite.
|
|
293
|
+
|
|
294
|
+
### Set and read nested values with constructor mirroring
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { Metadata, DecorationKeys } from "@decaf-ts/decoration";
|
|
298
|
+
|
|
299
|
+
class Person {
|
|
300
|
+
name!: string;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Metadata.set(Person, `${DecorationKeys.DESCRIPTION}.class`, "Person model");
|
|
304
|
+
Metadata.set(Person, `${DecorationKeys.PROPERTIES}.name`, String);
|
|
305
|
+
|
|
306
|
+
Metadata.description(Person); // "Person model"
|
|
307
|
+
Metadata.properties(Person); // ["name"]
|
|
308
|
+
|
|
309
|
+
const mirror = Object.getOwnPropertyDescriptor(Person, DecorationKeys.REFLECT);
|
|
310
|
+
mirror?.enumerable; // false
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Opt out of mirroring
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
(Metadata as any).mirror = false;
|
|
317
|
+
|
|
318
|
+
Metadata.set(Person, `${DecorationKeys.DESCRIPTION}.class`, "No mirror");
|
|
319
|
+
Object.getOwnPropertyDescriptor(Person, DecorationKeys.REFLECT); // undefined
|
|
320
|
+
|
|
321
|
+
(Metadata as any).mirror = true; // reset when you are done
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Work with method metadata
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
class Service {
|
|
328
|
+
get(): string {
|
|
329
|
+
return "value";
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
Metadata.set(
|
|
334
|
+
Service,
|
|
335
|
+
`${DecorationKeys.METHODS}.get.${DecorationKeys.DESIGN_PARAMS}`,
|
|
336
|
+
[]
|
|
337
|
+
);
|
|
338
|
+
Metadata.set(
|
|
339
|
+
Service,
|
|
340
|
+
`${DecorationKeys.METHODS}.get.${DecorationKeys.DESIGN_RETURN}`,
|
|
341
|
+
String
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
Metadata.methods(Service); // ["get"]
|
|
345
|
+
Metadata.params(Service, "get"); // []
|
|
346
|
+
Metadata.return(Service, "get") === String; // true
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Leverage convenience accessors
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
Metadata.type(Person, "name"); // Reflects design type recorded by @prop()
|
|
353
|
+
Metadata.get(Person); // Full metadata payload for advanced inspection
|
|
354
|
+
Metadata.get(Person, DecorationKeys.CONSTRUCTOR); // Underlying constructor reference
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Library Registration
|
|
358
|
+
|
|
359
|
+
Prevent duplicate registration of flavour libraries via `Metadata.registerLibrary`.
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
import { Metadata } from "@decaf-ts/decoration";
|
|
363
|
+
|
|
364
|
+
Metadata.registerLibrary("@decaf-ts/decoration", "0.0.6");
|
|
365
|
+
|
|
366
|
+
expect(() =>
|
|
367
|
+
Metadata.registerLibrary("@decaf-ts/decoration", "0.0.6")
|
|
368
|
+
).toThrow(/already/);
|
|
279
369
|
```
|
|
280
370
|
|
|
371
|
+
You now have end-to-end examples for every public API: builder setup, decorator helpers, metadata management, and library bookkeeping. Mirror the test suite for additional inspiration when adding new patterns.
|
|
372
|
+
|
|
281
373
|
Metadata class
|
|
282
374
|
|
|
283
375
|
1) Set and get nested values
|