@patterkit/cli 0.9.1 → 0.9.3

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.
Files changed (2) hide show
  1. package/dist/cli.js +84 -66
  2. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -198054,6 +198054,7 @@ function validateProject(input) {
198054
198054
  }
198055
198055
  }
198056
198056
  };
198057
+ const addressOf = (node) => effectiveGameId({ gameId: node.gameId, name: typeof node.name === "string" ? node.name : "" });
198057
198058
  const seeId = (id, kind, where) => {
198058
198059
  if (typeof id !== "string" || id.length === 0) {
198059
198060
  issues.push({ code: "missing-id", message: `${kind} in ${where} has no id` });
@@ -198078,7 +198079,7 @@ function validateProject(input) {
198078
198079
  if (scene.gameId && !isValidGameId(scene.gameId)) {
198079
198080
  issues.push({ code: "invalid-gameid", message: `${where} address '${scene.gameId}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)`, id: scene.id });
198080
198081
  }
198081
- const sgid = effectiveGameId(scene);
198082
+ const sgid = addressOf(scene);
198082
198083
  if (sgid) {
198083
198084
  const prev = sceneGameIds.get(sgid);
198084
198085
  if (prev && prev !== scene.id) issues.push({ code: "duplicate-gameid", message: `${where} address '${sgid}' is already used by scene '${prev}'`, id: scene.id });
@@ -198113,7 +198114,7 @@ function validateProject(input) {
198113
198114
  if (block.gameId && !isValidGameId(block.gameId)) {
198114
198115
  issues.push({ code: "invalid-gameid", message: `block '${block.id}' address '${block.gameId}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)`, id: block.id });
198115
198116
  }
198116
- const bgid = effectiveGameId(block);
198117
+ const bgid = addressOf(block);
198117
198118
  if (bgid) {
198118
198119
  const prev = blockGameIds.get(bgid);
198119
198120
  if (prev && prev !== block.id) issues.push({ code: "duplicate-gameid", message: `block '${block.id}' address '${bgid}' is already used by block '${prev}' in ${where}`, id: block.id });
@@ -247164,8 +247165,8 @@ function planEditableImport(loaded, returned, opts) {
247164
247165
  const { text: proposedRaw, notes } = takeNotes(b.rowDeleted ? "" : b.text.proposed, sent.baseline);
247165
247166
  const proposed = tidy(proposedRaw);
247166
247167
  const textChanged = normalise(proposed, !!opts.strictQuotes) !== normalise(sent.baseline, !!opts.strictQuotes);
247167
- const tracked = b.text.changes.length > 0;
247168
- if (tracked && normalise(b.text.original, !!opts.strictQuotes) !== normalise(sent.baseline, !!opts.strictQuotes)) {
247168
+ const tracked2 = b.text.changes.length > 0;
247169
+ if (tracked2 && normalise(b.text.original, !!opts.strictQuotes) !== normalise(sent.baseline, !!opts.strictQuotes)) {
247169
247170
  problems.push({ severity: "info", kind: "untracked-edits", marker: code, anchor: sent.id, message: "This line has tracked changes and some untracked ones too, so the credit may be incomplete." });
247170
247171
  }
247171
247172
  const cue = sent.kind === "line" ? readLead(b.lead.proposed) : void 0;
@@ -248612,15 +248613,61 @@ function cmAsync(args) {
248612
248613
  return runCommandAsync("cm", args);
248613
248614
  }
248614
248615
  function isTracked$1(filePath) {
248615
- return trackedFromShortStatus(cm(["status", "--short", filePath]));
248616
+ return tracked(localState(filePath));
248616
248617
  }
248617
248618
  async function isTrackedAsync$1(filePath) {
248618
- return trackedFromShortStatus(await cmAsync(["status", "--short", filePath]));
248619
+ return tracked(await localStateAsync(filePath));
248619
248620
  }
248620
- function trackedFromShortStatus(result) {
248621
- if (result.exitCode !== 0) return false;
248622
- const lines = result.output.split("\n").filter((l2) => l2.trim());
248623
- return lines.length > 0 && !lines[0].startsWith("?");
248621
+ function tracked(state) {
248622
+ return state === "controlled" || state === "checkedOut";
248623
+ }
248624
+ function localStateArgs(filePath) {
248625
+ return plasticStatusArgs([filePath]);
248626
+ }
248627
+ function localState(filePath) {
248628
+ return localStateFrom(cm(localStateArgs(filePath)), filePath);
248629
+ }
248630
+ async function localStateAsync(filePath) {
248631
+ return localStateFrom(await cmAsync(localStateArgs(filePath)), filePath);
248632
+ }
248633
+ function localStateFrom(result, filePath) {
248634
+ if (result.exitCode !== 0) return "outside";
248635
+ const info = findStatusInfo(result.output, filePath);
248636
+ if (!info) return "controlled";
248637
+ if (!info.tracked) return info.ignored ? "ignored" : "private";
248638
+ return info.checkedOut ? "checkedOut" : "controlled";
248639
+ }
248640
+ function findStatusInfo(output, filePath) {
248641
+ const key2 = pathKey(filePath);
248642
+ const sameName = [];
248643
+ for (const line2 of output.split("\n")) {
248644
+ const parsed = parseCmStatusLine(line2);
248645
+ if (!parsed) continue;
248646
+ for (const p of parsed.paths) {
248647
+ if (pathKey(p) === key2) return parsed.info;
248648
+ if (basename7(resolve8(p)) === basename7(resolve8(filePath))) sameName.push(parsed.info);
248649
+ }
248650
+ }
248651
+ return sameName.length === 1 ? sameName[0] : void 0;
248652
+ }
248653
+ function pathKey(p) {
248654
+ const abs = resolve8(p);
248655
+ return process.platform === "win32" ? abs.toLowerCase() : abs;
248656
+ }
248657
+ function checkoutResult(filePath, result) {
248658
+ if (result.exitCode === 0) return okResult();
248659
+ const combined = (result.output + " " + result.error).toLowerCase();
248660
+ if (combined.includes("locked") || combined.includes("exclusive")) {
248661
+ return errorResult("locked", `'${filePath}' is locked`);
248662
+ }
248663
+ if (combined.includes("out of date") || combined.includes("not latest")) {
248664
+ return errorResult("outOfDate", `'${filePath}' is out of date \u2014 update before editing`);
248665
+ }
248666
+ return errorResult("error", `Cannot check out '${filePath}': ${result.error || result.output}`);
248667
+ }
248668
+ function addResult(filePath, result) {
248669
+ if (result.exitCode === 0) return okResult("File added to Plastic SCM");
248670
+ return errorResult("error", `Cannot add '${filePath}' to Plastic SCM: ${result.error || result.output}`);
248624
248671
  }
248625
248672
  function undoCheckoutResult(filePath, result) {
248626
248673
  if (result.exitCode === 0) return okResult("Checkout undone in Plastic SCM");
@@ -248640,67 +248687,36 @@ var PlasticProvider = class {
248640
248687
  const u = await plasticWhoamiAsync();
248641
248688
  return u === "" ? void 0 : u;
248642
248689
  }
248690
+ /**
248691
+ * Check a controlled file out before it is written. A file already checked out (or
248692
+ * otherwise pending) in this workspace is not checked out again: a second `cm co` on
248693
+ * it fails, and under lock rules it fails as "locked" by our own checkout. Private,
248694
+ * ignored, and out-of-workspace files only need to be writable.
248695
+ */
248643
248696
  prepareToWrite(filePath) {
248644
248697
  if (!existsSync7(filePath)) return okResult();
248645
- if (!isTracked$1(filePath)) {
248646
- return fs$1.prepareToWrite(filePath);
248647
- }
248648
- const result = cm(["co", filePath]);
248649
- if (result.exitCode === 0) return okResult();
248650
- const combined = (result.output + " " + result.error).toLowerCase();
248651
- if (combined.includes("locked") || combined.includes("exclusive")) {
248652
- return errorResult("locked", `'${filePath}' is locked`);
248653
- }
248654
- if (combined.includes("out of date") || combined.includes("not latest")) {
248655
- return errorResult("outOfDate", `'${filePath}' is out of date \u2014 update before editing`);
248656
- }
248657
- return errorResult("error", `Cannot check out '${filePath}': ${result.error || result.output}`);
248698
+ if (localState(filePath) !== "controlled") return fs$1.prepareToWrite(filePath);
248699
+ return checkoutResult(filePath, cm(["co", filePath]));
248658
248700
  }
248701
+ /** Add a new private file to Plastic SCM after it is written. */
248659
248702
  finishedWrite(filePath) {
248660
248703
  if (!existsSync7(filePath))
248661
248704
  return errorResult("error", `'${filePath}' does not exist after write`);
248662
- const statusResult = cm(["status", "--short", filePath]);
248663
- if (statusResult.exitCode !== 0)
248664
- return fs$1.finishedWrite(filePath);
248665
- const lines = statusResult.output.split("\n").filter((l2) => l2.trim());
248666
- const tracked = lines.length > 0 && !lines[0].startsWith("?");
248667
- if (tracked) return okResult();
248668
- const result = cm(["add", filePath]);
248669
- if (result.exitCode === 0) return okResult("File added to Plastic SCM");
248670
- const combined = (result.output + " " + result.error).toLowerCase();
248671
- if (combined.includes("ignored")) return fs$1.finishedWrite(filePath);
248672
- return errorResult("error", `Cannot add '${filePath}' to Plastic SCM: ${result.error || result.output}`);
248705
+ if (localState(filePath) !== "private") return fs$1.finishedWrite(filePath);
248706
+ return addResult(filePath, cm(["add", filePath]));
248673
248707
  }
248674
248708
  /** Async twin of {@link prepareToWrite}. */
248675
248709
  async prepareToWriteAsync(filePath) {
248676
248710
  if (!existsSync7(filePath)) return okResult();
248677
- if (!await isTrackedAsync$1(filePath)) {
248678
- return fs$1.prepareToWriteAsync(filePath);
248679
- }
248680
- const result = await cmAsync(["co", filePath]);
248681
- if (result.exitCode === 0) return okResult();
248682
- const combined = (result.output + " " + result.error).toLowerCase();
248683
- if (combined.includes("locked") || combined.includes("exclusive")) {
248684
- return errorResult("locked", `'${filePath}' is locked`);
248685
- }
248686
- if (combined.includes("out of date") || combined.includes("not latest")) {
248687
- return errorResult("outOfDate", `'${filePath}' is out of date \u2014 update before editing`);
248688
- }
248689
- return errorResult("error", `Cannot check out '${filePath}': ${result.error || result.output}`);
248711
+ if (await localStateAsync(filePath) !== "controlled") return fs$1.prepareToWriteAsync(filePath);
248712
+ return checkoutResult(filePath, await cmAsync(["co", filePath]));
248690
248713
  }
248691
248714
  /** Async twin of {@link finishedWrite}. */
248692
248715
  async finishedWriteAsync(filePath) {
248693
248716
  if (!existsSync7(filePath))
248694
248717
  return errorResult("error", `'${filePath}' does not exist after write`);
248695
- const statusResult = await cmAsync(["status", "--short", filePath]);
248696
- if (statusResult.exitCode !== 0)
248697
- return fs$1.finishedWriteAsync(filePath);
248698
- if (trackedFromShortStatus(statusResult)) return okResult();
248699
- const result = await cmAsync(["add", filePath]);
248700
- if (result.exitCode === 0) return okResult("File added to Plastic SCM");
248701
- const combined = (result.output + " " + result.error).toLowerCase();
248702
- if (combined.includes("ignored")) return fs$1.finishedWriteAsync(filePath);
248703
- return errorResult("error", `Cannot add '${filePath}' to Plastic SCM: ${result.error || result.output}`);
248718
+ if (await localStateAsync(filePath) !== "private") return fs$1.finishedWriteAsync(filePath);
248719
+ return addResult(filePath, await cmAsync(["add", filePath]));
248704
248720
  }
248705
248721
  /**
248706
248722
  * Undo what {@link prepareToWrite} did, for an all-or-nothing batch that has to back
@@ -248876,7 +248892,7 @@ function buildPlasticBase(result, filePaths) {
248876
248892
  if (!parsed) continue;
248877
248893
  for (const p of parsed.paths) {
248878
248894
  const abs = resolve8(p);
248879
- byPath.set(abs, parsed.info);
248895
+ byPath.set(pathKey(abs), parsed.info);
248880
248896
  const base = basename7(abs);
248881
248897
  if (!byBase.has(base)) byBase.set(base, []);
248882
248898
  byBase.get(base).push(parsed.info);
@@ -248886,7 +248902,7 @@ function buildPlasticBase(result, filePaths) {
248886
248902
  return filePaths.map((filePath) => {
248887
248903
  const abs = resolve8(filePath);
248888
248904
  const status = { filePath: abs, system: "plastic", writable: writableBit(abs) };
248889
- let info = byPath.get(abs);
248905
+ let info = byPath.get(pathKey(abs));
248890
248906
  if (!info) {
248891
248907
  const sameName = byBase.get(basename7(abs));
248892
248908
  if (sameName && sameName.length === 1) info = sameName[0];
@@ -248960,18 +248976,20 @@ var PLASTIC_DIRTY_CODES = /* @__PURE__ */ new Set([
248960
248976
  "LM"
248961
248977
  ]);
248962
248978
  var PLASTIC_UNTRACKED_CODES = /* @__PURE__ */ new Set(["PR", "IG"]);
248979
+ var PLASTIC_CHECKED_OUT_CODES = /* @__PURE__ */ new Set(["CO", "AD", "CP", "RP", "MV"]);
248963
248980
  function parseCmStatusLine(line2) {
248964
248981
  const trimmed = line2.trim();
248965
248982
  const space = trimmed.indexOf(" ");
248966
248983
  if (space === -1) return null;
248967
- const code = trimmed.slice(0, space);
248968
- const dirty = PLASTIC_DIRTY_CODES.has(code);
248969
- const untracked = PLASTIC_UNTRACKED_CODES.has(code);
248984
+ const codes = trimmed.slice(0, space).split("+");
248985
+ const dirty = codes.some((c2) => PLASTIC_DIRTY_CODES.has(c2));
248986
+ const untracked = codes.some((c2) => PLASTIC_UNTRACKED_CODES.has(c2));
248970
248987
  if (!dirty && !untracked) return null;
248971
248988
  const rest = trimmed.slice(space + 1).trim();
248972
248989
  const quoted = [...rest.matchAll(/"([^"]*)"/g)].map((m) => m[1]);
248973
248990
  const paths = quoted.length > 0 ? quoted : [rest];
248974
- return { info: { tracked: !untracked, dirty }, paths };
248991
+ const checkedOut = !untracked && codes.some((c2) => PLASTIC_CHECKED_OUT_CODES.has(c2));
248992
+ return { info: { tracked: !untracked, dirty, checkedOut, ignored: codes.includes("IG") }, paths };
248975
248993
  }
248976
248994
  var fs3 = new FilesystemProvider();
248977
248995
  function svn(args) {
@@ -249543,7 +249561,7 @@ function fileStatus(filePaths, options = {}) {
249543
249561
  // package.json
249544
249562
  var package_default = {
249545
249563
  name: "@patterkit/cli",
249546
- version: "0.9.1",
249564
+ version: "0.9.3",
249547
249565
  description: "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
249548
249566
  type: "module",
249549
249567
  license: "MIT",
@@ -249572,9 +249590,9 @@ var package_default = {
249572
249590
  "build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
249573
249591
  },
249574
249592
  dependencies: {
249575
- "@patterkit/core": "0.4.0",
249576
- "@patterkit/ops": "0.14.1",
249577
- "@wildwinter/simple-vc-lib": "^0.5.0"
249593
+ "@patterkit/core": "0.4.1",
249594
+ "@patterkit/ops": "0.14.2",
249595
+ "@wildwinter/simple-vc-lib": "^0.5.1"
249578
249596
  }
249579
249597
  };
249580
249598
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/cli",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "The `patter` CLI: validate, format, export, play, test coverage, localise, and merge Patter projects.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,8 +29,8 @@
29
29
  "build:standalone:others": "node scripts/build-standalone.mjs --targets=linux-x64,linux-arm64,windows-x64"
30
30
  },
31
31
  "dependencies": {
32
- "@patterkit/core": "0.4.0",
33
- "@patterkit/ops": "0.14.1",
34
- "@wildwinter/simple-vc-lib": "^0.5.0"
32
+ "@patterkit/core": "0.4.1",
33
+ "@patterkit/ops": "0.14.2",
34
+ "@wildwinter/simple-vc-lib": "^0.5.1"
35
35
  }
36
36
  }