@generaltranslation/python-extractor 0.2.5 → 0.2.7

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.
@@ -89,16 +89,35 @@ async function processCall(callNode, imports, filePath, calls, errors, _warnings
89
89
  errors.push(`${locationStr(callNode)}: no string variants produced`);
90
90
  return;
91
91
  }
92
- const metadata = extractKwargs(argsNode, errors, callNode);
92
+ // Extract static metadata and check for derive in _context
93
+ const metadata = extractKwargs(argsNode, errors, callNode, imports);
94
+ const contextVariants = await extractDeriveContext(argsNode, imports, filePath, rootNode, errors);
93
95
  const staticId = `static-temp-id-${randomUUID()}`;
94
- for (const source of strings) {
95
- calls.push({
96
- source,
97
- ...metadata,
98
- staticId,
99
- line: callNode.startPosition.row + 1,
100
- column: callNode.startPosition.column,
101
- });
96
+ if (contextVariants) {
97
+ // Cross-product: content variants × context variants
98
+ for (const source of strings) {
99
+ for (const context of contextVariants) {
100
+ calls.push({
101
+ source,
102
+ ...metadata,
103
+ context,
104
+ staticId,
105
+ line: callNode.startPosition.row + 1,
106
+ column: callNode.startPosition.column,
107
+ });
108
+ }
109
+ }
110
+ }
111
+ else {
112
+ for (const source of strings) {
113
+ calls.push({
114
+ source,
115
+ ...metadata,
116
+ staticId,
117
+ line: callNode.startPosition.row + 1,
118
+ column: callNode.startPosition.column,
119
+ });
120
+ }
102
121
  }
103
122
  return;
104
123
  }
@@ -125,16 +144,35 @@ async function processCall(callNode, imports, filePath, calls, errors, _warnings
125
144
  errors.push(`${locationStr(callNode)}: could not extract string content`);
126
145
  return;
127
146
  }
128
- // Extract keyword arguments
129
- const metadata = extractKwargs(argsNode, errors, callNode);
130
- calls.push({
131
- source,
132
- ...metadata,
133
- line: callNode.startPosition.row + 1,
134
- column: callNode.startPosition.column,
135
- });
147
+ // Extract keyword arguments and check for derive in _context
148
+ const metadata = extractKwargs(argsNode, errors, callNode, imports);
149
+ const rootNode = callNode.tree?.rootNode;
150
+ const contextVariants = rootNode
151
+ ? await extractDeriveContext(argsNode, imports, filePath, rootNode, errors)
152
+ : null;
153
+ if (contextVariants) {
154
+ const staticId = `static-temp-id-${randomUUID()}`;
155
+ for (const context of contextVariants) {
156
+ calls.push({
157
+ source,
158
+ ...metadata,
159
+ context,
160
+ staticId,
161
+ line: callNode.startPosition.row + 1,
162
+ column: callNode.startPosition.column,
163
+ });
164
+ }
165
+ }
166
+ else {
167
+ calls.push({
168
+ source,
169
+ ...metadata,
170
+ line: callNode.startPosition.row + 1,
171
+ column: callNode.startPosition.column,
172
+ });
173
+ }
136
174
  }
137
- function extractKwargs(argsNode, errors, callNode) {
175
+ function extractKwargs(argsNode, errors, callNode, imports) {
138
176
  const result = {};
139
177
  for (let i = 0; i < argsNode.childCount; i++) {
140
178
  const child = argsNode.child(i);
@@ -166,6 +204,11 @@ function extractKwargs(argsNode, errors, callNode) {
166
204
  result.context = value;
167
205
  }
168
206
  }
207
+ else if (metadataKey === 'context' &&
208
+ imports &&
209
+ containsStaticCalls(valueNode, imports)) {
210
+ // _context contains derive() — skip error, caller handles derivation
211
+ }
169
212
  else {
170
213
  errors.push(`${locationStr(callNode)}: _${metadataKey} must be a string literal`);
171
214
  }
@@ -173,6 +216,37 @@ function extractKwargs(argsNode, errors, callNode) {
173
216
  }
174
217
  return result;
175
218
  }
219
+ /**
220
+ * Finds the _context keyword argument node and, if it contains a derive() call,
221
+ * parses it into context variants. Returns null if _context is static or absent.
222
+ */
223
+ async function extractDeriveContext(argsNode, imports, filePath, rootNode, errors) {
224
+ // Find _context kwarg
225
+ for (let i = 0; i < argsNode.childCount; i++) {
226
+ const child = argsNode.child(i);
227
+ if (!child || child.type !== 'keyword_argument')
228
+ continue;
229
+ const nameNode = child.childForFieldName('name');
230
+ const valueNode = child.childForFieldName('value');
231
+ if (!nameNode || !valueNode)
232
+ continue;
233
+ if (nameNode.text !== '_context')
234
+ continue;
235
+ // Check if value contains derive()
236
+ if (!containsStaticCalls(valueNode, imports))
237
+ return null;
238
+ const contextNode = await parseStringExpression(valueNode, {
239
+ rootNode,
240
+ imports,
241
+ filePath,
242
+ errors,
243
+ });
244
+ if (!contextNode)
245
+ return null;
246
+ return nodeToStrings(contextNode);
247
+ }
248
+ return null;
249
+ }
176
250
  function isFString(stringNode) {
177
251
  // Check if string_start begins with 'f' or 'F'
178
252
  for (let i = 0; i < stringNode.childCount; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@generaltranslation/python-extractor",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Python source code extraction for General Translation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -47,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "tree-sitter-python": "^0.25.0",
49
49
  "web-tree-sitter": "^0.26.6",
50
- "generaltranslation": "8.2.1"
50
+ "generaltranslation": "8.2.2"
51
51
  },
52
52
  "scripts": {
53
53
  "build": "tsc",