@jpp-toolkit/plugin-build-fivem 0.0.18 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -6,7 +6,6 @@ declare class FivemBuildCommand extends Command {
6
6
  static summary: string;
7
7
  static flags: {
8
8
  watch: _oclif_core_interfaces0.BooleanFlag<boolean>;
9
- env: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
10
9
  autoReload: _oclif_core_interfaces0.BooleanFlag<boolean>;
11
10
  server: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
12
11
  password: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
package/dist/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import path from "node:path";
2
2
  import { Command } from "@jpp-toolkit/core";
3
- import { createFivemRspackConfig } from "@jpp-toolkit/rspack-config";
4
- import { getErrMsg } from "@jpp-toolkit/utils";
3
+ import { createFivemScriptRspackConfig, createFivemUiRspackConfig } from "@jpp-toolkit/rspack-config";
4
+ import { debounce, getErrMsg } from "@jpp-toolkit/utils";
5
5
  import { Flags } from "@oclif/core";
6
6
  import { rspack } from "@rspack/core";
7
+ import { RspackDevServer } from "@rspack/dev-server";
7
8
  import dgram from "node:dgram";
8
9
 
9
10
  //#region src/fivem-rcon.ts
@@ -58,12 +59,6 @@ var FivemBuildCommand = class FivemBuildCommand extends Command {
58
59
  description: "Watch files for changes and rebuild automatically.",
59
60
  default: false
60
61
  }),
61
- env: Flags.string({
62
- char: "e",
63
- description: "Set the build environment (development or production).",
64
- options: ["development", "production"],
65
- required: false
66
- }),
67
62
  autoReload: Flags.boolean({
68
63
  char: "r",
69
64
  description: "Automatically reload FiveM resource after build.",
@@ -95,10 +90,6 @@ var FivemBuildCommand = class FivemBuildCommand extends Command {
95
90
  description: "Build the FiveM resource in watch mode.",
96
91
  command: "<%= config.bin %> <%= command.id %> --watch"
97
92
  },
98
- {
99
- description: "Build the FiveM resource in production environment.",
100
- command: "<%= config.bin %> <%= command.id %> --env=production"
101
- },
102
93
  {
103
94
  description: "Build the FiveM resource and automatically reload it on the server after build.",
104
95
  command: "<%= config.bin %> <%= command.id %> --auto-reload --password your_rcon_password"
@@ -113,42 +104,61 @@ var FivemBuildCommand = class FivemBuildCommand extends Command {
113
104
  }
114
105
  ];
115
106
  async run() {
116
- const { env, resourceName, rconOptions, watch } = await this._parseOptions();
117
- const compiler = rspack(createFivemRspackConfig({
118
- environment: env,
119
- resourceName
107
+ const { resourceName, rconOptions, watch } = await this._parseOptions();
108
+ const scriptCompiler = rspack(createFivemScriptRspackConfig(void 0, { isProduction: !watch })({
109
+ RSPACK_BUILD: !watch,
110
+ RSPACK_WATCH: watch
120
111
  }));
121
- const reloadFivemResource = async () => {
112
+ const uiConfig = createFivemUiRspackConfig({ resourceName }, { isProduction: !watch })({
113
+ RSPACK_BUILD: !watch,
114
+ RSPACK_SERVE: watch
115
+ });
116
+ const uiCompiler = rspack(uiConfig);
117
+ const reloadFivemResource = debounce(async () => {
122
118
  if (!rconOptions) return;
119
+ this.logger.log("");
123
120
  this.logger.info(`Reloading FiveM resource "${resourceName}"...`);
124
121
  try {
125
122
  await refreshAndEnsureFivemResource(resourceName, rconOptions);
126
123
  this.logger.success(`FiveM resource reloaded successfully.\n`);
127
124
  } catch (error) {
128
- this.logger.error(`Failed to reload FiveM resource: ${getErrMsg(error)}\n`);
125
+ this.logger.error(`Failed to reload FiveM resource: ${error}\n`);
129
126
  }
130
- };
127
+ }, 500);
131
128
  const compilerCallback = (err, stats) => {
132
129
  if (err) {
133
- this.logger.error(getErrMsg(err));
134
- if ("details" in err) this.logger.error(err.details);
135
- if ("stack" in err) this.logger.error(err.stack);
130
+ this.logger.error(err.toString());
136
131
  return;
137
132
  }
138
- if (stats) this.logger.log(stats.toString(true), "\n");
133
+ if (!stats) return;
134
+ this.logger.log(stats.toString({
135
+ preset: "normal",
136
+ colors: true
137
+ }), "\n");
139
138
  reloadFivemResource();
140
139
  };
141
140
  if (watch) {
142
- this.logger.info(`Building FiveM resource in watch mode...\n`);
143
- compiler.watch({}, compilerCallback);
141
+ const devServerOptions = uiConfig.devServer ?? {};
142
+ devServerOptions.hot = true;
143
+ await new RspackDevServer(devServerOptions, uiCompiler).start();
144
+ scriptCompiler.watch({}, compilerCallback);
144
145
  } else {
145
- this.logger.info(`Building FiveM resource...\n`);
146
- compiler.run(compilerCallback);
146
+ uiCompiler.run((error, stats) => {
147
+ uiCompiler.close((closeErr) => {
148
+ if (closeErr) this.logger.error(closeErr.toString());
149
+ compilerCallback(error, stats);
150
+ });
151
+ });
152
+ scriptCompiler.run((error, stats) => {
153
+ scriptCompiler.close((closeErr) => {
154
+ if (closeErr) this.logger.error(closeErr.toString());
155
+ compilerCallback(error, stats);
156
+ });
157
+ });
147
158
  }
148
159
  }
149
160
  async _parseOptions() {
150
161
  const { flags } = await this.parse(FivemBuildCommand);
151
- const env = flags.env ?? (flags.watch ? "development" : "production");
152
162
  const resourceName = flags.resourceName ?? path.basename(process.cwd());
153
163
  let rconOptions;
154
164
  if (flags.autoReload) {
@@ -162,7 +172,6 @@ var FivemBuildCommand = class FivemBuildCommand extends Command {
162
172
  };
163
173
  }
164
174
  return {
165
- env,
166
175
  resourceName,
167
176
  rconOptions,
168
177
  watch: flags.watch
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["timeoutId: NodeJS.Timeout","rconOptions: RconOptions | undefined"],"sources":["../src/fivem-rcon.ts","../src/fivem-build-command.ts","../src/index.ts"],"sourcesContent":["import dgram from 'node:dgram';\n\nimport { getErrMsg } from '@jpp-toolkit/utils';\n\nexport type RconOptions = {\n readonly host: string;\n readonly port: number;\n readonly password: string;\n readonly timeout?: number | undefined;\n};\n\nfunction buildCommand(command: string, password: string): Buffer {\n const buffer = Buffer.alloc(11 + password.length + command.length);\n buffer.writeUInt32LE(0xffffffff, 0);\n buffer.write('rcon ', 4);\n buffer.write(password, 9, password.length);\n buffer.write(' ', 9 + password.length, 1);\n buffer.write(command, 10 + password.length, command.length);\n buffer.write('\\n', 10 + password.length + command.length, 1);\n return buffer;\n}\n\nexport async function sendFivemRcon(command: string, options: RconOptions): Promise<string> {\n const socket = dgram.createSocket('udp4');\n\n return new Promise<string>((resolve, reject) => {\n let timeoutId: NodeJS.Timeout;\n\n const handleError = (err?: Error | string | null) => {\n if (!err) return;\n clearTimeout(timeoutId);\n const msg = getErrMsg(err);\n reject(new Error(`Failed to send command to ${options.host}:${options.port}: ${msg}`));\n };\n\n const handleMessage = (msg: Buffer) => {\n clearTimeout(timeoutId);\n const res = msg.toString('ascii').slice(4).trim();\n if (res.includes('Invalid password')) return void handleError('Invalid password');\n resolve(res);\n };\n\n timeoutId = setTimeout(\n () => handleError(`Timeout after ${options.timeout}ms`),\n options.timeout ?? 5000,\n );\n\n socket.once('error', handleError);\n socket.once('message', handleMessage);\n\n const cmd = buildCommand(command, options.password);\n socket.send(cmd, 0, cmd.length, options.port, options.host, handleError);\n }).finally(() => {\n socket.close();\n });\n}\n\nexport async function refreshAndEnsureFivemResource(\n resourceName: string,\n options: RconOptions,\n): Promise<void> {\n const res = await sendFivemRcon(`refresh; ensure ${resourceName}`, options);\n if (res.includes(\"Couldn't find resource\"))\n throw new Error(`Resource \"${resourceName}\" not found`);\n if (!res.includes('Started resource'))\n throw new Error(`Failed to start resource \"${resourceName}\"`);\n}\n","import path from 'node:path';\n\nimport { Command } from '@jpp-toolkit/core';\nimport { createFivemRspackConfig } from '@jpp-toolkit/rspack-config';\nimport { getErrMsg } from '@jpp-toolkit/utils';\nimport { Flags } from '@oclif/core';\nimport type { MultiStats } from '@rspack/core';\nimport { rspack } from '@rspack/core';\n\nimport { refreshAndEnsureFivemResource } from './fivem-rcon';\nimport type { RconOptions } from './fivem-rcon';\n\ntype Environment = 'development' | 'production';\n\ntype FivemBuildCommandOptions = {\n readonly env: Environment;\n readonly resourceName: string;\n readonly rconOptions?: RconOptions | undefined;\n readonly watch: boolean;\n};\n\nexport class FivemBuildCommand extends Command {\n static override summary = 'Build the FiveM resource using predefined config.';\n\n static override flags = {\n watch: Flags.boolean({\n char: 'w',\n description: 'Watch files for changes and rebuild automatically.',\n default: false,\n }),\n env: Flags.string({\n char: 'e',\n description: 'Set the build environment (development or production).',\n options: ['development', 'production'],\n required: false,\n }),\n autoReload: Flags.boolean({\n char: 'r',\n description: 'Automatically reload FiveM resource after build.',\n default: false,\n }),\n server: Flags.string({\n char: 's',\n description: 'Server \"ip:port\" to connect for reloading FiveM resource after build.',\n default: '127.0.0.1:30120',\n required: false,\n }),\n password: Flags.string({\n char: 'p',\n description: 'RCON password for the FiveM server to reload resource after build.',\n required: false,\n }),\n resourceName: Flags.string({\n char: 'n',\n description:\n 'Name of the FiveM resource to reload. If not provided, the name of the folder containing the resource will be used.',\n required: false,\n }),\n };\n\n static override examples = [\n {\n description: 'Build the FiveM resource.',\n command: '<%= config.bin %> <%= command.id %>',\n },\n {\n description: 'Build the FiveM resource in watch mode.',\n command: '<%= config.bin %> <%= command.id %> --watch',\n },\n {\n description: 'Build the FiveM resource in production environment.',\n command: '<%= config.bin %> <%= command.id %> --env=production',\n },\n {\n description:\n 'Build the FiveM resource and automatically reload it on the server after build.',\n command:\n '<%= config.bin %> <%= command.id %> --auto-reload --password your_rcon_password',\n },\n {\n description:\n 'Build the FiveM resource in watch mode and automatically reload it on the server after each build.',\n command:\n '<%= config.bin %> <%= command.id %> --watch --auto-reload --password your_rcon_password',\n },\n {\n description:\n 'Build the FiveM resource and connect to a specific server for auto-reload.',\n command:\n '<%= config.bin %> <%= command.id %> --auto-reload --server=127.0.0.1:30120 --password your_rcon_password',\n },\n ];\n\n public async run(): Promise<void> {\n const { env, resourceName, rconOptions, watch } = await this._parseOptions();\n\n const config = createFivemRspackConfig({ environment: env, resourceName });\n const compiler = rspack(config);\n\n const reloadFivemResource = async () => {\n if (!rconOptions) return;\n this.logger.info(`Reloading FiveM resource \"${resourceName}\"...`);\n try {\n await refreshAndEnsureFivemResource(resourceName, rconOptions);\n this.logger.success(`FiveM resource reloaded successfully.\\n`);\n } catch (error) {\n this.logger.error(`Failed to reload FiveM resource: ${getErrMsg(error)}\\n`);\n }\n };\n\n const compilerCallback = (err: Error | null, stats: MultiStats | undefined) => {\n if (err) {\n this.logger.error(getErrMsg(err));\n if ('details' in err) this.logger.error(err.details as string);\n if ('stack' in err) this.logger.error(err.stack);\n return;\n }\n\n if (stats) this.logger.log(stats.toString(true), '\\n');\n\n void reloadFivemResource();\n };\n\n if (watch) {\n this.logger.info(`Building FiveM resource in watch mode...\\n`);\n compiler.watch({}, compilerCallback);\n } else {\n this.logger.info(`Building FiveM resource...\\n`);\n compiler.run(compilerCallback);\n }\n }\n\n private async _parseOptions(): Promise<FivemBuildCommandOptions> {\n const { flags } = await this.parse(FivemBuildCommand);\n\n const env = (flags.env ?? (flags.watch ? 'development' : 'production')) as Environment;\n const resourceName = flags.resourceName ?? path.basename(process.cwd());\n\n let rconOptions: RconOptions | undefined;\n\n if (flags.autoReload) {\n if (!flags.password) {\n throw new Error(\n 'RCON password is required for auto-reload. Please provide it using the --password flag.',\n );\n }\n\n const match = /^(?<host>[^:]+):(?<port>\\d+)$/u.exec(flags.server);\n const { host, port } = match?.groups ?? {};\n\n if (!host || !port) {\n throw new Error(\n `Invalid server address format: ${flags.server}. Expected format is \"ip:port\".`,\n );\n }\n\n rconOptions = {\n host,\n port: parseInt(port),\n password: flags.password,\n };\n }\n\n return { env, resourceName, rconOptions, watch: flags.watch };\n }\n}\n","import { FivemBuildCommand } from './fivem-build-command';\n\nexport const commands = {\n 'build:fivem': FivemBuildCommand,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["timeoutId: NodeJS.Timeout","rconOptions: RconOptions | undefined"],"sources":["../src/fivem-rcon.ts","../src/fivem-build-command.ts","../src/index.ts"],"sourcesContent":["import dgram from 'node:dgram';\n\nimport { getErrMsg } from '@jpp-toolkit/utils';\n\nexport type RconOptions = {\n readonly host: string;\n readonly port: number;\n readonly password: string;\n readonly timeout?: number | undefined;\n};\n\nfunction buildCommand(command: string, password: string): Buffer {\n const buffer = Buffer.alloc(11 + password.length + command.length);\n buffer.writeUInt32LE(0xffffffff, 0);\n buffer.write('rcon ', 4);\n buffer.write(password, 9, password.length);\n buffer.write(' ', 9 + password.length, 1);\n buffer.write(command, 10 + password.length, command.length);\n buffer.write('\\n', 10 + password.length + command.length, 1);\n return buffer;\n}\n\nexport async function sendFivemRcon(command: string, options: RconOptions): Promise<string> {\n const socket = dgram.createSocket('udp4');\n\n return new Promise<string>((resolve, reject) => {\n let timeoutId: NodeJS.Timeout;\n\n const handleError = (err?: Error | string | null) => {\n if (!err) return;\n clearTimeout(timeoutId);\n const msg = getErrMsg(err);\n reject(new Error(`Failed to send command to ${options.host}:${options.port}: ${msg}`));\n };\n\n const handleMessage = (msg: Buffer) => {\n clearTimeout(timeoutId);\n const res = msg.toString('ascii').slice(4).trim();\n if (res.includes('Invalid password')) return void handleError('Invalid password');\n resolve(res);\n };\n\n timeoutId = setTimeout(\n () => handleError(`Timeout after ${options.timeout}ms`),\n options.timeout ?? 5000,\n );\n\n socket.once('error', handleError);\n socket.once('message', handleMessage);\n\n const cmd = buildCommand(command, options.password);\n socket.send(cmd, 0, cmd.length, options.port, options.host, handleError);\n }).finally(() => {\n socket.close();\n });\n}\n\nexport async function refreshAndEnsureFivemResource(\n resourceName: string,\n options: RconOptions,\n): Promise<void> {\n const res = await sendFivemRcon(`refresh; ensure ${resourceName}`, options);\n if (res.includes(\"Couldn't find resource\"))\n throw new Error(`Resource \"${resourceName}\" not found`);\n if (!res.includes('Started resource'))\n throw new Error(`Failed to start resource \"${resourceName}\"`);\n}\n","import path from 'node:path';\n\nimport { Command } from '@jpp-toolkit/core';\nimport {\n createFivemScriptRspackConfig,\n createFivemUiRspackConfig,\n} from '@jpp-toolkit/rspack-config';\nimport { debounce } from '@jpp-toolkit/utils';\nimport { Flags } from '@oclif/core';\nimport type { MultiStats, Stats } from '@rspack/core';\nimport { rspack } from '@rspack/core';\nimport { RspackDevServer } from '@rspack/dev-server';\n\nimport { refreshAndEnsureFivemResource } from './fivem-rcon';\nimport type { RconOptions } from './fivem-rcon';\n\ntype FivemBuildCommandOptions = {\n readonly resourceName: string;\n readonly rconOptions?: RconOptions | undefined;\n readonly watch: boolean;\n};\n\nexport class FivemBuildCommand extends Command {\n static override summary = 'Build the FiveM resource using predefined config.';\n\n static override flags = {\n watch: Flags.boolean({\n char: 'w',\n description: 'Watch files for changes and rebuild automatically.',\n default: false,\n }),\n autoReload: Flags.boolean({\n char: 'r',\n description: 'Automatically reload FiveM resource after build.',\n default: false,\n }),\n server: Flags.string({\n char: 's',\n description: 'Server \"ip:port\" to connect for reloading FiveM resource after build.',\n default: '127.0.0.1:30120',\n required: false,\n }),\n password: Flags.string({\n char: 'p',\n description: 'RCON password for the FiveM server to reload resource after build.',\n required: false,\n }),\n resourceName: Flags.string({\n char: 'n',\n description:\n 'Name of the FiveM resource to reload. If not provided, the name of the folder containing the resource will be used.',\n required: false,\n }),\n };\n\n static override examples = [\n {\n description: 'Build the FiveM resource.',\n command: '<%= config.bin %> <%= command.id %>',\n },\n {\n description: 'Build the FiveM resource in watch mode.',\n command: '<%= config.bin %> <%= command.id %> --watch',\n },\n {\n description:\n 'Build the FiveM resource and automatically reload it on the server after build.',\n command:\n '<%= config.bin %> <%= command.id %> --auto-reload --password your_rcon_password',\n },\n {\n description:\n 'Build the FiveM resource in watch mode and automatically reload it on the server after each build.',\n command:\n '<%= config.bin %> <%= command.id %> --watch --auto-reload --password your_rcon_password',\n },\n {\n description:\n 'Build the FiveM resource and connect to a specific server for auto-reload.',\n command:\n '<%= config.bin %> <%= command.id %> --auto-reload --server=127.0.0.1:30120 --password your_rcon_password',\n },\n ];\n\n public async run(): Promise<void> {\n const { resourceName, rconOptions, watch } = await this._parseOptions();\n\n const scriptConfig = createFivemScriptRspackConfig(undefined, {\n isProduction: !watch,\n })({\n RSPACK_BUILD: !watch,\n RSPACK_WATCH: watch,\n });\n const scriptCompiler = rspack(scriptConfig);\n\n const uiConfig = createFivemUiRspackConfig(\n { resourceName },\n { isProduction: !watch },\n )({\n RSPACK_BUILD: !watch,\n RSPACK_SERVE: watch,\n });\n const uiCompiler = rspack(uiConfig);\n\n const reloadFivemResource = debounce(async () => {\n if (!rconOptions) return;\n this.logger.log('');\n this.logger.info(`Reloading FiveM resource \"${resourceName}\"...`);\n try {\n await refreshAndEnsureFivemResource(resourceName, rconOptions);\n this.logger.success(`FiveM resource reloaded successfully.\\n`);\n } catch (error) {\n this.logger.error(`Failed to reload FiveM resource: ${error}\\n`);\n }\n }, 500);\n\n const compilerCallback = (err: Error | null, stats: Stats | MultiStats | undefined) => {\n if (err) {\n this.logger.error(err.toString());\n return;\n }\n if (!stats) return;\n this.logger.log(stats.toString({ preset: 'normal', colors: true }), '\\n');\n void reloadFivemResource();\n };\n\n if (watch) {\n const devServerOptions = uiConfig.devServer ?? {};\n devServerOptions.hot = true;\n const devServer = new RspackDevServer(devServerOptions, uiCompiler);\n await devServer.start();\n scriptCompiler.watch({}, compilerCallback);\n } else {\n uiCompiler.run((error: Error | null, stats: Stats | MultiStats | undefined) => {\n uiCompiler.close((closeErr) => {\n if (closeErr) this.logger.error(closeErr.toString());\n compilerCallback(error, stats);\n });\n });\n scriptCompiler.run((error: Error | null, stats: Stats | MultiStats | undefined) => {\n scriptCompiler.close((closeErr) => {\n if (closeErr) this.logger.error(closeErr.toString());\n compilerCallback(error, stats);\n });\n });\n }\n }\n\n private async _parseOptions(): Promise<FivemBuildCommandOptions> {\n const { flags } = await this.parse(FivemBuildCommand);\n\n const resourceName = flags.resourceName ?? path.basename(process.cwd());\n\n let rconOptions: RconOptions | undefined;\n\n if (flags.autoReload) {\n if (!flags.password) {\n throw new Error(\n 'RCON password is required for auto-reload. Please provide it using the --password flag.',\n );\n }\n\n const match = /^(?<host>[^:]+):(?<port>\\d+)$/u.exec(flags.server);\n const { host, port } = match?.groups ?? {};\n\n if (!host || !port) {\n throw new Error(\n `Invalid server address format: ${flags.server}. Expected format is \"ip:port\".`,\n );\n }\n\n rconOptions = {\n host,\n port: parseInt(port),\n password: flags.password,\n };\n }\n\n return { resourceName, rconOptions, watch: flags.watch };\n }\n}\n","import { FivemBuildCommand } from './fivem-build-command';\n\nexport const commands = {\n 'build:fivem': FivemBuildCommand,\n};\n"],"mappings":"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"}
@@ -12,10 +12,6 @@
12
12
  "description": "Build the FiveM resource in watch mode.",
13
13
  "command": "<%= config.bin %> <%= command.id %> --watch"
14
14
  },
15
- {
16
- "description": "Build the FiveM resource in production environment.",
17
- "command": "<%= config.bin %> <%= command.id %> --env=production"
18
- },
19
15
  {
20
16
  "description": "Build the FiveM resource and automatically reload it on the server after build.",
21
17
  "command": "<%= config.bin %> <%= command.id %> --auto-reload --password your_rcon_password"
@@ -37,19 +33,6 @@
37
33
  "allowNo": false,
38
34
  "type": "boolean"
39
35
  },
40
- "env": {
41
- "char": "e",
42
- "description": "Set the build environment (development or production).",
43
- "name": "env",
44
- "required": false,
45
- "hasDynamicHelp": false,
46
- "multiple": false,
47
- "options": [
48
- "development",
49
- "production"
50
- ],
51
- "type": "option"
52
- },
53
36
  "autoReload": {
54
37
  "char": "r",
55
38
  "description": "Automatically reload FiveM resource after build.",
@@ -96,5 +79,5 @@
96
79
  "summary": "Build the FiveM resource using predefined config."
97
80
  }
98
81
  },
99
- "version": "0.0.18"
82
+ "version": "0.0.20"
100
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jpp-toolkit/plugin-build-fivem",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "description": "Plugin that add the fivem build command to the jpp cli.",
5
5
  "keywords": [
6
6
  "jpp",
@@ -36,9 +36,10 @@
36
36
  "dependencies": {
37
37
  "@oclif/core": "4.8.0",
38
38
  "@rspack/core": "1.6.8",
39
- "@jpp-toolkit/core": "0.0.17",
40
- "@jpp-toolkit/rspack-config": "0.0.7",
41
- "@jpp-toolkit/utils": "0.0.15"
39
+ "@rspack/dev-server": "1.1.4",
40
+ "@jpp-toolkit/core": "0.0.19",
41
+ "@jpp-toolkit/rspack-config": "0.0.9",
42
+ "@jpp-toolkit/utils": "0.0.17"
42
43
  },
43
44
  "devDependencies": {
44
45
  "oclif": "4.22.57"
@@ -1,19 +1,20 @@
1
1
  import path from 'node:path';
2
2
 
3
3
  import { Command } from '@jpp-toolkit/core';
4
- import { createFivemRspackConfig } from '@jpp-toolkit/rspack-config';
5
- import { getErrMsg } from '@jpp-toolkit/utils';
4
+ import {
5
+ createFivemScriptRspackConfig,
6
+ createFivemUiRspackConfig,
7
+ } from '@jpp-toolkit/rspack-config';
8
+ import { debounce } from '@jpp-toolkit/utils';
6
9
  import { Flags } from '@oclif/core';
7
- import type { MultiStats } from '@rspack/core';
10
+ import type { MultiStats, Stats } from '@rspack/core';
8
11
  import { rspack } from '@rspack/core';
12
+ import { RspackDevServer } from '@rspack/dev-server';
9
13
 
10
14
  import { refreshAndEnsureFivemResource } from './fivem-rcon';
11
15
  import type { RconOptions } from './fivem-rcon';
12
16
 
13
- type Environment = 'development' | 'production';
14
-
15
17
  type FivemBuildCommandOptions = {
16
- readonly env: Environment;
17
18
  readonly resourceName: string;
18
19
  readonly rconOptions?: RconOptions | undefined;
19
20
  readonly watch: boolean;
@@ -28,12 +29,6 @@ export class FivemBuildCommand extends Command {
28
29
  description: 'Watch files for changes and rebuild automatically.',
29
30
  default: false,
30
31
  }),
31
- env: Flags.string({
32
- char: 'e',
33
- description: 'Set the build environment (development or production).',
34
- options: ['development', 'production'],
35
- required: false,
36
- }),
37
32
  autoReload: Flags.boolean({
38
33
  char: 'r',
39
34
  description: 'Automatically reload FiveM resource after build.',
@@ -67,10 +62,6 @@ export class FivemBuildCommand extends Command {
67
62
  description: 'Build the FiveM resource in watch mode.',
68
63
  command: '<%= config.bin %> <%= command.id %> --watch',
69
64
  },
70
- {
71
- description: 'Build the FiveM resource in production environment.',
72
- command: '<%= config.bin %> <%= command.id %> --env=production',
73
- },
74
65
  {
75
66
  description:
76
67
  'Build the FiveM resource and automatically reload it on the server after build.',
@@ -92,48 +83,72 @@ export class FivemBuildCommand extends Command {
92
83
  ];
93
84
 
94
85
  public async run(): Promise<void> {
95
- const { env, resourceName, rconOptions, watch } = await this._parseOptions();
96
-
97
- const config = createFivemRspackConfig({ environment: env, resourceName });
98
- const compiler = rspack(config);
99
-
100
- const reloadFivemResource = async () => {
86
+ const { resourceName, rconOptions, watch } = await this._parseOptions();
87
+
88
+ const scriptConfig = createFivemScriptRspackConfig(undefined, {
89
+ isProduction: !watch,
90
+ })({
91
+ RSPACK_BUILD: !watch,
92
+ RSPACK_WATCH: watch,
93
+ });
94
+ const scriptCompiler = rspack(scriptConfig);
95
+
96
+ const uiConfig = createFivemUiRspackConfig(
97
+ { resourceName },
98
+ { isProduction: !watch },
99
+ )({
100
+ RSPACK_BUILD: !watch,
101
+ RSPACK_SERVE: watch,
102
+ });
103
+ const uiCompiler = rspack(uiConfig);
104
+
105
+ const reloadFivemResource = debounce(async () => {
101
106
  if (!rconOptions) return;
107
+ this.logger.log('');
102
108
  this.logger.info(`Reloading FiveM resource "${resourceName}"...`);
103
109
  try {
104
110
  await refreshAndEnsureFivemResource(resourceName, rconOptions);
105
111
  this.logger.success(`FiveM resource reloaded successfully.\n`);
106
112
  } catch (error) {
107
- this.logger.error(`Failed to reload FiveM resource: ${getErrMsg(error)}\n`);
113
+ this.logger.error(`Failed to reload FiveM resource: ${error}\n`);
108
114
  }
109
- };
115
+ }, 500);
110
116
 
111
- const compilerCallback = (err: Error | null, stats: MultiStats | undefined) => {
117
+ const compilerCallback = (err: Error | null, stats: Stats | MultiStats | undefined) => {
112
118
  if (err) {
113
- this.logger.error(getErrMsg(err));
114
- if ('details' in err) this.logger.error(err.details as string);
115
- if ('stack' in err) this.logger.error(err.stack);
119
+ this.logger.error(err.toString());
116
120
  return;
117
121
  }
118
-
119
- if (stats) this.logger.log(stats.toString(true), '\n');
120
-
122
+ if (!stats) return;
123
+ this.logger.log(stats.toString({ preset: 'normal', colors: true }), '\n');
121
124
  void reloadFivemResource();
122
125
  };
123
126
 
124
127
  if (watch) {
125
- this.logger.info(`Building FiveM resource in watch mode...\n`);
126
- compiler.watch({}, compilerCallback);
128
+ const devServerOptions = uiConfig.devServer ?? {};
129
+ devServerOptions.hot = true;
130
+ const devServer = new RspackDevServer(devServerOptions, uiCompiler);
131
+ await devServer.start();
132
+ scriptCompiler.watch({}, compilerCallback);
127
133
  } else {
128
- this.logger.info(`Building FiveM resource...\n`);
129
- compiler.run(compilerCallback);
134
+ uiCompiler.run((error: Error | null, stats: Stats | MultiStats | undefined) => {
135
+ uiCompiler.close((closeErr) => {
136
+ if (closeErr) this.logger.error(closeErr.toString());
137
+ compilerCallback(error, stats);
138
+ });
139
+ });
140
+ scriptCompiler.run((error: Error | null, stats: Stats | MultiStats | undefined) => {
141
+ scriptCompiler.close((closeErr) => {
142
+ if (closeErr) this.logger.error(closeErr.toString());
143
+ compilerCallback(error, stats);
144
+ });
145
+ });
130
146
  }
131
147
  }
132
148
 
133
149
  private async _parseOptions(): Promise<FivemBuildCommandOptions> {
134
150
  const { flags } = await this.parse(FivemBuildCommand);
135
151
 
136
- const env = (flags.env ?? (flags.watch ? 'development' : 'production')) as Environment;
137
152
  const resourceName = flags.resourceName ?? path.basename(process.cwd());
138
153
 
139
154
  let rconOptions: RconOptions | undefined;
@@ -161,6 +176,6 @@ export class FivemBuildCommand extends Command {
161
176
  };
162
177
  }
163
178
 
164
- return { env, resourceName, rconOptions, watch: flags.watch };
179
+ return { resourceName, rconOptions, watch: flags.watch };
165
180
  }
166
181
  }