@swipt/cli 0.1.0-nightly.30 → 0.1.0-nightly.32
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/cli.mjs +95 -56
- package/package.json +2 -2
package/cli.mjs
CHANGED
|
@@ -265,62 +265,104 @@ async function buildCommand(root) {
|
|
|
265
265
|
|
|
266
266
|
const manifestEntry = /^(?:[^/]+\/)?manifest\.json$/;
|
|
267
267
|
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (
|
|
273
|
-
throw new
|
|
268
|
+
function loadSource(root) {
|
|
269
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
270
|
+
const config = manifest.source ?? {};
|
|
271
|
+
for (const field of ["name", "image", "url"]) {
|
|
272
|
+
if (config[field] !== undefined && typeof config[field] !== "string") {
|
|
273
|
+
throw new Error(`"source.${field}" in package.json must be a string`);
|
|
274
274
|
}
|
|
275
|
-
|
|
275
|
+
}
|
|
276
|
+
const localizedNames = config.localizedNames ?? {};
|
|
277
|
+
if (localizedNames === null || typeof localizedNames !== "object" || !Object.values(localizedNames).every(name => typeof name === "string")) {
|
|
278
|
+
throw new Error('"source.localizedNames" in package.json must map languages to names');
|
|
279
|
+
}
|
|
280
|
+
if (config.icon !== undefined && (typeof config.icon?.color !== "string" || typeof config.icon?.symbol !== "string")) {
|
|
281
|
+
throw new Error('"source.icon" in package.json must have a color and a symbol');
|
|
276
282
|
}
|
|
277
283
|
let prefix;
|
|
278
|
-
if (
|
|
284
|
+
if (config.url !== undefined) {
|
|
279
285
|
try {
|
|
280
|
-
prefix = new URL(
|
|
286
|
+
prefix = new URL(config.url.endsWith("/") ? config.url : `${config.url}/`);
|
|
281
287
|
} catch {
|
|
282
|
-
throw new
|
|
288
|
+
throw new Error('"source.url" in package.json is not an absolute URL');
|
|
283
289
|
}
|
|
284
290
|
}
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
const workspaces = Array.isArray(manifest.workspaces) ? manifest.workspaces : manifest.workspaces?.packages ?? [];
|
|
292
|
+
return { root, config, localizedNames, prefix, workspaces };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function workspaceSwiplets(source) {
|
|
296
|
+
const files = [];
|
|
297
|
+
for (const pattern of source.workspaces) {
|
|
298
|
+
for (const directory of fs.globSync(pattern, { cwd: source.root }).sort()) {
|
|
299
|
+
const project = path.join(source.root, directory, "package.json");
|
|
300
|
+
if (!fs.existsSync(project)) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const id = JSON.parse(fs.readFileSync(project, "utf8")).swiplet?.id;
|
|
304
|
+
if (typeof id !== "string") {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const built = path.join(source.root, directory, "dist", `${id}.swiplet`);
|
|
308
|
+
if (!fs.existsSync(built)) {
|
|
309
|
+
throw new Error(`${path.relative(source.root, built)} is missing; build ${directory} first`);
|
|
310
|
+
}
|
|
311
|
+
files.push(built);
|
|
294
312
|
}
|
|
295
|
-
|
|
296
|
-
|
|
313
|
+
}
|
|
314
|
+
return files;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function readSwiplet(file) {
|
|
318
|
+
const entry = execFileSync("unzip", ["-Z1", file], { encoding: "utf8" }).split("\n").filter(name => manifestEntry.test(name)).sort((a, b) => a.length - b.length)[0];
|
|
319
|
+
if (entry === undefined) {
|
|
320
|
+
throw new Error(`${file} has no manifest.json`);
|
|
321
|
+
}
|
|
322
|
+
const manifest = JSON.parse(execFileSync("unzip", ["-p", file, entry], { encoding: "utf8" }));
|
|
323
|
+
if (typeof manifest.id !== "string" || !idPattern.test(manifest.id)) {
|
|
324
|
+
throw new Error(`${file} has no valid swiplet id in its manifest`);
|
|
325
|
+
}
|
|
326
|
+
return { file, manifest, data: fs.readFileSync(file) };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function sourceCommand(root, given) {
|
|
330
|
+
const source = loadSource(root);
|
|
331
|
+
const files = given.length > 0 ? given.map(file => path.resolve(file)) : workspaceSwiplets(source);
|
|
332
|
+
if (files.length === 0) {
|
|
333
|
+
throw new Error("package.json lists no workspaces with swiplets; name the swiplets to list");
|
|
334
|
+
}
|
|
335
|
+
const swiplets = new Map();
|
|
336
|
+
for (const read of files.map(readSwiplet)) {
|
|
337
|
+
const { id } = read.manifest;
|
|
338
|
+
if (swiplets.has(id)) {
|
|
339
|
+
throw new Error(`${id} is both ${swiplets.get(id).file} and ${read.file}`);
|
|
297
340
|
}
|
|
298
|
-
|
|
299
|
-
swiplets.set(manifest.id, {
|
|
300
|
-
file,
|
|
301
|
-
entry: {
|
|
302
|
-
...manifest,
|
|
303
|
-
url: prefix ? new URL(path.basename(file), prefix).href : path.basename(file),
|
|
304
|
-
size: data.length,
|
|
305
|
-
sha256: createHash("sha256").update(data).digest("hex"),
|
|
306
|
-
},
|
|
307
|
-
});
|
|
341
|
+
swiplets.set(id, read);
|
|
308
342
|
}
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
process.stdout.write(listed);
|
|
343
|
+
const image = source.config.image === undefined ? undefined : { name: path.basename(source.config.image), data: fs.readFileSync(path.resolve(root, source.config.image)) };
|
|
344
|
+
const located = name => (source.prefix ? new URL(name, source.prefix).href : name);
|
|
345
|
+
const dist = path.join(root, "dist");
|
|
346
|
+
fs.rmSync(dist, { recursive: true, force: true });
|
|
347
|
+
fs.mkdirSync(dist, { recursive: true });
|
|
348
|
+
const entries = [...swiplets.keys()].sort().map(id => {
|
|
349
|
+
const { manifest, data } = swiplets.get(id);
|
|
350
|
+
const name = `${id}.swiplet`;
|
|
351
|
+
fs.writeFileSync(path.join(dist, name), data);
|
|
352
|
+
return { ...manifest, url: located(name), size: data.length, sha256: createHash("sha256").update(data).digest("hex") };
|
|
353
|
+
});
|
|
354
|
+
if (image) {
|
|
355
|
+
fs.writeFileSync(path.join(dist, image.name), image.data);
|
|
323
356
|
}
|
|
357
|
+
const index = {
|
|
358
|
+
...(source.config.name ? { name: source.config.name } : {}),
|
|
359
|
+
...(Object.keys(source.localizedNames).length > 0 ? { localizedNames: source.localizedNames } : {}),
|
|
360
|
+
...(source.config.icon ? { icon: { color: source.config.icon.color, symbol: source.config.icon.symbol } } : {}),
|
|
361
|
+
...(image ? { image: located(image.name) } : {}),
|
|
362
|
+
swiplets: entries,
|
|
363
|
+
};
|
|
364
|
+
fs.writeFileSync(path.join(dist, "swiplets.json"), `${JSON.stringify(index, null, 2)}\n`);
|
|
365
|
+
console.log(path.relative(process.cwd(), path.join(dist, "swiplets.json")));
|
|
324
366
|
}
|
|
325
367
|
|
|
326
368
|
const idOf = name => name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+/, "").slice(0, 128).replace(/[._-]+$/, "") || "swiplet";
|
|
@@ -417,24 +459,23 @@ function createCommand(directory) {
|
|
|
417
459
|
|
|
418
460
|
const usage = `usage: swiplet create <directory>
|
|
419
461
|
swiplet build
|
|
420
|
-
swiplet source [
|
|
462
|
+
swiplet source [<swiplet>...]
|
|
421
463
|
|
|
422
464
|
create make a swiplet project in a new or empty directory
|
|
423
465
|
build bundle the project in this directory into dist/<swiplet.id>.swiplet
|
|
424
|
-
source
|
|
466
|
+
source gather the built swiplets of this directory's workspaces, or the given ones, into dist/
|
|
467
|
+
with a swiplets.json described by "source" in package.json
|
|
425
468
|
|
|
426
469
|
Type-check a project with its "check" script.`;
|
|
427
470
|
|
|
428
|
-
const commands = { create: 1, build: 0, source:
|
|
471
|
+
const commands = { create: 1, build: 0, source: null };
|
|
429
472
|
|
|
430
473
|
class UsageError extends Error {}
|
|
431
474
|
|
|
432
475
|
try {
|
|
433
476
|
let parsed;
|
|
434
477
|
try {
|
|
435
|
-
parsed = parseArgs({ allowPositionals: true, options: {
|
|
436
|
-
help: { type: "boolean", short: "h" }, name: { type: "string" }, icon: { type: "string" }, image: { type: "string" }, url: { type: "string" }, output: { type: "string" },
|
|
437
|
-
} });
|
|
478
|
+
parsed = parseArgs({ allowPositionals: true, options: { help: { type: "boolean", short: "h" } } });
|
|
438
479
|
} catch (error) {
|
|
439
480
|
throw new UsageError(error.message);
|
|
440
481
|
}
|
|
@@ -445,14 +486,12 @@ try {
|
|
|
445
486
|
throw new UsageError("a command is missing");
|
|
446
487
|
} else if (!Object.hasOwn(commands, command)) {
|
|
447
488
|
throw new UsageError(`unknown command ${JSON.stringify(command)}`);
|
|
448
|
-
} else if (commands[command]
|
|
449
|
-
throw new UsageError(
|
|
450
|
-
commands[command] === 0 ? `${command} takes no arguments` : commands[command] < 0 ? `${command} takes one or more swiplets` : `${command} takes one directory`,
|
|
451
|
-
);
|
|
489
|
+
} else if (commands[command] !== null && rest.length !== commands[command]) {
|
|
490
|
+
throw new UsageError(commands[command] === 0 ? `${command} takes no arguments` : `${command} takes one directory`);
|
|
452
491
|
} else if (command === "create") {
|
|
453
492
|
createCommand(rest[0]);
|
|
454
493
|
} else if (command === "source") {
|
|
455
|
-
sourceCommand(
|
|
494
|
+
sourceCommand(process.cwd(), rest);
|
|
456
495
|
} else {
|
|
457
496
|
await buildCommand(process.cwd());
|
|
458
497
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swipt/cli",
|
|
3
|
-
"version": "0.1.0-nightly.
|
|
3
|
+
"version": "0.1.0-nightly.32",
|
|
4
4
|
"license": "GPL-3.0-or-later",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"esbuild": "^0.28.2"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@swipt/api": "0.1.0-nightly.
|
|
24
|
+
"@swipt/api": "0.1.0-nightly.32"
|
|
25
25
|
}
|
|
26
26
|
}
|