webring 1.7.1-dev.5538 → 1.7.1-dev.5768

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.
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,WAAW,EAEhB,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAwBpB,wBAAsB,QAAQ,CAAC,MAAM,EAAE,aAAa;;;;;;;;;;KAInD;AAED,wBAAsB,KAAK,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAyClC"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,WAAW,EAEhB,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AA8FpB,wBAAsB,QAAQ,CAAC,MAAM,EAAE,aAAa;;;;;;;;;;KAInD;AAED,wBAAsB,KAAK,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAyClC"}
package/dist/fetch.js CHANGED
@@ -1,27 +1,90 @@
1
1
  import Parser from "rss-parser";
2
2
  import sanitizeHtml from "sanitize-html";
3
- import * as truncateHtml from "truncate-html";
4
- import { z } from "zod/v3";
3
+ import { Parser as HtmlParser } from "htmlparser2";
5
4
  import { FeedEntrySchema, } from "./types.js";
6
5
  import * as R from "remeda";
7
6
  import { asyncMapFilterUndefined } from "./util.js";
8
- // Handle ESM/CommonJS interop - truncate-html exports differently in different environments
9
- const TruncateFnSchema = z
10
- .function()
11
- .args(z.string(), z.number().optional())
12
- .returns(z.string());
13
- const TruncateModuleSchema = z.object({ default: TruncateFnSchema });
14
- function truncate(html, length) {
15
- const mod = truncateHtml;
16
- const fnResult = TruncateFnSchema.safeParse(mod);
17
- if (fnResult.success) {
18
- return fnResult.data(html, length);
19
- }
20
- const modResult = TruncateModuleSchema.safeParse(mod);
21
- if (modResult.success) {
22
- return modResult.data.default(html, length);
7
+ const VOID_TAGS = new Set([
8
+ "area",
9
+ "base",
10
+ "br",
11
+ "col",
12
+ "embed",
13
+ "hr",
14
+ "img",
15
+ "input",
16
+ "link",
17
+ "meta",
18
+ "source",
19
+ "track",
20
+ "wbr",
21
+ ]);
22
+ function escapeText(text) {
23
+ return text
24
+ .replaceAll("&", "&")
25
+ .replaceAll("<", "&lt;")
26
+ .replaceAll(">", "&gt;");
27
+ }
28
+ function escapeAttributeValue(value) {
29
+ return escapeText(value).replaceAll('"', "&quot;");
30
+ }
31
+ /**
32
+ * Truncate HTML to `length` text characters while keeping tags balanced.
33
+ * Whitespace runs collapse to a single space and `...` marks a cut, matching
34
+ * the behavior of the retired `truncate-html` package (which was pinned to a
35
+ * dead cheerio 1.0.0-rc.12/htmlparser2@6 chain).
36
+ */
37
+ function truncate(html, length = 300) {
38
+ let out = "";
39
+ let remaining = length;
40
+ let truncated = false;
41
+ const openTags = [];
42
+ const parser = new HtmlParser({
43
+ onopentag(name, attributes) {
44
+ if (truncated) {
45
+ return;
46
+ }
47
+ const attributeText = Object.entries(attributes)
48
+ .map(([key, value]) => value === "" ? ` ${key}` : ` ${key}="${escapeAttributeValue(value)}"`)
49
+ .join("");
50
+ out += `<${name}${attributeText}>`;
51
+ if (!VOID_TAGS.has(name)) {
52
+ openTags.push(name);
53
+ }
54
+ },
55
+ ontext(text) {
56
+ if (truncated) {
57
+ return;
58
+ }
59
+ const collapsed = text.replaceAll(/\s+/g, " ");
60
+ if (collapsed.length <= remaining) {
61
+ out += escapeText(collapsed);
62
+ remaining -= collapsed.length;
63
+ return;
64
+ }
65
+ out += `${escapeText(collapsed.slice(0, remaining))}...`;
66
+ truncated = true;
67
+ },
68
+ onclosetag(name) {
69
+ if (VOID_TAGS.has(name)) {
70
+ return;
71
+ }
72
+ // Only emit closes for tags we actually opened (tags opened after the
73
+ // cut never enter the stack, so their close events are dropped too).
74
+ const index = openTags.lastIndexOf(name);
75
+ if (index === -1) {
76
+ return;
77
+ }
78
+ openTags.splice(index, 1);
79
+ out += `</${name}>`;
80
+ },
81
+ });
82
+ parser.write(html);
83
+ parser.end();
84
+ for (const name of openTags.reverse()) {
85
+ out += `</${name}>`;
23
86
  }
24
- throw new Error("truncate-html module could not be resolved");
87
+ return out;
25
88
  }
26
89
  export async function fetchAll(config) {
27
90
  return await asyncMapFilterUndefined(config.sources, (source) => fetch(source, config.truncate));
package/dist/fetch.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "url": "https://github.com/shepherdjerred/monorepo/issues"
18
18
  },
19
19
  "type": "module",
20
- "version": "1.7.1-dev.5538",
20
+ "version": "1.7.1-dev.5768",
21
21
  "scripts": {
22
22
  "lint": "eslint src --cache",
23
23
  "build": "tsc",
@@ -25,7 +25,9 @@
25
25
  "test": "bun test src/",
26
26
  "test:update": "bun test src/ --update-snapshots",
27
27
  "typedoc": "typedoc src/index.ts",
28
- "typecheck": "tsc --noEmit"
28
+ "typecheck": "tsc --noEmit",
29
+ "deploy": "bun ../../scripts/deploy-site.ts webring",
30
+ "publish:npm": "bun ../../scripts/publish-npm.ts ."
29
31
  },
30
32
  "main": "dist/index.js",
31
33
  "types": "dist/index.d.ts",
@@ -36,14 +38,14 @@
36
38
  }
37
39
  },
38
40
  "dependencies": {
41
+ "htmlparser2": "^10.0.0",
39
42
  "remeda": "^2.33.7",
40
43
  "rss-parser": "^3.13.0",
41
44
  "sanitize-html": "^2.17.4",
42
- "truncate-html": "^1.2.2",
43
45
  "zod": "^4.4.3"
44
46
  },
45
47
  "devDependencies": {
46
- "@shepherdjerred/eslint-config": "^0.3.0",
48
+ "@shepherdjerred/eslint-config": "0.3.0",
47
49
  "@tsconfig/node24": "^24.0.4",
48
50
  "@tsconfig/strictest": "^2.0.8",
49
51
  "@types/bun": "^1.3.13",
@@ -68,8 +70,5 @@
68
70
  "LICENSE",
69
71
  "bun.lock",
70
72
  "CHANGELOG.md"
71
- ],
72
- "overrides": {
73
- "sanitize-html": "^2.17.4"
74
- }
73
+ ]
75
74
  }
package/src/fetch.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import Parser from "rss-parser";
2
2
  import sanitizeHtml from "sanitize-html";
3
- import * as truncateHtml from "truncate-html";
4
- import { z } from "zod/v3";
3
+ import { Parser as HtmlParser } from "htmlparser2";
5
4
  import {
6
5
  type Source,
7
6
  type ResultEntry,
@@ -11,24 +10,94 @@ import {
11
10
  import * as R from "remeda";
12
11
  import { asyncMapFilterUndefined } from "./util.ts";
13
12
 
14
- // Handle ESM/CommonJS interop - truncate-html exports differently in different environments
15
- const TruncateFnSchema = z
16
- .function()
17
- .args(z.string(), z.number().optional())
18
- .returns(z.string());
19
- const TruncateModuleSchema = z.object({ default: TruncateFnSchema });
13
+ const VOID_TAGS = new Set([
14
+ "area",
15
+ "base",
16
+ "br",
17
+ "col",
18
+ "embed",
19
+ "hr",
20
+ "img",
21
+ "input",
22
+ "link",
23
+ "meta",
24
+ "source",
25
+ "track",
26
+ "wbr",
27
+ ]);
20
28
 
21
- function truncate(html: string, length?: number): string {
22
- const mod = truncateHtml as unknown;
23
- const fnResult = TruncateFnSchema.safeParse(mod);
24
- if (fnResult.success) {
25
- return fnResult.data(html, length);
26
- }
27
- const modResult = TruncateModuleSchema.safeParse(mod);
28
- if (modResult.success) {
29
- return modResult.data.default(html, length);
29
+ function escapeText(text: string): string {
30
+ return text
31
+ .replaceAll("&", "&amp;")
32
+ .replaceAll("<", "&lt;")
33
+ .replaceAll(">", "&gt;");
34
+ }
35
+
36
+ function escapeAttributeValue(value: string): string {
37
+ return escapeText(value).replaceAll('"', "&quot;");
38
+ }
39
+
40
+ /**
41
+ * Truncate HTML to `length` text characters while keeping tags balanced.
42
+ * Whitespace runs collapse to a single space and `...` marks a cut, matching
43
+ * the behavior of the retired `truncate-html` package (which was pinned to a
44
+ * dead cheerio 1.0.0-rc.12/htmlparser2@6 chain).
45
+ */
46
+ function truncate(html: string, length = 300): string {
47
+ let out = "";
48
+ let remaining = length;
49
+ let truncated = false;
50
+ const openTags: string[] = [];
51
+
52
+ const parser = new HtmlParser({
53
+ onopentag(name, attributes) {
54
+ if (truncated) {
55
+ return;
56
+ }
57
+ const attributeText = Object.entries(attributes)
58
+ .map(([key, value]) =>
59
+ value === "" ? ` ${key}` : ` ${key}="${escapeAttributeValue(value)}"`,
60
+ )
61
+ .join("");
62
+ out += `<${name}${attributeText}>`;
63
+ if (!VOID_TAGS.has(name)) {
64
+ openTags.push(name);
65
+ }
66
+ },
67
+ ontext(text) {
68
+ if (truncated) {
69
+ return;
70
+ }
71
+ const collapsed = text.replaceAll(/\s+/g, " ");
72
+ if (collapsed.length <= remaining) {
73
+ out += escapeText(collapsed);
74
+ remaining -= collapsed.length;
75
+ return;
76
+ }
77
+ out += `${escapeText(collapsed.slice(0, remaining))}...`;
78
+ truncated = true;
79
+ },
80
+ onclosetag(name) {
81
+ if (VOID_TAGS.has(name)) {
82
+ return;
83
+ }
84
+ // Only emit closes for tags we actually opened (tags opened after the
85
+ // cut never enter the stack, so their close events are dropped too).
86
+ const index = openTags.lastIndexOf(name);
87
+ if (index === -1) {
88
+ return;
89
+ }
90
+ openTags.splice(index, 1);
91
+ out += `</${name}>`;
92
+ },
93
+ });
94
+ parser.write(html);
95
+ parser.end();
96
+
97
+ for (const name of openTags.reverse()) {
98
+ out += `</${name}>`;
30
99
  }
31
- throw new Error("truncate-html module could not be resolved");
100
+ return out;
32
101
  }
33
102
 
34
103
  export async function fetchAll(config: Configuration) {