@leftium/gg 0.0.33 → 0.0.35
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 +123 -36
- package/dist/GgConsole.svelte +12 -0
- package/dist/GgConsole.svelte.d.ts +4 -0
- package/dist/OpenInEditorLink.svelte +17 -7
- package/dist/OpenInEditorLink.svelte.d.ts +8 -2
- package/dist/debug/browser.d.ts +10 -0
- package/dist/debug/browser.js +102 -0
- package/dist/debug/common.d.ts +41 -0
- package/dist/debug/common.js +191 -0
- package/dist/debug/index.d.ts +9 -0
- package/dist/debug/index.js +11 -0
- package/dist/debug/node.d.ts +10 -0
- package/dist/debug/node.js +137 -0
- package/dist/eruda/loader.js +0 -11
- package/dist/eruda/plugin.js +310 -29
- package/dist/eruda/types.d.ts +11 -5
- package/dist/gg-call-sites-plugin.d.ts +84 -0
- package/dist/gg-call-sites-plugin.js +600 -165
- package/dist/gg.d.ts +80 -0
- package/dist/gg.js +459 -110
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/vite.d.ts +37 -0
- package/dist/vite.js +46 -0
- package/package.json +20 -12
- package/dist/debug-bundled.d.ts +0 -2
- package/dist/debug-bundled.js +0 -3
- package/dist/debug.d.ts +0 -2
- package/dist/debug.js +0 -15
- package/patches/debug@4.4.3.patch +0 -35
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { parse } from 'svelte/compiler';
|
|
2
|
+
import * as acorn from 'acorn';
|
|
3
|
+
import { tsPlugin } from '@sveltejs/acorn-typescript';
|
|
1
4
|
/**
|
|
2
5
|
* Vite plugin that rewrites `gg(...)` and `gg.ns(...)` calls to
|
|
3
6
|
* `gg._ns({ns, file, line, col}, ...)` at build time. This gives each call
|
|
@@ -36,7 +39,16 @@ export default function ggCallSitesPlugin(options = {}) {
|
|
|
36
39
|
if (!/\.(js|ts|svelte|jsx|tsx|mjs|mts)(\?.*)?$/.test(id))
|
|
37
40
|
return null;
|
|
38
41
|
// Quick bail: no gg calls in this file
|
|
39
|
-
if (!code.includes('gg(') &&
|
|
42
|
+
if (!code.includes('gg(') &&
|
|
43
|
+
!code.includes('gg.ns(') &&
|
|
44
|
+
!code.includes('gg.warn(') &&
|
|
45
|
+
!code.includes('gg.error(') &&
|
|
46
|
+
!code.includes('gg.table(') &&
|
|
47
|
+
!code.includes('gg.trace(') &&
|
|
48
|
+
!code.includes('gg.assert(') &&
|
|
49
|
+
!code.includes('gg.time(') &&
|
|
50
|
+
!code.includes('gg.timeLog(') &&
|
|
51
|
+
!code.includes('gg.timeEnd('))
|
|
40
52
|
return null;
|
|
41
53
|
// Don't transform gg's own source files
|
|
42
54
|
if (id.includes('/lib/gg.') || id.includes('/lib/debug'))
|
|
@@ -48,119 +60,446 @@ export default function ggCallSitesPlugin(options = {}) {
|
|
|
48
60
|
// e.g. "/Users/me/project/src/routes/+page.svelte" → "src/routes/+page.svelte"
|
|
49
61
|
// $1 captures "/src/" or "/chunks/", so strip the leading slash
|
|
50
62
|
const filePath = id.replace(srcRootRegex, '$1').replace(/^\//, '');
|
|
51
|
-
// For .svelte files (
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
let
|
|
63
|
+
// For .svelte files, use svelte.parse() AST to find code ranges
|
|
64
|
+
// and function scopes. This distinguishes real JS expressions
|
|
65
|
+
// ({gg()}, onclick, etc.) from prose text mentioning "gg()",
|
|
66
|
+
// and uses estree AST for function name detection (no regex).
|
|
67
|
+
let svelteInfo;
|
|
68
|
+
let jsFunctionScopes;
|
|
56
69
|
if (/\.svelte(\?.*)?$/.test(id)) {
|
|
57
|
-
|
|
58
|
-
if (
|
|
70
|
+
svelteInfo = collectCodeRanges(code);
|
|
71
|
+
if (svelteInfo.ranges.length === 0)
|
|
59
72
|
return null;
|
|
60
73
|
}
|
|
61
|
-
|
|
74
|
+
else {
|
|
75
|
+
// For .js/.ts files, parse with acorn to extract function scopes
|
|
76
|
+
jsFunctionScopes = parseJavaScript(code);
|
|
77
|
+
}
|
|
78
|
+
return transformGgCalls(code, shortPath, filePath, svelteInfo, jsFunctionScopes);
|
|
62
79
|
}
|
|
63
80
|
};
|
|
64
81
|
}
|
|
65
82
|
/**
|
|
66
|
-
*
|
|
67
|
-
* Returns
|
|
83
|
+
* Parse JavaScript/TypeScript code using acorn to extract function scopes.
|
|
84
|
+
* Returns function scope ranges for accurate function name detection in .js/.ts files.
|
|
85
|
+
* Uses @sveltejs/acorn-typescript plugin to handle TypeScript syntax.
|
|
86
|
+
*
|
|
87
|
+
* For .svelte files, use `collectCodeRanges()` instead (which uses svelte.parse()).
|
|
68
88
|
*/
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
export function parseJavaScript(code) {
|
|
90
|
+
try {
|
|
91
|
+
// Parse as ES2022+ with TypeScript support
|
|
92
|
+
// sourceType: 'module' allows import/export, 'script' for regular scripts
|
|
93
|
+
// NOTE: @sveltejs/acorn-typescript REQUIRES locations: true
|
|
94
|
+
const parser = acorn.Parser.extend(tsPlugin());
|
|
95
|
+
const ast = parser.parse(code, {
|
|
96
|
+
ecmaVersion: 'latest',
|
|
97
|
+
sourceType: 'module',
|
|
98
|
+
locations: true, // Required by @sveltejs/acorn-typescript
|
|
99
|
+
ranges: true // Enable byte ranges for AST nodes
|
|
100
|
+
});
|
|
101
|
+
const scopes = [];
|
|
102
|
+
// Reuse the same AST walker we built for Svelte
|
|
103
|
+
collectFunctionScopes(ast.body, scopes);
|
|
104
|
+
scopes.sort((a, b) => a.start - b.start);
|
|
105
|
+
return scopes;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// If acorn can't parse it, fall back to empty scopes.
|
|
109
|
+
// The file might be malformed or use syntax we don't support yet.
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Use `svelte.parse()` to collect all code ranges and function scopes in a .svelte file.
|
|
115
|
+
*
|
|
116
|
+
* Code ranges identify where JS expressions live:
|
|
117
|
+
* - `<script>` blocks (context: 'script')
|
|
118
|
+
* - Template expressions: `{expr}`, `onclick={expr}`, `bind:value={expr}`,
|
|
119
|
+
* `class:name={expr}`, `{#if expr}`, `{#each expr}`, etc. (context: 'template')
|
|
120
|
+
*
|
|
121
|
+
* Function scopes are extracted from the estree AST in script blocks, mapping
|
|
122
|
+
* byte ranges to enclosing function names. This replaces regex-based function
|
|
123
|
+
* detection for .svelte files.
|
|
124
|
+
*
|
|
125
|
+
* Text nodes (prose) are NOT included, so `gg()` in `<p>text gg()</p>` is never transformed.
|
|
126
|
+
*/
|
|
127
|
+
export function collectCodeRanges(code) {
|
|
128
|
+
try {
|
|
129
|
+
const ast = parse(code, { modern: true });
|
|
130
|
+
const ranges = [];
|
|
131
|
+
const functionScopes = [];
|
|
132
|
+
// Script blocks (instance + module)
|
|
133
|
+
// The Svelte AST Program node has start/end at runtime but TypeScript's
|
|
134
|
+
// estree Program type doesn't declare them — we know they exist.
|
|
135
|
+
if (ast.instance) {
|
|
136
|
+
const content = ast.instance.content;
|
|
137
|
+
ranges.push({ start: content.start, end: content.end, context: 'script' });
|
|
138
|
+
collectFunctionScopes(ast.instance.content.body, functionScopes);
|
|
139
|
+
}
|
|
140
|
+
if (ast.module) {
|
|
141
|
+
const content = ast.module.content;
|
|
142
|
+
ranges.push({ start: content.start, end: content.end, context: 'script' });
|
|
143
|
+
collectFunctionScopes(ast.module.content.body, functionScopes);
|
|
79
144
|
}
|
|
145
|
+
// Walk the template fragment to find all expression positions
|
|
146
|
+
walkFragment(ast.fragment, ranges);
|
|
147
|
+
// Sort function scopes by start position for efficient lookup
|
|
148
|
+
functionScopes.sort((a, b) => a.start - b.start);
|
|
149
|
+
return { ranges, functionScopes };
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// If svelte.parse() fails, the Svelte compiler will also reject this file,
|
|
153
|
+
// so there's no point transforming gg() calls — return empty.
|
|
154
|
+
return { ranges: [], functionScopes: [] };
|
|
80
155
|
}
|
|
81
|
-
return ranges;
|
|
82
156
|
}
|
|
83
157
|
/**
|
|
84
|
-
*
|
|
158
|
+
* Walk an estree AST body to collect function scope ranges.
|
|
159
|
+
* Extracts function names from:
|
|
160
|
+
* - FunctionDeclaration: `function foo() {}`
|
|
161
|
+
* - VariableDeclarator with ArrowFunctionExpression/FunctionExpression: `const foo = () => {}`
|
|
162
|
+
* - Property with FunctionExpression: `{ method() {} }` or `{ prop: function() {} }`
|
|
163
|
+
* - MethodDefinition: `class Foo { bar() {} }`
|
|
85
164
|
*/
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
166
|
+
function collectFunctionScopes(nodes, scopes) {
|
|
167
|
+
if (!nodes)
|
|
168
|
+
return;
|
|
169
|
+
for (const node of nodes) {
|
|
170
|
+
collectFunctionScopesFromNode(node, scopes);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
174
|
+
function collectFunctionScopesFromNode(node, scopes) {
|
|
175
|
+
if (!node || typeof node !== 'object' || !node.type)
|
|
176
|
+
return;
|
|
177
|
+
switch (node.type) {
|
|
178
|
+
case 'ExportNamedDeclaration':
|
|
179
|
+
case 'ExportDefaultDeclaration':
|
|
180
|
+
// export function foo() {} or export default function foo() {}
|
|
181
|
+
if (node.declaration) {
|
|
182
|
+
collectFunctionScopesFromNode(node.declaration, scopes);
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
case 'FunctionDeclaration':
|
|
186
|
+
if (node.id?.name && node.body) {
|
|
187
|
+
scopes.push({ start: node.body.start, end: node.body.end, name: node.id.name });
|
|
188
|
+
}
|
|
189
|
+
// Recurse into the function body for nested functions
|
|
190
|
+
if (node.body?.body)
|
|
191
|
+
collectFunctionScopes(node.body.body, scopes);
|
|
192
|
+
return;
|
|
193
|
+
case 'VariableDeclaration':
|
|
194
|
+
for (const decl of node.declarations || []) {
|
|
195
|
+
collectFunctionScopesFromNode(decl, scopes);
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
case 'VariableDeclarator':
|
|
199
|
+
// const foo = () => {} or const foo = function() {}
|
|
200
|
+
if (node.id?.name &&
|
|
201
|
+
node.init &&
|
|
202
|
+
(node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression')) {
|
|
203
|
+
const body = node.init.body;
|
|
204
|
+
if (body) {
|
|
205
|
+
// Arrow with block body: () => { ... }
|
|
206
|
+
// Arrow with expression body: () => expr (use the arrow's range)
|
|
207
|
+
const start = body.type === 'BlockStatement' ? body.start : node.init.start;
|
|
208
|
+
const end = body.type === 'BlockStatement' ? body.end : node.init.end;
|
|
209
|
+
scopes.push({ start, end, name: node.id.name });
|
|
210
|
+
}
|
|
211
|
+
// Recurse into the function body
|
|
212
|
+
if (body?.body)
|
|
213
|
+
collectFunctionScopes(body.body, scopes);
|
|
214
|
+
}
|
|
215
|
+
// Recurse into object/array initializers for nested functions
|
|
216
|
+
if (node.init)
|
|
217
|
+
collectFunctionScopesFromNode(node.init, scopes);
|
|
218
|
+
return;
|
|
219
|
+
case 'ExpressionStatement':
|
|
220
|
+
collectFunctionScopesFromNode(node.expression, scopes);
|
|
221
|
+
return;
|
|
222
|
+
case 'ObjectExpression':
|
|
223
|
+
for (const prop of node.properties || []) {
|
|
224
|
+
collectFunctionScopesFromNode(prop, scopes);
|
|
225
|
+
}
|
|
226
|
+
return;
|
|
227
|
+
case 'Property':
|
|
228
|
+
// { method() {} } or { prop: function() {} }
|
|
229
|
+
if (node.key?.name &&
|
|
230
|
+
node.value &&
|
|
231
|
+
(node.value.type === 'FunctionExpression' || node.value.type === 'ArrowFunctionExpression')) {
|
|
232
|
+
const body = node.value.body;
|
|
233
|
+
if (body) {
|
|
234
|
+
const start = body.type === 'BlockStatement' ? body.start : node.value.start;
|
|
235
|
+
const end = body.type === 'BlockStatement' ? body.end : node.value.end;
|
|
236
|
+
scopes.push({ start, end, name: node.key.name });
|
|
237
|
+
}
|
|
238
|
+
if (body?.body)
|
|
239
|
+
collectFunctionScopes(body.body, scopes);
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
case 'MethodDefinition':
|
|
243
|
+
// class Foo { bar() {} }
|
|
244
|
+
if (node.key?.name && node.value?.body) {
|
|
245
|
+
scopes.push({
|
|
246
|
+
start: node.value.body.start,
|
|
247
|
+
end: node.value.body.end,
|
|
248
|
+
name: node.key.name
|
|
249
|
+
});
|
|
250
|
+
if (node.value.body?.body)
|
|
251
|
+
collectFunctionScopes(node.value.body.body, scopes);
|
|
252
|
+
}
|
|
253
|
+
return;
|
|
254
|
+
case 'ClassDeclaration':
|
|
255
|
+
case 'ClassExpression':
|
|
256
|
+
if (node.body?.body) {
|
|
257
|
+
for (const member of node.body.body) {
|
|
258
|
+
collectFunctionScopesFromNode(member, scopes);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
case 'IfStatement':
|
|
263
|
+
if (node.consequent)
|
|
264
|
+
collectFunctionScopesFromNode(node.consequent, scopes);
|
|
265
|
+
if (node.alternate)
|
|
266
|
+
collectFunctionScopesFromNode(node.alternate, scopes);
|
|
267
|
+
return;
|
|
268
|
+
case 'BlockStatement':
|
|
269
|
+
if (node.body)
|
|
270
|
+
collectFunctionScopes(node.body, scopes);
|
|
271
|
+
return;
|
|
272
|
+
case 'ForStatement':
|
|
273
|
+
case 'ForInStatement':
|
|
274
|
+
case 'ForOfStatement':
|
|
275
|
+
case 'WhileStatement':
|
|
276
|
+
case 'DoWhileStatement':
|
|
277
|
+
if (node.body)
|
|
278
|
+
collectFunctionScopesFromNode(node.body, scopes);
|
|
279
|
+
return;
|
|
280
|
+
case 'TryStatement':
|
|
281
|
+
if (node.block)
|
|
282
|
+
collectFunctionScopesFromNode(node.block, scopes);
|
|
283
|
+
if (node.handler?.body)
|
|
284
|
+
collectFunctionScopesFromNode(node.handler.body, scopes);
|
|
285
|
+
if (node.finalizer)
|
|
286
|
+
collectFunctionScopesFromNode(node.finalizer, scopes);
|
|
287
|
+
return;
|
|
288
|
+
case 'SwitchStatement':
|
|
289
|
+
for (const c of node.cases || []) {
|
|
290
|
+
if (c.consequent)
|
|
291
|
+
collectFunctionScopes(c.consequent, scopes);
|
|
292
|
+
}
|
|
293
|
+
return;
|
|
294
|
+
case 'ReturnStatement':
|
|
295
|
+
if (node.argument)
|
|
296
|
+
collectFunctionScopesFromNode(node.argument, scopes);
|
|
297
|
+
return;
|
|
298
|
+
case 'CallExpression':
|
|
299
|
+
// e.g. onMount(() => { gg() })
|
|
300
|
+
for (const arg of node.arguments || []) {
|
|
301
|
+
if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
|
|
302
|
+
// Anonymous callback — don't add a scope (no name to show),
|
|
303
|
+
// but recurse for nested named functions
|
|
304
|
+
if (arg.body?.body)
|
|
305
|
+
collectFunctionScopes(arg.body.body, scopes);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
90
309
|
}
|
|
91
|
-
return false;
|
|
92
310
|
}
|
|
93
311
|
/**
|
|
94
|
-
* Find the enclosing function name for a
|
|
95
|
-
*
|
|
312
|
+
* Find the innermost enclosing function name for a byte position
|
|
313
|
+
* using the pre-built function scope map.
|
|
314
|
+
* Returns empty string if not inside any named function.
|
|
96
315
|
*/
|
|
97
|
-
function
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const patterns = [
|
|
108
|
-
// function declarations: function foo(
|
|
109
|
-
/function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g,
|
|
110
|
-
// const/let/var assignment to arrow or function: const foo =
|
|
111
|
-
/(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/g,
|
|
112
|
-
// object method shorthand: foo() { or async foo() {
|
|
113
|
-
/(?:async\s+)?([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\([^)]*\)\s*\{/g,
|
|
114
|
-
// object property function: foo: function
|
|
115
|
-
/([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:\s*(?:async\s+)?function/g
|
|
116
|
-
];
|
|
117
|
-
let closestName = '';
|
|
118
|
-
let closestPos = -1;
|
|
119
|
-
for (const pattern of patterns) {
|
|
120
|
-
let match;
|
|
121
|
-
while ((match = pattern.exec(before)) !== null) {
|
|
122
|
-
const name = match[1];
|
|
123
|
-
// Skip common false positives
|
|
124
|
-
if ([
|
|
125
|
-
'if',
|
|
126
|
-
'for',
|
|
127
|
-
'while',
|
|
128
|
-
'switch',
|
|
129
|
-
'catch',
|
|
130
|
-
'return',
|
|
131
|
-
'import',
|
|
132
|
-
'export',
|
|
133
|
-
'from',
|
|
134
|
-
'new',
|
|
135
|
-
'typeof',
|
|
136
|
-
'instanceof',
|
|
137
|
-
'void',
|
|
138
|
-
'delete',
|
|
139
|
-
'throw',
|
|
140
|
-
'case',
|
|
141
|
-
'else',
|
|
142
|
-
'in',
|
|
143
|
-
'of',
|
|
144
|
-
'do',
|
|
145
|
-
'try',
|
|
146
|
-
'class',
|
|
147
|
-
'super',
|
|
148
|
-
'this',
|
|
149
|
-
'with',
|
|
150
|
-
'yield',
|
|
151
|
-
'await',
|
|
152
|
-
'debugger',
|
|
153
|
-
'default'
|
|
154
|
-
].includes(name)) {
|
|
155
|
-
continue;
|
|
316
|
+
export function findEnclosingFunctionFromScopes(pos, scopes) {
|
|
317
|
+
// Scopes can be nested; find the innermost (smallest range) that contains pos
|
|
318
|
+
let bestName = '';
|
|
319
|
+
let bestSize = Infinity;
|
|
320
|
+
for (const scope of scopes) {
|
|
321
|
+
if (pos >= scope.start && pos < scope.end) {
|
|
322
|
+
const size = scope.end - scope.start;
|
|
323
|
+
if (size < bestSize) {
|
|
324
|
+
bestSize = size;
|
|
325
|
+
bestName = scope.name;
|
|
156
326
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return bestName;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Recursively walk a Svelte AST fragment to collect template expression ranges.
|
|
333
|
+
*/
|
|
334
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
335
|
+
function walkFragment(fragment, ranges) {
|
|
336
|
+
if (!fragment?.nodes)
|
|
337
|
+
return;
|
|
338
|
+
for (const node of fragment.nodes) {
|
|
339
|
+
walkNode(node, ranges);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Walk a single AST node, collecting expression ranges for template code.
|
|
344
|
+
*/
|
|
345
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
346
|
+
function walkNode(node, ranges) {
|
|
347
|
+
if (!node || typeof node !== 'object')
|
|
348
|
+
return;
|
|
349
|
+
switch (node.type) {
|
|
350
|
+
// Template expression tags: {expr}
|
|
351
|
+
case 'ExpressionTag':
|
|
352
|
+
case 'HtmlTag':
|
|
353
|
+
case 'RenderTag':
|
|
354
|
+
case 'AttachTag':
|
|
355
|
+
if (node.expression && node.start != null && node.end != null) {
|
|
356
|
+
ranges.push({ start: node.start, end: node.end, context: 'template' });
|
|
357
|
+
}
|
|
358
|
+
return; // expressions are leaf nodes for our purposes
|
|
359
|
+
// Block tags with expressions: {#if expr}, {#each expr}, {#await expr}, {#key expr}
|
|
360
|
+
case 'IfBlock':
|
|
361
|
+
if (node.test)
|
|
362
|
+
addExprRange(node.test, ranges);
|
|
363
|
+
walkFragment(node.consequent, ranges);
|
|
364
|
+
if (node.alternate)
|
|
365
|
+
walkFragment(node.alternate, ranges);
|
|
366
|
+
return;
|
|
367
|
+
case 'EachBlock':
|
|
368
|
+
if (node.expression)
|
|
369
|
+
addExprRange(node.expression, ranges);
|
|
370
|
+
if (node.key)
|
|
371
|
+
addExprRange(node.key, ranges);
|
|
372
|
+
walkFragment(node.body, ranges);
|
|
373
|
+
if (node.fallback)
|
|
374
|
+
walkFragment(node.fallback, ranges);
|
|
375
|
+
return;
|
|
376
|
+
case 'AwaitBlock':
|
|
377
|
+
if (node.expression)
|
|
378
|
+
addExprRange(node.expression, ranges);
|
|
379
|
+
walkFragment(node.pending, ranges);
|
|
380
|
+
walkFragment(node.then, ranges);
|
|
381
|
+
walkFragment(node.catch, ranges);
|
|
382
|
+
return;
|
|
383
|
+
case 'KeyBlock':
|
|
384
|
+
if (node.expression)
|
|
385
|
+
addExprRange(node.expression, ranges);
|
|
386
|
+
walkFragment(node.fragment, ranges);
|
|
387
|
+
return;
|
|
388
|
+
case 'SnippetBlock':
|
|
389
|
+
walkFragment(node.body, ranges);
|
|
390
|
+
return;
|
|
391
|
+
// {@const ...} — contains a declaration, not a simple expression
|
|
392
|
+
case 'ConstTag':
|
|
393
|
+
if (node.declaration) {
|
|
394
|
+
ranges.push({ start: node.start, end: node.end, context: 'template' });
|
|
160
395
|
}
|
|
396
|
+
return;
|
|
397
|
+
// Elements and components — walk attributes + children
|
|
398
|
+
case 'RegularElement':
|
|
399
|
+
case 'Component':
|
|
400
|
+
case 'SvelteElement':
|
|
401
|
+
case 'SvelteComponent':
|
|
402
|
+
case 'SvelteBody':
|
|
403
|
+
case 'SvelteWindow':
|
|
404
|
+
case 'SvelteDocument':
|
|
405
|
+
case 'SvelteHead':
|
|
406
|
+
case 'SvelteSelf':
|
|
407
|
+
case 'SvelteFragment':
|
|
408
|
+
case 'SvelteBoundary':
|
|
409
|
+
case 'TitleElement':
|
|
410
|
+
case 'SlotElement':
|
|
411
|
+
walkAttributes(node.attributes, ranges);
|
|
412
|
+
walkFragment(node.fragment, ranges);
|
|
413
|
+
return;
|
|
414
|
+
// Text nodes — skip (prose, not code)
|
|
415
|
+
case 'Text':
|
|
416
|
+
case 'Comment':
|
|
417
|
+
return;
|
|
418
|
+
default:
|
|
419
|
+
// Unknown node type — try to walk children defensively
|
|
420
|
+
if (node.fragment)
|
|
421
|
+
walkFragment(node.fragment, ranges);
|
|
422
|
+
if (node.children)
|
|
423
|
+
walkFragment({ nodes: node.children }, ranges);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Walk element attributes to find expression ranges.
|
|
429
|
+
*/
|
|
430
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
431
|
+
function walkAttributes(attrs, ranges) {
|
|
432
|
+
if (!attrs)
|
|
433
|
+
return;
|
|
434
|
+
for (const attr of attrs) {
|
|
435
|
+
switch (attr.type) {
|
|
436
|
+
case 'Attribute':
|
|
437
|
+
// value can be: true | ExpressionTag | Array<Text | ExpressionTag>
|
|
438
|
+
if (attr.value === true)
|
|
439
|
+
break;
|
|
440
|
+
if (Array.isArray(attr.value)) {
|
|
441
|
+
for (const part of attr.value) {
|
|
442
|
+
if (part.type === 'ExpressionTag') {
|
|
443
|
+
ranges.push({ start: part.start, end: part.end, context: 'template' });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else if (attr.value?.type === 'ExpressionTag') {
|
|
448
|
+
ranges.push({ start: attr.value.start, end: attr.value.end, context: 'template' });
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
case 'SpreadAttribute':
|
|
452
|
+
if (attr.expression) {
|
|
453
|
+
ranges.push({ start: attr.start, end: attr.end, context: 'template' });
|
|
454
|
+
}
|
|
455
|
+
break;
|
|
456
|
+
// Directives: bind:, class:, style:, on:, use:, transition:, animate:, attach:
|
|
457
|
+
case 'BindDirective':
|
|
458
|
+
case 'ClassDirective':
|
|
459
|
+
case 'StyleDirective':
|
|
460
|
+
case 'OnDirective':
|
|
461
|
+
case 'UseDirective':
|
|
462
|
+
case 'TransitionDirective':
|
|
463
|
+
case 'AnimateDirective':
|
|
464
|
+
if (attr.expression) {
|
|
465
|
+
addExprRange(attr.expression, ranges);
|
|
466
|
+
}
|
|
467
|
+
// StyleDirective value can be an array
|
|
468
|
+
if (attr.value && Array.isArray(attr.value)) {
|
|
469
|
+
for (const part of attr.value) {
|
|
470
|
+
if (part.type === 'ExpressionTag') {
|
|
471
|
+
ranges.push({ start: part.start, end: part.end, context: 'template' });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
break;
|
|
476
|
+
case 'AttachTag':
|
|
477
|
+
if (attr.expression) {
|
|
478
|
+
ranges.push({ start: attr.start, end: attr.end, context: 'template' });
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
161
481
|
}
|
|
162
482
|
}
|
|
163
|
-
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Add a template expression range from an AST expression node.
|
|
486
|
+
*/
|
|
487
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
488
|
+
function addExprRange(expr, ranges) {
|
|
489
|
+
if (expr && expr.start != null && expr.end != null) {
|
|
490
|
+
ranges.push({ start: expr.start, end: expr.end, context: 'template' });
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Check if a character position falls within any of the given code ranges.
|
|
495
|
+
* Returns the matching range, or undefined if not in any range.
|
|
496
|
+
*/
|
|
497
|
+
function findCodeRange(pos, ranges) {
|
|
498
|
+
for (const r of ranges) {
|
|
499
|
+
if (pos >= r.start && pos < r.end)
|
|
500
|
+
return r;
|
|
501
|
+
}
|
|
502
|
+
return undefined;
|
|
164
503
|
}
|
|
165
504
|
/**
|
|
166
505
|
* Compute 1-based line number and column for a character offset in source code.
|
|
@@ -242,7 +581,7 @@ function findMatchingParen(code, openPos) {
|
|
|
242
581
|
/**
|
|
243
582
|
* Escape a string for embedding as a single-quoted JS string literal.
|
|
244
583
|
*/
|
|
245
|
-
function escapeForString(s) {
|
|
584
|
+
export function escapeForString(s) {
|
|
246
585
|
return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '\\r');
|
|
247
586
|
}
|
|
248
587
|
/**
|
|
@@ -251,78 +590,136 @@ function escapeForString(s) {
|
|
|
251
590
|
* Handles:
|
|
252
591
|
* - bare gg(expr) → gg._ns({ns, file, line, col, src: 'expr'}, expr)
|
|
253
592
|
* - gg.ns('label', expr) → gg._ns({ns, file, line, col, src: 'expr'}, expr)
|
|
593
|
+
* - label supports template variables: $NS, $FN, $FILE, $LINE, $COL
|
|
594
|
+
* - plain label (no variables) is used as-is (no auto @fn append)
|
|
254
595
|
* - gg.enable, gg.disable, gg.clearPersist, gg._onLog, gg._ns → left untouched
|
|
255
596
|
* - gg inside strings and comments → left untouched
|
|
597
|
+
*
|
|
598
|
+
* For .svelte files, `svelteInfo` (from `collectCodeRanges()`) determines which
|
|
599
|
+
* positions contain JS code and provides AST-based function scope detection.
|
|
600
|
+
* Script ranges use `{...}` object literal syntax; template ranges use `gg._o()`
|
|
601
|
+
* function-call syntax (no braces in Svelte markup). Positions outside any code
|
|
602
|
+
* range (e.g. prose text) are skipped.
|
|
603
|
+
*
|
|
604
|
+
* For .js/.ts files, `jsFunctionScopes` (from `parseJavaScript()`) provides
|
|
605
|
+
* AST-based function scope detection (no regex fallback).
|
|
256
606
|
*/
|
|
257
|
-
function transformGgCalls(code, shortPath, filePath,
|
|
607
|
+
export function transformGgCalls(code, shortPath, filePath, svelteInfo, jsFunctionScopes) {
|
|
258
608
|
// We use a manual scan approach to correctly handle strings and comments.
|
|
259
609
|
const result = [];
|
|
260
610
|
let lastIndex = 0;
|
|
261
611
|
let modified = false;
|
|
262
612
|
const escapedFile = escapeForString(filePath);
|
|
613
|
+
/**
|
|
614
|
+
* Find the code range containing `pos`, or undefined if outside all ranges.
|
|
615
|
+
* For non-.svelte files (no svelteInfo), returns a synthetic 'script' range.
|
|
616
|
+
*/
|
|
617
|
+
function rangeAt(pos) {
|
|
618
|
+
if (!svelteInfo)
|
|
619
|
+
return { start: 0, end: code.length, context: 'script' };
|
|
620
|
+
return findCodeRange(pos, svelteInfo.ranges);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Find the enclosing function name for a position.
|
|
624
|
+
* - .svelte files: uses estree AST function scope map from svelte.parse()
|
|
625
|
+
* - .js/.ts files: uses estree AST function scope map from acorn.parse()
|
|
626
|
+
* - template code ranges: always returns '' (no enclosing function from script)
|
|
627
|
+
*/
|
|
628
|
+
function getFunctionName(pos, range) {
|
|
629
|
+
if (range.context === 'template')
|
|
630
|
+
return '';
|
|
631
|
+
if (svelteInfo)
|
|
632
|
+
return findEnclosingFunctionFromScopes(pos, svelteInfo.functionScopes);
|
|
633
|
+
if (jsFunctionScopes)
|
|
634
|
+
return findEnclosingFunctionFromScopes(pos, jsFunctionScopes);
|
|
635
|
+
return ''; // Should not reach here unless both svelteInfo and jsFunctionScopes are undefined
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Build the options argument for gg._ns().
|
|
639
|
+
* Inside <script>: {ns:'...',file:'...',line:N,col:N} (object literal)
|
|
640
|
+
* In template: gg._o('...','...',N,N) (function call — no braces)
|
|
641
|
+
*/
|
|
642
|
+
function buildOptions(range, ns, line, col, src) {
|
|
643
|
+
if (range.context === 'script') {
|
|
644
|
+
return src
|
|
645
|
+
? `{ns:'${ns}',file:'${escapedFile}',line:${line},col:${col},src:'${src}'}`
|
|
646
|
+
: `{ns:'${ns}',file:'${escapedFile}',line:${line},col:${col}}`;
|
|
647
|
+
}
|
|
648
|
+
return src
|
|
649
|
+
? `gg._o('${ns}','${escapedFile}',${line},${col},'${src}')`
|
|
650
|
+
: `gg._o('${ns}','${escapedFile}',${line},${col})`;
|
|
651
|
+
}
|
|
263
652
|
// States for string/comment tracking
|
|
264
653
|
let i = 0;
|
|
265
654
|
while (i < code.length) {
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
i
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
655
|
+
// For .svelte files, only apply JS string/comment/backtick skipping inside
|
|
656
|
+
// code ranges (script blocks + template expressions). Outside code ranges,
|
|
657
|
+
// characters like ' " ` // /* are just HTML prose — NOT JS syntax.
|
|
658
|
+
// e.g. "Eruda's" contains an apostrophe that is NOT a JS string delimiter.
|
|
659
|
+
const inCodeRange = !svelteInfo || !!rangeAt(i);
|
|
660
|
+
if (inCodeRange) {
|
|
661
|
+
// Skip single-line comments
|
|
662
|
+
if (code[i] === '/' && code[i + 1] === '/') {
|
|
663
|
+
const end = code.indexOf('\n', i);
|
|
664
|
+
i = end === -1 ? code.length : end + 1;
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
// Skip multi-line comments
|
|
668
|
+
if (code[i] === '/' && code[i + 1] === '*') {
|
|
669
|
+
const end = code.indexOf('*/', i + 2);
|
|
670
|
+
i = end === -1 ? code.length : end + 2;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
// Skip template literals (backticks)
|
|
674
|
+
if (code[i] === '`') {
|
|
675
|
+
i++;
|
|
676
|
+
let depth = 0;
|
|
677
|
+
while (i < code.length) {
|
|
678
|
+
if (code[i] === '\\') {
|
|
679
|
+
i += 2;
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (code[i] === '$' && code[i + 1] === '{') {
|
|
683
|
+
depth++;
|
|
684
|
+
i += 2;
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (code[i] === '}' && depth > 0) {
|
|
688
|
+
depth--;
|
|
689
|
+
i++;
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
if (code[i] === '`' && depth === 0) {
|
|
693
|
+
i++;
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
298
696
|
i++;
|
|
299
|
-
break;
|
|
300
697
|
}
|
|
301
|
-
|
|
698
|
+
continue;
|
|
302
699
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
700
|
+
// Skip strings (single and double quotes)
|
|
701
|
+
if (code[i] === '"' || code[i] === "'") {
|
|
702
|
+
const quote = code[i];
|
|
703
|
+
i++;
|
|
704
|
+
while (i < code.length) {
|
|
705
|
+
if (code[i] === '\\') {
|
|
706
|
+
i += 2;
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
if (code[i] === quote) {
|
|
710
|
+
i++;
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
315
713
|
i++;
|
|
316
|
-
break;
|
|
317
714
|
}
|
|
318
|
-
|
|
715
|
+
continue;
|
|
319
716
|
}
|
|
320
|
-
continue;
|
|
321
717
|
}
|
|
322
718
|
// Look for 'gg' pattern — could be gg( or gg.ns(
|
|
323
719
|
if (code[i] === 'g' && code[i + 1] === 'g') {
|
|
324
|
-
// In .svelte files, skip gg outside
|
|
325
|
-
|
|
720
|
+
// In .svelte files, skip gg outside code ranges (prose text, etc.)
|
|
721
|
+
const range = rangeAt(i);
|
|
722
|
+
if (!range) {
|
|
326
723
|
i++;
|
|
327
724
|
continue;
|
|
328
725
|
}
|
|
@@ -335,7 +732,7 @@ function transformGgCalls(code, shortPath, filePath, scriptRanges) {
|
|
|
335
732
|
// Case 1: gg.ns('label', ...) → gg._ns({ns: 'label', file, line, col, src}, ...)
|
|
336
733
|
if (code.slice(i + 2, i + 6) === '.ns(') {
|
|
337
734
|
const { line, col } = getLineCol(code, i);
|
|
338
|
-
const fnName =
|
|
735
|
+
const fnName = getFunctionName(i, range);
|
|
339
736
|
const openParenPos = i + 5; // position of '(' in 'gg.ns('
|
|
340
737
|
// Find matching closing paren for the entire gg.ns(...) call
|
|
341
738
|
const closeParenPos = findMatchingParen(code, openParenPos);
|
|
@@ -357,33 +754,36 @@ function transformGgCalls(code, shortPath, filePath, scriptRanges) {
|
|
|
357
754
|
}
|
|
358
755
|
// j now points to closing quote
|
|
359
756
|
const nsLabelRaw = code.slice(afterNsParen + 1, j);
|
|
360
|
-
|
|
361
|
-
//
|
|
362
|
-
const
|
|
757
|
+
// Build callpoint: substitute $NS/$FN/$FILE/$LINE/$COL template variables.
|
|
758
|
+
// The auto-generated callpoint (file@fn) is what bare gg() would produce.
|
|
759
|
+
const autoCallpoint = `${shortPath}${fnName ? `@${fnName}` : ''}`;
|
|
760
|
+
const callpoint = escapeForString(nsLabelRaw
|
|
761
|
+
.replace(/\$NS/g, autoCallpoint)
|
|
762
|
+
.replace(/\$FN/g, fnName)
|
|
763
|
+
.replace(/\$FILE/g, shortPath)
|
|
764
|
+
.replace(/\$LINE/g, String(line))
|
|
765
|
+
.replace(/\$COL/g, String(col)));
|
|
363
766
|
// Check if there are more args after the string
|
|
364
767
|
const afterClosingQuote = j + 1;
|
|
365
768
|
let k = afterClosingQuote;
|
|
366
769
|
while (k < code.length && /\s/.test(code[k]))
|
|
367
770
|
k++;
|
|
368
771
|
if (code[k] === ')') {
|
|
369
|
-
// gg.ns('label') → gg._ns(
|
|
370
|
-
const optionsObj = `{ns:'${callpoint}',file:'${escapedFile}',line:${line},col:${col}}`;
|
|
772
|
+
// gg.ns('label') → gg._ns(opts)
|
|
371
773
|
result.push(code.slice(lastIndex, i));
|
|
372
|
-
result.push(`gg._ns(${
|
|
774
|
+
result.push(`gg._ns(${buildOptions(range, callpoint, line, col)})`);
|
|
373
775
|
lastIndex = k + 1;
|
|
374
776
|
i = k + 1;
|
|
375
777
|
}
|
|
376
778
|
else if (code[k] === ',') {
|
|
377
|
-
// gg.ns('label', args...) → gg._ns(
|
|
378
|
-
// Extract source text of remaining args (after the comma)
|
|
779
|
+
// gg.ns('label', args...) → gg._ns(opts, args...)
|
|
379
780
|
let argsStart = k + 1;
|
|
380
781
|
while (argsStart < closeParenPos && /\s/.test(code[argsStart]))
|
|
381
782
|
argsStart++;
|
|
382
783
|
const argsSrc = code.slice(argsStart, closeParenPos).trim();
|
|
383
784
|
const escapedSrc = escapeForString(argsSrc);
|
|
384
|
-
const optionsObj = `{ns:'${callpoint}',file:'${escapedFile}',line:${line},col:${col},src:'${escapedSrc}'}`;
|
|
385
785
|
result.push(code.slice(lastIndex, i));
|
|
386
|
-
result.push(`gg._ns(${
|
|
786
|
+
result.push(`gg._ns(${buildOptions(range, callpoint, line, col, escapedSrc)}, `);
|
|
387
787
|
lastIndex = k + 1; // skip past the comma, keep args as-is
|
|
388
788
|
i = k + 1;
|
|
389
789
|
}
|
|
@@ -399,7 +799,44 @@ function transformGgCalls(code, shortPath, filePath, scriptRanges) {
|
|
|
399
799
|
i += 6;
|
|
400
800
|
continue;
|
|
401
801
|
}
|
|
402
|
-
//
|
|
802
|
+
// Case 1b: gg.warn/error/table/trace/assert → gg._warn/_error/_table/_trace/_assert
|
|
803
|
+
// These methods are rewritten like bare gg() but with their internal variant.
|
|
804
|
+
const dotMethodMatch = code
|
|
805
|
+
.slice(i + 2)
|
|
806
|
+
.match(/^\.(warn|error|table|trace|assert|time|timeLog|timeEnd)\(/);
|
|
807
|
+
if (dotMethodMatch) {
|
|
808
|
+
const methodName = dotMethodMatch[1];
|
|
809
|
+
const internalName = `_${methodName}`;
|
|
810
|
+
const methodCallLen = 2 + 1 + methodName.length + 1; // 'gg' + '.' + method + '('
|
|
811
|
+
const openParenPos = i + methodCallLen - 1;
|
|
812
|
+
const { line, col } = getLineCol(code, i);
|
|
813
|
+
const fnName = getFunctionName(i, range);
|
|
814
|
+
const callpoint = `${shortPath}${fnName ? `@${fnName}` : ''}`;
|
|
815
|
+
const escapedNs = escapeForString(callpoint);
|
|
816
|
+
const closeParenPos = findMatchingParen(code, openParenPos);
|
|
817
|
+
if (closeParenPos === -1) {
|
|
818
|
+
i += methodCallLen;
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
const argsText = code.slice(openParenPos + 1, closeParenPos).trim();
|
|
822
|
+
result.push(code.slice(lastIndex, i));
|
|
823
|
+
if (argsText === '') {
|
|
824
|
+
// gg.warn() → gg._warn(opts)
|
|
825
|
+
result.push(`gg.${internalName}(${buildOptions(range, escapedNs, line, col)})`);
|
|
826
|
+
lastIndex = closeParenPos + 1;
|
|
827
|
+
i = closeParenPos + 1;
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
// gg.warn(expr) → gg._warn(opts, expr)
|
|
831
|
+
const escapedSrc = escapeForString(argsText);
|
|
832
|
+
result.push(`gg.${internalName}(${buildOptions(range, escapedNs, line, col, escapedSrc)}, `);
|
|
833
|
+
lastIndex = openParenPos + 1; // keep original args
|
|
834
|
+
i = openParenPos + 1;
|
|
835
|
+
}
|
|
836
|
+
modified = true;
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
// Skip other gg.* calls (gg.enable, gg.disable, gg._ns, gg._onLog, gg.time, etc.)
|
|
403
840
|
if (code[i + 2] === '.') {
|
|
404
841
|
i += 3;
|
|
405
842
|
continue;
|
|
@@ -407,7 +844,7 @@ function transformGgCalls(code, shortPath, filePath, scriptRanges) {
|
|
|
407
844
|
// Case 2: bare gg(...) → gg._ns({ns, file, line, col, src}, ...)
|
|
408
845
|
if (code[i + 2] === '(') {
|
|
409
846
|
const { line, col } = getLineCol(code, i);
|
|
410
|
-
const fnName =
|
|
847
|
+
const fnName = getFunctionName(i, range);
|
|
411
848
|
const callpoint = `${shortPath}${fnName ? `@${fnName}` : ''}`;
|
|
412
849
|
const escapedNs = escapeForString(callpoint);
|
|
413
850
|
const openParenPos = i + 2; // position of '(' in 'gg('
|
|
@@ -421,17 +858,15 @@ function transformGgCalls(code, shortPath, filePath, scriptRanges) {
|
|
|
421
858
|
// Emit everything before this match
|
|
422
859
|
result.push(code.slice(lastIndex, i));
|
|
423
860
|
if (argsText === '') {
|
|
424
|
-
// gg() → gg._ns(
|
|
425
|
-
|
|
426
|
-
result.push(`gg._ns(${optionsObj})`);
|
|
861
|
+
// gg() → gg._ns(opts)
|
|
862
|
+
result.push(`gg._ns(${buildOptions(range, escapedNs, line, col)})`);
|
|
427
863
|
lastIndex = closeParenPos + 1;
|
|
428
864
|
i = closeParenPos + 1;
|
|
429
865
|
}
|
|
430
866
|
else {
|
|
431
|
-
// gg(expr) → gg._ns(
|
|
867
|
+
// gg(expr) → gg._ns(opts, expr)
|
|
432
868
|
const escapedSrc = escapeForString(argsText);
|
|
433
|
-
|
|
434
|
-
result.push(`gg._ns(${optionsObj}, `);
|
|
869
|
+
result.push(`gg._ns(${buildOptions(range, escapedNs, line, col, escapedSrc)}, `);
|
|
435
870
|
lastIndex = openParenPos + 1; // keep original args
|
|
436
871
|
i = openParenPos + 1;
|
|
437
872
|
}
|