@@ -65,7 +65,114 @@ yarn add @flex-development/ext-regex@flex-development/ext-regex
65
65
66
66
## Use
67
67
68
- ** TODO** : usage example.
68
+ ``` typescript
69
+ import { DECORATOR_REGEX } from ' @flex-development/decorator-regex'
70
+ import { EXT_DTS_REGEX , EXT_TS_REGEX } from ' @flex-development/ext-regex'
71
+ import * as mlly from ' @flex-development/mlly'
72
+ import pathe from ' @flex-development/pathe'
73
+ import * as tscu from ' @flex-development/tsconfig-utils'
74
+ import type { Nullable } from ' @flex-development/tutils'
75
+ import type {
76
+ OnLoadArgs ,
77
+ OnLoadOptions ,
78
+ OnLoadResult ,
79
+ Plugin ,
80
+ PluginBuild
81
+ } from ' esbuild'
82
+ import type { URL } from ' node:url'
83
+
84
+ /**
85
+ * Returns a plugin that allows esbuild to handle [`emitDecoratorMetadata`][1].
86
+ *
87
+ * [1]: https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata
88
+ *
89
+ * @param {tscu.LoadTsconfigOptions?} [options] - Plugin options
90
+ * @return {Plugin} Decorator metadata plugin
91
+ */
92
+ const plugin = (options ? : tscu .LoadTsconfigOptions ): Plugin => ({
93
+ name: ' decorators' ,
94
+ setup : async ({ initialOptions , onLoad }: PluginBuild ): Promise <void > => {
95
+ const { absWorkingDir = ' .' , tsconfig = ' tsconfig.json' } = initialOptions
96
+
97
+ /**
98
+ * User compiler options.
99
+ *
100
+ * @const {tscu.CompilerOptions} compilerOptions
101
+ */
102
+ const compilerOptions: tscu .CompilerOptions = tscu .loadCompilerOptions (
103
+ pathe .resolve (absWorkingDir , tsconfig ),
104
+ options
105
+ )
106
+
107
+ // exit early if decorator metadata should not be emitted
108
+ if (! compilerOptions .emitDecoratorMetadata ) return void 0
109
+
110
+ /**
111
+ * TypeScript module.
112
+ *
113
+ * @const {typeof import('typescript')} ts
114
+ */
115
+ const ts: typeof import (' typescript' ) = (await import (' typescript' )).default
116
+
117
+ /**
118
+ * {@linkcode onLoad } callback options.
119
+ *
120
+ * @const {OnLoadOptions}
121
+ */
122
+ const opts: OnLoadOptions = { filter: / . * / }
123
+
124
+ // transpile typescript modules containing decorators
125
+ onLoad (opts , async (args : OnLoadArgs ): Promise <Nullable <OnLoadResult >> => {
126
+ /**
127
+ * Callback result.
128
+ *
129
+ * @var {Nullable<OnLoadResult>} result
130
+ */
131
+ let result: Nullable <OnLoadResult > = null
132
+
133
+ // transpile typescript modules, but skip typescript declaration modules
134
+ if (EXT_TS_REGEX .test (args .path ) && ! EXT_DTS_REGEX .test (args .path )) {
135
+ /**
136
+ * URL of module to load.
137
+ *
138
+ * @const {URL} url
139
+ */
140
+ const url: URL = mlly .toURL (args .path )
141
+
142
+ /**
143
+ * File content at {@linkcode args.path } .
144
+ *
145
+ * @const {string} source
146
+ */
147
+ const source: string = (await mlly .getSource (url )) as string
148
+
149
+ // do nothing if module does not use decorators
150
+ if (! DECORATOR_REGEX .test (source )) return null
151
+
152
+ // transpile module to emit decorator metadata
153
+ const { outputText : contents } = ts .transpileModule (source , {
154
+ compilerOptions: tscu .normalizeCompilerOptions (compilerOptions )
155
+ })
156
+
157
+ result = { contents }
158
+ }
159
+
160
+ return result
161
+ })
162
+
163
+ return void 0
164
+ }
165
+ })
166
+
167
+ export default plugin
168
+ ```
169
+
170
+ <blockquote >
171
+ <small >
172
+ Looking for a plugin like this? Check out
173
+ <a href='https://github.com/flex-development/mkbuild'><code>mkbuild</code></a> 😉
174
+ </small >
175
+ </blockquote >
69
176
70
177
## API
71
178
0 commit comments