exb-community-cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/commands/dev-setup.js +237 -0
- package/dist/commands/format.js +1 -1
- package/dist/commands/scaffold.js +12 -2
- package/dist/index.js +52 -32
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -4,9 +4,11 @@ A simple, lightweight command line interface to make using Experience Builder De
|
|
|
4
4
|
|
|
5
5
|
## Using this CLI
|
|
6
6
|
|
|
7
|
-
The ExB Community CLI has a few tools to make it easy to install, update, remove, and search for Experience Builder widgets that have been posted on NPM. Installing the CLI is as simple as running `npm i exb-community-cli` in your client directory of your Experience Builder application. To then run commands, the format is `npx exb-cli [command]`. If you don't want to prefix every command with npx, you can install the exb-community-cli globally, by running `npm i -g exb-community-cli
|
|
7
|
+
The ExB Community CLI has a few tools to make it easy to install, update, remove, and search for Experience Builder widgets that have been posted on NPM. Installing the CLI is as simple as running `npm i exb-community-cli` in your client directory of your Experience Builder application. To then run commands, the format is `npx exb-cli [command]`. If you don't want to prefix every command with npx, you can install the exb-community-cli globally, by running `npm i -g exb-community-cli` (Recommended). For information on the commands that exist, run `npx exb-cli help`, and commands, arguments, and descriptions will be listed.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Additionally, this CLI makes it easy to get up and running with Experience Builder, requiring a single command to install and run Experience Builder Developer Edition, making it easier than ever to get started. Just install the CLI globally `npm-i -g exb-community-cli` and then run `exb-cli dev-setup`. The tool will give you a few prompts to get started, and you'll end up with a fully functional install.
|
|
10
|
+
|
|
11
|
+
When using any of the ExB Community CLI commands, the command needs to be run in the client directory of your Experience builder application (excluding dev-setup). If you don't, you will get the error `Error: Run this from the ExB client folder.`.
|
|
10
12
|
|
|
11
13
|
## Formatting widgets for NPM (And Best Practices)
|
|
12
14
|
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.devSetup = devSetup;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const promises_1 = require("stream/promises");
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const readline_1 = require("readline");
|
|
13
|
+
const enquirer_1 = __importDefault(require("enquirer"));
|
|
14
|
+
const os_1 = __importDefault(require("os"));
|
|
15
|
+
async function devSetup(options) {
|
|
16
|
+
let version = options.version;
|
|
17
|
+
const response = await fetch("https://developers.arcgis.com/experience-builder/guide/downloads/");
|
|
18
|
+
const html = await response.text();
|
|
19
|
+
// Extract version numbers from the page using a regex
|
|
20
|
+
const versions = [...html.matchAll(/py-4 pr-3">v(\d+\.\d+)/g)].map((m) => m[1]);
|
|
21
|
+
if (options.version && options.version == "latest") {
|
|
22
|
+
console.log("Setting up development environment for latest version...");
|
|
23
|
+
const latestVersion = versions[0];
|
|
24
|
+
version = latestVersion;
|
|
25
|
+
}
|
|
26
|
+
else if (options.version === undefined) {
|
|
27
|
+
const prompt = new enquirer_1.default.Select({
|
|
28
|
+
name: "version",
|
|
29
|
+
message: "Choose an Experience Builder version",
|
|
30
|
+
choices: versions,
|
|
31
|
+
limit: 5
|
|
32
|
+
});
|
|
33
|
+
const chosenVersion = await prompt.run();
|
|
34
|
+
version = chosenVersion;
|
|
35
|
+
}
|
|
36
|
+
// Actually install and set up the chosen version of Experience Builder
|
|
37
|
+
await installExperienceBuilder(version);
|
|
38
|
+
// Ask user if they want to start the development servers now
|
|
39
|
+
const approveStart = await new enquirer_1.default.Confirm({
|
|
40
|
+
name: "start",
|
|
41
|
+
message: "Do you want to start the development server now?"
|
|
42
|
+
}).run();
|
|
43
|
+
if (!approveStart) {
|
|
44
|
+
console.log("You can start the development server later by running `npm start` in the client and server folders.");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const clientDir = path_1.default.join(process.cwd(), `arcgis-experience-builder-${version}`, "client");
|
|
48
|
+
const serverDir = path_1.default.join(process.cwd(), `arcgis-experience-builder-${version}`, "server");
|
|
49
|
+
console.log("Starting development servers in new terminal windows...");
|
|
50
|
+
openInNewTerminal(clientDir, "client");
|
|
51
|
+
openInNewTerminal(serverDir, "server");
|
|
52
|
+
}
|
|
53
|
+
const ciWithSpinner = (message, cwd) => {
|
|
54
|
+
const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
55
|
+
let frameIndex = 0;
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
const interval = setInterval(() => {
|
|
58
|
+
frameIndex = (frameIndex + 1) % spinnerFrames.length;
|
|
59
|
+
process.stdout.write(`\r ${spinnerFrames[frameIndex]} ${message}`);
|
|
60
|
+
}, 80);
|
|
61
|
+
process.stdout.write(` ${spinnerFrames[0]} ${message}`);
|
|
62
|
+
const proc = (0, child_process_1.spawn)("npm", ["ci"], { cwd, stdio: "pipe" });
|
|
63
|
+
proc.on("close", (code) => {
|
|
64
|
+
clearInterval(interval);
|
|
65
|
+
if (code === 0) {
|
|
66
|
+
process.stdout.write(`\r ✔ ${message} done.\n`);
|
|
67
|
+
resolve();
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
process.stdout.write(`\r ✖ ${message} failed.\n`);
|
|
71
|
+
reject(new Error(`npm ci exited with code ${code}`));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
proc.on("error", (err) => {
|
|
75
|
+
clearInterval(interval);
|
|
76
|
+
reject(err);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
const openInNewTerminal = (cwd, label) => {
|
|
81
|
+
const cmd = `cd "${cwd}" && npm start`;
|
|
82
|
+
if (process.platform === "darwin") {
|
|
83
|
+
const script = `tell application "Terminal"\nactivate\ndo script "${cmd.replace(/"/g, '\\"')}"\nend tell`;
|
|
84
|
+
(0, child_process_1.spawn)("osascript", ["-e", script], {
|
|
85
|
+
stdio: "ignore",
|
|
86
|
+
detached: true
|
|
87
|
+
}).unref();
|
|
88
|
+
}
|
|
89
|
+
else if (process.platform === "win32") {
|
|
90
|
+
(0, child_process_1.spawn)("cmd.exe", ["/c", "start", "cmd.exe", "/k", cmd], {
|
|
91
|
+
cwd,
|
|
92
|
+
stdio: "ignore",
|
|
93
|
+
detached: true,
|
|
94
|
+
shell: true
|
|
95
|
+
}).unref();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Linux: try common terminal emulators in order
|
|
99
|
+
const terminals = [
|
|
100
|
+
{
|
|
101
|
+
bin: "x-terminal-emulator",
|
|
102
|
+
args: ["-e", `bash -c '${cmd}; exec bash'`]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
bin: "gnome-terminal",
|
|
106
|
+
args: ["--", "bash", "-c", `${cmd}; exec bash`]
|
|
107
|
+
},
|
|
108
|
+
{ bin: "konsole", args: ["-e", "bash", "-c", `${cmd}; exec bash`] },
|
|
109
|
+
{
|
|
110
|
+
bin: "xfce4-terminal",
|
|
111
|
+
args: ["-e", `bash -c '${cmd}; exec bash'`]
|
|
112
|
+
},
|
|
113
|
+
{ bin: "xterm", args: ["-e", `bash -c '${cmd}; exec bash'`] }
|
|
114
|
+
];
|
|
115
|
+
let launched = false;
|
|
116
|
+
for (const t of terminals) {
|
|
117
|
+
try {
|
|
118
|
+
(0, child_process_1.execSync)(`which ${t.bin}`, { stdio: "pipe" });
|
|
119
|
+
(0, child_process_1.spawn)(t.bin, t.args, {
|
|
120
|
+
cwd,
|
|
121
|
+
stdio: "ignore",
|
|
122
|
+
detached: true
|
|
123
|
+
}).unref();
|
|
124
|
+
launched = true;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// not found, try next
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!launched) {
|
|
132
|
+
console.log(` ⚠ No supported terminal found. Run manually: cd "${cwd}" && npm start`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
console.log(` ✔ Opened ${label} in a new terminal window.`);
|
|
137
|
+
};
|
|
138
|
+
const installExperienceBuilder = async (version) => {
|
|
139
|
+
const url = `https://downloads.arcgis.com/dms/rest/download/secured/arcgis-experience-builder-${version}.zip?f=json&folder=software%2FExperienceBuilder%2F${version}`;
|
|
140
|
+
const res = await fetch(url);
|
|
141
|
+
if (!res.ok) {
|
|
142
|
+
throw new Error(`Failed to fetch download info: ${res.status} ${res.statusText}`);
|
|
143
|
+
}
|
|
144
|
+
const json = (await res.json());
|
|
145
|
+
const downloadUrl = json.url;
|
|
146
|
+
if (!downloadUrl) {
|
|
147
|
+
throw new Error("No download URL found in response");
|
|
148
|
+
}
|
|
149
|
+
// Download the zip file to a temporary location
|
|
150
|
+
const zipPath = path_1.default.join(os_1.default.tmpdir(), `exb-install-${Date.now()}`);
|
|
151
|
+
const extractDir = path_1.default.resolve(process.cwd(), `arcgis-experience-builder-${version}`);
|
|
152
|
+
try {
|
|
153
|
+
console.log(`Downloading zip`);
|
|
154
|
+
const zipRes = await fetch(downloadUrl);
|
|
155
|
+
if (!zipRes.ok || !zipRes.body) {
|
|
156
|
+
throw new Error(`Failed to download zip: ${zipRes.status} ${zipRes.statusText}`);
|
|
157
|
+
}
|
|
158
|
+
await (0, promises_1.pipeline)(zipRes.body, (0, fs_1.createWriteStream)(zipPath));
|
|
159
|
+
// Extract the zip file to a folder in the current directory
|
|
160
|
+
console.log(`Extracting to ${extractDir}...`);
|
|
161
|
+
await fs_extra_1.default.ensureDir(extractDir);
|
|
162
|
+
// Get total file count for progress bar (pipe through tail to avoid buffer overflow on large zips)
|
|
163
|
+
const listSummary = (0, child_process_1.execSync)(`unzip -l "${zipPath}" | tail -1`, {
|
|
164
|
+
encoding: "utf-8"
|
|
165
|
+
});
|
|
166
|
+
const countMatch = listSummary.match(/(\d+)\s+files?/);
|
|
167
|
+
const totalFiles = countMatch ? parseInt(countMatch[1], 10) : 1;
|
|
168
|
+
await new Promise((resolve, reject) => {
|
|
169
|
+
let extracted = 0;
|
|
170
|
+
const proc = (0, child_process_1.spawn)("unzip", ["-o", zipPath, "-d", extractDir]);
|
|
171
|
+
const rl = (0, readline_1.createInterface)({ input: proc.stdout });
|
|
172
|
+
rl.on("line", (line) => {
|
|
173
|
+
if (/^\s*(extracting|inflating|creating):/.test(line)) {
|
|
174
|
+
extracted++;
|
|
175
|
+
const pct = Math.min(100, Math.round((extracted / totalFiles) * 100));
|
|
176
|
+
const filled = Math.round(pct / 4);
|
|
177
|
+
const bar = "█".repeat(filled) + "░".repeat(25 - filled);
|
|
178
|
+
process.stdout.write(`\r [${bar}] ${pct}% (${extracted}/${totalFiles})`);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
proc.stderr.on("data", () => { }); // suppress stderr
|
|
182
|
+
proc.on("close", (code) => {
|
|
183
|
+
process.stdout.write("\n");
|
|
184
|
+
if (code === 0)
|
|
185
|
+
resolve();
|
|
186
|
+
else
|
|
187
|
+
reject(new Error(`unzip exited with code ${code}`));
|
|
188
|
+
});
|
|
189
|
+
proc.on("error", reject);
|
|
190
|
+
});
|
|
191
|
+
// Remove any directory nesting (some zip versions include an extra top-level folder)
|
|
192
|
+
const entries = await fs_extra_1.default.readdir(extractDir);
|
|
193
|
+
const hasClient = entries.includes("client");
|
|
194
|
+
const hasServer = entries.includes("server");
|
|
195
|
+
if (!hasClient || !hasServer) {
|
|
196
|
+
// Check if there's a single nested directory containing client/ and server/
|
|
197
|
+
const dirs = [];
|
|
198
|
+
for (const entry of entries) {
|
|
199
|
+
const stat = await fs_extra_1.default.stat(path_1.default.join(extractDir, entry));
|
|
200
|
+
if (stat.isDirectory())
|
|
201
|
+
dirs.push(entry);
|
|
202
|
+
}
|
|
203
|
+
if (dirs.length === 1) {
|
|
204
|
+
const nestedDir = path_1.default.join(extractDir, dirs[0]);
|
|
205
|
+
const nestedEntries = await fs_extra_1.default.readdir(nestedDir);
|
|
206
|
+
if (nestedEntries.includes("client") &&
|
|
207
|
+
nestedEntries.includes("server")) {
|
|
208
|
+
// Move all contents from the nested dir up to extractDir
|
|
209
|
+
for (const item of nestedEntries) {
|
|
210
|
+
await fs_extra_1.default.move(path_1.default.join(nestedDir, item), path_1.default.join(extractDir, item), { overwrite: true });
|
|
211
|
+
}
|
|
212
|
+
await fs_extra_1.default.remove(nestedDir);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
throw new Error("Extracted zip does not contain expected client/ and server/ folders");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
throw new Error("Extracted zip does not contain expected client/ and server/ folders");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Install dependencies in client and server folders
|
|
223
|
+
const clientDir = path_1.default.join(extractDir, "client");
|
|
224
|
+
const serverDir = path_1.default.join(extractDir, "server");
|
|
225
|
+
await ciWithSpinner("Installing client dependencies...", clientDir);
|
|
226
|
+
await ciWithSpinner("Installing server dependencies...", serverDir);
|
|
227
|
+
return extractDir;
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
231
|
+
return "";
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
// Clean up the zip file
|
|
235
|
+
await fs_extra_1.default.remove(zipPath);
|
|
236
|
+
}
|
|
237
|
+
};
|
package/dist/commands/format.js
CHANGED
|
@@ -60,7 +60,7 @@ async function formatWidget(widgetNameOrFolder, options = {}) {
|
|
|
60
60
|
// Collect package.json fields from manifest or prompt for missing values
|
|
61
61
|
const pkg = {};
|
|
62
62
|
const initialName = (manifest.name ?? (await askForValue('package name (npm-safe)'))) || '';
|
|
63
|
-
pkg.name = await validateAndChooseName(initialName, options.force);
|
|
63
|
+
pkg.name = await validateAndChooseName(initialName.toLowerCase(), options.force);
|
|
64
64
|
pkg.version = (await askForValue('version', '1.0.0')) || '1.0.0';
|
|
65
65
|
pkg.description = manifest.description ?? (await askForValue('description')) ?? '';
|
|
66
66
|
pkg.author = manifest.author ?? (await askForValue('author')) ?? '';
|
|
@@ -10,8 +10,17 @@ const os_1 = __importDefault(require("os"));
|
|
|
10
10
|
const degit_1 = __importDefault(require("degit"));
|
|
11
11
|
const enquirer_1 = __importDefault(require("enquirer"));
|
|
12
12
|
const manifestCollector_1 = require("../utils/manifestCollector");
|
|
13
|
-
const format_1 = require("./format");
|
|
14
13
|
async function scaffoldWidget(name) {
|
|
14
|
+
const rootDir = process.cwd();
|
|
15
|
+
const widgetsDir = path_1.default.join(rootDir, 'your-extensions', 'widgets');
|
|
16
|
+
if (!fs_extra_1.default.existsSync(path_1.default.join(rootDir, 'your-extensions'))) {
|
|
17
|
+
console.error('Error: Run this from the ExB client folder.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!(await fs_extra_1.default.pathExists(widgetsDir))) {
|
|
21
|
+
console.error('Error: No widgets folder found (your-extensions/widgets).');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
15
24
|
const repoSource = "Esri/arcgis-experience-builder-sdk-resources/widgets";
|
|
16
25
|
const tmpDir = path_1.default.join(os_1.default.tmpdir(), `exb-scaffold-${Date.now()}`);
|
|
17
26
|
await fs_extra_1.default.ensureDir(tmpDir);
|
|
@@ -45,7 +54,8 @@ async function scaffoldWidget(name) {
|
|
|
45
54
|
await fs_extra_1.default.copy(widgetFolder, targetPath);
|
|
46
55
|
const manifestPath = path_1.default.join(targetPath, 'manifest.json');
|
|
47
56
|
const manifest = await fs_extra_1.default.readJson(manifestPath);
|
|
48
|
-
manifest.name = await
|
|
57
|
+
//manifest.name = await validateAndChooseName(name);
|
|
58
|
+
manifest.name = name;
|
|
49
59
|
await fs_extra_1.default.writeJson(manifestPath, manifest, { spaces: 2 });
|
|
50
60
|
console.log(`${chosen.manifest.label ?? chosen.manifest.name} scaffolded to ${targetPath}`);
|
|
51
61
|
}
|
package/dist/index.js
CHANGED
|
@@ -45,70 +45,90 @@ const format_1 = require("./commands/format");
|
|
|
45
45
|
const scaffold_1 = require("./commands/scaffold");
|
|
46
46
|
const fs = __importStar(require("fs-extra"));
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
|
-
const
|
|
49
|
-
const
|
|
48
|
+
const dev_setup_1 = require("./commands/dev-setup");
|
|
49
|
+
const packageJsonPath = path.join(__dirname, "../package.json");
|
|
50
|
+
const packageJson = fs.readJsonSync(packageJsonPath, { throws: false }) || {
|
|
51
|
+
version: "1.0.0"
|
|
52
|
+
};
|
|
50
53
|
const program = new commander_1.Command();
|
|
51
54
|
program
|
|
52
|
-
.name(
|
|
53
|
-
.description(
|
|
55
|
+
.name("exb-cli")
|
|
56
|
+
.description("The community-led widget manager for ArcGIS Experience Builder")
|
|
54
57
|
.version(packageJson.version);
|
|
55
58
|
// --- COMMAND: INSTALL ---
|
|
56
59
|
program
|
|
57
|
-
.command(
|
|
58
|
-
.alias(
|
|
59
|
-
.description(
|
|
60
|
-
.option(
|
|
60
|
+
.command("install <package>")
|
|
61
|
+
.alias("i") // Allows users to type `exb-cli i widget-name`
|
|
62
|
+
.description("Install a widget from NPM into your Experience Builder project")
|
|
63
|
+
.option("--widget-only", "Skip running npm ci in the installed widget directory")
|
|
61
64
|
.action(async (pkg, options) => {
|
|
62
65
|
await (0, install_1.installWidget)(pkg, { widgetOnly: options.widgetOnly });
|
|
63
66
|
});
|
|
64
67
|
// --- COMMAND: UPDATE ---
|
|
65
68
|
program
|
|
66
|
-
.command(
|
|
67
|
-
.alias(
|
|
68
|
-
.description(
|
|
69
|
-
.option(
|
|
70
|
-
.option(
|
|
69
|
+
.command("update <package>")
|
|
70
|
+
.alias("u")
|
|
71
|
+
.description("Update an installed widget to the latest version (or a specified version)")
|
|
72
|
+
.option("--widget-only", "Skip running npm ci in the updated widget directory")
|
|
73
|
+
.option("--version <version>", "Specify a version or dist-tag to update to")
|
|
71
74
|
.action(async (pkg, options) => {
|
|
72
|
-
await (0, update_1.updateWidget)(pkg, {
|
|
75
|
+
await (0, update_1.updateWidget)(pkg, {
|
|
76
|
+
widgetOnly: options.widgetOnly,
|
|
77
|
+
version: options.version
|
|
78
|
+
});
|
|
73
79
|
});
|
|
74
80
|
// --- COMMAND: REMOVE ---
|
|
75
81
|
program
|
|
76
|
-
.command(
|
|
77
|
-
.alias(
|
|
78
|
-
.description(
|
|
79
|
-
.option(
|
|
82
|
+
.command("remove <package>")
|
|
83
|
+
.alias("rm")
|
|
84
|
+
.description("Remove an installed widget from your Experience Builder project")
|
|
85
|
+
.option("-f, --force", "Skip confirmation prompt")
|
|
80
86
|
.action(async (pkg, options) => {
|
|
81
87
|
await (0, remove_1.removeWidget)(pkg, { force: options.force });
|
|
82
88
|
});
|
|
83
89
|
// --- COMMAND: SEARCH ---
|
|
84
90
|
program
|
|
85
|
-
.command(
|
|
86
|
-
.alias(
|
|
87
|
-
.description(
|
|
88
|
-
.option(
|
|
89
|
-
.option(
|
|
90
|
-
.option(
|
|
91
|
+
.command("search")
|
|
92
|
+
.alias("s")
|
|
93
|
+
.description("Search npm for Experience Builder widgets (by keyword)")
|
|
94
|
+
.option("-k, --keyword <keyword>", "Additional keyword to include in search")
|
|
95
|
+
.option("-n, --size <size>", "Maximum number of npm results to fetch (default: 15)")
|
|
96
|
+
.option("--github-list", "Include results from a curated GitHub list when available")
|
|
91
97
|
.action(async (options) => {
|
|
92
98
|
const size = options.size ? Number(options.size) : undefined;
|
|
93
|
-
await (0, search_1.searchWidgets)({
|
|
99
|
+
await (0, search_1.searchWidgets)({
|
|
100
|
+
keyword: options.keyword,
|
|
101
|
+
size,
|
|
102
|
+
githubList: options.githubList
|
|
103
|
+
});
|
|
94
104
|
});
|
|
95
105
|
// --- COMMAND: FORMAT ---
|
|
96
106
|
program
|
|
97
|
-
.command(
|
|
98
|
-
.alias(
|
|
99
|
-
.description(
|
|
100
|
-
.option(
|
|
107
|
+
.command("format <widget>")
|
|
108
|
+
.alias("fmt")
|
|
109
|
+
.description("Format a widget package according to community standards, based on the manifest.json configuration.")
|
|
110
|
+
.option("-f, --force", "Skip prompts and overwrite package.json if present")
|
|
101
111
|
.action(async (widget, options) => {
|
|
102
112
|
await (0, format_1.formatWidget)(widget, { force: options.force });
|
|
103
113
|
});
|
|
104
114
|
// --- COMMAND: SCAFFOLD ---
|
|
105
115
|
program
|
|
106
|
-
.command(
|
|
107
|
-
.alias(
|
|
108
|
-
.description(
|
|
116
|
+
.command("scaffold <name>")
|
|
117
|
+
.alias("new")
|
|
118
|
+
.description("Scaffold a new widget package with a manifest.json, using an esri template")
|
|
109
119
|
.action(async (name, options) => {
|
|
110
120
|
await (0, scaffold_1.scaffoldWidget)(name);
|
|
111
121
|
});
|
|
122
|
+
program
|
|
123
|
+
.command("dev-setup")
|
|
124
|
+
.description("Download and set up the local development environment for Experience Builder widget development")
|
|
125
|
+
.option("--latest", "Download the latest version of Experience Builder Developer Edition")
|
|
126
|
+
.option("-v, --version <version>", "Specify a version of Experience Builder Developer Edition to download")
|
|
127
|
+
.action(async (options) => {
|
|
128
|
+
const version = options.version || (options.latest ? "latest" : undefined);
|
|
129
|
+
(0, dev_setup_1.devSetup)({ version });
|
|
130
|
+
//await scaffoldWidget('dev-environment', { version });
|
|
131
|
+
});
|
|
112
132
|
// Parse the arguments passed by the user in the terminal
|
|
113
133
|
program.parse(process.argv);
|
|
114
134
|
// If the user runs the CLI with no arguments, show the help menu
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exb-community-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A lightweight command line interface to allow Experience Builder developers to quickly import widgets published as NPM pacakges.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"exb-community",
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"commander": "^14.0.3",
|
|
27
|
+
"degit": "^2.8.0",
|
|
28
|
+
"enquirer": "^2.3.6",
|
|
27
29
|
"fs-extra": "^11.3.3",
|
|
28
30
|
"pacote": "^21.3.1",
|
|
29
31
|
"semver": "^7.7.4",
|
|
30
|
-
"validate-npm-package-name": "^7.0.2"
|
|
31
|
-
"degit": "^2.8.0",
|
|
32
|
-
"enquirer": "^2.3.6"
|
|
32
|
+
"validate-npm-package-name": "^7.0.2"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -42,4 +42,4 @@
|
|
|
42
42
|
"ts-jest": "^29.1.0",
|
|
43
43
|
"typescript": "^5.9.3"
|
|
44
44
|
}
|
|
45
|
-
}
|
|
45
|
+
}
|