Skip to content

Commit 6ec446d

Browse files
committed
refactor: rename descriptorType to descriptor in validator and types
- Updated `buildValidationResult` to use `descriptor` instead of `descriptorType`. - Reflected the renaming in validation return types and test assertions. - Expanded documentation and examples in README to align with the updated term.
1 parent f56bb64 commit 6ec446d

5 files changed

Lines changed: 260 additions & 52 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
22
.idea/
3+
.claude/
34

45
node_modules/
56
dist/
@@ -13,4 +14,3 @@ npm-debug.log*
1314
pnpm-debug.log*
1415
yarn-debug.log*
1516
yarn-error.log*
16-

README.md

Lines changed: 248 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,151 @@
11
# srcset-kit
22

3-
Tools for working with the HTML `srcset` attribute.
3+
Small, dependency-free tools for parsing, validating, and serializing HTML
4+
`srcset` values.
5+
6+
`srcset-kit` is built for code that needs to understand responsive image
7+
candidates without taking shortcuts. It handles `data:` URLs with commas,
8+
normalizes whitespace, reports stable validation codes, and keeps URL strings
9+
exactly as you provided them.
10+
11+
## Why
12+
13+
`srcset` looks simple until URLs contain commas:
14+
15+
```html
16+
<img srcset="data:image/png;base64,AAAA 1x, /image@2x.png 2x" />
17+
```
18+
19+
A plain `split(",")` breaks this value. `srcset-kit` tokenizes candidates with
20+
the descriptor boundary in mind, so inline image data, query strings, fragments,
21+
ports, relative URLs, and absolute URLs stay intact.
422

523
## Installation
624

725
```sh
826
pnpm add srcset-kit
927
```
1028

11-
## Usage
29+
```sh
30+
npm install srcset-kit
31+
```
32+
33+
```sh
34+
yarn add srcset-kit
35+
```
36+
37+
## Quick Start
38+
39+
```ts
40+
import {parse, stringify, validate} from "srcset-kit";
41+
42+
const candidates = parse("image.png 1x, image@2x.png 2x");
1243

13-
`srcset-kit` parses, validates, and serializes HTML `srcset` attribute values
14-
without splitting blindly on commas.
44+
const result = validate(candidates, {
45+
descriptor: "density",
46+
});
47+
48+
if (result.valid) {
49+
stringify(result.candidates);
50+
// "image.png 1x, image@2x.png 2x"
51+
}
52+
```
53+
54+
## Parse
55+
56+
Parse a `srcset` string into clean candidate objects.
1557

1658
```ts
17-
import { parse, stringify, validate } from "srcset-kit";
59+
import {parse} from "srcset-kit";
60+
61+
parse("image.png");
62+
// [{url: "image.png"}]
1863

1964
parse("image.png 1x, image@2x.png 2x");
2065
// [
21-
// { url: "image.png", density: 1 },
22-
// { url: "image@2x.png", density: 2 },
66+
// {url: "image.png", density: 1},
67+
// {url: "image@2x.png", density: 2},
2368
// ]
2469

2570
parse("small.png 640w, large.png 1280w");
2671
// [
27-
// { url: "small.png", width: 640 },
28-
// { url: "large.png", width: 1280 },
72+
// {url: "small.png", width: 640},
73+
// {url: "large.png", width: 1280},
2974
// ]
3075
```
3176

32-
### Validate
77+
Parsing is tolerant by default. It keeps the usable URL candidate shape and
78+
leaves validation decisions to `validate()`.
3379

3480
```ts
35-
const result = validate("image.png 1x, image@2x.png 2x");
81+
parse("image.png 1x 640w, image@2x.png 2x");
82+
// [
83+
// {url: "image.png"},
84+
// {url: "image@2x.png", density: 2},
85+
// ]
86+
```
3687

37-
if (result.valid) {
38-
result.descriptorType;
39-
// "density"
88+
Use strict parsing when invalid `srcset` values should throw.
89+
90+
```ts
91+
import {SrcsetValidationError, parse} from "srcset-kit";
92+
93+
try {
94+
parse("image.png 1x 640w", {strict: true});
95+
} catch (error) {
96+
error instanceof SrcsetValidationError;
97+
// true
4098
}
4199
```
42100

43-
Validation returns structured issue codes instead of throwing for normal invalid
44-
input.
101+
## Data URLs
102+
103+
Commas inside URLs are preserved.
104+
105+
```ts
106+
parse("data:image/png;base64,AAAA 1x, /image@2x.png 2x");
107+
// [
108+
// {url: "data:image/png;base64,AAAA", density: 1},
109+
// {url: "/image@2x.png", density: 2},
110+
// ]
111+
112+
parse("data:image/svg+xml,%3Csvg%3E%3C/svg%3E 1x, /image.svg 2x");
113+
// [
114+
// {url: "data:image/svg+xml,%3Csvg%3E%3C/svg%3E", density: 1},
115+
// {url: "/image.svg", density: 2},
116+
// ]
117+
```
118+
119+
## Validate
120+
121+
Validate a string or already parsed candidates. Validation never throws for
122+
normal invalid input.
123+
124+
```ts
125+
import {validate} from "srcset-kit";
126+
127+
validate("image.png 1x, image@2x.png 2x");
128+
// {
129+
// valid: true,
130+
// descriptor: "density",
131+
// candidates: [
132+
// {url: "image.png", density: 1},
133+
// {url: "image@2x.png", density: 2},
134+
// ],
135+
// errors: [],
136+
// }
137+
```
138+
139+
Invalid input returns stable issue codes.
45140

46141
```ts
47142
validate("image.png 1x, image@1x.png 1x");
48143
// {
49144
// valid: false,
50-
// descriptorType: "density",
145+
// descriptor: "density",
51146
// candidates: [
52-
// { url: "image.png", density: 1 },
53-
// { url: "image@1x.png", density: 1 },
147+
// {url: "image.png", density: 1},
148+
// {url: "image@1x.png", density: 1},
54149
// ],
55150
// errors: [
56151
// {
@@ -63,73 +158,180 @@ validate("image.png 1x, image@1x.png 1x");
63158
// }
64159
```
65160

66-
Use `descriptor` to require a specific descriptor type for all candidates.
161+
Validate candidate arrays directly.
162+
163+
```ts
164+
validate([
165+
{url: "image.png", density: 1},
166+
{url: "image@2x.png", density: 2},
167+
]);
168+
// valid: true
169+
```
170+
171+
### Descriptor Policy
172+
173+
Use `descriptor` when a caller expects a specific candidate style.
67174

68175
```ts
69-
validate("small.png 640w, large.png 1280w", { descriptor: "width" });
176+
validate("small.png 640w, large.png 1280w", {
177+
descriptor: "width",
178+
});
70179
// valid: true
71180

72-
validate("image.png 1x, image@2x.png 2x", { descriptor: "width" });
181+
validate("image.png 1x, image@2x.png 2x", {
182+
descriptor: "width",
183+
});
73184
// valid: false
74185

75-
validate("image.png 1x, image@2x.png 2x", { descriptor: "density" });
186+
validate("image.png, image@2x.png 2x", {
187+
descriptor: "density",
188+
});
76189
// valid: true
77190
```
78191

192+
A fallback candidate without a descriptor is treated as implicit `1x`, so it is
193+
accepted by the density policy.
194+
195+
### URL Context
196+
79197
Use `baseUrl` when relative URLs should be checked in the context of a page URL.
80198
Candidate URLs are not rewritten.
81199

82200
```ts
83-
validate("/image.png 1x", { baseUrl: "https://example.com" });
201+
validate("/images/photo.jpg 1x", {
202+
baseUrl: "https://example.com/gallery/",
203+
});
84204
// valid: true
205+
206+
parse("/images/photo.jpg 1x");
207+
// [{url: "/images/photo.jpg", density: 1}]
85208
```
86209

87-
### Stringify
210+
## Stringify
211+
212+
Serialize candidates into a normalized `srcset` string.
88213

89214
```ts
215+
import {stringify} from "srcset-kit";
216+
217+
stringify([{url: "image.png"}]);
218+
// "image.png"
219+
220+
stringify([{url: "image.png", density: 1.5}]);
221+
// "image.png 1.5x"
222+
223+
stringify([{url: "image.png", width: 640}]);
224+
// "image.png 640w"
225+
90226
stringify([
91-
{ url: "image.png", density: 1 },
92-
{ url: "image@2x.png", density: 2 },
227+
{url: "image.png", density: 1},
228+
{url: "image@2x.png", density: 2},
93229
]);
94230
// "image.png 1x, image@2x.png 2x"
95231
```
96232

97-
### Strict Parse
98-
99-
`parse()` is tolerant by default. Use `strict: true` when invalid descriptor sets
100-
should throw a package-specific `SrcsetValidationError`.
233+
Strict stringifying validates before serializing.
101234

102235
```ts
103-
parse("image.png 1x 640w");
104-
// [{ url: "image.png" }]
105-
106-
parse("image.png 1x 640w", { strict: true });
236+
stringify(
237+
[
238+
{url: "image.png", density: 1},
239+
{url: "image-copy.png", density: 1},
240+
],
241+
{strict: true},
242+
);
107243
// throws SrcsetValidationError
108244
```
109245

110-
### Data URLs
246+
## Round Trips
111247

112-
The parser keeps commas inside URLs, including `data:` URLs.
248+
Use `parse()` and `stringify()` together to normalize whitespace while preserving
249+
URLs.
113250

114251
```ts
115-
parse("data:image/png;base64,AAAA 1x, /image@2x.png 2x");
252+
stringify(parse(" image.png 1x,\n image@2x.png 2x "));
253+
// "image.png 1x, image@2x.png 2x"
254+
255+
parse(
256+
stringify([
257+
{url: "data:image/png;base64,AAAA+BB==", density: 1},
258+
{url: "/image.png?w=100,h=200&fmt=webp", density: 2},
259+
]),
260+
);
116261
// [
117-
// { url: "data:image/png;base64,AAAA", density: 1 },
118-
// { url: "/image@2x.png", density: 2 },
262+
// {url: "data:image/png;base64,AAAA+BB==", density: 1},
263+
// {url: "/image.png?w=100,h=200&fmt=webp", density: 2},
119264
// ]
120265
```
121266

122-
## Development
267+
## Validation Rules
123268

124-
```sh
125-
pnpm install
126-
pnpm run verify
269+
`srcset-kit` checks the parts that matter when accepting or transforming
270+
`srcset` values:
271+
272+
- Empty `srcset` values are invalid.
273+
- Candidate URLs must be non-empty.
274+
- Relative URLs can be validated with `baseUrl`.
275+
- A candidate may be fallback, density-based, or width-based.
276+
- A candidate must not contain both density and width.
277+
- Width descriptors must be positive integers.
278+
- Density descriptors must be positive finite numbers.
279+
- Width and density descriptors must not be mixed in one candidate set.
280+
- Duplicate descriptors are invalid.
281+
- A fallback candidate is equivalent to `1x`.
282+
- Descriptor policy mismatches are reported with stable issue codes.
283+
284+
## API
285+
286+
```ts
287+
import {
288+
parse,
289+
stringify,
290+
validate,
291+
SrcsetError,
292+
SrcsetValidationError,
293+
type DescriptorType,
294+
type ParseOptions,
295+
type SrcsetCandidate,
296+
type SrcsetValidationIssue,
297+
type StringifyOptions,
298+
type ValidateOptions,
299+
type ValidationResult,
300+
} from "srcset-kit";
301+
```
302+
303+
```ts
304+
type SrcsetCandidate =
305+
| {url: string; density: number}
306+
| {url: string; width: number}
307+
| {url: string};
308+
309+
type ParseOptions = {
310+
strict?: boolean;
311+
};
312+
313+
type ValidateOptions = {
314+
baseUrl?: string | URL;
315+
descriptor?: "width" | "density";
316+
};
317+
318+
type StringifyOptions = {
319+
strict?: boolean;
320+
};
127321
```
128322

129323
## Package
130324

131325
- Runtime dependencies: none.
326+
- Works in Node and browser environments.
132327
- Source language: TypeScript.
133-
- Build tool: Rslib.
134-
- Test runner: Rstest.
135328
- Published formats: ESM, CommonJS, and TypeScript declarations.
329+
- Public API: `parse`, `validate`, `stringify`, types, and package-specific
330+
errors.
331+
332+
## Development
333+
334+
```sh
335+
pnpm install
336+
pnpm run verify
337+
```

0 commit comments

Comments
 (0)