@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 +1 -1
- package/dist/es/cli.js +39 -0
- package/dist/es/cli.js.map +1 -0
- package/dist/es/commands/build.js +220 -0
- package/dist/es/commands/build.js.map +1 -0
- package/dist/{esm/index.mjs → es/commands/deploy.js} +24 -628
- package/dist/es/commands/deploy.js.map +1 -0
- package/dist/es/index.js +10 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/INetworkConfig.js +2 -0
- package/dist/es/models/INetworkConfig.js.map +1 -0
- package/dist/es/utils/buildArtifactUtils.js +28 -0
- package/dist/es/utils/buildArtifactUtils.js.map +1 -0
- package/dist/es/utils/envSetup.js +56 -0
- package/dist/es/utils/envSetup.js.map +1 -0
- package/dist/es/utils/environmentUtils.js +239 -0
- package/dist/es/utils/environmentUtils.js.map +1 -0
- package/dist/es/utils/iotaUtils.js +19 -0
- package/dist/es/utils/iotaUtils.js.map +1 -0
- package/dist/es/utils/moveToJsonUtils.js +27 -0
- package/dist/es/utils/moveToJsonUtils.js.map +1 -0
- package/dist/locales/en.json +39 -90
- package/dist/types/index.d.ts +7 -7
- package/docs/changelog.md +118 -0
- package/docs/reference/functions/getDeploymentSeed.md +2 -2
- package/locales/en.json +11 -51
- package/package.json +21 -11
- package/dist/cjs/index.cjs +0 -1481
package/bin/index.js
CHANGED
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"]}
|