svg_conform 0.1.11 → 0.1.12
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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +1 -0
- data/.rubocop_todo.yml +134 -25
- data/CLAUDE.md +115 -0
- data/lib/svg_conform/cli.rb +14 -7
- data/lib/svg_conform/commands/check.rb +41 -45
- data/lib/svg_conform/commands/profiles.rb +3 -3
- data/lib/svg_conform/commands/svgcheck_compare.rb +29 -37
- data/lib/svg_conform/commands/svgcheck_compatibility.rb +11 -6
- data/lib/svg_conform/requirements/base_requirement.rb +3 -3
- data/lib/svg_conform/version.rb +1 -1
- data/spec/svg_conform/commands/check_command_spec.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 162ed406493cfabb9a3cb94a3a6ba2ee3ed4ae95d3edf912034c17974d5cc496
|
|
4
|
+
data.tar.gz: 56cb8b2d5e94bcce3cc1e32b513a738ac2140f0e86848e6c101fc08ed16cfc7a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1bcbdf1a7dbc43f044aa875d7a310b85064bcd47d5f227cf962303d1f3a0d024fe926b87ffc201fbf5256b0e150cfa71b5a8702e84b062b37d7935a50bc96b5b
|
|
7
|
+
data.tar.gz: 817c9ac5561b509c39e9f4d228b4165d3943a242bb83a88c77e8388bbce99c6746dc9c9df464a729ce44160c331b08ce9dfcc06e98c676c2944631eaf35346e5
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2026-
|
|
3
|
+
# on 2026-04-04 08:57:24 UTC using RuboCop version 1.86.0.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
@@ -8,11 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
# Offense count: 1
|
|
10
10
|
# This cop supports safe autocorrection (--autocorrect).
|
|
11
|
-
|
|
12
|
-
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
13
|
-
Layout/ArgumentAlignment:
|
|
11
|
+
Gemspec/RequireMFA:
|
|
14
12
|
Exclude:
|
|
15
|
-
- '
|
|
13
|
+
- 'svg_conform.gemspec'
|
|
16
14
|
|
|
17
15
|
# Offense count: 1
|
|
18
16
|
# This cop supports safe autocorrection (--autocorrect).
|
|
@@ -24,28 +22,42 @@ Layout/BlockAlignment:
|
|
|
24
22
|
|
|
25
23
|
# Offense count: 1
|
|
26
24
|
# This cop supports safe autocorrection (--autocorrect).
|
|
27
|
-
# Configuration parameters:
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
|
31
|
-
Layout/HashAlignment:
|
|
25
|
+
# Configuration parameters: EnforcedStyle.
|
|
26
|
+
# SupportedStyles: leading, trailing
|
|
27
|
+
Layout/LineContinuationLeadingSpace:
|
|
32
28
|
Exclude:
|
|
33
|
-
- 'spec/
|
|
29
|
+
- 'spec/svgcheck_compatibility_spec.rb'
|
|
30
|
+
|
|
31
|
+
# Offense count: 5
|
|
32
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
33
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
34
|
+
# SupportedStyles: aligned, indented
|
|
35
|
+
Layout/LineEndStringConcatenationIndentation:
|
|
36
|
+
Exclude:
|
|
37
|
+
- 'lib/svg_conform/external_checkers/svgcheck/validation_pipeline.rb'
|
|
38
|
+
- 'spec/svgcheck_compatibility_spec.rb'
|
|
34
39
|
|
|
35
|
-
# Offense count:
|
|
40
|
+
# Offense count: 649
|
|
36
41
|
# This cop supports safe autocorrection (--autocorrect).
|
|
37
42
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
38
43
|
# URISchemes: http, https
|
|
39
44
|
Layout/LineLength:
|
|
40
45
|
Enabled: false
|
|
41
46
|
|
|
42
|
-
# Offense count:
|
|
47
|
+
# Offense count: 13
|
|
43
48
|
# This cop supports safe autocorrection (--autocorrect).
|
|
44
|
-
|
|
45
|
-
Layout/TrailingWhitespace:
|
|
49
|
+
Lint/AmbiguousOperatorPrecedence:
|
|
46
50
|
Exclude:
|
|
47
|
-
- 'lib/svg_conform/
|
|
48
|
-
- '
|
|
51
|
+
- 'lib/svg_conform/compatibility_analyzer.rb'
|
|
52
|
+
- 'lib/svg_conform/semantic_comparator.rb'
|
|
53
|
+
- 'lib/svg_conform/validation_result.rb'
|
|
54
|
+
|
|
55
|
+
# Offense count: 1
|
|
56
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
57
|
+
# Configuration parameters: RequireParenthesesForMethodChains.
|
|
58
|
+
Lint/AmbiguousRange:
|
|
59
|
+
Exclude:
|
|
60
|
+
- 'lib/svg_conform/compatibility/report_formatter.rb'
|
|
49
61
|
|
|
50
62
|
# Offense count: 3
|
|
51
63
|
# Configuration parameters: AllowedMethods.
|
|
@@ -73,10 +85,11 @@ Lint/DuplicateCaseCondition:
|
|
|
73
85
|
Exclude:
|
|
74
86
|
- 'lib/svg_conform/semantic_comparator.rb'
|
|
75
87
|
|
|
76
|
-
# Offense count:
|
|
88
|
+
# Offense count: 2
|
|
77
89
|
Lint/DuplicateMethods:
|
|
78
90
|
Exclude:
|
|
79
91
|
- 'lib/svg_conform/sax_validation_handler.rb'
|
|
92
|
+
- 'spec/svg_conform/validation_context_spec.rb'
|
|
80
93
|
|
|
81
94
|
# Offense count: 2
|
|
82
95
|
Lint/HashCompareByIdentity:
|
|
@@ -92,11 +105,18 @@ Lint/MissingSuper:
|
|
|
92
105
|
- 'lib/svg_conform/sax_validation_handler.rb'
|
|
93
106
|
|
|
94
107
|
# Offense count: 1
|
|
95
|
-
|
|
108
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
109
|
+
Lint/RedundantDirGlobSort:
|
|
96
110
|
Exclude:
|
|
97
|
-
- 'lib/
|
|
111
|
+
- 'lib/tasks/fixtures.rake'
|
|
98
112
|
|
|
99
|
-
# Offense count:
|
|
113
|
+
# Offense count: 1
|
|
114
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
115
|
+
Lint/SuppressedExceptionInNumberConversion:
|
|
116
|
+
Exclude:
|
|
117
|
+
- 'lib/svg_conform/remediations/viewbox_remediation.rb'
|
|
118
|
+
|
|
119
|
+
# Offense count: 145
|
|
100
120
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
101
121
|
Metrics/AbcSize:
|
|
102
122
|
Enabled: false
|
|
@@ -117,7 +137,7 @@ Metrics/BlockNesting:
|
|
|
117
137
|
Metrics/CyclomaticComplexity:
|
|
118
138
|
Enabled: false
|
|
119
139
|
|
|
120
|
-
# Offense count:
|
|
140
|
+
# Offense count: 256
|
|
121
141
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
122
142
|
Metrics/MethodLength:
|
|
123
143
|
Max: 154
|
|
@@ -132,6 +152,18 @@ Metrics/ParameterLists:
|
|
|
132
152
|
Metrics/PerceivedComplexity:
|
|
133
153
|
Enabled: false
|
|
134
154
|
|
|
155
|
+
# Offense count: 9
|
|
156
|
+
# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
|
|
157
|
+
# AllowedMethods: call
|
|
158
|
+
# WaywardPredicates: infinite?, nonzero?
|
|
159
|
+
Naming/PredicateMethod:
|
|
160
|
+
Exclude:
|
|
161
|
+
- 'lib/svg_conform/compatibility/file_processor.rb'
|
|
162
|
+
- 'lib/svg_conform/node_helpers.rb'
|
|
163
|
+
- 'lib/svg_conform/remediations/base_remediation.rb'
|
|
164
|
+
- 'lib/svg_conform/requirements/style_requirement.rb'
|
|
165
|
+
- 'lib/svg_conform/semantic_comparator.rb'
|
|
166
|
+
|
|
135
167
|
# Offense count: 2
|
|
136
168
|
# Configuration parameters: MinSize.
|
|
137
169
|
Performance/CollectionLiteralInLoop:
|
|
@@ -139,6 +171,12 @@ Performance/CollectionLiteralInLoop:
|
|
|
139
171
|
- 'lib/svg_conform/batch_report.rb'
|
|
140
172
|
- 'lib/svg_conform/compatibility/pattern_discovery.rb'
|
|
141
173
|
|
|
174
|
+
# Offense count: 2
|
|
175
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
176
|
+
Performance/MapCompact:
|
|
177
|
+
Exclude:
|
|
178
|
+
- 'lib/svg_conform/remediations/viewbox_remediation.rb'
|
|
179
|
+
|
|
142
180
|
# Offense count: 9
|
|
143
181
|
# Configuration parameters: IgnoredMetadata.
|
|
144
182
|
RSpec/DescribeClass:
|
|
@@ -168,6 +206,11 @@ RSpec/LeakyConstantDeclaration:
|
|
|
168
206
|
Exclude:
|
|
169
207
|
- 'spec/svg_conform/profiles/svg_1_2_rfc_profile_spec.rb'
|
|
170
208
|
|
|
209
|
+
# Offense count: 1
|
|
210
|
+
RSpec/LeakyLocalVariable:
|
|
211
|
+
Exclude:
|
|
212
|
+
- 'spec/svgcheck_compatibility_spec.rb'
|
|
213
|
+
|
|
171
214
|
# Offense count: 2
|
|
172
215
|
# Configuration parameters: .
|
|
173
216
|
# SupportedStyles: have_received, receive
|
|
@@ -183,11 +226,22 @@ RSpec/MultipleDescribes:
|
|
|
183
226
|
RSpec/MultipleExpectations:
|
|
184
227
|
Max: 8
|
|
185
228
|
|
|
229
|
+
# Offense count: 44
|
|
230
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
231
|
+
RSpec/Output:
|
|
232
|
+
Enabled: false
|
|
233
|
+
|
|
186
234
|
# Offense count: 1
|
|
187
235
|
Rake/MethodDefinitionInTask:
|
|
188
236
|
Exclude:
|
|
189
237
|
- 'lib/tasks/svgcheck.rake'
|
|
190
238
|
|
|
239
|
+
# Offense count: 3
|
|
240
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
241
|
+
Style/ComparableClamp:
|
|
242
|
+
Exclude:
|
|
243
|
+
- 'lib/svg_conform/css_color.rb'
|
|
244
|
+
|
|
191
245
|
# Offense count: 2
|
|
192
246
|
# This cop supports safe autocorrection (--autocorrect).
|
|
193
247
|
# Configuration parameters: EnforcedStyle, AllowComments.
|
|
@@ -205,9 +259,28 @@ Style/FormatStringToken:
|
|
|
205
259
|
EnforcedStyle: unannotated
|
|
206
260
|
|
|
207
261
|
# Offense count: 1
|
|
208
|
-
|
|
262
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
263
|
+
# Configuration parameters: AllowSplatArgument.
|
|
264
|
+
Style/HashConversion:
|
|
265
|
+
Exclude:
|
|
266
|
+
- 'lib/svg_conform/sax_validation_handler.rb'
|
|
267
|
+
|
|
268
|
+
# Offense count: 2
|
|
269
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
270
|
+
Style/HashSlice:
|
|
209
271
|
Exclude:
|
|
210
|
-
- '
|
|
272
|
+
- 'spec/svg_conform_spec.rb'
|
|
273
|
+
|
|
274
|
+
# Offense count: 1
|
|
275
|
+
# Configuration parameters: AllowedClasses.
|
|
276
|
+
Style/OneClassPerFile:
|
|
277
|
+
Exclude:
|
|
278
|
+
- 'lib/svg_conform/compatibility/report_formatter.rb'
|
|
279
|
+
|
|
280
|
+
# Offense count: 1
|
|
281
|
+
Style/OpenStructUse:
|
|
282
|
+
Exclude:
|
|
283
|
+
- 'lib/svg_conform/validator.rb'
|
|
211
284
|
|
|
212
285
|
# Offense count: 4
|
|
213
286
|
# Configuration parameters: AllowedMethods.
|
|
@@ -216,10 +289,46 @@ Style/OptionalBooleanParameter:
|
|
|
216
289
|
Exclude:
|
|
217
290
|
- 'lib/svg_conform/semantic_comparator.rb'
|
|
218
291
|
|
|
292
|
+
# Offense count: 1
|
|
293
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
294
|
+
Style/PartitionInsteadOfDoubleSelect:
|
|
295
|
+
Exclude:
|
|
296
|
+
- 'lib/svg_conform/requirements/style_requirement.rb'
|
|
297
|
+
|
|
298
|
+
# Offense count: 1
|
|
299
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
300
|
+
Style/ReduceToHash:
|
|
301
|
+
Exclude:
|
|
302
|
+
- 'lib/svg_conform/requirements/base_requirement.rb'
|
|
303
|
+
|
|
219
304
|
# Offense count: 1
|
|
220
305
|
# This cop supports safe autocorrection (--autocorrect).
|
|
221
306
|
# Configuration parameters: AllowedMethods.
|
|
222
|
-
# AllowedMethods: nonzero?
|
|
307
|
+
# AllowedMethods: infinite?, nonzero?
|
|
223
308
|
Style/RedundantCondition:
|
|
224
309
|
Exclude:
|
|
225
310
|
- 'lib/svg_conform/external_checkers/svgcheck/parser.rb'
|
|
311
|
+
|
|
312
|
+
# Offense count: 2
|
|
313
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
314
|
+
Style/RedundantFormat:
|
|
315
|
+
Exclude:
|
|
316
|
+
- 'lib/svg_conform/compatibility/report_formatter.rb'
|
|
317
|
+
|
|
318
|
+
# Offense count: 2
|
|
319
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
320
|
+
Style/SelectByKind:
|
|
321
|
+
Exclude:
|
|
322
|
+
- 'lib/svg_conform/sax_validation_handler.rb'
|
|
323
|
+
|
|
324
|
+
# Offense count: 1
|
|
325
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
326
|
+
Style/SelectByRegexp:
|
|
327
|
+
Exclude:
|
|
328
|
+
- 'lib/svg_conform/remediations/color_remediation.rb'
|
|
329
|
+
|
|
330
|
+
# Offense count: 1
|
|
331
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
332
|
+
Style/SuperArguments:
|
|
333
|
+
Exclude:
|
|
334
|
+
- 'lib/svg_conform/requirements/invalid_id_references_requirement.rb'
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Build/Test Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
bin/setup
|
|
10
|
+
|
|
11
|
+
# Run tests
|
|
12
|
+
rake spec
|
|
13
|
+
|
|
14
|
+
# Run a single spec file
|
|
15
|
+
bundle exec rspec spec/svg_conform/validator_spec.rb
|
|
16
|
+
|
|
17
|
+
# Run tests with detailed output
|
|
18
|
+
bundle exec rspec --format documentation
|
|
19
|
+
|
|
20
|
+
# Run linting
|
|
21
|
+
rake rubocop
|
|
22
|
+
|
|
23
|
+
# Run both tests and linting
|
|
24
|
+
rake
|
|
25
|
+
|
|
26
|
+
# Run a specific test by line number
|
|
27
|
+
bundle exec rspec spec/svg_conform/validator_spec.rb:42
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Architecture
|
|
31
|
+
|
|
32
|
+
### Two-Mode Validation System
|
|
33
|
+
|
|
34
|
+
SvgConform uses two distinct operating modes:
|
|
35
|
+
|
|
36
|
+
1. **SAX Validation Mode** (always used for validation):
|
|
37
|
+
- Memory-safe streaming XML parser
|
|
38
|
+
- Constant memory regardless of file size
|
|
39
|
+
- Handles files of any size (tested with 100MB+)
|
|
40
|
+
- Read-only, cannot modify documents
|
|
41
|
+
- Implemented via `SaxDocument` and `SaxValidationHandler`
|
|
42
|
+
|
|
43
|
+
2. **DOM Remediation Mode** (only when applying fixes):
|
|
44
|
+
- Full document tree loaded in memory
|
|
45
|
+
- XPath queries and tree modification
|
|
46
|
+
- Memory scales with file size
|
|
47
|
+
- Only activated when `fix: true` is specified
|
|
48
|
+
|
|
49
|
+
### Core Classes
|
|
50
|
+
|
|
51
|
+
- `Validator` - Main entry point; normalizes all inputs to SAX validation
|
|
52
|
+
- `SaxDocument` - SAX-based document wrapper for memory-safe validation
|
|
53
|
+
- `Document` - DOM-based document wrapper for modifications
|
|
54
|
+
- `Profile` - Collection of requirements and remediations; loaded from YAML
|
|
55
|
+
- `Profiles` - Factory for loading/retrieving profile instances
|
|
56
|
+
- `ValidationContext` - Carries state during validation (errors, data collected)
|
|
57
|
+
- `ValidationResult` - Holds validation outcomes after checking
|
|
58
|
+
- `ConformanceReport` - Formats validation results for output
|
|
59
|
+
|
|
60
|
+
### Requirements System
|
|
61
|
+
|
|
62
|
+
Requirements validate SVG documents. All requirements must support SAX validation:
|
|
63
|
+
|
|
64
|
+
- Requirements inherit from `BaseRequirement` in `lib/svg_conform/requirements/`
|
|
65
|
+
- Must implement `validate_sax_element(element, context)` for immediate checks
|
|
66
|
+
- Can optionally implement `collect_sax_data()` + `validate_sax_complete()` for deferred validation (e.g., cross-reference checks)
|
|
67
|
+
- Use `context.data` hash for per-document state (not instance variables - they leak across validations)
|
|
68
|
+
- Never attempt DOM operations in SAX callbacks
|
|
69
|
+
|
|
70
|
+
Key requirement classes: `NamespaceRequirement`, `ViewboxRequiredRequirement`, `IdReferenceRequirement`, `ForbiddenContentRequirement`, `ColorRestrictionsRequirement`, etc.
|
|
71
|
+
|
|
72
|
+
### Remediations System
|
|
73
|
+
|
|
74
|
+
Remediations fix validation failures. They run in DOM mode:
|
|
75
|
+
|
|
76
|
+
- Inherit from `BaseRemediation` in `lib/svg_conform/remediations/`
|
|
77
|
+
- Linked to requirements via `targets` array in profile YAML
|
|
78
|
+
- Only execute when `should_execute?(failed_requirements)` returns true
|
|
79
|
+
|
|
80
|
+
### Profile Configuration
|
|
81
|
+
|
|
82
|
+
Profiles are defined in YAML under `config/profiles/`:
|
|
83
|
+
- `base.yml` - Common requirements shared by other profiles
|
|
84
|
+
- `metanorma.yml`, `svg_1_2_rfc.yml`, etc. - Specific profile definitions
|
|
85
|
+
- Profile class map is built dynamically from filesystem in `Profile.build_class_map`
|
|
86
|
+
|
|
87
|
+
Profiles support `import` to inherit from other profiles (e.g., `metanorma` imports `base`).
|
|
88
|
+
|
|
89
|
+
### CLI Structure
|
|
90
|
+
|
|
91
|
+
- `lib/svg_conform/cli.rb` - Thor-based CLI entry point
|
|
92
|
+
- `lib/svg_conform/commands/check.rb` - Main validation command
|
|
93
|
+
- `lib/svg_conform/commands/profiles.rb` - Profile listing command
|
|
94
|
+
- `lib/svg_conform/commands/svgcheck.rb` - SVGCheck compatibility subcommands
|
|
95
|
+
|
|
96
|
+
### Input Handling
|
|
97
|
+
|
|
98
|
+
The `Validator` accepts multiple input types and always uses SAX:
|
|
99
|
+
- String (XML content) → parsed directly with SAX
|
|
100
|
+
- Moxml/Nokogiri documents → serialized once, then SAX validated
|
|
101
|
+
- Document/Element objects → same serialization approach
|
|
102
|
+
|
|
103
|
+
This ensures memory safety for large files regardless of input type.
|
|
104
|
+
|
|
105
|
+
### Key Files
|
|
106
|
+
|
|
107
|
+
- `lib/svg_conform.rb` - Main require file with autoloads
|
|
108
|
+
- `lib/svg_conform/element_proxy.rb` - Lightweight SAX element representation
|
|
109
|
+
- `lib/svg_conform/validation_context.rb` - Validation state container
|
|
110
|
+
- `lib/svg_conform/sax_document.rb` - SAX document wrapper
|
|
111
|
+
- `lib/svg_conform/sax_validation_handler.rb` - SAX callback handler
|
|
112
|
+
|
|
113
|
+
### Error Handling Pattern
|
|
114
|
+
|
|
115
|
+
The CLI uses `exit 1` for error exits. When improving error handling, prefer raising proper Ruby exceptions (`ValidationError`, `ProfileError`, `ArgumentError`) rather than using `exit`/`abort` directly, as Thor provides mechanisms for displaying errors gracefully to users.
|
data/lib/svg_conform/cli.rb
CHANGED
|
@@ -11,6 +11,16 @@ require_relative "commands/profiles"
|
|
|
11
11
|
module SvgConform
|
|
12
12
|
# Thor-based CLI for SvgConform with svgcheck-like functionality
|
|
13
13
|
class Cli < Thor
|
|
14
|
+
# Exit with non-zero status when a command raises an error
|
|
15
|
+
def self.exit_on_failure?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Use strict mode to catch unknown options at parse time rather than runtime
|
|
20
|
+
def self.check_unknown_options?(*)
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
|
|
14
24
|
def initialize(*args)
|
|
15
25
|
super
|
|
16
26
|
end
|
|
@@ -95,13 +105,10 @@ module SvgConform
|
|
|
95
105
|
puts "SvgConform #{SvgConform::VERSION}"
|
|
96
106
|
end
|
|
97
107
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
puts
|
|
103
|
-
help
|
|
104
|
-
exit 1
|
|
108
|
+
# Let Thor handle unknown commands - it raises Thor::UndefinedCommandError
|
|
109
|
+
# which provides helpful suggestions (e.g., "Did you mean?")
|
|
110
|
+
def self.handle_unknown_command?(*)
|
|
111
|
+
false
|
|
105
112
|
end
|
|
106
113
|
end
|
|
107
114
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "thor"
|
|
3
4
|
require "paint"
|
|
4
5
|
require "table_tennis"
|
|
5
6
|
require "fileutils"
|
|
@@ -9,6 +10,12 @@ module SvgConform
|
|
|
9
10
|
module Commands
|
|
10
11
|
# Check command for validating SVG files (single or batch)
|
|
11
12
|
class Check
|
|
13
|
+
# User-facing errors should be Thor::Error for nice CLI display
|
|
14
|
+
class CheckError < Thor::Error; end
|
|
15
|
+
class FileNotFoundError < CheckError; end
|
|
16
|
+
class DirectoryNotFoundError < CheckError; end
|
|
17
|
+
class NoFilesFoundError < CheckError; end
|
|
18
|
+
|
|
12
19
|
def initialize(files, options)
|
|
13
20
|
@files = Array(files)
|
|
14
21
|
@options = options
|
|
@@ -19,8 +26,7 @@ module SvgConform
|
|
|
19
26
|
files_to_process = determine_files
|
|
20
27
|
|
|
21
28
|
if files_to_process.empty?
|
|
22
|
-
|
|
23
|
-
exit 1
|
|
29
|
+
raise NoFilesFoundError, "No SVG files found"
|
|
24
30
|
end
|
|
25
31
|
|
|
26
32
|
# Single file mode vs batch mode
|
|
@@ -38,20 +44,17 @@ module SvgConform
|
|
|
38
44
|
# Directory mode: recursive scan
|
|
39
45
|
dir = @options[:directory]
|
|
40
46
|
unless Dir.exist?(dir)
|
|
41
|
-
|
|
42
|
-
exit 1
|
|
47
|
+
raise DirectoryNotFoundError, "Directory '#{dir}' not found"
|
|
43
48
|
end
|
|
44
|
-
|
|
49
|
+
|
|
50
|
+
Dir.glob(File.join(dir, "**/*.svg"))
|
|
45
51
|
elsif @files.empty?
|
|
46
|
-
|
|
47
|
-
exit 1
|
|
48
|
-
[]
|
|
52
|
+
raise NoFilesFoundError, "No files or directory specified"
|
|
49
53
|
else
|
|
50
54
|
# File mode: validate each file exists
|
|
51
55
|
@files.each do |file|
|
|
52
56
|
unless File.exist?(file)
|
|
53
|
-
|
|
54
|
-
exit 1
|
|
57
|
+
raise FileNotFoundError, "File '#{file}' not found"
|
|
55
58
|
end
|
|
56
59
|
end
|
|
57
60
|
@files
|
|
@@ -61,38 +64,33 @@ module SvgConform
|
|
|
61
64
|
def process_single_file(file)
|
|
62
65
|
@file = file
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
end
|
|
67
|
+
# Validate the file
|
|
68
|
+
validator = SvgConform::Validator.new
|
|
69
|
+
result = validator.validate_file(@file,
|
|
70
|
+
profile: @options[:profile].to_sym)
|
|
71
|
+
|
|
72
|
+
# Generate report
|
|
73
|
+
report = SvgConform::ConformanceReport.from_svg_conform_result(
|
|
74
|
+
File.basename(@file),
|
|
75
|
+
result,
|
|
76
|
+
profile: @options[:profile].to_sym,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Output based on format
|
|
80
|
+
case @options[:format]
|
|
81
|
+
when "yaml"
|
|
82
|
+
output_yaml(report)
|
|
83
|
+
when "json"
|
|
84
|
+
output_json(report)
|
|
85
|
+
else
|
|
86
|
+
output_table(report)
|
|
87
|
+
end
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
# Create remediated file if requested
|
|
90
|
+
create_remediated_file(result) if @options[:fix]
|
|
89
91
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
rescue StandardError => e
|
|
93
|
-
puts Paint["Error: #{e.message}", :red]
|
|
94
|
-
exit 1
|
|
95
|
-
end
|
|
92
|
+
# Exit with appropriate code based on validation result
|
|
93
|
+
exit(report.valid? ? 0 : 1)
|
|
96
94
|
end
|
|
97
95
|
|
|
98
96
|
def output_table(report)
|
|
@@ -275,15 +273,13 @@ module SvgConform
|
|
|
275
273
|
|
|
276
274
|
# Multiple files require output-dir
|
|
277
275
|
if files.length > 1 && !@options[:output_dir]
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
exit 1
|
|
276
|
+
raise CheckError,
|
|
277
|
+
"--output-dir required for multiple files with --fix"
|
|
281
278
|
end
|
|
282
279
|
|
|
283
280
|
# In-place requires force
|
|
284
281
|
if @options[:in_place] && !@options[:force]
|
|
285
|
-
|
|
286
|
-
exit 1
|
|
282
|
+
raise CheckError, "--in-place requires --force flag for safety"
|
|
287
283
|
end
|
|
288
284
|
end
|
|
289
285
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "thor"
|
|
3
4
|
require "paint"
|
|
4
5
|
require "table_tennis"
|
|
5
6
|
|
|
@@ -7,6 +8,8 @@ module SvgConform
|
|
|
7
8
|
module Commands
|
|
8
9
|
# Profiles command for listing available validation profiles
|
|
9
10
|
class Profiles
|
|
11
|
+
class ProfilesError < Thor::Error; end
|
|
12
|
+
|
|
10
13
|
def initialize(options)
|
|
11
14
|
@options = options
|
|
12
15
|
# Paint doesn't need initialization like Pastel
|
|
@@ -20,9 +23,6 @@ module SvgConform
|
|
|
20
23
|
else
|
|
21
24
|
display_profile_list(profiles)
|
|
22
25
|
end
|
|
23
|
-
rescue StandardError => e
|
|
24
|
-
puts Paint["Error: #{e.message}", :red]
|
|
25
|
-
exit 1
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
private
|
|
@@ -6,6 +6,8 @@ module SvgConform
|
|
|
6
6
|
module Commands
|
|
7
7
|
# Compare command for comparing SvgConform validation with svgcheck reports
|
|
8
8
|
class SvgcheckCompare
|
|
9
|
+
class CompareError < Thor::Error; end
|
|
10
|
+
|
|
9
11
|
def initialize(file, options)
|
|
10
12
|
@file = file
|
|
11
13
|
@options = options
|
|
@@ -17,46 +19,36 @@ module SvgConform
|
|
|
17
19
|
validation_file = determine_validation_file(@file)
|
|
18
20
|
|
|
19
21
|
unless File.exist?(validation_file)
|
|
20
|
-
|
|
21
|
-
exit 1
|
|
22
|
+
raise CompareError, "File '#{validation_file}' not found"
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if svgcheck_report_path
|
|
30
|
-
puts Paint["Expected: #{svgcheck_report_path}",
|
|
31
|
-
:dim]
|
|
32
|
-
end
|
|
33
|
-
exit 1
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Load svgcheck report using the new external checker parser
|
|
37
|
-
parser = SvgConform::ExternalCheckers::Svgcheck::Parser.new
|
|
38
|
-
error_content = File.read(svgcheck_report_path)
|
|
39
|
-
svgcheck_report = parser.parse(error_content, nil,
|
|
40
|
-
filename: File.basename(@file))
|
|
41
|
-
|
|
42
|
-
# Generate SvgConform report using the correct validation file
|
|
43
|
-
validator = SvgConform::Validator.new
|
|
44
|
-
result = validator.validate_file(validation_file,
|
|
45
|
-
profile: @options[:profile].to_sym)
|
|
46
|
-
svg_conform_report = SvgConform::ConformanceReport.from_svg_conform_result(
|
|
47
|
-
File.basename(@file),
|
|
48
|
-
result,
|
|
49
|
-
profile: @options[:profile].to_sym,
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Compare the reports
|
|
53
|
-
comparator = SvgConform::ReportComparator.new
|
|
54
|
-
comparator.compare_reports(svg_conform_report, svgcheck_report,
|
|
55
|
-
File.basename(@file))
|
|
56
|
-
rescue StandardError => e
|
|
57
|
-
puts Paint["Error: #{e.message}", :red]
|
|
58
|
-
exit 1
|
|
25
|
+
# Find or use specified svgcheck report
|
|
26
|
+
svgcheck_report_path = find_svgcheck_report
|
|
27
|
+
unless svgcheck_report_path && File.exist?(svgcheck_report_path)
|
|
28
|
+
raise CompareError,
|
|
29
|
+
"svgcheck report not found#{" - expected: #{svgcheck_report_path}" if svgcheck_report_path}"
|
|
59
30
|
end
|
|
31
|
+
|
|
32
|
+
# Load svgcheck report using the new external checker parser
|
|
33
|
+
parser = SvgConform::ExternalCheckers::Svgcheck::Parser.new
|
|
34
|
+
error_content = File.read(svgcheck_report_path)
|
|
35
|
+
svgcheck_report = parser.parse(error_content, nil,
|
|
36
|
+
filename: File.basename(@file))
|
|
37
|
+
|
|
38
|
+
# Generate SvgConform report using the correct validation file
|
|
39
|
+
validator = SvgConform::Validator.new
|
|
40
|
+
result = validator.validate_file(validation_file,
|
|
41
|
+
profile: @options[:profile].to_sym)
|
|
42
|
+
svg_conform_report = SvgConform::ConformanceReport.from_svg_conform_result(
|
|
43
|
+
File.basename(@file),
|
|
44
|
+
result,
|
|
45
|
+
profile: @options[:profile].to_sym,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Compare the reports
|
|
49
|
+
comparator = SvgConform::ReportComparator.new
|
|
50
|
+
comparator.compare_reports(svg_conform_report, svgcheck_report,
|
|
51
|
+
File.basename(@file))
|
|
60
52
|
end
|
|
61
53
|
|
|
62
54
|
private
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "thor"
|
|
3
4
|
require_relative "../compatibility/compatibility_analyzer"
|
|
4
5
|
|
|
5
6
|
module SvgConform
|
|
6
7
|
module Commands
|
|
7
8
|
# Command for comparing SvgConform with svgcheck compatibility
|
|
8
9
|
class SvgcheckCompatibility
|
|
10
|
+
class CompatibilityError < Thor::Error; end
|
|
11
|
+
|
|
9
12
|
def initialize(options = {})
|
|
10
13
|
@options = options
|
|
11
14
|
end
|
|
@@ -29,14 +32,16 @@ module SvgConform
|
|
|
29
32
|
|
|
30
33
|
def validate_options
|
|
31
34
|
mode = @options[:mode] || @options["mode"] || "check"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
unless %w[check repair].include?(mode)
|
|
36
|
+
raise CompatibilityError,
|
|
37
|
+
"Invalid mode: #{mode}. Must be 'check' or 'repair'"
|
|
38
|
+
end
|
|
35
39
|
|
|
36
40
|
file = @options[:file] || @options["file"]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
if file && !file.match?(/\.(svg|xml)$/i)
|
|
42
|
+
raise CompatibilityError,
|
|
43
|
+
"File must have .svg or .xml extension: #{file}"
|
|
44
|
+
end
|
|
40
45
|
end
|
|
41
46
|
end
|
|
42
47
|
end
|
|
@@ -96,7 +96,7 @@ module SvgConform
|
|
|
96
96
|
attribute :id, :string
|
|
97
97
|
attribute :description, :string
|
|
98
98
|
attribute :type, :string, polymorphic_class: true, default: -> {
|
|
99
|
-
self.class.name
|
|
99
|
+
self.class.name&.split("::")&.last
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
yaml do
|
|
@@ -161,8 +161,8 @@ module SvgConform
|
|
|
161
161
|
|
|
162
162
|
attrs = node.attributes || []
|
|
163
163
|
# Convert array of Moxml::Attribute objects to hash
|
|
164
|
-
attrs.
|
|
165
|
-
|
|
164
|
+
attrs.to_h do |attr|
|
|
165
|
+
[attr.name, attr.value]
|
|
166
166
|
end
|
|
167
167
|
end
|
|
168
168
|
|
data/lib/svg_conform/version.rb
CHANGED
|
@@ -83,8 +83,8 @@ RSpec.describe SvgConform::Commands::Check do
|
|
|
83
83
|
|
|
84
84
|
expect do
|
|
85
85
|
command.execute
|
|
86
|
-
end.to
|
|
87
|
-
|
|
86
|
+
end.to raise_error(SvgConform::Commands::Check::FileNotFoundError,
|
|
87
|
+
/nonexistent.svg/)
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: svg_conform
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-04-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -87,6 +87,7 @@ files:
|
|
|
87
87
|
- ".rspec"
|
|
88
88
|
- ".rubocop.yml"
|
|
89
89
|
- ".rubocop_todo.yml"
|
|
90
|
+
- CLAUDE.md
|
|
90
91
|
- CODE_OF_CONDUCT.md
|
|
91
92
|
- Gemfile
|
|
92
93
|
- README.adoc
|