TypeScript Resource Management
This guide covers everything you need to know about building and managing TypeScript resources with BJJ FX Manager.
Table of Contents
- Why This Matters
- Automatic Detection
- Build Process
- Isolated Build Environments
- Resource Structure
- Configuration Examples
- Build Performance
- Troubleshooting Builds
Why This Matters
FiveM's ecosystem has rapidly adopted TypeScript for resource development, but the platform provides no native automation for TypeScript compilation. Before BJJ FXM, developers had to:
- Manually connect to servers (SSH on Linux, PowerShell/RDP on Windows)
- Navigate to each resource directory
- Run
npm installandnpm run buildindividually - Manage Node.js versions and build tools system-wide
- Restart the server manually after builds
- Deal with contaminated system environments from build dependencies
BJJ FXM automates this entire workflow while maintaining system cleanliness through isolated build contexts.
Automatic Detection
BJJ FXM automatically identifies TypeScript resources by scanning for:
.tsfiles in resource directoriestsconfig.jsonconfiguration filespackage.jsonwith TypeScript dependencies- Custom build scripts defined in
package.json
Detection Process
When you run fxm build or fxm start, BJJ FXM:
- Scans the resources directory
- Identifies all directories containing TypeScript files
- Checks for configuration files
- Determines the appropriate build method
- Queues resources for building
Build Process
BJJ FXM executes an intelligent build pipeline for each TypeScript resource:
Phase 1: Dependency Management
Checks:
- Looks for
package.json - Detects package manager (npm/yarn/pnpm) by lock files
- Checks if
node_modulesexists
Actions:
- Installs dependencies if
node_modulesis missing - Uses the appropriate package manager
- Runs in isolated context
- Logs installation progress
Phase 2: Build Execution
Priority Order:
- Custom build script - Uses
npm run buildif defined - Direct TypeScript compilation - Uses
npx tsciftsconfig.jsonexists - Fallback compilation - Generates basic
tsconfig.jsonand compiles
Build Command Examples:
# Custom script (if package.json has "build" script)
npm run build
# Direct compilation (if tsconfig.json exists)
npx tsc
# Fallback (generates config and compiles)
npx tsc --init && npx tsc
Phase 3: Validation
Checks:
- Verifies build output exists
- Checks for compilation errors
- Validates file permissions
- Confirms resource structure
Actions:
- Logs build results
- Reports errors with context
- Updates resource status
- Prepares for server start
Isolated Build Environments
Critical for production servers on both Linux and Windows: All build operations run in separate Node.js processes that terminate after completion.
Benefits
System Cleanliness:
- No global npm package pollution
- No persistent build tool processes
- Clean system Node environment
Performance:
- Build processes don't consume memory after completion
- No background build tool daemons
- Resources free immediately after build
Reliability:
- No dependency conflicts between resources
- Each resource builds in isolation
- Failures don't affect other resources
How It Works
// Conceptual overview - BJJ FXM's build process
for (const resource of typescriptResources) {
// Spawn isolated Node process
const buildProcess = spawn('node', [buildScript], {
cwd: resource.path,
env: cleanEnvironment
});
// Wait for completion
await buildProcess;
// Process terminates, memory freed
}
Resource Structure
Recommended Directory Layout
Organize TypeScript resources for optimal BJJ FXM integration:
resources/
├── my-typescript-resource/
│ ├── src/ # TypeScript source files
│ │ ├── client/
│ │ │ ├── index.ts
│ │ │ └── controllers/
│ │ ├── server/
│ │ │ ├── index.ts
│ │ │ └── services/
│ │ └── shared/
│ │ ├── types.ts
│ │ ├── config.ts
│ │ └── utils.ts
│ ├── dist/ # Build output (git-ignored)
│ │ ├── client/
│ │ │ └── index.js
│ │ └── server/
│ │ └── index.js
│ ├── fxmanifest.lua # FiveM resource manifest
│ ├── package.json # Dependencies and build scripts
│ ├── tsconfig.json # TypeScript configuration
│ └── .gitignore # Ignore dist/ and node_modules/
Multi-Project Structure
For complex resources with web UI:
resources/
├── my-fullstack-resource/
│ ├── src/ # Server-side TypeScript
│ │ ├── client/
│ │ └── server/
│ ├── web/ # React/Vue/etc. UI
│ │ ├── src/
│ │ ├── public/
│ │ └── package.json
│ ├── dist/ # Server build output
│ ├── web-dist/ # UI build output
│ ├── fxmanifest.lua
│ ├── package.json
│ └── tsconfig.json
Configuration Examples
Basic package.json
{
"name": "my-fivem-resource",
"version": "1.0.0",
"description": "My awesome FiveM resource",
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"clean": "rm -rf dist"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}
Advanced package.json with Custom Build
{
"name": "advanced-resource",
"version": "2.0.0",
"scripts": {
"prebuild": "npm run clean",
"build": "npm run build:client && npm run build:server",
"build:client": "tsc -p tsconfig.client.json",
"build:server": "tsc -p tsconfig.server.json",
"postbuild": "npm run copy-assets",
"clean": "rm -rf dist",
"copy-assets": "cp -r static/* dist/",
"watch": "concurrently \"npm run build:client -- --watch\" \"npm run build:server -- --watch\""
},
"devDependencies": {
"@citizenfx/client": "^2.0.0",
"@citizenfx/server": "^2.0.0",
"@types/node": "^20.0.0",
"concurrently": "^8.0.0",
"typescript": "^5.0.0"
}
}
Basic tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Advanced tsconfig.json with Path Mapping
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@shared/*": ["src/shared/*"],
"@client/*": ["src/client/*"],
"@server/*": ["src/server/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts"]
}
Separate Client/Server Configs
tsconfig.client.json:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist/client",
"rootDir": "./src/client",
"lib": ["ES2020", "DOM"]
},
"include": ["src/client/**/*", "src/shared/**/*"]
}
tsconfig.server.json:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist/server",
"rootDir": "./src/server"
},
"include": ["src/server/**/*", "src/shared/**/*"]
}
Build Performance
Optimization Tips
1. Incremental Compilation
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./dist/.tsbuildinfo"
}
}
2. Skip Lib Check
{
"compilerOptions": {
"skipLibCheck": true
}
}
3. Parallel Builds BJJ FXM automatically builds independent resources in parallel.
4. Watch Mode for Development
# Automatic rebuilds on file changes
fxm build --watch
Build Time Expectations
| Resource Size | Files | Build Time | Watch Rebuild |
|---|---|---|---|
| Small | <10 | 5-15 sec | 1-3 sec |
| Medium | 10-50 | 15-45 sec | 2-5 sec |
| Large | 50+ | 45-90 sec | 5-10 sec |
Troubleshooting Builds
Common Build Errors
Error: Cannot find module
Solution: Install missing dependencies
cd resources/your-resource
npm install
Error: Build timeout
Solution: Increase timeout in fxm.config.yml
resources:
buildTimeout: 120000 # 2 minutes
Error: Permission denied
Solution: Fix file permissions
# Linux:
chmod -R 755 resources/your-resource
# Windows: Right-click the folder → Properties → Security, or use icacls
Error: TSC not found
Solution: Install TypeScript in resource
cd resources/your-resource
npm install --save-dev typescript
Debug Build Issues
Enable verbose logging:
# fxm.config.yml
logLevel: debug
Manual build test:
cd resources/your-resource
npm install
npm run build # or npx tsc
Check build logs:
cat logs/build.log | grep ERROR
Resource-Specific Build Configuration
Create .fxmrc.json in resource directory:
{
"buildTimeout": 120000,
"buildCommand": "npm run custom-build",
"watchIgnore": ["*.log", "temp/*"],
"preBuildScript": "./scripts/prepare.sh",
"postBuildScript": "./scripts/finalize.sh"
}
Best Practices
1. Use Proper .gitignore
# Build outputs
dist/
*.js
*.js.map
# Dependencies
node_modules/
package-lock.json
yarn.lock
# TypeScript cache
*.tsbuildinfo
# IDE
.vscode/
.idea/
# Logs
*.log
2. Separate Source and Build
Always keep TypeScript source in src/ and output in dist/:
- Cleaner project structure
- Easier to exclude from git
- Clear separation of concerns
3. Version Lock Dependencies
Use exact versions for critical dependencies:
{
"devDependencies": {
"typescript": "5.3.0"
}
}
4. Document Build Process
Add README to complex resources:
# My Resource
## Building
npm install
npm run build
## Development
npm run watch
5. Test Locally Before Deployment
# Local build test
fxm build
# Verify output (Linux)
ls -la resources/my-resource/dist/
# Verify output (Windows)
dir resources\my-resource\dist\