@markuplint/types 5.0.0-rc.1 → 5.0.0-rc.2

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.
@@ -0,0 +1,89 @@
1
+ import { log } from '../debug.js';
2
+ import { matched, unmatched } from '../match-result.js';
3
+ /**
4
+ * Dummy base URL for resolving relative URLs.
5
+ * Used only for syntax validation — the actual base URL is irrelevant.
6
+ * Matches the approach used by nu-html-checker (galimatias).
7
+ */
8
+ const DUMMY_BASE = 'http://example.org/foo/bar';
9
+ /**
10
+ * Characters that are illegal in URLs per WHATWG URL Standard.
11
+ * Tabs and newlines are stripped during parsing but are validation errors.
12
+ *
13
+ * @see https://url.spec.whatwg.org/#url-code-points
14
+ */
15
+ // eslint-disable-next-line no-control-regex
16
+ const ILLEGAL_WHITESPACE = /[\t\n\r]/;
17
+ /**
18
+ * Malformed percent-encoding: `%` not followed by exactly two hex digits.
19
+ */
20
+ const MALFORMED_PERCENT = /%(?![0-9A-F]{2})/i;
21
+ /**
22
+ * C0 control characters (U+0000–U+001F) and DEL (U+007F) are forbidden
23
+ * in URLs. Excludes TAB/LF/CR which are caught by ILLEGAL_WHITESPACE.
24
+ */
25
+ // eslint-disable-next-line no-control-regex
26
+ const C0_CONTROL = /[\u0000-\u0008\v\u000E-\u001F\u007F]/;
27
+ /**
28
+ * Unencoded space in URL body (after trimming leading/trailing spaces).
29
+ * `new URL()` accepts spaces by percent-encoding them, but the WHATWG spec
30
+ * considers them a validation error. nu-validator rejects them.
31
+ */
32
+ const UNENCODED_SPACE = / /;
33
+ /**
34
+ * Validates a URL string (potentially surrounded by spaces) per the WHATWG
35
+ * URL Standard. Accepts both absolute and relative URLs.
36
+ *
37
+ * Uses `new URL()` for structural parsing and adds strict checks matching
38
+ * the nu-html-checker's galimatias StrictErrorHandler behavior:
39
+ * - Illegal whitespace (tabs, newlines) in URL
40
+ * - Malformed percent-encoding
41
+ * - C0 control characters
42
+ * - URLs that fail `new URL()` parsing (even with a dummy base)
43
+ *
44
+ * @see https://html.spec.whatwg.org/multipage/urls-and-fetching.html#valid-url-potentially-surrounded-by-spaces
45
+ * @see https://url.spec.whatwg.org/#url-code-points
46
+ */
47
+ export const checkURL = () => function checkURL(value) {
48
+ log('CHECK: url');
49
+ const trimmed = value.trim();
50
+ // Empty URL is valid for some attributes (e.g., <a href="">)
51
+ // The spec says "valid URL potentially surrounded by spaces"
52
+ // and the empty string resolves to the document's URL.
53
+ if (trimmed === '') {
54
+ return matched();
55
+ }
56
+ // Check for illegal whitespace (tab, newline, CR)
57
+ if (ILLEGAL_WHITESPACE.test(trimmed)) {
58
+ return unmatched(trimmed, 'unexpected-token');
59
+ }
60
+ // Check for C0 control characters
61
+ if (C0_CONTROL.test(trimmed)) {
62
+ return unmatched(trimmed, 'unexpected-token');
63
+ }
64
+ // Check for malformed percent-encoding
65
+ if (MALFORMED_PERCENT.test(trimmed)) {
66
+ return unmatched(trimmed, 'unexpected-token');
67
+ }
68
+ // Check for unencoded spaces
69
+ if (UNENCODED_SPACE.test(trimmed)) {
70
+ return unmatched(trimmed, 'unexpected-token');
71
+ }
72
+ // Try parsing as absolute URL first
73
+ try {
74
+ new URL(trimmed);
75
+ return matched();
76
+ }
77
+ catch {
78
+ // Not a valid absolute URL — try as relative
79
+ }
80
+ // Try parsing as relative URL with dummy base
81
+ try {
82
+ new URL(trimmed, DUMMY_BASE);
83
+ return matched();
84
+ }
85
+ catch {
86
+ // Both absolute and relative parsing failed
87
+ return unmatched(trimmed, 'unexpected-token');
88
+ }
89
+ };
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@markuplint/types",
3
- "version": "5.0.0-rc.1",
3
+ "version": "5.0.0-rc.2",
4
4
  "description": "Type declaration and value checker",
5
- "repository": "git@github.com:markuplint/markuplint.git",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/markuplint/markuplint.git",
8
+ "directory": "packages/@markuplint/types"
9
+ },
6
10
  "author": "Yusuke Hirao <yusukehirao@me.com>",
7
11
  "license": "MIT",
8
12
  "engines": {
@@ -25,14 +29,12 @@
25
29
  "build": "tsc --project tsconfig.build.json",
26
30
  "dev": "tsc --watch --project tsconfig.build.json",
27
31
  "clean": "tsc --build --clean tsconfig.build.json",
28
- "schema": "run-s schema:types schema:schema build schema:prettier schema:eslint schema:prettier",
29
- "schema:types": "tsx \"./gen/types.ts\"",
30
- "schema:schema": "npx json2ts \"./types.schema.json\" > \"./src/types.schema.ts\"",
31
- "schema:eslint": "npx eslint --fix \"./src/types.schema.ts\"",
32
- "schema:prettier": "npx prettier --write \"./src/types.schema.ts\" \"./types.schema.json\" --log-level warn"
32
+ "schema": "run-s schema:types schema:schema",
33
+ "schema:types": "npx tsx \"./gen/types.ts\"",
34
+ "schema:schema": "npx json2ts \"./types.schema.json\" > \"./src/types.schema.ts\""
33
35
  },
34
36
  "dependencies": {
35
- "@markuplint/shared": "5.0.0-rc.1",
37
+ "@markuplint/shared": "5.0.0-rc.2",
36
38
  "@types/css-tree": "2.3.11",
37
39
  "@types/debug": "4.1.13",
38
40
  "bcp-47": "2.1.0",
@@ -42,5 +44,5 @@
42
44
  "type-fest": "5.5.0",
43
45
  "whatwg-mimetype": "5.0.0"
44
46
  },
45
- "gitHead": "0d6b4324d9a7d6b9e1ba57d4a57e45d36975cba9"
47
+ "gitHead": "e43763858d9234c417053becc73dbd088c1e7ea6"
46
48
  }