@@ -65,7 +65,114 @@ yarn add @flex-development/ext-regex@flex-development/ext-regex
6565
6666## Use
6767
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 >
69176
70177## API
71178
0 commit comments