@emasoft/svg-matrix 1.0.34 → 1.2.0
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/README.md +58 -2
- package/bin/svg-matrix.js +7 -6
- package/bin/svgm.js +109 -40
- package/dist/svg-matrix.min.js +7 -7
- package/dist/svg-toolbox.min.js +125 -205
- package/dist/svgm.min.js +115 -195
- package/dist/version.json +5 -5
- package/package.json +6 -1
- package/scripts/postinstall.js +72 -41
- package/scripts/test-postinstall.js +18 -16
- package/scripts/version-sync.js +144 -49
- package/src/animation-optimization.js +190 -98
- package/src/animation-references.js +11 -3
- package/src/arc-length.js +23 -20
- package/src/bezier-analysis.js +9 -13
- package/src/bezier-intersections.js +18 -4
- package/src/browser-verify.js +35 -8
- package/src/clip-path-resolver.js +285 -114
- package/src/convert-path-data.js +20 -8
- package/src/css-specificity.js +33 -9
- package/src/douglas-peucker.js +272 -141
- package/src/geometry-to-path.js +79 -22
- package/src/gjk-collision.js +287 -126
- package/src/index.js +56 -21
- package/src/inkscape-support.js +122 -101
- package/src/logger.js +43 -27
- package/src/marker-resolver.js +201 -121
- package/src/mask-resolver.js +231 -98
- package/src/matrix.js +9 -5
- package/src/mesh-gradient.js +22 -14
- package/src/off-canvas-detection.js +53 -17
- package/src/path-optimization.js +356 -171
- package/src/path-simplification.js +671 -256
- package/src/pattern-resolver.js +1 -3
- package/src/polygon-clip.js +396 -78
- package/src/svg-boolean-ops.js +90 -23
- package/src/svg-collections.js +1546 -667
- package/src/svg-flatten.js +152 -38
- package/src/svg-matrix-lib.js +6 -4
- package/src/svg-parser.js +5 -1
- package/src/svg-rendering-context.js +3 -1
- package/src/svg-toolbox-lib.js +2 -2
- package/src/svg-toolbox.js +99 -457
- package/src/svg-validation-data.js +513 -345
- package/src/svg2-polyfills.js +177 -87
- package/src/svgm-lib.js +10 -6
- package/src/transform-optimization.js +168 -51
- package/src/transforms2d.js +73 -40
- package/src/transforms3d.js +34 -27
- package/src/use-symbol-resolver.js +175 -76
- package/src/vector.js +80 -44
- package/src/vendor/inkscape-hatch-polyfill.js +143 -108
- package/src/vendor/inkscape-hatch-polyfill.min.js +291 -1
- package/src/vendor/inkscape-mesh-polyfill.js +953 -766
- package/src/vendor/inkscape-mesh-polyfill.min.js +896 -1
- package/src/verification.js +3 -4
package/dist/version.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0
|
|
3
|
-
"buildTime": "2025-12-
|
|
2
|
+
"version": "1.2.0",
|
|
3
|
+
"buildTime": "2025-12-31T23:29:02.390Z",
|
|
4
4
|
"files": [
|
|
5
5
|
{
|
|
6
6
|
"name": "svg-matrix.min.js",
|
|
7
|
-
"size":
|
|
7
|
+
"size": 56106
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"name": "svg-toolbox.min.js",
|
|
11
|
-
"size":
|
|
11
|
+
"size": 577198
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
"name": "svgm.min.js",
|
|
15
|
-
"size":
|
|
15
|
+
"size": 602581
|
|
16
16
|
}
|
|
17
17
|
]
|
|
18
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emasoft/svg-matrix",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Arbitrary-precision matrix, vector and affine transformation library for JavaScript using decimal.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -70,6 +70,11 @@
|
|
|
70
70
|
"./dist/svg-toolbox.min.js": "./dist/svg-toolbox.min.js",
|
|
71
71
|
"./dist/svgm.min.js": "./dist/svgm.min.js"
|
|
72
72
|
},
|
|
73
|
+
"browser": {
|
|
74
|
+
"./src/svg-matrix-lib.js": "./dist/svg-matrix.min.js",
|
|
75
|
+
"./src/svg-toolbox-lib.js": "./dist/svg-toolbox.min.js",
|
|
76
|
+
"./src/svgm-lib.js": "./dist/svgm.min.js"
|
|
77
|
+
},
|
|
73
78
|
"author": "Emasoft",
|
|
74
79
|
"license": "MIT",
|
|
75
80
|
"homepage": "https://github.com/Emasoft/SVG-MATRIX#readme",
|
package/scripts/postinstall.js
CHANGED
|
@@ -10,74 +10,105 @@
|
|
|
10
10
|
* @license MIT
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync, createWriteStream, existsSync } from
|
|
14
|
-
import { fileURLToPath } from
|
|
15
|
-
import { dirname, join } from
|
|
13
|
+
import { readFileSync, createWriteStream, existsSync } from "fs";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
import { dirname, join } from "path";
|
|
16
16
|
|
|
17
17
|
function getVersion() {
|
|
18
18
|
try {
|
|
19
19
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
-
const pkg = JSON.parse(
|
|
21
|
-
|
|
20
|
+
const pkg = JSON.parse(
|
|
21
|
+
readFileSync(join(__dirname, "..", "package.json"), "utf8"),
|
|
22
|
+
);
|
|
23
|
+
return pkg.version || "unknown";
|
|
22
24
|
} catch {
|
|
23
|
-
return
|
|
25
|
+
return "unknown";
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
function shouldDisableColors() {
|
|
28
30
|
if (process.env.NO_COLOR !== undefined) return true;
|
|
29
|
-
if (process.platform ===
|
|
30
|
-
return !(
|
|
31
|
-
|
|
31
|
+
if (process.platform === "win32") {
|
|
32
|
+
return !(
|
|
33
|
+
process.env.WT_SESSION ||
|
|
34
|
+
process.env.ConEmuANSI === "ON" ||
|
|
35
|
+
process.env.TERM_PROGRAM ||
|
|
36
|
+
process.env.ANSICON
|
|
37
|
+
);
|
|
32
38
|
}
|
|
33
39
|
return false;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
function getColors(disabled) {
|
|
37
43
|
if (disabled) {
|
|
38
|
-
return {
|
|
39
|
-
|
|
44
|
+
return {
|
|
45
|
+
reset: "",
|
|
46
|
+
bright: "",
|
|
47
|
+
dim: "",
|
|
48
|
+
cyan: "",
|
|
49
|
+
green: "",
|
|
50
|
+
yellow: "",
|
|
51
|
+
magenta: "",
|
|
52
|
+
blue: "",
|
|
53
|
+
white: "",
|
|
54
|
+
};
|
|
40
55
|
}
|
|
41
56
|
return {
|
|
42
|
-
reset:
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
reset: "\x1b[0m",
|
|
58
|
+
bright: "\x1b[1m",
|
|
59
|
+
dim: "\x1b[2m",
|
|
60
|
+
cyan: "\x1b[36m",
|
|
61
|
+
green: "\x1b[32m",
|
|
62
|
+
yellow: "\x1b[33m",
|
|
63
|
+
magenta: "\x1b[35m",
|
|
64
|
+
blue: "\x1b[34m",
|
|
65
|
+
white: "\x1b[37m",
|
|
45
66
|
};
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
function supportsUnicode() {
|
|
49
|
-
if (process.platform ===
|
|
50
|
-
return !!(
|
|
51
|
-
|
|
70
|
+
if (process.platform === "win32") {
|
|
71
|
+
return !!(
|
|
72
|
+
process.env.WT_SESSION ||
|
|
73
|
+
process.env.CHCP === "65001" ||
|
|
74
|
+
process.env.ConEmuANSI === "ON" ||
|
|
75
|
+
process.env.TERM_PROGRAM
|
|
76
|
+
);
|
|
52
77
|
}
|
|
53
78
|
return true;
|
|
54
79
|
}
|
|
55
80
|
|
|
56
81
|
function isCI() {
|
|
57
|
-
return !!(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
return !!(
|
|
83
|
+
process.env.CI ||
|
|
84
|
+
process.env.CONTINUOUS_INTEGRATION ||
|
|
85
|
+
process.env.GITHUB_ACTIONS ||
|
|
86
|
+
process.env.GITLAB_CI ||
|
|
87
|
+
process.env.CIRCLECI ||
|
|
88
|
+
process.env.TRAVIS ||
|
|
89
|
+
process.env.JENKINS_URL ||
|
|
90
|
+
process.env.BUILD_ID
|
|
91
|
+
);
|
|
61
92
|
}
|
|
62
93
|
|
|
63
94
|
// Strip ANSI escape codes for accurate length calculation
|
|
64
95
|
function stripAnsi(s) {
|
|
65
|
-
return s.replace(/\x1b\[[0-9;]*m/g,
|
|
96
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
66
97
|
}
|
|
67
98
|
|
|
68
99
|
// Pad string to width W (accounting for ANSI codes)
|
|
69
100
|
function pad(s, w) {
|
|
70
101
|
const visible = stripAnsi(s).length;
|
|
71
|
-
return s +
|
|
102
|
+
return s + " ".repeat(Math.max(0, w - visible));
|
|
72
103
|
}
|
|
73
104
|
|
|
74
105
|
// Get a writable stream that bypasses npm's output suppression
|
|
75
106
|
function getOutputStream() {
|
|
76
107
|
// On Unix, try to write directly to /dev/tty (the terminal)
|
|
77
108
|
// This bypasses npm's stdout/stderr redirection
|
|
78
|
-
if (process.platform !==
|
|
109
|
+
if (process.platform !== "win32" && existsSync("/dev/tty")) {
|
|
79
110
|
try {
|
|
80
|
-
return createWriteStream(
|
|
111
|
+
return createWriteStream("/dev/tty");
|
|
81
112
|
} catch {
|
|
82
113
|
// Fall back to stderr
|
|
83
114
|
}
|
|
@@ -95,8 +126,8 @@ function showWelcome() {
|
|
|
95
126
|
const u = supportsUnicode();
|
|
96
127
|
|
|
97
128
|
const B = u
|
|
98
|
-
? { tl:
|
|
99
|
-
: { tl:
|
|
129
|
+
? { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│", dot: "•" }
|
|
130
|
+
: { tl: "+", tr: "+", bl: "+", br: "+", h: "-", v: "|", dot: "*" };
|
|
100
131
|
|
|
101
132
|
const W = 70;
|
|
102
133
|
const hr = B.h.repeat(W);
|
|
@@ -111,34 +142,34 @@ ${c.cyan}${B.tl}${hr}${B.tr}${c.reset}
|
|
|
111
142
|
${c.cyan}${B.v}${c.reset}${R(` ${c.bright}@emasoft/svg-matrix${c.reset} v${version}`)}${c.cyan}${B.v}${c.reset}
|
|
112
143
|
${c.cyan}${B.v}${c.reset}${R(` ${c.dim}Arbitrary-precision SVG transforms with decimal.js${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
113
144
|
${c.cyan}${B.v}${hr}${B.v}${c.reset}
|
|
114
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
145
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
115
146
|
${c.cyan}${B.v}${c.reset}${R(` ${c.yellow}CLI Commands:${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
116
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
147
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
117
148
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}svg-matrix flatten${c.reset} ${c.dim}Bake transforms into path coordinates${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
118
149
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}svg-matrix convert${c.reset} ${c.dim}Convert shapes to <path> elements${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
119
150
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}svg-matrix normalize${c.reset} ${c.dim}Convert paths to cubic Beziers${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
120
151
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}svg-matrix info${c.reset} ${c.dim}Show SVG file information${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
121
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
152
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
122
153
|
${c.cyan}${B.v}${c.reset}${R(` ${c.dim}Run${c.reset} svg-matrix --help ${c.dim}or${c.reset} svg-matrix <cmd> --help`)}${c.cyan}${B.v}${c.reset}
|
|
123
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
154
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
124
155
|
${c.cyan}${B.v}${hr}${B.v}${c.reset}
|
|
125
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
156
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
126
157
|
${c.cyan}${B.v}${c.reset}${R(` ${c.yellow}JavaScript API:${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
127
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
158
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
128
159
|
${c.cyan}${B.v}${c.reset}${R(` ${c.magenta}import${c.reset} { Matrix, Vector, Transforms2D } ${c.magenta}from${c.reset} '@emasoft/svg-matrix'`)}${c.cyan}${B.v}${c.reset}
|
|
129
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
160
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
130
161
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot} Matrix${c.reset} Arbitrary-precision matrix operations`)}${c.cyan}${B.v}${c.reset}
|
|
131
162
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot} Vector${c.reset} High-precision vector math`)}${c.cyan}${B.v}${c.reset}
|
|
132
163
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot} Transforms2D${c.reset} rotate, scale, translate, skew, reflect`)}${c.cyan}${B.v}${c.reset}
|
|
133
164
|
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot} Transforms3D${c.reset} 3D affine transformations`)}${c.cyan}${B.v}${c.reset}
|
|
134
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
165
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
135
166
|
${c.cyan}${B.v}${hr}${B.v}${c.reset}
|
|
136
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
137
|
-
${c.cyan}${B.v}${c.reset}${R(` ${c.yellow}New in v1.0
|
|
138
|
-
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset}
|
|
139
|
-
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset}
|
|
140
|
-
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset}
|
|
141
|
-
${c.cyan}${B.v}${c.reset}${R(
|
|
167
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
168
|
+
${c.cyan}${B.v}${c.reset}${R(` ${c.yellow}New in v1.1.0:${c.reset}`)}${c.cyan}${B.v}${c.reset}
|
|
169
|
+
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} Universal browser bundles (dist/svg-matrix.min.js, etc.)`)}${c.cyan}${B.v}${c.reset}
|
|
170
|
+
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} I/O abstraction: loadInput/saveOutput for all environments`)}${c.cyan}${B.v}${c.reset}
|
|
171
|
+
${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} 70+ SVG toolbox functions, 163+ tests passing`)}${c.cyan}${B.v}${c.reset}
|
|
172
|
+
${c.cyan}${B.v}${c.reset}${R("")}${c.cyan}${B.v}${c.reset}
|
|
142
173
|
${c.cyan}${B.bl}${hr}${B.br}${c.reset}
|
|
143
174
|
|
|
144
175
|
${c.blue}Docs:${c.reset} https://github.com/Emasoft/SVG-MATRIX#readme
|
|
@@ -5,24 +5,24 @@
|
|
|
5
5
|
* proper rendering of box characters.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { spawn } from
|
|
8
|
+
import { spawn } from "child_process";
|
|
9
9
|
|
|
10
|
-
console.log(
|
|
10
|
+
console.log("\n=== Testing postinstall.js Unicode/ASCII support ===\n");
|
|
11
11
|
|
|
12
12
|
const tests = [
|
|
13
13
|
{
|
|
14
|
-
name:
|
|
15
|
-
env: { LANG:
|
|
14
|
+
name: "Unicode mode (UTF-8 locale)",
|
|
15
|
+
env: { LANG: "en_US.UTF-8" },
|
|
16
16
|
expectUnicode: true,
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
name:
|
|
20
|
-
env: { LANG:
|
|
19
|
+
name: "ASCII fallback (C locale)",
|
|
20
|
+
env: { LANG: "C" },
|
|
21
21
|
expectUnicode: false,
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
|
-
name:
|
|
25
|
-
env: { LC_CTYPE:
|
|
24
|
+
name: "UTF-8 in LC_CTYPE",
|
|
25
|
+
env: { LC_CTYPE: "en_US.UTF-8", LANG: "" },
|
|
26
26
|
expectUnicode: true,
|
|
27
27
|
},
|
|
28
28
|
];
|
|
@@ -37,18 +37,18 @@ async function runTest(test) {
|
|
|
37
37
|
// Prepare environment
|
|
38
38
|
const env = { ...process.env, ...test.env };
|
|
39
39
|
|
|
40
|
-
const child = spawn(
|
|
40
|
+
const child = spawn("node", ["scripts/postinstall.js"], {
|
|
41
41
|
env,
|
|
42
|
-
stdio:
|
|
42
|
+
stdio: "pipe",
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
let output =
|
|
45
|
+
let output = "";
|
|
46
46
|
|
|
47
|
-
child.stdout.on(
|
|
47
|
+
child.stdout.on("data", (data) => {
|
|
48
48
|
output += data.toString();
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
child.on(
|
|
51
|
+
child.on("close", () => {
|
|
52
52
|
// Check for Unicode or ASCII box characters
|
|
53
53
|
const hasUnicodeBox = /[╭╮╰╯─│]/.test(output);
|
|
54
54
|
const hasAsciiBox = /[+\-|]/.test(output);
|
|
@@ -60,12 +60,14 @@ async function runTest(test) {
|
|
|
60
60
|
console.log(` ✓ PASS: ASCII fallback characters detected\n`);
|
|
61
61
|
resolve(true);
|
|
62
62
|
} else {
|
|
63
|
-
console.log(
|
|
63
|
+
console.log(
|
|
64
|
+
` ✗ FAIL: Expected ${test.expectUnicode ? "Unicode" : "ASCII"} but got different output\n`,
|
|
65
|
+
);
|
|
64
66
|
resolve(false);
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
69
|
|
|
68
|
-
child.on(
|
|
70
|
+
child.on("error", (error) => {
|
|
69
71
|
console.log(` ✗ FAIL: ${error.message}\n`);
|
|
70
72
|
resolve(false);
|
|
71
73
|
});
|
|
@@ -82,7 +84,7 @@ async function runAllTests() {
|
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
console.log(
|
|
87
|
+
console.log("=== Test Results ===");
|
|
86
88
|
console.log(`Passed: ${passed}`);
|
|
87
89
|
console.log(`Failed: ${failed}`);
|
|
88
90
|
console.log(`Total: ${passed + failed}\n`);
|
package/scripts/version-sync.js
CHANGED
|
@@ -18,27 +18,41 @@
|
|
|
18
18
|
* @license MIT
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { readFileSync, writeFileSync } from
|
|
22
|
-
import { fileURLToPath } from
|
|
23
|
-
import { dirname, join } from
|
|
21
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
22
|
+
import { fileURLToPath } from "url";
|
|
23
|
+
import { dirname, join } from "path";
|
|
24
24
|
|
|
25
25
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
|
-
const ROOT = join(__dirname,
|
|
26
|
+
const ROOT = join(__dirname, "..");
|
|
27
27
|
|
|
28
28
|
// ANSI colors for output
|
|
29
|
-
const colors =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const colors =
|
|
30
|
+
process.env.NO_COLOR !== undefined
|
|
31
|
+
? {
|
|
32
|
+
reset: "",
|
|
33
|
+
red: "",
|
|
34
|
+
green: "",
|
|
35
|
+
yellow: "",
|
|
36
|
+
cyan: "",
|
|
37
|
+
dim: "",
|
|
38
|
+
bright: "",
|
|
39
|
+
}
|
|
40
|
+
: {
|
|
41
|
+
reset: "\x1b[0m",
|
|
42
|
+
red: "\x1b[31m",
|
|
43
|
+
green: "\x1b[32m",
|
|
44
|
+
yellow: "\x1b[33m",
|
|
45
|
+
cyan: "\x1b[36m",
|
|
46
|
+
dim: "\x1b[2m",
|
|
47
|
+
bright: "\x1b[1m",
|
|
48
|
+
};
|
|
35
49
|
|
|
36
50
|
/**
|
|
37
51
|
* Read the canonical version from package.json
|
|
38
52
|
* @returns {string} Version string
|
|
39
53
|
*/
|
|
40
54
|
function getPackageVersion() {
|
|
41
|
-
const pkg = JSON.parse(readFileSync(join(ROOT,
|
|
55
|
+
const pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
|
|
42
56
|
return pkg.version;
|
|
43
57
|
}
|
|
44
58
|
|
|
@@ -47,7 +61,7 @@ function getPackageVersion() {
|
|
|
47
61
|
* @returns {string|null} Version string or null if not found
|
|
48
62
|
*/
|
|
49
63
|
function getIndexVersion() {
|
|
50
|
-
const content = readFileSync(join(ROOT,
|
|
64
|
+
const content = readFileSync(join(ROOT, "src", "index.js"), "utf8");
|
|
51
65
|
const match = content.match(/export const VERSION = ['"]([^'"]+)['"]/);
|
|
52
66
|
return match ? match[1] : null;
|
|
53
67
|
}
|
|
@@ -57,18 +71,66 @@ function getIndexVersion() {
|
|
|
57
71
|
* @returns {string|null} Version string or null if not found
|
|
58
72
|
*/
|
|
59
73
|
function getIndexJsDocVersion() {
|
|
60
|
-
const content = readFileSync(join(ROOT,
|
|
74
|
+
const content = readFileSync(join(ROOT, "src", "index.js"), "utf8");
|
|
61
75
|
const match = content.match(/@version\s+(\S+)/);
|
|
62
76
|
return match ? match[1] : null;
|
|
63
77
|
}
|
|
64
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Get version from a library entry point file
|
|
81
|
+
* @param {string} filename - Filename relative to src/
|
|
82
|
+
* @returns {string|null} Version string or null if not found
|
|
83
|
+
*/
|
|
84
|
+
function getLibVersion(filename) {
|
|
85
|
+
try {
|
|
86
|
+
const content = readFileSync(join(ROOT, "src", filename), "utf8");
|
|
87
|
+
const match = content.match(/export const VERSION = ['"]([^'"]+)['"]/);
|
|
88
|
+
return match ? match[1] : null;
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Update a library entry point file's VERSION constant and @version jsdoc
|
|
96
|
+
* @param {string} filename - Filename relative to src/
|
|
97
|
+
* @param {string} version - New version
|
|
98
|
+
* @returns {boolean} True if updated
|
|
99
|
+
*/
|
|
100
|
+
function updateLibVersion(filename, version) {
|
|
101
|
+
const filePath = join(ROOT, "src", filename);
|
|
102
|
+
try {
|
|
103
|
+
let content = readFileSync(filePath, "utf8");
|
|
104
|
+
const original = content;
|
|
105
|
+
|
|
106
|
+
// Update VERSION constant
|
|
107
|
+
content = content.replace(
|
|
108
|
+
/export const VERSION = ['"][^'"]+['"]/,
|
|
109
|
+
`export const VERSION = "${version}"`,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Update @version in jsdoc
|
|
113
|
+
content = content.replace(/@version\s+\S+/, `@version ${version}`);
|
|
114
|
+
|
|
115
|
+
if (content !== original) {
|
|
116
|
+
writeFileSync(filePath, content, "utf8");
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
// File might not exist
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
65
125
|
/**
|
|
66
126
|
* Get version from package-lock.json root
|
|
67
127
|
* @returns {string|null} Version string or null if not found
|
|
68
128
|
*/
|
|
69
129
|
function getLockfileVersion() {
|
|
70
130
|
try {
|
|
71
|
-
const lock = JSON.parse(
|
|
131
|
+
const lock = JSON.parse(
|
|
132
|
+
readFileSync(join(ROOT, "package-lock.json"), "utf8"),
|
|
133
|
+
);
|
|
72
134
|
return lock.version;
|
|
73
135
|
} catch {
|
|
74
136
|
return null;
|
|
@@ -81,24 +143,21 @@ function getLockfileVersion() {
|
|
|
81
143
|
* @returns {boolean} True if updated
|
|
82
144
|
*/
|
|
83
145
|
function updateIndexVersion(version) {
|
|
84
|
-
const filePath = join(ROOT,
|
|
85
|
-
let content = readFileSync(filePath,
|
|
146
|
+
const filePath = join(ROOT, "src", "index.js");
|
|
147
|
+
let content = readFileSync(filePath, "utf8");
|
|
86
148
|
const original = content;
|
|
87
149
|
|
|
88
150
|
// Update VERSION constant
|
|
89
151
|
content = content.replace(
|
|
90
152
|
/export const VERSION = ['"][^'"]+['"]/,
|
|
91
|
-
`export const VERSION = '${version}'
|
|
153
|
+
`export const VERSION = '${version}'`,
|
|
92
154
|
);
|
|
93
155
|
|
|
94
156
|
// Update @version in jsdoc
|
|
95
|
-
content = content.replace(
|
|
96
|
-
/@version\s+\S+/,
|
|
97
|
-
`@version ${version}`
|
|
98
|
-
);
|
|
157
|
+
content = content.replace(/@version\s+\S+/, `@version ${version}`);
|
|
99
158
|
|
|
100
159
|
if (content !== original) {
|
|
101
|
-
writeFileSync(filePath, content,
|
|
160
|
+
writeFileSync(filePath, content, "utf8");
|
|
102
161
|
return true;
|
|
103
162
|
}
|
|
104
163
|
return false;
|
|
@@ -110,19 +169,19 @@ function updateIndexVersion(version) {
|
|
|
110
169
|
* @returns {boolean} True if updated
|
|
111
170
|
*/
|
|
112
171
|
function updateLockfileVersion(version) {
|
|
113
|
-
const filePath = join(ROOT,
|
|
172
|
+
const filePath = join(ROOT, "package-lock.json");
|
|
114
173
|
try {
|
|
115
|
-
const lock = JSON.parse(readFileSync(filePath,
|
|
174
|
+
const lock = JSON.parse(readFileSync(filePath, "utf8"));
|
|
116
175
|
const original = JSON.stringify(lock);
|
|
117
176
|
|
|
118
177
|
lock.version = version;
|
|
119
|
-
if (lock.packages && lock.packages[
|
|
120
|
-
lock.packages[
|
|
178
|
+
if (lock.packages && lock.packages[""]) {
|
|
179
|
+
lock.packages[""].version = version;
|
|
121
180
|
}
|
|
122
181
|
|
|
123
|
-
const updated = JSON.stringify(lock, null, 2) +
|
|
182
|
+
const updated = JSON.stringify(lock, null, 2) + "\n";
|
|
124
183
|
if (updated !== original) {
|
|
125
|
-
writeFileSync(filePath, updated,
|
|
184
|
+
writeFileSync(filePath, updated, "utf8");
|
|
126
185
|
return true;
|
|
127
186
|
}
|
|
128
187
|
} catch {
|
|
@@ -131,6 +190,13 @@ function updateLockfileVersion(version) {
|
|
|
131
190
|
return false;
|
|
132
191
|
}
|
|
133
192
|
|
|
193
|
+
// Library entry points to sync (relative to src/)
|
|
194
|
+
const LIB_ENTRY_POINTS = [
|
|
195
|
+
"svg-matrix-lib.js",
|
|
196
|
+
"svg-toolbox-lib.js",
|
|
197
|
+
"svgm-lib.js",
|
|
198
|
+
];
|
|
199
|
+
|
|
134
200
|
/**
|
|
135
201
|
* Check if all versions are in sync
|
|
136
202
|
* @returns {{inSync: boolean, versions: Object}}
|
|
@@ -142,15 +208,27 @@ function checkVersions() {
|
|
|
142
208
|
const lockVersion = getLockfileVersion();
|
|
143
209
|
|
|
144
210
|
const versions = {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
211
|
+
"package.json": pkgVersion,
|
|
212
|
+
"src/index.js (VERSION)": indexVersion,
|
|
213
|
+
"src/index.js (@version)": jsDocVersion,
|
|
214
|
+
"package-lock.json": lockVersion,
|
|
149
215
|
};
|
|
150
216
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
217
|
+
// WHY: Check VERSION constants in all library entry points
|
|
218
|
+
let allLibsInSync = true;
|
|
219
|
+
for (const lib of LIB_ENTRY_POINTS) {
|
|
220
|
+
const libVersion = getLibVersion(lib);
|
|
221
|
+
versions[`src/${lib}`] = libVersion;
|
|
222
|
+
if (libVersion !== null && libVersion !== pkgVersion) {
|
|
223
|
+
allLibsInSync = false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const inSync =
|
|
228
|
+
indexVersion === pkgVersion &&
|
|
229
|
+
jsDocVersion === pkgVersion &&
|
|
230
|
+
(lockVersion === null || lockVersion === pkgVersion) &&
|
|
231
|
+
allLibsInSync;
|
|
154
232
|
|
|
155
233
|
return { inSync, versions, canonical: pkgVersion };
|
|
156
234
|
}
|
|
@@ -161,7 +239,7 @@ function checkVersions() {
|
|
|
161
239
|
function main() {
|
|
162
240
|
const args = process.argv.slice(2);
|
|
163
241
|
|
|
164
|
-
if (args.includes(
|
|
242
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
165
243
|
console.log(`
|
|
166
244
|
${colors.bright}version-sync.js${colors.reset} - Synchronize version numbers across the codebase
|
|
167
245
|
|
|
@@ -171,29 +249,34 @@ ${colors.cyan}Usage:${colors.reset}
|
|
|
171
249
|
node scripts/version-sync.js --help Show this help
|
|
172
250
|
|
|
173
251
|
${colors.cyan}Files updated:${colors.reset}
|
|
174
|
-
- src/index.js (VERSION constant)
|
|
175
|
-
- src/
|
|
252
|
+
- src/index.js (VERSION constant and @version jsdoc)
|
|
253
|
+
- src/svg-matrix-lib.js (VERSION constant and @version jsdoc)
|
|
254
|
+
- src/svg-toolbox-lib.js (VERSION constant and @version jsdoc)
|
|
255
|
+
- src/svgm-lib.js (VERSION constant and @version jsdoc)
|
|
176
256
|
- package-lock.json (if present)
|
|
177
257
|
`);
|
|
178
258
|
process.exit(0);
|
|
179
259
|
}
|
|
180
260
|
|
|
181
|
-
const checkOnly = args.includes(
|
|
261
|
+
const checkOnly = args.includes("--check");
|
|
182
262
|
const { inSync, versions, canonical } = checkVersions();
|
|
183
263
|
|
|
184
264
|
console.log(`${colors.bright}Version Check${colors.reset}`);
|
|
185
|
-
console.log(`${colors.dim}${
|
|
265
|
+
console.log(`${colors.dim}${"─".repeat(50)}${colors.reset}`);
|
|
186
266
|
|
|
187
267
|
for (const [file, version] of Object.entries(versions)) {
|
|
188
|
-
const status =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
268
|
+
const status =
|
|
269
|
+
version === canonical
|
|
270
|
+
? `${colors.green}OK${colors.reset}`
|
|
271
|
+
: version === null
|
|
272
|
+
? `${colors.yellow}N/A${colors.reset}`
|
|
273
|
+
: `${colors.red}MISMATCH${colors.reset}`;
|
|
274
|
+
console.log(
|
|
275
|
+
` ${file.padEnd(30)} ${(version || "N/A").padEnd(12)} ${status}`,
|
|
276
|
+
);
|
|
194
277
|
}
|
|
195
278
|
|
|
196
|
-
console.log(`${colors.dim}${
|
|
279
|
+
console.log(`${colors.dim}${"─".repeat(50)}${colors.reset}`);
|
|
197
280
|
console.log(` Canonical version: ${colors.cyan}${canonical}${colors.reset}`);
|
|
198
281
|
|
|
199
282
|
if (inSync) {
|
|
@@ -203,7 +286,9 @@ ${colors.cyan}Files updated:${colors.reset}
|
|
|
203
286
|
|
|
204
287
|
if (checkOnly) {
|
|
205
288
|
console.log(`\n${colors.red}Version mismatch detected!${colors.reset}`);
|
|
206
|
-
console.log(
|
|
289
|
+
console.log(
|
|
290
|
+
`Run ${colors.cyan}node scripts/version-sync.js${colors.reset} to fix.`,
|
|
291
|
+
);
|
|
207
292
|
process.exit(1);
|
|
208
293
|
}
|
|
209
294
|
|
|
@@ -220,8 +305,18 @@ ${colors.cyan}Files updated:${colors.reset}
|
|
|
220
305
|
updated++;
|
|
221
306
|
}
|
|
222
307
|
|
|
308
|
+
// WHY: Sync VERSION constants in all library entry points
|
|
309
|
+
for (const lib of LIB_ENTRY_POINTS) {
|
|
310
|
+
if (updateLibVersion(lib, canonical)) {
|
|
311
|
+
console.log(` ${colors.green}Updated${colors.reset} src/${lib}`);
|
|
312
|
+
updated++;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
223
316
|
if (updated > 0) {
|
|
224
|
-
console.log(
|
|
317
|
+
console.log(
|
|
318
|
+
`\n${colors.green}Synced ${updated} file(s) to version ${canonical}${colors.reset}`,
|
|
319
|
+
);
|
|
225
320
|
} else {
|
|
226
321
|
console.log(`\n${colors.green}All files already in sync.${colors.reset}`);
|
|
227
322
|
}
|