@knapsack/rendering-utils 4.102.1--canary.7823.8675e86.0 → 4.102.1--canary.8157.a7ffa8f.0

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/.lintstagedrc.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import { dirname } from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
 
4
- import defaultConfig from '../../.lintstagedrc.mjs';
4
+ import defaultConfig, { buildEslintCommand } from '../../.lintstagedrc.mjs';
5
5
 
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
 
8
8
  export default {
9
- '*.{ts,tsx,js,jsx,cjs,mjs,mts,cts}': `cd ${__dirname} && ./node_modules/.bin/eslint`,
9
+ '*.{ts,tsx,js,jsx,cjs,mjs,mts,cts}': buildEslintCommand(__dirname),
10
10
  ...defaultConfig,
11
11
  };
@@ -1,22 +1,6 @@
1
- $ vitest run
1
+ $ tsc -p tsconfig.test.json && vitest run
2
2
 
3
-  RUN  v4.1.9 /home/runner/work/app-monorepo/app-monorepo/libs/rendering-utils
4
-
5
- stderr | src/blocks-to-html.test.ts > blocksToHtml - single markdown-block with string
6
- marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.
7
- marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.
8
-
9
- stderr | src/blocks-to-html.test.ts > blocksToHtml - single markdown-block with array
10
- marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.
11
- marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.
12
-
13
- stderr | src/blocks-to-html.test.ts > blocksToHtml - single markdown-block with empty string
14
- marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.
15
- marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.
16
-
17
- stderr | src/blocks-to-html.test.ts > blocksToHtml - single markdown-block with undefined md
18
- marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.
19
- marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.
3
+  RUN  v4.1.11 /home/runner/work/app-monorepo/app-monorepo/libs/rendering-utils
20
4
 
21
5
  stderr | src/blocks-to-html.test.ts > blocksToHtml - text-editor with unknown TipTap node type does not throw
22
6
  [blocks-to-html] TipTap render failed for block "test-1" (text-editor): RangeError: Unknown node type: this-node-type-does-not-exist
@@ -28,17 +12,13 @@ marked(): headerIds and headerPrefix parameters enabled by default, but are depr
28
12
  at Node.fromJSON (file:///home/runner/work/app-monorepo/app-monorepo/node_modules/.pnpm/prosemirror-model@1.25.7/node_modules/prosemirror-model/dist/index.js:1529:32)
29
13
  at generateHTML (file:///home/runner/work/app-monorepo/app-monorepo/node_modules/.pnpm/@tiptap+html@3.24.0_@tiptap+core@3.24.0_@tiptap+pm@3.24.0__@tiptap+pm@3.24.0_happy-dom@_5581d52037c54da583424e26a68fd3a3/node_modules/@tiptap/html/dist/server/index.js:54:28)
30
14
  at convertTipTapJsonToHtml (/home/runner/work/app-monorepo/app-monorepo/libs/tiptap-utils/src/tiptap-utils.ts:27:22)
31
- at tiptapToHtmlSafe (/home/runner/work/app-monorepo/app-monorepo/libs/rendering-utils/src/blocks-to-html.ts:43:12)
32
- at blockToHtml (/home/runner/work/app-monorepo/app-monorepo/libs/rendering-utils/src/blocks-to-html.ts:65:14)
33
-
34
- stderr | src/blocks-to-html.test.ts > blocksToHtml - multiple different block types
35
- marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.
36
- marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.
15
+ at tiptapToHtmlSafe (/home/runner/work/app-monorepo/app-monorepo/libs/rendering-utils/src/blocks-to-html.ts:56:12)
16
+ at blockToHtml (/home/runner/work/app-monorepo/app-monorepo/libs/rendering-utils/src/blocks-to-html.ts:78:14)
37
17
 
38
- ✓ src/blocks-to-html.test.ts (34 tests) 214ms
18
+ ✓ src/blocks-to-html.test.ts (38 tests) 179ms
39
19
 
40
20
   Test Files  1 passed (1)
41
-  Tests  34 passed (34)
42
-  Start at  20:40:01
43
-  Duration  1.86s (transform 242ms, setup 0ms, import 1.43s, tests 214ms, environment 0ms)
21
+  Tests  38 passed (38)
22
+  Start at  22:11:33
23
+  Duration  1.80s (transform 281ms, setup 0ms, import 1.41s, tests 179ms, environment 0ms)
44
24
 
@@ -4,5 +4,15 @@
4
4
  * @description Convert Knapsack Blocks to html
5
5
  */
6
6
  import { type BlockConfig } from '@knapsack/types';
7
+ /**
8
+ * Whether an empty render of this block means author content was lost.
9
+ *
10
+ * Many block types render nothing server-side by design (they render in the
11
+ * browser or embed a live source), so `''` from `blocksToHtml` is only a
12
+ * loss signal for TipTap-backed blocks that hold renderable content — the
13
+ * blocks where `tiptapToHtmlSafe` can swallow a render error into `''`.
14
+ * Every render-comparison tool judges empties by this one rule.
15
+ */
16
+ export declare function mustRenderNonEmpty(block: BlockConfig): boolean;
7
17
  export declare function blocksToHtml(blocks: BlockConfig[]): Promise<string>;
8
18
  //# sourceMappingURL=blocks-to-html.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"blocks-to-html.d.ts","sourceRoot":"","sources":["../src/blocks-to-html.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAuKnD,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzE"}
1
+ {"version":3,"file":"blocks-to-html.d.ts","sourceRoot":"","sources":["../src/blocks-to-html.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAoLnD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAe9D;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAMzE"}
@@ -9,7 +9,16 @@ import domPurify from 'isomorphic-dompurify';
9
9
  const EOL = os.EOL || '\n';
10
10
  async function mdToHtml(md) {
11
11
  const { marked } = await import('marked');
12
- return marked.parse(md);
12
+ // This output is both served (api-server's Block.html resolver) and
13
+ // hashed for render-parity comparisons. headerIds and mangle default to
14
+ // on in marked 6 and warn on every call. Disabling them DOES change what
15
+ // gets rendered here — headerIds stops emitting auto-generated heading id
16
+ // attributes, and nothing here consumes heading anchors or benefits from
17
+ // mangled mailto text, so that's an acceptable, deliberate change. It's
18
+ // still safe for i7-render-equivalence: gate.ts compares a before/after
19
+ // signature that both go through this same mdToHtml, so both sides drop
20
+ // the ids identically and the signatures move together, not apart.
21
+ return marked.parse(md, { headerIds: false, mangle: false });
13
22
  }
14
23
  // Utility to escape HTML attribute values
15
24
  function escapeAttr(val = '') {
@@ -26,6 +35,11 @@ function escapeAttr(val = '') {
26
35
  // guard below and degrade to empty content so the rest of the document
27
36
  // still renders.
28
37
  function tiptapToHtmlSafe(content, blockId, blockType) {
38
+ // Stored content with no root `type` (e.g. `{}` from an author never
39
+ // typing into the block) carries nothing to render — return empty without
40
+ // the error log, which is reserved for real schema drift.
41
+ if (!content?.type)
42
+ return '';
29
43
  try {
30
44
  return convertTipTapJsonToHtml(content);
31
45
  }
@@ -139,6 +153,30 @@ async function blockToHtml(b) {
139
153
  }
140
154
  }
141
155
  }
156
+ /**
157
+ * Whether an empty render of this block means author content was lost.
158
+ *
159
+ * Many block types render nothing server-side by design (they render in the
160
+ * browser or embed a live source), so `''` from `blocksToHtml` is only a
161
+ * loss signal for TipTap-backed blocks that hold renderable content — the
162
+ * blocks where `tiptapToHtmlSafe` can swallow a render error into `''`.
163
+ * Every render-comparison tool judges empties by this one rule.
164
+ */
165
+ export function mustRenderNonEmpty(block) {
166
+ if (!block.data)
167
+ return false;
168
+ switch (block.blockType) {
169
+ case 'text-editor':
170
+ case 'code-snippet':
171
+ case 'table':
172
+ case 'callout':
173
+ return (block.data.content?.content?.length ?? 0) > 0;
174
+ case 'guidelines':
175
+ return (block.data.guidelines ?? []).some((g) => (g.content?.content?.length ?? 0) > 0 || Boolean(g.image?.src));
176
+ default:
177
+ return false;
178
+ }
179
+ }
142
180
  export async function blocksToHtml(blocks) {
143
181
  // Sanitize each block's HTML output individually before joining
144
182
  const htmls = await Promise.all(blocks.map(async (block) => domPurify.sanitize(await blockToHtml(block))));
@@ -1 +1 @@
1
- {"version":3,"file":"blocks-to-html.js","sourceRoot":"","sources":["../src/blocks-to-html.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"blocks-to-html.js","sourceRoot":"","sources":["../src/blocks-to-html.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@knapsack/rendering-utils",
3
3
  "description": "",
4
- "version": "4.102.1--canary.7823.8675e86.0",
4
+ "version": "4.102.1--canary.8157.a7ffa8f.0",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
@@ -22,23 +22,23 @@
22
22
  "build": "tsc",
23
23
  "clean": "rm -rf ./dist",
24
24
  "lint": "eslint ./",
25
- "test": "vitest run"
25
+ "test": "tsc -p tsconfig.test.json && vitest run"
26
26
  },
27
27
  "dependencies": {
28
- "@knapsack/tiptap-utils": "4.102.1--canary.7823.8675e86.0",
29
- "@knapsack/utils": "4.102.1--canary.7823.8675e86.0",
28
+ "@knapsack/tiptap-utils": "4.102.1--canary.8157.a7ffa8f.0",
29
+ "@knapsack/utils": "4.102.1--canary.8157.a7ffa8f.0",
30
30
  "isomorphic-dompurify": "^1.13.0",
31
31
  "marked": "^6.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@knapsack/eslint-config-starter": "4.102.1--canary.7823.8675e86.0",
35
- "@knapsack/prettier-config": "4.102.1--canary.7823.8675e86.0",
36
- "@knapsack/types": "4.102.1--canary.7823.8675e86.0",
37
- "@knapsack/typescript-config-starter": "4.102.1--canary.7823.8675e86.0",
34
+ "@knapsack/eslint-config-starter": "4.102.1--canary.8157.a7ffa8f.0",
35
+ "@knapsack/prettier-config": "4.102.1--canary.8157.a7ffa8f.0",
36
+ "@knapsack/types": "4.102.1--canary.8157.a7ffa8f.0",
37
+ "@knapsack/typescript-config-starter": "4.102.1--canary.8157.a7ffa8f.0",
38
38
  "@types/node": "^24.13.2",
39
39
  "eslint": "^9.20.0",
40
- "typescript": "^5.9.3",
41
- "vitest": "^4.1.9"
40
+ "typescript": "^6.0.3",
41
+ "vitest": "^4.1.11"
42
42
  },
43
43
  "license": "GPL-2.0-or-later",
44
44
  "publishConfig": {
@@ -49,5 +49,5 @@
49
49
  "directory": "libs/rendering-utils",
50
50
  "type": "git"
51
51
  },
52
- "gitHead": "8675e860d101d7de6c09e39317ac00ba5e8639a4"
52
+ "gitHead": "a7ffa8fd21717341453999632af58d34192fb281"
53
53
  }
@@ -16,7 +16,16 @@ const EOL = os.EOL || '\n';
16
16
 
17
17
  async function mdToHtml(md: string): Promise<string> {
18
18
  const { marked } = await import('marked');
19
- return marked.parse(md);
19
+ // This output is both served (api-server's Block.html resolver) and
20
+ // hashed for render-parity comparisons. headerIds and mangle default to
21
+ // on in marked 6 and warn on every call. Disabling them DOES change what
22
+ // gets rendered here — headerIds stops emitting auto-generated heading id
23
+ // attributes, and nothing here consumes heading anchors or benefits from
24
+ // mangled mailto text, so that's an acceptable, deliberate change. It's
25
+ // still safe for i7-render-equivalence: gate.ts compares a before/after
26
+ // signature that both go through this same mdToHtml, so both sides drop
27
+ // the ids identically and the signatures move together, not apart.
28
+ return marked.parse(md, { headerIds: false, mangle: false });
20
29
  }
21
30
 
22
31
  // Utility to escape HTML attribute values
@@ -39,6 +48,10 @@ function tiptapToHtmlSafe(
39
48
  blockId: string,
40
49
  blockType: string,
41
50
  ): string {
51
+ // Stored content with no root `type` (e.g. `{}` from an author never
52
+ // typing into the block) carries nothing to render — return empty without
53
+ // the error log, which is reserved for real schema drift.
54
+ if (!content?.type) return '';
42
55
  try {
43
56
  return convertTipTapJsonToHtml(content);
44
57
  } catch (err) {
@@ -171,6 +184,32 @@ async function blockToHtml(b: BlockConfig): Promise<string> {
171
184
  }
172
185
  }
173
186
 
187
+ /**
188
+ * Whether an empty render of this block means author content was lost.
189
+ *
190
+ * Many block types render nothing server-side by design (they render in the
191
+ * browser or embed a live source), so `''` from `blocksToHtml` is only a
192
+ * loss signal for TipTap-backed blocks that hold renderable content — the
193
+ * blocks where `tiptapToHtmlSafe` can swallow a render error into `''`.
194
+ * Every render-comparison tool judges empties by this one rule.
195
+ */
196
+ export function mustRenderNonEmpty(block: BlockConfig): boolean {
197
+ if (!block.data) return false;
198
+ switch (block.blockType) {
199
+ case 'text-editor':
200
+ case 'code-snippet':
201
+ case 'table':
202
+ case 'callout':
203
+ return (block.data.content?.content?.length ?? 0) > 0;
204
+ case 'guidelines':
205
+ return (block.data.guidelines ?? []).some(
206
+ (g) => (g.content?.content?.length ?? 0) > 0 || Boolean(g.image?.src),
207
+ );
208
+ default:
209
+ return false;
210
+ }
211
+ }
212
+
174
213
  export async function blocksToHtml(blocks: BlockConfig[]): Promise<string> {
175
214
  // Sanitize each block's HTML output individually before joining
176
215
  const htmls = await Promise.all(
package/tsconfig.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "extends": "@knapsack/typescript-config-starter/tsconfig.build.json",
3
3
  "compilerOptions": {
4
+ "rootDir": "./src",
4
5
  "noImplicitAny": true,
5
6
  "strict": true,
6
7
  "outDir": "./dist"
7
8
  },
8
- "include": ["./src"]
9
+ "include": ["./src"],
10
+ "exclude": [
11
+ "src/**/*.test.ts",
12
+ "src/**/*.test.tsx",
13
+ "src/**/*.spec.ts",
14
+ "src/**/*.spec.tsx"
15
+ ]
9
16
  }