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.
Files changed (3) hide show
  1. package/README.md +29 -0
  2. package/bin/plccheck.js +191 -16
  3. 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
- let binaryPath;
229
- resolveBinaryPath()
230
- .then((p) => {
231
- binaryPath = p;
232
- const child = spawn(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
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
- process.exit(1);
393
+ resolve(1);
238
394
  });
239
395
 
240
396
  child.on('exit', (code, signal) => {
241
- if (signal) process.kill(process.pid, signal);
242
- process.exit(code == null ? 1 : code);
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.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.3.2",
18
- "@danielv123/plccheck-win-arm64": "2.3.2",
19
- "@danielv123/plccheck-linux-x64": "2.3.2",
20
- "@danielv123/plccheck-linux-arm64": "2.3.2",
21
- "@danielv123/plccheck-darwin-x64": "2.3.2",
22
- "@danielv123/plccheck-darwin-arm64": "2.3.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",