@getmonoceros/workbench 1.9.5 → 1.9.7
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/dist/bin.js +189 -160
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -311,7 +311,7 @@ import { consola as consola2 } from "consola";
|
|
|
311
311
|
import { promises as fs8 } from "fs";
|
|
312
312
|
import { consola } from "consola";
|
|
313
313
|
import { createPatch } from "diff";
|
|
314
|
-
import
|
|
314
|
+
import path8 from "path";
|
|
315
315
|
|
|
316
316
|
// src/config/io.ts
|
|
317
317
|
import { promises as fs } from "fs";
|
|
@@ -2237,6 +2237,153 @@ import {
|
|
|
2237
2237
|
YAMLMap as YAMLMap2,
|
|
2238
2238
|
YAMLSeq
|
|
2239
2239
|
} from "yaml";
|
|
2240
|
+
|
|
2241
|
+
// src/init/manifest.ts
|
|
2242
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
2243
|
+
import path7 from "path";
|
|
2244
|
+
function resolveManifestPath(name, checkoutRoot) {
|
|
2245
|
+
if (checkoutRoot) {
|
|
2246
|
+
const checkoutPath = path7.join(
|
|
2247
|
+
checkoutRoot,
|
|
2248
|
+
"images",
|
|
2249
|
+
"features",
|
|
2250
|
+
name,
|
|
2251
|
+
"devcontainer-feature.json"
|
|
2252
|
+
);
|
|
2253
|
+
if (existsSync4(checkoutPath)) return checkoutPath;
|
|
2254
|
+
}
|
|
2255
|
+
const bundlePath = path7.join(
|
|
2256
|
+
bundledFeaturesDir(),
|
|
2257
|
+
name,
|
|
2258
|
+
"devcontainer-feature.json"
|
|
2259
|
+
);
|
|
2260
|
+
if (existsSync4(bundlePath)) return bundlePath;
|
|
2261
|
+
return null;
|
|
2262
|
+
}
|
|
2263
|
+
function loadFeatureManifestSummary(ref, checkoutRoot = workbenchCheckoutRoot()) {
|
|
2264
|
+
const match = matchMonocerosFeature(ref);
|
|
2265
|
+
if (!match) return void 0;
|
|
2266
|
+
const manifestPath = resolveManifestPath(match.name, checkoutRoot);
|
|
2267
|
+
if (!manifestPath) return void 0;
|
|
2268
|
+
try {
|
|
2269
|
+
const text = readFileSync2(manifestPath, "utf8");
|
|
2270
|
+
const parsed = JSON.parse(text);
|
|
2271
|
+
const rawHints = parsed["x-monoceros"]?.optionHints;
|
|
2272
|
+
const optionHints = Array.isArray(rawHints) ? rawHints.filter(
|
|
2273
|
+
(x) => typeof x === "string" && x.length > 0
|
|
2274
|
+
) : [];
|
|
2275
|
+
const rawNotes = parsed["x-monoceros"]?.usageNotes;
|
|
2276
|
+
const usageNotes = Array.isArray(rawNotes) ? rawNotes.filter(
|
|
2277
|
+
(x) => typeof x === "string" && x.length > 0
|
|
2278
|
+
) : [];
|
|
2279
|
+
const optionDescriptions = {};
|
|
2280
|
+
const optionTypes = {};
|
|
2281
|
+
const optionNames = [];
|
|
2282
|
+
if (parsed.options) {
|
|
2283
|
+
for (const [key, opt] of Object.entries(parsed.options)) {
|
|
2284
|
+
if (!opt || typeof opt !== "object") continue;
|
|
2285
|
+
optionNames.push(key);
|
|
2286
|
+
if (typeof opt.description === "string" && opt.description.length > 0) {
|
|
2287
|
+
optionDescriptions[key] = opt.description;
|
|
2288
|
+
}
|
|
2289
|
+
if (opt.type === "boolean") {
|
|
2290
|
+
optionTypes[key] = "boolean";
|
|
2291
|
+
} else if (opt.type === "string") {
|
|
2292
|
+
optionTypes[key] = "string";
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
const name = typeof parsed.name === "string" ? parsed.name : "";
|
|
2297
|
+
const description = typeof parsed.description === "string" ? parsed.description : "";
|
|
2298
|
+
const rawUrl = typeof parsed.documentationURL === "string" ? parsed.documentationURL.trim() : "";
|
|
2299
|
+
const documentationURL = rawUrl.length > 0 && rawUrl.toLowerCase() !== "tbd" ? rawUrl : void 0;
|
|
2300
|
+
return {
|
|
2301
|
+
name,
|
|
2302
|
+
description,
|
|
2303
|
+
documentationURL,
|
|
2304
|
+
optionHints,
|
|
2305
|
+
optionDescriptions,
|
|
2306
|
+
optionNames,
|
|
2307
|
+
optionTypes,
|
|
2308
|
+
usageNotes
|
|
2309
|
+
};
|
|
2310
|
+
} catch {
|
|
2311
|
+
return void 0;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
// src/init/feature-doc.ts
|
|
2316
|
+
function buildFeatureHeaderLines(summary, width) {
|
|
2317
|
+
const paragraphs = buildHeaderParagraphs(summary);
|
|
2318
|
+
const wrapped = [];
|
|
2319
|
+
for (const para of paragraphs) {
|
|
2320
|
+
for (const line of wrapToComment(para, width)) {
|
|
2321
|
+
wrapped.push(line);
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
return wrapped;
|
|
2325
|
+
}
|
|
2326
|
+
function buildFeatureHeaderCommentBefore(summary, width) {
|
|
2327
|
+
const lines = buildFeatureHeaderLines(summary, width);
|
|
2328
|
+
return lines.map((l) => ` ${l}`).join("\n");
|
|
2329
|
+
}
|
|
2330
|
+
function buildHeaderParagraphs(summary) {
|
|
2331
|
+
if (!summary) return [];
|
|
2332
|
+
const out = [];
|
|
2333
|
+
const tagline = summary.name?.trim();
|
|
2334
|
+
const description = summary.description?.trim();
|
|
2335
|
+
if (tagline && description) {
|
|
2336
|
+
out.push(`${tagline} \u2014 ${description}`);
|
|
2337
|
+
} else if (tagline) {
|
|
2338
|
+
out.push(tagline);
|
|
2339
|
+
} else if (description) {
|
|
2340
|
+
out.push(description);
|
|
2341
|
+
}
|
|
2342
|
+
for (const note of summary.usageNotes) {
|
|
2343
|
+
const trimmed = note.trim();
|
|
2344
|
+
if (trimmed.length > 0) out.push(trimmed);
|
|
2345
|
+
}
|
|
2346
|
+
if (summary.optionHints.length > 0) {
|
|
2347
|
+
const parts = summary.optionHints.map((key) => {
|
|
2348
|
+
const desc = summary.optionDescriptions[key];
|
|
2349
|
+
const short = desc ? shortenOptionDescription(desc) : void 0;
|
|
2350
|
+
return short ? `${key} (${short})` : key;
|
|
2351
|
+
});
|
|
2352
|
+
out.push(`Options: ${parts.join(", ")}.`);
|
|
2353
|
+
}
|
|
2354
|
+
if (summary.documentationURL) {
|
|
2355
|
+
out.push(`See ${summary.documentationURL} for further information.`);
|
|
2356
|
+
}
|
|
2357
|
+
return out;
|
|
2358
|
+
}
|
|
2359
|
+
function shortenOptionDescription(desc) {
|
|
2360
|
+
const firstSentence = desc.split(/(?<=[.!?])\s+/)[0]?.trim() ?? desc.trim();
|
|
2361
|
+
return firstSentence.replace(/[.!?]+$/, "").trim();
|
|
2362
|
+
}
|
|
2363
|
+
function wrapToComment(text, width) {
|
|
2364
|
+
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
2365
|
+
if (words.length === 0) return [""];
|
|
2366
|
+
const usable = Math.max(width, 20);
|
|
2367
|
+
const lines = [];
|
|
2368
|
+
let current = "";
|
|
2369
|
+
for (const w of words) {
|
|
2370
|
+
if (current.length === 0) {
|
|
2371
|
+
current = w;
|
|
2372
|
+
continue;
|
|
2373
|
+
}
|
|
2374
|
+
if (current.length + 1 + w.length <= usable) {
|
|
2375
|
+
current += " " + w;
|
|
2376
|
+
} else {
|
|
2377
|
+
lines.push(current);
|
|
2378
|
+
current = w;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
if (current.length > 0) lines.push(current);
|
|
2382
|
+
return lines;
|
|
2383
|
+
}
|
|
2384
|
+
var FEATURE_HEADER_WIDTH = 76 - 2;
|
|
2385
|
+
|
|
2386
|
+
// src/modify/yml.ts
|
|
2240
2387
|
function ensureSeq(doc, key) {
|
|
2241
2388
|
const existing = doc.get(key, true);
|
|
2242
2389
|
if (existing && isSeq(existing)) return existing;
|
|
@@ -2474,6 +2621,15 @@ function addFeatureToDoc(doc, ref, options = {}, displayName) {
|
|
|
2474
2621
|
if (Object.keys(options).length > 0) {
|
|
2475
2622
|
entry2.set("options", options);
|
|
2476
2623
|
}
|
|
2624
|
+
const summary = loadFeatureManifestSummary(ref);
|
|
2625
|
+
const headerBefore = buildFeatureHeaderCommentBefore(
|
|
2626
|
+
summary,
|
|
2627
|
+
FEATURE_HEADER_WIDTH
|
|
2628
|
+
);
|
|
2629
|
+
if (headerBefore.length > 0) {
|
|
2630
|
+
entry2.commentBefore = headerBefore;
|
|
2631
|
+
entry2.spaceBefore = true;
|
|
2632
|
+
}
|
|
2477
2633
|
seq.add(entry2);
|
|
2478
2634
|
return true;
|
|
2479
2635
|
}
|
|
@@ -2570,6 +2726,15 @@ function removeFeatureFromDoc(doc, ref) {
|
|
|
2570
2726
|
if (!seq || !isSeq(seq)) return false;
|
|
2571
2727
|
const idx = seq.items.findIndex((i) => isMap2(i) && i.get("ref") === ref);
|
|
2572
2728
|
if (idx < 0) return false;
|
|
2729
|
+
if (idx > 0) {
|
|
2730
|
+
const prev = seq.items[idx - 1];
|
|
2731
|
+
if (prev && typeof prev.comment === "string" && prev.comment.length > 0) {
|
|
2732
|
+
const blank = prev.comment.match(/\n[ \t]*\n/);
|
|
2733
|
+
if (blank && blank.index !== void 0) {
|
|
2734
|
+
prev.comment = prev.comment.slice(0, blank.index);
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2573
2738
|
seq.items.splice(idx, 1);
|
|
2574
2739
|
pruneEmptySeq(doc, "features");
|
|
2575
2740
|
return true;
|
|
@@ -2777,7 +2942,7 @@ async function tryCloneInRunningContainer(input, entry2) {
|
|
|
2777
2942
|
logger.info(
|
|
2778
2943
|
`Cloned ${entry2.url} into /workspaces/${containerName2}/${targetRel} inside the running container.`
|
|
2779
2944
|
);
|
|
2780
|
-
void
|
|
2945
|
+
void path8;
|
|
2781
2946
|
}
|
|
2782
2947
|
function shquote(value) {
|
|
2783
2948
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -3456,12 +3621,12 @@ var addServiceCommand = defineCommand7({
|
|
|
3456
3621
|
import { defineCommand as defineCommand8 } from "citty";
|
|
3457
3622
|
|
|
3458
3623
|
// src/apply/index.ts
|
|
3459
|
-
import { existsSync as
|
|
3624
|
+
import { existsSync as existsSync6, promises as fs11 } from "fs";
|
|
3460
3625
|
import { consola as consola11 } from "consola";
|
|
3461
3626
|
|
|
3462
3627
|
// src/config/state.ts
|
|
3463
3628
|
import { promises as fs9 } from "fs";
|
|
3464
|
-
import
|
|
3629
|
+
import path9 from "path";
|
|
3465
3630
|
function buildStateFile(opts) {
|
|
3466
3631
|
return {
|
|
3467
3632
|
schemaVersion: CONFIG_SCHEMA_VERSION,
|
|
@@ -3471,7 +3636,7 @@ function buildStateFile(opts) {
|
|
|
3471
3636
|
};
|
|
3472
3637
|
}
|
|
3473
3638
|
function stateFilePath(targetDir) {
|
|
3474
|
-
return
|
|
3639
|
+
return path9.join(targetDir, ".monoceros", "state.json");
|
|
3475
3640
|
}
|
|
3476
3641
|
async function readStateFile(targetDir) {
|
|
3477
3642
|
try {
|
|
@@ -3482,7 +3647,7 @@ async function readStateFile(targetDir) {
|
|
|
3482
3647
|
}
|
|
3483
3648
|
}
|
|
3484
3649
|
async function writeStateFile(targetDir, state) {
|
|
3485
|
-
const monocerosDir =
|
|
3650
|
+
const monocerosDir = path9.join(targetDir, ".monoceros");
|
|
3486
3651
|
await fs9.mkdir(monocerosDir, { recursive: true });
|
|
3487
3652
|
await fs9.writeFile(
|
|
3488
3653
|
stateFilePath(targetDir),
|
|
@@ -3554,8 +3719,8 @@ function solutionConfigToCreateOptions(config, featureDefaults = {}) {
|
|
|
3554
3719
|
|
|
3555
3720
|
// src/devcontainer/compose.ts
|
|
3556
3721
|
import { spawn as spawn5 } from "child_process";
|
|
3557
|
-
import { existsSync as
|
|
3558
|
-
import
|
|
3722
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3723
|
+
import path11 from "path";
|
|
3559
3724
|
import { consola as consola9 } from "consola";
|
|
3560
3725
|
|
|
3561
3726
|
// src/util/mask-secrets.ts
|
|
@@ -3616,20 +3781,20 @@ function createSecretMaskStream() {
|
|
|
3616
3781
|
|
|
3617
3782
|
// src/devcontainer/cli.ts
|
|
3618
3783
|
import { spawn as spawn4 } from "child_process";
|
|
3619
|
-
import { readFileSync as
|
|
3784
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
3620
3785
|
import { createRequire } from "module";
|
|
3621
|
-
import
|
|
3786
|
+
import path10 from "path";
|
|
3622
3787
|
var require_ = createRequire(import.meta.url);
|
|
3623
3788
|
var cachedBinaryPath = null;
|
|
3624
3789
|
function devcontainerCliPath() {
|
|
3625
3790
|
if (cachedBinaryPath) return cachedBinaryPath;
|
|
3626
3791
|
const pkgJsonPath = require_.resolve("@devcontainers/cli/package.json");
|
|
3627
|
-
const pkg = JSON.parse(
|
|
3792
|
+
const pkg = JSON.parse(readFileSync3(pkgJsonPath, "utf8"));
|
|
3628
3793
|
const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.devcontainer ?? "";
|
|
3629
3794
|
if (!binEntry) {
|
|
3630
3795
|
throw new Error("Could not resolve @devcontainers/cli bin entry.");
|
|
3631
3796
|
}
|
|
3632
|
-
cachedBinaryPath =
|
|
3797
|
+
cachedBinaryPath = path10.resolve(path10.dirname(pkgJsonPath), binEntry);
|
|
3633
3798
|
return cachedBinaryPath;
|
|
3634
3799
|
}
|
|
3635
3800
|
var spawnDevcontainer = (args, cwd, options = {}) => {
|
|
@@ -3701,16 +3866,16 @@ var spawnBash = (args, cwd) => {
|
|
|
3701
3866
|
});
|
|
3702
3867
|
};
|
|
3703
3868
|
function composeProjectName(root) {
|
|
3704
|
-
return `${
|
|
3869
|
+
return `${path11.basename(root)}_devcontainer`;
|
|
3705
3870
|
}
|
|
3706
3871
|
function resolveCompose(root) {
|
|
3707
|
-
if (!
|
|
3872
|
+
if (!existsSync5(path11.join(root, ".devcontainer"))) {
|
|
3708
3873
|
throw new Error(
|
|
3709
3874
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
3710
3875
|
);
|
|
3711
3876
|
}
|
|
3712
|
-
const composeFile =
|
|
3713
|
-
if (!
|
|
3877
|
+
const composeFile = path11.join(root, ".devcontainer", "compose.yaml");
|
|
3878
|
+
if (!existsSync5(composeFile)) {
|
|
3714
3879
|
throw new Error(
|
|
3715
3880
|
`No compose.yaml at ${composeFile}. \`start\` / \`stop\` / \`status\` / \`logs\` require services configured via \`monoceros add-service <name> <svc>\`. Use \`monoceros shell <name>\` to enter the container directly.`
|
|
3716
3881
|
);
|
|
@@ -4005,7 +4170,7 @@ function formatRootlessNotSupportedError() {
|
|
|
4005
4170
|
// src/devcontainer/identity.ts
|
|
4006
4171
|
import { spawn as spawn8 } from "child_process";
|
|
4007
4172
|
import { promises as fs10 } from "fs";
|
|
4008
|
-
import
|
|
4173
|
+
import path12 from "path";
|
|
4009
4174
|
import { consola as consola10 } from "consola";
|
|
4010
4175
|
var realGitConfigGet = (key) => {
|
|
4011
4176
|
return new Promise((resolve, reject) => {
|
|
@@ -4119,8 +4284,8 @@ async function resolveIdentityWithPrompt(options = {}) {
|
|
|
4119
4284
|
};
|
|
4120
4285
|
}
|
|
4121
4286
|
async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
4122
|
-
const gitconfigDir =
|
|
4123
|
-
const gitconfigPath =
|
|
4287
|
+
const gitconfigDir = path12.join(devContainerRoot, ".monoceros");
|
|
4288
|
+
const gitconfigPath = path12.join(gitconfigDir, "gitconfig");
|
|
4124
4289
|
const logger = options.logger ?? { info: () => {
|
|
4125
4290
|
}, warn: () => {
|
|
4126
4291
|
} };
|
|
@@ -4210,7 +4375,7 @@ ${sectionLine(label)}
|
|
|
4210
4375
|
);
|
|
4211
4376
|
}
|
|
4212
4377
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
4213
|
-
if (!
|
|
4378
|
+
if (!existsSync6(ymlPath)) {
|
|
4214
4379
|
throw new Error(
|
|
4215
4380
|
`No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
4216
4381
|
);
|
|
@@ -4339,7 +4504,7 @@ ${sectionLine(label)}
|
|
|
4339
4504
|
return { targetDir, configPath: ymlPath, containerExitCode: exitCode };
|
|
4340
4505
|
}
|
|
4341
4506
|
async function assertSafeTargetDir(targetDir, expectedOrigin) {
|
|
4342
|
-
if (!
|
|
4507
|
+
if (!existsSync6(targetDir)) return;
|
|
4343
4508
|
const entries = await fs11.readdir(targetDir);
|
|
4344
4509
|
if (entries.length === 0) return;
|
|
4345
4510
|
const state = await readStateFile(targetDir);
|
|
@@ -4432,7 +4597,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
4432
4597
|
}
|
|
4433
4598
|
|
|
4434
4599
|
// src/version.ts
|
|
4435
|
-
var CLI_VERSION = true ? "1.9.
|
|
4600
|
+
var CLI_VERSION = true ? "1.9.7" : "dev";
|
|
4436
4601
|
|
|
4437
4602
|
// src/commands/_dispatch.ts
|
|
4438
4603
|
import { consola as consola12 } from "consola";
|
|
@@ -4594,82 +4759,6 @@ import { defineCommand as defineCommand10 } from "citty";
|
|
|
4594
4759
|
// src/completion/resolve.ts
|
|
4595
4760
|
import { existsSync as existsSync7, promises as fs12 } from "fs";
|
|
4596
4761
|
import path13 from "path";
|
|
4597
|
-
|
|
4598
|
-
// src/init/manifest.ts
|
|
4599
|
-
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
4600
|
-
import path12 from "path";
|
|
4601
|
-
function resolveManifestPath(name, checkoutRoot) {
|
|
4602
|
-
if (checkoutRoot) {
|
|
4603
|
-
const checkoutPath = path12.join(
|
|
4604
|
-
checkoutRoot,
|
|
4605
|
-
"images",
|
|
4606
|
-
"features",
|
|
4607
|
-
name,
|
|
4608
|
-
"devcontainer-feature.json"
|
|
4609
|
-
);
|
|
4610
|
-
if (existsSync6(checkoutPath)) return checkoutPath;
|
|
4611
|
-
}
|
|
4612
|
-
const bundlePath = path12.join(
|
|
4613
|
-
bundledFeaturesDir(),
|
|
4614
|
-
name,
|
|
4615
|
-
"devcontainer-feature.json"
|
|
4616
|
-
);
|
|
4617
|
-
if (existsSync6(bundlePath)) return bundlePath;
|
|
4618
|
-
return null;
|
|
4619
|
-
}
|
|
4620
|
-
function loadFeatureManifestSummary(ref, checkoutRoot = workbenchCheckoutRoot()) {
|
|
4621
|
-
const match = matchMonocerosFeature(ref);
|
|
4622
|
-
if (!match) return void 0;
|
|
4623
|
-
const manifestPath = resolveManifestPath(match.name, checkoutRoot);
|
|
4624
|
-
if (!manifestPath) return void 0;
|
|
4625
|
-
try {
|
|
4626
|
-
const text = readFileSync3(manifestPath, "utf8");
|
|
4627
|
-
const parsed = JSON.parse(text);
|
|
4628
|
-
const rawHints = parsed["x-monoceros"]?.optionHints;
|
|
4629
|
-
const optionHints = Array.isArray(rawHints) ? rawHints.filter(
|
|
4630
|
-
(x) => typeof x === "string" && x.length > 0
|
|
4631
|
-
) : [];
|
|
4632
|
-
const rawNotes = parsed["x-monoceros"]?.usageNotes;
|
|
4633
|
-
const usageNotes = Array.isArray(rawNotes) ? rawNotes.filter(
|
|
4634
|
-
(x) => typeof x === "string" && x.length > 0
|
|
4635
|
-
) : [];
|
|
4636
|
-
const optionDescriptions = {};
|
|
4637
|
-
const optionTypes = {};
|
|
4638
|
-
const optionNames = [];
|
|
4639
|
-
if (parsed.options) {
|
|
4640
|
-
for (const [key, opt] of Object.entries(parsed.options)) {
|
|
4641
|
-
if (!opt || typeof opt !== "object") continue;
|
|
4642
|
-
optionNames.push(key);
|
|
4643
|
-
if (typeof opt.description === "string" && opt.description.length > 0) {
|
|
4644
|
-
optionDescriptions[key] = opt.description;
|
|
4645
|
-
}
|
|
4646
|
-
if (opt.type === "boolean") {
|
|
4647
|
-
optionTypes[key] = "boolean";
|
|
4648
|
-
} else if (opt.type === "string") {
|
|
4649
|
-
optionTypes[key] = "string";
|
|
4650
|
-
}
|
|
4651
|
-
}
|
|
4652
|
-
}
|
|
4653
|
-
const name = typeof parsed.name === "string" ? parsed.name : "";
|
|
4654
|
-
const description = typeof parsed.description === "string" ? parsed.description : "";
|
|
4655
|
-
const rawUrl = typeof parsed.documentationURL === "string" ? parsed.documentationURL.trim() : "";
|
|
4656
|
-
const documentationURL = rawUrl.length > 0 && rawUrl.toLowerCase() !== "tbd" ? rawUrl : void 0;
|
|
4657
|
-
return {
|
|
4658
|
-
name,
|
|
4659
|
-
description,
|
|
4660
|
-
documentationURL,
|
|
4661
|
-
optionHints,
|
|
4662
|
-
optionDescriptions,
|
|
4663
|
-
optionNames,
|
|
4664
|
-
optionTypes,
|
|
4665
|
-
usageNotes
|
|
4666
|
-
};
|
|
4667
|
-
} catch {
|
|
4668
|
-
return void 0;
|
|
4669
|
-
}
|
|
4670
|
-
}
|
|
4671
|
-
|
|
4672
|
-
// src/completion/resolve.ts
|
|
4673
4762
|
async function resolveCompletions(line, point, opts = {}) {
|
|
4674
4763
|
const { prev, current } = parseCompletionLine(line, point);
|
|
4675
4764
|
const ctx = { prev, current, opts };
|
|
@@ -5305,12 +5394,8 @@ function routingHeader(name) {
|
|
|
5305
5394
|
}
|
|
5306
5395
|
function renderFeatureBlock(out, feature, summary, commented) {
|
|
5307
5396
|
const yamlPrefix = commented ? "# " : "";
|
|
5308
|
-
const
|
|
5309
|
-
|
|
5310
|
-
const wrapped = wrapToComment(line, COMMENT_WIDTH - 2);
|
|
5311
|
-
for (const wl of wrapped) {
|
|
5312
|
-
out.push(`# ${wl}`.trimEnd());
|
|
5313
|
-
}
|
|
5397
|
+
for (const wl of buildFeatureHeaderLines(summary, COMMENT_WIDTH - 2)) {
|
|
5398
|
+
out.push(`# ${wl}`.trimEnd());
|
|
5314
5399
|
}
|
|
5315
5400
|
out.push(`${yamlPrefix} - ref: ${feature.ref}`);
|
|
5316
5401
|
const options = feature.options ?? {};
|
|
@@ -5340,41 +5425,6 @@ function renderFeatureBlock(out, feature, summary, commented) {
|
|
|
5340
5425
|
}
|
|
5341
5426
|
}
|
|
5342
5427
|
}
|
|
5343
|
-
function buildHeaderLines(summary) {
|
|
5344
|
-
const out = [];
|
|
5345
|
-
if (!summary) {
|
|
5346
|
-
return out;
|
|
5347
|
-
}
|
|
5348
|
-
const tagline = summary.name?.trim();
|
|
5349
|
-
const description = summary.description?.trim();
|
|
5350
|
-
if (tagline && description) {
|
|
5351
|
-
out.push(`${tagline} \u2014 ${description}`);
|
|
5352
|
-
} else if (tagline) {
|
|
5353
|
-
out.push(tagline);
|
|
5354
|
-
} else if (description) {
|
|
5355
|
-
out.push(description);
|
|
5356
|
-
}
|
|
5357
|
-
for (const note of summary.usageNotes) {
|
|
5358
|
-
const trimmed = note.trim();
|
|
5359
|
-
if (trimmed.length > 0) out.push(trimmed);
|
|
5360
|
-
}
|
|
5361
|
-
if (summary.optionHints.length > 0) {
|
|
5362
|
-
const parts = summary.optionHints.map((key) => {
|
|
5363
|
-
const desc = summary.optionDescriptions[key];
|
|
5364
|
-
const short = desc ? shortenOptionDescription(desc) : void 0;
|
|
5365
|
-
return short ? `${key} (${short})` : key;
|
|
5366
|
-
});
|
|
5367
|
-
out.push(`Options: ${parts.join(", ")}.`);
|
|
5368
|
-
}
|
|
5369
|
-
if (summary.documentationURL) {
|
|
5370
|
-
out.push(`See ${summary.documentationURL} for further information.`);
|
|
5371
|
-
}
|
|
5372
|
-
return out;
|
|
5373
|
-
}
|
|
5374
|
-
function shortenOptionDescription(desc) {
|
|
5375
|
-
const firstSentence = desc.split(/(?<=[.!?])\s+/)[0]?.trim() ?? desc.trim();
|
|
5376
|
-
return firstSentence.replace(/[.!?]+$/, "").trim();
|
|
5377
|
-
}
|
|
5378
5428
|
function pushHeader(out, header, name) {
|
|
5379
5429
|
for (const line of header.replace(/<name>/g, name).split("\n")) {
|
|
5380
5430
|
out.push(line);
|
|
@@ -5387,27 +5437,6 @@ function pushSectionHeader(out, text, _commented) {
|
|
|
5387
5437
|
out.push(`# ${wl}`.trimEnd());
|
|
5388
5438
|
}
|
|
5389
5439
|
}
|
|
5390
|
-
function wrapToComment(text, width) {
|
|
5391
|
-
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
5392
|
-
if (words.length === 0) return [""];
|
|
5393
|
-
const usable = Math.max(width, 20);
|
|
5394
|
-
const lines = [];
|
|
5395
|
-
let current = "";
|
|
5396
|
-
for (const w of words) {
|
|
5397
|
-
if (current.length === 0) {
|
|
5398
|
-
current = w;
|
|
5399
|
-
continue;
|
|
5400
|
-
}
|
|
5401
|
-
if (current.length + 1 + w.length <= usable) {
|
|
5402
|
-
current += " " + w;
|
|
5403
|
-
} else {
|
|
5404
|
-
lines.push(current);
|
|
5405
|
-
current = w;
|
|
5406
|
-
}
|
|
5407
|
-
}
|
|
5408
|
-
if (current.length > 0) lines.push(current);
|
|
5409
|
-
return lines;
|
|
5410
|
-
}
|
|
5411
5440
|
function renderScalarValue(value) {
|
|
5412
5441
|
if (typeof value === "string") {
|
|
5413
5442
|
return /^[A-Za-z_][A-Za-z0-9._-]*$/.test(value) ? value : JSON.stringify(value);
|