@bf6mods/cli 1.2.0 → 1.2.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/README.md +78 -78
- package/dist/cli/index.js +106 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/resources/prepare/tsconfig.json +4 -1
- package/dist/resources/templates/All/package.json +15 -14
- package/dist/resources/templates/All/tsconfig.json +4 -4
- package/dist/resources/templates/Basic/bf6.config.ts +8 -7
- package/dist/resources/templates/Basic/src/index.ts +112 -112
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
# bf6mods
|
|
2
|
-
|
|
3
|
-
Create Battlefield 6 mods quickly with a vite like development experience.
|
|
4
|
-
|
|
5
|
-
## Getting Started
|
|
6
|
-
|
|
7
|
-
To create a new project, simply run the following command and answer the questions
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
npx @bf6mods/cli init
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-

|
|
14
|
-
|
|
15
|
-
## Import from JSON
|
|
16
|
-
|
|
17
|
-
Already have a project? Just export your currently existing project in [portal.battlefield.com](https://portal.battlefield.com),
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
npx @bf6mods/cli import <
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Deploying Project to Portal
|
|
24
|
-
|
|
25
|
-
Just run `npm run build` in your project dir, open [portal.battlefield.com](https://portal.battlefield.com), click import, and select the `dist/mod.json` file.
|
|
26
|
-
|
|
27
|
-
## Features
|
|
28
|
-
|
|
29
|
-
- Use multiple different files, instead of just one large TypeScript file!
|
|
30
|
-
- Use a programatic interface for defining your gamemode.
|
|
31
|
-
- Extensive documentation
|
|
32
|
-
- Extended standard library (still in progress)
|
|
33
|
-
- Hot reload
|
|
34
|
-
- Automatic string injection
|
|
35
|
-
- Logging from BF6! (Only when in hosting locally)
|
|
36
|
-
|
|
37
|
-
## Structure
|
|
38
|
-
|
|
39
|
-
`bf6mods` is very configurable, but the following structure is what can be expected from any mod.
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
my-mod/
|
|
43
|
-
├─ src/
|
|
44
|
-
│ ├─ index.ts
|
|
45
|
-
│ ├─ scenes/
|
|
46
|
-
│ │ └─ MyMap.spatial.json
|
|
47
|
-
├─ bf6.config.ts
|
|
48
|
-
├─ package.json
|
|
49
|
-
└─ dist/
|
|
50
|
-
└─ mod.json
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## `bf6.config.ts`
|
|
54
|
-
|
|
55
|
-
Here is a short example of a `bf6.config.ts`.
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
export default defineBf6Config({
|
|
59
|
-
name: "AcePursuit",
|
|
60
|
-
description: "A fast-paced race mod",
|
|
61
|
-
outDir: "dist",
|
|
62
|
-
entrypoint: "src/index.ts",
|
|
63
|
-
scenes: [[MapId.LiberationPeak, "src/scenes/AcePursuit.spatial.json"]],
|
|
64
|
-
game: {
|
|
65
|
-
mutators: {
|
|
66
|
-
// ...
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
````
|
|
71
|
-
|
|
72
|
-
## @bf6mods/sdk
|
|
73
|
-
|
|
74
|
-
This is a seperate library that exports the `PortalSdk`'s `mod` and `modlib`. Additionally it exports some stdlib helper functions and classes to help accelerate development.
|
|
75
|
-
|
|
76
|
-
## Join the discord with fellow modders!
|
|
77
|
-
|
|
78
|
-
You can join the discord by clicking this [link](https://discord.gg/2gJ9fheYYK)!
|
|
1
|
+
# bf6mods
|
|
2
|
+
|
|
3
|
+
Create Battlefield 6 mods quickly with a vite like development experience.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
To create a new project, simply run the following command and answer the questions, ensure that you are using Node v22 and above.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npx @bf6mods/cli init
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## Import from JSON
|
|
16
|
+
|
|
17
|
+
Already have a project? Just export your currently existing project in [portal.battlefield.com](https://portal.battlefield.com), by editing your mod, clicking the three dots on the top right, click export. Finally just run...
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
npx @bf6mods/cli import <export file> <output directory>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Deploying Project to Portal
|
|
24
|
+
|
|
25
|
+
Just run `npm run build` in your project dir, open [portal.battlefield.com](https://portal.battlefield.com), click import, and select the `dist/mod.json` file.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- Use multiple different files, instead of just one large TypeScript file!
|
|
30
|
+
- Use a programatic interface for defining your gamemode.
|
|
31
|
+
- Extensive documentation
|
|
32
|
+
- Extended standard library (still in progress)
|
|
33
|
+
- Hot reload
|
|
34
|
+
- Automatic string injection
|
|
35
|
+
- Logging from BF6! (Only when in hosting locally)
|
|
36
|
+
|
|
37
|
+
## Structure
|
|
38
|
+
|
|
39
|
+
`bf6mods` is very configurable, but the following structure is what can be expected from any mod.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
my-mod/
|
|
43
|
+
├─ src/
|
|
44
|
+
│ ├─ index.ts
|
|
45
|
+
│ ├─ scenes/
|
|
46
|
+
│ │ └─ MyMap.spatial.json
|
|
47
|
+
├─ bf6.config.ts
|
|
48
|
+
├─ package.json
|
|
49
|
+
└─ dist/
|
|
50
|
+
└─ mod.json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## `bf6.config.ts`
|
|
54
|
+
|
|
55
|
+
Here is a short example of a `bf6.config.ts`.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
export default defineBf6Config({
|
|
59
|
+
name: "AcePursuit",
|
|
60
|
+
description: "A fast-paced race mod",
|
|
61
|
+
outDir: "dist",
|
|
62
|
+
entrypoint: "src/index.ts",
|
|
63
|
+
scenes: [[MapId.LiberationPeak, "src/scenes/AcePursuit.spatial.json"]],
|
|
64
|
+
game: {
|
|
65
|
+
mutators: {
|
|
66
|
+
// ...
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
````
|
|
71
|
+
|
|
72
|
+
## @bf6mods/sdk
|
|
73
|
+
|
|
74
|
+
This is a seperate library that exports the `PortalSdk`'s `mod` and `modlib`. Additionally it exports some stdlib helper functions and classes to help accelerate development.
|
|
75
|
+
|
|
76
|
+
## Join the discord with fellow modders!
|
|
77
|
+
|
|
78
|
+
You can join the discord by clicking this [link](https://discord.gg/2gJ9fheYYK)!
|
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import colors from "colors";
|
|
2
3
|
import { Command } from "commander";
|
|
3
4
|
import fs from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { AttachmentType } from "@bf6mods/sdk";
|
|
7
|
-
import colors from "colors";
|
|
8
8
|
import { createJiti } from "jiti";
|
|
9
9
|
import { rolldown } from "rolldown";
|
|
10
10
|
import stripAnsi from "strip-ansi";
|
|
@@ -16,7 +16,7 @@ import * as prompts from "@clack/prompts";
|
|
|
16
16
|
import { genExport, genInlineTypeImport } from "knitwork";
|
|
17
17
|
|
|
18
18
|
//#region package.json
|
|
19
|
-
var version = "1.2.
|
|
19
|
+
var version$1 = "1.2.2";
|
|
20
20
|
var description = "CLI and library for bundling BF6 mods";
|
|
21
21
|
var bin = { "bf6mods": "./dist/cli/index.js" };
|
|
22
22
|
|
|
@@ -50,6 +50,11 @@ const printToConsole = (message, error = false) => {
|
|
|
50
50
|
if (error) console.error(`${message}${spacing}${formattedTime}`);
|
|
51
51
|
else console.log(`${message}${spacing}${formattedTime}`);
|
|
52
52
|
};
|
|
53
|
+
const readableList = (initialValues) => {
|
|
54
|
+
const values = initialValues.map((v) => `"${v}"`);
|
|
55
|
+
if (values.length > 1) return `${values.slice(0, -1).join(", ")} or ${values.slice(-1)}`;
|
|
56
|
+
return values[0];
|
|
57
|
+
};
|
|
53
58
|
|
|
54
59
|
//#endregion
|
|
55
60
|
//#region src/cli/build/generated-strings.ts
|
|
@@ -192,7 +197,8 @@ async function buildEntrypoint(entry, bf6Strings, generateStringsFromLiterals) {
|
|
|
192
197
|
return (await (await rolldown({
|
|
193
198
|
input: entry,
|
|
194
199
|
plugins: [extractBf6Strings(bf6Strings, generateStringsFromLiterals)],
|
|
195
|
-
logLevel: "debug"
|
|
200
|
+
logLevel: "debug",
|
|
201
|
+
resolve: { alias: { modlib: "@bf6mods/sdk" } }
|
|
196
202
|
})).generate({
|
|
197
203
|
format: "esm",
|
|
198
204
|
inlineDynamicImports: true
|
|
@@ -208,10 +214,12 @@ async function collectAttachments(config, workingDir, tsAttachment, generatedStr
|
|
|
208
214
|
const attachments = [];
|
|
209
215
|
const mapRotation = [];
|
|
210
216
|
if (tsAttachment) attachments.push(tsAttachment);
|
|
211
|
-
if (config.strings) {
|
|
212
|
-
const strPath = path.resolve(workingDir, config.strings);
|
|
213
|
-
|
|
214
|
-
|
|
217
|
+
if (config.strings || Object.keys(generatedStrings ?? {}).length > 0) {
|
|
218
|
+
const strPath = path.resolve(workingDir, config.strings ?? "strings.json");
|
|
219
|
+
let raw = "{}";
|
|
220
|
+
if (fs.existsSync(strPath)) raw = await fs.promises.readFile(strPath, "utf8");
|
|
221
|
+
else printToConsole(colors.yellow(`⚠️ No strings.json found, generating from literals only.`));
|
|
222
|
+
const attachment = createStringsAttachment(strPath, raw, generatedStrings);
|
|
215
223
|
attachments.push(attachment);
|
|
216
224
|
}
|
|
217
225
|
if (config.scenes) {
|
|
@@ -469,6 +477,10 @@ async function dev() {
|
|
|
469
477
|
}
|
|
470
478
|
}
|
|
471
479
|
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region ../sdk/package.json
|
|
482
|
+
var version = "1.2.0";
|
|
483
|
+
|
|
472
484
|
//#endregion
|
|
473
485
|
//#region src/cli/init.ts
|
|
474
486
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -492,6 +504,9 @@ function renameFilesRecursively(dir, modName) {
|
|
|
492
504
|
if (entry.name === "package.json") {
|
|
493
505
|
const pkg = JSON.parse(fs.readFileSync(newPath, "utf-8"));
|
|
494
506
|
pkg.name = modName;
|
|
507
|
+
if (!pkg.devDependencies) pkg.devDependencies = {};
|
|
508
|
+
pkg.devDependencies["@bf6mods/cli"] = `^${version$1}`;
|
|
509
|
+
pkg.devDependencies["@bf6mods/sdk"] = `^${version}`;
|
|
495
510
|
fs.writeFileSync(newPath, JSON.stringify(pkg, null, 2));
|
|
496
511
|
}
|
|
497
512
|
if (fs.statSync(newPath).isDirectory()) renameFilesRecursively(newPath, modName);
|
|
@@ -544,7 +559,7 @@ function emptyDir(dir) {
|
|
|
544
559
|
});
|
|
545
560
|
}
|
|
546
561
|
}
|
|
547
|
-
async function init(argTargetDir) {
|
|
562
|
+
async function init(argTargetDir, options) {
|
|
548
563
|
prompts.intro("Initialize Bf6 Mod");
|
|
549
564
|
const path$1 = argTargetDir ? argTargetDir : await prompts.text({
|
|
550
565
|
message: "Where should we create your project?",
|
|
@@ -555,7 +570,7 @@ async function init(argTargetDir) {
|
|
|
555
570
|
}
|
|
556
571
|
});
|
|
557
572
|
if (prompts.isCancel(path$1)) return cancel();
|
|
558
|
-
let name = await prompts.text({
|
|
573
|
+
let name = options?.name ? options.name : await prompts.text({
|
|
559
574
|
message: "What is the name of your mod?",
|
|
560
575
|
placeholder: "Ace Pursuit",
|
|
561
576
|
validate: (value) => {
|
|
@@ -566,7 +581,18 @@ async function init(argTargetDir) {
|
|
|
566
581
|
name = name.trim();
|
|
567
582
|
if (fs.existsSync(path$1) && !isEmpty(path$1)) {
|
|
568
583
|
let overwrite;
|
|
569
|
-
|
|
584
|
+
if (options?.onExists) switch (options.onExists) {
|
|
585
|
+
case "overwrite":
|
|
586
|
+
overwrite = "yes";
|
|
587
|
+
break;
|
|
588
|
+
case "cancel":
|
|
589
|
+
overwrite = "no";
|
|
590
|
+
break;
|
|
591
|
+
case "ignore":
|
|
592
|
+
overwrite = "ignore";
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
const res = overwrite ? overwrite : await prompts.select({
|
|
570
596
|
message: (path$1 === "." ? "Current directory" : `Target directory "${path$1}"`) + ` is not empty. Please choose how to proceed:`,
|
|
571
597
|
options: [
|
|
572
598
|
{
|
|
@@ -594,7 +620,7 @@ async function init(argTargetDir) {
|
|
|
594
620
|
return;
|
|
595
621
|
}
|
|
596
622
|
}
|
|
597
|
-
const template = await prompts.select({
|
|
623
|
+
const template = options?.template ? options.template : await prompts.select({
|
|
598
624
|
message: "Select a template:",
|
|
599
625
|
options: templates.map((template$1) => {
|
|
600
626
|
return {
|
|
@@ -605,12 +631,17 @@ async function init(argTargetDir) {
|
|
|
605
631
|
});
|
|
606
632
|
if (prompts.isCancel(template)) return cancel();
|
|
607
633
|
await startProject(path$1, template, name);
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
634
|
+
if (options?.installDependencies) {
|
|
635
|
+
const s = prompts.spinner();
|
|
636
|
+
s.start("Installing via npm");
|
|
637
|
+
if (installDependencies(path$1)) s.stop("Installed via npm");
|
|
638
|
+
else s.stop("Failed to install via npm", 1);
|
|
639
|
+
const nextSteps = `cd ${path$1}\nnpm run build`;
|
|
640
|
+
prompts.note(nextSteps, "Next steps.");
|
|
641
|
+
} else {
|
|
642
|
+
const nextSteps = `cd ${path$1}\nnpm i\nnpm run build`;
|
|
643
|
+
prompts.note(nextSteps, "Next steps.");
|
|
644
|
+
}
|
|
614
645
|
}
|
|
615
646
|
|
|
616
647
|
//#endregion
|
|
@@ -623,27 +654,47 @@ async function writeFileSafe(filePath, data) {
|
|
|
623
654
|
function getMapKeyByValue(value) {
|
|
624
655
|
return Object.keys(MapId).find((k) => MapId[k] === value);
|
|
625
656
|
}
|
|
626
|
-
async function importFile(input, output, name) {
|
|
657
|
+
async function importFile(input, output, name, logging = false) {
|
|
658
|
+
const start = performance.now();
|
|
627
659
|
const workingDir = path.resolve(".");
|
|
628
660
|
const entrypoint = path.resolve(workingDir, input);
|
|
629
661
|
const outDir = path.resolve(workingDir, output);
|
|
630
662
|
if (!fs.existsSync(entrypoint)) throw new Error("Cannot find strings file");
|
|
663
|
+
if (logging) printToConsole(colors.cyan(`📦 Importing config from: ${entrypoint}`));
|
|
631
664
|
const config = JSON.parse(await fs.promises.readFile(entrypoint, { encoding: "utf8" }));
|
|
632
|
-
if (!fs.existsSync(outDir))
|
|
665
|
+
if (!fs.existsSync(outDir)) {
|
|
666
|
+
await fs.promises.mkdir(outDir, { recursive: true });
|
|
667
|
+
if (logging) printToConsole(colors.cyan(`📁 Created output directory: ${outDir}`));
|
|
668
|
+
}
|
|
633
669
|
await startProject(outDir, "None", name ?? config.name);
|
|
634
670
|
let typescriptFile;
|
|
635
671
|
let stringsFile;
|
|
636
672
|
const scenes = [];
|
|
637
673
|
const promises = [];
|
|
638
|
-
if (config.attachments)
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
674
|
+
if (config.attachments) {
|
|
675
|
+
let unamedAttachmentIndex = 0;
|
|
676
|
+
for (const attachment of config.attachments) {
|
|
677
|
+
let filename = attachment.filename?.trim();
|
|
678
|
+
if (!filename) {
|
|
679
|
+
unamedAttachmentIndex++;
|
|
680
|
+
let ext = "";
|
|
681
|
+
if (attachment.attachmentType === AttachmentType.TypeScript) ext = ".ts";
|
|
682
|
+
else if (attachment.attachmentType === AttachmentType.Strings) ext = ".strings.json";
|
|
683
|
+
else if (attachment.attachmentType === AttachmentType.SpatialData) ext = ".spatial.json";
|
|
684
|
+
if (unamedAttachmentIndex > 1) filename = `attachment_${unamedAttachmentIndex}${ext}`;
|
|
685
|
+
else filename = `attachment${ext}`;
|
|
686
|
+
}
|
|
687
|
+
if (attachment.attachmentType === AttachmentType.TypeScript) typescriptFile = filename;
|
|
688
|
+
if (attachment.attachmentType === AttachmentType.Strings) stringsFile = filename;
|
|
689
|
+
if (attachment.attachmentType === AttachmentType.SpatialData) continue;
|
|
690
|
+
promises.push(writeFileSafe(path.resolve(outDir, "src", filename), atob(attachment.attachmentData.original)));
|
|
691
|
+
if (logging) printToConsole(`${colors.green("✓")} Wrote attachment: ${colors.yellow(filename)}`);
|
|
692
|
+
}
|
|
643
693
|
}
|
|
644
694
|
if (config.mapRotation?.length) for (const map of config.mapRotation) {
|
|
645
695
|
promises.push(writeFileSafe(path.resolve(outDir, "src", "scenes", map.spatialAttachment.filename), atob(map.spatialAttachment.attachmentData.original)));
|
|
646
696
|
scenes.push([map.id, `src/scenes/${map.spatialAttachment.filename}`]);
|
|
697
|
+
if (logging) printToConsole(`${colors.green("✓")} Added scene: ${colors.yellow(map.id)}`);
|
|
647
698
|
}
|
|
648
699
|
const bf6ConfigContent = `export default defineBf6Config(${stringifyWithRaw({
|
|
649
700
|
name: name ?? config.name,
|
|
@@ -661,6 +712,8 @@ async function importFile(input, output, name) {
|
|
|
661
712
|
})});\n`;
|
|
662
713
|
promises.push(writeFileSafe(path.resolve(outDir, "bf6.config.ts"), bf6ConfigContent));
|
|
663
714
|
await Promise.all(promises);
|
|
715
|
+
const duration = ((performance.now() - start) / 1e3).toFixed(2);
|
|
716
|
+
if (logging) printToConsole(`${colors.green.bold("✓")} Import complete (${duration}s) → ${colors.yellow(outDir)}`);
|
|
664
717
|
}
|
|
665
718
|
function stringifyWithRaw(value, options = {}) {
|
|
666
719
|
return stringifyObject(value, {
|
|
@@ -707,9 +760,27 @@ const genNamespaceAugmentation = (name, contents) => {
|
|
|
707
760
|
//#endregion
|
|
708
761
|
//#region src/cli/index.ts
|
|
709
762
|
const program = new Command();
|
|
710
|
-
program.name(Object.keys(bin)[0]).description(description).version(version);
|
|
711
|
-
program.command("init").argument("[directory]").description("Create a new bf6 mod").action(async (directory) => {
|
|
712
|
-
|
|
763
|
+
program.name(Object.keys(bin)[0]).description(description).version(version$1);
|
|
764
|
+
program.command("init").argument("[directory]").option("--name <name>", "The name of your mod").option("--template <template>", "The template to use").option("--on-exists <action>", `Either "overwrite", "cancel", or "ignore" as to what to do if files exist in the directory`).option("--no-install-dependencies", `Prevents install of dependencies`).description("Create a new bf6 mod").action(async (directory, options) => {
|
|
765
|
+
const { name, template, onExists, installDependencies: installDependencies$1 } = options;
|
|
766
|
+
if (onExists !== void 0 && ![
|
|
767
|
+
"overwrite",
|
|
768
|
+
"cancel",
|
|
769
|
+
"ignore"
|
|
770
|
+
].includes(onExists)) {
|
|
771
|
+
printToConsole(`${colors.red.bold("✗")} Invalid value for --on-exists: "${onExists}". Expected one of "overwrite", "cancel", or "ignore".`, true);
|
|
772
|
+
process.exit(1);
|
|
773
|
+
}
|
|
774
|
+
if (template !== void 0 && !templates.includes(template)) {
|
|
775
|
+
printToConsole(`${colors.red.bold("✗")} Invalid value for --template: "${template}". Expected one of ${readableList(templates)}.`, true);
|
|
776
|
+
process.exit(1);
|
|
777
|
+
}
|
|
778
|
+
await init(directory, {
|
|
779
|
+
name,
|
|
780
|
+
template,
|
|
781
|
+
onExists,
|
|
782
|
+
installDependencies: installDependencies$1
|
|
783
|
+
});
|
|
713
784
|
});
|
|
714
785
|
program.command("build").description("build the bf6 mod").action(async () => {
|
|
715
786
|
await build();
|
|
@@ -720,9 +791,10 @@ program.command("prepare").description("prepare the types for bf6 mod").action(a
|
|
|
720
791
|
program.command("dev").description("watch the changes in src, and recompile as needed").action(async () => {
|
|
721
792
|
await dev();
|
|
722
793
|
});
|
|
723
|
-
program.command("import").argument("<input>").argument("<output>").description("decompiles the json config of a mod into a new project").action(async (input, output) => {
|
|
724
|
-
|
|
725
|
-
|
|
794
|
+
program.command("import").argument("<input>").argument("<output>").option("--no-install-dependencies", `Prevents install of dependencies`).description("decompiles the json config of a mod into a new project").action(async (input, output, options) => {
|
|
795
|
+
const { installDependencies: installDependencies$1 } = options;
|
|
796
|
+
await importFile(input, output, void 0, true);
|
|
797
|
+
if (installDependencies$1) installDependencies$1(output);
|
|
726
798
|
});
|
|
727
799
|
program.command("log").argument("[input]").description("logs the output from a locally running server").action(async (input) => {
|
|
728
800
|
new Bf6Logger(input).start();
|
|
@@ -730,6 +802,10 @@ program.command("log").argument("[input]").description("logs the output from a l
|
|
|
730
802
|
program.exitOverride((_err) => {
|
|
731
803
|
if (process.env.EXIT_CODE === "none") process.exit(0);
|
|
732
804
|
});
|
|
805
|
+
if (!process.argv.slice(2).length) {
|
|
806
|
+
program.outputHelp();
|
|
807
|
+
process.exit(0);
|
|
808
|
+
}
|
|
733
809
|
program.parse();
|
|
734
810
|
|
|
735
811
|
//#endregion
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["program","key:\n\t\t\t\t\t\t\t| string\n\t\t\t\t\t\t\t| number\n\t\t\t\t\t\t\t| bigint\n\t\t\t\t\t\t\t| boolean\n\t\t\t\t\t\t\t| RegExp\n\t\t\t\t\t\t\t| null\n\t\t\t\t\t\t\t| undefined","MapIdEnum","tsAttachment: Attachment | undefined","__filename","__dirname","attachments: Attachment[]","mapRotation: MapType[]","mapId: MapIdEnum","scene: string","finalJson: ConfigType","watcher: FSWatcher | undefined","timer: NodeJS.Timeout | undefined","targets: string[]","logger: Bf6Logger | undefined","path","overwrite: \"yes\" | \"no\" | \"ignore\" | undefined","template","MapIdEnum","typescriptFile: string | undefined","stringsFile: string | undefined","scenes: [string, string][]","promises: Promise<unknown>[]","path","__filename","__dirname","pkg.bin","pkg.description","pkg.version"],"sources":["../../package.json","../../src/resources/prepare/types/config.ts","../../src/cli/utils.ts","../../src/cli/build/generated-strings.ts","../../src/cli/build/index.ts","../../src/cli/log.ts","../../src/cli/dev.ts","../../src/cli/init.ts","../../src/cli/import.ts","../../src/cli/prepare.ts","../../src/cli/index.ts"],"sourcesContent":["{\n\t\"name\": \"@bf6mods/cli\",\n\t\"version\": \"1.2.0\",\n\t\"description\": \"CLI and library for bundling BF6 mods\",\n\t\"license\": \"MIT\",\n\t\"type\": \"module\",\n\t\"bin\": {\n\t\t\"bf6mods\": \"./dist/cli/index.js\"\n\t},\n\t\"main\": \"./dist/index.js\",\n\t\"types\": \"./dist/index.d.ts\",\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"start\": \"cross-env EXIT_CODE=none tsx ./src/cli/index.ts\"\n\t},\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.ts\",\n\t\t\t\"import\": \"./dist/index.js\"\n\t\t}\n\t},\n\t\"files\": [\n\t\t\"dist/\"\n\t],\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/bf6mods/bf6mods.git\"\n\t},\n\t\"dependencies\": {\n\t\t\"@bf6mods/sdk\": \"*\",\n\t\t\"@clack/prompts\": \"^0.11.0\",\n\t\t\"chokidar\": \"^4.0.3\",\n\t\t\"colors\": \"^1.4.0\",\n\t\t\"commander\": \"^14.0.1\",\n\t\t\"jiti\": \"^2.6.1\",\n\t\t\"knitwork\": \"^1.2.0\",\n\t\t\"rolldown\": \"^1.0.0-beta.44\",\n\t\t\"stringify-object\": \"^6.0.0\",\n\t\t\"strip-ansi\": \"^7.1.2\"\n\t},\n\t\"publishConfig\": {\n\t\t\"access\": \"public\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@oxc-project/types\": \"^0.95.0\",\n\t\t\"@types/stringify-object\": \"^4.0.5\",\n\t\t\"cross-env\": \"^10.1.0\",\n\t\t\"tsx\": \"^4.20.6\"\n\t}\n}\n","import type { ConfigType } from \"@bf6mods/sdk\";\n\nexport enum MapId {\n\tFireStorm = \"MP_FireStorm-ModBuilderCustom0\",\n\tSiegeOfCairo = \"MP_Abbasid-ModBuilderCustom0\",\n\tEmpireState = \"MP_Aftermath-ModBuilderCustom0\",\n\tIberianOffensive = \"MP_Battery-ModBuilderCustom0\",\n\tLiberationPeak = \"MP_Capstone-ModBuilderCustom0\",\n\tManhattanBridge = \"MP_Dumbo-ModBuilderCustom0\",\n\tSaintsQuarter = \"MP_Limestone-ModBuilderCustom0\",\n\tNewSobekCity = \"MP_Outskirts-ModBuilderCustom0\",\n\tMirakValley = \"MP_Tungsten-ModBuilderCustom0\",\n}\n\n/**\n * Represents the root configuration for a Battlefield 6 mod project.\n *\n * This file combines both build-level settings (like entrypoints and output directories)\n * and in-game configuration data (such as mutators and asset restrictions)\n * under the `game` key.\n *\n * Use {@link defineBf6Config} to define and validate this structure\n * in your `bf6.config.ts` file.\n */\nexport type Bf6Config = {\n\t/**\n\t * The human-readable name of the mod.\n\t *\n\t * Typically matches the project name used in the BF6 mod editor or\n\t * the display name shown in the in-game mod browser.\n\t */\n\tname: string;\n\n\t/**\n\t * A short description of the mod’s purpose or content.\n\t * Used for display and metadata purposes.\n\t */\n\tdescription: string;\n\n\t/**\n\t * The output directory where compiled or bundled files are written.\n\t * Usually something like `\"dist\"` or `\"build\"`.\n\t */\n\toutDir: string;\n\n\t/**\n\t * Whether or not to output the built files like the entrypoint typescript file\n\t */\n\toutputArtifacts?: boolean;\n\n\t/**\n\t * The main entrypoint for your mod’s TypeScript source file.\n\t *\n\t * This is typically something like `\"src/index.ts\"` or `\"src/main.ts\"`.\n\t */\n\tentrypoint?: string;\n\n\t/**\n\t * One or more scene file paths (.json) that the mod includes.\n\t * If no file path is provided, it loads in the default spatial data.\n\t */\n\tscenes?: ([MapId, string] | MapId)[];\n\n\t/**\n\t * Path to a JSON file or other source that defines localized strings\n\t * used by the mod (optional).\n\t */\n\tstrings?: string;\n\n\t/**\n\t * Generate strings automatically, by default this is enabled\n\t */\n\tgenerateStrings?: boolean;\n\n\t/**\n\t * Controls whether build output should be minified.\n\t *\n\t * Can be a boolean (to toggle all minification),\n\t * or an object specifying independent settings for JS and JSON files.\n\t *\n\t * @example\n\t * ```ts\n\t * minify: true\n\t * // or\n\t * minify: { js: true, json: false }\n\t * ```\n\t */\n\tminify?:\n\t\t| {\n\t\t\t\t/** Whether to minify JavaScript output. */\n\t\t\t\tjs?: boolean;\n\t\t\t\t/** Whether to minify JSON output. */\n\t\t\t\tjson?: boolean;\n\t\t }\n\t\t/** Enables or disables all minification. */\n\t\t| boolean;\n\n\t/**\n\t * The underlying in-game configuration derived from `ConfigType`\n\t * in the BF6 SDK. Includes gameplay-level details like mutators,\n\t * asset restrictions, and team compositions.\n\t *\n\t * The following properties are **excluded**:\n\t * - `attachments`\n\t * - `workspace`\n\t * - `mapRotation`\n\t * - `name`\n\t * - `description`\n\t *\n\t * since those are either redundant or handled elsewhere.\n\t */\n\tgame: Omit<\n\t\tConfigType,\n\t\t\"attachments\" | \"workspace\" | \"mapRotation\" | \"name\" | \"description\"\n\t>;\n};\n\n/**\n * Defines and validates a Battlefield 6 mod configuration object.\n *\n * This helper ensures your config is properly typed and can be\n * statically analyzed by TypeScript.\n *\n * @example\n * ```ts\n * export default defineBf6Config({\n * name: \"My Custom Mod\",\n * description: \"Adds custom rules\",\n * outDir: \"dist\",\n * entrypoint: \"src/main.ts\",\n * game: {\n * mutators: [],\n * assetRestrictions: {},\n * teamComposition: [],\n * },\n * });\n * ```\n *\n * @param bf6Config The configuration object to validate and return.\n * @returns The validated Battlefield 6 configuration object.\n */\nexport function defineBf6Config(bf6Config: Bf6Config): Bf6Config {\n\treturn bf6Config;\n}\n","import colors from \"colors\";\nimport stripAnsi from \"strip-ansi\";\n\n/**\n * Safely prints a message with a right-aligned timestamp.\n */\nexport const printToConsole = (message: string, error: boolean = false) => {\n\tconst now = new Date();\n\tconst formattedTime = colors.grey(now.toLocaleTimeString());\n\tconst terminalWidth = process.stdout.columns || 80;\n\n\tconst timeLength = stripAnsi(formattedTime).length;\n\tconst messageLength = stripAnsi(message).length;\n\tconst available = terminalWidth - (messageLength + timeLength);\n\n\tconst spacing = available > 1 ? \" \".repeat(available) : \" \";\n\n\tif (error) console.error(`${message}${spacing}${formattedTime}`);\n\telse console.log(`${message}${spacing}${formattedTime}`);\n};\n","import type { Node, VariableDeclarator } from \"@oxc-project/types\";\nimport colors from \"colors\";\nimport type { Plugin } from \"rolldown\";\n\nexport function extractBf6Strings(\n\tbf6Strings: Record<string, string>,\n\tgenerateFromLiterals: boolean,\n): Plugin {\n\treturn {\n\t\tname: \"extract-bf6-strings\",\n\t\tgenerateBundle(_options, bundle) {\n\t\t\tif (!generateFromLiterals) return;\n\n\t\t\tlet id = 0;\n\t\t\tconst existingStrings = new Set(Object.values(bf6Strings));\n\n\t\t\tfor (const [_file, output] of Object.entries(bundle)) {\n\t\t\t\tif (output.type !== \"chunk\") continue;\n\t\t\t\tconst code = output.code;\n\n\t\t\t\t// Parse the code with rolldown’s parser\n\t\t\t\tconst program = this.parse(code, {\n\t\t\t\t\tlang: \"ts\",\n\t\t\t\t\tastType: \"js\",\n\t\t\t\t\trange: true,\n\t\t\t\t});\n\n\t\t\t\t// Visit every node recursively to collect string literals\n\t\t\t\tfunction walk(node: Node) {\n\t\t\t\t\tif (node.type === \"Literal\") {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof node.value !== \"string\" ||\n\t\t\t\t\t\t\tnode.value === \"\" ||\n\t\t\t\t\t\t\tnode.value === \"----uniquename----\"\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!(node.value in bf6Strings) &&\n\t\t\t\t\t\t\t!existingStrings.has(node.value)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\texistingStrings.add(node.value);\n\t\t\t\t\t\t\tbf6Strings[`__auto__${id++}`] = node.value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const value of Object.values(node)) {\n\t\t\t\t\t\tif (!value) continue;\n\t\t\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\t\t\tfor (const v of value) {\n\t\t\t\t\t\t\t\tif (v && typeof v === \"object\" && \"type\" in v) walk(v as Node);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (typeof value === \"object\" && \"type\" in value) {\n\t\t\t\t\t\t\twalk(value as Node);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (const item of program.body) {\n\t\t\t\t\twalk(item);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tmoduleParsed(moduleInfo) {\n\t\t\tif (moduleInfo.code && moduleInfo.exports.includes(\"bf6Strings\")) {\n\t\t\t\tconst code = moduleInfo.code;\n\t\t\t\tconst getSnippet = (\n\t\t\t\t\tstart: number,\n\t\t\t\t\tend: number,\n\t\t\t\t\tlinesBefore = 2,\n\t\t\t\t\tlinesAfter = 2,\n\t\t\t\t) => {\n\t\t\t\t\tconst lines = code.split(/\\r?\\n/);\n\t\t\t\t\tconst startLine = Math.max(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tcode.slice(0, start).split(/\\r?\\n/).length - 1 - linesBefore,\n\t\t\t\t\t);\n\t\t\t\t\tconst endLine = Math.min(\n\t\t\t\t\t\tlines.length,\n\t\t\t\t\t\tcode.slice(0, end).split(/\\r?\\n/).length - 1 + linesAfter,\n\t\t\t\t\t);\n\t\t\t\t\treturn lines\n\t\t\t\t\t\t.slice(startLine, endLine)\n\t\t\t\t\t\t.map((line, i) => {\n\t\t\t\t\t\t\tconst actualLine = startLine + i + 1;\n\t\t\t\t\t\t\treturn `${actualLine.toString().padStart(3)} | ${line}`;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t};\n\n\t\t\t\tconst program = this.parse(code, {\n\t\t\t\t\tlang: \"ts\",\n\t\t\t\t\tastType: \"js\",\n\t\t\t\t\trange: true,\n\t\t\t\t});\n\n\t\t\t\tconst extractObjectLiteral = (declaration: VariableDeclarator) => {\n\t\t\t\t\tif (declaration.init?.type !== \"ObjectExpression\") {\n\t\t\t\t\t\tconst snippet = getSnippet(...(declaration.range ?? [0, 0]));\n\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings export: expected an object literal.\\n\\n${snippet}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const property of declaration.init.properties) {\n\t\t\t\t\t\tif (property.type !== \"Property\") {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings property: expected a key/value pair.\\nFound type: ${property.type}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet key:\n\t\t\t\t\t\t\t| string\n\t\t\t\t\t\t\t| number\n\t\t\t\t\t\t\t| bigint\n\t\t\t\t\t\t\t| boolean\n\t\t\t\t\t\t\t| RegExp\n\t\t\t\t\t\t\t| null\n\t\t\t\t\t\t\t| undefined;\n\t\t\t\t\t\tif (property.key.type === \"Literal\") {\n\t\t\t\t\t\t\tkey = property.key.value;\n\t\t\t\t\t\t} else if (property.key.type === \"Identifier\") {\n\t\t\t\t\t\t\tkey = property.key.name;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings key: expected a string literal or identifier.\\nFound type: ${property.key.type}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (property.value.type !== \"Literal\") {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings value: expected a string literal.\\nFound type: ${property.value.type}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst value = property.value.value;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof key !== \"string\" &&\n\t\t\t\t\t\t\ttypeof key !== \"number\" &&\n\t\t\t\t\t\t\ttypeof key !== \"bigint\" &&\n\t\t\t\t\t\t\ttypeof key !== \"boolean\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} bf6Strings key must be a string, found: ${typeof key}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof value !== \"string\" &&\n\t\t\t\t\t\t\ttypeof value !== \"number\" &&\n\t\t\t\t\t\t\ttypeof value !== \"bigint\" &&\n\t\t\t\t\t\t\ttypeof value !== \"boolean\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} bf6Strings value must be a string, found: ${typeof value}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbf6Strings[`${key}`] = `${value}`;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tfor (const item of program.body) {\n\t\t\t\t\t// Case 1: plain variable `var bf6Strings = {...}`\n\t\t\t\t\tif (item.type === \"VariableDeclaration\") {\n\t\t\t\t\t\tfor (const declaration of item.declarations) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tdeclaration.type === \"VariableDeclarator\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.type === \"Identifier\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.name === \"bf6Strings\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\textractObjectLiteral(declaration);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Case 2: exported variable `export const bf6Strings = {...}`\n\t\t\t\t\tif (\n\t\t\t\t\t\titem.type === \"ExportNamedDeclaration\" &&\n\t\t\t\t\t\titem.declaration?.type === \"VariableDeclaration\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tfor (const declaration of item.declaration.declarations) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tdeclaration.type === \"VariableDeclarator\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.type === \"Identifier\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.name === \"bf6Strings\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\textractObjectLiteral(declaration);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n\ttype Attachment,\n\tAttachmentType,\n\ttype ConfigType,\n\ttype MapType,\n} from \"@bf6mods/sdk\";\nimport colors from \"colors\";\nimport { createJiti } from \"jiti\";\nimport { rolldown } from \"rolldown\";\nimport {\n\ttype Bf6Config,\n\tMapId as MapIdEnum,\n} from \"../../resources/prepare/types/config.ts\";\nimport { printToConsole } from \"../utils.ts\";\nimport { extractBf6Strings } from \"./generated-strings.ts\";\n\ndeclare global {\n\tvar defineBf6Config: ((config: Bf6Config) => Bf6Config) | undefined;\n\tvar MapId: typeof MapIdEnum | undefined;\n}\n\n/**\n * Dynamically imports and validates bf6.config.ts\n */\nexport async function getBf6Config(rootDir: string): Promise<Bf6Config> {\n\tglobalThis.defineBf6Config = (c) => c;\n\tglobalThis.MapId = MapIdEnum;\n\tconst jiti = createJiti(rootDir, { interopDefault: true });\n\tconst result = await jiti.import(\"./bf6.config\", { default: true });\n\tdelete globalThis.defineBf6Config;\n\tdelete globalThis.MapId;\n\treturn result as Bf6Config;\n}\n\n/**\n * Builds the entire mod project once (for production or single run).\n */\nexport async function build() {\n\tconst workingDir = path.resolve(\".\");\n\tconst config = await getBf6Config(workingDir);\n\n\tconst outDir = path.resolve(workingDir, config.outDir);\n\tif (fs.existsSync(outDir))\n\t\tfs.rmSync(outDir, { recursive: true, force: true });\n\tfs.mkdirSync(outDir, { recursive: true });\n\n\tconst minifyJson =\n\t\ttypeof config.minify === \"boolean\"\n\t\t\t? config.minify\n\t\t\t: (config.minify?.json ?? false);\n\n\tconst generatedStrings = {};\n\tlet tsAttachment: Attachment | undefined;\n\tif (config.entrypoint) {\n\t\tconst entryAbs = path.resolve(workingDir, config.entrypoint);\n\t\tconst compiled = await buildEntrypoint(\n\t\t\tentryAbs,\n\t\t\tgeneratedStrings,\n\t\t\tconfig.generateStrings ?? true,\n\t\t);\n\t\ttsAttachment = createTsAttachment(entryAbs, compiled);\n\t}\n\n\tconst { attachments, mapRotation } = await collectAttachments(\n\t\tconfig,\n\t\tworkingDir,\n\t\ttsAttachment,\n\t\tgeneratedStrings,\n\t);\n\n\tawait writeModJson(config, outDir, attachments, mapRotation, minifyJson);\n\tprintToConsole(`${colors.green.bold(\"✓\")} Built mod: ${config.name}`);\n}\n\n/**\n * Compiles the TypeScript entrypoint using rolldown and returns the compiled code.\n */\nexport async function buildEntrypoint(\n\tentry: string,\n\tbf6Strings: Record<string, string>,\n\tgenerateStringsFromLiterals: boolean,\n): Promise<string> {\n\tconst bundle = await rolldown({\n\t\tinput: entry,\n\t\tplugins: [extractBf6Strings(bf6Strings, generateStringsFromLiterals)],\n\t\tlogLevel: \"debug\",\n\t});\n\tconst result = await bundle.generate({\n\t\tformat: \"esm\",\n\t\tinlineDynamicImports: true,\n\t});\n\n\tconst code = result.output[0].code;\n\treturn code;\n}\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst spatialsDir = path.resolve(__dirname, \"../resources/maps/spatial\");\n/**\n * Collects all attachments (TS, scenes, strings) and returns them + mapRotation.\n */\nexport async function collectAttachments(\n\tconfig: Bf6Config,\n\tworkingDir: string,\n\ttsAttachment: Attachment | undefined,\n\tgeneratedStrings: Record<string, number | string> | undefined,\n) {\n\tconst attachments: Attachment[] = [];\n\tconst mapRotation: MapType[] = [];\n\n\tif (tsAttachment) attachments.push(tsAttachment);\n\n\t// Strings\n\tif (config.strings) {\n\t\tconst strPath = path.resolve(workingDir, config.strings);\n\t\tif (!fs.existsSync(strPath)) throw new Error(\"Cannot find strings file\");\n\t\tconst raw = await fs.promises.readFile(strPath, \"utf8\");\n\t\tconst attachment = createStringsAttachment(strPath, raw, generatedStrings);\n\t\tattachments.push(attachment);\n\t}\n\n\t// Scenes\n\tif (config.scenes) {\n\t\tlet mapIdx = 0;\n\t\tfor (const map of config.scenes) {\n\t\t\tlet mapId: MapIdEnum;\n\t\t\tlet scene: string;\n\t\t\tif (Array.isArray(map)) {\n\t\t\t\t[mapId, scene] = map;\n\t\t\t} else {\n\t\t\t\tmapId = map;\n\t\t\t\tscene = path.resolve(spatialsDir, `${mapId}.spatial.json`);\n\t\t\t}\n\n\t\t\tconst scenePath = path.resolve(workingDir, scene);\n\t\t\tif (!fs.existsSync(scenePath))\n\t\t\t\tthrow new Error(`Cannot find spatial data file: ${scene}`);\n\t\t\tconst raw = await fs.promises.readFile(scenePath, \"utf8\");\n\t\t\tconst spatial = createSpatialAttachment(scenePath, raw, mapIdx++);\n\t\t\tattachments.push(spatial);\n\t\t\tmapRotation.push({ id: mapId, spatialAttachment: spatial });\n\t\t}\n\t}\n\n\treturn { attachments, mapRotation };\n}\n\n/**\n * Writes the mod.json output to disk.\n */\nexport async function writeModJson(\n\tconfig: Bf6Config,\n\toutDir: string,\n\tattachments: ConfigType[\"attachments\"],\n\tmapRotation: MapType[],\n\tminify: boolean,\n) {\n\tconst baseGame = config.game;\n\tconst finalJson: ConfigType = {\n\t\tname: config.name,\n\t\tdescription: config.description,\n\t\tgameMode: \"ModBuilderCustom\",\n\t\tmutators: baseGame.mutators ?? {},\n\t\tassetRestrictions: baseGame.assetRestrictions ?? {},\n\t\tteamComposition: baseGame.teamComposition ?? [],\n\t\tmapRotation,\n\t\tattachments,\n\t};\n\n\tconst jsonOutput = minify\n\t\t? JSON.stringify(finalJson)\n\t\t: JSON.stringify(finalJson, null, 2);\n\tawait fs.promises.writeFile(path.resolve(outDir, \"mod.json\"), jsonOutput);\n\n\tif (config.outputArtifacts && finalJson?.attachments) {\n\t\tfor (const attachment of finalJson.attachments) {\n\t\t\tconst attachmentsDir = path.resolve(outDir, \"attachments\");\n\t\t\tif (!fs.existsSync(attachmentsDir))\n\t\t\t\tfs.mkdirSync(attachmentsDir, {\n\t\t\t\t\trecursive: true,\n\t\t\t\t});\n\t\t\tawait fs.promises.writeFile(\n\t\t\t\tpath.resolve(outDir, \"attachments\", attachment.filename),\n\t\t\t\tatob(attachment.attachmentData.original),\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport function createTsAttachment(\n\tfilePath: string,\n\tcompiled: string,\n): Attachment {\n\treturn {\n\t\tid: crypto.randomUUID(),\n\t\tversion: \"1.0\",\n\t\tfilename: `${path.parse(filePath).name}.js`,\n\t\tisProcessable: true,\n\t\tprocessingStatus: 2,\n\t\tattachmentType: AttachmentType.TypeScript,\n\t\tattachmentData: { original: toBase64(compiled), compiled: \"\" },\n\t\terrors: [],\n\t};\n}\n\nexport function createStringsAttachment(\n\tfilePath: string,\n\traw: string,\n\tgeneratedStrings?: Record<string, number | string> | undefined,\n): Attachment {\n\tlet result = raw;\n\tif (generatedStrings) {\n\t\tresult = JSON.stringify(\n\t\t\t{\n\t\t\t\t...generatedStrings,\n\t\t\t\t...JSON.parse(raw),\n\t\t\t},\n\t\t\tnull,\n\t\t\t4,\n\t\t);\n\t}\n\n\treturn {\n\t\tid: crypto.randomUUID(),\n\t\tversion: \"1.0\",\n\t\tfilename: path.basename(filePath),\n\t\tisProcessable: true,\n\t\tprocessingStatus: 2,\n\t\tattachmentType: AttachmentType.Strings,\n\t\tattachmentData: { original: toBase64(result), compiled: \"\" },\n\t\terrors: [],\n\t};\n}\n\nexport function createSpatialAttachment(\n\tfilePath: string,\n\traw: string,\n\tmapIdx: number,\n): MapType[\"spatialAttachment\"] {\n\treturn {\n\t\tid: crypto.randomUUID(),\n\t\tversion: \"1.0\",\n\t\tfilename: path.basename(filePath),\n\t\tisProcessable: true,\n\t\tprocessingStatus: 2,\n\t\tattachmentType: AttachmentType.SpatialData,\n\t\tattachmentData: { original: toBase64(raw), compiled: \"\" },\n\t\tmetadata: `mapIdx=${mapIdx}`,\n\t\terrors: [],\n\t};\n}\n\nexport function toBase64(input: string | Buffer): string {\n\treturn Buffer.isBuffer(input)\n\t\t? input.toString(\"base64\")\n\t\t: Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport colors from \"colors\";\nimport { printToConsole } from \"./utils.ts\";\n\nexport class Bf6Logger {\n\tprivate static instance: Bf6Logger | undefined;\n\n\tprivate watchTarget: string;\n\tprivate watcher?: FSWatcher;\n\tprivate lastSize: number;\n\n\tconstructor(input?: string) {\n\t\tif (Bf6Logger.instance) {\n\t\t\tprintToConsole(\n\t\t\t\tcolors.yellow(\"⚠ Existing logger found — closing it first...\"),\n\t\t\t);\n\t\t\tBf6Logger.instance.stop();\n\t\t}\n\n\t\tif (input) this.watchTarget = path.resolve(input);\n\t\telse if (process.env.LOCALAPPDATA)\n\t\t\tthis.watchTarget = path.resolve(\n\t\t\t\tprocess.env.LOCALAPPDATA,\n\t\t\t\t\"temp\",\n\t\t\t\t\"Battlefieldâ„¢ 6\",\n\t\t\t\t\"PortalLog.txt\",\n\t\t\t);\n\t\telse\n\t\t\tthrow new Error(\n\t\t\t\t`${colors.red.bold(\"✗\")} Env variable LOCALAPPDATA not defined, and input file to watch was not provided!`,\n\t\t\t);\n\n\t\tif (!fs.existsSync(this.watchTarget))\n\t\t\tthrow new Error(\n\t\t\t\t`${colors.red.bold(\"✗\")} Cannot find file '${this.watchTarget}' to watch for changes`,\n\t\t\t);\n\n\t\tthis.lastSize = fs.statSync(this.watchTarget).size;\n\t\tBf6Logger.instance = this;\n\t}\n\n\tasync start() {\n\t\tprintToConsole(\n\t\t\tcolors.cyan(\n\t\t\t\t`👀 Watching Battlefield logs at: ${colors.yellow(this.watchTarget)}`,\n\t\t\t),\n\t\t);\n\n\t\tthis.watcher = chokidar.watch(this.watchTarget, {\n\t\t\tpersistent: true,\n\t\t\tignoreInitial: true,\n\t\t});\n\n\t\tthis.watcher.on(\"change\", this.handleChange.bind(this));\n\t}\n\n\tasync stop() {\n\t\tif (this.watcher) {\n\t\t\tthis.watcher.close();\n\t\t\tthis.watcher = undefined;\n\t\t\tprintToConsole(\n\t\t\t\tcolors.yellow(\n\t\t\t\t\t`${colors.red.bold(\"✗\")} Stopped watching: ${colors.gray(this.watchTarget)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tBf6Logger.instance = undefined;\n\t}\n\n\tprivate handleChange(file: string, stats?: fs.Stats) {\n\t\tif (!stats) return;\n\t\tif (stats.size > this.lastSize) {\n\t\t\tconst newBytes = stats.size - this.lastSize;\n\t\t\tconst fd = fs.openSync(file, \"r\");\n\t\t\tconst buffer = Buffer.alloc(newBytes);\n\t\t\tfs.readSync(fd, buffer, 0, newBytes, this.lastSize);\n\t\t\tfs.closeSync(fd);\n\n\t\t\tconst newContent = buffer\n\t\t\t\t.toString(\"utf8\")\n\t\t\t\t.split(\"QuickJS: \")\n\t\t\t\t.filter((debug) => debug.trim() !== \"\")\n\t\t\t\t.map((debug) => debug.trim())\n\t\t\t\t.map((debug) => {\n\t\t\t\t\tif (debug.startsWith(\"console.log: \")) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"console.log\",\n\t\t\t\t\t\t\ttext: debug.replace(\"console.log: \", \"\"),\n\t\t\t\t\t\t};\n\t\t\t\t\t} else if (debug.startsWith(\"Exception:\")) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"exception\",\n\t\t\t\t\t\t\ttext: debug.replace(\"Exception:\", \"\"),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: \"unknown\",\n\t\t\t\t\t\ttext: debug,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\tfor (const content of newContent) {\n\t\t\t\tif (content.type === \"console.log\")\n\t\t\t\t\tprintToConsole(colors.gray(content.text));\n\t\t\t\telse if (content.type === \"exception\")\n\t\t\t\t\tprintToConsole(colors.red(content.text), true);\n\t\t\t\telse printToConsole(colors.bold(content.text));\n\t\t\t}\n\t\t}\n\t\tthis.lastSize = stats.size;\n\t}\n}\n","import fs from \"node:fs\";\nimport { glob } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport colors from \"colors\";\nimport { build, getBf6Config } from \"./build/index.ts\";\nimport { Bf6Logger } from \"./log.ts\";\nimport { printToConsole } from \"./utils.ts\";\n\nexport async function dev() {\n\tconst workingDir = path.resolve(\".\");\n\tlet config = await getBf6Config(workingDir);\n\tconst outDir = path.resolve(workingDir, config.outDir);\n\tfs.mkdirSync(outDir, { recursive: true });\n\n\tprintToConsole(colors.cyan(`▶ Starting dev for ${config.name}`));\n\n\tlet watcher: FSWatcher | undefined;\n\n\t// biome-ignore lint/suspicious/noExplicitAny: doesn't really matter in this case\n\tfunction debounce<T extends (...args: any[]) => void>(fn: T, delay: number) {\n\t\tlet timer: NodeJS.Timeout | undefined;\n\t\treturn (...args: Parameters<T>) => {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\ttimer = setTimeout(() => fn(...args), delay);\n\t\t};\n\t}\n\n\tconst rebuild = async (trigger: string) => {\n\t\tprintToConsole(\n\t\t\tcolors.yellow(\n\t\t\t\t`↻ Change detected in ${path.basename(trigger)}, rebuilding...`,\n\t\t\t),\n\t\t);\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tawait build();\n\t\t\tconst duration = ((performance.now() - start) / 1000).toFixed(2);\n\t\t\tprintToConsole(\n\t\t\t\t`${colors.green.bold(\"✓\")} Updated mod.json (${duration}s)`,\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tprintToConsole(\n\t\t\t\tcolors.red(\n\t\t\t\t\t`${colors.red.bold(\"✗\")} Rebuild failed: ${(err as Error).message}`,\n\t\t\t\t),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\t};\n\n\tconst debouncedRebuild = debounce(rebuild, 200);\n\n\tasync function collectWatchTargets(): Promise<string[]> {\n\t\tconst targets: string[] = [];\n\n\t\tif (config.entrypoint)\n\t\t\ttargets.push(path.resolve(workingDir, config.entrypoint));\n\t\tif (config.scenes)\n\t\t\tfor (const [, scene] of config.scenes)\n\t\t\t\ttargets.push(path.resolve(workingDir, scene));\n\t\tif (config.strings) targets.push(path.resolve(workingDir, config.strings));\n\t\tfor await (const entry of glob(\"bf6.config.*\")) targets.push(entry);\n\t\tfor await (const entry of glob(\"src/**/*\")) targets.push(entry);\n\n\t\treturn targets;\n\t}\n\n\tasync function setupWatcher() {\n\t\tif (watcher) {\n\t\t\tawait watcher.close();\n\t\t\tprintToConsole(\n\t\t\t\tcolors.grey(\"♻ Reloading watcher due to config change...\"),\n\t\t\t);\n\t\t\tawait new Promise((r) => setTimeout(r, 10));\n\t\t}\n\n\t\tconst watchTargets = await collectWatchTargets();\n\t\tprintToConsole(colors.cyan(`👀 Watching ${watchTargets.length} files...`));\n\n\t\twatcher = chokidar.watch(watchTargets, {\n\t\t\tpersistent: true,\n\t\t\tignoreInitial: true,\n\t\t\tawaitWriteFinish: { stabilityThreshold: 250, pollInterval: 100 },\n\t\t\tignored: [outDir, `${outDir}/**`, \"node_modules/**\", \".git/**\"],\n\t\t});\n\n\t\twatcher.on(\"error\", (err) => {\n\t\t\tprintToConsole(\n\t\t\t\tcolors.red(`⚠ Watcher error: ${(err as Error).message}`),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tsetTimeout(setupWatcher, 1000);\n\t\t});\n\n\t\twatcher.on(\"change\", async (file) => {\n\t\t\tif (file.includes(\"bf6.config.\")) {\n\t\t\t\tprintToConsole(\n\t\t\t\t\tcolors.magenta(\"⚙ Config changed — reloading watcher...\"),\n\t\t\t\t);\n\t\t\t\tconfig = await getBf6Config(workingDir);\n\t\t\t\tawait setupWatcher();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdebouncedRebuild(file);\n\t\t});\n\n\t\twatcher.on(\"add\", debouncedRebuild);\n\t\tawait rebuild(\"initial\");\n\t}\n\n\tprocess.on(\"SIGINT\", async () => {\n\t\tprintToConsole(colors.red(`\\n${colors.red.bold(\"✗\")} Exiting dev mode...`));\n\t\tawait watcher?.close();\n\t\tprocess.exit(0);\n\t});\n\n\tawait setupWatcher();\n\n\tlet logger: Bf6Logger | undefined;\n\ttry {\n\t\tlogger = new Bf6Logger();\n\t\tlogger.start();\n\t} catch (error) {\n\t\tprintToConsole(\n\t\t\tcolors.grey(\n\t\t\t\t\"Failed to start logging for the following reason (building will still work)\",\n\t\t\t),\n\t\t\ttrue,\n\t\t);\n\t\tprintToConsole((error as Error).message, true);\n\t}\n}\n","import child_process from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as prompts from \"@clack/prompts\";\nimport { importFile } from \"./import.ts\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst templatesDir = path.resolve(__dirname, \"../resources/templates\");\n\nfunction renameFilesRecursively(dir: string, modName: string) {\n\tconst entries = fs.readdirSync(dir, { withFileTypes: true });\n\tfor (const entry of entries) {\n\t\tconst oldPath = path.join(dir, entry.name);\n\t\tlet newPath = oldPath;\n\n\t\t// Rename file or folder if it includes {name}\n\t\tif (entry.name.includes(\"{name}\")) {\n\t\t\tconst newName = entry.name.replace(\"{name}\", modName);\n\t\t\tnewPath = path.join(dir, newName);\n\t\t\tfs.renameSync(oldPath, newPath);\n\t\t}\n\n\t\t// If it's a bf6.config.ts, replace the {bf6ConfigName} placeholder\n\t\tif (entry.name === \"bf6.config.ts\") {\n\t\t\tlet content = fs.readFileSync(newPath, \"utf-8\");\n\t\t\tcontent = content.replace(/{bf6ConfigName}/g, modName);\n\t\t\tfs.writeFileSync(newPath, content, \"utf-8\");\n\t\t}\n\n\t\t// If it's a package.json, rewrite the \"name\" field\n\t\tif (entry.name === \"package.json\") {\n\t\t\tconst pkg = JSON.parse(fs.readFileSync(newPath, \"utf-8\"));\n\t\t\tpkg.name = modName;\n\t\t\tfs.writeFileSync(newPath, JSON.stringify(pkg, null, 2));\n\t\t}\n\n\t\t// Recurse into directories (using the new name if renamed)\n\t\tif (fs.statSync(newPath).isDirectory()) {\n\t\t\trenameFilesRecursively(newPath, modName);\n\t\t}\n\t}\n}\n\nexport const templates = [\n\t\"Basic\",\n\t\"AcePursuit\",\n\t\"BombSquad\",\n\t\"Exfil\",\n\t\"Vertigo\",\n] as const;\n\nexport async function startProject(\n\tdestination: string,\n\ttemplate: (typeof templates)[number] | \"None\",\n\tname?: string,\n) {\n\tif ([\"AcePursuit\", \"BombSquad\", \"Exfil\", \"Vertigo\"].includes(template)) {\n\t\tconst importPath = path.resolve(templatesDir, `${template}.json`);\n\t\tawait importFile(importPath, destination, name);\n\t} else if (template === \"None\") {\n\t\t// Do nothing if none\n\t} else {\n\t\t// Handles the Basic and any other template provided\n\t\tconst templateDir = path.resolve(templatesDir, template);\n\t\tfs.cpSync(templateDir, destination, { recursive: true });\n\t}\n\n\tfs.cpSync(path.resolve(templatesDir, \"All\"), destination, {\n\t\trecursive: true,\n\t});\n\n\tif (name) renameFilesRecursively(destination, name);\n}\n\nexport function installDependencies(projectDir: string) {\n\ttry {\n\t\tchild_process.execSync(`npm install`, {\n\t\t\tstdio: \"inherit\",\n\t\t\tcwd: projectDir,\n\t\t});\n\t\treturn true;\n\t} catch (_err) {\n\t\treturn false;\n\t}\n}\n\nconst _defaultTargetDir = \"bf6-mod\";\nconst cancel = () => prompts.cancel(\"Operation cancelled\");\n\nfunction isEmpty(path: string) {\n\tconst files = fs.readdirSync(path);\n\treturn files.length === 0 || (files.length === 1 && files[0] === \".git\");\n}\n\nfunction emptyDir(dir: string) {\n\tif (!fs.existsSync(dir)) {\n\t\treturn;\n\t}\n\tfor (const file of fs.readdirSync(dir)) {\n\t\tif (file === \".git\") {\n\t\t\tcontinue;\n\t\t}\n\t\tfs.rmSync(path.resolve(dir, file), { recursive: true, force: true });\n\t}\n}\n\nexport async function init(argTargetDir?: string) {\n\tprompts.intro(\"Initialize Bf6 Mod\");\n\n\tconst path = argTargetDir\n\t\t? argTargetDir\n\t\t: await prompts.text({\n\t\t\t\tmessage: \"Where should we create your project?\",\n\t\t\t\tplaceholder: \"./ace-pursuit\",\n\t\t\t\tvalidate: (value) => {\n\t\t\t\t\tif (!value) return \"Please enter a path.\";\n\t\t\t\t\tif (value[0] !== \".\") return \"Please enter a relative path.\";\n\t\t\t\t},\n\t\t\t});\n\tif (prompts.isCancel(path)) return cancel();\n\n\tlet name = await prompts.text({\n\t\tmessage: \"What is the name of your mod?\",\n\t\tplaceholder: \"Ace Pursuit\",\n\t\tvalidate: (value) => {\n\t\t\tif (!value.trim()) return \"Please enter a name.\";\n\t\t},\n\t});\n\tif (prompts.isCancel(name)) return cancel();\n\tname = name.trim();\n\n\tif (fs.existsSync(path) && !isEmpty(path)) {\n\t\tlet overwrite: \"yes\" | \"no\" | \"ignore\" | undefined;\n\t\tconst res = await prompts.select({\n\t\t\tmessage:\n\t\t\t\t(path === \".\" ? \"Current directory\" : `Target directory \"${path}\"`) +\n\t\t\t\t` is not empty. Please choose how to proceed:`,\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Cancel operation\",\n\t\t\t\t\tvalue: \"no\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Remove existing files and continue\",\n\t\t\t\t\tvalue: \"yes\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tlabel: \"Ignore files and continue\",\n\t\t\t\t\tvalue: \"ignore\",\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t\tif (prompts.isCancel(res)) return cancel();\n\t\toverwrite = res;\n\n\t\tswitch (overwrite) {\n\t\t\tcase \"yes\":\n\t\t\t\temptyDir(path);\n\t\t\t\tbreak;\n\t\t\tcase \"no\":\n\t\t\t\tcancel();\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tconst template = await prompts.select({\n\t\tmessage: \"Select a template:\",\n\t\toptions: templates.map((template) => {\n\t\t\treturn {\n\t\t\t\tlabel: template,\n\t\t\t\tvalue: template,\n\t\t\t};\n\t\t}),\n\t});\n\tif (prompts.isCancel(template)) return cancel();\n\n\tawait startProject(path, template, name);\n\n\tconst s = prompts.spinner();\n\ts.start(\"Installing via npm\");\n\tconst installed = installDependencies(path);\n\tif (installed) s.stop(\"Installed via npm\");\n\telse s.stop(\"Failed to install via npm\", 1);\n\n\tconst nextSteps = `cd ${path}\\nnpm run build`;\n\n\tprompts.note(nextSteps, \"Next steps.\");\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { AttachmentType, type ConfigType } from \"@bf6mods/sdk\";\nimport stringifyObject from \"stringify-object\";\nimport { MapId as MapIdEnum } from \"../resources/prepare/types/config.ts\";\nimport { startProject } from \"./init.ts\";\n\nasync function writeFileSafe(filePath: string, data: string | Buffer) {\n\tconst dir = path.dirname(filePath);\n\tawait fs.promises.mkdir(dir, { recursive: true });\n\tawait fs.promises.writeFile(filePath, data);\n}\n\nfunction getMapKeyByValue(value: string): keyof typeof MapIdEnum | undefined {\n\treturn Object.keys(MapIdEnum).find(\n\t\t(k) => MapIdEnum[k as keyof typeof MapIdEnum] === value,\n\t) as keyof typeof MapIdEnum | undefined;\n}\n\nexport async function importFile(input: string, output: string, name?: string) {\n\tconst workingDir = path.resolve(\".\");\n\tconst entrypoint = path.resolve(workingDir, input);\n\tconst outDir = path.resolve(workingDir, output);\n\n\tif (!fs.existsSync(entrypoint)) throw new Error(\"Cannot find strings file\");\n\n\tconst config = JSON.parse(\n\t\tawait fs.promises.readFile(entrypoint, { encoding: \"utf8\" }),\n\t) as ConfigType;\n\n\tif (!fs.existsSync(outDir))\n\t\tawait fs.promises.mkdir(outDir, { recursive: true });\n\n\tawait startProject(outDir, \"None\", name ?? config.name);\n\n\tlet typescriptFile: string | undefined;\n\tlet stringsFile: string | undefined;\n\tconst scenes: [string, string][] = [];\n\tconst promises: Promise<unknown>[] = [];\n\n\tif (config.attachments) {\n\t\tfor (const attachment of config.attachments) {\n\t\t\tif (attachment.attachmentType === AttachmentType.TypeScript)\n\t\t\t\ttypescriptFile = attachment.filename;\n\n\t\t\tif (attachment.attachmentType === AttachmentType.Strings)\n\t\t\t\tstringsFile = attachment.filename;\n\n\t\t\tif (attachment.attachmentType === AttachmentType.SpatialData) {\n\t\t\t\t// We'll just use the data attached to mapRotation instead\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpromises.push(\n\t\t\t\twriteFileSafe(\n\t\t\t\t\tpath.resolve(outDir, \"src\", attachment.filename),\n\t\t\t\t\tatob(attachment.attachmentData.original),\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\t// Extract spatial scenes from mapRotation instead of guessing\n\tif (config.mapRotation?.length) {\n\t\tfor (const map of config.mapRotation) {\n\t\t\tpromises.push(\n\t\t\t\twriteFileSafe(\n\t\t\t\t\tpath.resolve(outDir, \"src\", \"scenes\", map.spatialAttachment.filename),\n\t\t\t\t\tatob(map.spatialAttachment.attachmentData.original),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tscenes.push([map.id, `src/scenes/${map.spatialAttachment.filename}`]);\n\t\t}\n\t}\n\n\t// build the new bf6.config.ts structure\n\tconst bf6Config = {\n\t\tname: name ?? config.name,\n\t\tdescription: config.description,\n\t\toutDir: \"dist\",\n\t\tentrypoint: typescriptFile ? `src/${typescriptFile}` : undefined,\n\t\tscenes: scenes\n\t\t\t? scenes.map(([id, path]) => [\n\t\t\t\t\t`MapId.${getMapKeyByValue(id) ?? id}`,\n\t\t\t\t\tpath,\n\t\t\t\t])\n\t\t\t: undefined,\n\t\tstrings: stringsFile ? `src/${stringsFile}` : undefined,\n\t\tgame: {\n\t\t\tmutators: config.mutators,\n\t\t\tassetRestrictions: config.assetRestrictions,\n\t\t\tgameMode: config.gameMode,\n\t\t\tteamComposition: config.teamComposition,\n\t\t},\n\t};\n\n\tconst bf6ConfigContent = `export default defineBf6Config(${stringifyWithRaw(\n\t\tbf6Config,\n\t)});\\n`;\n\n\tpromises.push(\n\t\twriteFileSafe(path.resolve(outDir, \"bf6.config.ts\"), bf6ConfigContent),\n\t);\n\n\tawait Promise.all(promises);\n}\n\nfunction stringifyWithRaw(value: unknown, options = {}) {\n\treturn stringifyObject(value, {\n\t\tindent: \" \", // 4 spaces\n\t\tsingleQuotes: true,\n\t\ttransform: (_obj, _prop, originalResult) => {\n\t\t\t// Remove quotes from MapId.* expressions\n\t\t\tif (/^['\"]MapId\\.[A-Za-z0-9_]+['\"]$/.test(originalResult)) {\n\t\t\t\treturn originalResult.slice(1, -1); // remove surrounding quotes\n\t\t\t}\n\t\t\treturn originalResult;\n\t\t},\n\t\t...options,\n\t});\n}\n","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport colors from \"colors\";\r\nimport { genExport, genInlineTypeImport } from \"knitwork\";\r\nimport { printToConsole } from \"./utils.js\";\r\n\r\nexport async function prepare() {\r\n\ttry {\r\n\t\tconst __filename = fileURLToPath(import.meta.url);\r\n\t\tconst __dirname = path.dirname(__filename);\r\n\r\n\t\tconst _workingDir = path.resolve(\".\");\r\n\t\tconst buildDir = path.resolve(\".bf6\");\r\n\r\n\t\tconst resources = path.resolve(__dirname, \"../resources/prepare\");\r\n\r\n\t\tfs.cpSync(\r\n\t\t\tpath.resolve(resources, \"tsconfig.json\"),\r\n\t\t\tpath.resolve(buildDir, \"tsconfig.json\"),\r\n\t\t);\r\n\t\tfs.cpSync(\r\n\t\t\tpath.resolve(resources, \"bf6.d.ts\"),\r\n\t\t\tpath.resolve(buildDir, \"bf6.d.ts\"),\r\n\t\t);\r\n\t\tfs.cpSync(\r\n\t\t\tpath.resolve(resources, \"types\", \"config.ts\"),\r\n\t\t\tpath.resolve(buildDir, \"types\", \"config.ts\"),\r\n\t\t);\r\n\r\n\t\tconst ConfigFileExports = genExport(\"./types/config.ts\", [\r\n\t\t\t\"defineBf6Config\",\r\n\t\t]);\r\n\t\tfs.writeFileSync(\r\n\t\t\tpath.resolve(buildDir, \"imports.d.ts\"),\r\n\t\t\t`${ConfigFileExports}\\n`,\r\n\t\t);\r\n\r\n\t\tconst augmentations: Record<string, string> = {\r\n\t\t\tdefineBf6Config: genInlineTypeImport(\r\n\t\t\t\t\"./types/config.ts\",\r\n\t\t\t\t`defineBf6Config`,\r\n\t\t\t),\r\n\t\t\tMapId: genInlineTypeImport(\"./types/config.ts\", `MapId`),\r\n\t\t};\r\n\r\n\t\tconst args = genNamespaceAugmentation(\"global\", augmentations);\r\n\t\tfs.writeFileSync(\r\n\t\t\tpath.resolve(buildDir, \"globals.d.ts\"),\r\n\t\t\t`export {}\\n\\n${args}\\n`,\r\n\t\t);\r\n\r\n\t\tprintToConsole(`${colors.green.bold(\"✓\")} Types generated in .bf6`);\r\n\t} catch (error) {\r\n\t\tconsole.error(error);\r\n\t\tprintToConsole(\r\n\t\t\t`${colors.red.bold(\"✗\")} Types failed to generate in .bf6`,\r\n\t\t\ttrue,\r\n\t\t);\r\n\t}\r\n}\r\n\r\nconst genNamespaceAugmentation = (\r\n\tname: string,\r\n\tcontents: Record<string, string>,\r\n) => {\r\n\tif (!contents || Object.keys(contents).length === 0)\r\n\t\treturn `declare ${name} {}`;\r\n\tconst decls = Object.entries(contents)\r\n\t\t.map(([k, v]) => `\\tconst ${k}: ${v};`)\r\n\t\t.join(\"\\n\");\r\n\treturn `declare ${name} {\\n${decls}\\n}`;\r\n};\r\n","#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport pkg from \"../../package.json\" with { type: \"json\" };\nimport { build } from \"./build/index.ts\";\nimport { dev } from \"./dev.ts\";\nimport { importFile } from \"./import.ts\";\nimport { init, installDependencies } from \"./init.js\";\nimport { Bf6Logger } from \"./log.ts\";\nimport { prepare } from \"./prepare.js\";\n\nconst program = new Command();\n\nprogram\n\t.name(Object.keys(pkg.bin)[0])\n\t.description(pkg.description)\n\t.version(pkg.version);\n\nprogram\n\t.command(\"init\")\n\t.argument(\"[directory]\")\n\t.description(\"Create a new bf6 mod\")\n\t.action(async (directory) => {\n\t\tawait init(directory);\n\t});\n\nprogram\n\t.command(\"build\")\n\t.description(\"build the bf6 mod\")\n\t.action(async () => {\n\t\tawait build();\n\t});\n\nprogram\n\t.command(\"prepare\")\n\t.description(\"prepare the types for bf6 mod\")\n\t.action(async () => {\n\t\tawait prepare();\n\t});\n\nprogram\n\t.command(\"dev\")\n\t.description(\"watch the changes in src, and recompile as needed\")\n\t.action(async () => {\n\t\tawait dev();\n\t});\n\nprogram\n\t.command(\"import\")\n\t.argument(\"<input>\")\n\t.argument(\"<output>\")\n\t.description(\"decompiles the json config of a mod into a new project\")\n\t.action(async (input, output) => {\n\t\tawait importFile(input as string, output as string);\n\t\tinstallDependencies(output);\n\t});\n\nprogram\n\t.command(\"log\")\n\t.argument(\"[input]\")\n\t.description(\"logs the output from a locally running server\")\n\t.action(async (input) => {\n\t\tconst logger = new Bf6Logger(input);\n\t\tlogger.start();\n\t});\n\nprogram.exitOverride((_err) => {\n\tif (process.env.EXIT_CODE === \"none\") process.exit(0);\n});\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["program","key:\n\t\t\t\t\t\t\t| string\n\t\t\t\t\t\t\t| number\n\t\t\t\t\t\t\t| bigint\n\t\t\t\t\t\t\t| boolean\n\t\t\t\t\t\t\t| RegExp\n\t\t\t\t\t\t\t| null\n\t\t\t\t\t\t\t| undefined","MapIdEnum","tsAttachment: Attachment | undefined","__filename","__dirname","attachments: Attachment[]","mapRotation: MapType[]","mapId: MapIdEnum","scene: string","finalJson: ConfigType","watcher: FSWatcher | undefined","timer: NodeJS.Timeout | undefined","targets: string[]","logger: Bf6Logger | undefined","bf6CliPackageJson.version","bf6SdkPackageJson.version","path","overwrite: \"yes\" | \"no\" | \"ignore\" | undefined","template","MapIdEnum","typescriptFile: string | undefined","stringsFile: string | undefined","scenes: [string, string][]","promises: Promise<unknown>[]","ext: string","path","__filename","__dirname","pkg.bin","pkg.description","pkg.version","installDependencies"],"sources":["../../package.json","../../src/resources/prepare/types/config.ts","../../src/cli/utils.ts","../../src/cli/build/generated-strings.ts","../../src/cli/build/index.ts","../../src/cli/log.ts","../../src/cli/dev.ts","../../../sdk/package.json","../../src/cli/init.ts","../../src/cli/import.ts","../../src/cli/prepare.ts","../../src/cli/index.ts"],"sourcesContent":["{\n\t\"name\": \"@bf6mods/cli\",\n\t\"version\": \"1.2.2\",\n\t\"description\": \"CLI and library for bundling BF6 mods\",\n\t\"license\": \"MIT\",\n\t\"type\": \"module\",\n\t\"bin\": {\n\t\t\"bf6mods\": \"./dist/cli/index.js\"\n\t},\n\t\"main\": \"./dist/index.js\",\n\t\"types\": \"./dist/index.d.ts\",\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"start\": \"cross-env EXIT_CODE=none tsx ./src/cli/index.ts\"\n\t},\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.ts\",\n\t\t\t\"import\": \"./dist/index.js\"\n\t\t}\n\t},\n\t\"files\": [\n\t\t\"dist/\"\n\t],\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/bf6mods/bf6mods.git\"\n\t},\n\t\"dependencies\": {\n\t\t\"@bf6mods/sdk\": \"*\",\n\t\t\"@clack/prompts\": \"^0.11.0\",\n\t\t\"chokidar\": \"^4.0.3\",\n\t\t\"colors\": \"^1.4.0\",\n\t\t\"commander\": \"^14.0.1\",\n\t\t\"jiti\": \"^2.6.1\",\n\t\t\"knitwork\": \"^1.2.0\",\n\t\t\"rolldown\": \"^1.0.0-beta.44\",\n\t\t\"stringify-object\": \"^6.0.0\",\n\t\t\"strip-ansi\": \"^7.1.2\"\n\t},\n\t\"publishConfig\": {\n\t\t\"access\": \"public\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@oxc-project/types\": \"^0.95.0\",\n\t\t\"@types/stringify-object\": \"^4.0.5\",\n\t\t\"cross-env\": \"^10.1.0\",\n\t\t\"execa\": \"^9.6.0\",\n\t\t\"tsx\": \"^4.20.6\"\n\t}\n}\n","import type { ConfigType } from \"@bf6mods/sdk\";\n\nexport enum MapId {\n\tFireStorm = \"MP_FireStorm-ModBuilderCustom0\",\n\tSiegeOfCairo = \"MP_Abbasid-ModBuilderCustom0\",\n\tEmpireState = \"MP_Aftermath-ModBuilderCustom0\",\n\tIberianOffensive = \"MP_Battery-ModBuilderCustom0\",\n\tLiberationPeak = \"MP_Capstone-ModBuilderCustom0\",\n\tManhattanBridge = \"MP_Dumbo-ModBuilderCustom0\",\n\tSaintsQuarter = \"MP_Limestone-ModBuilderCustom0\",\n\tNewSobekCity = \"MP_Outskirts-ModBuilderCustom0\",\n\tMirakValley = \"MP_Tungsten-ModBuilderCustom0\",\n}\n\n/**\n * Represents the root configuration for a Battlefield 6 mod project.\n *\n * This file combines both build-level settings (like entrypoints and output directories)\n * and in-game configuration data (such as mutators and asset restrictions)\n * under the `game` key.\n *\n * Use {@link defineBf6Config} to define and validate this structure\n * in your `bf6.config.ts` file.\n */\nexport type Bf6Config = {\n\t/**\n\t * The human-readable name of the mod.\n\t *\n\t * Typically matches the project name used in the BF6 mod editor or\n\t * the display name shown in the in-game mod browser.\n\t */\n\tname: string;\n\n\t/**\n\t * A short description of the mod’s purpose or content.\n\t * Used for display and metadata purposes.\n\t */\n\tdescription: string;\n\n\t/**\n\t * The output directory where compiled or bundled files are written.\n\t * Usually something like `\"dist\"` or `\"build\"`.\n\t */\n\toutDir: string;\n\n\t/**\n\t * Whether or not to output the built files like the entrypoint typescript file\n\t */\n\toutputArtifacts?: boolean;\n\n\t/**\n\t * The main entrypoint for your mod’s TypeScript source file.\n\t *\n\t * This is typically something like `\"src/index.ts\"` or `\"src/main.ts\"`.\n\t */\n\tentrypoint?: string;\n\n\t/**\n\t * One or more scene file paths (.json) that the mod includes.\n\t * If no file path is provided, it loads in the default spatial data.\n\t */\n\tscenes?: ([MapId, string] | MapId)[];\n\n\t/**\n\t * Path to a JSON file or other source that defines localized strings\n\t * used by the mod (optional).\n\t */\n\tstrings?: string;\n\n\t/**\n\t * Generate strings automatically, by default this is enabled\n\t */\n\tgenerateStrings?: boolean;\n\n\t/**\n\t * Controls whether build output should be minified.\n\t *\n\t * Can be a boolean (to toggle all minification),\n\t * or an object specifying independent settings for JS and JSON files.\n\t *\n\t * @example\n\t * ```ts\n\t * minify: true\n\t * // or\n\t * minify: { js: true, json: false }\n\t * ```\n\t */\n\tminify?:\n\t\t| {\n\t\t\t\t/** Whether to minify JavaScript output. */\n\t\t\t\tjs?: boolean;\n\t\t\t\t/** Whether to minify JSON output. */\n\t\t\t\tjson?: boolean;\n\t\t }\n\t\t/** Enables or disables all minification. */\n\t\t| boolean;\n\n\t/**\n\t * The underlying in-game configuration derived from `ConfigType`\n\t * in the BF6 SDK. Includes gameplay-level details like mutators,\n\t * asset restrictions, and team compositions.\n\t *\n\t * The following properties are **excluded**:\n\t * - `attachments`\n\t * - `workspace`\n\t * - `mapRotation`\n\t * - `name`\n\t * - `description`\n\t *\n\t * since those are either redundant or handled elsewhere.\n\t */\n\tgame: Omit<\n\t\tConfigType,\n\t\t\"attachments\" | \"workspace\" | \"mapRotation\" | \"name\" | \"description\"\n\t>;\n};\n\n/**\n * Defines and validates a Battlefield 6 mod configuration object.\n *\n * This helper ensures your config is properly typed and can be\n * statically analyzed by TypeScript.\n *\n * @example\n * ```ts\n * export default defineBf6Config({\n * name: \"My Custom Mod\",\n * description: \"Adds custom rules\",\n * outDir: \"dist\",\n * entrypoint: \"src/main.ts\",\n * game: {\n * mutators: [],\n * assetRestrictions: {},\n * teamComposition: [],\n * },\n * });\n * ```\n *\n * @param bf6Config The configuration object to validate and return.\n * @returns The validated Battlefield 6 configuration object.\n */\nexport function defineBf6Config(bf6Config: Bf6Config): Bf6Config {\n\treturn bf6Config;\n}\n","import colors from \"colors\";\nimport stripAnsi from \"strip-ansi\";\n\n/**\n * Safely prints a message with a right-aligned timestamp.\n */\nexport const printToConsole = (message: string, error: boolean = false) => {\n\tconst now = new Date();\n\tconst formattedTime = colors.grey(now.toLocaleTimeString());\n\tconst terminalWidth = process.stdout.columns || 80;\n\n\tconst timeLength = stripAnsi(formattedTime).length;\n\tconst messageLength = stripAnsi(message).length;\n\tconst available = terminalWidth - (messageLength + timeLength);\n\n\tconst spacing = available > 1 ? \" \".repeat(available) : \" \";\n\n\tif (error) console.error(`${message}${spacing}${formattedTime}`);\n\telse console.log(`${message}${spacing}${formattedTime}`);\n};\n\nexport const readableList = (initialValues: readonly string[]) => {\n\tconst values = initialValues.map((v) => `\"${v}\"`);\n\tif (values.length > 1)\n\t\treturn `${values.slice(0, -1).join(\", \")} or ${values.slice(-1)}`;\n\treturn values[0];\n};\n","import type { Node, VariableDeclarator } from \"@oxc-project/types\";\nimport colors from \"colors\";\nimport type { Plugin } from \"rolldown\";\n\nexport function extractBf6Strings(\n\tbf6Strings: Record<string, string>,\n\tgenerateFromLiterals: boolean,\n): Plugin {\n\treturn {\n\t\tname: \"extract-bf6-strings\",\n\t\tgenerateBundle(_options, bundle) {\n\t\t\tif (!generateFromLiterals) return;\n\n\t\t\tlet id = 0;\n\t\t\tconst existingStrings = new Set(Object.values(bf6Strings));\n\n\t\t\tfor (const [_file, output] of Object.entries(bundle)) {\n\t\t\t\tif (output.type !== \"chunk\") continue;\n\t\t\t\tconst code = output.code;\n\n\t\t\t\t// Parse the code with rolldown’s parser\n\t\t\t\tconst program = this.parse(code, {\n\t\t\t\t\tlang: \"ts\",\n\t\t\t\t\tastType: \"js\",\n\t\t\t\t\trange: true,\n\t\t\t\t});\n\n\t\t\t\t// Visit every node recursively to collect string literals\n\t\t\t\tfunction walk(node: Node) {\n\t\t\t\t\tif (node.type === \"Literal\") {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof node.value !== \"string\" ||\n\t\t\t\t\t\t\tnode.value === \"\" ||\n\t\t\t\t\t\t\tnode.value === \"----uniquename----\"\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!(node.value in bf6Strings) &&\n\t\t\t\t\t\t\t!existingStrings.has(node.value)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\texistingStrings.add(node.value);\n\t\t\t\t\t\t\tbf6Strings[`__auto__${id++}`] = node.value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const value of Object.values(node)) {\n\t\t\t\t\t\tif (!value) continue;\n\t\t\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\t\t\tfor (const v of value) {\n\t\t\t\t\t\t\t\tif (v && typeof v === \"object\" && \"type\" in v) walk(v as Node);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (typeof value === \"object\" && \"type\" in value) {\n\t\t\t\t\t\t\twalk(value as Node);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (const item of program.body) {\n\t\t\t\t\twalk(item);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tmoduleParsed(moduleInfo) {\n\t\t\tif (moduleInfo.code && moduleInfo.exports.includes(\"bf6Strings\")) {\n\t\t\t\tconst code = moduleInfo.code;\n\t\t\t\tconst getSnippet = (\n\t\t\t\t\tstart: number,\n\t\t\t\t\tend: number,\n\t\t\t\t\tlinesBefore = 2,\n\t\t\t\t\tlinesAfter = 2,\n\t\t\t\t) => {\n\t\t\t\t\tconst lines = code.split(/\\r?\\n/);\n\t\t\t\t\tconst startLine = Math.max(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tcode.slice(0, start).split(/\\r?\\n/).length - 1 - linesBefore,\n\t\t\t\t\t);\n\t\t\t\t\tconst endLine = Math.min(\n\t\t\t\t\t\tlines.length,\n\t\t\t\t\t\tcode.slice(0, end).split(/\\r?\\n/).length - 1 + linesAfter,\n\t\t\t\t\t);\n\t\t\t\t\treturn lines\n\t\t\t\t\t\t.slice(startLine, endLine)\n\t\t\t\t\t\t.map((line, i) => {\n\t\t\t\t\t\t\tconst actualLine = startLine + i + 1;\n\t\t\t\t\t\t\treturn `${actualLine.toString().padStart(3)} | ${line}`;\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t};\n\n\t\t\t\tconst program = this.parse(code, {\n\t\t\t\t\tlang: \"ts\",\n\t\t\t\t\tastType: \"js\",\n\t\t\t\t\trange: true,\n\t\t\t\t});\n\n\t\t\t\tconst extractObjectLiteral = (declaration: VariableDeclarator) => {\n\t\t\t\t\tif (declaration.init?.type !== \"ObjectExpression\") {\n\t\t\t\t\t\tconst snippet = getSnippet(...(declaration.range ?? [0, 0]));\n\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings export: expected an object literal.\\n\\n${snippet}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const property of declaration.init.properties) {\n\t\t\t\t\t\tif (property.type !== \"Property\") {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings property: expected a key/value pair.\\nFound type: ${property.type}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet key:\n\t\t\t\t\t\t\t| string\n\t\t\t\t\t\t\t| number\n\t\t\t\t\t\t\t| bigint\n\t\t\t\t\t\t\t| boolean\n\t\t\t\t\t\t\t| RegExp\n\t\t\t\t\t\t\t| null\n\t\t\t\t\t\t\t| undefined;\n\t\t\t\t\t\tif (property.key.type === \"Literal\") {\n\t\t\t\t\t\t\tkey = property.key.value;\n\t\t\t\t\t\t} else if (property.key.type === \"Identifier\") {\n\t\t\t\t\t\t\tkey = property.key.name;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings key: expected a string literal or identifier.\\nFound type: ${property.key.type}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (property.value.type !== \"Literal\") {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} Invalid bf6Strings value: expected a string literal.\\nFound type: ${property.value.type}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst value = property.value.value;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof key !== \"string\" &&\n\t\t\t\t\t\t\ttypeof key !== \"number\" &&\n\t\t\t\t\t\t\ttypeof key !== \"bigint\" &&\n\t\t\t\t\t\t\ttypeof key !== \"boolean\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} bf6Strings key must be a string, found: ${typeof key}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof value !== \"string\" &&\n\t\t\t\t\t\t\ttypeof value !== \"number\" &&\n\t\t\t\t\t\t\ttypeof value !== \"bigint\" &&\n\t\t\t\t\t\t\ttypeof value !== \"boolean\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst snippet = getSnippet(...(property.range ?? [0, 0]));\n\t\t\t\t\t\t\tthis.error(\n\t\t\t\t\t\t\t\t`${colors.red.bold(\"✗\")} bf6Strings value must be a string, found: ${typeof value}\\n\\n${snippet}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbf6Strings[`${key}`] = `${value}`;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tfor (const item of program.body) {\n\t\t\t\t\t// Case 1: plain variable `var bf6Strings = {...}`\n\t\t\t\t\tif (item.type === \"VariableDeclaration\") {\n\t\t\t\t\t\tfor (const declaration of item.declarations) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tdeclaration.type === \"VariableDeclarator\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.type === \"Identifier\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.name === \"bf6Strings\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\textractObjectLiteral(declaration);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Case 2: exported variable `export const bf6Strings = {...}`\n\t\t\t\t\tif (\n\t\t\t\t\t\titem.type === \"ExportNamedDeclaration\" &&\n\t\t\t\t\t\titem.declaration?.type === \"VariableDeclaration\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tfor (const declaration of item.declaration.declarations) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tdeclaration.type === \"VariableDeclarator\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.type === \"Identifier\" &&\n\t\t\t\t\t\t\t\tdeclaration.id.name === \"bf6Strings\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\textractObjectLiteral(declaration);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n\ttype Attachment,\n\tAttachmentType,\n\ttype ConfigType,\n\ttype MapType,\n} from \"@bf6mods/sdk\";\nimport colors from \"colors\";\nimport { createJiti } from \"jiti\";\nimport { rolldown } from \"rolldown\";\nimport {\n\ttype Bf6Config,\n\tMapId as MapIdEnum,\n} from \"../../resources/prepare/types/config.ts\";\nimport { printToConsole } from \"../utils.ts\";\nimport { extractBf6Strings } from \"./generated-strings.ts\";\n\ndeclare global {\n\tvar defineBf6Config: ((config: Bf6Config) => Bf6Config) | undefined;\n\tvar MapId: typeof MapIdEnum | undefined;\n}\n\n/**\n * Dynamically imports and validates bf6.config.ts\n */\nexport async function getBf6Config(rootDir: string): Promise<Bf6Config> {\n\tglobalThis.defineBf6Config = (c) => c;\n\tglobalThis.MapId = MapIdEnum;\n\tconst jiti = createJiti(rootDir, { interopDefault: true });\n\tconst result = await jiti.import(\"./bf6.config\", { default: true });\n\tdelete globalThis.defineBf6Config;\n\tdelete globalThis.MapId;\n\treturn result as Bf6Config;\n}\n\n/**\n * Builds the entire mod project once (for production or single run).\n */\nexport async function build() {\n\tconst workingDir = path.resolve(\".\");\n\tconst config = await getBf6Config(workingDir);\n\n\tconst outDir = path.resolve(workingDir, config.outDir);\n\tif (fs.existsSync(outDir))\n\t\tfs.rmSync(outDir, { recursive: true, force: true });\n\tfs.mkdirSync(outDir, { recursive: true });\n\n\tconst minifyJson =\n\t\ttypeof config.minify === \"boolean\"\n\t\t\t? config.minify\n\t\t\t: (config.minify?.json ?? false);\n\n\tconst generatedStrings = {};\n\tlet tsAttachment: Attachment | undefined;\n\tif (config.entrypoint) {\n\t\tconst entryAbs = path.resolve(workingDir, config.entrypoint);\n\t\tconst compiled = await buildEntrypoint(\n\t\t\tentryAbs,\n\t\t\tgeneratedStrings,\n\t\t\tconfig.generateStrings ?? true,\n\t\t);\n\t\ttsAttachment = createTsAttachment(entryAbs, compiled);\n\t}\n\n\tconst { attachments, mapRotation } = await collectAttachments(\n\t\tconfig,\n\t\tworkingDir,\n\t\ttsAttachment,\n\t\tgeneratedStrings,\n\t);\n\n\tawait writeModJson(config, outDir, attachments, mapRotation, minifyJson);\n\tprintToConsole(`${colors.green.bold(\"✓\")} Built mod: ${config.name}`);\n}\n\n/**\n * Compiles the TypeScript entrypoint using rolldown and returns the compiled code.\n */\nexport async function buildEntrypoint(\n\tentry: string,\n\tbf6Strings: Record<string, string>,\n\tgenerateStringsFromLiterals: boolean,\n): Promise<string> {\n\tconst bundle = await rolldown({\n\t\tinput: entry,\n\t\tplugins: [extractBf6Strings(bf6Strings, generateStringsFromLiterals)],\n\t\tlogLevel: \"debug\",\n\t\tresolve: {\n\t\t\talias: {\n\t\t\t\tmodlib: \"@bf6mods/sdk\",\n\t\t\t},\n\t\t},\n\t});\n\tconst result = await bundle.generate({\n\t\tformat: \"esm\",\n\t\tinlineDynamicImports: true,\n\t});\n\n\tconst code = result.output[0].code;\n\treturn code;\n}\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst spatialsDir = path.resolve(__dirname, \"../resources/maps/spatial\");\n/**\n * Collects all attachments (TS, scenes, strings) and returns them + mapRotation.\n */\nexport async function collectAttachments(\n\tconfig: Bf6Config,\n\tworkingDir: string,\n\ttsAttachment: Attachment | undefined,\n\tgeneratedStrings: Record<string, number | string> | undefined,\n) {\n\tconst attachments: Attachment[] = [];\n\tconst mapRotation: MapType[] = [];\n\n\tif (tsAttachment) attachments.push(tsAttachment);\n\n\t// Strings\n\tif (config.strings || Object.keys(generatedStrings ?? {}).length > 0) {\n\t\tconst strPath = path.resolve(workingDir, config.strings ?? \"strings.json\");\n\t\tlet raw = \"{}\";\n\t\tif (fs.existsSync(strPath)) {\n\t\t\traw = await fs.promises.readFile(strPath, \"utf8\");\n\t\t} else {\n\t\t\t// Create a placeholder for clarity (optional)\n\t\t\tprintToConsole(\n\t\t\t\tcolors.yellow(\n\t\t\t\t\t`⚠️ No strings.json found, generating from literals only.`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tconst attachment = createStringsAttachment(strPath, raw, generatedStrings);\n\t\tattachments.push(attachment);\n\t}\n\n\t// Scenes\n\tif (config.scenes) {\n\t\tlet mapIdx = 0;\n\t\tfor (const map of config.scenes) {\n\t\t\tlet mapId: MapIdEnum;\n\t\t\tlet scene: string;\n\t\t\tif (Array.isArray(map)) {\n\t\t\t\t[mapId, scene] = map;\n\t\t\t} else {\n\t\t\t\tmapId = map;\n\t\t\t\tscene = path.resolve(spatialsDir, `${mapId}.spatial.json`);\n\t\t\t}\n\n\t\t\tconst scenePath = path.resolve(workingDir, scene);\n\t\t\tif (!fs.existsSync(scenePath))\n\t\t\t\tthrow new Error(`Cannot find spatial data file: ${scene}`);\n\t\t\tconst raw = await fs.promises.readFile(scenePath, \"utf8\");\n\t\t\tconst spatial = createSpatialAttachment(scenePath, raw, mapIdx++);\n\t\t\tattachments.push(spatial);\n\t\t\tmapRotation.push({ id: mapId, spatialAttachment: spatial });\n\t\t}\n\t}\n\n\treturn { attachments, mapRotation };\n}\n\n/**\n * Writes the mod.json output to disk.\n */\nexport async function writeModJson(\n\tconfig: Bf6Config,\n\toutDir: string,\n\tattachments: ConfigType[\"attachments\"],\n\tmapRotation: MapType[],\n\tminify: boolean,\n) {\n\tconst baseGame = config.game;\n\tconst finalJson: ConfigType = {\n\t\tname: config.name,\n\t\tdescription: config.description,\n\t\tgameMode: \"ModBuilderCustom\",\n\t\tmutators: baseGame.mutators ?? {},\n\t\tassetRestrictions: baseGame.assetRestrictions ?? {},\n\t\tteamComposition: baseGame.teamComposition ?? [],\n\t\tmapRotation,\n\t\tattachments,\n\t};\n\n\tconst jsonOutput = minify\n\t\t? JSON.stringify(finalJson)\n\t\t: JSON.stringify(finalJson, null, 2);\n\tawait fs.promises.writeFile(path.resolve(outDir, \"mod.json\"), jsonOutput);\n\n\tif (config.outputArtifacts && finalJson?.attachments) {\n\t\tfor (const attachment of finalJson.attachments) {\n\t\t\tconst attachmentsDir = path.resolve(outDir, \"attachments\");\n\t\t\tif (!fs.existsSync(attachmentsDir))\n\t\t\t\tfs.mkdirSync(attachmentsDir, {\n\t\t\t\t\trecursive: true,\n\t\t\t\t});\n\t\t\tawait fs.promises.writeFile(\n\t\t\t\tpath.resolve(outDir, \"attachments\", attachment.filename),\n\t\t\t\tatob(attachment.attachmentData.original),\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport function createTsAttachment(\n\tfilePath: string,\n\tcompiled: string,\n): Attachment {\n\treturn {\n\t\tid: crypto.randomUUID(),\n\t\tversion: \"1.0\",\n\t\tfilename: `${path.parse(filePath).name}.js`,\n\t\tisProcessable: true,\n\t\tprocessingStatus: 2,\n\t\tattachmentType: AttachmentType.TypeScript,\n\t\tattachmentData: { original: toBase64(compiled), compiled: \"\" },\n\t\terrors: [],\n\t};\n}\n\nexport function createStringsAttachment(\n\tfilePath: string,\n\traw: string,\n\tgeneratedStrings?: Record<string, number | string> | undefined,\n): Attachment {\n\tlet result = raw;\n\tif (generatedStrings) {\n\t\tresult = JSON.stringify(\n\t\t\t{\n\t\t\t\t...generatedStrings,\n\t\t\t\t...JSON.parse(raw),\n\t\t\t},\n\t\t\tnull,\n\t\t\t4,\n\t\t);\n\t}\n\n\treturn {\n\t\tid: crypto.randomUUID(),\n\t\tversion: \"1.0\",\n\t\tfilename: path.basename(filePath),\n\t\tisProcessable: true,\n\t\tprocessingStatus: 2,\n\t\tattachmentType: AttachmentType.Strings,\n\t\tattachmentData: { original: toBase64(result), compiled: \"\" },\n\t\terrors: [],\n\t};\n}\n\nexport function createSpatialAttachment(\n\tfilePath: string,\n\traw: string,\n\tmapIdx: number,\n): MapType[\"spatialAttachment\"] {\n\treturn {\n\t\tid: crypto.randomUUID(),\n\t\tversion: \"1.0\",\n\t\tfilename: path.basename(filePath),\n\t\tisProcessable: true,\n\t\tprocessingStatus: 2,\n\t\tattachmentType: AttachmentType.SpatialData,\n\t\tattachmentData: { original: toBase64(raw), compiled: \"\" },\n\t\tmetadata: `mapIdx=${mapIdx}`,\n\t\terrors: [],\n\t};\n}\n\nexport function toBase64(input: string | Buffer): string {\n\treturn Buffer.isBuffer(input)\n\t\t? input.toString(\"base64\")\n\t\t: Buffer.from(input, \"utf8\").toString(\"base64\");\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport colors from \"colors\";\nimport { printToConsole } from \"./utils.ts\";\n\nexport class Bf6Logger {\n\tprivate static instance: Bf6Logger | undefined;\n\n\tprivate watchTarget: string;\n\tprivate watcher?: FSWatcher;\n\tprivate lastSize: number;\n\n\tconstructor(input?: string) {\n\t\tif (Bf6Logger.instance) {\n\t\t\tprintToConsole(\n\t\t\t\tcolors.yellow(\"⚠ Existing logger found — closing it first...\"),\n\t\t\t);\n\t\t\tBf6Logger.instance.stop();\n\t\t}\n\n\t\tif (input) this.watchTarget = path.resolve(input);\n\t\telse if (process.env.LOCALAPPDATA)\n\t\t\tthis.watchTarget = path.resolve(\n\t\t\t\tprocess.env.LOCALAPPDATA,\n\t\t\t\t\"temp\",\n\t\t\t\t\"Battlefieldâ„¢ 6\",\n\t\t\t\t\"PortalLog.txt\",\n\t\t\t);\n\t\telse\n\t\t\tthrow new Error(\n\t\t\t\t`${colors.red.bold(\"✗\")} Env variable LOCALAPPDATA not defined, and input file to watch was not provided!`,\n\t\t\t);\n\n\t\tif (!fs.existsSync(this.watchTarget))\n\t\t\tthrow new Error(\n\t\t\t\t`${colors.red.bold(\"✗\")} Cannot find file '${this.watchTarget}' to watch for changes`,\n\t\t\t);\n\n\t\tthis.lastSize = fs.statSync(this.watchTarget).size;\n\t\tBf6Logger.instance = this;\n\t}\n\n\tasync start() {\n\t\tprintToConsole(\n\t\t\tcolors.cyan(\n\t\t\t\t`👀 Watching Battlefield logs at: ${colors.yellow(this.watchTarget)}`,\n\t\t\t),\n\t\t);\n\n\t\tthis.watcher = chokidar.watch(this.watchTarget, {\n\t\t\tpersistent: true,\n\t\t\tignoreInitial: true,\n\t\t});\n\n\t\tthis.watcher.on(\"change\", this.handleChange.bind(this));\n\t}\n\n\tasync stop() {\n\t\tif (this.watcher) {\n\t\t\tthis.watcher.close();\n\t\t\tthis.watcher = undefined;\n\t\t\tprintToConsole(\n\t\t\t\tcolors.yellow(\n\t\t\t\t\t`${colors.red.bold(\"✗\")} Stopped watching: ${colors.gray(this.watchTarget)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tBf6Logger.instance = undefined;\n\t}\n\n\tprivate handleChange(file: string, stats?: fs.Stats) {\n\t\tif (!stats) return;\n\t\tif (stats.size > this.lastSize) {\n\t\t\tconst newBytes = stats.size - this.lastSize;\n\t\t\tconst fd = fs.openSync(file, \"r\");\n\t\t\tconst buffer = Buffer.alloc(newBytes);\n\t\t\tfs.readSync(fd, buffer, 0, newBytes, this.lastSize);\n\t\t\tfs.closeSync(fd);\n\n\t\t\tconst newContent = buffer\n\t\t\t\t.toString(\"utf8\")\n\t\t\t\t.split(\"QuickJS: \")\n\t\t\t\t.filter((debug) => debug.trim() !== \"\")\n\t\t\t\t.map((debug) => debug.trim())\n\t\t\t\t.map((debug) => {\n\t\t\t\t\tif (debug.startsWith(\"console.log: \")) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"console.log\",\n\t\t\t\t\t\t\ttext: debug.replace(\"console.log: \", \"\"),\n\t\t\t\t\t\t};\n\t\t\t\t\t} else if (debug.startsWith(\"Exception:\")) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttype: \"exception\",\n\t\t\t\t\t\t\ttext: debug.replace(\"Exception:\", \"\"),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: \"unknown\",\n\t\t\t\t\t\ttext: debug,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\tfor (const content of newContent) {\n\t\t\t\tif (content.type === \"console.log\")\n\t\t\t\t\tprintToConsole(colors.gray(content.text));\n\t\t\t\telse if (content.type === \"exception\")\n\t\t\t\t\tprintToConsole(colors.red(content.text), true);\n\t\t\t\telse printToConsole(colors.bold(content.text));\n\t\t\t}\n\t\t}\n\t\tthis.lastSize = stats.size;\n\t}\n}\n","import fs from \"node:fs\";\nimport { glob } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport colors from \"colors\";\nimport { build, getBf6Config } from \"./build/index.ts\";\nimport { Bf6Logger } from \"./log.ts\";\nimport { printToConsole } from \"./utils.ts\";\n\nexport async function dev() {\n\tconst workingDir = path.resolve(\".\");\n\tlet config = await getBf6Config(workingDir);\n\tconst outDir = path.resolve(workingDir, config.outDir);\n\tfs.mkdirSync(outDir, { recursive: true });\n\n\tprintToConsole(colors.cyan(`▶ Starting dev for ${config.name}`));\n\n\tlet watcher: FSWatcher | undefined;\n\n\t// biome-ignore lint/suspicious/noExplicitAny: doesn't really matter in this case\n\tfunction debounce<T extends (...args: any[]) => void>(fn: T, delay: number) {\n\t\tlet timer: NodeJS.Timeout | undefined;\n\t\treturn (...args: Parameters<T>) => {\n\t\t\tif (timer) clearTimeout(timer);\n\t\t\ttimer = setTimeout(() => fn(...args), delay);\n\t\t};\n\t}\n\n\tconst rebuild = async (trigger: string) => {\n\t\tprintToConsole(\n\t\t\tcolors.yellow(\n\t\t\t\t`↻ Change detected in ${path.basename(trigger)}, rebuilding...`,\n\t\t\t),\n\t\t);\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\tawait build();\n\t\t\tconst duration = ((performance.now() - start) / 1000).toFixed(2);\n\t\t\tprintToConsole(\n\t\t\t\t`${colors.green.bold(\"✓\")} Updated mod.json (${duration}s)`,\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tprintToConsole(\n\t\t\t\tcolors.red(\n\t\t\t\t\t`${colors.red.bold(\"✗\")} Rebuild failed: ${(err as Error).message}`,\n\t\t\t\t),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\t};\n\n\tconst debouncedRebuild = debounce(rebuild, 200);\n\n\tasync function collectWatchTargets(): Promise<string[]> {\n\t\tconst targets: string[] = [];\n\n\t\tif (config.entrypoint)\n\t\t\ttargets.push(path.resolve(workingDir, config.entrypoint));\n\t\tif (config.scenes)\n\t\t\tfor (const [, scene] of config.scenes)\n\t\t\t\ttargets.push(path.resolve(workingDir, scene));\n\t\tif (config.strings) targets.push(path.resolve(workingDir, config.strings));\n\t\tfor await (const entry of glob(\"bf6.config.*\")) targets.push(entry);\n\t\tfor await (const entry of glob(\"src/**/*\")) targets.push(entry);\n\n\t\treturn targets;\n\t}\n\n\tasync function setupWatcher() {\n\t\tif (watcher) {\n\t\t\tawait watcher.close();\n\t\t\tprintToConsole(\n\t\t\t\tcolors.grey(\"♻ Reloading watcher due to config change...\"),\n\t\t\t);\n\t\t\tawait new Promise((r) => setTimeout(r, 10));\n\t\t}\n\n\t\tconst watchTargets = await collectWatchTargets();\n\t\tprintToConsole(colors.cyan(`👀 Watching ${watchTargets.length} files...`));\n\n\t\twatcher = chokidar.watch(watchTargets, {\n\t\t\tpersistent: true,\n\t\t\tignoreInitial: true,\n\t\t\tawaitWriteFinish: { stabilityThreshold: 250, pollInterval: 100 },\n\t\t\tignored: [outDir, `${outDir}/**`, \"node_modules/**\", \".git/**\"],\n\t\t});\n\n\t\twatcher.on(\"error\", (err) => {\n\t\t\tprintToConsole(\n\t\t\t\tcolors.red(`⚠ Watcher error: ${(err as Error).message}`),\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tsetTimeout(setupWatcher, 1000);\n\t\t});\n\n\t\twatcher.on(\"change\", async (file) => {\n\t\t\tif (file.includes(\"bf6.config.\")) {\n\t\t\t\tprintToConsole(\n\t\t\t\t\tcolors.magenta(\"⚙ Config changed — reloading watcher...\"),\n\t\t\t\t);\n\t\t\t\tconfig = await getBf6Config(workingDir);\n\t\t\t\tawait setupWatcher();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdebouncedRebuild(file);\n\t\t});\n\n\t\twatcher.on(\"add\", debouncedRebuild);\n\t\tawait rebuild(\"initial\");\n\t}\n\n\tprocess.on(\"SIGINT\", async () => {\n\t\tprintToConsole(colors.red(`\\n${colors.red.bold(\"✗\")} Exiting dev mode...`));\n\t\tawait watcher?.close();\n\t\tprocess.exit(0);\n\t});\n\n\tawait setupWatcher();\n\n\tlet logger: Bf6Logger | undefined;\n\ttry {\n\t\tlogger = new Bf6Logger();\n\t\tlogger.start();\n\t} catch (error) {\n\t\tprintToConsole(\n\t\t\tcolors.grey(\n\t\t\t\t\"Failed to start logging for the following reason (building will still work)\",\n\t\t\t),\n\t\t\ttrue,\n\t\t);\n\t\tprintToConsole((error as Error).message, true);\n\t}\n}\n","{\n\t\"name\": \"@bf6mods/sdk\",\n\t\"version\": \"1.2.0\",\n\t\"description\": \"TypeScript definitions for the BF6 modding API\",\n\t\"type\": \"module\",\n\t\"license\": \"MIT\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./dist/index.d.ts\",\n\t\t\t\"import\": \"./dist/index.js\"\n\t\t},\n\t\t\"./std\": {\n\t\t\t\"types\": \"./dist/std/index.d.ts\",\n\t\t\t\"import\": \"./dist/std/index.js\"\n\t\t}\n\t},\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/bf6mods/bf6mods.git\"\n\t},\n\t\"files\": [\n\t\t\"dist/\"\n\t],\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\"\n\t},\n\t\"keywords\": [\n\t\t\"bf6\",\n\t\t\"mods\",\n\t\t\"typescript\",\n\t\t\"definitions\",\n\t\t\"api\"\n\t],\n\t\"publishConfig\": {\n\t\t\"access\": \"public\"\n\t}\n}\n","import child_process from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as prompts from \"@clack/prompts\";\nimport bf6SdkPackageJson from \"../../../sdk/package.json\" with { type: \"json\" };\nimport bf6CliPackageJson from \"../../package.json\" with { type: \"json\" };\nimport { importFile } from \"./import.ts\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst templatesDir = path.resolve(__dirname, \"../resources/templates\");\n\nfunction renameFilesRecursively(dir: string, modName: string) {\n\tconst entries = fs.readdirSync(dir, { withFileTypes: true });\n\tfor (const entry of entries) {\n\t\tconst oldPath = path.join(dir, entry.name);\n\t\tlet newPath = oldPath;\n\n\t\t// Rename file or folder if it includes {name}\n\t\tif (entry.name.includes(\"{name}\")) {\n\t\t\tconst newName = entry.name.replace(\"{name}\", modName);\n\t\t\tnewPath = path.join(dir, newName);\n\t\t\tfs.renameSync(oldPath, newPath);\n\t\t}\n\n\t\t// If it's a bf6.config.ts, replace the {bf6ConfigName} placeholder\n\t\tif (entry.name === \"bf6.config.ts\") {\n\t\t\tlet content = fs.readFileSync(newPath, \"utf-8\");\n\t\t\tcontent = content.replace(/{bf6ConfigName}/g, modName);\n\t\t\tfs.writeFileSync(newPath, content, \"utf-8\");\n\t\t}\n\n\t\t// If it's a package.json, rewrite the \"name\" field\n\t\tif (entry.name === \"package.json\") {\n\t\t\tconst pkg = JSON.parse(fs.readFileSync(newPath, \"utf-8\"));\n\t\t\tpkg.name = modName;\n\t\t\tif (!pkg.devDependencies) pkg.devDependencies = {};\n\t\t\tpkg.devDependencies[\"@bf6mods/cli\"] = `^${bf6CliPackageJson.version}`;\n\t\t\tpkg.devDependencies[\"@bf6mods/sdk\"] = `^${bf6SdkPackageJson.version}`;\n\t\t\tfs.writeFileSync(newPath, JSON.stringify(pkg, null, 2));\n\t\t}\n\n\t\t// Recurse into directories (using the new name if renamed)\n\t\tif (fs.statSync(newPath).isDirectory()) {\n\t\t\trenameFilesRecursively(newPath, modName);\n\t\t}\n\t}\n}\n\nexport const templates = [\n\t\"Basic\",\n\t\"AcePursuit\",\n\t\"BombSquad\",\n\t\"Exfil\",\n\t\"Vertigo\",\n] as const;\n\nexport async function startProject(\n\tdestination: string,\n\ttemplate: (typeof templates)[number] | \"None\",\n\tname?: string,\n) {\n\tif ([\"AcePursuit\", \"BombSquad\", \"Exfil\", \"Vertigo\"].includes(template)) {\n\t\tconst importPath = path.resolve(templatesDir, `${template}.json`);\n\t\tawait importFile(importPath, destination, name);\n\t} else if (template === \"None\") {\n\t\t// Do nothing if none\n\t} else {\n\t\t// Handles the Basic and any other template provided\n\t\tconst templateDir = path.resolve(templatesDir, template);\n\t\tfs.cpSync(templateDir, destination, { recursive: true });\n\t}\n\n\tfs.cpSync(path.resolve(templatesDir, \"All\"), destination, {\n\t\trecursive: true,\n\t});\n\n\tif (name) renameFilesRecursively(destination, name);\n}\n\nexport function installDependencies(projectDir: string) {\n\ttry {\n\t\tchild_process.execSync(`npm install`, {\n\t\t\tstdio: \"inherit\",\n\t\t\tcwd: projectDir,\n\t\t});\n\t\treturn true;\n\t} catch (_err) {\n\t\treturn false;\n\t}\n}\n\nconst _defaultTargetDir = \"bf6-mod\";\nconst cancel = () => prompts.cancel(\"Operation cancelled\");\n\nfunction isEmpty(path: string) {\n\tconst files = fs.readdirSync(path);\n\treturn files.length === 0 || (files.length === 1 && files[0] === \".git\");\n}\n\nfunction emptyDir(dir: string) {\n\tif (!fs.existsSync(dir)) {\n\t\treturn;\n\t}\n\tfor (const file of fs.readdirSync(dir)) {\n\t\tif (file === \".git\") {\n\t\t\tcontinue;\n\t\t}\n\t\tfs.rmSync(path.resolve(dir, file), { recursive: true, force: true });\n\t}\n}\n\nexport async function init(\n\targTargetDir?: string,\n\toptions?: {\n\t\tname?: string;\n\t\ttemplate?: (typeof templates)[number];\n\t\tonExists?: \"overwrite\" | \"cancel\" | \"ignore\";\n\t\tinstallDependencies?: boolean;\n\t},\n) {\n\tprompts.intro(\"Initialize Bf6 Mod\");\n\n\tconst path = argTargetDir\n\t\t? argTargetDir\n\t\t: await prompts.text({\n\t\t\t\tmessage: \"Where should we create your project?\",\n\t\t\t\tplaceholder: \"./ace-pursuit\",\n\t\t\t\tvalidate: (value) => {\n\t\t\t\t\tif (!value) return \"Please enter a path.\";\n\t\t\t\t\tif (value[0] !== \".\") return \"Please enter a relative path.\";\n\t\t\t\t},\n\t\t\t});\n\tif (prompts.isCancel(path)) return cancel();\n\n\tlet name = options?.name\n\t\t? options.name\n\t\t: await prompts.text({\n\t\t\t\tmessage: \"What is the name of your mod?\",\n\t\t\t\tplaceholder: \"Ace Pursuit\",\n\t\t\t\tvalidate: (value) => {\n\t\t\t\t\tif (!value.trim()) return \"Please enter a name.\";\n\t\t\t\t},\n\t\t\t});\n\tif (prompts.isCancel(name)) return cancel();\n\tname = name.trim();\n\n\tif (fs.existsSync(path) && !isEmpty(path)) {\n\t\tlet overwrite: \"yes\" | \"no\" | \"ignore\" | undefined;\n\t\tif (options?.onExists) {\n\t\t\tswitch (options.onExists) {\n\t\t\t\tcase \"overwrite\":\n\t\t\t\t\toverwrite = \"yes\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"cancel\":\n\t\t\t\t\toverwrite = \"no\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"ignore\":\n\t\t\t\t\toverwrite = \"ignore\";\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tconst res = overwrite\n\t\t\t? overwrite\n\t\t\t: await prompts.select({\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t(path === \".\"\n\t\t\t\t\t\t\t? \"Current directory\"\n\t\t\t\t\t\t\t: `Target directory \"${path}\"`) +\n\t\t\t\t\t\t` is not empty. Please choose how to proceed:`,\n\t\t\t\t\toptions: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"Cancel operation\",\n\t\t\t\t\t\t\tvalue: \"no\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"Remove existing files and continue\",\n\t\t\t\t\t\t\tvalue: \"yes\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlabel: \"Ignore files and continue\",\n\t\t\t\t\t\t\tvalue: \"ignore\",\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t});\n\t\tif (prompts.isCancel(res)) return cancel();\n\t\toverwrite = res;\n\n\t\tswitch (overwrite) {\n\t\t\tcase \"yes\":\n\t\t\t\temptyDir(path);\n\t\t\t\tbreak;\n\t\t\tcase \"no\":\n\t\t\t\tcancel();\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tconst template = options?.template\n\t\t? options.template\n\t\t: await prompts.select({\n\t\t\t\tmessage: \"Select a template:\",\n\t\t\t\toptions: templates.map((template) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tlabel: template,\n\t\t\t\t\t\tvalue: template,\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t});\n\tif (prompts.isCancel(template)) return cancel();\n\n\tawait startProject(path, template, name);\n\n\tif (options?.installDependencies) {\n\t\tconst s = prompts.spinner();\n\t\ts.start(\"Installing via npm\");\n\t\tconst installed = installDependencies(path);\n\t\tif (installed) s.stop(\"Installed via npm\");\n\t\telse s.stop(\"Failed to install via npm\", 1);\n\n\t\tconst nextSteps = `cd ${path}\\nnpm run build`;\n\n\t\tprompts.note(nextSteps, \"Next steps.\");\n\t} else {\n\t\tconst nextSteps = `cd ${path}\\nnpm i\\nnpm run build`;\n\n\t\tprompts.note(nextSteps, \"Next steps.\");\n\t}\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { AttachmentType, type ConfigType } from \"@bf6mods/sdk\";\nimport colors from \"colors\";\nimport stringifyObject from \"stringify-object\";\nimport { MapId as MapIdEnum } from \"../resources/prepare/types/config.ts\";\nimport { startProject } from \"./init.ts\";\nimport { printToConsole } from \"./utils.ts\";\n\nasync function writeFileSafe(filePath: string, data: string | Buffer) {\n\tconst dir = path.dirname(filePath);\n\tawait fs.promises.mkdir(dir, { recursive: true });\n\tawait fs.promises.writeFile(filePath, data);\n}\n\nfunction getMapKeyByValue(value: string): keyof typeof MapIdEnum | undefined {\n\treturn Object.keys(MapIdEnum).find(\n\t\t(k) => MapIdEnum[k as keyof typeof MapIdEnum] === value,\n\t) as keyof typeof MapIdEnum | undefined;\n}\n\nexport async function importFile(\n\tinput: string,\n\toutput: string,\n\tname?: string,\n\tlogging = false,\n) {\n\tconst start = performance.now();\n\tconst workingDir = path.resolve(\".\");\n\tconst entrypoint = path.resolve(workingDir, input);\n\tconst outDir = path.resolve(workingDir, output);\n\n\tif (!fs.existsSync(entrypoint)) throw new Error(\"Cannot find strings file\");\n\n\tif (logging)\n\t\tprintToConsole(colors.cyan(`📦 Importing config from: ${entrypoint}`));\n\n\tconst config = JSON.parse(\n\t\tawait fs.promises.readFile(entrypoint, { encoding: \"utf8\" }),\n\t) as ConfigType;\n\n\tif (!fs.existsSync(outDir)) {\n\t\tawait fs.promises.mkdir(outDir, { recursive: true });\n\t\tif (logging)\n\t\t\tprintToConsole(colors.cyan(`📁 Created output directory: ${outDir}`));\n\t}\n\n\tawait startProject(outDir, \"None\", name ?? config.name);\n\n\tlet typescriptFile: string | undefined;\n\tlet stringsFile: string | undefined;\n\tconst scenes: [string, string][] = [];\n\tconst promises: Promise<unknown>[] = [];\n\n\tif (config.attachments) {\n\t\tlet unamedAttachmentIndex = 0;\n\t\tfor (const attachment of config.attachments) {\n\t\t\tlet filename = attachment.filename?.trim();\n\t\t\tif (!filename) {\n\t\t\t\tunamedAttachmentIndex++;\n\n\t\t\t\tlet ext: string = \"\";\n\t\t\t\tif (attachment.attachmentType === AttachmentType.TypeScript)\n\t\t\t\t\text = \".ts\";\n\t\t\t\telse if (attachment.attachmentType === AttachmentType.Strings)\n\t\t\t\t\text = \".strings.json\";\n\t\t\t\telse if (attachment.attachmentType === AttachmentType.SpatialData)\n\t\t\t\t\text = \".spatial.json\";\n\n\t\t\t\tif (unamedAttachmentIndex > 1)\n\t\t\t\t\tfilename = `attachment_${unamedAttachmentIndex}${ext}`;\n\t\t\t\telse filename = `attachment${ext}`;\n\t\t\t}\n\n\t\t\tif (attachment.attachmentType === AttachmentType.TypeScript)\n\t\t\t\ttypescriptFile = filename;\n\n\t\t\tif (attachment.attachmentType === AttachmentType.Strings)\n\t\t\t\tstringsFile = filename;\n\n\t\t\tif (attachment.attachmentType === AttachmentType.SpatialData) {\n\t\t\t\t// We'll just use the data attached to mapRotation instead\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpromises.push(\n\t\t\t\twriteFileSafe(\n\t\t\t\t\tpath.resolve(outDir, \"src\", filename),\n\t\t\t\t\tatob(attachment.attachmentData.original),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tif (logging)\n\t\t\t\tprintToConsole(\n\t\t\t\t\t`${colors.green(\"✓\")} Wrote attachment: ${colors.yellow(filename)}`,\n\t\t\t\t);\n\t\t}\n\t}\n\n\t// Extract spatial scenes from mapRotation instead of guessing\n\tif (config.mapRotation?.length) {\n\t\tfor (const map of config.mapRotation) {\n\t\t\tpromises.push(\n\t\t\t\twriteFileSafe(\n\t\t\t\t\tpath.resolve(outDir, \"src\", \"scenes\", map.spatialAttachment.filename),\n\t\t\t\t\tatob(map.spatialAttachment.attachmentData.original),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tscenes.push([map.id, `src/scenes/${map.spatialAttachment.filename}`]);\n\n\t\t\tif (logging)\n\t\t\t\tprintToConsole(\n\t\t\t\t\t`${colors.green(\"✓\")} Added scene: ${colors.yellow(map.id)}`,\n\t\t\t\t);\n\t\t}\n\t}\n\n\t// build the new bf6.config.ts structure\n\tconst bf6Config = {\n\t\tname: name ?? config.name,\n\t\tdescription: config.description,\n\t\toutDir: \"dist\",\n\t\tentrypoint: typescriptFile ? `src/${typescriptFile}` : undefined,\n\t\tscenes: scenes\n\t\t\t? scenes.map(([id, path]) => [\n\t\t\t\t\t`MapId.${getMapKeyByValue(id) ?? id}`,\n\t\t\t\t\tpath,\n\t\t\t\t])\n\t\t\t: undefined,\n\t\tstrings: stringsFile ? `src/${stringsFile}` : undefined,\n\t\tgame: {\n\t\t\tmutators: config.mutators,\n\t\t\tassetRestrictions: config.assetRestrictions,\n\t\t\tgameMode: config.gameMode,\n\t\t\tteamComposition: config.teamComposition,\n\t\t},\n\t};\n\n\tconst bf6ConfigContent = `export default defineBf6Config(${stringifyWithRaw(\n\t\tbf6Config,\n\t)});\\n`;\n\n\tpromises.push(\n\t\twriteFileSafe(path.resolve(outDir, \"bf6.config.ts\"), bf6ConfigContent),\n\t);\n\n\tawait Promise.all(promises);\n\n\tconst duration = ((performance.now() - start) / 1000).toFixed(2);\n\tif (logging)\n\t\tprintToConsole(\n\t\t\t`${colors.green.bold(\"✓\")} Import complete (${duration}s) → ${colors.yellow(outDir)}`,\n\t\t);\n}\n\nfunction stringifyWithRaw(value: unknown, options = {}) {\n\treturn stringifyObject(value, {\n\t\tindent: \" \", // 4 spaces\n\t\tsingleQuotes: true,\n\t\ttransform: (_obj, _prop, originalResult) => {\n\t\t\t// Remove quotes from MapId.* expressions\n\t\t\tif (/^['\"]MapId\\.[A-Za-z0-9_]+['\"]$/.test(originalResult)) {\n\t\t\t\treturn originalResult.slice(1, -1); // remove surrounding quotes\n\t\t\t}\n\t\t\treturn originalResult;\n\t\t},\n\t\t...options,\n\t});\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport colors from \"colors\";\nimport { genExport, genInlineTypeImport } from \"knitwork\";\nimport { printToConsole } from \"./utils.js\";\n\nexport async function prepare() {\n\ttry {\n\t\tconst __filename = fileURLToPath(import.meta.url);\n\t\tconst __dirname = path.dirname(__filename);\n\n\t\tconst _workingDir = path.resolve(\".\");\n\t\tconst buildDir = path.resolve(\".bf6\");\n\n\t\tconst resources = path.resolve(__dirname, \"../resources/prepare\");\n\n\t\tfs.cpSync(\n\t\t\tpath.resolve(resources, \"tsconfig.json\"),\n\t\t\tpath.resolve(buildDir, \"tsconfig.json\"),\n\t\t);\n\t\tfs.cpSync(\n\t\t\tpath.resolve(resources, \"bf6.d.ts\"),\n\t\t\tpath.resolve(buildDir, \"bf6.d.ts\"),\n\t\t);\n\t\tfs.cpSync(\n\t\t\tpath.resolve(resources, \"types\", \"config.ts\"),\n\t\t\tpath.resolve(buildDir, \"types\", \"config.ts\"),\n\t\t);\n\n\t\tconst ConfigFileExports = genExport(\"./types/config.ts\", [\n\t\t\t\"defineBf6Config\",\n\t\t]);\n\t\tfs.writeFileSync(\n\t\t\tpath.resolve(buildDir, \"imports.d.ts\"),\n\t\t\t`${ConfigFileExports}\\n`,\n\t\t);\n\n\t\tconst augmentations: Record<string, string> = {\n\t\t\tdefineBf6Config: genInlineTypeImport(\n\t\t\t\t\"./types/config.ts\",\n\t\t\t\t`defineBf6Config`,\n\t\t\t),\n\t\t\tMapId: genInlineTypeImport(\"./types/config.ts\", `MapId`),\n\t\t};\n\n\t\tconst args = genNamespaceAugmentation(\"global\", augmentations);\n\t\tfs.writeFileSync(\n\t\t\tpath.resolve(buildDir, \"globals.d.ts\"),\n\t\t\t`export {}\\n\\n${args}\\n`,\n\t\t);\n\n\t\tprintToConsole(`${colors.green.bold(\"✓\")} Types generated in .bf6`);\n\t} catch (error) {\n\t\tconsole.error(error);\n\t\tprintToConsole(\n\t\t\t`${colors.red.bold(\"✗\")} Types failed to generate in .bf6`,\n\t\t\ttrue,\n\t\t);\n\t}\n}\n\nconst genNamespaceAugmentation = (\n\tname: string,\n\tcontents: Record<string, string>,\n) => {\n\tif (!contents || Object.keys(contents).length === 0)\n\t\treturn `declare ${name} {}`;\n\tconst decls = Object.entries(contents)\n\t\t.map(([k, v]) => `\\tconst ${k}: ${v};`)\n\t\t.join(\"\\n\");\n\treturn `declare ${name} {\\n${decls}\\n}`;\n};\n","#!/usr/bin/env node\nimport colors from \"colors\";\nimport { Command } from \"commander\";\nimport pkg from \"../../package.json\" with { type: \"json\" };\nimport { build } from \"./build/index.ts\";\nimport { dev } from \"./dev.ts\";\nimport { importFile } from \"./import.ts\";\nimport { init, templates } from \"./init.js\";\nimport { Bf6Logger } from \"./log.ts\";\nimport { prepare } from \"./prepare.js\";\nimport { printToConsole, readableList } from \"./utils.ts\";\n\nconst program = new Command();\n\nprogram\n\t.name(Object.keys(pkg.bin)[0])\n\t.description(pkg.description)\n\t.version(pkg.version);\n\nprogram\n\t.command(\"init\")\n\t.argument(\"[directory]\")\n\t.option(\"--name <name>\", \"The name of your mod\")\n\t.option(\"--template <template>\", \"The template to use\")\n\t.option(\n\t\t\"--on-exists <action>\",\n\t\t`Either \"overwrite\", \"cancel\", or \"ignore\" as to what to do if files exist in the directory`,\n\t)\n\t.option(\"--no-install-dependencies\", `Prevents install of dependencies`)\n\t.description(\"Create a new bf6 mod\")\n\t.action(async (directory, options) => {\n\t\tconst { name, template, onExists, installDependencies } = options;\n\t\tif (\n\t\t\tonExists !== undefined &&\n\t\t\t![\"overwrite\", \"cancel\", \"ignore\"].includes(onExists)\n\t\t) {\n\t\t\tprintToConsole(\n\t\t\t\t`${colors.red.bold(\"✗\")} Invalid value for --on-exists: \"${onExists}\". Expected one of \"overwrite\", \"cancel\", or \"ignore\".`,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (template !== undefined && !templates.includes(template)) {\n\t\t\tprintToConsole(\n\t\t\t\t`${colors.red.bold(\"✗\")} Invalid value for --template: \"${template}\". Expected one of ${readableList(templates)}.`,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tawait init(directory, {\n\t\t\tname,\n\t\t\ttemplate,\n\t\t\tonExists,\n\t\t\tinstallDependencies,\n\t\t});\n\t});\n\nprogram\n\t.command(\"build\")\n\t.description(\"build the bf6 mod\")\n\t.action(async () => {\n\t\tawait build();\n\t});\n\nprogram\n\t.command(\"prepare\")\n\t.description(\"prepare the types for bf6 mod\")\n\t.action(async () => {\n\t\tawait prepare();\n\t});\n\nprogram\n\t.command(\"dev\")\n\t.description(\"watch the changes in src, and recompile as needed\")\n\t.action(async () => {\n\t\tawait dev();\n\t});\n\nprogram\n\t.command(\"import\")\n\t.argument(\"<input>\")\n\t.argument(\"<output>\")\n\t.option(\"--no-install-dependencies\", `Prevents install of dependencies`)\n\t.description(\"decompiles the json config of a mod into a new project\")\n\t.action(async (input, output, options) => {\n\t\tconst { installDependencies } = options;\n\t\tawait importFile(input as string, output as string, undefined, true);\n\t\tif (installDependencies) installDependencies(output);\n\t});\n\nprogram\n\t.command(\"log\")\n\t.argument(\"[input]\")\n\t.description(\"logs the output from a locally running server\")\n\t.action(async (input) => {\n\t\tconst logger = new Bf6Logger(input);\n\t\tlogger.start();\n\t});\n\nprogram.exitOverride((_err) => {\n\tif (process.env.EXIT_CODE === \"none\") process.exit(0);\n});\n\nif (!process.argv.slice(2).length) {\n\tprogram.outputHelp();\n\tprocess.exit(0);\n}\n\nprogram.parse();\n"],"mappings":"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"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"baseUrl": "..",
|
|
4
|
-
"paths": {
|
|
4
|
+
"paths": {
|
|
5
|
+
"#imports": ["./.bf6/imports"],
|
|
6
|
+
"modlib": ["./node_modules/@bf6mods/sdk/dist/index.d.ts"]
|
|
7
|
+
},
|
|
5
8
|
"target": "ES2021",
|
|
6
9
|
"module": "NodeNext",
|
|
7
10
|
"moduleResolution": "nodenext",
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{name}",
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "{name}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"build": "bf6mods build",
|
|
6
|
+
"dev": "bf6mods dev",
|
|
7
|
+
"log": "bf6mods log",
|
|
8
|
+
"postinstall": "bf6mods prepare",
|
|
9
|
+
"typecheck": "tsc --noEmit"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"typescript": "^5.9.3"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Do not touch unless you know what you are doing
|
|
3
|
-
"extends": "./.bf6/tsconfig.json",
|
|
4
|
-
}
|
|
1
|
+
{
|
|
2
|
+
// Do not touch unless you know what you are doing
|
|
3
|
+
"extends": "./.bf6/tsconfig.json",
|
|
4
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export default defineBf6Config({
|
|
2
|
-
outDir: 'dist',
|
|
3
|
-
entrypoint: 'src/index.ts',
|
|
4
|
-
name: '{bf6ConfigName}',
|
|
5
|
-
description: 'A basic example',
|
|
6
|
-
|
|
7
|
-
}
|
|
1
|
+
export default defineBf6Config({
|
|
2
|
+
outDir: 'dist',
|
|
3
|
+
entrypoint: 'src/index.ts',
|
|
4
|
+
name: '{bf6ConfigName}',
|
|
5
|
+
description: 'A basic example',
|
|
6
|
+
scenes: [MapId.EmpireState],
|
|
7
|
+
game: {},
|
|
8
|
+
})
|
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
Welcome to basic templates!
|
|
4
|
-
|
|
5
|
-
Here you can find some of the commonly used events and functions that'll hopefully kickstart your scripting journey.
|
|
6
|
-
'mod/index.d.ts' is your bestfriend when it comes to learning about all that is capable within Portal scripting.
|
|
7
|
-
If you're ever unsure how to do something, try searching for a relevant function in it!
|
|
8
|
-
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
////////////////////////////////// ON PLAYER EVENTS ///////////////////////////////////////
|
|
12
|
-
//////////// Useful player action-related events to hook up reactive logics to ////////////
|
|
13
|
-
|
|
14
|
-
// Triggered when player joins the game. Useful for pregame setup, team management, etc.
|
|
15
|
-
export function OnPlayerJoinGame(eventPlayer: mod.Player): void {}
|
|
16
|
-
|
|
17
|
-
// Triggered when player leaves the game. Useful for clean up logic, team management, etc.
|
|
18
|
-
export function OnPlayerLeaveGame(eventNumber: number): void {}
|
|
19
|
-
|
|
20
|
-
// Triggered when player selects their class and deploys into game. Useful for any spawn/start logic.
|
|
21
|
-
export function OnPlayerDeployed(eventPlayer: mod.Player): void {}
|
|
22
|
-
|
|
23
|
-
// Triggered on player death/kill, returns dying player, the killer, etc. Useful for updating scores, updating progression, handling any death/kill related logic.
|
|
24
|
-
export function OnPlayerDied(eventPlayer: mod.Player, eventOtherPlayer: mod.Player, eventDeathType: mod.DeathType, eventWeaponUnlock: mod.WeaponUnlock): void {}
|
|
25
|
-
|
|
26
|
-
export function OnPlayerEarnedKill(
|
|
27
|
-
eventPlayer: mod.Player,
|
|
28
|
-
eventOtherPlayer: mod.Player,
|
|
29
|
-
eventDeathType: mod.DeathType,
|
|
30
|
-
eventWeaponUnlock: mod.WeaponUnlock
|
|
31
|
-
): void {}
|
|
32
|
-
|
|
33
|
-
// Triggered when a player is damaged, returns same variables as OnPlayerDied. Useful for custom on damage logic and updating custom UI.
|
|
34
|
-
export function OnPlayerDamaged(
|
|
35
|
-
eventPlayer: mod.Player,
|
|
36
|
-
eventOtherPlayer: mod.Player,
|
|
37
|
-
eventDamageType: mod.DamageType,
|
|
38
|
-
eventWeaponUnlock: mod.WeaponUnlock
|
|
39
|
-
): void {}
|
|
40
|
-
|
|
41
|
-
// Triggered when a player interacts with InteractPoint. Reference by using 'mod.GetObjId(InteractPoint);'.
|
|
42
|
-
// Useful for any custom logic on player interaction such as updating check point, open custom UI, etc.
|
|
43
|
-
// Note that InteractPoint has to be placed in Godot scene and assigned an ObjId for reference.
|
|
44
|
-
export function OnPlayerInteract(eventPlayer: mod.Player, eventInteractPoint: mod.InteractPoint): void {}
|
|
45
|
-
|
|
46
|
-
// Triggered when a player enters/leaves referenced BF6 capture point. Useful for tracking capture point activities and overlapping players.
|
|
47
|
-
// Note that CapturePoint has to be placed in Godot scene, assigned an ObjId and a CapturePointArea(volume).
|
|
48
|
-
export function OnPlayerEnterCapturePoint(eventPlayer: mod.Player, eventCapturePoint: mod.CapturePoint): void {}
|
|
49
|
-
export function OnPlayerExitCapturePoint(eventPlayer: mod.Player, eventCapturePoint: mod.CapturePoint): void {}
|
|
50
|
-
|
|
51
|
-
// Triggered when a player enters/leaves referenced AreaTrigger volume. Useful for creating custom OnOverlap logic, creating custom capture point, etc.
|
|
52
|
-
// Note that AreaTrigger has to be placed in Godot scene, assigned an ObjId and a CollisionPolygon3D(volume).
|
|
53
|
-
export function OnPlayerEnterAreaTrigger(eventPlayer: mod.Player, eventAreaTrigger: mod.AreaTrigger): void {}
|
|
54
|
-
export function OnPlayerExitAreaTrigger(eventPlayer: mod.Player, eventAreaTrigger: mod.AreaTrigger): void {}
|
|
55
|
-
|
|
56
|
-
/////////////////////// GAMEMODE EVENTS AND USEFUL FUNCTIONS //////////////////////////////
|
|
57
|
-
////////// Various useful events and functions to manipulate gameplay and actors //////////
|
|
58
|
-
|
|
59
|
-
export function OnGameModeEnding(): void {}
|
|
60
|
-
|
|
61
|
-
export function OngoingGlobal(): void {}
|
|
62
|
-
|
|
63
|
-
// Triggered on main gamemode start/end. Useful for game start setup and cleanup.
|
|
64
|
-
export async function OnGameModeStarted() {
|
|
65
|
-
|
|
66
|
-
// Enables or disables a headquater. Note that HQ_PlayerSpawner has to be placed in Godot scene, assigned an ObjId and a HQArea(CollisionPolygon3D).
|
|
67
|
-
const hq = mod.GetHQ(0);
|
|
68
|
-
mod.EnableHQ(hq, true);
|
|
69
|
-
|
|
70
|
-
// Enables or disables the provided objective.
|
|
71
|
-
const capturePoint = mod.GetCapturePoint(0);
|
|
72
|
-
mod.EnableGameModeObjective(capturePoint, true);
|
|
73
|
-
|
|
74
|
-
// Returns the id corresponding to the provided object.
|
|
75
|
-
const capturePointId = mod.GetObjId(capturePoint);
|
|
76
|
-
|
|
77
|
-
// Returns a vector composed of three provided 'X' (left), 'Y' (up), and 'Z' (forward) values.
|
|
78
|
-
// Useful for specifying transform, 3d velocity or RGB color.
|
|
79
|
-
const vector = mod.CreateVector(1, 2, 3);
|
|
80
|
-
|
|
81
|
-
// Get player closest to a point
|
|
82
|
-
const player = mod.ClosestPlayerTo(vector);
|
|
83
|
-
|
|
84
|
-
// Returns the team value of the specified player OR the corresponding team of the provided number.
|
|
85
|
-
const teamOfPlayer = mod.GetTeam(player);
|
|
86
|
-
const teamObject = mod.GetTeam(0);
|
|
87
|
-
|
|
88
|
-
// Displays a notification-type Message on the top-right of the screen for 6 seconds. Useful for communicating game state/info or debugging.
|
|
89
|
-
const exampleMessage = mod.Message('example');
|
|
90
|
-
mod.DisplayNotificationMessage(exampleMessage);
|
|
91
|
-
mod.DisplayNotificationMessage(exampleMessage, player);
|
|
92
|
-
mod.DisplayNotificationMessage(exampleMessage, teamOfPlayer);
|
|
93
|
-
|
|
94
|
-
// Adds X delay in seconds. Useful for making sure that everything has been initialized before running logic or delaying triggers.
|
|
95
|
-
await mod.Wait(5);
|
|
96
|
-
|
|
97
|
-
// Teleports a target to a provided valid position facing a specified angle (in radians).
|
|
98
|
-
mod.Teleport(player, mod.CreateVector(100, 0, 100), mod.Pi());
|
|
99
|
-
|
|
100
|
-
// Returns the 'X', 'Y', or 'Z' component of a provided vector.
|
|
101
|
-
// Useful for modifying specific vector component or debugging transform.
|
|
102
|
-
const x = mod.XComponentOf(vector);
|
|
103
|
-
const y = mod.YComponentOf(vector);
|
|
104
|
-
const z = mod.ZComponentOf(vector);
|
|
105
|
-
const changedVector = mod.CreateVector(x + 10, y - 5, z * 2);
|
|
106
|
-
|
|
107
|
-
// Returns various player state information
|
|
108
|
-
const eyePosition = mod.GetSoldierState(player, mod.SoldierStateVector.EyePosition);
|
|
109
|
-
const facingDirection = mod.GetSoldierState(player, mod.SoldierStateVector.GetFacingDirection);
|
|
110
|
-
const health = mod.GetSoldierState(player, mod.SoldierStateNumber.CurrentHealth);
|
|
111
|
-
const isInWater = mod.GetSoldierState(player, mod.SoldierStateBool.IsInWater);
|
|
112
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
|
|
3
|
+
Welcome to basic templates!
|
|
4
|
+
|
|
5
|
+
Here you can find some of the commonly used events and functions that'll hopefully kickstart your scripting journey.
|
|
6
|
+
'mod/index.d.ts' is your bestfriend when it comes to learning about all that is capable within Portal scripting.
|
|
7
|
+
If you're ever unsure how to do something, try searching for a relevant function in it!
|
|
8
|
+
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
////////////////////////////////// ON PLAYER EVENTS ///////////////////////////////////////
|
|
12
|
+
//////////// Useful player action-related events to hook up reactive logics to ////////////
|
|
13
|
+
|
|
14
|
+
// Triggered when player joins the game. Useful for pregame setup, team management, etc.
|
|
15
|
+
export function OnPlayerJoinGame(eventPlayer: mod.Player): void {}
|
|
16
|
+
|
|
17
|
+
// Triggered when player leaves the game. Useful for clean up logic, team management, etc.
|
|
18
|
+
export function OnPlayerLeaveGame(eventNumber: number): void {}
|
|
19
|
+
|
|
20
|
+
// Triggered when player selects their class and deploys into game. Useful for any spawn/start logic.
|
|
21
|
+
export function OnPlayerDeployed(eventPlayer: mod.Player): void {}
|
|
22
|
+
|
|
23
|
+
// Triggered on player death/kill, returns dying player, the killer, etc. Useful for updating scores, updating progression, handling any death/kill related logic.
|
|
24
|
+
export function OnPlayerDied(eventPlayer: mod.Player, eventOtherPlayer: mod.Player, eventDeathType: mod.DeathType, eventWeaponUnlock: mod.WeaponUnlock): void {}
|
|
25
|
+
|
|
26
|
+
export function OnPlayerEarnedKill(
|
|
27
|
+
eventPlayer: mod.Player,
|
|
28
|
+
eventOtherPlayer: mod.Player,
|
|
29
|
+
eventDeathType: mod.DeathType,
|
|
30
|
+
eventWeaponUnlock: mod.WeaponUnlock
|
|
31
|
+
): void {}
|
|
32
|
+
|
|
33
|
+
// Triggered when a player is damaged, returns same variables as OnPlayerDied. Useful for custom on damage logic and updating custom UI.
|
|
34
|
+
export function OnPlayerDamaged(
|
|
35
|
+
eventPlayer: mod.Player,
|
|
36
|
+
eventOtherPlayer: mod.Player,
|
|
37
|
+
eventDamageType: mod.DamageType,
|
|
38
|
+
eventWeaponUnlock: mod.WeaponUnlock
|
|
39
|
+
): void {}
|
|
40
|
+
|
|
41
|
+
// Triggered when a player interacts with InteractPoint. Reference by using 'mod.GetObjId(InteractPoint);'.
|
|
42
|
+
// Useful for any custom logic on player interaction such as updating check point, open custom UI, etc.
|
|
43
|
+
// Note that InteractPoint has to be placed in Godot scene and assigned an ObjId for reference.
|
|
44
|
+
export function OnPlayerInteract(eventPlayer: mod.Player, eventInteractPoint: mod.InteractPoint): void {}
|
|
45
|
+
|
|
46
|
+
// Triggered when a player enters/leaves referenced BF6 capture point. Useful for tracking capture point activities and overlapping players.
|
|
47
|
+
// Note that CapturePoint has to be placed in Godot scene, assigned an ObjId and a CapturePointArea(volume).
|
|
48
|
+
export function OnPlayerEnterCapturePoint(eventPlayer: mod.Player, eventCapturePoint: mod.CapturePoint): void {}
|
|
49
|
+
export function OnPlayerExitCapturePoint(eventPlayer: mod.Player, eventCapturePoint: mod.CapturePoint): void {}
|
|
50
|
+
|
|
51
|
+
// Triggered when a player enters/leaves referenced AreaTrigger volume. Useful for creating custom OnOverlap logic, creating custom capture point, etc.
|
|
52
|
+
// Note that AreaTrigger has to be placed in Godot scene, assigned an ObjId and a CollisionPolygon3D(volume).
|
|
53
|
+
export function OnPlayerEnterAreaTrigger(eventPlayer: mod.Player, eventAreaTrigger: mod.AreaTrigger): void {}
|
|
54
|
+
export function OnPlayerExitAreaTrigger(eventPlayer: mod.Player, eventAreaTrigger: mod.AreaTrigger): void {}
|
|
55
|
+
|
|
56
|
+
/////////////////////// GAMEMODE EVENTS AND USEFUL FUNCTIONS //////////////////////////////
|
|
57
|
+
////////// Various useful events and functions to manipulate gameplay and actors //////////
|
|
58
|
+
|
|
59
|
+
export function OnGameModeEnding(): void {}
|
|
60
|
+
|
|
61
|
+
export function OngoingGlobal(): void {}
|
|
62
|
+
|
|
63
|
+
// Triggered on main gamemode start/end. Useful for game start setup and cleanup.
|
|
64
|
+
export async function OnGameModeStarted() {
|
|
65
|
+
|
|
66
|
+
// Enables or disables a headquater. Note that HQ_PlayerSpawner has to be placed in Godot scene, assigned an ObjId and a HQArea(CollisionPolygon3D).
|
|
67
|
+
const hq = mod.GetHQ(0);
|
|
68
|
+
mod.EnableHQ(hq, true);
|
|
69
|
+
|
|
70
|
+
// Enables or disables the provided objective.
|
|
71
|
+
const capturePoint = mod.GetCapturePoint(0);
|
|
72
|
+
mod.EnableGameModeObjective(capturePoint, true);
|
|
73
|
+
|
|
74
|
+
// Returns the id corresponding to the provided object.
|
|
75
|
+
const capturePointId = mod.GetObjId(capturePoint);
|
|
76
|
+
|
|
77
|
+
// Returns a vector composed of three provided 'X' (left), 'Y' (up), and 'Z' (forward) values.
|
|
78
|
+
// Useful for specifying transform, 3d velocity or RGB color.
|
|
79
|
+
const vector = mod.CreateVector(1, 2, 3);
|
|
80
|
+
|
|
81
|
+
// Get player closest to a point
|
|
82
|
+
const player = mod.ClosestPlayerTo(vector);
|
|
83
|
+
|
|
84
|
+
// Returns the team value of the specified player OR the corresponding team of the provided number.
|
|
85
|
+
const teamOfPlayer = mod.GetTeam(player);
|
|
86
|
+
const teamObject = mod.GetTeam(0);
|
|
87
|
+
|
|
88
|
+
// Displays a notification-type Message on the top-right of the screen for 6 seconds. Useful for communicating game state/info or debugging.
|
|
89
|
+
const exampleMessage = mod.Message('example');
|
|
90
|
+
mod.DisplayNotificationMessage(exampleMessage);
|
|
91
|
+
mod.DisplayNotificationMessage(exampleMessage, player);
|
|
92
|
+
mod.DisplayNotificationMessage(exampleMessage, teamOfPlayer);
|
|
93
|
+
|
|
94
|
+
// Adds X delay in seconds. Useful for making sure that everything has been initialized before running logic or delaying triggers.
|
|
95
|
+
await mod.Wait(5);
|
|
96
|
+
|
|
97
|
+
// Teleports a target to a provided valid position facing a specified angle (in radians).
|
|
98
|
+
mod.Teleport(player, mod.CreateVector(100, 0, 100), mod.Pi());
|
|
99
|
+
|
|
100
|
+
// Returns the 'X', 'Y', or 'Z' component of a provided vector.
|
|
101
|
+
// Useful for modifying specific vector component or debugging transform.
|
|
102
|
+
const x = mod.XComponentOf(vector);
|
|
103
|
+
const y = mod.YComponentOf(vector);
|
|
104
|
+
const z = mod.ZComponentOf(vector);
|
|
105
|
+
const changedVector = mod.CreateVector(x + 10, y - 5, z * 2);
|
|
106
|
+
|
|
107
|
+
// Returns various player state information
|
|
108
|
+
const eyePosition = mod.GetSoldierState(player, mod.SoldierStateVector.EyePosition);
|
|
109
|
+
const facingDirection = mod.GetSoldierState(player, mod.SoldierStateVector.GetFacingDirection);
|
|
110
|
+
const health = mod.GetSoldierState(player, mod.SoldierStateNumber.CurrentHealth);
|
|
111
|
+
const isInWater = mod.GetSoldierState(player, mod.SoldierStateBool.IsInWater);
|
|
112
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bf6mods/cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "CLI and library for bundling BF6 mods",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"@oxc-project/types": "^0.95.0",
|
|
46
46
|
"@types/stringify-object": "^4.0.5",
|
|
47
47
|
"cross-env": "^10.1.0",
|
|
48
|
+
"execa": "^9.6.0",
|
|
48
49
|
"tsx": "^4.20.6"
|
|
49
50
|
}
|
|
50
51
|
}
|