@fuzdev/fuz_util 0.49.1 → 0.49.3
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.
|
@@ -67,6 +67,31 @@ export declare const evaluate_static_expr: (expr: Expression, bindings?: Readonl
|
|
|
67
67
|
* @returns The resolved static string, or `null` if the value is dynamic.
|
|
68
68
|
*/
|
|
69
69
|
export declare const extract_static_string: (value: AST.Attribute["value"], bindings?: ReadonlyMap<string, string>) => string | null;
|
|
70
|
+
/** Result of extracting a conditional expression with static string branches. */
|
|
71
|
+
export interface ConditionalStaticStrings {
|
|
72
|
+
/** The source text of the test/condition expression. */
|
|
73
|
+
test_source: string;
|
|
74
|
+
/** The static string value of the consequent (truthy) branch. */
|
|
75
|
+
consequent: string;
|
|
76
|
+
/** The static string value of the alternate (falsy) branch. */
|
|
77
|
+
alternate: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Extracts a conditional expression where both branches are static strings.
|
|
81
|
+
*
|
|
82
|
+
* Handles `content={test ? 'a' : 'b'}` where both the consequent and alternate
|
|
83
|
+
* branches resolve to static strings via `evaluate_static_expr`. The test expression
|
|
84
|
+
* is preserved as source text (sliced from the original source) since it may be dynamic.
|
|
85
|
+
*
|
|
86
|
+
* Returns `null` if the attribute value is not an `ExpressionTag` containing a
|
|
87
|
+
* `ConditionalExpression`, or if either branch is not statically resolvable.
|
|
88
|
+
*
|
|
89
|
+
* @param value The attribute value from `AST.Attribute['value']`.
|
|
90
|
+
* @param source The full source string (needed to slice the test expression source text).
|
|
91
|
+
* @param bindings Map of variable names to their resolved static string values.
|
|
92
|
+
* @returns The condition source and both branch values, or `null` if not extractable.
|
|
93
|
+
*/
|
|
94
|
+
export declare const try_extract_conditional: (value: AST.Attribute["value"], source: string, bindings: ReadonlyMap<string, string>) => ConditionalStaticStrings | null;
|
|
70
95
|
/**
|
|
71
96
|
* Builds a map of statically resolvable `const` bindings from a Svelte AST.
|
|
72
97
|
*
|
|
@@ -123,6 +148,10 @@ export declare const generate_import_lines: (imports: Map<string, PreprocessImpo
|
|
|
123
148
|
* `skip` set are excluded from traversal — used to skip `ImportDeclaration`
|
|
124
149
|
* nodes so the import's own specifier identifier doesn't false-positive.
|
|
125
150
|
*
|
|
151
|
+
* Skips `Identifier` nodes in non-reference positions defined by
|
|
152
|
+
* `NON_REFERENCE_FIELDS` — for example, `obj.Mdz` (non-computed member property),
|
|
153
|
+
* `{ Mdz: value }` (non-computed object key), and statement labels.
|
|
154
|
+
*
|
|
126
155
|
* Safe for Svelte template ASTs: `Component.name` is a plain string property
|
|
127
156
|
* (not an `Identifier` node), so `<Mdz>` tags do not produce false matches.
|
|
128
157
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svelte_preprocess_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte_preprocess_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,UAAU,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,eAAe,EAAC,MAAM,QAAQ,CAAC;AACnG,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,iBAAiB,CAAC;AAEzC,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACpC,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;CAC1B;AAED,qDAAqD;AACrD,MAAM,WAAW,uBAAuB;IACvC,gEAAgE;IAChE,WAAW,EAAE,iBAAiB,CAAC;IAC/B,mDAAmD;IACnD,SAAS,EAAE,eAAe,GAAG,sBAAsB,CAAC;CACpD;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,GAAG,CAAC,SAAS,EAAE,MAAM,MAAM,KAAG,GAAG,CAAC,SAAS,GAAG,SAOlF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,UAAU,EAChB,WAAW,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KACpC,MAAM,GAAG,IA+BX,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,GACjC,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,EAC7B,WAAW,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KACpC,MAAM,GAAG,IAkBX,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,GAAI,KAAK,GAAG,CAAC,IAAI,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAiBvE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GACnC,KAAK,GAAG,CAAC,IAAI,EACb,mBAAmB,KAAK,CAAC,MAAM,CAAC,KAC9B,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAcrC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,MAYhE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAC1C,SAAQ,MAAa,KACnB,MAyBF,CAAC;
|
|
1
|
+
{"version":3,"file":"svelte_preprocess_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte_preprocess_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,UAAU,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,eAAe,EAAC,MAAM,QAAQ,CAAC;AACnG,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,iBAAiB,CAAC;AAEzC,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACpC,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;CAC1B;AAED,qDAAqD;AACrD,MAAM,WAAW,uBAAuB;IACvC,gEAAgE;IAChE,WAAW,EAAE,iBAAiB,CAAC;IAC/B,mDAAmD;IACnD,SAAS,EAAE,eAAe,GAAG,sBAAsB,CAAC;CACpD;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,GAAG,CAAC,SAAS,EAAE,MAAM,MAAM,KAAG,GAAG,CAAC,SAAS,GAAG,SAOlF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,UAAU,EAChB,WAAW,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KACpC,MAAM,GAAG,IA+BX,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,GACjC,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,EAC7B,WAAW,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KACpC,MAAM,GAAG,IAkBX,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,wBAAwB;IACxC,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,GACnC,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,EAC7B,QAAQ,MAAM,EACd,UAAU,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,KACnC,wBAAwB,GAAG,IAa7B,CAAC;AAOF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,GAAI,KAAK,GAAG,CAAC,IAAI,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAiBvE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GACnC,KAAK,GAAG,CAAC,IAAI,EACb,mBAAmB,KAAK,CAAC,MAAM,CAAC,KAC9B,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAcrC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,GAAI,QAAQ,GAAG,CAAC,MAAM,KAAG,MAYhE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAC1C,SAAQ,MAAa,KACnB,MAyBF,CAAC;AA0BF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,MAAM,MAAM,EACZ,OAAO,GAAG,CAAC,OAAO,CAAC,KACjB,OAgBF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,MAc/C,CAAC"}
|
|
@@ -112,6 +112,41 @@ export const extract_static_string = (value, bindings) => {
|
|
|
112
112
|
return null;
|
|
113
113
|
return evaluate_static_expr(expr, bindings);
|
|
114
114
|
};
|
|
115
|
+
/**
|
|
116
|
+
* Extracts a conditional expression where both branches are static strings.
|
|
117
|
+
*
|
|
118
|
+
* Handles `content={test ? 'a' : 'b'}` where both the consequent and alternate
|
|
119
|
+
* branches resolve to static strings via `evaluate_static_expr`. The test expression
|
|
120
|
+
* is preserved as source text (sliced from the original source) since it may be dynamic.
|
|
121
|
+
*
|
|
122
|
+
* Returns `null` if the attribute value is not an `ExpressionTag` containing a
|
|
123
|
+
* `ConditionalExpression`, or if either branch is not statically resolvable.
|
|
124
|
+
*
|
|
125
|
+
* @param value The attribute value from `AST.Attribute['value']`.
|
|
126
|
+
* @param source The full source string (needed to slice the test expression source text).
|
|
127
|
+
* @param bindings Map of variable names to their resolved static string values.
|
|
128
|
+
* @returns The condition source and both branch values, or `null` if not extractable.
|
|
129
|
+
*/
|
|
130
|
+
export const try_extract_conditional = (value, source, bindings) => {
|
|
131
|
+
if (value === true || Array.isArray(value))
|
|
132
|
+
return null;
|
|
133
|
+
const expr = value.expression;
|
|
134
|
+
if (expr.type !== 'ConditionalExpression')
|
|
135
|
+
return null;
|
|
136
|
+
const consequent = evaluate_static_expr(expr.consequent, bindings);
|
|
137
|
+
if (consequent === null)
|
|
138
|
+
return null;
|
|
139
|
+
const alternate = evaluate_static_expr(expr.alternate, bindings);
|
|
140
|
+
if (alternate === null)
|
|
141
|
+
return null;
|
|
142
|
+
const test = expr.test;
|
|
143
|
+
const test_source = source.slice(test.start, test.end);
|
|
144
|
+
return { test_source, consequent, alternate };
|
|
145
|
+
};
|
|
146
|
+
// TODO cross-import tracing: resolve `import {x} from './constants.js'` by reading
|
|
147
|
+
// and parsing the imported module, extracting `export const` values. Would need path
|
|
148
|
+
// resolution ($lib, tsconfig paths), a Program-node variant of this function, and
|
|
149
|
+
// cache invalidation when the imported file changes. Start with relative .ts/.js only.
|
|
115
150
|
/**
|
|
116
151
|
* Builds a map of statically resolvable `const` bindings from a Svelte AST.
|
|
117
152
|
*
|
|
@@ -235,6 +270,26 @@ export const generate_import_lines = (imports, indent = '\t') => {
|
|
|
235
270
|
}
|
|
236
271
|
return lines.join('\n');
|
|
237
272
|
};
|
|
273
|
+
/**
|
|
274
|
+
* ESTree node fields that contain `Identifier` nodes which are NOT binding references.
|
|
275
|
+
*
|
|
276
|
+
* Keyed by node type. Each entry lists fields to skip during identifier search,
|
|
277
|
+
* optionally conditioned on the node's `computed` property being falsy.
|
|
278
|
+
*
|
|
279
|
+
* Examples of non-reference positions:
|
|
280
|
+
* - `obj.Mdz` — `MemberExpression.property` when `computed: false`
|
|
281
|
+
* - `{ Mdz: value }` — `Property.key` when `computed: false`
|
|
282
|
+
* - `label: for(...)` — `LabeledStatement.label`
|
|
283
|
+
*/
|
|
284
|
+
const NON_REFERENCE_FIELDS = new Map([
|
|
285
|
+
['MemberExpression', [{ field: 'property', when_not_computed: true }]],
|
|
286
|
+
['Property', [{ field: 'key', when_not_computed: true }]],
|
|
287
|
+
['PropertyDefinition', [{ field: 'key', when_not_computed: true }]],
|
|
288
|
+
['MethodDefinition', [{ field: 'key', when_not_computed: true }]],
|
|
289
|
+
['LabeledStatement', [{ field: 'label' }]],
|
|
290
|
+
['BreakStatement', [{ field: 'label' }]],
|
|
291
|
+
['ContinueStatement', [{ field: 'label' }]],
|
|
292
|
+
]);
|
|
238
293
|
/**
|
|
239
294
|
* Checks if an identifier with the given name appears anywhere in an AST subtree.
|
|
240
295
|
*
|
|
@@ -243,6 +298,10 @@ export const generate_import_lines = (imports, indent = '\t') => {
|
|
|
243
298
|
* `skip` set are excluded from traversal — used to skip `ImportDeclaration`
|
|
244
299
|
* nodes so the import's own specifier identifier doesn't false-positive.
|
|
245
300
|
*
|
|
301
|
+
* Skips `Identifier` nodes in non-reference positions defined by
|
|
302
|
+
* `NON_REFERENCE_FIELDS` — for example, `obj.Mdz` (non-computed member property),
|
|
303
|
+
* `{ Mdz: value }` (non-computed object key), and statement labels.
|
|
304
|
+
*
|
|
246
305
|
* Safe for Svelte template ASTs: `Component.name` is a plain string property
|
|
247
306
|
* (not an `Identifier` node), so `<Mdz>` tags do not produce false matches.
|
|
248
307
|
*
|
|
@@ -262,7 +321,11 @@ export const has_identifier_in_tree = (node, name, skip) => {
|
|
|
262
321
|
const record = node;
|
|
263
322
|
if (record.type === 'Identifier' && record.name === name)
|
|
264
323
|
return true;
|
|
324
|
+
const rules = NON_REFERENCE_FIELDS.get(record.type);
|
|
265
325
|
for (const key of Object.keys(record)) {
|
|
326
|
+
if (rules?.some((r) => r.field === key && (!r.when_not_computed || !record.computed))) {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
266
329
|
if (has_identifier_in_tree(record[key], name, skip))
|
|
267
330
|
return true;
|
|
268
331
|
}
|
package/package.json
CHANGED
|
@@ -136,6 +136,55 @@ export const extract_static_string = (
|
|
|
136
136
|
return evaluate_static_expr(expr, bindings);
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
+
/** Result of extracting a conditional expression with static string branches. */
|
|
140
|
+
export interface ConditionalStaticStrings {
|
|
141
|
+
/** The source text of the test/condition expression. */
|
|
142
|
+
test_source: string;
|
|
143
|
+
/** The static string value of the consequent (truthy) branch. */
|
|
144
|
+
consequent: string;
|
|
145
|
+
/** The static string value of the alternate (falsy) branch. */
|
|
146
|
+
alternate: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Extracts a conditional expression where both branches are static strings.
|
|
151
|
+
*
|
|
152
|
+
* Handles `content={test ? 'a' : 'b'}` where both the consequent and alternate
|
|
153
|
+
* branches resolve to static strings via `evaluate_static_expr`. The test expression
|
|
154
|
+
* is preserved as source text (sliced from the original source) since it may be dynamic.
|
|
155
|
+
*
|
|
156
|
+
* Returns `null` if the attribute value is not an `ExpressionTag` containing a
|
|
157
|
+
* `ConditionalExpression`, or if either branch is not statically resolvable.
|
|
158
|
+
*
|
|
159
|
+
* @param value The attribute value from `AST.Attribute['value']`.
|
|
160
|
+
* @param source The full source string (needed to slice the test expression source text).
|
|
161
|
+
* @param bindings Map of variable names to their resolved static string values.
|
|
162
|
+
* @returns The condition source and both branch values, or `null` if not extractable.
|
|
163
|
+
*/
|
|
164
|
+
export const try_extract_conditional = (
|
|
165
|
+
value: AST.Attribute['value'],
|
|
166
|
+
source: string,
|
|
167
|
+
bindings: ReadonlyMap<string, string>,
|
|
168
|
+
): ConditionalStaticStrings | null => {
|
|
169
|
+
if (value === true || Array.isArray(value)) return null;
|
|
170
|
+
const expr = value.expression;
|
|
171
|
+
if (expr.type !== 'ConditionalExpression') return null;
|
|
172
|
+
|
|
173
|
+
const consequent = evaluate_static_expr(expr.consequent, bindings);
|
|
174
|
+
if (consequent === null) return null;
|
|
175
|
+
const alternate = evaluate_static_expr(expr.alternate, bindings);
|
|
176
|
+
if (alternate === null) return null;
|
|
177
|
+
|
|
178
|
+
const test = expr.test as any;
|
|
179
|
+
const test_source = source.slice(test.start, test.end);
|
|
180
|
+
return {test_source, consequent, alternate};
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// TODO cross-import tracing: resolve `import {x} from './constants.js'` by reading
|
|
184
|
+
// and parsing the imported module, extracting `export const` values. Would need path
|
|
185
|
+
// resolution ($lib, tsconfig paths), a Program-node variant of this function, and
|
|
186
|
+
// cache invalidation when the imported file changes. Start with relative .ts/.js only.
|
|
187
|
+
|
|
139
188
|
/**
|
|
140
189
|
* Builds a map of statically resolvable `const` bindings from a Svelte AST.
|
|
141
190
|
*
|
|
@@ -262,6 +311,30 @@ export const generate_import_lines = (
|
|
|
262
311
|
return lines.join('\n');
|
|
263
312
|
};
|
|
264
313
|
|
|
314
|
+
/**
|
|
315
|
+
* ESTree node fields that contain `Identifier` nodes which are NOT binding references.
|
|
316
|
+
*
|
|
317
|
+
* Keyed by node type. Each entry lists fields to skip during identifier search,
|
|
318
|
+
* optionally conditioned on the node's `computed` property being falsy.
|
|
319
|
+
*
|
|
320
|
+
* Examples of non-reference positions:
|
|
321
|
+
* - `obj.Mdz` — `MemberExpression.property` when `computed: false`
|
|
322
|
+
* - `{ Mdz: value }` — `Property.key` when `computed: false`
|
|
323
|
+
* - `label: for(...)` — `LabeledStatement.label`
|
|
324
|
+
*/
|
|
325
|
+
const NON_REFERENCE_FIELDS: Map<
|
|
326
|
+
string,
|
|
327
|
+
Array<{field: string; when_not_computed?: boolean}>
|
|
328
|
+
> = new Map([
|
|
329
|
+
['MemberExpression', [{field: 'property', when_not_computed: true}]],
|
|
330
|
+
['Property', [{field: 'key', when_not_computed: true}]],
|
|
331
|
+
['PropertyDefinition', [{field: 'key', when_not_computed: true}]],
|
|
332
|
+
['MethodDefinition', [{field: 'key', when_not_computed: true}]],
|
|
333
|
+
['LabeledStatement', [{field: 'label'}]],
|
|
334
|
+
['BreakStatement', [{field: 'label'}]],
|
|
335
|
+
['ContinueStatement', [{field: 'label'}]],
|
|
336
|
+
]);
|
|
337
|
+
|
|
265
338
|
/**
|
|
266
339
|
* Checks if an identifier with the given name appears anywhere in an AST subtree.
|
|
267
340
|
*
|
|
@@ -270,6 +343,10 @@ export const generate_import_lines = (
|
|
|
270
343
|
* `skip` set are excluded from traversal — used to skip `ImportDeclaration`
|
|
271
344
|
* nodes so the import's own specifier identifier doesn't false-positive.
|
|
272
345
|
*
|
|
346
|
+
* Skips `Identifier` nodes in non-reference positions defined by
|
|
347
|
+
* `NON_REFERENCE_FIELDS` — for example, `obj.Mdz` (non-computed member property),
|
|
348
|
+
* `{ Mdz: value }` (non-computed object key), and statement labels.
|
|
349
|
+
*
|
|
273
350
|
* Safe for Svelte template ASTs: `Component.name` is a plain string property
|
|
274
351
|
* (not an `Identifier` node), so `<Mdz>` tags do not produce false matches.
|
|
275
352
|
*
|
|
@@ -290,7 +367,11 @@ export const has_identifier_in_tree = (
|
|
|
290
367
|
}
|
|
291
368
|
const record = node as Record<string, unknown>;
|
|
292
369
|
if (record.type === 'Identifier' && record.name === name) return true;
|
|
370
|
+
const rules = NON_REFERENCE_FIELDS.get(record.type as string);
|
|
293
371
|
for (const key of Object.keys(record)) {
|
|
372
|
+
if (rules?.some((r) => r.field === key && (!r.when_not_computed || !record.computed))) {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
294
375
|
if (has_identifier_in_tree(record[key], name, skip)) return true;
|
|
295
376
|
}
|
|
296
377
|
return false;
|