plccheck 2.3.2 → 2.5.0
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 +29 -0
- package/bin/plccheck.js +191 -16
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -13,8 +13,37 @@ npx plccheck check ./path/to/project
|
|
|
13
13
|
- `plccheck serve` (LSP server over stdio)
|
|
14
14
|
- `plccheck check <files-or-folders...>`
|
|
15
15
|
- `plccheck emit --target <language> <file>`
|
|
16
|
+
- `plccheck test [--filter <text>] [--events-json] [--coverage-json <file>] [--coverage-lcov <file>] [--coverage-branches] <file-or-folder>`
|
|
16
17
|
- `plccheck version`
|
|
17
18
|
|
|
19
|
+
## Testing and Coverage
|
|
20
|
+
|
|
21
|
+
Run tests for a PLC root:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx plccheck test ./my-plc-project
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Request coverage artifacts in JSON and LCOV formats:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx plccheck test ./my-plc-project --coverage-json coverage.json --coverage-lcov coverage.info
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For zero-config discovery in the Coverage Gutters VS Code extension, write the LCOV tracefile as `lcov.info` instead:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx plccheck test ./my-plc-project --coverage-json coverage.json --coverage-lcov lcov.info
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Keep the default Stage 1 line-focused coverage view, but opt into additive Stage 2 branch data when needed:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx plccheck test ./my-plc-project --coverage-json coverage.json --coverage-branches
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Note: `--events-json` prints a stream of execution events to stdout, while `--coverage-json` writes a finalized coverage report to a file. By default, the JSON artifact stays statement/line-focused and still includes function summaries/details where available, and LCOV includes the matching function records. `--coverage-branches` widens the JSON artifact with additive branch summaries/details and also adds LCOV branch records where branch runtime data is available. For AI analysis, use the file artifact from `--coverage-json`.
|
|
46
|
+
|
|
18
47
|
## Publishing (maintainers)
|
|
19
48
|
|
|
20
49
|
Initial publish requires npm auth once (to create the package entries in the registry). After that, releases use GitHub Actions + npm Trusted Publishing.
|
package/bin/plccheck.js
CHANGED
|
@@ -32,6 +32,35 @@ if (!pkg) {
|
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const REPO_DEV_PACKAGE_NAME = 'dynamic-siemens-language-support';
|
|
36
|
+
|
|
37
|
+
const coverageValueFlags = [
|
|
38
|
+
'--coverage-json', '-coverage-json',
|
|
39
|
+
'--coverage-lcov', '-coverage-lcov'
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const testValueFlags = [
|
|
43
|
+
'--filter', '-filter',
|
|
44
|
+
'--parallel', '-parallel',
|
|
45
|
+
'--coverage-json', '-coverage-json',
|
|
46
|
+
'--coverage-lcov', '-coverage-lcov',
|
|
47
|
+
'--module', '-module',
|
|
48
|
+
'--entry-ob', '-entry-ob'
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const commandValueFlags = {
|
|
52
|
+
check: ['--jobs', '-jobs'],
|
|
53
|
+
emit: ['--target', '-target'],
|
|
54
|
+
transpile: ['--from', '-from', '--to', '-to'],
|
|
55
|
+
'go-gen': ['--out', '-out', '--module', '-module', '--entry-ob', '-entry-ob'],
|
|
56
|
+
test: testValueFlags,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const pathValueFlags = [
|
|
60
|
+
...coverageValueFlags,
|
|
61
|
+
'--out', '-out',
|
|
62
|
+
];
|
|
63
|
+
|
|
35
64
|
function cacheRoot() {
|
|
36
65
|
if (process.env.XDG_CACHE_HOME) return path.join(process.env.XDG_CACHE_HOME, 'plccheck');
|
|
37
66
|
if (process.platform === 'darwin') return path.join(os.homedir(), 'Library', 'Caches', 'plccheck');
|
|
@@ -186,7 +215,25 @@ function readOptionalDepVersion(pkgName) {
|
|
|
186
215
|
}
|
|
187
216
|
}
|
|
188
217
|
|
|
218
|
+
function isRepoDevelopmentMode(rootDir = path.join(__dirname, '..', '..', '..')) {
|
|
219
|
+
const repoRoot = path.resolve(rootDir);
|
|
220
|
+
const localGoMod = path.join(repoRoot, 'go_lsp', 'go.mod');
|
|
221
|
+
const localMain = path.join(repoRoot, 'go_lsp', 'cmd', 'siemens-lsp', 'main.go');
|
|
222
|
+
const localExtension = path.join(repoRoot, 'client', 'src', 'extension.ts');
|
|
223
|
+
if (!fs.existsSync(localGoMod) || !fs.existsSync(localMain) || !fs.existsSync(localExtension)) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
|
|
228
|
+
return pkgJson && pkgJson.name === REPO_DEV_PACKAGE_NAME;
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
189
234
|
async function resolveBinaryPath() {
|
|
235
|
+
if (isRepoDevelopmentMode()) return 'go';
|
|
236
|
+
|
|
190
237
|
try {
|
|
191
238
|
return require(pkg);
|
|
192
239
|
} catch (err) {
|
|
@@ -225,28 +272,156 @@ async function resolveBinaryPath() {
|
|
|
225
272
|
return binPath;
|
|
226
273
|
}
|
|
227
274
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
275
|
+
function resolveCoverageOutputPaths(args, cwd) {
|
|
276
|
+
const nextArgs = args.slice();
|
|
277
|
+
for (let i = 0; i < nextArgs.length; i++) {
|
|
278
|
+
const baseFlag = nextArgs[i].split('=')[0];
|
|
279
|
+
if (!coverageValueFlags.includes(baseFlag)) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (nextArgs[i].includes('=')) {
|
|
283
|
+
const value = nextArgs[i].slice(nextArgs[i].indexOf('=') + 1);
|
|
284
|
+
if (value && !path.isAbsolute(value)) {
|
|
285
|
+
nextArgs[i] = `${baseFlag}=${path.resolve(cwd, value)}`;
|
|
286
|
+
}
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (i + 1 < nextArgs.length && !nextArgs[i + 1].startsWith('-') && !path.isAbsolute(nextArgs[i + 1])) {
|
|
290
|
+
nextArgs[i + 1] = path.resolve(cwd, nextArgs[i + 1]);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return nextArgs;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function resolveTestPositionals(args, cwd) {
|
|
297
|
+
const nextArgs = args.slice();
|
|
298
|
+
if (nextArgs[0] !== 'test') {
|
|
299
|
+
return nextArgs;
|
|
300
|
+
}
|
|
301
|
+
for (let i = 1; i < nextArgs.length; i++) {
|
|
302
|
+
if (nextArgs[i].startsWith('-')) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const prev = nextArgs[i - 1];
|
|
306
|
+
if (testValueFlags.includes(prev)) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (!path.isAbsolute(nextArgs[i])) {
|
|
310
|
+
nextArgs[i] = path.resolve(cwd, nextArgs[i]);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return nextArgs;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function resolveGoRunPaths(args, cwd) {
|
|
317
|
+
const nextArgs = args.slice();
|
|
318
|
+
if (nextArgs.length === 0) {
|
|
319
|
+
return nextArgs;
|
|
320
|
+
}
|
|
321
|
+
const valueFlags = commandValueFlags[nextArgs[0]] || [];
|
|
322
|
+
for (let i = 1; i < nextArgs.length; i++) {
|
|
323
|
+
const arg = nextArgs[i];
|
|
324
|
+
if (arg.startsWith('-')) {
|
|
325
|
+
const baseFlag = arg.split('=')[0];
|
|
326
|
+
if (arg.includes('=')) {
|
|
327
|
+
const value = arg.slice(arg.indexOf('=') + 1);
|
|
328
|
+
if (value && pathValueFlags.includes(baseFlag) && !path.isAbsolute(value)) {
|
|
329
|
+
nextArgs[i] = `${baseFlag}=${path.resolve(cwd, value)}`;
|
|
330
|
+
}
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (valueFlags.includes(baseFlag) && i + 1 < nextArgs.length) {
|
|
334
|
+
if (pathValueFlags.includes(baseFlag) && !nextArgs[i + 1].startsWith('-') && !path.isAbsolute(nextArgs[i + 1])) {
|
|
335
|
+
nextArgs[i + 1] = path.resolve(cwd, nextArgs[i + 1]);
|
|
336
|
+
}
|
|
337
|
+
i++;
|
|
338
|
+
}
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (!path.isAbsolute(arg)) {
|
|
342
|
+
nextArgs[i] = path.resolve(cwd, arg);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return nextArgs;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function reorderTestArgs(args) {
|
|
349
|
+
if (args[0] !== 'test' && !(args[0] === 'run' && args[1] === './cmd/siemens-lsp' && args[2] === 'test')) {
|
|
350
|
+
return args.slice();
|
|
351
|
+
}
|
|
352
|
+
const isGoRun = args[0] === 'run';
|
|
353
|
+
const testArgsStartIndex = isGoRun ? 3 : 1;
|
|
354
|
+
const testPrefix = args.slice(0, testArgsStartIndex);
|
|
355
|
+
const testArgs = args.slice(testArgsStartIndex);
|
|
356
|
+
const flags = [];
|
|
357
|
+
const positionals = [];
|
|
358
|
+
for (let i = 0; i < testArgs.length; i++) {
|
|
359
|
+
if (testArgs[i].startsWith('-')) {
|
|
360
|
+
flags.push(testArgs[i]);
|
|
361
|
+
const baseFlag = testArgs[i].split('=')[0];
|
|
362
|
+
if (testValueFlags.includes(baseFlag) && !testArgs[i].includes('=') && i + 1 < testArgs.length) {
|
|
363
|
+
flags.push(testArgs[++i]);
|
|
364
|
+
}
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
positionals.push(testArgs[i]);
|
|
368
|
+
}
|
|
369
|
+
return [...testPrefix, ...flags, ...positionals];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async function main(argv = process.argv.slice(2)) {
|
|
373
|
+
const binaryPath = await resolveBinaryPath();
|
|
374
|
+
let executable = binaryPath;
|
|
375
|
+
let args = argv.slice();
|
|
376
|
+
|
|
377
|
+
if (binaryPath === 'go') {
|
|
378
|
+
const goLspPath = path.join(__dirname, '..', '..', '..', 'go_lsp');
|
|
379
|
+
executable = 'go';
|
|
380
|
+
args = resolveGoRunPaths(args, process.cwd());
|
|
381
|
+
args = ['run', './cmd/siemens-lsp', ...args];
|
|
382
|
+
process.chdir(goLspPath);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
args = reorderTestArgs(args);
|
|
386
|
+
|
|
387
|
+
return new Promise((resolve) => {
|
|
388
|
+
const child = spawn(executable, args, { stdio: 'inherit' });
|
|
233
389
|
|
|
234
390
|
child.on('error', (err) => {
|
|
235
391
|
console.error(`plccheck: failed to start binary: ${binaryPath}`);
|
|
236
392
|
console.error(err && err.stack ? err.stack : String(err));
|
|
237
|
-
|
|
393
|
+
resolve(1);
|
|
238
394
|
});
|
|
239
395
|
|
|
240
396
|
child.on('exit', (code, signal) => {
|
|
241
|
-
if (signal)
|
|
242
|
-
|
|
397
|
+
if (signal) {
|
|
398
|
+
process.kill(process.pid, signal);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
resolve(code == null ? 1 : code);
|
|
243
402
|
});
|
|
244
|
-
})
|
|
245
|
-
.catch((err) => {
|
|
246
|
-
console.error(`plccheck: failed to resolve platform binary package: ${pkg}`);
|
|
247
|
-
console.error(
|
|
248
|
-
'plccheck: install failed to include optionalDependencies; falling back to direct download from npm registry.',
|
|
249
|
-
);
|
|
250
|
-
if (err && err.stack) console.error(err.stack);
|
|
251
|
-
process.exit(1);
|
|
252
403
|
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
module.exports = {
|
|
407
|
+
isRepoDevelopmentMode,
|
|
408
|
+
reorderTestArgs,
|
|
409
|
+
resolveCoverageOutputPaths,
|
|
410
|
+
resolveGoRunPaths,
|
|
411
|
+
resolveTestPositionals,
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
if (require.main === module) {
|
|
415
|
+
main()
|
|
416
|
+
.then((code) => {
|
|
417
|
+
process.exit(code);
|
|
418
|
+
})
|
|
419
|
+
.catch((err) => {
|
|
420
|
+
console.error(`plccheck: failed to resolve platform binary package: ${pkg}`);
|
|
421
|
+
console.error(
|
|
422
|
+
'plccheck: install failed to include optionalDependencies; falling back to direct download from npm registry.',
|
|
423
|
+
);
|
|
424
|
+
if (err && err.stack) console.error(err.stack);
|
|
425
|
+
process.exit(1);
|
|
426
|
+
});
|
|
427
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plccheck",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Siemens PLC language checker + LSP CLI (SCL/ST/LAD/FBD, etc.)",
|
|
5
5
|
"license": "CC-BY-NC-4.0",
|
|
6
6
|
"bin": {
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
"version": "node ../scripts/plccheck-npm-version-hook.mjs"
|
|
15
15
|
},
|
|
16
16
|
"optionalDependencies": {
|
|
17
|
-
"@danielv123/plccheck-win-x64": "2.
|
|
18
|
-
"@danielv123/plccheck-win-arm64": "2.
|
|
19
|
-
"@danielv123/plccheck-linux-x64": "2.
|
|
20
|
-
"@danielv123/plccheck-linux-arm64": "2.
|
|
21
|
-
"@danielv123/plccheck-darwin-x64": "2.
|
|
22
|
-
"@danielv123/plccheck-darwin-arm64": "2.
|
|
17
|
+
"@danielv123/plccheck-win-x64": "2.5.0",
|
|
18
|
+
"@danielv123/plccheck-win-arm64": "2.5.0",
|
|
19
|
+
"@danielv123/plccheck-linux-x64": "2.5.0",
|
|
20
|
+
"@danielv123/plccheck-linux-arm64": "2.5.0",
|
|
21
|
+
"@danielv123/plccheck-darwin-x64": "2.5.0",
|
|
22
|
+
"@danielv123/plccheck-darwin-arm64": "2.5.0"
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|