@fuzdev/fuz_code 0.43.0 → 0.44.1
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":"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;
|
|
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;AAgBxE,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"}
|
|
@@ -3,10 +3,10 @@ import MagicString from 'magic-string';
|
|
|
3
3
|
import { walk } from 'zimmerframe';
|
|
4
4
|
import { should_exclude_path } from '@fuzdev/fuz_util/path.js';
|
|
5
5
|
import { escape_js_string } from '@fuzdev/fuz_util/string.js';
|
|
6
|
-
import { find_attribute,
|
|
6
|
+
import { find_attribute, extract_static_string, try_extract_conditional_chain, build_static_bindings, resolve_component_names, handle_preprocess_error, } from '@fuzdev/fuz_util/svelte_preprocess_helpers.js';
|
|
7
7
|
import { syntax_styler_global } from './syntax_styler_global.js';
|
|
8
8
|
export const svelte_preprocess_fuz_code = (options = {}) => {
|
|
9
|
-
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;
|
|
10
10
|
// In-memory cache: content+lang hash → highlighted HTML
|
|
11
11
|
const highlight_cache = new Map();
|
|
12
12
|
return {
|
|
@@ -27,12 +27,14 @@ export const svelte_preprocess_fuz_code = (options = {}) => {
|
|
|
27
27
|
if (code_names.size === 0) {
|
|
28
28
|
return { code: content };
|
|
29
29
|
}
|
|
30
|
+
const bindings = build_static_bindings(ast);
|
|
30
31
|
// Find Code component usages with static content
|
|
31
32
|
const transformations = find_code_usages(ast, syntax_styler, code_names, {
|
|
32
33
|
cache: cache ? highlight_cache : null,
|
|
33
34
|
on_error,
|
|
34
35
|
filename,
|
|
35
36
|
source: content,
|
|
37
|
+
bindings,
|
|
36
38
|
});
|
|
37
39
|
if (transformations.length === 0) {
|
|
38
40
|
return { code: content };
|
|
@@ -61,7 +63,7 @@ const try_highlight = (text, lang, syntax_styler, options) => {
|
|
|
61
63
|
options.cache?.set(cache_key, html);
|
|
62
64
|
}
|
|
63
65
|
catch (error) {
|
|
64
|
-
|
|
66
|
+
handle_preprocess_error(error, '[fuz-code]', options.filename, options.on_error);
|
|
65
67
|
return null;
|
|
66
68
|
}
|
|
67
69
|
}
|
|
@@ -95,13 +97,15 @@ const find_code_usages = (ast, syntax_styler, code_names, options) => {
|
|
|
95
97
|
}
|
|
96
98
|
// Resolve language - must be static and supported
|
|
97
99
|
const lang_attr = find_attribute(node, 'lang');
|
|
98
|
-
const lang_value = lang_attr
|
|
100
|
+
const lang_value = lang_attr
|
|
101
|
+
? extract_static_string(lang_attr.value, options.bindings)
|
|
102
|
+
: 'svelte';
|
|
99
103
|
if (lang_value === null)
|
|
100
104
|
return;
|
|
101
105
|
if (!syntax_styler.langs[lang_value])
|
|
102
106
|
return;
|
|
103
107
|
// Try simple static string
|
|
104
|
-
const content_value = extract_static_string(content_attr.value);
|
|
108
|
+
const content_value = extract_static_string(content_attr.value, options.bindings);
|
|
105
109
|
if (content_value !== null) {
|
|
106
110
|
const html = try_highlight(content_value, lang_value, syntax_styler, options);
|
|
107
111
|
if (html === null || html === content_value)
|
|
@@ -113,53 +117,43 @@ const find_code_usages = (ast, syntax_styler, code_names, options) => {
|
|
|
113
117
|
});
|
|
114
118
|
return;
|
|
115
119
|
}
|
|
116
|
-
// Try conditional
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
// Try conditional chain (handles both simple and nested ternaries)
|
|
121
|
+
const chain = try_extract_conditional_chain(content_attr.value, options.source, options.bindings);
|
|
122
|
+
if (chain) {
|
|
123
|
+
// Highlight all branches
|
|
124
|
+
const highlighted = [];
|
|
125
|
+
let any_changed = false;
|
|
126
|
+
for (const branch of chain) {
|
|
127
|
+
const html = try_highlight(branch.value, lang_value, syntax_styler, options);
|
|
128
|
+
if (html === null)
|
|
129
|
+
return;
|
|
130
|
+
if (html !== branch.value)
|
|
131
|
+
any_changed = true;
|
|
132
|
+
highlighted.push({ html, original: branch.value });
|
|
133
|
+
}
|
|
134
|
+
if (!any_changed)
|
|
124
135
|
return;
|
|
136
|
+
// Build nested ternary expression for dangerous_raw_html
|
|
137
|
+
// chain: [{test_source: 'a', value: ...}, {test_source: 'b', value: ...}, {test_source: null, value: ...}]
|
|
138
|
+
// → a ? 'html_a' : b ? 'html_b' : 'html_c'
|
|
139
|
+
let expr = '';
|
|
140
|
+
for (let i = 0; i < chain.length; i++) {
|
|
141
|
+
const branch = chain[i];
|
|
142
|
+
const html = highlighted[i].html;
|
|
143
|
+
if (branch.test_source !== null) {
|
|
144
|
+
expr += `${branch.test_source} ? '${escape_js_string(html)}' : `;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
expr += `'${escape_js_string(html)}'`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
125
150
|
transformations.push({
|
|
126
151
|
start: content_attr.start,
|
|
127
152
|
end: content_attr.end,
|
|
128
|
-
replacement: `dangerous_raw_html={${
|
|
153
|
+
replacement: `dangerous_raw_html={${expr}}`,
|
|
129
154
|
});
|
|
130
155
|
}
|
|
131
156
|
},
|
|
132
157
|
});
|
|
133
158
|
return transformations;
|
|
134
159
|
};
|
|
135
|
-
/**
|
|
136
|
-
* Try to extract a conditional expression where both branches are static strings.
|
|
137
|
-
* Returns the condition source text and both branch values, or `null` if not applicable.
|
|
138
|
-
*/
|
|
139
|
-
const try_extract_conditional = (value, source) => {
|
|
140
|
-
if (value === true || Array.isArray(value))
|
|
141
|
-
return null;
|
|
142
|
-
const expr = value.expression;
|
|
143
|
-
if (expr.type !== 'ConditionalExpression')
|
|
144
|
-
return null;
|
|
145
|
-
const consequent = evaluate_static_expr(expr.consequent);
|
|
146
|
-
if (consequent === null)
|
|
147
|
-
return null;
|
|
148
|
-
const alternate = evaluate_static_expr(expr.alternate);
|
|
149
|
-
if (alternate === null)
|
|
150
|
-
return null;
|
|
151
|
-
const test = expr.test;
|
|
152
|
-
const test_source = source.slice(test.start, test.end);
|
|
153
|
-
return { test_source, consequent, alternate };
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* Handle errors during highlighting.
|
|
157
|
-
*/
|
|
158
|
-
const handle_error = (error, options) => {
|
|
159
|
-
const message = `[fuz-code] Highlighting failed${options.filename ? ` in ${options.filename}` : ''}: ${error instanceof Error ? error.message : String(error)}`;
|
|
160
|
-
if (options.on_error === 'throw') {
|
|
161
|
-
throw new Error(message);
|
|
162
|
-
}
|
|
163
|
-
// eslint-disable-next-line no-console
|
|
164
|
-
console.error(message);
|
|
165
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.1",
|
|
4
4
|
"description": "syntax styling utilities and components for TypeScript, Svelte, and Markdown",
|
|
5
5
|
"glyph": "🎨",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"node": ">=22.15"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@fuzdev/fuz_css": ">=0.
|
|
36
|
-
"@fuzdev/fuz_util": ">=0.49.
|
|
35
|
+
"@fuzdev/fuz_css": ">=0.47.0",
|
|
36
|
+
"@fuzdev/fuz_util": ">=0.49.2",
|
|
37
37
|
"esm-env": "^1",
|
|
38
38
|
"magic-string": "^0.30",
|
|
39
39
|
"svelte": "^5",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"@changesets/changelog-git": "^0.2.1",
|
|
61
61
|
"@fuzdev/fuz_css": "^0.47.0",
|
|
62
62
|
"@fuzdev/fuz_ui": "^0.181.1",
|
|
63
|
-
"@fuzdev/fuz_util": "^0.
|
|
63
|
+
"@fuzdev/fuz_util": "^0.50.0",
|
|
64
64
|
"@ryanatkn/eslint-config": "^0.9.0",
|
|
65
|
-
"@ryanatkn/gro": "^0.
|
|
65
|
+
"@ryanatkn/gro": "^0.191.0",
|
|
66
66
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
67
67
|
"@sveltejs/kit": "^2.50.1",
|
|
68
68
|
"@sveltejs/package": "^2.5.7",
|
|
@@ -5,9 +5,11 @@ import {should_exclude_path} from '@fuzdev/fuz_util/path.js';
|
|
|
5
5
|
import {escape_js_string} from '@fuzdev/fuz_util/string.js';
|
|
6
6
|
import {
|
|
7
7
|
find_attribute,
|
|
8
|
-
evaluate_static_expr,
|
|
9
8
|
extract_static_string,
|
|
9
|
+
try_extract_conditional_chain,
|
|
10
|
+
build_static_bindings,
|
|
10
11
|
resolve_component_names,
|
|
12
|
+
handle_preprocess_error,
|
|
11
13
|
type ResolvedComponentImport,
|
|
12
14
|
} from '@fuzdev/fuz_util/svelte_preprocess_helpers.js';
|
|
13
15
|
|
|
@@ -47,7 +49,7 @@ export const svelte_preprocess_fuz_code = (
|
|
|
47
49
|
syntax_styler = syntax_styler_global,
|
|
48
50
|
cache = true,
|
|
49
51
|
component_imports = ['@fuzdev/fuz_code/Code.svelte'],
|
|
50
|
-
on_error = process.env.CI ? 'throw' : 'log',
|
|
52
|
+
on_error = process.env.CI === 'true' ? 'throw' : 'log',
|
|
51
53
|
} = options;
|
|
52
54
|
|
|
53
55
|
// In-memory cache: content+lang hash → highlighted HTML
|
|
@@ -76,12 +78,15 @@ export const svelte_preprocess_fuz_code = (
|
|
|
76
78
|
return {code: content};
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
const bindings = build_static_bindings(ast);
|
|
82
|
+
|
|
79
83
|
// Find Code component usages with static content
|
|
80
84
|
const transformations = find_code_usages(ast, syntax_styler, code_names, {
|
|
81
85
|
cache: cache ? highlight_cache : null,
|
|
82
86
|
on_error,
|
|
83
87
|
filename,
|
|
84
88
|
source: content,
|
|
89
|
+
bindings,
|
|
85
90
|
});
|
|
86
91
|
|
|
87
92
|
if (transformations.length === 0) {
|
|
@@ -112,6 +117,7 @@ interface FindCodeUsagesOptions {
|
|
|
112
117
|
on_error: 'log' | 'throw';
|
|
113
118
|
filename: string | undefined;
|
|
114
119
|
source: string;
|
|
120
|
+
bindings: ReadonlyMap<string, string>;
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
/**
|
|
@@ -131,7 +137,7 @@ const try_highlight = (
|
|
|
131
137
|
html = syntax_styler.stylize(text, lang);
|
|
132
138
|
options.cache?.set(cache_key, html);
|
|
133
139
|
} catch (error) {
|
|
134
|
-
|
|
140
|
+
handle_preprocess_error(error, '[fuz-code]', options.filename, options.on_error);
|
|
135
141
|
return null;
|
|
136
142
|
}
|
|
137
143
|
}
|
|
@@ -160,7 +166,7 @@ const find_code_usages = (
|
|
|
160
166
|
if (!code_names.has(node.name)) return;
|
|
161
167
|
|
|
162
168
|
// Skip if spread attributes present — can't determine content statically
|
|
163
|
-
if (node.attributes.some((attr
|
|
169
|
+
if (node.attributes.some((attr) => attr.type === 'SpreadAttribute')) return;
|
|
164
170
|
|
|
165
171
|
const content_attr = find_attribute(node, 'content');
|
|
166
172
|
if (!content_attr) return;
|
|
@@ -176,12 +182,14 @@ const find_code_usages = (
|
|
|
176
182
|
|
|
177
183
|
// Resolve language - must be static and supported
|
|
178
184
|
const lang_attr = find_attribute(node, 'lang');
|
|
179
|
-
const lang_value = lang_attr
|
|
185
|
+
const lang_value = lang_attr
|
|
186
|
+
? extract_static_string(lang_attr.value, options.bindings)
|
|
187
|
+
: 'svelte';
|
|
180
188
|
if (lang_value === null) return;
|
|
181
189
|
if (!syntax_styler.langs[lang_value]) return;
|
|
182
190
|
|
|
183
191
|
// Try simple static string
|
|
184
|
-
const content_value = extract_static_string(content_attr.value);
|
|
192
|
+
const content_value = extract_static_string(content_attr.value, options.bindings);
|
|
185
193
|
if (content_value !== null) {
|
|
186
194
|
const html = try_highlight(content_value, lang_value, syntax_styler, options);
|
|
187
195
|
if (html === null || html === content_value) return;
|
|
@@ -193,17 +201,42 @@ const find_code_usages = (
|
|
|
193
201
|
return;
|
|
194
202
|
}
|
|
195
203
|
|
|
196
|
-
// Try conditional
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
204
|
+
// Try conditional chain (handles both simple and nested ternaries)
|
|
205
|
+
const chain = try_extract_conditional_chain(
|
|
206
|
+
content_attr.value,
|
|
207
|
+
options.source,
|
|
208
|
+
options.bindings,
|
|
209
|
+
);
|
|
210
|
+
if (chain) {
|
|
211
|
+
// Highlight all branches
|
|
212
|
+
const highlighted: Array<{html: string; original: string}> = [];
|
|
213
|
+
let any_changed = false;
|
|
214
|
+
for (const branch of chain) {
|
|
215
|
+
const html = try_highlight(branch.value, lang_value, syntax_styler, options);
|
|
216
|
+
if (html === null) return;
|
|
217
|
+
if (html !== branch.value) any_changed = true;
|
|
218
|
+
highlighted.push({html, original: branch.value});
|
|
219
|
+
}
|
|
220
|
+
if (!any_changed) return;
|
|
221
|
+
|
|
222
|
+
// Build nested ternary expression for dangerous_raw_html
|
|
223
|
+
// chain: [{test_source: 'a', value: ...}, {test_source: 'b', value: ...}, {test_source: null, value: ...}]
|
|
224
|
+
// → a ? 'html_a' : b ? 'html_b' : 'html_c'
|
|
225
|
+
let expr = '';
|
|
226
|
+
for (let i = 0; i < chain.length; i++) {
|
|
227
|
+
const branch = chain[i]!;
|
|
228
|
+
const html = highlighted[i]!.html;
|
|
229
|
+
if (branch.test_source !== null) {
|
|
230
|
+
expr += `${branch.test_source} ? '${escape_js_string(html)}' : `;
|
|
231
|
+
} else {
|
|
232
|
+
expr += `'${escape_js_string(html)}'`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
203
236
|
transformations.push({
|
|
204
237
|
start: content_attr.start,
|
|
205
238
|
end: content_attr.end,
|
|
206
|
-
replacement: `dangerous_raw_html={${
|
|
239
|
+
replacement: `dangerous_raw_html={${expr}}`,
|
|
207
240
|
});
|
|
208
241
|
}
|
|
209
242
|
},
|
|
@@ -211,46 +244,3 @@ const find_code_usages = (
|
|
|
211
244
|
|
|
212
245
|
return transformations;
|
|
213
246
|
};
|
|
214
|
-
|
|
215
|
-
type Attribute_Value = AST.Attribute['value'];
|
|
216
|
-
|
|
217
|
-
interface ConditionalStaticStrings {
|
|
218
|
-
test_source: string;
|
|
219
|
-
consequent: string;
|
|
220
|
-
alternate: string;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Try to extract a conditional expression where both branches are static strings.
|
|
225
|
-
* Returns the condition source text and both branch values, or `null` if not applicable.
|
|
226
|
-
*/
|
|
227
|
-
const try_extract_conditional = (
|
|
228
|
-
value: Attribute_Value,
|
|
229
|
-
source: string,
|
|
230
|
-
): ConditionalStaticStrings | null => {
|
|
231
|
-
if (value === true || Array.isArray(value)) return null;
|
|
232
|
-
const expr = value.expression;
|
|
233
|
-
if (expr.type !== 'ConditionalExpression') return null;
|
|
234
|
-
|
|
235
|
-
const consequent = evaluate_static_expr(expr.consequent);
|
|
236
|
-
if (consequent === null) return null;
|
|
237
|
-
const alternate = evaluate_static_expr(expr.alternate);
|
|
238
|
-
if (alternate === null) return null;
|
|
239
|
-
|
|
240
|
-
const test = expr.test as any;
|
|
241
|
-
const test_source = source.slice(test.start, test.end);
|
|
242
|
-
return {test_source, consequent, alternate};
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Handle errors during highlighting.
|
|
247
|
-
*/
|
|
248
|
-
const handle_error = (error: unknown, options: FindCodeUsagesOptions): void => {
|
|
249
|
-
const message = `[fuz-code] Highlighting failed${options.filename ? ` in ${options.filename}` : ''}: ${error instanceof Error ? error.message : String(error)}`;
|
|
250
|
-
|
|
251
|
-
if (options.on_error === 'throw') {
|
|
252
|
-
throw new Error(message);
|
|
253
|
-
}
|
|
254
|
-
// eslint-disable-next-line no-console
|
|
255
|
-
console.error(message);
|
|
256
|
-
};
|