@blastlauncher/cli 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ # @blastlauncher/cli
2
+
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Initial npm publish
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2023 Yukai Huang
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # `blast-cli`
2
+
3
+ ## Development
4
+
5
+ ```bash
6
+ # Output single cjs file with esbuild
7
+ pnpm build
8
+
9
+ # Run the cli
10
+ node ./dist/index.cjs --help
11
+
12
+ # Package to cli executable with pkg
13
+ pnpm package
14
+
15
+ # Run the cli executable
16
+ ./dist/cli-macos --help
17
+
18
+
19
+ ```
package/build.js ADDED
@@ -0,0 +1,15 @@
1
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
+ const esbuild = require("esbuild");
3
+ const { nodeExternalsPlugin } = require("esbuild-node-externals");
4
+
5
+ esbuild.build({
6
+ entryPoints: ["index.js"],
7
+ bundle: true,
8
+ platform: "node",
9
+ outfile: "dist/index.cjs",
10
+ plugins: [
11
+ nodeExternalsPlugin({
12
+ allowList: ["tempy"],
13
+ }),
14
+ ],
15
+ });
package/dist/cli-linux ADDED
Binary file
package/dist/cli-macos ADDED
Binary file
Binary file
package/dist/index.cjs ADDED
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+
29
+ // ../../node_modules/.pnpm/temp-dir@2.0.0/node_modules/temp-dir/index.js
30
+ var require_temp_dir = __commonJS({
31
+ "../../node_modules/.pnpm/temp-dir@2.0.0/node_modules/temp-dir/index.js"(exports, module2) {
32
+ "use strict";
33
+ var fs3 = require("fs");
34
+ var os = require("os");
35
+ var tempDirectorySymbol = Symbol.for("__RESOLVED_TEMP_DIRECTORY__");
36
+ if (!global[tempDirectorySymbol]) {
37
+ Object.defineProperty(global, tempDirectorySymbol, {
38
+ value: fs3.realpathSync(os.tmpdir())
39
+ });
40
+ }
41
+ module2.exports = global[tempDirectorySymbol];
42
+ }
43
+ });
44
+
45
+ // index.js
46
+ var import_child_process = require("child_process");
47
+ var import_https = __toESM(require("https"));
48
+ var import_path = __toESM(require("path"));
49
+ var import_commander = require("commander");
50
+ var import_esbuild = __toESM(require("esbuild"));
51
+ var import_fs_extra = __toESM(require("fs-extra"));
52
+ var import_semver = __toESM(require("semver"));
53
+
54
+ // ../../node_modules/.pnpm/tempy@3.0.0/node_modules/tempy/index.js
55
+ var import_node_fs = __toESM(require("node:fs"), 1);
56
+ var import_node_path = __toESM(require("node:path"), 1);
57
+ var import_node_stream = __toESM(require("node:stream"), 1);
58
+ var import_node_util = require("node:util");
59
+
60
+ // ../../node_modules/.pnpm/crypto-random-string@4.0.0/node_modules/crypto-random-string/index.js
61
+ var import_util = require("util");
62
+ var import_crypto = __toESM(require("crypto"), 1);
63
+ var randomBytesAsync = (0, import_util.promisify)(import_crypto.default.randomBytes);
64
+ var urlSafeCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~".split("");
65
+ var numericCharacters = "0123456789".split("");
66
+ var distinguishableCharacters = "CDEHKMPRTUWXY012458".split("");
67
+ var asciiPrintableCharacters = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".split("");
68
+ var alphanumericCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
69
+ var generateForCustomCharacters = (length, characters) => {
70
+ const characterCount = characters.length;
71
+ const maxValidSelector = Math.floor(65536 / characterCount) * characterCount - 1;
72
+ const entropyLength = 2 * Math.ceil(1.1 * length);
73
+ let string = "";
74
+ let stringLength = 0;
75
+ while (stringLength < length) {
76
+ const entropy = import_crypto.default.randomBytes(entropyLength);
77
+ let entropyPosition = 0;
78
+ while (entropyPosition < entropyLength && stringLength < length) {
79
+ const entropyValue = entropy.readUInt16LE(entropyPosition);
80
+ entropyPosition += 2;
81
+ if (entropyValue > maxValidSelector) {
82
+ continue;
83
+ }
84
+ string += characters[entropyValue % characterCount];
85
+ stringLength++;
86
+ }
87
+ }
88
+ return string;
89
+ };
90
+ var generateForCustomCharactersAsync = async (length, characters) => {
91
+ const characterCount = characters.length;
92
+ const maxValidSelector = Math.floor(65536 / characterCount) * characterCount - 1;
93
+ const entropyLength = 2 * Math.ceil(1.1 * length);
94
+ let string = "";
95
+ let stringLength = 0;
96
+ while (stringLength < length) {
97
+ const entropy = await randomBytesAsync(entropyLength);
98
+ let entropyPosition = 0;
99
+ while (entropyPosition < entropyLength && stringLength < length) {
100
+ const entropyValue = entropy.readUInt16LE(entropyPosition);
101
+ entropyPosition += 2;
102
+ if (entropyValue > maxValidSelector) {
103
+ continue;
104
+ }
105
+ string += characters[entropyValue % characterCount];
106
+ stringLength++;
107
+ }
108
+ }
109
+ return string;
110
+ };
111
+ var generateRandomBytes = (byteLength, type, length) => import_crypto.default.randomBytes(byteLength).toString(type).slice(0, length);
112
+ var generateRandomBytesAsync = async (byteLength, type, length) => {
113
+ const buffer = await randomBytesAsync(byteLength);
114
+ return buffer.toString(type).slice(0, length);
115
+ };
116
+ var allowedTypes = /* @__PURE__ */ new Set([
117
+ void 0,
118
+ "hex",
119
+ "base64",
120
+ "url-safe",
121
+ "numeric",
122
+ "distinguishable",
123
+ "ascii-printable",
124
+ "alphanumeric"
125
+ ]);
126
+ var createGenerator = (generateForCustomCharacters2, generateRandomBytes2) => ({ length, type, characters }) => {
127
+ if (!(length >= 0 && Number.isFinite(length))) {
128
+ throw new TypeError("Expected a `length` to be a non-negative finite number");
129
+ }
130
+ if (type !== void 0 && characters !== void 0) {
131
+ throw new TypeError("Expected either `type` or `characters`");
132
+ }
133
+ if (characters !== void 0 && typeof characters !== "string") {
134
+ throw new TypeError("Expected `characters` to be string");
135
+ }
136
+ if (!allowedTypes.has(type)) {
137
+ throw new TypeError(`Unknown type: ${type}`);
138
+ }
139
+ if (type === void 0 && characters === void 0) {
140
+ type = "hex";
141
+ }
142
+ if (type === "hex" || type === void 0 && characters === void 0) {
143
+ return generateRandomBytes2(Math.ceil(length * 0.5), "hex", length);
144
+ }
145
+ if (type === "base64") {
146
+ return generateRandomBytes2(Math.ceil(length * 0.75), "base64", length);
147
+ }
148
+ if (type === "url-safe") {
149
+ return generateForCustomCharacters2(length, urlSafeCharacters);
150
+ }
151
+ if (type === "numeric") {
152
+ return generateForCustomCharacters2(length, numericCharacters);
153
+ }
154
+ if (type === "distinguishable") {
155
+ return generateForCustomCharacters2(length, distinguishableCharacters);
156
+ }
157
+ if (type === "ascii-printable") {
158
+ return generateForCustomCharacters2(length, asciiPrintableCharacters);
159
+ }
160
+ if (type === "alphanumeric") {
161
+ return generateForCustomCharacters2(length, alphanumericCharacters);
162
+ }
163
+ if (characters.length === 0) {
164
+ throw new TypeError("Expected `characters` string length to be greater than or equal to 1");
165
+ }
166
+ if (characters.length > 65536) {
167
+ throw new TypeError("Expected `characters` string length to be less or equal to 65536");
168
+ }
169
+ return generateForCustomCharacters2(length, characters.split(""));
170
+ };
171
+ var cryptoRandomString = createGenerator(generateForCustomCharacters, generateRandomBytes);
172
+ cryptoRandomString.async = createGenerator(generateForCustomCharactersAsync, generateRandomBytesAsync);
173
+ var crypto_random_string_default = cryptoRandomString;
174
+
175
+ // ../../node_modules/.pnpm/unique-string@3.0.0/node_modules/unique-string/index.js
176
+ function uniqueString() {
177
+ return crypto_random_string_default({ length: 32 });
178
+ }
179
+
180
+ // ../../node_modules/.pnpm/tempy@3.0.0/node_modules/tempy/index.js
181
+ var import_temp_dir = __toESM(require_temp_dir(), 1);
182
+ var import_temp_dir2 = __toESM(require_temp_dir(), 1);
183
+ var pipeline = (0, import_node_util.promisify)(import_node_stream.default.pipeline);
184
+ var getPath = (prefix = "") => import_node_path.default.join(import_temp_dir.default, prefix + uniqueString());
185
+ function temporaryFile({ name, extension } = {}) {
186
+ if (name) {
187
+ if (extension !== void 0 && extension !== null) {
188
+ throw new Error("The `name` and `extension` options are mutually exclusive");
189
+ }
190
+ return import_node_path.default.join(temporaryDirectory(), name);
191
+ }
192
+ return getPath() + (extension === void 0 || extension === null ? "" : "." + extension.replace(/^\./, ""));
193
+ }
194
+ function temporaryDirectory({ prefix = "" } = {}) {
195
+ const directory = getPath(prefix);
196
+ import_node_fs.default.mkdirSync(directory);
197
+ return directory;
198
+ }
199
+
200
+ // index.js
201
+ function getJSON(url) {
202
+ return new Promise((resolve, reject) => {
203
+ import_https.default.get(url, (res) => {
204
+ let body = "";
205
+ res.on("data", (chunk) => {
206
+ body += chunk;
207
+ });
208
+ res.on("end", () => {
209
+ try {
210
+ resolve(JSON.parse(body));
211
+ } catch (e) {
212
+ reject(e);
213
+ }
214
+ });
215
+ }).on("error", (e) => {
216
+ reject(e);
217
+ });
218
+ });
219
+ }
220
+ var PackageVersionHelper = class {
221
+ constructor(npmOrg, registryUrl = "https://registry.npmjs.org") {
222
+ this.npmOrg = npmOrg;
223
+ this.registryUrl = registryUrl;
224
+ }
225
+ async fetchPackageVersions(packageName) {
226
+ let res;
227
+ try {
228
+ console.log(`fetching ${this.registryUrl}/@${this.npmOrg}/${packageName}`);
229
+ res = await getJSON(`${this.registryUrl}/@${this.npmOrg}/${packageName}`);
230
+ if (res.error) {
231
+ return null;
232
+ }
233
+ return Object.keys(res.versions);
234
+ } catch (e) {
235
+ return null;
236
+ }
237
+ }
238
+ async getLatestVersion(versions) {
239
+ const sortedVersions = versions.sort((a, b) => import_semver.default.compare(a, b));
240
+ return sortedVersions[sortedVersions.length - 1];
241
+ }
242
+ async getPackageVersion(packageName) {
243
+ const versions = await this.fetchPackageVersions(packageName);
244
+ if (!versions) {
245
+ return null;
246
+ }
247
+ return this.getLatestVersion(versions);
248
+ }
249
+ getPackageName(extensionFolder) {
250
+ const packageJson = JSON.parse(import_fs_extra.default.readFileSync(import_path.default.resolve(extensionFolder, "package.json"), "utf8"));
251
+ let packageName = packageJson.name;
252
+ if (packageName.startsWith("@")) {
253
+ packageName = packageName.split("/")[1];
254
+ }
255
+ packageName = packageName.toLowerCase();
256
+ console.debug(`package name is ${packageName}`);
257
+ return packageName;
258
+ }
259
+ };
260
+ async function publishExtensions(extensionFolder, npmOrganization, npmRegistry) {
261
+ const versionHelper = new PackageVersionHelper(npmOrganization, npmRegistry);
262
+ console.log(`
263
+ Entering ${extensionFolder}
264
+ `);
265
+ (0, import_child_process.execSync)(`cd "${extensionFolder}"`);
266
+ const distDir = import_path.default.resolve(extensionFolder, "dist");
267
+ if (!import_fs_extra.default.existsSync(distDir)) {
268
+ console.error(`dist directory does not exist at ${distDir}`);
269
+ process.exit(1);
270
+ }
271
+ const packageName = versionHelper.getPackageName(extensionFolder);
272
+ let version = await versionHelper.getPackageVersion(packageName);
273
+ if (version) {
274
+ console.info(`current version is ${version}`);
275
+ version = import_semver.default.inc(version, "major");
276
+ } else {
277
+ console.info(`no version found, using 1.0.0`);
278
+ version = "1.0.0";
279
+ }
280
+ updatePackageInfo(distDir, version, npmOrganization);
281
+ console.info(`publishing version ${version}`);
282
+ (0, import_child_process.execSync)(`npm publish --access public`, {
283
+ cwd: distDir
284
+ });
285
+ }
286
+ function getTSConfigPath(extensionDir) {
287
+ const originalTSconfigPath = import_path.default.resolve(extensionDir, "tsconfig.json");
288
+ const tsconfigJson = JSON.parse(import_fs_extra.default.readFileSync(originalTSconfigPath, "utf8"));
289
+ tsconfigJson.compilerOptions.jsx = "react";
290
+ tsconfigJson.compilerOptions.strict = false;
291
+ const tempTSconfigPath = temporaryFile({ extension: "json" });
292
+ import_fs_extra.default.writeFileSync(tempTSconfigPath, JSON.stringify(tsconfigJson, null, 2));
293
+ return tempTSconfigPath;
294
+ }
295
+ async function buildExtension(extensionDir, outputFolder) {
296
+ const packageJson = JSON.parse(import_fs_extra.default.readFileSync(import_path.default.resolve(extensionDir, "package.json"), "utf8"));
297
+ const commandNames = packageJson.commands.map((command) => command.name);
298
+ const supportedExts = [".js", ".ts", ".tsx", ".jsx"];
299
+ const tsconfigPath = getTSConfigPath(extensionDir);
300
+ const esbuildConfigs = commandNames.map((commandName) => {
301
+ const files = import_fs_extra.default.readdirSync(import_path.default.join(extensionDir, "src"));
302
+ const entry = files.find((file) => {
303
+ return supportedExts.some((ext) => file === `${commandName}${ext}`);
304
+ });
305
+ if (!entry) {
306
+ throw new Error(`No entry point found for ${commandName}`);
307
+ }
308
+ const entryPath = import_path.default.join(extensionDir, "src", entry);
309
+ const outputPath = import_path.default.join(outputFolder, `${commandName}.js`);
310
+ console.debug(`entry point for ${commandName} is ${entryPath}`);
311
+ return {
312
+ input: entryPath,
313
+ output: outputPath
314
+ };
315
+ });
316
+ console.info(`entry points [${esbuildConfigs.map((config) => config.input).join(", ")}]`);
317
+ for (const config of esbuildConfigs) {
318
+ await import_esbuild.default.build({
319
+ entryPoints: [config.input],
320
+ bundle: true,
321
+ platform: "node",
322
+ outfile: config.output,
323
+ external: ["@raycast/api", "react"],
324
+ jsx: "transform",
325
+ jsxFactory: "_jsx",
326
+ jsxFragment: "_jsxFragment",
327
+ tsconfig: tsconfigPath,
328
+ minify: true
329
+ });
330
+ }
331
+ import_fs_extra.default.copyFileSync(import_path.default.join(extensionDir, "package.json"), import_path.default.join(outputFolder, "package.json"));
332
+ const assetsDir = import_path.default.join(extensionDir, "assets");
333
+ if (import_fs_extra.default.existsSync(assetsDir)) {
334
+ import_fs_extra.default.copySync(assetsDir, import_path.default.join(outputFolder, "assets"));
335
+ }
336
+ }
337
+ function updatePackageInfo(targetDir, version, npmOrganization) {
338
+ const packagePath = import_path.default.join(targetDir, "package.json");
339
+ const packageJson = JSON.parse(import_fs_extra.default.readFileSync(packagePath, "utf8"));
340
+ if (!packageJson.name.startsWith(`@${npmOrganization}`)) {
341
+ packageJson.name = `@${npmOrganization}/${packageJson.name}`;
342
+ }
343
+ packageJson.repository = "https://github.com/BlastLauncher/extensions.git";
344
+ packageJson.version = version;
345
+ import_fs_extra.default.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2), "utf8");
346
+ }
347
+ var program = new import_commander.Command();
348
+ program.name("blast-cli").description(`CLI for Blast Launcher
349
+
350
+ Example Usage:
351
+ blast build ./extensions/todo-list -o ./extensions/todo-list/dist
352
+ blast publish ./extensions/todo-list blast-extensions`);
353
+ program.command("publish").description("Publish extensions").argument("<path>", "Path to extensions folder").argument("<organization>", "NPM organization to publish to, e.g. blast-extensions").option("-r, --registry <registry>", "NPM registry to publish to", "https://registry.npmjs.org").action((dir, organization, options) => {
354
+ return publishExtensions(dir, organization, options.registry);
355
+ });
356
+ program.command("build").description("Build extensions").argument("<path>", "Path to extension").option("-o, --output <output>", "Output directory").action((path3, options) => {
357
+ return buildExtension(path3, options.output);
358
+ });
359
+ program.parse();
package/index.js ADDED
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from "child_process";
3
+ import https from "https";
4
+ import path from "path";
5
+
6
+ import { Command } from "commander";
7
+ import esbuild from "esbuild";
8
+ import fs from "fs-extra";
9
+ import semver from "semver";
10
+ import { temporaryFile } from "tempy";
11
+
12
+ function getJSON(url) {
13
+ return new Promise((resolve, reject) => {
14
+ https
15
+ .get(url, (res) => {
16
+ let body = "";
17
+ res.on("data", (chunk) => {
18
+ body += chunk;
19
+ });
20
+ res.on("end", () => {
21
+ try {
22
+ resolve(JSON.parse(body));
23
+ } catch (e) {
24
+ reject(e);
25
+ }
26
+ });
27
+ })
28
+ .on("error", (e) => {
29
+ reject(e);
30
+ });
31
+ });
32
+ }
33
+
34
+ class PackageVersionHelper {
35
+ constructor(npmOrg, registryUrl = "https://registry.npmjs.org") {
36
+ this.npmOrg = npmOrg;
37
+ this.registryUrl = registryUrl;
38
+ }
39
+
40
+ async fetchPackageVersions(packageName) {
41
+ let res;
42
+ try {
43
+ console.log(`fetching ${this.registryUrl}/@${this.npmOrg}/${packageName}`);
44
+ res = await getJSON(`${this.registryUrl}/@${this.npmOrg}/${packageName}`);
45
+
46
+ if (res.error) {
47
+ return null;
48
+ }
49
+
50
+ return Object.keys(res.versions);
51
+ } catch (e) {
52
+ // pacakge not found
53
+ return null;
54
+ }
55
+ }
56
+
57
+ async getLatestVersion(versions) {
58
+ const sortedVersions = versions.sort((a, b) => semver.compare(a, b));
59
+ return sortedVersions[sortedVersions.length - 1];
60
+ }
61
+
62
+ async getPackageVersion(packageName) {
63
+ const versions = await this.fetchPackageVersions(packageName);
64
+ if (!versions) {
65
+ return null;
66
+ }
67
+
68
+ return this.getLatestVersion(versions);
69
+ }
70
+
71
+ getPackageName(extensionFolder) {
72
+ const packageJson = JSON.parse(fs.readFileSync(path.resolve(extensionFolder, "package.json"), "utf8"));
73
+ let packageName = packageJson.name;
74
+ if (packageName.startsWith("@")) {
75
+ packageName = packageName.split("/")[1];
76
+ }
77
+ packageName = packageName.toLowerCase();
78
+
79
+ console.debug(`package name is ${packageName}`);
80
+
81
+ return packageName;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * @param {string} extensionFolder
87
+ * @param {string} npmOrganization
88
+ * @param {string} npmRegistry
89
+ */
90
+ async function publishExtensions(extensionFolder, npmOrganization, npmRegistry) {
91
+ const versionHelper = new PackageVersionHelper(npmOrganization, npmRegistry);
92
+
93
+ console.log(`\nEntering ${extensionFolder}\n`);
94
+ execSync(`cd "${extensionFolder}"`);
95
+
96
+ const distDir = path.resolve(extensionFolder, "dist");
97
+
98
+ if (!fs.existsSync(distDir)) {
99
+ console.error(`dist directory does not exist at ${distDir}`);
100
+ process.exit(1);
101
+ }
102
+
103
+ const packageName = versionHelper.getPackageName(extensionFolder);
104
+ let version = await versionHelper.getPackageVersion(packageName);
105
+ if (version) {
106
+ console.info(`current version is ${version}`);
107
+ version = semver.inc(version, "major");
108
+ } else {
109
+ console.info(`no version found, using 1.0.0`);
110
+ version = "1.0.0";
111
+ }
112
+
113
+ updatePackageInfo(distDir, version, npmOrganization);
114
+
115
+ console.info(`publishing version ${version}`);
116
+
117
+ // publish to npm
118
+ execSync(`npm publish --access public`, {
119
+ cwd: distDir,
120
+ });
121
+ }
122
+
123
+ function getTSConfigPath(extensionDir) {
124
+ const originalTSconfigPath = path.resolve(extensionDir, "tsconfig.json");
125
+ const tsconfigJson = JSON.parse(fs.readFileSync(originalTSconfigPath, "utf8"));
126
+
127
+ tsconfigJson.compilerOptions.jsx = "react";
128
+ tsconfigJson.compilerOptions.strict = false;
129
+
130
+ const tempTSconfigPath = temporaryFile({ extension: "json" });
131
+
132
+ fs.writeFileSync(tempTSconfigPath, JSON.stringify(tsconfigJson, null, 2));
133
+
134
+ return tempTSconfigPath;
135
+ }
136
+
137
+ async function buildExtension(extensionDir, outputFolder) {
138
+ // Gather entry points from target extension's packages.json
139
+ const packageJson = JSON.parse(fs.readFileSync(path.resolve(extensionDir, "package.json"), "utf8"));
140
+
141
+ const commandNames = packageJson.commands.map((command) => command.name);
142
+
143
+ const supportedExts = [".js", ".ts", ".tsx", ".jsx"];
144
+
145
+ const tsconfigPath = getTSConfigPath(extensionDir);
146
+
147
+ const esbuildConfigs = commandNames.map((commandName) => {
148
+ const files = fs.readdirSync(path.join(extensionDir, "src"));
149
+ const entry = files.find((file) => {
150
+ return supportedExts.some((ext) => file === `${commandName}${ext}`);
151
+ });
152
+
153
+ if (!entry) {
154
+ throw new Error(`No entry point found for ${commandName}`);
155
+ }
156
+
157
+ const entryPath = path.join(extensionDir, "src", entry);
158
+ const outputPath = path.join(outputFolder, `${commandName}.js`);
159
+
160
+ console.debug(`entry point for ${commandName} is ${entryPath}`);
161
+
162
+ return {
163
+ input: entryPath,
164
+ output: outputPath,
165
+ };
166
+ });
167
+
168
+ console.info(`entry points [${esbuildConfigs.map((config) => config.input).join(", ")}]`);
169
+
170
+ // Build each entry point
171
+ for (const config of esbuildConfigs) {
172
+ await esbuild.build({
173
+ entryPoints: [config.input],
174
+ bundle: true,
175
+ platform: "node",
176
+ outfile: config.output,
177
+ external: ["@raycast/api", "react"],
178
+ jsx: "transform",
179
+ jsxFactory: "_jsx",
180
+ jsxFragment: "_jsxFragment",
181
+ tsconfig: tsconfigPath,
182
+ minify: true,
183
+ });
184
+ }
185
+
186
+ // copy the package.json to the output folder
187
+ fs.copyFileSync(path.join(extensionDir, "package.json"), path.join(outputFolder, "package.json"));
188
+
189
+ // copy assets directory to the output folder
190
+ const assetsDir = path.join(extensionDir, "assets");
191
+ if (fs.existsSync(assetsDir)) {
192
+ fs.copySync(assetsDir, path.join(outputFolder, "assets"));
193
+ }
194
+ }
195
+
196
+ /**
197
+ * @param {string} targetDir
198
+ * @param {string} version
199
+ * @param {string} npmOrganization
200
+ * @returns {void}
201
+ * add extra information to package.json
202
+ **/
203
+ function updatePackageInfo(targetDir, version, npmOrganization) {
204
+ const packagePath = path.join(targetDir, "package.json");
205
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
206
+
207
+ // prepend @BlastLauncher to name if it's not already there
208
+ if (!packageJson.name.startsWith(`@${npmOrganization}`)) {
209
+ packageJson.name = `@${npmOrganization}/${packageJson.name}`;
210
+ }
211
+
212
+ // add repository field
213
+ packageJson.repository = "https://github.com/BlastLauncher/extensions.git";
214
+
215
+ // add version
216
+ packageJson.version = version;
217
+
218
+ // write package.json
219
+ fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2), "utf8");
220
+ }
221
+
222
+ const program = new Command();
223
+
224
+ program.name("blast-cli").description(`CLI for Blast Launcher
225
+
226
+ Example Usage:
227
+ blast build ./extensions/todo-list -o ./extensions/todo-list/dist
228
+ blast publish ./extensions/todo-list blast-extensions`);
229
+
230
+ program
231
+ .command("publish")
232
+ .description("Publish extensions")
233
+ .argument("<path>", "Path to extensions folder")
234
+ .argument("<organization>", "NPM organization to publish to, e.g. blast-extensions")
235
+ .option("-r, --registry <registry>", "NPM registry to publish to", "https://registry.npmjs.org")
236
+ .action((dir, organization, options) => {
237
+ return publishExtensions(dir, organization, options.registry);
238
+ });
239
+
240
+ program
241
+ .command("build")
242
+ .description("Build extensions")
243
+ .argument("<path>", "Path to extension")
244
+ .option("-o, --output <output>", "Output directory")
245
+ .action((path, options) => {
246
+ return buildExtension(path, options.output);
247
+ });
248
+
249
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@blastlauncher/cli",
3
+ "version": "1.0.1",
4
+ "description": "Blast CLI Tools",
5
+ "main": "dist/index.cjs",
6
+ "bin": {
7
+ "blast": "dist/index.cjs"
8
+ },
9
+ "dependencies": {
10
+ "commander": "^10.0.0",
11
+ "esbuild": "^0.17.5",
12
+ "esbuild-node-externals": "^1.6.0",
13
+ "fast-glob": "^3.2.12",
14
+ "fs-extra": "^11.1.0",
15
+ "semver": "^7.3.8",
16
+ "tempy": "^3.0.0"
17
+ },
18
+ "repository": "https://github.com/BlastLauncher/blast.git",
19
+ "devDependencies": {
20
+ "@types/fs-extra": "^9.0.13",
21
+ "pkg": "^5.8.0"
22
+ },
23
+ "pkg": {
24
+ "outputPath": "dist",
25
+ "outputName": "blast"
26
+ },
27
+ "scripts": {
28
+ "build": "node build.js",
29
+ "package": "pkg ."
30
+ }
31
+ }