Skip to content

Commit bbe35a1

Browse files
committed
feat(helper): add macroable helper
1 parent 335c43a commit bbe35a1

29 files changed

+624
-33
lines changed

bin/test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
*/
99

1010
import { Runner } from '@athenna/test'
11+
import { expectTypeOf } from '@japa/expect-type'
1112

1213
await Runner.setTsEnv()
1314
.addAssertPlugin()
15+
.addPlugin(expectTypeOf())
1416
.addPath('tests/unit/**/*.ts')
1517
.setCliArgs(process.argv.slice(2))
1618
.setGlobalTimeout(5000)

package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
"devDependencies": {
109109
"@athenna/test": "^5.2.0",
110110
"@athenna/tsconfig": "^5.0.0",
111+
"@japa/expect-type": "^2.0.3",
111112
"@types/bytes": "^3.1.5",
112113
"@types/callsite": "^1.0.34",
113114
"@types/deasync": "^0.1.5",

src/helpers/Clean.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
import { Is } from '#src/helpers/Is'
1111
import { Options } from '#src/helpers/Options'
12+
import { Macroable } from '#src/helpers/Macroable'
1213

13-
export class Clean {
14+
export class Clean extends Macroable {
1415
/**
1516
* Remove all falsy values from an array.
1617
*/

src/helpers/Collection.ts

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,119 @@ import { Collection as CollectJS } from 'collect.js'
1111

1212
export class Collection<T = any> extends CollectJS<T> {
1313
/**
14-
* An alias for macro instance method:
14+
* Add standard property to your class prototype.
1515
*
1616
* @example
17-
* Collection.macro('upperAndTrim', (value) => {
18-
* return value.trim().toUpperCase()
19-
* })
17+
* ```ts
18+
* YourClass.macro('foo', 'bar')
19+
* ```
20+
*/
21+
public static macro<K extends keyof Collection>(
22+
name: K,
23+
value: Collection[K]
24+
) {
25+
this.prototype[name] = value
26+
}
27+
28+
/**
29+
* Add getters to the class prototype using the Object.defineProperty()
30+
* method.
31+
*
32+
* @example
33+
* ```ts
34+
* YourClass.getter('foo', function foo () {
35+
* return 'bar'
36+
* })
37+
*
38+
* const singleton = true
39+
*
40+
* // Add singleton getter:
41+
* YourClass.getter('foo', function foo() {
42+
* return 'bar'
43+
* }, singleton)
44+
* ```
2045
*/
21-
public static macro(name: string, fn: any): void {
22-
return new Collection().macro(name, fn)
46+
public static getter<K extends keyof Collection>(
47+
name: K,
48+
accumulator: () => Collection[K],
49+
singleton: boolean = false
50+
) {
51+
Object.defineProperty(this.prototype, name, {
52+
get() {
53+
const value = accumulator.call(this)
54+
55+
if (singleton) {
56+
Object.defineProperty(this, name, {
57+
configurable: false,
58+
enumerable: false,
59+
value,
60+
writable: false
61+
})
62+
}
63+
64+
return value
65+
},
66+
configurable: true,
67+
enumerable: false
68+
})
69+
}
70+
71+
/**
72+
* Add a standard static property to the class itself.
73+
*
74+
* @example
75+
* ```ts
76+
* YourClass.staticMacro('foo', 'bar')
77+
* ```
78+
*/
79+
public static staticMacro<K extends keyof Collection>(
80+
name: K,
81+
value: Collection[K]
82+
) {
83+
Object.defineProperty(this, name, {
84+
value,
85+
writable: true,
86+
enumerable: true,
87+
configurable: true
88+
})
89+
}
90+
91+
/**
92+
* Add static getters to the class itself using Object.defineProperty().
93+
*
94+
* @example
95+
* ```ts
96+
* YourClass.staticGetter('foo', () => 'bar')
97+
*
98+
* const singleton = true
99+
*
100+
* // Add singleton static getter:
101+
* YourClass.staticGetter('foo', () => 'bar', singleton)
102+
* ```
103+
*/
104+
public static staticGetter<K extends keyof Collection>(
105+
name: K,
106+
accumulator: () => Collection[K],
107+
singleton: boolean = false
108+
) {
109+
Object.defineProperty(this, name, {
110+
get: function () {
111+
const value = accumulator.call(this)
112+
113+
if (singleton) {
114+
Object.defineProperty(this, name, {
115+
configurable: false,
116+
enumerable: true,
117+
value,
118+
writable: false
119+
})
120+
}
121+
122+
return value
123+
},
124+
configurable: true,
125+
enumerable: true
126+
})
23127
}
24128

25129
/**

src/helpers/Color.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
import { format } from 'node:util'
1111
import { Is } from '#src/helpers/Is'
1212
import { Chalk, type ChalkInstance } from 'chalk'
13+
import { Macroable } from '#src/helpers/Macroable'
1314

14-
export class Color {
15+
export class Color extends Macroable {
1516
/**
1617
* Chalk instance.
1718
*/

src/helpers/Enum.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
* file that was distributed with this source code.
88
*/
99

10-
export class Enum {
10+
import { Macroable } from '#src/helpers/Macroable'
11+
12+
export class Enum extends Macroable {
1113
/**
1214
* Get all keys from enum.
1315
*

src/helpers/Exec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ import { Transform } from 'node:stream'
2222
import { File } from '#src/helpers/File'
2323
import { Path } from '#src/helpers/Path'
2424
import { Options } from '#src/helpers/Options'
25+
import { Macroable } from '#src/helpers/Macroable'
2526
import { request as requestHttp } from 'node:http'
2627
import { request as requestHttps } from 'node:https'
2728
import { execa, execaNode, execaCommand, type ExecaChildProcess } from 'execa'
2829

29-
export class Exec {
30+
export class Exec extends Macroable {
3031
/**
3132
* Sleep the code in the line that this function
3233
* is being called.

src/helpers/FakeApi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ import { File } from '#src/helpers/File'
1515
import { Path } from '#src/helpers/Path'
1616
import { Json } from '#src/helpers/Json'
1717
import { Folder } from '#src/helpers/Folder'
18+
import { Macroable } from '#src/helpers/Macroable'
1819
import type { FastifyInstance, HTTPMethods, RouteOptions } from 'fastify'
1920

20-
export class FakeApi {
21+
export class FakeApi extends Macroable {
2122
/**
2223
* Set if the FakeApi server is running.
2324
*/

src/helpers/File.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import prependFile from 'prepend-file'
1111

1212
import type { StreamOptions } from 'node:stream'
13+
import { Macroable } from '#src/helpers/Macroable'
1314
import type { FileJson, ObjectBuilderOptions } from '#src/types'
1415

1516
import {
@@ -40,7 +41,7 @@ import { isAbsolute, parse, sep } from 'node:path'
4041
import { Json, ObjectBuilder } from '#src/helpers/Json'
4142
import { NotFoundFileException } from '#src/exceptions/NotFoundFileException'
4243

43-
export class File {
44+
export class File extends Macroable {
4445
/**
4546
* The original or faked file directory.
4647
*/
@@ -147,6 +148,8 @@ export class File {
147148
mockedValues = false,
148149
isCopy = false
149150
) {
151+
super()
152+
150153
const { ext, dir, name, base, mime, path } = File.parsePath(filePath)
151154

152155
this.originalDir = dir

0 commit comments

Comments
 (0)