@emasoft/svg-matrix 1.0.30 → 1.0.31
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/bin/svg-matrix.js +310 -61
- package/bin/svglinter.cjs +102 -3
- package/bin/svgm.js +236 -27
- package/package.json +1 -1
- package/src/animation-optimization.js +137 -17
- package/src/animation-references.js +123 -6
- package/src/arc-length.js +213 -4
- package/src/bezier-analysis.js +217 -21
- package/src/bezier-intersections.js +275 -12
- package/src/browser-verify.js +237 -4
- package/src/clip-path-resolver.js +168 -0
- package/src/convert-path-data.js +479 -28
- package/src/css-specificity.js +73 -10
- package/src/douglas-peucker.js +219 -2
- package/src/flatten-pipeline.js +284 -26
- package/src/geometry-to-path.js +250 -25
- package/src/gjk-collision.js +236 -33
- package/src/index.js +261 -3
- package/src/inkscape-support.js +86 -28
- package/src/logger.js +48 -3
- package/src/marker-resolver.js +278 -74
- package/src/mask-resolver.js +265 -66
- package/src/matrix.js +44 -5
- package/src/mesh-gradient.js +352 -102
- package/src/off-canvas-detection.js +382 -13
- package/src/path-analysis.js +192 -18
- package/src/path-data-plugins.js +309 -5
- package/src/path-optimization.js +129 -5
- package/src/path-simplification.js +188 -32
- package/src/pattern-resolver.js +454 -106
- package/src/polygon-clip.js +324 -1
- package/src/svg-boolean-ops.js +226 -9
- package/src/svg-collections.js +7 -5
- package/src/svg-flatten.js +386 -62
- package/src/svg-parser.js +179 -8
- package/src/svg-rendering-context.js +235 -6
- package/src/svg-toolbox.js +45 -8
- package/src/svg2-polyfills.js +40 -10
- package/src/transform-decomposition.js +258 -32
- package/src/transform-optimization.js +259 -13
- package/src/transforms2d.js +82 -9
- package/src/transforms3d.js +62 -10
- package/src/use-symbol-resolver.js +286 -42
- package/src/vector.js +64 -8
- package/src/verification.js +392 -1
package/bin/svglinter.cjs
CHANGED
|
@@ -75,6 +75,10 @@ function getTomlParser() {
|
|
|
75
75
|
* @returns {Object} Parsed object with sections as nested objects
|
|
76
76
|
*/
|
|
77
77
|
function parseIni(content) {
|
|
78
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
79
|
+
if (typeof content !== "string") {
|
|
80
|
+
throw new TypeError("parseIni expects a string argument");
|
|
81
|
+
}
|
|
78
82
|
const result = {};
|
|
79
83
|
let currentSection = result;
|
|
80
84
|
|
|
@@ -97,7 +101,8 @@ function parseIni(content) {
|
|
|
97
101
|
|
|
98
102
|
// Key=value pair
|
|
99
103
|
const eqIndex = trimmed.indexOf("=");
|
|
100
|
-
|
|
104
|
+
// Why: Use >= 0 to handle edge case where "=" is first character (eqIndex === 0)
|
|
105
|
+
if (eqIndex >= 0) {
|
|
101
106
|
const key = trimmed.slice(0, eqIndex).trim();
|
|
102
107
|
let value = trimmed.slice(eqIndex + 1).trim();
|
|
103
108
|
|
|
@@ -134,6 +139,10 @@ function parseIni(content) {
|
|
|
134
139
|
* @returns {string} JSON content with comments removed
|
|
135
140
|
*/
|
|
136
141
|
function stripJsonComments(str) {
|
|
142
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
143
|
+
if (typeof str !== "string") {
|
|
144
|
+
throw new TypeError("stripJsonComments expects a string argument");
|
|
145
|
+
}
|
|
137
146
|
let result = "";
|
|
138
147
|
let i = 0;
|
|
139
148
|
const len = str.length;
|
|
@@ -1061,6 +1070,15 @@ function getNestedValue(obj, keyPath) {
|
|
|
1061
1070
|
* @throws {Error} If parsing fails or format is unsupported
|
|
1062
1071
|
*/
|
|
1063
1072
|
function parseConfigContent(content, filepath) {
|
|
1073
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
1074
|
+
if (typeof content !== "string") {
|
|
1075
|
+
throw new TypeError("parseConfigContent expects content to be a string");
|
|
1076
|
+
}
|
|
1077
|
+
if (typeof filepath !== "string" || filepath.trim() === "") {
|
|
1078
|
+
throw new TypeError(
|
|
1079
|
+
"parseConfigContent expects filepath to be a non-empty string",
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1064
1082
|
const ext = path.extname(filepath).toLowerCase();
|
|
1065
1083
|
const basename = path.basename(filepath).toLowerCase();
|
|
1066
1084
|
|
|
@@ -1277,6 +1295,10 @@ function loadConfig(configPath) {
|
|
|
1277
1295
|
* @returns {Object} Parsed arguments object
|
|
1278
1296
|
*/
|
|
1279
1297
|
function parseArgs(argv) {
|
|
1298
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
1299
|
+
if (!Array.isArray(argv) || argv.length < 2) {
|
|
1300
|
+
throw new TypeError("parseArgs expects argv to be an array with at least 2 elements");
|
|
1301
|
+
}
|
|
1280
1302
|
const args = {
|
|
1281
1303
|
files: [],
|
|
1282
1304
|
fix: false,
|
|
@@ -1954,6 +1976,12 @@ ${c("bold", "Usage:")}
|
|
|
1954
1976
|
* @returns {void}
|
|
1955
1977
|
*/
|
|
1956
1978
|
function explainRule(code) {
|
|
1979
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
1980
|
+
if (typeof code !== "string" || code.trim() === "") {
|
|
1981
|
+
console.error(c("red", "Error: Rule code must be a non-empty string"));
|
|
1982
|
+
console.error(`Run 'svglinter --list-rules' to see all available rules.`);
|
|
1983
|
+
process.exit(2);
|
|
1984
|
+
}
|
|
1957
1985
|
const rule = RULES[code];
|
|
1958
1986
|
|
|
1959
1987
|
if (!rule) {
|
|
@@ -2667,6 +2695,10 @@ ${c("dim", "Run svglinter --help for more information.")}
|
|
|
2667
2695
|
* @returns {void}
|
|
2668
2696
|
*/
|
|
2669
2697
|
function showStats(results) {
|
|
2698
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
2699
|
+
if (!Array.isArray(results)) {
|
|
2700
|
+
throw new TypeError("showStats expects results to be an array");
|
|
2701
|
+
}
|
|
2670
2702
|
const stats = {};
|
|
2671
2703
|
const totalFiles = results.length;
|
|
2672
2704
|
let filesWithIssues = 0;
|
|
@@ -2748,6 +2780,10 @@ ${c("bold", "Summary:")}
|
|
|
2748
2780
|
* @returns {Promise<string[]>} Array of resolved SVG file paths
|
|
2749
2781
|
*/
|
|
2750
2782
|
async function expandFiles(patterns) {
|
|
2783
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
2784
|
+
if (!Array.isArray(patterns)) {
|
|
2785
|
+
throw new TypeError("expandFiles expects patterns to be an array");
|
|
2786
|
+
}
|
|
2751
2787
|
const files = new Set();
|
|
2752
2788
|
|
|
2753
2789
|
for (const pattern of patterns) {
|
|
@@ -3009,7 +3045,13 @@ async function findSvgFilesWithExt(dir, ext, visited = new Set()) {
|
|
|
3009
3045
|
* @returns {boolean} True if rule should be ignored
|
|
3010
3046
|
*/
|
|
3011
3047
|
function shouldIgnoreRule(code, ignoreList) {
|
|
3012
|
-
|
|
3048
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
3049
|
+
if (typeof code !== "string") {
|
|
3050
|
+
return false; // Graceful degradation for invalid code
|
|
3051
|
+
}
|
|
3052
|
+
if (!Array.isArray(ignoreList) || ignoreList.length === 0) {
|
|
3053
|
+
return false;
|
|
3054
|
+
}
|
|
3013
3055
|
|
|
3014
3056
|
for (const pattern of ignoreList) {
|
|
3015
3057
|
// Exact match
|
|
@@ -3032,7 +3074,13 @@ function shouldIgnoreRule(code, ignoreList) {
|
|
|
3032
3074
|
* @returns {boolean} True if rule is selected
|
|
3033
3075
|
*/
|
|
3034
3076
|
function isRuleSelected(code, selectList) {
|
|
3035
|
-
|
|
3077
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
3078
|
+
if (typeof code !== "string") {
|
|
3079
|
+
return false; // Graceful degradation for invalid code
|
|
3080
|
+
}
|
|
3081
|
+
if (!Array.isArray(selectList) || selectList.length === 0) {
|
|
3082
|
+
return true;
|
|
3083
|
+
}
|
|
3036
3084
|
|
|
3037
3085
|
for (const pattern of selectList) {
|
|
3038
3086
|
if (code === pattern) return true;
|
|
@@ -3119,6 +3167,17 @@ function parseInlineDisables(content) {
|
|
|
3119
3167
|
* @returns {boolean} True if issue is disabled
|
|
3120
3168
|
*/
|
|
3121
3169
|
function isDisabledByInline(issue, disabledRanges) {
|
|
3170
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
3171
|
+
if (
|
|
3172
|
+
typeof issue !== "object" ||
|
|
3173
|
+
issue === null ||
|
|
3174
|
+
typeof issue.type !== "string"
|
|
3175
|
+
) {
|
|
3176
|
+
return false; // Graceful degradation for invalid issue
|
|
3177
|
+
}
|
|
3178
|
+
if (!Array.isArray(disabledRanges)) {
|
|
3179
|
+
return false; // Graceful degradation for invalid ranges
|
|
3180
|
+
}
|
|
3122
3181
|
const code = TYPE_TO_CODE[issue.type] || "";
|
|
3123
3182
|
const line = issue.line || 0;
|
|
3124
3183
|
|
|
@@ -3941,6 +4000,15 @@ function formatResults(
|
|
|
3941
4000
|
showIgnored,
|
|
3942
4001
|
maxLineWidth = 80,
|
|
3943
4002
|
) {
|
|
4003
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
4004
|
+
if (!Array.isArray(results)) {
|
|
4005
|
+
throw new TypeError("formatResults expects results to be an array");
|
|
4006
|
+
}
|
|
4007
|
+
if (typeof outputFormat !== "string" || outputFormat.trim() === "") {
|
|
4008
|
+
throw new TypeError(
|
|
4009
|
+
"formatResults expects outputFormat to be a non-empty string",
|
|
4010
|
+
);
|
|
4011
|
+
}
|
|
3944
4012
|
switch (outputFormat) {
|
|
3945
4013
|
case "json":
|
|
3946
4014
|
return formatJson(results);
|
|
@@ -4696,6 +4764,13 @@ if (require.main === module) {
|
|
|
4696
4764
|
* @returns {Promise<Array>} Array of results
|
|
4697
4765
|
*/
|
|
4698
4766
|
async function lint(patterns, options = {}) {
|
|
4767
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
4768
|
+
if (!Array.isArray(patterns)) {
|
|
4769
|
+
throw new TypeError("lint expects patterns to be an array");
|
|
4770
|
+
}
|
|
4771
|
+
if (typeof options !== "object" || options === null) {
|
|
4772
|
+
throw new TypeError("lint expects options to be an object");
|
|
4773
|
+
}
|
|
4699
4774
|
// validateSVGAsync is dynamically imported per-file via lintFile
|
|
4700
4775
|
const files = await expandFiles(patterns);
|
|
4701
4776
|
const results = [];
|
|
@@ -4715,6 +4790,13 @@ async function lint(patterns, options = {}) {
|
|
|
4715
4790
|
* @returns {Promise<Object>} Lint result
|
|
4716
4791
|
*/
|
|
4717
4792
|
async function lintFile(filePath, options = {}) {
|
|
4793
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
4794
|
+
if (typeof filePath !== "string" || filePath.trim() === "") {
|
|
4795
|
+
throw new TypeError("lintFile expects filePath to be a non-empty string");
|
|
4796
|
+
}
|
|
4797
|
+
if (typeof options !== "object" || options === null) {
|
|
4798
|
+
throw new TypeError("lintFile expects options to be an object");
|
|
4799
|
+
}
|
|
4718
4800
|
const { validateSVGAsync } = await import("../src/svg-toolbox.js");
|
|
4719
4801
|
|
|
4720
4802
|
const content = await readFileWithTimeout(filePath);
|
|
@@ -4783,6 +4865,13 @@ async function lintFile(filePath, options = {}) {
|
|
|
4783
4865
|
* @returns {Promise<Object>} Lint result
|
|
4784
4866
|
*/
|
|
4785
4867
|
async function lintString(svgContent, options = {}) {
|
|
4868
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
4869
|
+
if (typeof svgContent !== "string") {
|
|
4870
|
+
throw new TypeError("lintString expects svgContent to be a string");
|
|
4871
|
+
}
|
|
4872
|
+
if (typeof options !== "object" || options === null) {
|
|
4873
|
+
throw new TypeError("lintString expects options to be an object");
|
|
4874
|
+
}
|
|
4786
4875
|
const { validateSVGAsync } = await import("../src/svg-toolbox.js");
|
|
4787
4876
|
|
|
4788
4877
|
const processedContent = normalizeLineEndings(stripBOM(svgContent));
|
|
@@ -4852,6 +4941,16 @@ async function lintString(svgContent, options = {}) {
|
|
|
4852
4941
|
* @returns {string} Formatted output
|
|
4853
4942
|
*/
|
|
4854
4943
|
function format(results, formatType = "stylish", options = {}) {
|
|
4944
|
+
// Why: Parameter validation prevents runtime errors from invalid input
|
|
4945
|
+
if (!Array.isArray(results)) {
|
|
4946
|
+
throw new TypeError("format expects results to be an array");
|
|
4947
|
+
}
|
|
4948
|
+
if (typeof formatType !== "string" || formatType.trim() === "") {
|
|
4949
|
+
throw new TypeError("format expects formatType to be a non-empty string");
|
|
4950
|
+
}
|
|
4951
|
+
if (typeof options !== "object" || options === null) {
|
|
4952
|
+
throw new TypeError("format expects options to be an object");
|
|
4953
|
+
}
|
|
4855
4954
|
// BUG FIX: Validate maxLineWidth bounds for library users (CLI validates in main())
|
|
4856
4955
|
let maxLineWidth = options.maxLineWidth || DEFAULT_MAX_LINE_WIDTH;
|
|
4857
4956
|
if (maxLineWidth < MIN_MAX_LINE_WIDTH) {
|