@olenbetong/appframe-cli 4.4.10 → 4.5.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [987f8f8]
8
+ - Updated dependencies [30befdb]
9
+ - @olenbetong/appframe-vite@6.3.0
10
+
11
+ ## 4.5.0
12
+
13
+ ### Minor Changes
14
+
15
+ - e61d41b: af resources generate now reads types.json from cwd for type overrides keyed by database object name
16
+
17
+ ### Patch Changes
18
+
19
+ - 19ef84b: af resources generate: add --output flag to write to a file and compute the correct relative import path for custom.d.ts
20
+ - 1e0c584: Add resources.yaml config system for data objects and stored procedures
21
+ - `appframe-vite`: New `resources.yaml` config file support — declare all data objects and stored procedures in a single YAML file per app.
22
+ - `appframe-vite`: New CLI commands: `appframe-vite resources generate` (regenerate all files from config), `resources add` (interactive wizard), `resources edit [id]` (interactive editor).
23
+ - `appframe-vite`: Dev server now watches `resources.yaml` and auto-regenerates output files on change.
24
+ - `appframe-vite`: New `@olenbetong/appframe-vite/resources` package export with shared code generation utilities (`fetchAndGenerate`, `buildYamlConfig`, `parseYamlConfig`, etc.).
25
+ - `appframe-cli`: Remove `af resources regenerate` command (superseded by `appframe-vite resources generate`).
26
+ - `appframe-cli`: `af resources generate` now uses generation logic from `@olenbetong/appframe-vite/resources`.
27
+
28
+ - Updated dependencies [1e0c584]
29
+ - @olenbetong/appframe-vite@6.2.0
30
+
3
31
  ## 4.4.10
4
32
 
5
33
  ### Patch Changes
package/README.md CHANGED
@@ -68,6 +68,9 @@ Use `af <command> --help` for full usage with options.
68
68
  - `af resources add <id>`: Adds a data resource by DB object id. Options: `--server <host>`, `--name [name]`.
69
69
  - `af resources delete <id>`: Deletes a data resource by id or name. Options: `--server <host>`.
70
70
 
71
+ > **Note:** To manage data objects and procedures declaratively, use the `resources.yaml` config system in `@olenbetong/appframe-vite`. See `appframe-vite resources add/edit/generate` commands.
72
+
73
+
71
74
  ### Bundles
72
75
 
73
76
  - `af bundle create`: Scaffold a new bundle project.
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ import { mkdirSync, renameSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import AdmZip from "adm-zip";
2
5
  import chalk from "chalk";
3
6
  import { Command } from "commander";
4
7
  import { config } from "dotenv";
@@ -9,6 +12,11 @@ import { execShellCommand } from "./lib/execShellCommand.js";
9
12
  import { importJson } from "./lib/importJson.js";
10
13
  import { Server } from "./lib/Server.js";
11
14
  config({ path: `${process.cwd()}/.env`, quiet: true });
15
+ function createZip(sourceDir, outputFile) {
16
+ let zip = new AdmZip();
17
+ zip.addLocalFolder(sourceDir, sourceDir);
18
+ zip.writeZip(outputFile);
19
+ }
12
20
  async function prepareBundle(pkgName, options) {
13
21
  try {
14
22
  let name;
@@ -74,23 +82,23 @@ async function prepareBundle(pkgName, options) {
74
82
  console.log(chalk.yellow("No bundle found..."));
75
83
  }
76
84
  console.log("Removing previous...");
77
- await execShellCommand(`rm -rf ${rootOutputFolder}`);
85
+ rmSync(rootOutputFolder, { recursive: true, force: true });
78
86
  console.log("Packing...");
79
87
  await execShellCommand(pkgName ? `npm pack ${pkgName}` : "npm pack");
80
88
  console.log("Making output directory...");
81
- await execShellCommand(`mkdir -p ${name}`);
89
+ mkdirSync(name, { recursive: true });
82
90
  console.log(`Extracting '${tarball}'...`);
83
91
  await execShellCommand(`tar -xvzf ${tarball} -C ${name}`);
84
92
  console.log(`Moving from 'package' to '${version}'...`);
85
- await execShellCommand(`mv ${name}/package ${name}/${version}`);
93
+ renameSync(join(name, "package"), join(name, version));
86
94
  console.log("Zipping...");
87
- await execShellCommand(`zip -r ${zipFileName} ./${rootOutputFolder}`);
95
+ createZip(rootOutputFolder, zipFileName);
88
96
  console.log("Cleaning...");
89
- await execShellCommand(`rm -rf ${rootOutputFolder}`);
90
- await execShellCommand(`rm ${tarball}`);
97
+ rmSync(rootOutputFolder, { recursive: true, force: true });
98
+ rmSync(tarball);
91
99
  if (id) {
92
100
  await server.uploadBundle(Project_ID, id, zipFileName);
93
- await execShellCommand(`rm ${zipFileName}`);
101
+ rmSync(zipFileName);
94
102
  console.log("Done!");
95
103
  } else {
96
104
  console.log("Launching explorer...");
@@ -1,280 +1,23 @@
1
1
  "use strict";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { Client } from "@olenbetong/appframe-data";
4
+ import {
5
+ buildYamlConfig,
6
+ fetchAndGenerate,
7
+ formatWithBiome
8
+ } from "@olenbetong/appframe-vite/resources";
9
+ import { config } from "dotenv";
2
10
  import inquirer from "inquirer";
3
11
  import { Command } from "./lib/Command.js";
4
12
  import { importJson } from "./lib/importJson.js";
5
13
  import { Server } from "./lib/Server.js";
14
+ config({ path: `${process.cwd()}/.env`, quiet: true });
6
15
  const appPkg = await importJson("../package.json");
7
- function afTypeToTsType(type, dateStyle = false) {
8
- switch (type) {
9
- case "bigint":
10
- return "bigint";
11
- case "int":
12
- case "decimal":
13
- case "smallint":
14
- case "tinyint":
15
- case "float":
16
- case "numeric":
17
- return "number";
18
- case "bit":
19
- return "boolean";
20
- case "datetime2":
21
- case "datetime":
22
- case "smalldatetime":
23
- case "date":
24
- if (dateStyle === "ts") {
25
- return "Date";
26
- } else if (dateStyle === "ts-proc") {
27
- return "string | Date";
28
- } else {
29
- return type === "date" ? "date" : "datetime";
30
- }
31
- default:
32
- return "string";
33
- }
34
- }
35
- function getProcedureDefinition(name, procDefinition, options) {
36
- let parameters = [];
37
- let typeOverrides = {};
38
- if (options.overrides) {
39
- let overrides = options.overrides.split(",");
40
- for (let override of overrides) {
41
- let [param, type] = override.split(":");
42
- typeOverrides[param] = type;
43
- }
44
- }
45
- for (let parameter of procDefinition.Parameters) {
46
- parameters.push({
47
- name: parameter.ParamName,
48
- type: afTypeToTsType(parameter.TypeName, "field"),
49
- hasDefault: parameter.has_default_value,
50
- required: !parameter.has_default_value && !parameter.is_nullable
51
- });
52
- }
53
- let output = [];
54
- if (!options.global) {
55
- output.push(`import { ProcedureAPI } from "@olenbetong/appframe-data";`);
56
- if (options.expose) {
57
- output.push(`import { expose } from "@olenbetong/appframe-core";`);
58
- }
59
- output.push("");
60
- }
61
- let paramTypeName = "ProcParams";
62
- let procName = "proc";
63
- if (options.id) {
64
- procName = options.id;
65
- paramTypeName = `${options.id}Params`;
66
- if (paramTypeName.startsWith("proc")) {
67
- paramTypeName = paramTypeName.substring(4);
68
- }
69
- }
70
- if (options.types) {
71
- if (procDefinition.Parameters.length > 0) {
72
- let typeOutput = [`export type ${paramTypeName} = {`];
73
- for (let parameter of procDefinition.Parameters) {
74
- let type = typeOverrides[parameter.ParamName];
75
- let name2 = parameter.ParamName;
76
- if (!type) {
77
- type = afTypeToTsType(parameter.TypeName, "ts-proc");
78
- }
79
- if (parameter.has_default_value || parameter.is_nullable) {
80
- type += " | null";
81
- name2 += "?";
82
- }
83
- typeOutput.push(` ${name2}: ${type}`);
84
- }
85
- typeOutput.push("};");
86
- output.push(typeOutput.join("\n"));
87
- output.push("");
88
- } else {
89
- output.push(`export type ${paramTypeName} = null | undefined | Record<string, unknown>;
90
- `);
91
- }
92
- }
93
- output.push(`export const ${procName} = new ${options.global ? "af." : ""}ProcedureAPI${options.types ? `<${paramTypeName}, unknown>` : ""}({
94
- procedureId: "${name}",
95
- parameters: ${JSON.stringify(parameters, null, 2)},
96
- timeout: 30000
97
- });`);
98
- if (options.expose) {
99
- output.push("");
100
- output.push(
101
- `${options.global ? "af.common." : ""}expose("af.article.procedures.${procName}", ${procName}, { overwrite: true });`
102
- );
103
- }
104
- return output.join("\n");
105
- }
106
- function getDataObjectDefinition(name, viewDefinition, options) {
107
- let fields = [];
108
- let includeFields = typeof options.fields === "string" ? options.fields?.split(",").filter((f) => !!f) ?? [] : [];
109
- let aggregates = {};
110
- if (options.aggregates) {
111
- for (let aggregateDef of options.aggregates.split(",")) {
112
- let [field, aggregate] = aggregateDef.split(":");
113
- aggregates[field] = aggregate;
114
- }
115
- }
116
- for (let field of viewDefinition.Parameters) {
117
- if (includeFields.length > 0 && !includeFields.includes(field.Name)) {
118
- continue;
119
- }
120
- let fieldDefinition = {
121
- name: field.Name,
122
- type: field.DataType,
123
- nullable: field.Nullable
124
- };
125
- if (field.HasDefault) {
126
- fieldDefinition.hasDefault = true;
127
- }
128
- if (field.Computed) {
129
- fieldDefinition.computed = true;
130
- }
131
- if (field.Identity) {
132
- fieldDefinition.identity = true;
133
- }
134
- if (aggregates[field.Name]) {
135
- fieldDefinition.aggregate = aggregates[field.Name];
136
- }
137
- fields.push(fieldDefinition);
138
- }
139
- let api = "generateApiDataObject";
140
- let types = "";
141
- let typeName = `${options.id}Record`;
142
- let typeOverrides = {};
143
- if (options.overrides) {
144
- let overrides = options.overrides.split(",");
145
- for (let override of overrides) {
146
- let [param, type] = override.split(":");
147
- typeOverrides[param] = type;
148
- }
149
- }
150
- if (typeName.startsWith("ds")) {
151
- typeName = typeName.substring(2);
152
- }
153
- if (options.types) {
154
- api = `generateApiDataObject<${typeName}>`;
155
- let fieldTypes = "";
156
- for (let field of fields) {
157
- let type = typeOverrides[field.name];
158
- if (!type) {
159
- type = afTypeToTsType(field.type, "ts");
160
- if (field.nullable) {
161
- type += " | null";
162
- }
163
- }
164
- fieldTypes += ` ${field.name}: ${type};
165
- `;
166
- }
167
- types = `export type ${typeName} = {
168
- ${fieldTypes}}`;
169
- }
170
- if (options.global) {
171
- api = `af.data.${api}`;
172
- }
173
- let output = "";
174
- if (!options.global) {
175
- output += `import { generateApiDataObject${options.sortOrder ? ", SortOrder" : ""} } from "@olenbetong/appframe-data";`;
176
- if (options.expose) {
177
- output += `
178
- import { expose } from "@olenbetong/appframe-core";`;
179
- }
180
- output += "\n\n";
181
- }
182
- if (options.master && options.master.indexOf(":") > 0) {
183
- let [name2, path] = options.master.split(":");
184
- output += `import { ${name2} } from "${path}";
185
-
186
- `;
187
- }
188
- if (options.types) {
189
- output += `${types}
190
-
191
- `;
192
- }
193
- let linkFields = "";
194
- let dsOptions = [];
195
- dsOptions.push(`resource: "${name}"`);
196
- if (options.unique) {
197
- dsOptions.push(`uniqueName: "${options.unique}"`);
198
- }
199
- dsOptions.push(`id: "${options.id}"`);
200
- if (options.master && options.linkFields) {
201
- dsOptions.push(`masterDataObject: ${options.master.split(":")[0]}`);
202
- let fields2 = options.linkFields.split(",");
203
- linkFields = `linkFields: {
204
- ${fields2.map((field) => {
205
- let [thisField, masterField] = field.split(":");
206
- return `${thisField}: "${masterField ?? thisField}",`;
207
- }).join("\n ")}
208
- }`;
209
- dsOptions.push(linkFields);
210
- }
211
- dsOptions.push(`allowUpdate: ${options.permissions?.includes("U") ?? false}`);
212
- dsOptions.push(`allowInsert: ${options.permissions?.includes("I") ?? false}`);
213
- dsOptions.push(`allowDelete: ${options.permissions?.includes("D") ?? false}`);
214
- dsOptions.push(`dynamicLoading: ${options.dynamic || false}`);
215
- let fieldsOption = fields.map((field) => ({
216
- ...field,
217
- type: afTypeToTsType(field.type, "field")
218
- }));
219
- dsOptions.push(`fields: ${JSON.stringify(fieldsOption, null, 2).split("\n").join("\n ")}`);
220
- let parametersOption = [`maxRecords: ${options.maxRecords}`];
221
- if (options.sortOrder) {
222
- let sorts = options.sortOrder.split(",");
223
- let sortPrefix = options.global ? "af.data.SortOrder." : "SortOrder.";
224
- let sortOrder = sorts.map((sort) => {
225
- let [field, order = "asc"] = sort.split(":");
226
- switch (order.toLocaleLowerCase()) {
227
- case "asc":
228
- order = "Asc";
229
- break;
230
- case "desc":
231
- order = "Desc";
232
- break;
233
- case "ascnullslast":
234
- order = "AscNullsLast";
235
- break;
236
- case "descnullsfirst":
237
- order = "DescNullsFirst";
238
- break;
239
- }
240
- return `{ ${field}: ${sortPrefix}${order} }`;
241
- }).join(", ");
242
- parametersOption.push(`sortOrder: [${sortOrder}]`);
243
- }
244
- if (options.groupBy) {
245
- parametersOption.push(`groupBy: ${JSON.stringify(options.groupBy.split(","))}`);
246
- }
247
- if (options.where) {
248
- parametersOption.push(`whereClause: "${options.where}"`);
249
- }
250
- if (options.distinct) {
251
- parametersOption.push(`distinctRows: true`);
252
- }
253
- dsOptions.push(`parameters: {
254
- ${parametersOption.join(",\n ")}
255
- }`);
256
- output += `export const ${options.id} = ${api}({
257
- ${dsOptions.join(",\n ")}
258
- });
259
- `;
260
- if (options.expose) {
261
- let id = typeof options.expose === "string" ? options.expose : options.id;
262
- if (options.global) {
263
- output += `
264
- af.common.expose("af.article.dataObjects.${id}", ${options.id}, { overwrite: true });`;
265
- } else {
266
- output += `
267
- expose("af.article.dataObjects.${id}", ${options.id}, { overwrite: true });`;
268
- }
269
- }
270
- return output;
271
- }
272
16
  async function getResourceDefinition(resourceName, options) {
273
- let server = new Server("dev.obet.no");
274
- let resource = await server.getResourceArgument(resourceName);
275
- let definition = await server.getResourceDefinition(resource);
276
- definition.Parameters = definition.Parameters.filter((p) => !["CUT", "CDL"].includes(p.Name));
277
17
  if (options.fields === true) {
18
+ let server2 = new Server(options.server);
19
+ let resource2 = await server2.getResourceArgument(resourceName);
20
+ let definition = await server2.getResourceDefinition(resource2);
278
21
  let response = await inquirer.prompt([
279
22
  {
280
23
  type: "checkbox",
@@ -287,31 +30,43 @@ async function getResourceDefinition(resourceName, options) {
287
30
  ]);
288
31
  options.fields = response.fields.join(",");
289
32
  }
290
- let command = [`af resources generate ${resource}`];
291
- if (options.id) command.push(`--id ${options.id}`);
292
- if (options.unique) command.push(`--unique ${options.unique}`);
293
- if (options.global) command.push("--global");
294
- if (options.types) command.push("--types");
295
- if (options.maxRecords && definition.ObjectType !== "P") command.push(`--max-records ${options.maxRecords}`);
296
- if (options.sortOrder) command.push(`--sort-order ${options.sortOrder}`);
297
- if (options.permissions) command.push(`--permissions ${options.permissions}`);
298
- if (options.master) command.push(`--master ${options.master}`);
299
- if (options.linkFields) command.push(`--link-fields ${options.linkFields}`);
300
- if (options.expose) command.push(`--expose${typeof options.expose === "string" ? ` ${options.expose}` : ""}`);
301
- if (options.dynamic) command.push(`--dynamic`);
302
- if (options.overrides) command.push(`--overrides "${options.overrides}"`);
303
- if (options.distinct) command.push("--distinct");
304
- if (options.aggregates) command.push(`--aggregates ${options.aggregates}`);
305
- if (options.groupBy) command.push(`--group-by ${options.groupBy}`);
306
- if (options.where) command.push(`--where "${options.where}"`);
307
- if (options.fields) command.push(`--fields ${options.fields}`);
308
- console.log(`/*
33
+ let server = new Server(options.server);
34
+ let resource = await server.getResourceArgument(resourceName);
35
+ let { APPFRAME_LOGIN: username = "", APPFRAME_PWD: password = "" } = process.env;
36
+ let client = new Client(options.server);
37
+ await client.login(username, password);
38
+ let content = await fetchAndGenerate(resource, options, client);
39
+ if (options.output) {
40
+ let header = buildYamlConfig(resource, options);
41
+ await writeFile(options.output, `${header}
42
+ ${content}`, "utf-8");
43
+ await formatWithBiome(options.output);
44
+ console.log(`Written to ${options.output}`);
45
+ } else {
46
+ let command = [`af resources generate ${resource}`];
47
+ if (options.id) command.push(`--id ${options.id}`);
48
+ if (options.unique) command.push(`--unique ${options.unique}`);
49
+ if (options.global) command.push("--global");
50
+ if (options.types) command.push("--types");
51
+ if (options.maxRecords) command.push(`--max-records ${options.maxRecords}`);
52
+ if (options.sortOrder) command.push(`--sort-order ${options.sortOrder}`);
53
+ if (options.permissions) command.push(`--permissions ${options.permissions}`);
54
+ if (options.master) command.push(`--master ${options.master}`);
55
+ if (options.linkFields) command.push(`--link-fields ${options.linkFields}`);
56
+ if (options.expose) command.push(`--expose${typeof options.expose === "string" ? ` ${options.expose}` : ""}`);
57
+ if (options.dynamic) command.push("--dynamic");
58
+ if (options.overrides) command.push(`--overrides "${options.overrides}"`);
59
+ if (options.distinct) command.push("--distinct");
60
+ if (options.aggregates) command.push(`--aggregates ${options.aggregates}`);
61
+ if (options.groupBy) command.push(`--group-by ${options.groupBy}`);
62
+ if (options.where) command.push(`--where "${options.where}"`);
63
+ if (options.fields) command.push(`--fields ${options.fields}`);
64
+ console.log(`/*
309
65
  auto-generated by CLI:
310
66
  ${command.join(" \\\n ")}
311
67
  */`);
312
- console.log(
313
- definition.ObjectType === "V" ? getDataObjectDefinition(resource, definition, options) : getProcedureDefinition(resource, definition, options)
314
- );
68
+ console.log(content);
69
+ }
315
70
  }
316
71
  const program = new Command();
317
72
  program.version(appPkg.version).addResourceArgument().option("-i, --id <id>", "ID to use as name for the data object", "dsDataObject").option("-g, --global", "Use af.data globals to generate the data object").option("-t, --types", "Include data object types").option("-f, --fields [fields]", "Comma-separated list of fields to include").option(
@@ -332,5 +87,8 @@ program.version(appPkg.version).addResourceArgument().option("-i, --id <id>", "I
332
87
  ).option("--group-by <fields>", "Comma separated list of fields to group by").option(
333
88
  "--aggregates <fields>",
334
89
  "Comma separated list of aggregate binding for non-grouped fields, e.g. Quantity:SUM,Created:MAX"
335
- ).option("--distinct", "Data object should fetch distinct data").option("--where <whereClause>", "Initial where clause to set on the data object").action(getResourceDefinition);
90
+ ).option("--distinct", "Data object should fetch distinct data").option("--where <whereClause>", "Initial where clause to set on the data object").option(
91
+ "-O, --output <path>",
92
+ "Write output to this file instead of stdout; also sets the correct relative import path for custom.d.ts"
93
+ ).action(getResourceDefinition);
336
94
  await program.parseAsync(process.argv);
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "af": "./cli/af.js"
6
6
  },
7
7
  "type": "module",
8
- "version": "4.4.10",
8
+ "version": "4.5.1",
9
9
  "license": "MIT",
10
10
  "keywords": [
11
11
  "appframe",
@@ -13,6 +13,7 @@
13
13
  "appframe web"
14
14
  ],
15
15
  "devDependencies": {
16
+ "@types/adm-zip": "^0.5.8",
16
17
  "@types/d3": "^7.4.3",
17
18
  "@types/degit": "^2.8.6",
18
19
  "@types/inquirer": "^9.0.9",
@@ -22,7 +23,8 @@
22
23
  "tough-cookie": "^6.0.0"
23
24
  },
24
25
  "dependencies": {
25
- "@biomejs/biome": "2.4.12",
26
+ "@biomejs/biome": "2.4.14",
27
+ "adm-zip": "^0.5.17",
26
28
  "chalk": "^5.6.2",
27
29
  "commander": "^14.0.1",
28
30
  "compare-versions": "^6.1.1",
@@ -41,7 +43,8 @@
41
43
  "signal-exit": "^4.1.0",
42
44
  "typescript": "6.0.3",
43
45
  "@olenbetong/appframe-data": "1.5.0",
44
- "@olenbetong/appframe-updater": "0.4.10"
46
+ "@olenbetong/appframe-updater": "0.4.10",
47
+ "@olenbetong/appframe-vite": "6.3.0"
45
48
  },
46
49
  "optionalDependencies": {
47
50
  "keytar": "^8.3.1"
@@ -1,3 +1,6 @@
1
+ import { mkdirSync, renameSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import AdmZip from "adm-zip";
1
4
  import chalk from "chalk";
2
5
  import { Command } from "commander";
3
6
  import { config } from "dotenv";
@@ -10,6 +13,12 @@ import { Server } from "./lib/Server.js";
10
13
 
11
14
  config({ path: `${process.cwd()}/.env`, quiet: true });
12
15
 
16
+ function createZip(sourceDir: string, outputFile: string) {
17
+ let zip = new AdmZip();
18
+ zip.addLocalFolder(sourceDir, sourceDir);
19
+ zip.writeZip(outputFile);
20
+ }
21
+
13
22
  /**
14
23
  * @param {string} pkgName
15
24
  * @param {{ bundle?: string | null }} options
@@ -90,24 +99,24 @@ async function prepareBundle(pkgName: string, options: { bundle: string }) {
90
99
  }
91
100
 
92
101
  console.log("Removing previous...");
93
- await execShellCommand(`rm -rf ${rootOutputFolder}`);
102
+ rmSync(rootOutputFolder, { recursive: true, force: true });
94
103
  console.log("Packing...");
95
104
  await execShellCommand(pkgName ? `npm pack ${pkgName}` : "npm pack");
96
105
  console.log("Making output directory...");
97
- await execShellCommand(`mkdir -p ${name}`);
106
+ mkdirSync(name, { recursive: true });
98
107
  console.log(`Extracting '${tarball}'...`);
99
108
  await execShellCommand(`tar -xvzf ${tarball} -C ${name}`);
100
109
  console.log(`Moving from 'package' to '${version}'...`);
101
- await execShellCommand(`mv ${name}/package ${name}/${version}`);
110
+ renameSync(join(name, "package"), join(name, version));
102
111
  console.log("Zipping...");
103
- await execShellCommand(`zip -r ${zipFileName} ./${rootOutputFolder}`);
112
+ createZip(rootOutputFolder, zipFileName);
104
113
  console.log("Cleaning...");
105
- await execShellCommand(`rm -rf ${rootOutputFolder}`);
106
- await execShellCommand(`rm ${tarball}`);
114
+ rmSync(rootOutputFolder, { recursive: true, force: true });
115
+ rmSync(tarball);
107
116
 
108
117
  if (id) {
109
118
  await server.uploadBundle(Project_ID, id, zipFileName);
110
- await execShellCommand(`rm ${zipFileName}`);
119
+ rmSync(zipFileName);
111
120
  console.log("Done!");
112
121
  } else {
113
122
  console.log("Launching explorer...");