@twin.org/move-to-json 0.0.2-next.9 → 0.0.3-next.3

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/bin/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // Copyright 2024 IOTA Stiftung.
3
3
  // SPDX-License-Identifier: Apache-2.0.
4
- import { CLI } from '../dist/esm/index.mjs';
4
+ import { CLI } from '../dist/es/index.js';
5
5
 
6
6
  const cli = new CLI();
7
7
  const result = await cli.run(process.argv);
package/dist/es/cli.js ADDED
@@ -0,0 +1,39 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { CLIBase } from "@twin.org/cli-core";
6
+ import { buildCommandBuild } from "./commands/build.js";
7
+ import { buildCommandDeploy } from "./commands/deploy.js";
8
+ /**
9
+ * The main entry point for the Move to JSON CLI.
10
+ */
11
+ export class CLI extends CLIBase {
12
+ /**
13
+ * Run the app.
14
+ * @param argv The process arguments.
15
+ * @param localesDirectory The directory for the locales, default to relative to the script.
16
+ * @param options Additional options.
17
+ * @param options.overrideOutputWidth Override the output width.
18
+ * @returns The exit code.
19
+ */
20
+ async run(argv, localesDirectory, options) {
21
+ return this.execute({
22
+ title: "TWIN Move to JSON",
23
+ appName: "move-to-json",
24
+ version: "0.0.3-next.3", // x-release-please-version
25
+ icon: "⚙️ ",
26
+ supportsEnvFiles: true,
27
+ overrideOutputWidth: options?.overrideOutputWidth
28
+ }, localesDirectory ?? path.join(path.dirname(fileURLToPath(import.meta.url)), "../locales"), argv);
29
+ }
30
+ /**
31
+ * Configure any options or actions at the root program level.
32
+ * @param program The root program command.
33
+ */
34
+ configureRoot(program) {
35
+ buildCommandBuild(program);
36
+ buildCommandDeploy(program);
37
+ }
38
+ }
39
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,MAAM,OAAO,GAAI,SAAQ,OAAO;IAC/B;;;;;;;OAOG;IACI,KAAK,CAAC,GAAG,CACf,IAAc,EACd,gBAAyB,EACzB,OAEC;QAED,OAAO,IAAI,CAAC,OAAO,CAClB;YACC,KAAK,EAAE,mBAAmB;YAC1B,OAAO,EAAE,cAAc;YACvB,OAAO,EAAE,cAAc,EAAE,2BAA2B;YACpD,IAAI,EAAE,KAAK;YACX,gBAAgB,EAAE,IAAI;YACtB,mBAAmB,EAAE,OAAO,EAAE,mBAAmB;SACjD,EACD,gBAAgB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,EACzF,IAAI,CACJ,CAAC;IACH,CAAC;IAED;;;OAGG;IACO,aAAa,CAAC,OAAgB;QACvC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC3B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACD","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { CLIBase } from \"@twin.org/cli-core\";\nimport type { Command } from \"commander\";\nimport { buildCommandBuild } from \"./commands/build.js\";\nimport { buildCommandDeploy } from \"./commands/deploy.js\";\n\n/**\n * The main entry point for the Move to JSON CLI.\n */\nexport class CLI extends CLIBase {\n\t/**\n\t * Run the app.\n\t * @param argv The process arguments.\n\t * @param localesDirectory The directory for the locales, default to relative to the script.\n\t * @param options Additional options.\n\t * @param options.overrideOutputWidth Override the output width.\n\t * @returns The exit code.\n\t */\n\tpublic async run(\n\t\targv: string[],\n\t\tlocalesDirectory?: string,\n\t\toptions?: {\n\t\t\toverrideOutputWidth?: number;\n\t\t}\n\t): Promise<number> {\n\t\treturn this.execute(\n\t\t\t{\n\t\t\t\ttitle: \"TWIN Move to JSON\",\n\t\t\t\tappName: \"move-to-json\",\n\t\t\t\tversion: \"0.0.3-next.3\", // x-release-please-version\n\t\t\t\ticon: \"⚙️ \",\n\t\t\t\tsupportsEnvFiles: true,\n\t\t\t\toverrideOutputWidth: options?.overrideOutputWidth\n\t\t\t},\n\t\t\tlocalesDirectory ?? path.join(path.dirname(fileURLToPath(import.meta.url)), \"../locales\"),\n\t\t\targv\n\t\t);\n\t}\n\n\t/**\n\t * Configure any options or actions at the root program level.\n\t * @param program The root program command.\n\t */\n\tprotected configureRoot(program: Command): void {\n\t\tbuildCommandBuild(program);\n\t\tbuildCommandDeploy(program);\n\t}\n}\n"]}
@@ -0,0 +1,220 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { promises as fsPromises } from "node:fs";
4
+ import path from "node:path";
5
+ import { CLIDisplay, CLIParam, CLIUtils } from "@twin.org/cli-core";
6
+ import { Converter, GeneralError, Guards, I18n, Is, StringHelper } from "@twin.org/core";
7
+ import { Sha3 } from "@twin.org/crypto";
8
+ import { NetworkTypes } from "@twin.org/dlt-iota";
9
+ import FastGlob from "fast-glob";
10
+ import { verifyIotaSDK } from "../utils/iotaUtils.js";
11
+ import { searchDirectoryForMoveToml } from "../utils/moveToJsonUtils.js";
12
+ /**
13
+ * Build the build command to be consumed by the CLI.
14
+ * @param program The command to build on.
15
+ */
16
+ export function buildCommandBuild(program) {
17
+ program
18
+ .command("build")
19
+ .description(I18n.formatMessage("commands.build.description"))
20
+ .argument("<inputGlob>", I18n.formatMessage("commands.build.options.inputGlob.description"))
21
+ .option(I18n.formatMessage("commands.build.options.network.param"), I18n.formatMessage("commands.build.options.network.description"), "!NETWORK")
22
+ .option(I18n.formatMessage("commands.build.options.output.param"), I18n.formatMessage("commands.build.options.output.description"), "smart-contract-deployments.json")
23
+ .action(actionCommandBuild);
24
+ }
25
+ /**
26
+ * Action for the build command.
27
+ * @param inputGlob A glob pattern that matches one or more Move files
28
+ * @param opts Additional options.
29
+ * @param opts.network Target network (testnet/devnet/mainnet).
30
+ * @param opts.output Where we store the final compiled modules.
31
+ */
32
+ export async function actionCommandBuild(inputGlob, opts) {
33
+ try {
34
+ const networkRaw = CLIParam.stringValue("network", opts.network);
35
+ const network = networkRaw;
36
+ Guards.arrayOneOf("commands", "network", network, Object.values(NetworkTypes));
37
+ // Verify the IOTA SDK before we do anything else
38
+ await verifyIotaSDK();
39
+ const { normalizedGlob, normalizedOutput, executionDir } = normalizePathsAndWorkingDir(inputGlob, opts.output ?? "smart-contract-deployments.json");
40
+ CLIDisplay.section(I18n.formatMessage("commands.build.section.buildingMoveContracts", {
41
+ network
42
+ }));
43
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.inputGlob"), inputGlob);
44
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.outputJson"), normalizedOutput);
45
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.network"), network);
46
+ CLIDisplay.break();
47
+ // Find matching .move files
48
+ CLIDisplay.task(I18n.formatMessage("commands.build.progress.searchingFiles"));
49
+ const matchedFiles = await FastGlob([normalizedGlob, "!**/build/**/*.move", "!**/dependencies/**/*.move"], {
50
+ cwd: executionDir,
51
+ absolute: true,
52
+ dot: true,
53
+ followSymbolicLinks: false,
54
+ caseSensitiveMatch: false, // Important for Windows
55
+ onlyFiles: true,
56
+ stats: false
57
+ });
58
+ if (matchedFiles.length === 0) {
59
+ CLIDisplay.value(I18n.formatMessage("commands.build.warnings.noMoveFilesFound", { inputGlob }), "", 2);
60
+ }
61
+ else if (matchedFiles.length > 1) {
62
+ throw new GeneralError("commands", "commands.build.multipleFilesNotSupported", {
63
+ fileCount: matchedFiles.length,
64
+ fileList: matchedFiles.map(f => path.basename(f)).join(", ")
65
+ });
66
+ }
67
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.matchedFilesCount"), matchedFiles.length.toString());
68
+ CLIDisplay.break();
69
+ // Prepare build environment
70
+ CLIDisplay.task(I18n.formatMessage("commands.build.progress.preparingBuildEnvironment"));
71
+ // Find all Move projects in the directory tree
72
+ const moveProjects = [];
73
+ await searchDirectoryForMoveToml(executionDir, moveProjects);
74
+ for (const projectRoot of moveProjects) {
75
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.preparedProject"), projectRoot, 1);
76
+ }
77
+ const existingJson = await CLIUtils.readJsonFile(normalizedOutput);
78
+ const finalJson = existingJson ?? {};
79
+ if (existingJson) {
80
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.mergingWithExistingJson"), normalizedOutput);
81
+ }
82
+ else {
83
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.noExistingJsonFound"), I18n.formatMessage("commands.build.labels.creatingNewJsonStructure"), 1);
84
+ }
85
+ CLIDisplay.break();
86
+ // Process the single Move file (validation ensures exactly 0 or 1 file)
87
+ if (matchedFiles.length === 1) {
88
+ const moveFile = matchedFiles[0];
89
+ CLIDisplay.task(I18n.formatMessage("commands.build.progress.processingMoveFile"), moveFile);
90
+ try {
91
+ const compiled = await processMoveFile(moveFile);
92
+ if (compiled) {
93
+ const { contractName, packageId, packageBytecode } = compiled;
94
+ const contractData = finalJson[network] ?? {
95
+ packageId: "",
96
+ packageBytecode: ""
97
+ };
98
+ // If the packageId changed that means the bytecode changed
99
+ // since the packageId is a hash of the bytecode.
100
+ if (packageId !== contractData.packageId) {
101
+ contractData.packageId = packageId; // Update packageId to new value
102
+ contractData.packageBytecode = packageBytecode; // Update bytecode
103
+ // If deployedPackageId exists, it means this contract was previously deployed.
104
+ // so we need to clear it to signal that a redeployment (upgrade) is needed.
105
+ // but also capture it so that we can perform upgrades
106
+ if (Is.stringValue(contractData.deployedPackageId)) {
107
+ contractData.lastDeployedPackageId = contractData.deployedPackageId;
108
+ // Clear deployedPackageId when bytecode changed, preserve upgrade metadata
109
+ delete contractData.deployedPackageId; // Clear to signal upgrade needed
110
+ }
111
+ finalJson[network] = contractData;
112
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.bytecodeChanged"), I18n.formatMessage("commands.build.labels.deployedPackageIdCleared"), 2);
113
+ }
114
+ else {
115
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.bytecodeUnchanged"), I18n.formatMessage("commands.build.labels.deployedPackageIdPreserved"), 2);
116
+ }
117
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.updatedNetworkPackage", { network }), contractName, 2);
118
+ }
119
+ }
120
+ catch (err) {
121
+ throw new GeneralError("commands", "commands.build.contractProcessingFailed", { file: moveFile }, err);
122
+ }
123
+ CLIDisplay.break();
124
+ }
125
+ CLIDisplay.task(I18n.formatMessage("commands.build.progress.writingJsonFile"));
126
+ await CLIUtils.writeJsonFile(normalizedOutput, finalJson, false);
127
+ CLIDisplay.break();
128
+ CLIDisplay.done();
129
+ }
130
+ catch (err) {
131
+ CLIDisplay.error(err);
132
+ throw err;
133
+ }
134
+ }
135
+ /**
136
+ * Process a single Move file by compiling it, computing the packageId, and base64-encoding the .mv modules.
137
+ * @param moveFile The path to a single Move source file.
138
+ * @returns The compiled results or undefined if no modules found.
139
+ */
140
+ async function processMoveFile(moveFile) {
141
+ // The contract name is based on the .move file's base name in kebab-case
142
+ const { name: baseName } = path.parse(moveFile);
143
+ const contractName = StringHelper.kebabCase(baseName);
144
+ // Find the "project root" (the directory containing Move.toml).
145
+ const projectRoot = getProjectRoot(moveFile);
146
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.contractName"), contractName, 1);
147
+ // Compile the contract
148
+ try {
149
+ const cliArgs = ["move", "build"];
150
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.compileCommand"), `iota ${cliArgs.join(" ")}`, 1);
151
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.workingDirectory"), projectRoot, 1);
152
+ await CLIUtils.runShellApp("iota", cliArgs, projectRoot);
153
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.compileResult"), I18n.formatMessage("commands.build.labels.buildCompleted"), 1);
154
+ }
155
+ catch (error) {
156
+ throw new GeneralError("commands", "commands.build.buildFailed", { file: moveFile }, error);
157
+ }
158
+ // Get the bytecode modules
159
+ const buildFolderName = StringHelper.snakeCase(baseName);
160
+ const bytecodeModulesPath = path.join(projectRoot, "build", buildFolderName, "bytecode_modules");
161
+ try {
162
+ await fsPromises.access(bytecodeModulesPath);
163
+ }
164
+ catch {
165
+ CLIDisplay.value(I18n.formatMessage("commands.build.warnings.noBytecodeModulesFolder", { contractName }), "", 2);
166
+ return;
167
+ }
168
+ const moduleFiles = await fsPromises.readdir(bytecodeModulesPath);
169
+ const mvFiles = moduleFiles.filter(f => f.endsWith(".mv"));
170
+ if (mvFiles.length === 0) {
171
+ CLIDisplay.value(I18n.formatMessage("commands.build.warnings.noMvFilesFound", { contractName }), "", 2);
172
+ return;
173
+ }
174
+ // Compute the package ID
175
+ const modulesBytesForHash = [];
176
+ const modulesBase64 = [];
177
+ for (const file of mvFiles) {
178
+ const modulePath = path.join(bytecodeModulesPath, file);
179
+ const moduleBytes = await fsPromises.readFile(modulePath);
180
+ modulesBytesForHash.push(moduleBytes);
181
+ modulesBase64.push(Converter.bytesToBase64(moduleBytes));
182
+ }
183
+ const concatenated = Buffer.concat(modulesBytesForHash);
184
+ const computedPackageIdBytes = Sha3.sum256(concatenated);
185
+ const computedPackageId = `${Converter.bytesToHex(computedPackageIdBytes, true)}`;
186
+ CLIDisplay.value(I18n.formatMessage("commands.build.labels.computedPackageId"), computedPackageId, 2);
187
+ // If multiple modules, store them as an array
188
+ const packageData = modulesBase64.length === 1 ? modulesBase64[0] : modulesBase64;
189
+ return {
190
+ contractName,
191
+ packageId: computedPackageId,
192
+ packageBytecode: packageData
193
+ };
194
+ }
195
+ /**
196
+ * Get the project root directory from a Move file path.
197
+ * @param moveFilePath The path to a Move source file.
198
+ * @returns The project root directory.
199
+ */
200
+ function getProjectRoot(moveFilePath) {
201
+ return path.resolve(path.parse(moveFilePath).dir, "..");
202
+ }
203
+ /**
204
+ * Normalize paths and resolve working directory for cross-platform compatibility.
205
+ * @param inputGlob The input glob pattern
206
+ * @param outputJson The output JSON file path
207
+ * @returns Normalized paths and working directory
208
+ */
209
+ function normalizePathsAndWorkingDir(inputGlob, outputJson) {
210
+ const executionDir = process.cwd();
211
+ // Improved path normalization with StringHelper
212
+ const normalizedGlob = StringHelper.trimTrailingSlashes(path.resolve(inputGlob).replace(/\\/g, "/"));
213
+ const normalizedOutput = path.resolve(outputJson);
214
+ return {
215
+ normalizedGlob,
216
+ normalizedOutput,
217
+ executionDir
218
+ };
219
+ }
220
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/commands/build.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { promises as fsPromises } from \"node:fs\";\nimport path from \"node:path\";\nimport { CLIDisplay, CLIParam, CLIUtils } from \"@twin.org/cli-core\";\nimport { Converter, GeneralError, Guards, I18n, Is, StringHelper } from \"@twin.org/core\";\nimport { Sha3 } from \"@twin.org/crypto\";\nimport type { IContractData, ISmartContractDeployments } from \"@twin.org/dlt-iota\";\nimport { NetworkTypes } from \"@twin.org/dlt-iota\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { Command } from \"commander\";\nimport FastGlob from \"fast-glob\";\nimport { verifyIotaSDK } from \"../utils/iotaUtils.js\";\nimport { searchDirectoryForMoveToml } from \"../utils/moveToJsonUtils.js\";\n\n/**\n * Build the build command to be consumed by the CLI.\n * @param program The command to build on.\n */\nexport function buildCommandBuild(program: Command): void {\n\tprogram\n\t\t.command(\"build\")\n\t\t.description(I18n.formatMessage(\"commands.build.description\"))\n\t\t.argument(\"<inputGlob>\", I18n.formatMessage(\"commands.build.options.inputGlob.description\"))\n\t\t.option(\n\t\t\tI18n.formatMessage(\"commands.build.options.network.param\"),\n\t\t\tI18n.formatMessage(\"commands.build.options.network.description\"),\n\t\t\t\"!NETWORK\"\n\t\t)\n\t\t.option(\n\t\t\tI18n.formatMessage(\"commands.build.options.output.param\"),\n\t\t\tI18n.formatMessage(\"commands.build.options.output.description\"),\n\t\t\t\"smart-contract-deployments.json\"\n\t\t)\n\t\t.action(actionCommandBuild);\n}\n\n/**\n * Action for the build command.\n * @param inputGlob A glob pattern that matches one or more Move files\n * @param opts Additional options.\n * @param opts.network Target network (testnet/devnet/mainnet).\n * @param opts.output Where we store the final compiled modules.\n */\nexport async function actionCommandBuild(\n\tinputGlob: string,\n\topts: { network?: NetworkTypes; output?: string }\n): Promise<void> {\n\ttry {\n\t\tconst networkRaw = CLIParam.stringValue(\"network\", opts.network);\n\t\tconst network = networkRaw as NetworkTypes;\n\n\t\tGuards.arrayOneOf(\"commands\", nameof(network), network, Object.values(NetworkTypes));\n\n\t\t// Verify the IOTA SDK before we do anything else\n\t\tawait verifyIotaSDK();\n\n\t\tconst { normalizedGlob, normalizedOutput, executionDir } = normalizePathsAndWorkingDir(\n\t\t\tinputGlob,\n\t\t\topts.output ?? \"smart-contract-deployments.json\"\n\t\t);\n\n\t\tCLIDisplay.section(\n\t\t\tI18n.formatMessage(\"commands.build.section.buildingMoveContracts\", {\n\t\t\t\tnetwork\n\t\t\t})\n\t\t);\n\n\t\tCLIDisplay.value(I18n.formatMessage(\"commands.build.labels.inputGlob\"), inputGlob);\n\t\tCLIDisplay.value(I18n.formatMessage(\"commands.build.labels.outputJson\"), normalizedOutput);\n\t\tCLIDisplay.value(I18n.formatMessage(\"commands.build.labels.network\"), network);\n\t\tCLIDisplay.break();\n\n\t\t// Find matching .move files\n\t\tCLIDisplay.task(I18n.formatMessage(\"commands.build.progress.searchingFiles\"));\n\n\t\tconst matchedFiles = await FastGlob(\n\t\t\t[normalizedGlob, \"!**/build/**/*.move\", \"!**/dependencies/**/*.move\"],\n\t\t\t{\n\t\t\t\tcwd: executionDir,\n\t\t\t\tabsolute: true,\n\t\t\t\tdot: true,\n\t\t\t\tfollowSymbolicLinks: false,\n\t\t\t\tcaseSensitiveMatch: false, // Important for Windows\n\t\t\t\tonlyFiles: true,\n\t\t\t\tstats: false\n\t\t\t}\n\t\t);\n\n\t\tif (matchedFiles.length === 0) {\n\t\t\tCLIDisplay.value(\n\t\t\t\tI18n.formatMessage(\"commands.build.warnings.noMoveFilesFound\", { inputGlob }),\n\t\t\t\t\"\",\n\t\t\t\t2\n\t\t\t);\n\t\t} else if (matchedFiles.length > 1) {\n\t\t\tthrow new GeneralError(\"commands\", \"commands.build.multipleFilesNotSupported\", {\n\t\t\t\tfileCount: matchedFiles.length,\n\t\t\t\tfileList: matchedFiles.map(f => path.basename(f)).join(\", \")\n\t\t\t});\n\t\t}\n\t\tCLIDisplay.value(\n\t\t\tI18n.formatMessage(\"commands.build.labels.matchedFilesCount\"),\n\t\t\tmatchedFiles.length.toString()\n\t\t);\n\t\tCLIDisplay.break();\n\n\t\t// Prepare build environment\n\t\tCLIDisplay.task(I18n.formatMessage(\"commands.build.progress.preparingBuildEnvironment\"));\n\n\t\t// Find all Move projects in the directory tree\n\t\tconst moveProjects: string[] = [];\n\t\tawait searchDirectoryForMoveToml(executionDir, moveProjects);\n\n\t\tfor (const projectRoot of moveProjects) {\n\t\t\tCLIDisplay.value(I18n.formatMessage(\"commands.build.labels.preparedProject\"), projectRoot, 1);\n\t\t}\n\n\t\tconst existingJson = await CLIUtils.readJsonFile<ISmartContractDeployments>(normalizedOutput);\n\t\tconst finalJson: ISmartContractDeployments = existingJson ?? {};\n\n\t\tif (existingJson) {\n\t\t\tCLIDisplay.value(\n\t\t\t\tI18n.formatMessage(\"commands.build.labels.mergingWithExistingJson\"),\n\t\t\t\tnormalizedOutput\n\t\t\t);\n\t\t} else {\n\t\t\tCLIDisplay.value(\n\t\t\t\tI18n.formatMessage(\"commands.build.labels.noExistingJsonFound\"),\n\t\t\t\tI18n.formatMessage(\"commands.build.labels.creatingNewJsonStructure\"),\n\t\t\t\t1\n\t\t\t);\n\t\t}\n\n\t\tCLIDisplay.break();\n\n\t\t// Process the single Move file (validation ensures exactly 0 or 1 file)\n\t\tif (matchedFiles.length === 1) {\n\t\t\tconst moveFile = matchedFiles[0];\n\t\t\tCLIDisplay.task(I18n.formatMessage(\"commands.build.progress.processingMoveFile\"), moveFile);\n\n\t\t\ttry {\n\t\t\t\tconst compiled = await processMoveFile(moveFile);\n\t\t\t\tif (compiled) {\n\t\t\t\t\tconst { contractName, packageId, packageBytecode } = compiled;\n\n\t\t\t\t\tconst contractData: IContractData = finalJson[network] ?? {\n\t\t\t\t\t\tpackageId: \"\",\n\t\t\t\t\t\tpackageBytecode: \"\"\n\t\t\t\t\t};\n\n\t\t\t\t\t// If the packageId changed that means the bytecode changed\n\t\t\t\t\t// since the packageId is a hash of the bytecode.\n\t\t\t\t\tif (packageId !== contractData.packageId) {\n\t\t\t\t\t\tcontractData.packageId = packageId; // Update packageId to new value\n\t\t\t\t\t\tcontractData.packageBytecode = packageBytecode; // Update bytecode\n\n\t\t\t\t\t\t// If deployedPackageId exists, it means this contract was previously deployed.\n\t\t\t\t\t\t// so we need to clear it to signal that a redeployment (upgrade) is needed.\n\t\t\t\t\t\t// but also capture it so that we can perform upgrades\n\t\t\t\t\t\tif (Is.stringValue(contractData.deployedPackageId)) {\n\t\t\t\t\t\t\tcontractData.lastDeployedPackageId = contractData.deployedPackageId;\n\t\t\t\t\t\t\t// Clear deployedPackageId when bytecode changed, preserve upgrade metadata\n\t\t\t\t\t\t\tdelete contractData.deployedPackageId; // Clear to signal upgrade needed\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfinalJson[network] = contractData;\n\n\t\t\t\t\t\tCLIDisplay.value(\n\t\t\t\t\t\t\tI18n.formatMessage(\"commands.build.labels.bytecodeChanged\"),\n\t\t\t\t\t\t\tI18n.formatMessage(\"commands.build.labels.deployedPackageIdCleared\"),\n\t\t\t\t\t\t\t2\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tCLIDisplay.value(\n\t\t\t\t\t\t\tI18n.formatMessage(\"commands.build.labels.bytecodeUnchanged\"),\n\t\t\t\t\t\t\tI18n.formatMessage(\"commands.build.labels.deployedPackageIdPreserved\"),\n\t\t\t\t\t\t\t2\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tCLIDisplay.value(\n\t\t\t\t\t\tI18n.formatMessage(\"commands.build.labels.updatedNetworkPackage\", { network }),\n\t\t\t\t\t\tcontractName,\n\t\t\t\t\t\t2\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\t\"commands\",\n\t\t\t\t\t\"commands.build.contractProcessingFailed\",\n\t\t\t\t\t{ file: moveFile },\n\t\t\t\t\terr\n\t\t\t\t);\n\t\t\t}\n\t\t\tCLIDisplay.break();\n\t\t}\n\n\t\tCLIDisplay.task(I18n.formatMessage(\"commands.build.progress.writingJsonFile\"));\n\t\tawait CLIUtils.writeJsonFile(normalizedOutput, finalJson, false);\n\n\t\tCLIDisplay.break();\n\t\tCLIDisplay.done();\n\t} catch (err) {\n\t\tCLIDisplay.error(err);\n\t\tthrow err;\n\t}\n}\n\n/**\n * Process a single Move file by compiling it, computing the packageId, and base64-encoding the .mv modules.\n * @param moveFile The path to a single Move source file.\n * @returns The compiled results or undefined if no modules found.\n */\nasync function processMoveFile(moveFile: string): Promise<\n\t| {\n\t\t\tcontractName: string;\n\t\t\tpackageId: string;\n\t\t\tpackageBytecode: string | string[];\n\t }\n\t| undefined\n> {\n\t// The contract name is based on the .move file's base name in kebab-case\n\tconst { name: baseName } = path.parse(moveFile);\n\tconst contractName = StringHelper.kebabCase(baseName);\n\n\t// Find the \"project root\" (the directory containing Move.toml).\n\tconst projectRoot = getProjectRoot(moveFile);\n\n\tCLIDisplay.value(I18n.formatMessage(\"commands.build.labels.contractName\"), contractName, 1);\n\n\t// Compile the contract\n\ttry {\n\t\tconst cliArgs = [\"move\", \"build\"];\n\n\t\tCLIDisplay.value(\n\t\t\tI18n.formatMessage(\"commands.build.labels.compileCommand\"),\n\t\t\t`iota ${cliArgs.join(\" \")}`,\n\t\t\t1\n\t\t);\n\t\tCLIDisplay.value(I18n.formatMessage(\"commands.build.labels.workingDirectory\"), projectRoot, 1);\n\n\t\tawait CLIUtils.runShellApp(\"iota\", cliArgs, projectRoot);\n\t\tCLIDisplay.value(\n\t\t\tI18n.formatMessage(\"commands.build.labels.compileResult\"),\n\t\t\tI18n.formatMessage(\"commands.build.labels.buildCompleted\"),\n\t\t\t1\n\t\t);\n\t} catch (error) {\n\t\tthrow new GeneralError(\"commands\", \"commands.build.buildFailed\", { file: moveFile }, error);\n\t}\n\n\t// Get the bytecode modules\n\tconst buildFolderName = StringHelper.snakeCase(baseName);\n\tconst bytecodeModulesPath = path.join(projectRoot, \"build\", buildFolderName, \"bytecode_modules\");\n\n\ttry {\n\t\tawait fsPromises.access(bytecodeModulesPath);\n\t} catch {\n\t\tCLIDisplay.value(\n\t\t\tI18n.formatMessage(\"commands.build.warnings.noBytecodeModulesFolder\", { contractName }),\n\t\t\t\"\",\n\t\t\t2\n\t\t);\n\t\treturn;\n\t}\n\n\tconst moduleFiles = await fsPromises.readdir(bytecodeModulesPath);\n\tconst mvFiles = moduleFiles.filter(f => f.endsWith(\".mv\"));\n\tif (mvFiles.length === 0) {\n\t\tCLIDisplay.value(\n\t\t\tI18n.formatMessage(\"commands.build.warnings.noMvFilesFound\", { contractName }),\n\t\t\t\"\",\n\t\t\t2\n\t\t);\n\t\treturn;\n\t}\n\n\t// Compute the package ID\n\tconst modulesBytesForHash: Buffer[] = [];\n\tconst modulesBase64: string[] = [];\n\n\tfor (const file of mvFiles) {\n\t\tconst modulePath = path.join(bytecodeModulesPath, file);\n\t\tconst moduleBytes = await fsPromises.readFile(modulePath);\n\n\t\tmodulesBytesForHash.push(moduleBytes);\n\t\tmodulesBase64.push(Converter.bytesToBase64(moduleBytes));\n\t}\n\n\tconst concatenated = Buffer.concat(modulesBytesForHash);\n\tconst computedPackageIdBytes = Sha3.sum256(concatenated);\n\tconst computedPackageId = `${Converter.bytesToHex(computedPackageIdBytes, true)}`;\n\n\tCLIDisplay.value(\n\t\tI18n.formatMessage(\"commands.build.labels.computedPackageId\"),\n\t\tcomputedPackageId,\n\t\t2\n\t);\n\n\t// If multiple modules, store them as an array\n\tconst packageData = modulesBase64.length === 1 ? modulesBase64[0] : modulesBase64;\n\n\treturn {\n\t\tcontractName,\n\t\tpackageId: computedPackageId,\n\t\tpackageBytecode: packageData\n\t};\n}\n\n/**\n * Get the project root directory from a Move file path.\n * @param moveFilePath The path to a Move source file.\n * @returns The project root directory.\n */\nfunction getProjectRoot(moveFilePath: string): string {\n\treturn path.resolve(path.parse(moveFilePath).dir, \"..\");\n}\n\n/**\n * Normalize paths and resolve working directory for cross-platform compatibility.\n * @param inputGlob The input glob pattern\n * @param outputJson The output JSON file path\n * @returns Normalized paths and working directory\n */\nfunction normalizePathsAndWorkingDir(\n\tinputGlob: string,\n\toutputJson: string\n): {\n\tnormalizedGlob: string;\n\tnormalizedOutput: string;\n\texecutionDir: string;\n} {\n\tconst executionDir = process.cwd();\n\n\t// Improved path normalization with StringHelper\n\tconst normalizedGlob = StringHelper.trimTrailingSlashes(\n\t\tpath.resolve(inputGlob).replace(/\\\\/g, \"/\")\n\t);\n\tconst normalizedOutput = path.resolve(outputJson);\n\n\treturn {\n\t\tnormalizedGlob,\n\t\tnormalizedOutput,\n\t\texecutionDir\n\t};\n}\n"]}