@getmonoceros/workbench 1.9.5 → 1.9.6
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 +180 -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
|
}
|
|
@@ -2777,7 +2933,7 @@ async function tryCloneInRunningContainer(input, entry2) {
|
|
|
2777
2933
|
logger.info(
|
|
2778
2934
|
`Cloned ${entry2.url} into /workspaces/${containerName2}/${targetRel} inside the running container.`
|
|
2779
2935
|
);
|
|
2780
|
-
void
|
|
2936
|
+
void path8;
|
|
2781
2937
|
}
|
|
2782
2938
|
function shquote(value) {
|
|
2783
2939
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -3456,12 +3612,12 @@ var addServiceCommand = defineCommand7({
|
|
|
3456
3612
|
import { defineCommand as defineCommand8 } from "citty";
|
|
3457
3613
|
|
|
3458
3614
|
// src/apply/index.ts
|
|
3459
|
-
import { existsSync as
|
|
3615
|
+
import { existsSync as existsSync6, promises as fs11 } from "fs";
|
|
3460
3616
|
import { consola as consola11 } from "consola";
|
|
3461
3617
|
|
|
3462
3618
|
// src/config/state.ts
|
|
3463
3619
|
import { promises as fs9 } from "fs";
|
|
3464
|
-
import
|
|
3620
|
+
import path9 from "path";
|
|
3465
3621
|
function buildStateFile(opts) {
|
|
3466
3622
|
return {
|
|
3467
3623
|
schemaVersion: CONFIG_SCHEMA_VERSION,
|
|
@@ -3471,7 +3627,7 @@ function buildStateFile(opts) {
|
|
|
3471
3627
|
};
|
|
3472
3628
|
}
|
|
3473
3629
|
function stateFilePath(targetDir) {
|
|
3474
|
-
return
|
|
3630
|
+
return path9.join(targetDir, ".monoceros", "state.json");
|
|
3475
3631
|
}
|
|
3476
3632
|
async function readStateFile(targetDir) {
|
|
3477
3633
|
try {
|
|
@@ -3482,7 +3638,7 @@ async function readStateFile(targetDir) {
|
|
|
3482
3638
|
}
|
|
3483
3639
|
}
|
|
3484
3640
|
async function writeStateFile(targetDir, state) {
|
|
3485
|
-
const monocerosDir =
|
|
3641
|
+
const monocerosDir = path9.join(targetDir, ".monoceros");
|
|
3486
3642
|
await fs9.mkdir(monocerosDir, { recursive: true });
|
|
3487
3643
|
await fs9.writeFile(
|
|
3488
3644
|
stateFilePath(targetDir),
|
|
@@ -3554,8 +3710,8 @@ function solutionConfigToCreateOptions(config, featureDefaults = {}) {
|
|
|
3554
3710
|
|
|
3555
3711
|
// src/devcontainer/compose.ts
|
|
3556
3712
|
import { spawn as spawn5 } from "child_process";
|
|
3557
|
-
import { existsSync as
|
|
3558
|
-
import
|
|
3713
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3714
|
+
import path11 from "path";
|
|
3559
3715
|
import { consola as consola9 } from "consola";
|
|
3560
3716
|
|
|
3561
3717
|
// src/util/mask-secrets.ts
|
|
@@ -3616,20 +3772,20 @@ function createSecretMaskStream() {
|
|
|
3616
3772
|
|
|
3617
3773
|
// src/devcontainer/cli.ts
|
|
3618
3774
|
import { spawn as spawn4 } from "child_process";
|
|
3619
|
-
import { readFileSync as
|
|
3775
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
3620
3776
|
import { createRequire } from "module";
|
|
3621
|
-
import
|
|
3777
|
+
import path10 from "path";
|
|
3622
3778
|
var require_ = createRequire(import.meta.url);
|
|
3623
3779
|
var cachedBinaryPath = null;
|
|
3624
3780
|
function devcontainerCliPath() {
|
|
3625
3781
|
if (cachedBinaryPath) return cachedBinaryPath;
|
|
3626
3782
|
const pkgJsonPath = require_.resolve("@devcontainers/cli/package.json");
|
|
3627
|
-
const pkg = JSON.parse(
|
|
3783
|
+
const pkg = JSON.parse(readFileSync3(pkgJsonPath, "utf8"));
|
|
3628
3784
|
const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.devcontainer ?? "";
|
|
3629
3785
|
if (!binEntry) {
|
|
3630
3786
|
throw new Error("Could not resolve @devcontainers/cli bin entry.");
|
|
3631
3787
|
}
|
|
3632
|
-
cachedBinaryPath =
|
|
3788
|
+
cachedBinaryPath = path10.resolve(path10.dirname(pkgJsonPath), binEntry);
|
|
3633
3789
|
return cachedBinaryPath;
|
|
3634
3790
|
}
|
|
3635
3791
|
var spawnDevcontainer = (args, cwd, options = {}) => {
|
|
@@ -3701,16 +3857,16 @@ var spawnBash = (args, cwd) => {
|
|
|
3701
3857
|
});
|
|
3702
3858
|
};
|
|
3703
3859
|
function composeProjectName(root) {
|
|
3704
|
-
return `${
|
|
3860
|
+
return `${path11.basename(root)}_devcontainer`;
|
|
3705
3861
|
}
|
|
3706
3862
|
function resolveCompose(root) {
|
|
3707
|
-
if (!
|
|
3863
|
+
if (!existsSync5(path11.join(root, ".devcontainer"))) {
|
|
3708
3864
|
throw new Error(
|
|
3709
3865
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
3710
3866
|
);
|
|
3711
3867
|
}
|
|
3712
|
-
const composeFile =
|
|
3713
|
-
if (!
|
|
3868
|
+
const composeFile = path11.join(root, ".devcontainer", "compose.yaml");
|
|
3869
|
+
if (!existsSync5(composeFile)) {
|
|
3714
3870
|
throw new Error(
|
|
3715
3871
|
`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
3872
|
);
|
|
@@ -4005,7 +4161,7 @@ function formatRootlessNotSupportedError() {
|
|
|
4005
4161
|
// src/devcontainer/identity.ts
|
|
4006
4162
|
import { spawn as spawn8 } from "child_process";
|
|
4007
4163
|
import { promises as fs10 } from "fs";
|
|
4008
|
-
import
|
|
4164
|
+
import path12 from "path";
|
|
4009
4165
|
import { consola as consola10 } from "consola";
|
|
4010
4166
|
var realGitConfigGet = (key) => {
|
|
4011
4167
|
return new Promise((resolve, reject) => {
|
|
@@ -4119,8 +4275,8 @@ async function resolveIdentityWithPrompt(options = {}) {
|
|
|
4119
4275
|
};
|
|
4120
4276
|
}
|
|
4121
4277
|
async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
4122
|
-
const gitconfigDir =
|
|
4123
|
-
const gitconfigPath =
|
|
4278
|
+
const gitconfigDir = path12.join(devContainerRoot, ".monoceros");
|
|
4279
|
+
const gitconfigPath = path12.join(gitconfigDir, "gitconfig");
|
|
4124
4280
|
const logger = options.logger ?? { info: () => {
|
|
4125
4281
|
}, warn: () => {
|
|
4126
4282
|
} };
|
|
@@ -4210,7 +4366,7 @@ ${sectionLine(label)}
|
|
|
4210
4366
|
);
|
|
4211
4367
|
}
|
|
4212
4368
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
4213
|
-
if (!
|
|
4369
|
+
if (!existsSync6(ymlPath)) {
|
|
4214
4370
|
throw new Error(
|
|
4215
4371
|
`No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
4216
4372
|
);
|
|
@@ -4339,7 +4495,7 @@ ${sectionLine(label)}
|
|
|
4339
4495
|
return { targetDir, configPath: ymlPath, containerExitCode: exitCode };
|
|
4340
4496
|
}
|
|
4341
4497
|
async function assertSafeTargetDir(targetDir, expectedOrigin) {
|
|
4342
|
-
if (!
|
|
4498
|
+
if (!existsSync6(targetDir)) return;
|
|
4343
4499
|
const entries = await fs11.readdir(targetDir);
|
|
4344
4500
|
if (entries.length === 0) return;
|
|
4345
4501
|
const state = await readStateFile(targetDir);
|
|
@@ -4432,7 +4588,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
4432
4588
|
}
|
|
4433
4589
|
|
|
4434
4590
|
// src/version.ts
|
|
4435
|
-
var CLI_VERSION = true ? "1.9.
|
|
4591
|
+
var CLI_VERSION = true ? "1.9.6" : "dev";
|
|
4436
4592
|
|
|
4437
4593
|
// src/commands/_dispatch.ts
|
|
4438
4594
|
import { consola as consola12 } from "consola";
|
|
@@ -4594,82 +4750,6 @@ import { defineCommand as defineCommand10 } from "citty";
|
|
|
4594
4750
|
// src/completion/resolve.ts
|
|
4595
4751
|
import { existsSync as existsSync7, promises as fs12 } from "fs";
|
|
4596
4752
|
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
4753
|
async function resolveCompletions(line, point, opts = {}) {
|
|
4674
4754
|
const { prev, current } = parseCompletionLine(line, point);
|
|
4675
4755
|
const ctx = { prev, current, opts };
|
|
@@ -5305,12 +5385,8 @@ function routingHeader(name) {
|
|
|
5305
5385
|
}
|
|
5306
5386
|
function renderFeatureBlock(out, feature, summary, commented) {
|
|
5307
5387
|
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
|
-
}
|
|
5388
|
+
for (const wl of buildFeatureHeaderLines(summary, COMMENT_WIDTH - 2)) {
|
|
5389
|
+
out.push(`# ${wl}`.trimEnd());
|
|
5314
5390
|
}
|
|
5315
5391
|
out.push(`${yamlPrefix} - ref: ${feature.ref}`);
|
|
5316
5392
|
const options = feature.options ?? {};
|
|
@@ -5340,41 +5416,6 @@ function renderFeatureBlock(out, feature, summary, commented) {
|
|
|
5340
5416
|
}
|
|
5341
5417
|
}
|
|
5342
5418
|
}
|
|
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
5419
|
function pushHeader(out, header, name) {
|
|
5379
5420
|
for (const line of header.replace(/<name>/g, name).split("\n")) {
|
|
5380
5421
|
out.push(line);
|
|
@@ -5387,27 +5428,6 @@ function pushSectionHeader(out, text, _commented) {
|
|
|
5387
5428
|
out.push(`# ${wl}`.trimEnd());
|
|
5388
5429
|
}
|
|
5389
5430
|
}
|
|
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
5431
|
function renderScalarValue(value) {
|
|
5412
5432
|
if (typeof value === "string") {
|
|
5413
5433
|
return /^[A-Za-z_][A-Za-z0-9._-]*$/.test(value) ? value : JSON.stringify(value);
|