@rulecad/versions 0.1.3 → 0.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulecad/versions",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Semver for physical parts: a change to what other parts fit against is major, so you know when an old print stops fitting.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -30,11 +30,15 @@
30
30
  "url": "git+https://github.com/JoniBach/rulecad.git",
31
31
  "directory": "packages/versions"
32
32
  },
33
+ "homepage": "https://github.com/JoniBach/rulecad/tree/main/packages/versions#readme",
34
+ "bugs": {
35
+ "url": "https://github.com/JoniBach/rulecad/issues"
36
+ },
33
37
  "scripts": {
34
38
  "test": "node --test"
35
39
  },
36
40
  "dependencies": {
37
- "@rulecad/variables": "^0.1.0 || ^0.2.0",
41
+ "@rulecad/variables": "^0.1.0 || ^0.2.0 || ^0.3.0",
38
42
  "@rulecad/keyboard-layout": "^0.1.0"
39
43
  },
40
44
  "types": "./versions.d.mts"
package/versions.d.mts CHANGED
@@ -14,9 +14,9 @@ export function compare(before: Snapshot, after: Snapshot): {
14
14
  text: string;
15
15
  }[];
16
16
  };
17
- export function parse(v: any): number[];
18
- export function newer(x: any, y: any): boolean;
19
- export function bigger(k1: any, k2: any): any;
17
+ export function parse(v: string): number[];
18
+ export function newer(x: string, y: string): boolean;
19
+ export function bigger(k1: "major" | "minor" | "patch", k2: "major" | "minor" | "patch"): "patch" | "minor" | "major";
20
20
  /**
21
21
  * a design: its .json, resolve()'s entries and its code
22
22
  */
package/versions.mjs CHANGED
@@ -12,13 +12,17 @@ import { project } from '@rulecad/keyboard-layout';
12
12
  /** @typedef {{ object: object, entries: { name: string, value: any }[], js: string, layouts?: Record<string, object>, engine?: string }} Snapshot a design: its .json, resolve()'s entries and its code */
13
13
 
14
14
  const KINDS = ['patch', 'minor', 'major'];
15
+ /** A version's three numbers. @param {string} v '1.2.3' @returns {number[]} */
15
16
  export const parse = (v) => { const m = String(v).match(/^(\d+)\.(\d+)\.(\d+)$/); if (!m) throw new Error(`"${v}" isn't a version like 1.2.0`); return m.slice(1).map(Number); };
16
17
  /** The next version. @param {string} v '1.2.3' @param {'major' | 'minor' | 'patch'} kind @returns {string} */
17
18
  export function bump(v, kind) {
18
19
  const [a, b, c] = parse(v);
20
+ if (!KINDS.includes(kind)) throw new Error(`a bump is major, minor or patch (got ${JSON.stringify(kind)})`);
19
21
  return kind === 'major' ? `${a + 1}.0.0` : kind === 'minor' ? `${a}.${b + 1}.0` : `${a}.${b}.${c + 1}`;
20
22
  }
23
+ /** Whether version x is after version y. @param {string} x @param {string} y @returns {boolean} */
21
24
  export const newer = (x, y) => { const [p, q] = [parse(x), parse(y)]; for (let i = 0; i < 3; i++) if (p[i] !== q[i]) return p[i] > q[i]; return false; };
25
+ /** The bigger of two bumps. @param {'major' | 'minor' | 'patch'} k1 @param {'major' | 'minor' | 'patch'} k2 */
22
26
  export const bigger = (k1, k2) => (KINDS.indexOf(k1) >= KINDS.indexOf(k2) ? k1 : k2);
23
27
 
24
28
 
@@ -45,6 +49,9 @@ export function compare(before, after) {
45
49
  const reasons = [], add = (kind, text) => reasons.push({ kind, text });
46
50
  const val = (r, name) => r.entries.find((e) => e.name === name);
47
51
  const v = (r, name) => fmt(val(r, name).value); // for reading: 4.2, not 4.199999999999999
52
+ // a change, for reading; where rounding would show the same number twice (4.2 → 4.2), the numbers as they are
53
+ const change = (name) => (v(before, name) === v(after, name) ? `${val(before, name).value} → ${val(after, name).value}` : `${v(before, name)} → ${v(after, name)}`);
54
+ const sorted = (x) => (Array.isArray(x) ? x.map(sorted) : x && typeof x === 'object' ? Object.fromEntries(Object.keys(x).sort().map((k) => [k, sorted(x[k])])) : x); // (the same settings in another order are the same)
48
55
  const vars = (r) => r.object.variables || {};
49
56
  const same = (a, b) => (typeof a === 'number' && typeof b === 'number' ? Math.abs(a - b) < 1e-9 : a === b); // numbers, or choices
50
57
 
@@ -53,14 +60,15 @@ export function compare(before, after) {
53
60
  if (d.interface) {
54
61
  if (!now) add('major', `interface ${name} removed`);
55
62
  else if (!now.interface) add('major', `${name} is no longer interface`);
56
- else if (!same(val(before, name).value, val(after, name).value)) add('major', `interface ${name} ${v(before, name)} → ${v(after, name)}`);
63
+ else if (!same(val(before, name).value, val(after, name).value)) add('major', `interface ${name} ${change(name)}`);
64
+ else if ((d.unit || '') !== (now.unit || '')) add('major', `interface ${name} is now in ${now.unit || 'no unit'} (was ${d.unit || 'no unit'})`);
57
65
  } else if (!now) add('patch', `${name} removed`);
58
- else if (!same(val(before, name).value, val(after, name).value)) add('patch', `${name} ${v(before, name)} → ${v(after, name)}`);
66
+ else if (!same(val(before, name).value, val(after, name).value)) add('patch', `${name} ${change(name)}`);
59
67
  }
60
68
  for (const [name, d] of Object.entries(vars(after))) {
61
69
  const was = vars(before)[name];
62
- if (d.interface && !was?.interface && was) continue; // reported above as a change of role
63
- if (d.interface && !was) add('minor', `interface ${name} added (${v(after, name)})`);
70
+ if (d.interface && was && !was.interface) add('minor', `${name} is now interface (${v(after, name)}): other parts may depend on it from here`);
71
+ else if (d.interface && !was) add('minor', `interface ${name} added (${v(after, name)})`);
64
72
  else if (!was) add('patch', `${name} added`);
65
73
  if ((d.type === 'text' || d.type === 'anchor') && !was?.type) add('minor', `${name} can now be set (${d.type})`); // a new input
66
74
  if (d.choice) { // a choice: options added are new ways to build it; options taken away break what used them
@@ -69,6 +77,7 @@ export function compare(before, after) {
69
77
  }
70
78
  if (d.tweak && !was?.tweak) add('minor', `${name} can now be set (${d.tweak.min} to ${d.tweak.max})`);
71
79
  else if (d.tweak && was?.tweak && (d.tweak.min !== was.tweak.min || d.tweak.max !== was.tweak.max)) add('patch', `${name} range now ${d.tweak.min} to ${d.tweak.max}`);
80
+ else if (!d.tweak && was?.tweak) add(d.interface ? 'major' : 'patch', `${name} can no longer be set (it was ${was.tweak.min} to ${was.tweak.max})`);
72
81
  }
73
82
  // Values the design builds from that changed without any variable changing: a rule's value
74
83
  for (const e of after.entries) if (e.name.includes('.')) { const b = val(before, e.name); if (b && !same(b.value, e.value)) add('patch', `rule ${e.name} ${fmt(b.value)} → ${fmt(e.value)}`); }
@@ -90,7 +99,7 @@ export function compare(before, after) {
90
99
  const vb = before.object.variants || {}, va = after.object.variants || {};
91
100
  for (const [k, v] of Object.entries(vb)) {
92
101
  if (!va[k]) add('major', `variant ${k} removed`);
93
- else if (JSON.stringify(v) !== JSON.stringify(va[k])) add('major', `variant ${k} changed`);
102
+ else if (JSON.stringify(sorted(v)) !== JSON.stringify(sorted(va[k]))) add('major', `variant ${k} changed`);
94
103
  }
95
104
  for (const k of Object.keys(va)) if (!vb[k]) add('minor', `variant ${k} added`);
96
105
  // Layout files: compared only on what this object reads of each key (its layout variable's fields), plus positions