@typecad/expect 1.0.0-alpha.10 → 1.0.0-alpha.12
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/dist/host/config.js +220 -102
- package/package.json +6 -3
package/dist/host/config.js
CHANGED
|
@@ -79,6 +79,60 @@ function findConfigFile(projectRoot) {
|
|
|
79
79
|
];
|
|
80
80
|
return candidates.find(c => fs.existsSync(c));
|
|
81
81
|
}
|
|
82
|
+
/** Unwrap `as const` / `satisfies T` / parenthesized wrappers so initializers
|
|
83
|
+
* like `const config = {...} satisfies CuttlefishConfig` still parse. */
|
|
84
|
+
function unwrapExpr(node) {
|
|
85
|
+
let curr = node;
|
|
86
|
+
for (;;) {
|
|
87
|
+
if (ts.isAsExpression(curr) || ts.isTypeAssertionExpression(curr) || ts.isParenthesizedExpression(curr)) {
|
|
88
|
+
curr = curr.expression;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// satisfies is TS ≥4.9 — guard for older typings, then cast for .expression.
|
|
92
|
+
const isSatisfies = ts.isSatisfiesExpression;
|
|
93
|
+
if (typeof isSatisfies === 'function' && isSatisfies(curr)) {
|
|
94
|
+
curr = curr.expression;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
return curr;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Property key as plain text — accepts both `target:` and `'target':` forms. */
|
|
101
|
+
function propName(prop) {
|
|
102
|
+
const name = prop.name;
|
|
103
|
+
if (name && (ts.isIdentifier(name) || ts.isStringLiteral(name)))
|
|
104
|
+
return name.text;
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
/** String-literal (or plain template-literal) text, through as-cast wrappers. */
|
|
108
|
+
function stringLikeText(node) {
|
|
109
|
+
const curr = unwrapExpr(node);
|
|
110
|
+
return (ts.isStringLiteral(curr) || ts.isNoSubstitutionTemplateLiteral(curr))
|
|
111
|
+
? curr.text
|
|
112
|
+
: undefined;
|
|
113
|
+
}
|
|
114
|
+
/** Numeric literal via Number() so hex (0x38), decimals, and separators parse
|
|
115
|
+
* correctly — parseInt(text, 10) silently corrupted hex values to 0. */
|
|
116
|
+
function numericValue(node) {
|
|
117
|
+
const curr = unwrapExpr(node);
|
|
118
|
+
if (ts.isNumericLiteral(curr))
|
|
119
|
+
return Number(curr.text);
|
|
120
|
+
if (ts.isPrefixUnaryExpression(curr)
|
|
121
|
+
&& (curr.operator === ts.SyntaxKind.MinusToken || curr.operator === ts.SyntaxKind.PlusToken)
|
|
122
|
+
&& ts.isNumericLiteral(curr.operand)) {
|
|
123
|
+
const magnitude = Number(curr.operand.text);
|
|
124
|
+
return curr.operator === ts.SyntaxKind.MinusToken ? -magnitude : magnitude;
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
function boolValue(node) {
|
|
129
|
+
const curr = unwrapExpr(node);
|
|
130
|
+
if (curr.kind === ts.SyntaxKind.TrueKeyword)
|
|
131
|
+
return true;
|
|
132
|
+
if (curr.kind === ts.SyntaxKind.FalseKeyword)
|
|
133
|
+
return false;
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
82
136
|
export function parseConfigAST(configPath) {
|
|
83
137
|
const text = fs.readFileSync(configPath, 'utf8');
|
|
84
138
|
const sf = ts.createSourceFile(configPath, text, ts.ScriptTarget.Latest, true);
|
|
@@ -87,155 +141,212 @@ export function parseConfigAST(configPath) {
|
|
|
87
141
|
// `const config: CuttlefishConfig = { ... };`
|
|
88
142
|
if (ts.isVariableStatement(stmt)) {
|
|
89
143
|
for (const decl of stmt.declarationList.declarations) {
|
|
90
|
-
|
|
91
|
-
|
|
144
|
+
const init = decl.initializer ? unwrapExpr(decl.initializer) : undefined;
|
|
145
|
+
if (init && ts.isObjectLiteralExpression(init)) {
|
|
146
|
+
extractConfigProperties(init, result);
|
|
92
147
|
}
|
|
93
148
|
}
|
|
94
149
|
}
|
|
95
150
|
// `export default { ... };`
|
|
96
|
-
if (ts.isExportAssignment(stmt)
|
|
97
|
-
|
|
151
|
+
if (ts.isExportAssignment(stmt)) {
|
|
152
|
+
const expr = unwrapExpr(stmt.expression);
|
|
153
|
+
if (ts.isObjectLiteralExpression(expr)) {
|
|
154
|
+
extractConfigProperties(expr, result);
|
|
155
|
+
}
|
|
98
156
|
}
|
|
99
157
|
}
|
|
100
158
|
return result;
|
|
101
159
|
}
|
|
102
160
|
function extractConfigProperties(obj, out) {
|
|
103
161
|
for (const prop of obj.properties) {
|
|
104
|
-
if (!ts.isPropertyAssignment(prop)
|
|
162
|
+
if (!ts.isPropertyAssignment(prop))
|
|
163
|
+
continue;
|
|
164
|
+
const name = propName(prop);
|
|
165
|
+
if (!name)
|
|
105
166
|
continue;
|
|
106
|
-
const name = prop.name.text;
|
|
107
167
|
switch (name) {
|
|
108
|
-
case 'target':
|
|
109
|
-
|
|
110
|
-
|
|
168
|
+
case 'target': {
|
|
169
|
+
const v = stringLikeText(prop.initializer);
|
|
170
|
+
if (v !== undefined)
|
|
171
|
+
out.target = v;
|
|
111
172
|
break;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
173
|
+
}
|
|
174
|
+
case 'board': {
|
|
175
|
+
const v = stringLikeText(prop.initializer);
|
|
176
|
+
if (v !== undefined)
|
|
177
|
+
out.board = v;
|
|
115
178
|
break;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
179
|
+
}
|
|
180
|
+
case 'mcu': {
|
|
181
|
+
const v = stringLikeText(prop.initializer);
|
|
182
|
+
if (v !== undefined)
|
|
183
|
+
out.mcu = v;
|
|
119
184
|
break;
|
|
120
|
-
|
|
121
|
-
|
|
185
|
+
}
|
|
186
|
+
case 'frameworkData': {
|
|
187
|
+
const init = unwrapExpr(prop.initializer);
|
|
188
|
+
if (ts.isObjectLiteralExpression(init)) {
|
|
122
189
|
out.frameworkData = {};
|
|
123
|
-
for (const fProp of
|
|
124
|
-
if (ts.isPropertyAssignment(fProp) &&
|
|
125
|
-
|
|
126
|
-
|
|
190
|
+
for (const fProp of init.properties) {
|
|
191
|
+
if (ts.isPropertyAssignment(fProp) && propName(fProp) === 'buildTarget') {
|
|
192
|
+
const v = stringLikeText(fProp.initializer);
|
|
193
|
+
if (v !== undefined)
|
|
194
|
+
out.frameworkData.buildTarget = v;
|
|
127
195
|
}
|
|
128
196
|
}
|
|
129
197
|
}
|
|
130
198
|
break;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
199
|
+
}
|
|
200
|
+
case 'framework': {
|
|
201
|
+
const v = stringLikeText(prop.initializer);
|
|
202
|
+
if (v !== undefined)
|
|
203
|
+
out.framework = v;
|
|
134
204
|
break;
|
|
135
|
-
|
|
136
|
-
|
|
205
|
+
}
|
|
206
|
+
case 'toolchain': {
|
|
207
|
+
const init = unwrapExpr(prop.initializer);
|
|
208
|
+
if (ts.isObjectLiteralExpression(init)) {
|
|
137
209
|
out.toolchain = {};
|
|
138
|
-
for (const tProp of
|
|
139
|
-
if (ts.isPropertyAssignment(tProp) && tProp
|
|
140
|
-
|
|
210
|
+
for (const tProp of init.properties) {
|
|
211
|
+
if (ts.isPropertyAssignment(tProp) && propName(tProp) === 'type') {
|
|
212
|
+
const v = stringLikeText(tProp.initializer);
|
|
213
|
+
if (v !== undefined)
|
|
214
|
+
out.toolchain.type = v;
|
|
141
215
|
}
|
|
142
216
|
}
|
|
143
217
|
}
|
|
144
218
|
break;
|
|
145
|
-
|
|
219
|
+
}
|
|
220
|
+
case 'zephyr': {
|
|
146
221
|
// Parse the zephyr section (kconfig, runner, cmakeArgs) as a generic
|
|
147
222
|
// object so it can be passed through to the Zephyr toolchain.
|
|
148
|
-
|
|
223
|
+
const init = unwrapExpr(prop.initializer);
|
|
224
|
+
if (ts.isObjectLiteralExpression(init)) {
|
|
149
225
|
out.zephyr = {};
|
|
150
|
-
for (const zProp of
|
|
151
|
-
if (ts.isPropertyAssignment(zProp))
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
226
|
+
for (const zProp of init.properties) {
|
|
227
|
+
if (!ts.isPropertyAssignment(zProp))
|
|
228
|
+
continue;
|
|
229
|
+
const key = propName(zProp);
|
|
230
|
+
if (!key)
|
|
231
|
+
continue;
|
|
232
|
+
const zInit = unwrapExpr(zProp.initializer);
|
|
233
|
+
if (ts.isObjectLiteralExpression(zInit)) {
|
|
234
|
+
// kconfig: { 'CONFIG_X': 'y' }
|
|
235
|
+
const sub = {};
|
|
236
|
+
for (const subProp of zInit.properties) {
|
|
237
|
+
if (!ts.isPropertyAssignment(subProp))
|
|
238
|
+
continue;
|
|
239
|
+
const subKey = propName(subProp);
|
|
240
|
+
const v = stringLikeText(subProp.initializer);
|
|
241
|
+
if (subKey !== undefined && v !== undefined)
|
|
242
|
+
sub[subKey] = v;
|
|
165
243
|
}
|
|
244
|
+
out.zephyr[key] = sub;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
const v = stringLikeText(zInit);
|
|
248
|
+
if (v !== undefined)
|
|
249
|
+
out.zephyr[key] = v;
|
|
166
250
|
}
|
|
167
251
|
}
|
|
168
252
|
}
|
|
169
253
|
break;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
254
|
+
}
|
|
255
|
+
case 'test': {
|
|
256
|
+
const init = unwrapExpr(prop.initializer);
|
|
257
|
+
if (ts.isObjectLiteralExpression(init)) {
|
|
258
|
+
out.test = extractTestConfig(init);
|
|
173
259
|
}
|
|
174
260
|
break;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
261
|
+
}
|
|
262
|
+
case 'output': {
|
|
263
|
+
const init = unwrapExpr(prop.initializer);
|
|
264
|
+
if (ts.isObjectLiteralExpression(init)) {
|
|
265
|
+
out.output = extractOutputConfig(init);
|
|
178
266
|
}
|
|
179
267
|
break;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
268
|
+
}
|
|
269
|
+
case 'console': {
|
|
270
|
+
const init = unwrapExpr(prop.initializer);
|
|
271
|
+
if (ts.isObjectLiteralExpression(init)) {
|
|
272
|
+
out.console = extractConsoleConfig(init);
|
|
183
273
|
}
|
|
184
274
|
break;
|
|
275
|
+
}
|
|
185
276
|
}
|
|
186
277
|
}
|
|
187
278
|
}
|
|
188
279
|
function extractTestConfig(obj) {
|
|
189
280
|
const result = {};
|
|
190
281
|
for (const prop of obj.properties) {
|
|
191
|
-
if (!ts.isPropertyAssignment(prop)
|
|
282
|
+
if (!ts.isPropertyAssignment(prop))
|
|
283
|
+
continue;
|
|
284
|
+
const name = propName(prop);
|
|
285
|
+
if (!name)
|
|
192
286
|
continue;
|
|
193
|
-
const name = prop.name.text;
|
|
194
287
|
switch (name) {
|
|
195
|
-
case 'port':
|
|
196
|
-
|
|
197
|
-
|
|
288
|
+
case 'port': {
|
|
289
|
+
const v = stringLikeText(prop.initializer);
|
|
290
|
+
if (v !== undefined)
|
|
291
|
+
result.port = v;
|
|
198
292
|
break;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
293
|
+
}
|
|
294
|
+
case 'baudRate': {
|
|
295
|
+
const v = numericValue(prop.initializer);
|
|
296
|
+
if (v !== undefined)
|
|
297
|
+
result.baudRate = v;
|
|
202
298
|
break;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
299
|
+
}
|
|
300
|
+
case 'timeout': {
|
|
301
|
+
const v = numericValue(prop.initializer);
|
|
302
|
+
if (v !== undefined)
|
|
303
|
+
result.timeout = v;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
case 'serialOpenDelay': {
|
|
307
|
+
const v = numericValue(prop.initializer);
|
|
308
|
+
if (v !== undefined)
|
|
309
|
+
result.serialOpenDelay = v;
|
|
206
310
|
break;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
311
|
+
}
|
|
312
|
+
case 'buildTarget': {
|
|
313
|
+
const v = stringLikeText(prop.initializer);
|
|
314
|
+
if (v !== undefined)
|
|
315
|
+
result.buildTarget = v;
|
|
210
316
|
break;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
317
|
+
}
|
|
318
|
+
case 'resetAfterOpen': {
|
|
319
|
+
const v = boolValue(prop.initializer);
|
|
320
|
+
if (v !== undefined)
|
|
321
|
+
result.resetAfterOpen = v;
|
|
214
322
|
break;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (
|
|
219
|
-
result.
|
|
323
|
+
}
|
|
324
|
+
case 'verbose': {
|
|
325
|
+
const v = boolValue(prop.initializer);
|
|
326
|
+
if (v !== undefined)
|
|
327
|
+
result.verbose = v;
|
|
220
328
|
break;
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
329
|
+
}
|
|
330
|
+
case 'board': {
|
|
331
|
+
const v = stringLikeText(prop.initializer);
|
|
332
|
+
if (v !== undefined)
|
|
333
|
+
result.board = v;
|
|
224
334
|
break;
|
|
335
|
+
}
|
|
225
336
|
case 'include':
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
.
|
|
236
|
-
.map(el => el.text);
|
|
337
|
+
case 'exclude': {
|
|
338
|
+
const init = unwrapExpr(prop.initializer);
|
|
339
|
+
if (ts.isArrayLiteralExpression(init)) {
|
|
340
|
+
const values = init.elements
|
|
341
|
+
.map(el => stringLikeText(el))
|
|
342
|
+
.filter((v) => v !== undefined);
|
|
343
|
+
if (name === 'include')
|
|
344
|
+
result.include = values;
|
|
345
|
+
else
|
|
346
|
+
result.exclude = values;
|
|
237
347
|
}
|
|
238
348
|
break;
|
|
349
|
+
}
|
|
239
350
|
}
|
|
240
351
|
}
|
|
241
352
|
return result;
|
|
@@ -243,25 +354,32 @@ function extractTestConfig(obj) {
|
|
|
243
354
|
function extractOutputConfig(obj) {
|
|
244
355
|
const result = {};
|
|
245
356
|
for (const prop of obj.properties) {
|
|
246
|
-
if (!ts.isPropertyAssignment(prop)
|
|
357
|
+
if (!ts.isPropertyAssignment(prop))
|
|
358
|
+
continue;
|
|
359
|
+
const name = propName(prop);
|
|
360
|
+
if (!name)
|
|
361
|
+
continue;
|
|
362
|
+
const v = stringLikeText(prop.initializer);
|
|
363
|
+
if (v === undefined)
|
|
247
364
|
continue;
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
result.outDir = prop.initializer.text;
|
|
365
|
+
if (name === 'framework')
|
|
366
|
+
result.framework = v;
|
|
367
|
+
if (name === 'optimize')
|
|
368
|
+
result.optimize = v;
|
|
369
|
+
if (name === 'outDir')
|
|
370
|
+
result.outDir = v;
|
|
255
371
|
}
|
|
256
372
|
return result;
|
|
257
373
|
}
|
|
258
374
|
function extractConsoleConfig(obj) {
|
|
259
375
|
const result = {};
|
|
260
376
|
for (const prop of obj.properties) {
|
|
261
|
-
if (!ts.isPropertyAssignment(prop)
|
|
377
|
+
if (!ts.isPropertyAssignment(prop))
|
|
262
378
|
continue;
|
|
263
|
-
if (prop
|
|
264
|
-
|
|
379
|
+
if (propName(prop) === 'baudRate') {
|
|
380
|
+
const v = numericValue(prop.initializer);
|
|
381
|
+
if (v !== undefined)
|
|
382
|
+
result.baudRate = v;
|
|
265
383
|
}
|
|
266
384
|
}
|
|
267
385
|
return result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecad/expect",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.12",
|
|
4
4
|
"description": "Hardware test framework for TypeCAD — vitest-style assertions over serial",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,10 +40,13 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"typescript": "^5.7.3",
|
|
42
42
|
"serialport": "^12.0.0",
|
|
43
|
-
"@typecad/arduino-cli": "1.0.0-alpha.
|
|
43
|
+
"@typecad/arduino-cli": "1.0.0-alpha.12",
|
|
44
|
+
"@typecad/hal": "1.0.0-alpha.12"
|
|
45
|
+
},
|
|
46
|
+
"optionalDependencies": {
|
|
47
|
+
"@typecad/framework-zephyr": "1.0.0-alpha.12"
|
|
44
48
|
},
|
|
45
49
|
"devDependencies": {
|
|
46
|
-
"@typecad/cuttlefish": "1.0.0-alpha.10",
|
|
47
50
|
"@types/node": "^22.10.7"
|
|
48
51
|
},
|
|
49
52
|
"license": "MIT",
|