@fuzdev/fuz_code 0.42.0 → 0.44.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/README.md CHANGED
@@ -175,7 +175,7 @@ Experimental modules:
175
175
 
176
176
  ## License [🐦](https://wikipedia.org/wiki/Free_and_open-source_software)
177
177
 
178
- based on [Prism](https://github.com/PrismJS/prism)
178
+ based on [Prism](https://github.com/PrismJS/prism) ([prismjs.com](https://prismjs.com/))
179
179
  by [Lea Verou](https://lea.verou.me/)
180
180
 
181
181
  the [Svelte grammar](src/lib/grammar_svelte.ts)
@@ -1 +1 @@
1
- {"version":3,"file":"svelte_preprocess_fuz_code.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte_preprocess_fuz_code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,iBAAiB,EAAW,MAAM,iBAAiB,CAAC;AAKxE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACxC,gCAAgC;IAChC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAEjC,0DAA0D;IAC1D,aAAa,CAAC,EAAE,YAAY,CAAC;IAE7B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,0BAA0B,GACtC,UAAS,wBAA6B,KACpC,iBA0DF,CAAC"}
1
+ {"version":3,"file":"svelte_preprocess_fuz_code.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte_preprocess_fuz_code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,iBAAiB,EAAW,MAAM,iBAAiB,CAAC;AAexE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACxC,gCAAgC;IAChC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAEjC,0DAA0D;IAC1D,aAAa,CAAC,EAAE,YAAY,CAAC;IAE7B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,0BAA0B,GACtC,UAAS,wBAA6B,KACpC,iBA6DF,CAAC"}
@@ -1,16 +1,19 @@
1
1
  import { parse } from 'svelte/compiler';
2
2
  import MagicString from 'magic-string';
3
3
  import { walk } from 'zimmerframe';
4
+ import { should_exclude_path } from '@fuzdev/fuz_util/path.js';
5
+ import { escape_js_string } from '@fuzdev/fuz_util/string.js';
6
+ import { find_attribute, evaluate_static_expr, extract_static_string, build_static_bindings, resolve_component_names, } from '@fuzdev/fuz_util/svelte_preprocess_helpers.js';
4
7
  import { syntax_styler_global } from './syntax_styler_global.js';
5
8
  export const svelte_preprocess_fuz_code = (options = {}) => {
6
- const { exclude = [], syntax_styler = syntax_styler_global, cache = true, component_imports = ['@fuzdev/fuz_code/Code.svelte'], on_error = process.env.CI ? 'throw' : 'log', } = options;
9
+ const { exclude = [], syntax_styler = syntax_styler_global, cache = true, component_imports = ['@fuzdev/fuz_code/Code.svelte'], on_error = process.env.CI === 'true' ? 'throw' : 'log', } = options;
7
10
  // In-memory cache: content+lang hash → highlighted HTML
8
11
  const highlight_cache = new Map();
9
12
  return {
10
13
  name: 'fuz-code',
11
14
  markup: ({ content, filename }) => {
12
15
  // Skip excluded files
13
- if (should_exclude(filename, exclude)) {
16
+ if (should_exclude_path(filename, exclude)) {
14
17
  return { code: content };
15
18
  }
16
19
  // Quick check: does file import from a known Code component source?
@@ -20,16 +23,18 @@ export const svelte_preprocess_fuz_code = (options = {}) => {
20
23
  const s = new MagicString(content);
21
24
  const ast = parse(content, { filename, modern: true });
22
25
  // Resolve which local names map to the Code component
23
- const code_names = resolve_code_names(ast, component_imports);
26
+ const code_names = resolve_component_names(ast, component_imports);
24
27
  if (code_names.size === 0) {
25
28
  return { code: content };
26
29
  }
30
+ const bindings = build_static_bindings(ast);
27
31
  // Find Code component usages with static content
28
32
  const transformations = find_code_usages(ast, syntax_styler, code_names, {
29
33
  cache: cache ? highlight_cache : null,
30
34
  on_error,
31
35
  filename,
32
36
  source: content,
37
+ bindings,
33
38
  });
34
39
  if (transformations.length === 0) {
35
40
  return { code: content };
@@ -45,39 +50,6 @@ export const svelte_preprocess_fuz_code = (options = {}) => {
45
50
  },
46
51
  };
47
52
  };
48
- /**
49
- * Check if a filename matches any exclusion pattern.
50
- */
51
- const should_exclude = (filename, exclude) => {
52
- if (!filename || exclude.length === 0)
53
- return false;
54
- return exclude.some((pattern) => typeof pattern === 'string' ? filename.includes(pattern) : pattern.test(filename));
55
- };
56
- /**
57
- * Scans import declarations to find local names that import from known Code component sources.
58
- * Handles default imports, named imports, and aliased imports.
59
- * Checks both instance (`<script>`) and module (`<script module>`) scripts.
60
- */
61
- const resolve_code_names = (ast, component_imports) => {
62
- const names = new Set();
63
- for (const script of [ast.instance, ast.module]) {
64
- if (!script)
65
- continue;
66
- for (const node of script.content.body) {
67
- if (node.type !== 'ImportDeclaration')
68
- continue;
69
- if (!component_imports.includes(node.source.value))
70
- continue;
71
- for (const specifier of node.specifiers) {
72
- // default import: `import Code from '...'`
73
- // aliased: `import Highlighter from '...'`
74
- // named: `import { default as Code } from '...'`
75
- names.add(specifier.local.name);
76
- }
77
- }
78
- }
79
- return names;
80
- };
81
53
  /**
82
54
  * Attempt to highlight content, using cache if available.
83
55
  * Returns the highlighted HTML, or `null` on error.
@@ -111,6 +83,9 @@ const find_code_usages = (ast, syntax_styler, code_names, options) => {
111
83
  context.next();
112
84
  if (!code_names.has(node.name))
113
85
  return;
86
+ // Skip if spread attributes present — can't determine content statically
87
+ if (node.attributes.some((attr) => attr.type === 'SpreadAttribute'))
88
+ return;
114
89
  const content_attr = find_attribute(node, 'content');
115
90
  if (!content_attr)
116
91
  return;
@@ -122,13 +97,15 @@ const find_code_usages = (ast, syntax_styler, code_names, options) => {
122
97
  }
123
98
  // Resolve language - must be static and supported
124
99
  const lang_attr = find_attribute(node, 'lang');
125
- const lang_value = lang_attr ? extract_static_string(lang_attr.value) : 'svelte';
100
+ const lang_value = lang_attr
101
+ ? extract_static_string(lang_attr.value, options.bindings)
102
+ : 'svelte';
126
103
  if (lang_value === null)
127
104
  return;
128
105
  if (!syntax_styler.langs[lang_value])
129
106
  return;
130
107
  // Try simple static string
131
- const content_value = extract_static_string(content_attr.value);
108
+ const content_value = extract_static_string(content_attr.value, options.bindings);
132
109
  if (content_value !== null) {
133
110
  const html = try_highlight(content_value, lang_value, syntax_styler, options);
134
111
  if (html === null || html === content_value)
@@ -141,7 +118,7 @@ const find_code_usages = (ast, syntax_styler, code_names, options) => {
141
118
  return;
142
119
  }
143
120
  // Try conditional expression with static string branches
144
- const conditional = try_extract_conditional(content_attr.value, options.source);
121
+ const conditional = try_extract_conditional(content_attr.value, options.source, options.bindings);
145
122
  if (conditional) {
146
123
  const html_a = try_highlight(conditional.consequent, lang_value, syntax_styler, options);
147
124
  const html_b = try_highlight(conditional.alternate, lang_value, syntax_styler, options);
@@ -159,94 +136,26 @@ const find_code_usages = (ast, syntax_styler, code_names, options) => {
159
136
  });
160
137
  return transformations;
161
138
  };
162
- /**
163
- * Find an attribute by name on a component node.
164
- */
165
- const find_attribute = (node, name) => {
166
- for (const attr of node.attributes) {
167
- if (attr.type === 'Attribute' && attr.name === name) {
168
- return attr;
169
- }
170
- }
171
- return undefined;
172
- };
173
- /**
174
- * Recursively evaluate an expression AST node to a static string value.
175
- * Handles string literals, template literals (no interpolation), and string concatenation.
176
- * Returns `null` for dynamic or non-string expressions.
177
- */
178
- const evaluate_static_expr = (expr) => {
179
- if (expr.type === 'Literal' && typeof expr.value === 'string')
180
- return expr.value;
181
- if (expr.type === 'TemplateLiteral' && expr.expressions.length === 0) {
182
- return expr.quasis.map((q) => q.value.cooked ?? q.value.raw).join('');
183
- }
184
- if (expr.type === 'BinaryExpression' && expr.operator === '+') {
185
- const left = evaluate_static_expr(expr.left);
186
- if (left === null)
187
- return null;
188
- const right = evaluate_static_expr(expr.right);
189
- if (right === null)
190
- return null;
191
- return left + right;
192
- }
193
- return null;
194
- };
195
- /**
196
- * Extract the string value from a static attribute value.
197
- * Returns `null` for dynamic, non-string, or null literal values.
198
- */
199
- const extract_static_string = (value) => {
200
- // Boolean attribute
201
- if (value === true)
202
- return null;
203
- // Plain attribute: content="text"
204
- if (Array.isArray(value)) {
205
- const first = value[0];
206
- if (value.length === 1 && first?.type === 'Text') {
207
- return first.data;
208
- }
209
- return null;
210
- }
211
- // ExpressionTag
212
- const expr = value.expression;
213
- // Null literal
214
- if (expr.type === 'Literal' && expr.value === null)
215
- return null;
216
- return evaluate_static_expr(expr);
217
- };
218
139
  /**
219
140
  * Try to extract a conditional expression where both branches are static strings.
220
141
  * Returns the condition source text and both branch values, or `null` if not applicable.
221
142
  */
222
- const try_extract_conditional = (value, source) => {
143
+ const try_extract_conditional = (value, source, bindings) => {
223
144
  if (value === true || Array.isArray(value))
224
145
  return null;
225
146
  const expr = value.expression;
226
147
  if (expr.type !== 'ConditionalExpression')
227
148
  return null;
228
- const consequent = evaluate_static_expr(expr.consequent);
149
+ const consequent = evaluate_static_expr(expr.consequent, bindings);
229
150
  if (consequent === null)
230
151
  return null;
231
- const alternate = evaluate_static_expr(expr.alternate);
152
+ const alternate = evaluate_static_expr(expr.alternate, bindings);
232
153
  if (alternate === null)
233
154
  return null;
234
155
  const test = expr.test;
235
156
  const test_source = source.slice(test.start, test.end);
236
157
  return { test_source, consequent, alternate };
237
158
  };
238
- /**
239
- * Escapes a string for use inside a single-quoted JS string literal.
240
- * Single quotes are used because `stylize()` output contains double quotes
241
- * on every token span, so wrapping with single quotes avoids escaping those.
242
- */
243
- const escape_js_string = (html) => {
244
- return html
245
- .replace(/\\/g, '\\\\') // backslashes first
246
- .replace(/'/g, "\\'") // single quotes
247
- .replace(/\n/g, '\\n') // newlines
248
- .replace(/\r/g, '\\r'); // carriage returns
249
- };
250
159
  /**
251
160
  * Handle errors during highlighting.
252
161
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_code",
3
- "version": "0.42.0",
3
+ "version": "0.44.0",
4
4
  "description": "syntax styling utilities and components for TypeScript, Svelte, and Markdown",
5
5
  "glyph": "🎨",
6
6
  "logo": "logo.svg",
@@ -32,7 +32,9 @@
32
32
  "node": ">=22.15"
33
33
  },
34
34
  "peerDependencies": {
35
- "@fuzdev/fuz_css": ">=0.44.1",
35
+ "@fuzdev/fuz_css": ">=0.47.0",
36
+ "@fuzdev/fuz_util": ">=0.49.2",
37
+ "esm-env": "^1",
36
38
  "magic-string": "^0.30",
37
39
  "svelte": "^5",
38
40
  "zimmerframe": "^1"
@@ -41,6 +43,9 @@
41
43
  "@fuzdev/fuz_css": {
42
44
  "optional": true
43
45
  },
46
+ "@fuzdev/fuz_util": {
47
+ "optional": true
48
+ },
44
49
  "magic-string": {
45
50
  "optional": true
46
51
  },
@@ -55,9 +60,9 @@
55
60
  "@changesets/changelog-git": "^0.2.1",
56
61
  "@fuzdev/fuz_css": "^0.47.0",
57
62
  "@fuzdev/fuz_ui": "^0.181.1",
58
- "@fuzdev/fuz_util": "^0.48.3",
63
+ "@fuzdev/fuz_util": "^0.49.2",
59
64
  "@ryanatkn/eslint-config": "^0.9.0",
60
- "@ryanatkn/gro": "^0.190.0",
65
+ "@ryanatkn/gro": "^0.191.0",
61
66
  "@sveltejs/adapter-static": "^3.0.10",
62
67
  "@sveltejs/kit": "^2.50.1",
63
68
  "@sveltejs/package": "^2.5.7",
@@ -1,6 +1,16 @@
1
1
  import {parse, type PreprocessorGroup, type AST} from 'svelte/compiler';
2
2
  import MagicString from 'magic-string';
3
3
  import {walk} from 'zimmerframe';
4
+ import {should_exclude_path} from '@fuzdev/fuz_util/path.js';
5
+ import {escape_js_string} from '@fuzdev/fuz_util/string.js';
6
+ import {
7
+ find_attribute,
8
+ evaluate_static_expr,
9
+ extract_static_string,
10
+ build_static_bindings,
11
+ resolve_component_names,
12
+ type ResolvedComponentImport,
13
+ } from '@fuzdev/fuz_util/svelte_preprocess_helpers.js';
4
14
 
5
15
  import {syntax_styler_global} from './syntax_styler_global.js';
6
16
  import type {SyntaxStyler} from './syntax_styler.js';
@@ -38,7 +48,7 @@ export const svelte_preprocess_fuz_code = (
38
48
  syntax_styler = syntax_styler_global,
39
49
  cache = true,
40
50
  component_imports = ['@fuzdev/fuz_code/Code.svelte'],
41
- on_error = process.env.CI ? 'throw' : 'log',
51
+ on_error = process.env.CI === 'true' ? 'throw' : 'log',
42
52
  } = options;
43
53
 
44
54
  // In-memory cache: content+lang hash → highlighted HTML
@@ -49,7 +59,7 @@ export const svelte_preprocess_fuz_code = (
49
59
 
50
60
  markup: ({content, filename}) => {
51
61
  // Skip excluded files
52
- if (should_exclude(filename, exclude)) {
62
+ if (should_exclude_path(filename, exclude)) {
53
63
  return {code: content};
54
64
  }
55
65
 
@@ -62,17 +72,20 @@ export const svelte_preprocess_fuz_code = (
62
72
  const ast = parse(content, {filename, modern: true});
63
73
 
64
74
  // Resolve which local names map to the Code component
65
- const code_names = resolve_code_names(ast, component_imports);
75
+ const code_names = resolve_component_names(ast, component_imports);
66
76
  if (code_names.size === 0) {
67
77
  return {code: content};
68
78
  }
69
79
 
80
+ const bindings = build_static_bindings(ast);
81
+
70
82
  // Find Code component usages with static content
71
83
  const transformations = find_code_usages(ast, syntax_styler, code_names, {
72
84
  cache: cache ? highlight_cache : null,
73
85
  on_error,
74
86
  filename,
75
87
  source: content,
88
+ bindings,
76
89
  });
77
90
 
78
91
  if (transformations.length === 0) {
@@ -92,43 +105,6 @@ export const svelte_preprocess_fuz_code = (
92
105
  };
93
106
  };
94
107
 
95
- /**
96
- * Check if a filename matches any exclusion pattern.
97
- */
98
- const should_exclude = (filename: string | undefined, exclude: Array<string | RegExp>): boolean => {
99
- if (!filename || exclude.length === 0) return false;
100
- return exclude.some((pattern) =>
101
- typeof pattern === 'string' ? filename.includes(pattern) : pattern.test(filename),
102
- );
103
- };
104
-
105
- /**
106
- * Scans import declarations to find local names that import from known Code component sources.
107
- * Handles default imports, named imports, and aliased imports.
108
- * Checks both instance (`<script>`) and module (`<script module>`) scripts.
109
- */
110
- const resolve_code_names = (ast: AST.Root, component_imports: Array<string>): Set<string> => {
111
- const names: Set<string> = new Set();
112
-
113
- for (const script of [ast.instance, ast.module]) {
114
- if (!script) continue;
115
-
116
- for (const node of script.content.body) {
117
- if (node.type !== 'ImportDeclaration') continue;
118
- if (!component_imports.includes(node.source.value as string)) continue;
119
-
120
- for (const specifier of node.specifiers) {
121
- // default import: `import Code from '...'`
122
- // aliased: `import Highlighter from '...'`
123
- // named: `import { default as Code } from '...'`
124
- names.add(specifier.local.name);
125
- }
126
- }
127
- }
128
-
129
- return names;
130
- };
131
-
132
108
  interface Transformation {
133
109
  start: number;
134
110
  end: number;
@@ -140,6 +116,7 @@ interface FindCodeUsagesOptions {
140
116
  on_error: 'log' | 'throw';
141
117
  filename: string | undefined;
142
118
  source: string;
119
+ bindings: ReadonlyMap<string, string>;
143
120
  }
144
121
 
145
122
  /**
@@ -173,7 +150,7 @@ const try_highlight = (
173
150
  const find_code_usages = (
174
151
  ast: AST.Root,
175
152
  syntax_styler: SyntaxStyler,
176
- code_names: Set<string>,
153
+ code_names: Map<string, ResolvedComponentImport>,
177
154
  options: FindCodeUsagesOptions,
178
155
  ): Array<Transformation> => {
179
156
  const transformations: Array<Transformation> = [];
@@ -187,6 +164,9 @@ const find_code_usages = (
187
164
 
188
165
  if (!code_names.has(node.name)) return;
189
166
 
167
+ // Skip if spread attributes present — can't determine content statically
168
+ if (node.attributes.some((attr: any) => attr.type === 'SpreadAttribute')) return;
169
+
190
170
  const content_attr = find_attribute(node, 'content');
191
171
  if (!content_attr) return;
192
172
 
@@ -201,12 +181,14 @@ const find_code_usages = (
201
181
 
202
182
  // Resolve language - must be static and supported
203
183
  const lang_attr = find_attribute(node, 'lang');
204
- const lang_value = lang_attr ? extract_static_string(lang_attr.value) : 'svelte';
184
+ const lang_value = lang_attr
185
+ ? extract_static_string(lang_attr.value, options.bindings)
186
+ : 'svelte';
205
187
  if (lang_value === null) return;
206
188
  if (!syntax_styler.langs[lang_value]) return;
207
189
 
208
190
  // Try simple static string
209
- const content_value = extract_static_string(content_attr.value);
191
+ const content_value = extract_static_string(content_attr.value, options.bindings);
210
192
  if (content_value !== null) {
211
193
  const html = try_highlight(content_value, lang_value, syntax_styler, options);
212
194
  if (html === null || html === content_value) return;
@@ -219,7 +201,11 @@ const find_code_usages = (
219
201
  }
220
202
 
221
203
  // Try conditional expression with static string branches
222
- const conditional = try_extract_conditional(content_attr.value, options.source);
204
+ const conditional = try_extract_conditional(
205
+ content_attr.value,
206
+ options.source,
207
+ options.bindings,
208
+ );
223
209
  if (conditional) {
224
210
  const html_a = try_highlight(conditional.consequent, lang_value, syntax_styler, options);
225
211
  const html_b = try_highlight(conditional.alternate, lang_value, syntax_styler, options);
@@ -237,63 +223,7 @@ const find_code_usages = (
237
223
  return transformations;
238
224
  };
239
225
 
240
- /**
241
- * Find an attribute by name on a component node.
242
- */
243
- const find_attribute = (node: AST.Component, name: string): AST.Attribute | undefined => {
244
- for (const attr of node.attributes) {
245
- if (attr.type === 'Attribute' && attr.name === name) {
246
- return attr;
247
- }
248
- }
249
- return undefined;
250
- };
251
-
252
- type Attribute_Value = AST.Attribute['value'];
253
-
254
- /**
255
- * Recursively evaluate an expression AST node to a static string value.
256
- * Handles string literals, template literals (no interpolation), and string concatenation.
257
- * Returns `null` for dynamic or non-string expressions.
258
- */
259
- const evaluate_static_expr = (expr: any): string | null => {
260
- if (expr.type === 'Literal' && typeof expr.value === 'string') return expr.value;
261
- if (expr.type === 'TemplateLiteral' && expr.expressions.length === 0) {
262
- return expr.quasis.map((q: any) => q.value.cooked ?? q.value.raw).join('');
263
- }
264
- if (expr.type === 'BinaryExpression' && expr.operator === '+') {
265
- const left = evaluate_static_expr(expr.left);
266
- if (left === null) return null;
267
- const right = evaluate_static_expr(expr.right);
268
- if (right === null) return null;
269
- return left + right;
270
- }
271
- return null;
272
- };
273
-
274
- /**
275
- * Extract the string value from a static attribute value.
276
- * Returns `null` for dynamic, non-string, or null literal values.
277
- */
278
- const extract_static_string = (value: Attribute_Value): string | null => {
279
- // Boolean attribute
280
- if (value === true) return null;
281
-
282
- // Plain attribute: content="text"
283
- if (Array.isArray(value)) {
284
- const first = value[0];
285
- if (value.length === 1 && first?.type === 'Text') {
286
- return first.data;
287
- }
288
- return null;
289
- }
290
-
291
- // ExpressionTag
292
- const expr = value.expression;
293
- // Null literal
294
- if (expr.type === 'Literal' && expr.value === null) return null;
295
- return evaluate_static_expr(expr);
296
- };
226
+ type AttributeValue = AST.Attribute['value'];
297
227
 
298
228
  interface ConditionalStaticStrings {
299
229
  test_source: string;
@@ -306,16 +236,17 @@ interface ConditionalStaticStrings {
306
236
  * Returns the condition source text and both branch values, or `null` if not applicable.
307
237
  */
308
238
  const try_extract_conditional = (
309
- value: Attribute_Value,
239
+ value: AttributeValue,
310
240
  source: string,
241
+ bindings: ReadonlyMap<string, string>,
311
242
  ): ConditionalStaticStrings | null => {
312
243
  if (value === true || Array.isArray(value)) return null;
313
244
  const expr = value.expression;
314
245
  if (expr.type !== 'ConditionalExpression') return null;
315
246
 
316
- const consequent = evaluate_static_expr(expr.consequent);
247
+ const consequent = evaluate_static_expr(expr.consequent, bindings);
317
248
  if (consequent === null) return null;
318
- const alternate = evaluate_static_expr(expr.alternate);
249
+ const alternate = evaluate_static_expr(expr.alternate, bindings);
319
250
  if (alternate === null) return null;
320
251
 
321
252
  const test = expr.test as any;
@@ -323,19 +254,6 @@ const try_extract_conditional = (
323
254
  return {test_source, consequent, alternate};
324
255
  };
325
256
 
326
- /**
327
- * Escapes a string for use inside a single-quoted JS string literal.
328
- * Single quotes are used because `stylize()` output contains double quotes
329
- * on every token span, so wrapping with single quotes avoids escaping those.
330
- */
331
- const escape_js_string = (html: string): string => {
332
- return html
333
- .replace(/\\/g, '\\\\') // backslashes first
334
- .replace(/'/g, "\\'") // single quotes
335
- .replace(/\n/g, '\\n') // newlines
336
- .replace(/\r/g, '\\r'); // carriage returns
337
- };
338
-
339
257
  /**
340
258
  * Handle errors during highlighting.
341
259
  */