@pikku/inspector 0.12.21 → 0.12.22

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,7 +1,7 @@
1
1
  import { test, describe } from 'node:test'
2
2
  import { strict as assert } from 'node:assert'
3
3
  import * as ts from 'typescript'
4
- import { extractDescription } from './extract-node-value'
4
+ import { extractDescription, extractStringLiteral } from './extract-node-value'
5
5
 
6
6
  const createChecker = (source: string) => {
7
7
  const sourceFile = ts.createSourceFile(
@@ -67,3 +67,51 @@ describe('extractDescription', () => {
67
67
  assert.equal(extractDescription(sourceFile, checker), null)
68
68
  })
69
69
  })
70
+
71
+ describe('extractStringLiteral — concatenation/template symmetry', () => {
72
+ const findInitializer = (node: ts.Node): ts.Expression | undefined => {
73
+ if (ts.isVariableDeclaration(node) && node.initializer) {
74
+ return node.initializer
75
+ }
76
+ let result: ts.Expression | undefined
77
+ ts.forEachChild(node, (child) => {
78
+ if (!result) result = findInitializer(child)
79
+ })
80
+ return result
81
+ }
82
+
83
+ test('a `+` operand that cannot be statically resolved becomes a ${...} placeholder', () => {
84
+ const { checker, sourceFile } = createChecker(
85
+ `const x = 'Enrich event ' + (event.id ?? event.name)`
86
+ )
87
+ const init = findInitializer(sourceFile)!
88
+ assert.equal(
89
+ extractStringLiteral(init, checker),
90
+ 'Enrich event ${event.id ?? event.name}'
91
+ )
92
+ })
93
+
94
+ test('`+` concatenation and template literal produce the same display string', () => {
95
+ const concat = createChecker(
96
+ `const x = 'Enrich event ' + (event.id ?? event.name)`
97
+ )
98
+ const template = createChecker(
99
+ 'const x = `Enrich event ${event.id ?? event.name}`'
100
+ )
101
+ const concatValue = extractStringLiteral(
102
+ findInitializer(concat.sourceFile)!,
103
+ concat.checker
104
+ )
105
+ const templateValue = extractStringLiteral(
106
+ findInitializer(template.sourceFile)!,
107
+ template.checker
108
+ )
109
+ assert.equal(concatValue, templateValue)
110
+ })
111
+
112
+ test('still resolves fully-static concatenation exactly', () => {
113
+ const { checker, sourceFile } = createChecker(`const x = 'a' + 'b' + 'c'`)
114
+ const init = findInitializer(sourceFile)!
115
+ assert.equal(extractStringLiteral(init, checker), 'abc')
116
+ })
117
+ })
@@ -37,8 +37,8 @@ export function extractStringLiteral(
37
37
  node.operatorToken.kind === ts.SyntaxKind.PlusToken
38
38
  ) {
39
39
  return (
40
- extractStringLiteral(node.left, checker) +
41
- extractStringLiteral(node.right, checker)
40
+ extractConcatOperand(node.left, checker) +
41
+ extractConcatOperand(node.right, checker)
42
42
  )
43
43
  }
44
44
 
@@ -59,6 +59,23 @@ export function extractStringLiteral(
59
59
  throw new Error('Unable to extract string literal from node')
60
60
  }
61
61
 
62
+ /**
63
+ * Resolve one operand of a `+` string concatenation.
64
+ *
65
+ * An operand that can't be statically resolved (e.g. `a ?? b`) becomes a
66
+ * `${...}` placeholder rather than throwing — mirroring the TemplateExpression
67
+ * branch above, so `'x ' + expr` and `` `x ${expr}` `` produce the same string.
68
+ * This keeps an unresolvable display name from aborting the whole extraction.
69
+ */
70
+ function extractConcatOperand(node: ts.Node, checker: ts.TypeChecker): string {
71
+ try {
72
+ return extractStringLiteral(node, checker)
73
+ } catch {
74
+ const inner = ts.isParenthesizedExpression(node) ? node.expression : node
75
+ return '${' + inner.getText() + '}'
76
+ }
77
+ }
78
+
62
79
  /**
63
80
  * Check if node is string-like (string literal or template expression)
64
81
  */
@@ -344,6 +344,7 @@ const mockLogger = {
344
344
  error: () => {},
345
345
  warn: () => {},
346
346
  debug: () => {},
347
+ diagnostic: () => {},
347
348
  critical: () => {},
348
349
  hasCriticalErrors: () => false,
349
350
  }
@@ -10,6 +10,7 @@ describe('matchesFilters', () => {
10
10
  error: () => {},
11
11
  warn: () => {},
12
12
  debug: () => {},
13
+ diagnostic: () => {},
13
14
  critical: () => {},
14
15
  hasCriticalErrors: () => false,
15
16
  }
@@ -43,6 +43,7 @@ function makeLogger() {
43
43
  info: () => {},
44
44
  warn: () => {},
45
45
  error: (msg: string) => errors.push(msg),
46
+ diagnostic: ({ message }: { message: string }) => errors.push(message),
46
47
  critical: (_code: string, msg: string) => errors.push(msg),
47
48
  },
48
49
  errors,
@@ -386,6 +386,22 @@ function extractVariableDeclaration(
386
386
  return step
387
387
  }
388
388
  }
389
+
390
+ // Promise.all fanout/group captured into a variable
391
+ // (const results = await Promise.all(array.map(...)))
392
+ if (isParallelFanout(call) || isParallelGroup(call)) {
393
+ const step = isParallelFanout(call)
394
+ ? extractParallelFanout(call, context)
395
+ : extractParallelGroup(call, context)
396
+ if (step) {
397
+ const type = context.checker.getTypeAtLocation(decl)
398
+ context.outputVars.set(varName, { type, node: decl })
399
+ if (isArrayType(type, context.checker)) {
400
+ context.arrayVars.add(varName)
401
+ }
402
+ return step
403
+ }
404
+ }
389
405
  }
390
406
 
391
407
  // Check for array.filter(...)