@swipt/cli 0.1.0-nightly.31 → 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 +94 -66
- package/package.json +2 -2
package/cli.mjs
CHANGED
|
@@ -265,71 +265,104 @@ async function buildCommand(root) {
|
|
|
265
265
|
|
|
266
266
|
const manifestEntry = /^(?:[^/]+\/)?manifest\.json$/;
|
|
267
267
|
|
|
268
|
-
function
|
|
269
|
-
const
|
|
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
|
-
names[language] = rest.join("=");
|
|
276
275
|
}
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
shownIcon = { color, symbol: symbol.join("/") };
|
|
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');
|
|
284
282
|
}
|
|
285
283
|
let prefix;
|
|
286
|
-
if (
|
|
284
|
+
if (config.url !== undefined) {
|
|
287
285
|
try {
|
|
288
|
-
prefix = new URL(
|
|
286
|
+
prefix = new URL(config.url.endsWith("/") ? config.url : `${config.url}/`);
|
|
289
287
|
} catch {
|
|
290
|
-
throw new
|
|
288
|
+
throw new Error('"source.url" in package.json is not an absolute URL');
|
|
291
289
|
}
|
|
292
290
|
}
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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);
|
|
302
312
|
}
|
|
303
|
-
|
|
304
|
-
|
|
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}`);
|
|
305
340
|
}
|
|
306
|
-
|
|
307
|
-
swiplets.set(manifest.id, {
|
|
308
|
-
file,
|
|
309
|
-
entry: {
|
|
310
|
-
...manifest,
|
|
311
|
-
url: prefix ? new URL(path.basename(file), prefix).href : path.basename(file),
|
|
312
|
-
size: data.length,
|
|
313
|
-
sha256: createHash("sha256").update(data).digest("hex"),
|
|
314
|
-
},
|
|
315
|
-
});
|
|
341
|
+
swiplets.set(id, read);
|
|
316
342
|
}
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
)
|
|
328
|
-
if (
|
|
329
|
-
fs.writeFileSync(
|
|
330
|
-
} else {
|
|
331
|
-
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);
|
|
332
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")));
|
|
333
366
|
}
|
|
334
367
|
|
|
335
368
|
const idOf = name => name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+/, "").slice(0, 128).replace(/[._-]+$/, "") || "swiplet";
|
|
@@ -426,26 +459,23 @@ function createCommand(directory) {
|
|
|
426
459
|
|
|
427
460
|
const usage = `usage: swiplet create <directory>
|
|
428
461
|
swiplet build
|
|
429
|
-
swiplet source [
|
|
430
|
-
[--url <address>] [--output <file>] <swiplet>...
|
|
462
|
+
swiplet source [<swiplet>...]
|
|
431
463
|
|
|
432
464
|
create make a swiplet project in a new or empty directory
|
|
433
465
|
build bundle the project in this directory into dist/<swiplet.id>.swiplet
|
|
434
|
-
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
|
|
435
468
|
|
|
436
469
|
Type-check a project with its "check" script.`;
|
|
437
470
|
|
|
438
|
-
const commands = { create: 1, build: 0, source:
|
|
471
|
+
const commands = { create: 1, build: 0, source: null };
|
|
439
472
|
|
|
440
473
|
class UsageError extends Error {}
|
|
441
474
|
|
|
442
475
|
try {
|
|
443
476
|
let parsed;
|
|
444
477
|
try {
|
|
445
|
-
parsed = parseArgs({ allowPositionals: true, options: {
|
|
446
|
-
help: { type: "boolean", short: "h" }, name: { type: "string" }, "localized-name": { type: "string", multiple: true }, icon: { type: "string" },
|
|
447
|
-
image: { type: "string" }, url: { type: "string" }, output: { type: "string" },
|
|
448
|
-
} });
|
|
478
|
+
parsed = parseArgs({ allowPositionals: true, options: { help: { type: "boolean", short: "h" } } });
|
|
449
479
|
} catch (error) {
|
|
450
480
|
throw new UsageError(error.message);
|
|
451
481
|
}
|
|
@@ -456,14 +486,12 @@ try {
|
|
|
456
486
|
throw new UsageError("a command is missing");
|
|
457
487
|
} else if (!Object.hasOwn(commands, command)) {
|
|
458
488
|
throw new UsageError(`unknown command ${JSON.stringify(command)}`);
|
|
459
|
-
} else if (commands[command]
|
|
460
|
-
throw new UsageError(
|
|
461
|
-
commands[command] === 0 ? `${command} takes no arguments` : commands[command] < 0 ? `${command} takes one or more swiplets` : `${command} takes one directory`,
|
|
462
|
-
);
|
|
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`);
|
|
463
491
|
} else if (command === "create") {
|
|
464
492
|
createCommand(rest[0]);
|
|
465
493
|
} else if (command === "source") {
|
|
466
|
-
sourceCommand(
|
|
494
|
+
sourceCommand(process.cwd(), rest);
|
|
467
495
|
} else {
|
|
468
496
|
await buildCommand(process.cwd());
|
|
469
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
|
}
|