Skip to content

Commit 01eab03

Browse files
committed
feat: add automatic reflect-metadata injection to all bundles
Added automatic injection of `import 'reflect-metadata';` at the top of all client and server bundles via a new esbuild plugin. The plugin forces reflect-metadata to be bundled (not external), injects the import into entry points, and skips injection for view builds. Updated package.json templates to include reflect-metadata as a dependency.
1 parent 897fa82 commit 01eab03

7 files changed

Lines changed: 60 additions & 4 deletions

File tree

RELEASE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
## OpenCore CLI v0.5.0
1+
## OpenCore CLI v0.5.1
22

33
### Highlights
44
- **Smart Views Auto-Discovery**: The builder now automatically discovers UI/Views directories (`ui`, `view`, `views`, `web`, `html`) within resources and standalones, detecting the framework and configuring build tasks without manual intervention.
5+
- **Reflect-Metadata Injection**: Added automatic injection of `import 'reflect-metadata';` at the top of all client and server bundles.
56

67
### Changes
78
- **Views Discovery Logic**: Implemented `findViewsPath` to search for common UI directory conventions.
89
- **Enhanced Task Generation**: Integrated auto-discovery into both glob-based and explicit resource/standalone collection.
10+
- **Global Decorator Support**: Injected `reflect-metadata` via esbuild banners for server/client builds.
911

1012
## OpenCore CLI v0.5.0
1113

internal/builder/embedded/build_functions.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
const path = require('path')
22
const fs = require('fs')
3-
const { getEsbuild, createSwcPlugin, createExcludeNodeAdaptersPlugin, createExternalPackagesPlugin, preserveFiveMExportsPlugin, createNodeGlobalsShimPlugin, createTsconfigPathsPlugin } = require('./plugins')
3+
const { getEsbuild, createSwcPlugin, createExcludeNodeAdaptersPlugin, createExternalPackagesPlugin, preserveFiveMExportsPlugin, createNodeGlobalsShimPlugin, createTsconfigPathsPlugin, createReflectMetadataPlugin } = require('./plugins')
44
const { getSharedConfig, getBuildOptions, getExternals } = require('./config')
55
const { handleDependencies, shouldHandleDependencies, detectNativePackages, printNativePackageWarnings } = require('./dependencies')
66

77
function getCorePlugins(isServerBuild = false, externals = [], target = 'es2020', format = 'iife', resourcePath = null) {
88
const plugins = [
9+
createReflectMetadataPlugin(),
910
createExternalPackagesPlugin(externals),
1011
createSwcPlugin(target),
1112
createExcludeNodeAdaptersPlugin(isServerBuild),
@@ -26,6 +27,7 @@ function getCorePlugins(isServerBuild = false, externals = [], target = 'es2020'
2627

2728
function getResourcePlugins(isServerBuild = false, externals = [], target = 'es2020', format = 'iife', resourcePath = null) {
2829
const plugins = [
30+
createReflectMetadataPlugin(),
2931
createExternalPackagesPlugin(externals),
3032
createSwcPlugin(target),
3133
createExcludeNodeAdaptersPlugin(isServerBuild),
@@ -45,6 +47,7 @@ function getResourcePlugins(isServerBuild = false, externals = [], target = 'es2
4547

4648
function getStandalonePlugins(isServerBuild = false, externals = [], target = 'es2020', format = 'iife', resourcePath = null) {
4749
const plugins = [
50+
createReflectMetadataPlugin(),
4851
createExternalPackagesPlugin(externals),
4952
createSwcPlugin(target),
5053
createExcludeNodeAdaptersPlugin(isServerBuild),

internal/builder/embedded/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function getSharedConfig(options = {}) {
2121
supported: {
2222
'class-static-blocks': false,
2323
},
24-
alias: {}
24+
alias: {},
2525
}
2626

2727
return config

internal/builder/embedded/plugins.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,58 @@ function createTsconfigPathsPlugin(resourcePath) {
321321
}
322322
}
323323

324+
function createReflectMetadataPlugin() {
325+
return {
326+
name: 'reflect-metadata-injector',
327+
setup(build) {
328+
// Force reflect-metadata to be bundled even if marked as external
329+
build.onResolve({ filter: /^reflect-metadata$/ }, () => {
330+
try {
331+
return { path: require.resolve('reflect-metadata'), external: false }
332+
} catch (e) {
333+
return { errors: [{ text: 'reflect-metadata not found. Please install it with: pnpm add reflect-metadata' }] }
334+
}
335+
})
336+
337+
// Inject import at the top of the entry point
338+
build.onLoad({ filter: /\.(ts|tsx|js|jsx)$/ }, async (args) => {
339+
// Skip node_modules
340+
if (args.path.includes('node_modules')) return null
341+
342+
// Only inject into the main entry points (client/server main files)
343+
const isEntry = build.initialOptions.entryPoints.some(e =>
344+
path.resolve(e) === path.resolve(args.path)
345+
)
346+
347+
if (!isEntry) return null
348+
349+
try {
350+
const contents = await fs.promises.readFile(args.path, 'utf8')
351+
if (contents.includes('reflect-metadata')) return null
352+
353+
const ext = path.extname(args.path).slice(1)
354+
// If it's TS, use 'ts' or 'tsx' loader, otherwise esbuild will fail
355+
const loader = ext === 'ts' || ext === 'tsx' ? ext : 'js'
356+
357+
return {
358+
contents: `import 'reflect-metadata';\n${contents}`,
359+
loader: loader,
360+
}
361+
} catch (e) {
362+
return null
363+
}
364+
})
365+
},
366+
}
367+
}
368+
324369
module.exports = {
325370
getEsbuild,
326371
createSwcPlugin,
327372
createExcludeNodeAdaptersPlugin,
328373
createExternalPackagesPlugin,
329374
preserveFiveMExportsPlugin,
330375
createNodeGlobalsShimPlugin,
331-
createTsconfigPathsPlugin
376+
createTsconfigPathsPlugin,
377+
createReflectMetadataPlugin
332378
}

internal/builder/embedded/views.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ async function buildViews(viewPath, outDir, options = {}) {
329329

330330
await esbuild.build({
331331
...shared,
332+
banner: {
333+
js: "", // No reflect-metadata for views
334+
},
332335
entryPoints: [entryPoint],
333336
outdir: outDir,
334337
platform: 'browser',

internal/templates/resource/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"build": "tsc"
88
},
99
"dependencies": {
10+
"reflect-metadata": "^0.2.2",
1011
"@open-core/framework": "latest"
1112
},
1213
"devDependencies": {

internal/templates/starter-project/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"dependencies": {
1818
"@citizenfx/client": "2.0.22443-1",
1919
"@citizenfx/server": "2.0.22443-1",
20+
"reflect-metadata": "^0.2.2",
2021
"@open-core/framework": "latest"{{if .InstallIdentity}},
2122
"@open-core/identity": "latest"{{end}}
2223
}

0 commit comments

Comments
 (0)