@orkestrel/scaffold 0.0.84 → 0.0.86

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.
@@ -4,7 +4,7 @@ let _orkestrel_template = require("@orkestrel/template");
4
4
  let _orkestrel_emitter = require("@orkestrel/emitter");
5
5
  var package_default = {
6
6
  name: "@orkestrel/scaffold",
7
- version: "0.0.84",
7
+ version: "0.0.86",
8
8
  description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
9
9
  keywords: [
10
10
  "audit",
@@ -7361,6 +7361,185 @@ function replaceManifestRanges(manifest, pins) {
7361
7361
  return declaredRuntime && declaredDevelopment ? compiled : void 0;
7362
7362
  }
7363
7363
  /**
7364
+ * Inserts dependencies a package manifest does not declare into the section each one names.
7365
+ *
7366
+ * @param manifest - The manifest text to compile.
7367
+ * @param additions - The runtime and development names and ranges to declare.
7368
+ * @returns The manifest declaring every addition, or `undefined` when the text is not a
7369
+ * manifest, an addition's section is not an object or holds a value that is not a range, a name
7370
+ * is repeated, or `dependencies` or `devDependencies` already declares a name.
7371
+ *
7372
+ * @remarks
7373
+ * Runtime additions enter `dependencies` and development additions enter `devDependencies`. The
7374
+ * compiler inserts text instead of serializing the manifest, so every declared byte survives.
7375
+ * Each addition lands before the first declared key that sorts after it, or after the last
7376
+ * declared key when none does, so a section kept in key order stays in key order. An addition
7377
+ * copies the indentation of the section's first entry; an empty section takes its entries one
7378
+ * level inside the indentation of its opening line. An absent section is created as one
7379
+ * top-level key holding its additions: `devDependencies` after `dependencies`, `dependencies`
7380
+ * before `devDependencies`, and either one last in the manifest object when its sibling is
7381
+ * absent. The created key copies the indentation of the manifest's first key, and its entries sit
7382
+ * one level further in. A name either writable section already declares is the caller's mistake
7383
+ * rather than an addition, so the compiler refuses it instead of declaring a package twice.
7384
+ *
7385
+ * @example
7386
+ * ```ts
7387
+ * import { insertManifestDependencies } from '@orkestrel/scaffold'
7388
+ *
7389
+ * insertManifestDependencies('{\n\t"devDependencies": {\n\t\t"vite": "^8.3.2"\n\t}\n}\n', {
7390
+ * runtime: [],
7391
+ * development: [{ name: 'typescript', range: '^6.0.3' }],
7392
+ * })
7393
+ * // '{\n\t"devDependencies": {\n\t\t"typescript": "^6.0.3",\n\t\t"vite": "^8.3.2"\n\t}\n}\n'
7394
+ * ```
7395
+ */
7396
+ function insertManifestDependencies(manifest, additions) {
7397
+ const requested = [...additions.runtime, ...additions.development];
7398
+ if (requested.length === 0) return manifest;
7399
+ const parsed = (0, _orkestrel_contract.parseJSON)(manifest);
7400
+ if (!(0, _orkestrel_contract.isRecord)(parsed)) return void 0;
7401
+ const declared = /* @__PURE__ */ new Set();
7402
+ for (const section of ["dependencies", "devDependencies"]) {
7403
+ const entries = parsed[section];
7404
+ if ((0, _orkestrel_contract.isRecord)(entries)) for (const name of Object.keys(entries)) declared.add(name);
7405
+ }
7406
+ const names = new Set(requested.map(({ name }) => name));
7407
+ if (names.size !== requested.length || [...names].some((name) => declared.has(name))) return;
7408
+ let compiled = manifest;
7409
+ const sections = [{
7410
+ name: "dependencies",
7411
+ dependencies: additions.runtime
7412
+ }, {
7413
+ name: "devDependencies",
7414
+ dependencies: additions.development
7415
+ }];
7416
+ for (const section of sections) {
7417
+ if (section.dependencies.length === 0) continue;
7418
+ const entries = parsed[section.name];
7419
+ if (entries !== void 0 && (!(0, _orkestrel_contract.isRecord)(entries) || !Object.values(entries).every(_orkestrel_contract.isString))) return;
7420
+ const ordered = [...section.dependencies].sort((left, right) => (0, _orkestrel_contract.compareValues)(left.name, right.name));
7421
+ const lines = ordered.map(({ name, range }) => `${JSON.stringify(name)}: ${JSON.stringify(range)}`);
7422
+ const sibling = section.name === "dependencies" ? "devDependencies" : "dependencies";
7423
+ const located = /* @__PURE__ */ new Map();
7424
+ let current;
7425
+ let head = -1;
7426
+ let root = -1;
7427
+ let depth = 0;
7428
+ let cursor = 0;
7429
+ while (cursor < compiled.length && root < 0) {
7430
+ const character = compiled.charAt(cursor);
7431
+ if (character === "\"") {
7432
+ const start = cursor;
7433
+ let quote = cursor + 1;
7434
+ while (quote < compiled.length && compiled.charAt(quote) !== "\"") quote += compiled.charAt(quote) === "\\" ? 2 : 1;
7435
+ if (quote >= compiled.length) return void 0;
7436
+ let colon = quote + 1;
7437
+ while (/\s/u.test(compiled.charAt(colon))) colon += 1;
7438
+ cursor = quote + 1;
7439
+ if (depth !== 1 || compiled.charAt(colon) !== ":") continue;
7440
+ if (head < 0) head = start;
7441
+ const name = (0, _orkestrel_contract.parseJSON)(compiled.slice(start, quote + 1));
7442
+ if (!(0, _orkestrel_contract.isString)(name) || name !== section.name && name !== sibling) continue;
7443
+ if (located.has(name)) continue;
7444
+ let brace = colon + 1;
7445
+ while (/\s/u.test(compiled.charAt(brace))) brace += 1;
7446
+ if (compiled.charAt(brace) !== "{") {
7447
+ if (name === section.name) return void 0;
7448
+ continue;
7449
+ }
7450
+ current = {
7451
+ name,
7452
+ start,
7453
+ open: brace
7454
+ };
7455
+ depth += 1;
7456
+ cursor = brace + 1;
7457
+ continue;
7458
+ }
7459
+ if (character === "{") depth += 1;
7460
+ else if (character === "}") {
7461
+ depth -= 1;
7462
+ if (depth === 1 && current !== void 0) {
7463
+ located.set(current.name, {
7464
+ ...current,
7465
+ close: cursor
7466
+ });
7467
+ current = void 0;
7468
+ }
7469
+ if (depth === 0) root = cursor;
7470
+ }
7471
+ cursor += 1;
7472
+ }
7473
+ const bounds = located.get(section.name);
7474
+ if (bounds === void 0) {
7475
+ if (entries !== void 0 || root < 0) return void 0;
7476
+ const breaking = head < 0 ? -1 : compiled.lastIndexOf("\n", head);
7477
+ const margin = breaking < 0 ? "" : compiled.slice(breaking + 1, head);
7478
+ const gap = breaking < 0 || /\S/u.test(margin) ? "" : `\n${margin}`;
7479
+ const block = `${JSON.stringify(section.name)}: {${lines.map((line) => `${gap === "" ? "" : `${gap}${margin}`}${line}`).join(",")}${gap}}`;
7480
+ const neighbor = located.get(sibling);
7481
+ let tail = root - 1;
7482
+ while (tail > 0 && /\s/u.test(compiled.charAt(tail))) tail -= 1;
7483
+ const [position, insertion] = neighbor === void 0 ? [tail + 1, compiled.charAt(tail) === "{" ? block : `,${gap}${block}`] : section.name === "devDependencies" ? [neighbor.close + 1, `,${gap}${block}`] : [neighbor.start, `${block},${gap}`];
7484
+ compiled = compiled.slice(0, position) + insertion + compiled.slice(position);
7485
+ continue;
7486
+ }
7487
+ const { open, close } = bounds;
7488
+ const declarations = [];
7489
+ let key;
7490
+ let scan = open + 1;
7491
+ while (scan < close) {
7492
+ if (compiled.charAt(scan) !== "\"") {
7493
+ scan += 1;
7494
+ continue;
7495
+ }
7496
+ let quote = scan + 1;
7497
+ while (quote < close && compiled.charAt(quote) !== "\"") quote += compiled.charAt(quote) === "\\" ? 2 : 1;
7498
+ if (quote >= close) return void 0;
7499
+ if (key === void 0) {
7500
+ const name = (0, _orkestrel_contract.parseJSON)(compiled.slice(scan, quote + 1));
7501
+ if (!(0, _orkestrel_contract.isString)(name)) return void 0;
7502
+ key = {
7503
+ name,
7504
+ start: scan
7505
+ };
7506
+ } else {
7507
+ declarations.push({
7508
+ ...key,
7509
+ end: quote + 1
7510
+ });
7511
+ key = void 0;
7512
+ }
7513
+ scan = quote + 1;
7514
+ }
7515
+ const first = declarations[0];
7516
+ const last = declarations.at(-1);
7517
+ const edits = /* @__PURE__ */ new Map();
7518
+ if (first === void 0 || last === void 0) {
7519
+ const opening = compiled.lastIndexOf("\n", open);
7520
+ let column = opening + 1;
7521
+ while (column < open && /\s/u.test(compiled.charAt(column))) column += 1;
7522
+ const level = opening < 0 ? "" : compiled.slice(opening + 1, column);
7523
+ const separator = opening < 0 ? "" : `\n${level}${level}`;
7524
+ const ending = opening < 0 ? "" : `\n${level}`;
7525
+ compiled = `${compiled.slice(0, open + 1)}${lines.map((line) => `${separator}${line}`).join(",")}${ending}${compiled.slice(close)}`;
7526
+ continue;
7527
+ }
7528
+ const newline = compiled.lastIndexOf("\n", first.start);
7529
+ const indent = newline < 0 ? "" : compiled.slice(newline + 1, first.start);
7530
+ const separator = newline < 0 || /\S/u.test(indent) ? "" : `\n${indent}`;
7531
+ for (const [index, dependency] of ordered.entries()) {
7532
+ const line = lines[index] ?? "";
7533
+ const anchor = declarations.find((declaration) => (0, _orkestrel_contract.compareValues)(declaration.name, dependency.name) > 0);
7534
+ const position = anchor?.start ?? last.end;
7535
+ const text = anchor === void 0 ? `,${separator}${line}` : `${line},${separator}`;
7536
+ edits.set(position, `${edits.get(position) ?? ""}${text}`);
7537
+ }
7538
+ for (const [position, text] of [...edits].sort(([left], [right]) => right - left)) compiled = compiled.slice(0, position) + text + compiled.slice(position);
7539
+ }
7540
+ return compiled;
7541
+ }
7542
+ /**
7364
7543
  * Replaces named script values in package manifest text.
7365
7544
  *
7366
7545
  * @param manifest - The manifest text to compile.
@@ -8570,6 +8749,7 @@ exports.extractRangeMajor = extractRangeMajor;
8570
8749
  exports.extractVersion = extractVersion;
8571
8750
  exports.inferDrift = inferDrift;
8572
8751
  exports.inferGroup = inferGroup;
8752
+ exports.insertManifestDependencies = insertManifestDependencies;
8573
8753
  exports.isArtifact = isArtifact;
8574
8754
  exports.isAudit = isAudit;
8575
8755
  exports.isBlueprint = isBlueprint;