@jpp-toolkit/plugin-build-fivem 0.0.2

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Julien Papini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,28 @@
1
+ import { Command } from "@jpp-toolkit/core";
2
+ import * as _oclif_core_interfaces0 from "@oclif/core/interfaces";
3
+
4
+ //#region src/fivem-build-command.d.ts
5
+ declare class FivemBuildCommand extends Command {
6
+ static summary: string;
7
+ static flags: {
8
+ watch: _oclif_core_interfaces0.BooleanFlag<boolean>;
9
+ mode: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
10
+ autoReload: _oclif_core_interfaces0.BooleanFlag<boolean>;
11
+ server: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
12
+ password: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
13
+ resourceName: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
14
+ };
15
+ static examples: never[];
16
+ run(): Promise<void>;
17
+ private _parseServerAddress;
18
+ private _compilerCallback;
19
+ private _enableAutoReload;
20
+ }
21
+ //#endregion
22
+ //#region src/index.d.ts
23
+ declare const commands: {
24
+ 'build:fivem': typeof FivemBuildCommand;
25
+ };
26
+ //#endregion
27
+ export { commands };
28
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,113 @@
1
+ import path from "node:path";
2
+ import { Command } from "@jpp-toolkit/core";
3
+ import { createFivemRspackConfig } from "@jpp-toolkit/rspack-config";
4
+ import { getErrMsg } from "@jpp-toolkit/utils";
5
+ import { Flags } from "@oclif/core";
6
+ import { rspack } from "@rspack/core";
7
+ import { Rcon } from "rcon-client";
8
+
9
+ //#region src/fivem-build-command.ts
10
+ var FivemBuildCommand = class FivemBuildCommand extends Command {
11
+ static summary = "Build the FiveM resource using predefined config.";
12
+ static flags = {
13
+ watch: Flags.boolean({
14
+ char: "w",
15
+ description: "Watch files for changes and rebuild automatically.",
16
+ default: false
17
+ }),
18
+ mode: Flags.string({
19
+ char: "m",
20
+ description: "Set the build mode (development or production).",
21
+ options: ["development", "production"],
22
+ required: false
23
+ }),
24
+ autoReload: Flags.boolean({
25
+ char: "r",
26
+ description: "Automatically reload FiveM resource after build.",
27
+ default: false
28
+ }),
29
+ server: Flags.string({
30
+ char: "s",
31
+ description: "Server \"ip:port\" to connect for reloading FiveM resource after build.",
32
+ default: "127.0.0.1:30120",
33
+ required: false
34
+ }),
35
+ password: Flags.string({
36
+ char: "p",
37
+ description: "RCON password for the FiveM server to reload resource after build.",
38
+ required: false
39
+ }),
40
+ resourceName: Flags.string({
41
+ char: "n",
42
+ description: "Name of the FiveM resource to reload. If not provided, the name of the folder containing the resource will be used.",
43
+ required: false
44
+ })
45
+ };
46
+ static examples = [];
47
+ async run() {
48
+ const { flags } = await this.parse(FivemBuildCommand);
49
+ const { watch, autoReload, password } = flags;
50
+ const mode = flags.mode ?? (watch ? "development" : "production");
51
+ const { host, port } = this._parseServerAddress(flags.server);
52
+ const resourceName = flags.resourceName ?? path.basename(process.cwd());
53
+ const compiler = rspack(createFivemRspackConfig({ mode }));
54
+ if (autoReload) {
55
+ if (!password) {
56
+ this.logger.error("RCON password is required for auto-reload. Please provide it using the --password flag.");
57
+ this.exit(1);
58
+ }
59
+ const rcon = new Rcon({
60
+ host,
61
+ port,
62
+ password
63
+ });
64
+ await this._enableAutoReload(compiler, rcon, resourceName);
65
+ }
66
+ if (flags.watch) {
67
+ this.logger.info(`Building FiveM resource in watch mode...\n`);
68
+ compiler.watch({}, this._compilerCallback.bind(this));
69
+ } else {
70
+ this.logger.info(`Building FiveM resource...\n`);
71
+ compiler.run(this._compilerCallback.bind(this));
72
+ }
73
+ }
74
+ _parseServerAddress(address) {
75
+ const { host, port } = /^(?<host>[^:]+):(?<port>\d+)$/u.exec(address)?.groups ?? {};
76
+ if (!host || !port) throw new Error(`Invalid server address format: ${address}. Expected format is "ip:port".`);
77
+ return {
78
+ host,
79
+ port: parseInt(port)
80
+ };
81
+ }
82
+ _compilerCallback(err, stats) {
83
+ if (err) {
84
+ this.logger.error(getErrMsg(err));
85
+ if ("details" in err) this.logger.error(err.details);
86
+ if ("stack" in err) this.logger.error(err.stack);
87
+ return;
88
+ }
89
+ if (stats) this.logger.log(stats.toString(), "\n");
90
+ }
91
+ async _enableAutoReload(compiler, rcon, resourceName) {
92
+ compiler.hooks.done.tapPromise("FivemAutoReloadPlugin", async (stats) => {
93
+ if (stats.hasErrors()) {
94
+ this.logger.warning("Build failed. Skipping FiveM resource reload.");
95
+ return;
96
+ }
97
+ this.logger.info(`Reloading FiveM resource "${resourceName}"...`);
98
+ try {
99
+ this.logger.success(`FiveM resource reloaded successfully.\n`);
100
+ } catch (error) {
101
+ this.logger.error(`Failed to reload FiveM resource: ${getErrMsg(error)}\n`);
102
+ }
103
+ });
104
+ }
105
+ };
106
+
107
+ //#endregion
108
+ //#region src/index.ts
109
+ const commands = { "build:fivem": FivemBuildCommand };
110
+
111
+ //#endregion
112
+ export { commands };
113
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/fivem-build-command.ts","../src/index.ts"],"sourcesContent":["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 { Compiler, Stats } from '@rspack/core';\nimport { rspack } from '@rspack/core';\nimport { Rcon } from 'rcon-client';\n\ntype Mode = 'development' | 'production';\ntype ServerAddress = { readonly host: string; readonly port: number };\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 mode: Flags.string({\n char: 'm',\n description: 'Set the build mode (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 public async run(): Promise<void> {\n const { flags } = await this.parse(FivemBuildCommand);\n\n const { watch, autoReload, password } = flags;\n const mode = (flags.mode ?? (watch ? 'development' : 'production')) as Mode;\n const { host, port } = this._parseServerAddress(flags.server);\n const resourceName = flags.resourceName ?? path.basename(process.cwd());\n\n const config = createFivemRspackConfig({ mode });\n const compiler = rspack(config);\n\n if (autoReload) {\n if (!password) {\n this.logger.error(\n 'RCON password is required for auto-reload. Please provide it using the --password flag.',\n );\n this.exit(1);\n }\n\n const rcon = new Rcon({ host, port, password });\n await this._enableAutoReload(compiler, rcon, resourceName);\n }\n\n if (flags.watch) {\n this.logger.info(`Building FiveM resource in watch mode...\\n`);\n compiler.watch({}, this._compilerCallback.bind(this));\n } else {\n this.logger.info(`Building FiveM resource...\\n`);\n compiler.run(this._compilerCallback.bind(this));\n }\n }\n\n private _parseServerAddress(address: string): ServerAddress {\n const match = /^(?<host>[^:]+):(?<port>\\d+)$/u.exec(address);\n const { host, port } = match?.groups ?? {};\n\n if (!host || !port) {\n throw new Error(\n `Invalid server address format: ${address}. Expected format is \"ip:port\".`,\n );\n }\n\n return { host, port: parseInt(port) };\n }\n\n private _compilerCallback(err: Error | null, stats: Stats | undefined): void {\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(), '\\n');\n }\n\n private async _enableAutoReload(\n compiler: Compiler,\n rcon: Rcon,\n resourceName: string,\n ): Promise<void> {\n try {\n // await rcon.connect();\n } catch (error) {\n this.logger.error(`Failed to connect to FiveM server via RCON: ${getErrMsg(error)}`);\n this.exit(1);\n }\n\n compiler.hooks.done.tapPromise('FivemAutoReloadPlugin', async (stats) => {\n if (stats.hasErrors()) {\n this.logger.warning('Build failed. Skipping FiveM resource reload.');\n return;\n }\n\n this.logger.info(`Reloading FiveM resource \"${resourceName}\"...`);\n try {\n // await rcon.send(`refresh; ensure ${resourceName}`);\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}\n","import { FivemBuildCommand } from './fivem-build-command';\n\nexport const commands = {\n 'build:fivem': FivemBuildCommand,\n};\n"],"mappings":";;;;;;;;;AAaA,IAAa,oBAAb,MAAa,0BAA0B,QAAQ;CAC3C,OAAgB,UAAU;CAE1B,OAAgB,QAAQ;EACpB,OAAO,MAAM,QAAQ;GACjB,MAAM;GACN,aAAa;GACb,SAAS;GACZ,CAAC;EACF,MAAM,MAAM,OAAO;GACf,MAAM;GACN,aAAa;GACb,SAAS,CAAC,eAAe,aAAa;GACtC,UAAU;GACb,CAAC;EACF,YAAY,MAAM,QAAQ;GACtB,MAAM;GACN,aAAa;GACb,SAAS;GACZ,CAAC;EACF,QAAQ,MAAM,OAAO;GACjB,MAAM;GACN,aAAa;GACb,SAAS;GACT,UAAU;GACb,CAAC;EACF,UAAU,MAAM,OAAO;GACnB,MAAM;GACN,aAAa;GACb,UAAU;GACb,CAAC;EACF,cAAc,MAAM,OAAO;GACvB,MAAM;GACN,aACI;GACJ,UAAU;GACb,CAAC;EACL;CAED,OAAgB,WAAW,EAAE;CAE7B,MAAa,MAAqB;EAC9B,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,kBAAkB;EAErD,MAAM,EAAE,OAAO,YAAY,aAAa;EACxC,MAAM,OAAQ,MAAM,SAAS,QAAQ,gBAAgB;EACrD,MAAM,EAAE,MAAM,SAAS,KAAK,oBAAoB,MAAM,OAAO;EAC7D,MAAM,eAAe,MAAM,gBAAgB,KAAK,SAAS,QAAQ,KAAK,CAAC;EAGvE,MAAM,WAAW,OADF,wBAAwB,EAAE,MAAM,CAAC,CACjB;AAE/B,MAAI,YAAY;AACZ,OAAI,CAAC,UAAU;AACX,SAAK,OAAO,MACR,0FACH;AACD,SAAK,KAAK,EAAE;;GAGhB,MAAM,OAAO,IAAI,KAAK;IAAE;IAAM;IAAM;IAAU,CAAC;AAC/C,SAAM,KAAK,kBAAkB,UAAU,MAAM,aAAa;;AAG9D,MAAI,MAAM,OAAO;AACb,QAAK,OAAO,KAAK,6CAA6C;AAC9D,YAAS,MAAM,EAAE,EAAE,KAAK,kBAAkB,KAAK,KAAK,CAAC;SAClD;AACH,QAAK,OAAO,KAAK,+BAA+B;AAChD,YAAS,IAAI,KAAK,kBAAkB,KAAK,KAAK,CAAC;;;CAIvD,AAAQ,oBAAoB,SAAgC;EAExD,MAAM,EAAE,MAAM,SADA,iCAAiC,KAAK,QAAQ,EAC9B,UAAU,EAAE;AAE1C,MAAI,CAAC,QAAQ,CAAC,KACV,OAAM,IAAI,MACN,kCAAkC,QAAQ,iCAC7C;AAGL,SAAO;GAAE;GAAM,MAAM,SAAS,KAAK;GAAE;;CAGzC,AAAQ,kBAAkB,KAAmB,OAAgC;AACzE,MAAI,KAAK;AACL,QAAK,OAAO,MAAM,UAAU,IAAI,CAAC;AACjC,OAAI,aAAa,IAAK,MAAK,OAAO,MAAM,IAAI,QAAkB;AAC9D,OAAI,WAAW,IAAK,MAAK,OAAO,MAAM,IAAI,MAAM;AAChD;;AAGJ,MAAI,MAAO,MAAK,OAAO,IAAI,MAAM,UAAU,EAAE,KAAK;;CAGtD,MAAc,kBACV,UACA,MACA,cACa;AAQb,WAAS,MAAM,KAAK,WAAW,yBAAyB,OAAO,UAAU;AACrE,OAAI,MAAM,WAAW,EAAE;AACnB,SAAK,OAAO,QAAQ,gDAAgD;AACpE;;AAGJ,QAAK,OAAO,KAAK,6BAA6B,aAAa,MAAM;AACjE,OAAI;AAEA,SAAK,OAAO,QAAQ,0CAA0C;YACzD,OAAO;AACZ,SAAK,OAAO,MAAM,oCAAoC,UAAU,MAAM,CAAC,IAAI;;IAEjF;;;;;;ACrIV,MAAa,WAAW,EACpB,eAAe,mBAClB"}
@@ -0,0 +1,13 @@
1
+ export default {
2
+ description: 'Plugin that add the fivem build command to the jpp cli.',
3
+ commands: {
4
+ strategy: 'explicit',
5
+ target: './dist/index.mjs',
6
+ identifier: 'commands',
7
+ },
8
+ topics: {
9
+ build: {
10
+ description: 'Commands for building code.',
11
+ },
12
+ },
13
+ };
@@ -0,0 +1,75 @@
1
+ {
2
+ "commands": {
3
+ "build:fivem": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "examples": [],
7
+ "flags": {
8
+ "watch": {
9
+ "char": "w",
10
+ "description": "Watch files for changes and rebuild automatically.",
11
+ "name": "watch",
12
+ "allowNo": false,
13
+ "type": "boolean"
14
+ },
15
+ "mode": {
16
+ "char": "m",
17
+ "description": "Set the build mode (development or production).",
18
+ "name": "mode",
19
+ "required": false,
20
+ "hasDynamicHelp": false,
21
+ "multiple": false,
22
+ "options": [
23
+ "development",
24
+ "production"
25
+ ],
26
+ "type": "option"
27
+ },
28
+ "autoReload": {
29
+ "char": "r",
30
+ "description": "Automatically reload FiveM resource after build.",
31
+ "name": "autoReload",
32
+ "allowNo": false,
33
+ "type": "boolean"
34
+ },
35
+ "server": {
36
+ "char": "s",
37
+ "description": "Server \"ip:port\" to connect for reloading FiveM resource after build.",
38
+ "name": "server",
39
+ "required": false,
40
+ "default": "127.0.0.1:30120",
41
+ "hasDynamicHelp": false,
42
+ "multiple": false,
43
+ "type": "option"
44
+ },
45
+ "password": {
46
+ "char": "p",
47
+ "description": "RCON password for the FiveM server to reload resource after build.",
48
+ "name": "password",
49
+ "required": false,
50
+ "hasDynamicHelp": false,
51
+ "multiple": false,
52
+ "type": "option"
53
+ },
54
+ "resourceName": {
55
+ "char": "n",
56
+ "description": "Name of the FiveM resource to reload. If not provided, the name of the folder containing the resource will be used.",
57
+ "name": "resourceName",
58
+ "required": false,
59
+ "hasDynamicHelp": false,
60
+ "multiple": false,
61
+ "type": "option"
62
+ }
63
+ },
64
+ "hasDynamicHelp": false,
65
+ "hiddenAliases": [],
66
+ "id": "build:fivem",
67
+ "pluginAlias": "@jpp-toolkit/plugin-build-fivem",
68
+ "pluginName": "@jpp-toolkit/plugin-build-fivem",
69
+ "pluginType": "core",
70
+ "strict": true,
71
+ "summary": "Build the FiveM resource using predefined config."
72
+ }
73
+ },
74
+ "version": "0.0.2"
75
+ }
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@jpp-toolkit/plugin-build-fivem",
3
+ "version": "0.0.2",
4
+ "description": "Plugin that add the fivem build command to the jpp cli.",
5
+ "keywords": [
6
+ "jpp",
7
+ "plugin",
8
+ "build",
9
+ "fivem"
10
+ ],
11
+ "homepage": "https://github.com/jpapini/jpp-toolkit/tree/main/packages/plugin-build-fivem#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/jpapini/jpp-toolkit/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/jpapini/jpp-toolkit.git",
18
+ "directory": "packages/plugin-build-fivem"
19
+ },
20
+ "license": "MIT",
21
+ "author": "Julien Papini <julien.papini@gmail.com> (https://github.com/jpapini)",
22
+ "type": "module",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.mts",
26
+ "default": "./dist/index.mjs"
27
+ },
28
+ "./package.json": "./package.json"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "oclif.config.js",
34
+ "oclif.manifest.json"
35
+ ],
36
+ "dependencies": {
37
+ "@oclif/core": "4.8.0",
38
+ "@rspack/core": "1.6.7",
39
+ "rcon-client": "4.2.5",
40
+ "@jpp-toolkit/core": "0.0.14",
41
+ "@jpp-toolkit/utils": "0.0.14",
42
+ "@jpp-toolkit/rspack-config": "0.0.2"
43
+ },
44
+ "devDependencies": {
45
+ "oclif": "4.22.54"
46
+ },
47
+ "engines": {
48
+ "node": "24",
49
+ "pnpm": "10"
50
+ },
51
+ "volta": {
52
+ "extends": "../../package.json"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "scripts": {
58
+ "typecheck": "tsc --noEmit --pretty",
59
+ "dev": "build-lib --watch",
60
+ "build": "build-lib"
61
+ }
62
+ }
@@ -0,0 +1,138 @@
1
+ import path from 'node:path';
2
+
3
+ import { Command } from '@jpp-toolkit/core';
4
+ import { createFivemRspackConfig } from '@jpp-toolkit/rspack-config';
5
+ import { getErrMsg } from '@jpp-toolkit/utils';
6
+ import { Flags } from '@oclif/core';
7
+ import type { Compiler, Stats } from '@rspack/core';
8
+ import { rspack } from '@rspack/core';
9
+ import { Rcon } from 'rcon-client';
10
+
11
+ type Mode = 'development' | 'production';
12
+ type ServerAddress = { readonly host: string; readonly port: number };
13
+
14
+ export class FivemBuildCommand extends Command {
15
+ static override summary = 'Build the FiveM resource using predefined config.';
16
+
17
+ static override flags = {
18
+ watch: Flags.boolean({
19
+ char: 'w',
20
+ description: 'Watch files for changes and rebuild automatically.',
21
+ default: false,
22
+ }),
23
+ mode: Flags.string({
24
+ char: 'm',
25
+ description: 'Set the build mode (development or production).',
26
+ options: ['development', 'production'],
27
+ required: false,
28
+ }),
29
+ autoReload: Flags.boolean({
30
+ char: 'r',
31
+ description: 'Automatically reload FiveM resource after build.',
32
+ default: false,
33
+ }),
34
+ server: Flags.string({
35
+ char: 's',
36
+ description: 'Server "ip:port" to connect for reloading FiveM resource after build.',
37
+ default: '127.0.0.1:30120',
38
+ required: false,
39
+ }),
40
+ password: Flags.string({
41
+ char: 'p',
42
+ description: 'RCON password for the FiveM server to reload resource after build.',
43
+ required: false,
44
+ }),
45
+ resourceName: Flags.string({
46
+ char: 'n',
47
+ description:
48
+ 'Name of the FiveM resource to reload. If not provided, the name of the folder containing the resource will be used.',
49
+ required: false,
50
+ }),
51
+ };
52
+
53
+ static override examples = [];
54
+
55
+ public async run(): Promise<void> {
56
+ const { flags } = await this.parse(FivemBuildCommand);
57
+
58
+ const { watch, autoReload, password } = flags;
59
+ const mode = (flags.mode ?? (watch ? 'development' : 'production')) as Mode;
60
+ const { host, port } = this._parseServerAddress(flags.server);
61
+ const resourceName = flags.resourceName ?? path.basename(process.cwd());
62
+
63
+ const config = createFivemRspackConfig({ mode });
64
+ const compiler = rspack(config);
65
+
66
+ if (autoReload) {
67
+ if (!password) {
68
+ this.logger.error(
69
+ 'RCON password is required for auto-reload. Please provide it using the --password flag.',
70
+ );
71
+ this.exit(1);
72
+ }
73
+
74
+ const rcon = new Rcon({ host, port, password });
75
+ await this._enableAutoReload(compiler, rcon, resourceName);
76
+ }
77
+
78
+ if (flags.watch) {
79
+ this.logger.info(`Building FiveM resource in watch mode...\n`);
80
+ compiler.watch({}, this._compilerCallback.bind(this));
81
+ } else {
82
+ this.logger.info(`Building FiveM resource...\n`);
83
+ compiler.run(this._compilerCallback.bind(this));
84
+ }
85
+ }
86
+
87
+ private _parseServerAddress(address: string): ServerAddress {
88
+ const match = /^(?<host>[^:]+):(?<port>\d+)$/u.exec(address);
89
+ const { host, port } = match?.groups ?? {};
90
+
91
+ if (!host || !port) {
92
+ throw new Error(
93
+ `Invalid server address format: ${address}. Expected format is "ip:port".`,
94
+ );
95
+ }
96
+
97
+ return { host, port: parseInt(port) };
98
+ }
99
+
100
+ private _compilerCallback(err: Error | null, stats: Stats | undefined): void {
101
+ if (err) {
102
+ this.logger.error(getErrMsg(err));
103
+ if ('details' in err) this.logger.error(err.details as string);
104
+ if ('stack' in err) this.logger.error(err.stack);
105
+ return;
106
+ }
107
+
108
+ if (stats) this.logger.log(stats.toString(), '\n');
109
+ }
110
+
111
+ private async _enableAutoReload(
112
+ compiler: Compiler,
113
+ rcon: Rcon,
114
+ resourceName: string,
115
+ ): Promise<void> {
116
+ try {
117
+ await rcon.connect();
118
+ } catch (error) {
119
+ this.logger.error(`Failed to connect to FiveM server via RCON: ${getErrMsg(error)}`);
120
+ this.exit(1);
121
+ }
122
+
123
+ compiler.hooks.done.tapPromise('FivemAutoReloadPlugin', async (stats) => {
124
+ if (stats.hasErrors()) {
125
+ this.logger.warning('Build failed. Skipping FiveM resource reload.\n');
126
+ return;
127
+ }
128
+
129
+ this.logger.info(`Reloading FiveM resource "${resourceName}"...`);
130
+ try {
131
+ await rcon.send(`refresh; ensure ${resourceName}`);
132
+ this.logger.success(`FiveM resource reloaded successfully.\n`);
133
+ } catch (error) {
134
+ this.logger.error(`Failed to reload FiveM resource: ${getErrMsg(error)}\n`);
135
+ }
136
+ });
137
+ }
138
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { FivemBuildCommand } from './fivem-build-command';
2
+
3
+ export const commands = {
4
+ 'build:fivem': FivemBuildCommand,
5
+ };