@winccoa-tools-pack/npm-winccoa-ctrl-code-style 0.1.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +122 -0
  3. package/dist/cjs/cli.js +204 -0
  4. package/dist/cjs/cli.js.map +1 -0
  5. package/dist/cjs/index.js +29 -0
  6. package/dist/cjs/index.js.map +1 -0
  7. package/dist/cjs/paths.js +50 -0
  8. package/dist/cjs/paths.js.map +1 -0
  9. package/dist/cjs/register.js +154 -0
  10. package/dist/cjs/register.js.map +1 -0
  11. package/dist/cjs/style-check.js +110 -0
  12. package/dist/cjs/style-check.js.map +1 -0
  13. package/dist/cjs/types.js +3 -0
  14. package/dist/cjs/types.js.map +1 -0
  15. package/dist/cli.d.ts +15 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/esm/cli.js +204 -0
  18. package/dist/esm/cli.js.map +1 -0
  19. package/dist/esm/index.js +29 -0
  20. package/dist/esm/index.js.map +1 -0
  21. package/dist/esm/paths.js +50 -0
  22. package/dist/esm/paths.js.map +1 -0
  23. package/dist/esm/register.js +154 -0
  24. package/dist/esm/register.js.map +1 -0
  25. package/dist/esm/style-check.js +110 -0
  26. package/dist/esm/style-check.js.map +1 -0
  27. package/dist/esm/types.js +3 -0
  28. package/dist/esm/types.js.map +1 -0
  29. package/dist/index.d.ts +13 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/paths.d.ts +16 -0
  32. package/dist/paths.d.ts.map +1 -0
  33. package/dist/register.d.ts +49 -0
  34. package/dist/register.d.ts.map +1 -0
  35. package/dist/style-check.d.ts +31 -0
  36. package/dist/style-check.d.ts.map +1 -0
  37. package/dist/types.d.ts +71 -0
  38. package/dist/types.d.ts.map +1 -0
  39. package/package.json +68 -0
  40. package/scripts/approve-open-pr-workflows.sh +85 -0
  41. package/scripts/check-changelog-version.mjs +37 -0
  42. package/scripts/fix-esm-imports.mjs +115 -0
  43. package/scripts/generate-changelog.mjs +190 -0
  44. package/scripts/register-stylecheck-projects.ps1 +92 -0
  45. package/scripts/register-stylecheck-projects.sh +193 -0
  46. package/scripts/run-node-tests.ts +74 -0
  47. package/scripts/wait-for-winccoa.sh +26 -0
  48. package/winccoa/StyleCheck/scripts/astyle.ctl +142 -0
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ASTYLE_SCRIPT = void 0;
7
+ exports.buildCtrlArgs = buildCtrlArgs;
8
+ exports.runStyleCheck = runStyleCheck;
9
+ exports.checkStyle = checkStyle;
10
+ exports.formatStyle = formatStyle;
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const CtrlComponent_1 = require("@winccoa-tools-pack/npm-winccoa-core/types/components/implementations/CtrlComponent");
14
+ const paths_1 = require("./paths");
15
+ const register_1 = require("./register");
16
+ const DEFAULT_TIMEOUT = 120000;
17
+ /** Script name relative to StyleCheck scripts/ (OA proj_path resolution). */
18
+ exports.ASTYLE_SCRIPT = 'astyle.ctl';
19
+ /**
20
+ * Build WCCOActrl argv for astyle.ctl against the worker project config.
21
+ *
22
+ * WCCOActrl -config <worker>/config/config -n -log +stderr astyle.ctl <source> [TRUE]
23
+ *
24
+ * Script is always the bare name `astyle.ctl` (resolved via StyleCheck on
25
+ * proj_path). Worker project is runnable; logs stay on the worker.
26
+ */
27
+ function buildCtrlArgs(options) {
28
+ const args = [
29
+ '-config',
30
+ options.configPath,
31
+ '-n',
32
+ '-log',
33
+ '+stderr',
34
+ options.scriptName ?? exports.ASTYLE_SCRIPT,
35
+ options.sourcePath,
36
+ ];
37
+ if (options.applyChanges) {
38
+ args.push('TRUE');
39
+ }
40
+ return args;
41
+ }
42
+ /**
43
+ * Run CTL style check or format via worker project + StyleCheck sub-project.
44
+ */
45
+ async function runStyleCheck(options) {
46
+ const version = (0, register_1.resolveWinCCOAVersion)(options.version);
47
+ const projectPath = node_path_1.default.resolve(options.projectPath);
48
+ const sourcePath = node_path_1.default.resolve(options.sourcePath ?? projectPath);
49
+ const applyChanges = options.applyChanges === true;
50
+ const styleCheckPath = node_path_1.default.resolve(options.styleCheckProjectPath ?? (0, paths_1.getDefaultStyleCheckProjectPath)());
51
+ // Existence check only — CTRL receives bare script name, not this path.
52
+ const bundledScriptPath = (0, paths_1.getAstyleScriptPath)(styleCheckPath);
53
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT;
54
+ const registerProject = options.registerProject !== false;
55
+ if (!node_fs_1.default.existsSync(projectPath)) {
56
+ throw new Error(`projectPath does not exist: ${projectPath}`);
57
+ }
58
+ if (!node_fs_1.default.existsSync(sourcePath)) {
59
+ throw new Error(`sourcePath does not exist: ${sourcePath}`);
60
+ }
61
+ if (!node_fs_1.default.existsSync(bundledScriptPath)) {
62
+ throw new Error(`astyle.ctl not found: ${bundledScriptPath}`);
63
+ }
64
+ let configPath = node_path_1.default.join(projectPath, 'config', 'config');
65
+ if (registerProject) {
66
+ const registered = await (0, register_1.registerWorkerProjectWithStyleCheck)({
67
+ projectPath,
68
+ version,
69
+ langs: options.langs,
70
+ styleCheckProjectPath: styleCheckPath,
71
+ forceRewriteConfig: true,
72
+ });
73
+ configPath = registered.configPath;
74
+ }
75
+ if (!node_fs_1.default.existsSync(configPath)) {
76
+ throw new Error(`Worker project config not found: ${configPath}. Register first ` +
77
+ `(registerProject or scripts/register-stylecheck-projects.sh).`);
78
+ }
79
+ const ctrl = new CtrlComponent_1.CtrlComponent();
80
+ ctrl.setVersion(version);
81
+ const args = buildCtrlArgs({
82
+ configPath,
83
+ sourcePath,
84
+ applyChanges,
85
+ });
86
+ const exitCode = await ctrl.start(args, { timeout });
87
+ return {
88
+ success: exitCode === 0,
89
+ exitCode,
90
+ stdout: ctrl.stdOut ?? '',
91
+ stderr: ctrl.stdErr ?? '',
92
+ projectPath,
93
+ sourcePath,
94
+ configPath,
95
+ applyChanges,
96
+ };
97
+ }
98
+ /**
99
+ * Dry-run style check (no file changes).
100
+ */
101
+ async function checkStyle(options) {
102
+ return runStyleCheck({ ...options, applyChanges: false });
103
+ }
104
+ /**
105
+ * Format CTL sources in place.
106
+ */
107
+ async function formatStyle(options) {
108
+ return runStyleCheck({ ...options, applyChanges: true });
109
+ }
110
+ //# sourceMappingURL=style-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style-check.js","sourceRoot":"","sources":["../../src/style-check.ts"],"names":[],"mappings":";;;;;;AAoBA,sCAsBC;AAKD,sCAgEC;AAKD,gCAIC;AAKD,kCAIC;AAjID,sDAAyB;AACzB,0DAA6B;AAC7B,uHAAoH;AAEpH,mCAA+E;AAC/E,yCAAwF;AAExF,MAAM,eAAe,GAAG,MAAO,CAAC;AAEhC,6EAA6E;AAChE,QAAA,aAAa,GAAG,YAAY,CAAC;AAE1C;;;;;;;GAOG;AACH,SAAgB,aAAa,CAAC,OAM7B;IACG,MAAM,IAAI,GAAG;QACT,SAAS;QACT,OAAO,CAAC,UAAU;QAClB,IAAI;QACJ,MAAM;QACN,SAAS;QACT,OAAO,CAAC,UAAU,IAAI,qBAAa;QACnC,OAAO,CAAC,UAAU;KACrB,CAAC;IAEF,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,aAAa,CAAC,OAA0B;IAC1D,MAAM,OAAO,GAAG,IAAA,gCAAqB,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,WAAW,GAAG,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,UAAU,GAAG,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,IAAI,WAAW,CAAC,CAAC;IACnE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,KAAK,IAAI,CAAC;IACnD,MAAM,cAAc,GAAG,mBAAI,CAAC,OAAO,CAC/B,OAAO,CAAC,qBAAqB,IAAI,IAAA,uCAA+B,GAAE,CACrE,CAAC;IACF,wEAAwE;IACxE,MAAM,iBAAiB,GAAG,IAAA,2BAAmB,EAAC,cAAc,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC;IACnD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,KAAK,KAAK,CAAC;IAE1D,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,+BAA+B,WAAW,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,8BAA8B,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,yBAAyB,iBAAiB,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,UAAU,GAAG,mBAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE5D,IAAI,eAAe,EAAE,CAAC;QAClB,MAAM,UAAU,GAAG,MAAM,IAAA,8CAAmC,EAAC;YACzD,WAAW;YACX,OAAO;YACP,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,qBAAqB,EAAE,cAAc;YACrC,kBAAkB,EAAE,IAAI;SAC3B,CAAC,CAAC;QACH,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACX,oCAAoC,UAAU,mBAAmB;YAC7D,+DAA+D,CACtE,CAAC;IACN,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,6BAAa,EAAE,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAEzB,MAAM,IAAI,GAAG,aAAa,CAAC;QACvB,UAAU;QACV,UAAU;QACV,YAAY;KACf,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAErD,OAAO;QACH,OAAO,EAAE,QAAQ,KAAK,CAAC;QACvB,QAAQ;QACR,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;QACzB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;QACzB,WAAW;QACX,UAAU;QACV,UAAU;QACV,YAAY;KACf,CAAC;AACN,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,UAAU,CAC5B,OAAgD;IAEhD,OAAO,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,WAAW,CAC7B,OAAgD;IAEhD,OAAO,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @winccoa-tools-pack/npm-winccoa-ctrl-code-style
3
+ *
4
+ * Check and format WinCC OA CTL sources via WCCOActrl + astyle.ctl.
5
+ * Worker project is runnable; StyleCheck is a non-runnable sub-project.
6
+ */
7
+ export type { StyleCheckOptions, StyleCheckResult } from './types';
8
+ export type { RegisterProjectsOptions, RegisterProjectsResult } from './register';
9
+ export { runStyleCheck, checkStyle, formatStyle, buildCtrlArgs, ASTYLE_SCRIPT, } from './style-check';
10
+ export { getPackageRoot, getDefaultStyleCheckProjectPath, getAstyleScriptPath } from './paths';
11
+ export { registerStyleCheckSubProject, registerWorkerProjectWithStyleCheck, resolveWinCCOAVersion, spawnRegisterProjectCli, } from './register';
12
+ export { parseArgs, printUsage, main } from './cli';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACnE,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EACH,aAAa,EACb,UAAU,EACV,WAAW,EACX,aAAa,EACb,aAAa,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,+BAA+B,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC/F,OAAO,EACH,4BAA4B,EAC5B,mCAAmC,EACnC,qBAAqB,EACrB,uBAAuB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Resolve the package root that contains `winccoa/StyleCheck`.
3
+ * Works from source (`src/`) and built layouts (`dist/cjs`, `dist/esm`).
4
+ */
5
+ export declare function getPackageRoot(startDir?: string): string;
6
+ /**
7
+ * Absolute path to the bundled StyleCheck WinCC OA subproject.
8
+ */
9
+ export declare function getDefaultStyleCheckProjectPath(): string;
10
+ /**
11
+ * Absolute path to astyle.ctl inside the StyleCheck project.
12
+ * Used to verify the bundled script exists. WCCOActrl must receive the bare
13
+ * name `astyle.ctl` (relative to scripts/), not this full path.
14
+ */
15
+ export declare function getAstyleScriptPath(projectPath?: string): string;
16
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAqBxD;AAED;;GAEG;AACH,wBAAgB,+BAA+B,IAAI,MAAM,CAExD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhE"}
@@ -0,0 +1,49 @@
1
+ export interface RegisterProjectsOptions {
2
+ /** Runnable worker / source project (e.g. Squirt). */
3
+ projectPath: string;
4
+ /** WinCC OA version, e.g. 3.21. */
5
+ version: string;
6
+ /** Languages for the runnable worker project. */
7
+ langs?: string[];
8
+ /** StyleCheck sub-project path (default: package winccoa/StyleCheck). */
9
+ styleCheckProjectPath?: string;
10
+ /**
11
+ * When true, always rewrite worker config so StyleCheck is listed as
12
+ * sub-project before the main proj_path (even if config already exists).
13
+ * @default true
14
+ */
15
+ forceRewriteConfig?: boolean;
16
+ }
17
+ export interface RegisterProjectsResult {
18
+ styleCheckPath: string;
19
+ projectPath: string;
20
+ configPath: string;
21
+ }
22
+ /**
23
+ * Resolve a WinCC OA version: explicit, or the single installed version.
24
+ */
25
+ export declare function resolveWinCCOAVersion(explicit?: string): string;
26
+ /**
27
+ * Register StyleCheck as a non-runnable WinCC OA project.
28
+ */
29
+ export declare function registerStyleCheckSubProject(styleCheckPath: string, version: string): Promise<string>;
30
+ /**
31
+ * Write / refresh worker project config with StyleCheck as sub-project, then
32
+ * register the worker as runnable.
33
+ *
34
+ * Config shape (matches npm-winccoa-register-project --sub-project):
35
+ * [general]
36
+ * pvss_path = "..."
37
+ * proj_path = "<StyleCheck>"
38
+ * proj_path = "<worker>"
39
+ * proj_version = "..."
40
+ * langs = "..."
41
+ */
42
+ export declare function registerWorkerProjectWithStyleCheck(options: RegisterProjectsOptions): Promise<RegisterProjectsResult>;
43
+ /**
44
+ * Spawn npm-winccoa-register-project CLI (for shells / debugging).
45
+ * Prefer registerWorkerProjectWithStyleCheck() in-process for reliability
46
+ * (register-project skips config rewrite when config already exists).
47
+ */
48
+ export declare function spawnRegisterProjectCli(args: string[]): void;
49
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,uBAAuB;IACpC,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,yEAAyE;IACzE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAiB/D;AAkBD;;GAEG;AACH,wBAAsB,4BAA4B,CAC9C,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mCAAmC,CACrD,OAAO,EAAE,uBAAuB,GACjC,OAAO,CAAC,sBAAsB,CAAC,CAgEjC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA8C5D"}
@@ -0,0 +1,31 @@
1
+ import type { StyleCheckOptions, StyleCheckResult } from './types';
2
+ /** Script name relative to StyleCheck scripts/ (OA proj_path resolution). */
3
+ export declare const ASTYLE_SCRIPT = "astyle.ctl";
4
+ /**
5
+ * Build WCCOActrl argv for astyle.ctl against the worker project config.
6
+ *
7
+ * WCCOActrl -config <worker>/config/config -n -log +stderr astyle.ctl <source> [TRUE]
8
+ *
9
+ * Script is always the bare name `astyle.ctl` (resolved via StyleCheck on
10
+ * proj_path). Worker project is runnable; logs stay on the worker.
11
+ */
12
+ export declare function buildCtrlArgs(options: {
13
+ configPath: string;
14
+ sourcePath: string;
15
+ applyChanges: boolean;
16
+ /** @default ASTYLE_SCRIPT (`astyle.ctl`) */
17
+ scriptName?: string;
18
+ }): string[];
19
+ /**
20
+ * Run CTL style check or format via worker project + StyleCheck sub-project.
21
+ */
22
+ export declare function runStyleCheck(options: StyleCheckOptions): Promise<StyleCheckResult>;
23
+ /**
24
+ * Dry-run style check (no file changes).
25
+ */
26
+ export declare function checkStyle(options: Omit<StyleCheckOptions, 'applyChanges'>): Promise<StyleCheckResult>;
27
+ /**
28
+ * Format CTL sources in place.
29
+ */
30
+ export declare function formatStyle(options: Omit<StyleCheckOptions, 'applyChanges'>): Promise<StyleCheckResult>;
31
+ //# sourceMappingURL=style-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style-check.d.ts","sourceRoot":"","sources":["../src/style-check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAMnE,6EAA6E;AAC7E,eAAO,MAAM,aAAa,eAAe,CAAC;AAE1C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,EAAE,CAgBX;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgEzF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC5B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,GACjD,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC7B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,GACjD,OAAO,CAAC,gBAAgB,CAAC,CAE3B"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Options for CTL style check / format via WCCOActrl + astyle.ctl.
3
+ *
4
+ * Worker project is the runnable source project (e.g. Squirt). StyleCheck is
5
+ * registered as a non-runnable sub-project so scripts resolve from there while
6
+ * logs and artifacts stay on the worker project.
7
+ */
8
+ export interface StyleCheckOptions {
9
+ /**
10
+ * Absolute or relative path to the runnable WinCC OA source project
11
+ * (the worker project, e.g. `src/Squirt`).
12
+ */
13
+ projectPath: string;
14
+ /**
15
+ * WinCC OA version (e.g. `3.21`).
16
+ */
17
+ version: string;
18
+ /**
19
+ * Directory to scan for `*.ctl` files.
20
+ * Defaults to `projectPath` when omitted.
21
+ */
22
+ sourcePath?: string;
23
+ /**
24
+ * When true, format files in place. When false (default), dry-run only.
25
+ * @default false
26
+ */
27
+ applyChanges?: boolean;
28
+ /**
29
+ * Languages for runnable project registration.
30
+ * @default ['en_US.utf8']
31
+ */
32
+ langs?: string[];
33
+ /**
34
+ * Path or registered id of the StyleCheck sub-project.
35
+ * Defaults to the `winccoa/StyleCheck` directory shipped with this package.
36
+ */
37
+ styleCheckProjectPath?: string;
38
+ /**
39
+ * When true, register StyleCheck (non-runnable) and the worker project
40
+ * (runnable, with StyleCheck as --sub-project) before running.
41
+ * @default true
42
+ */
43
+ registerProject?: boolean;
44
+ /**
45
+ * Timeout in milliseconds for the WCCOActrl process.
46
+ * @default 120000
47
+ */
48
+ timeout?: number;
49
+ }
50
+ /**
51
+ * Result of a style check / format run.
52
+ */
53
+ export interface StyleCheckResult {
54
+ /** True when exit code is 0. */
55
+ success: boolean;
56
+ /** WCCOActrl / astyle exit code. */
57
+ exitCode: number;
58
+ /** Captured stdout. */
59
+ stdout: string;
60
+ /** Captured stderr (includes WinCC OA throwError log lines). */
61
+ stderr: string;
62
+ /** Absolute worker project path. */
63
+ projectPath: string;
64
+ /** Absolute source path that was checked. */
65
+ sourcePath: string;
66
+ /** Absolute path to the worker project config file used with -config. */
67
+ configPath: string;
68
+ /** Whether formatting was applied. */
69
+ applyChanges: boolean;
70
+ }
71
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC9B;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,gCAAgC;IAChC,OAAO,EAAE,OAAO,CAAC;IAEjB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IAEjB,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IAEf,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IAEf,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAEpB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IAEnB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IAEnB,sCAAsC;IACtC,YAAY,EAAE,OAAO,CAAC;CACzB"}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@winccoa-tools-pack/npm-winccoa-ctrl-code-style",
3
+ "version": "0.1.0",
4
+ "description": "CLI and API to check/format WinCC OA CTL sources via WCCOActrl and astyle.ctl",
5
+ "author": "winccoa-tools-pack",
6
+ "license": "MIT",
7
+ "main": "dist/cjs/index.js",
8
+ "module": "dist/esm/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/esm/index.js",
13
+ "require": "./dist/cjs/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "bin": {
18
+ "winccoa-ctrl-style": "./dist/cjs/cli.js"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "winccoa",
23
+ "scripts"
24
+ ],
25
+ "scripts": {
26
+ "build:types": "tsc -p tsconfig.build.types.json",
27
+ "build:cjs": "tsc -p tsconfig.build.cjs.json",
28
+ "build:esm": "tsc -p tsconfig.build.esm.json && node scripts/fix-esm-imports.mjs",
29
+ "build": "npm run build:types && npm run build:cjs && npm run build:esm",
30
+ "compile": "tsc --build",
31
+ "prepublishOnly": "npm run build",
32
+ "lint": "eslint \"src/**/*.ts\"",
33
+ "lint:fix": "eslint \"src/**/*.ts\" --fix",
34
+ "lint:md": "markdownlint \"**/*.md\" --ignore node_modules",
35
+ "lint:md:fix": "markdownlint \"**/*.md\" --ignore node_modules --fix",
36
+ "format": "prettier --write \"src/**/*.ts\"",
37
+ "format:check": "prettier --check \"src/**/*.ts\"",
38
+ "style-check": "npm run lint && npm run format:check && npm run lint:md",
39
+ "style-fix": "npm run lint:fix && npm run format && npm run lint:md:fix",
40
+ "test": "npm run style-check && npm run build && npm run test:unit",
41
+ "test:unit": "node --import tsx scripts/run-node-tests.ts test/unit",
42
+ "test:integration": "node --import tsx scripts/run-node-tests.ts test/integration",
43
+ "ci:integration": "npm ci && npm run build && npm run test:integration",
44
+ "register:projects": "node dist/cjs/cli.js register"
45
+ },
46
+ "dependencies": {
47
+ "@winccoa-tools-pack/npm-winccoa-core": "^2.0.8",
48
+ "@winccoa-tools-pack/npm-winccoa-register-project": "^1.1.1"
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/js": "^9.39.5",
52
+ "@types/node": "^26.4.0",
53
+ "eslint": "^9.39.5",
54
+ "eslint-config-prettier": "^10.1.8",
55
+ "eslint-plugin-import": "^2.32.0",
56
+ "globals": "^17.12.0",
57
+ "markdownlint": "^0.41.1",
58
+ "markdownlint-cli": "^0.49.1",
59
+ "prettier": "^3.9.6",
60
+ "ts-node": "^10.9.1",
61
+ "tsx": "^4.23.13",
62
+ "typescript": "^5.9.3",
63
+ "typescript-eslint": "^8.68.0"
64
+ },
65
+ "engines": {
66
+ "node": ">=20"
67
+ }
68
+ }
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env bash
2
+ # Approve waiting workflow runs for open PRs using only the `gh` CLI.
3
+ # Usage: ./scripts/approve-open-pr-workflows.sh [owner/repo]
4
+ # Requires: gh CLI authenticated (or set GH_TOKEN). Does NOT require `jq`.
5
+
6
+ set -euo pipefail
7
+ REPO=${1:-}
8
+ if [ -z "$REPO" ]; then
9
+ REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)
10
+ if [ -z "$REPO" ]; then
11
+ echo "Repository not specified and could not detect from gh. Use: $0 owner/repo" >&2
12
+ exit 2
13
+ fi
14
+ fi
15
+
16
+ echo "Using repo: $REPO"
17
+
18
+ # Optional env flags
19
+ DEPENDABOT_ONLY=${DEPENDABOT_ONLY:-true}
20
+ # Comma-separated workflow names to include (empty = any)
21
+ WORKFLOWS=${WORKFLOWS:-}
22
+
23
+ TMPDIR=$(mktemp -d)
24
+ trap 'rm -rf "$TMPDIR"' EXIT
25
+
26
+ echo "Fetching open PR numbers..."
27
+ PR_NUMBERS=$(gh api repos/$REPO/pulls --jq '.[].number' 2>/dev/null || true)
28
+ if [ -z "$PR_NUMBERS" ]; then
29
+ echo "No open PRs found or 'gh' failed to list PRs." >&2
30
+ exit 0
31
+ fi
32
+
33
+ APPROVED=0
34
+
35
+ # Iterate PR numbers and fetch head shas per-PR using gh --jq (no external jq required)
36
+ while read -r PR_NUMBER; do
37
+ [ -z "$PR_NUMBER" ] && continue
38
+ PR_HEAD_SHA=$(gh api repos/$REPO/pulls/$PR_NUMBER --jq '.head.sha' 2>/dev/null || true)
39
+ if [ -z "$PR_HEAD_SHA" ]; then
40
+ echo "Could not get head SHA for PR #$PR_NUMBER, skipping." >&2
41
+ continue
42
+ fi
43
+
44
+ # Query workflow runs for this head_sha and only output matching runs as tab-separated lines: id\tname\tactor
45
+ runs_output=$(gh api "repos/$REPO/actions/runs?event=pull_request&per_page=100&head_sha=$PR_HEAD_SHA" --jq '.workflow_runs[] | select(.status=="waiting" or .status=="requested") | "\(.id)\t\(.name)\t\(.actor.login // "")"' 2>/dev/null || true)
46
+ if [ -z "$runs_output" ]; then
47
+ continue
48
+ fi
49
+
50
+ # Process each matching run line
51
+ echo "$runs_output" | while IFS=$'\t' read -r RUN_ID RUN_NAME ACTOR; do
52
+ if [ -z "$RUN_ID" ]; then
53
+ continue
54
+ fi
55
+
56
+ if [ "${DEPENDABOT_ONLY}" = "true" ]; then
57
+ ACTOR_LOWER=$(echo "$ACTOR" | tr '[:upper:]' '[:lower:]')
58
+ case "$ACTOR_LOWER" in
59
+ dependabot* ) ;;
60
+ * ) echo "SKIP run $RUN_ID for PR #$PR_NUMBER (actor $ACTOR)"; continue ;;
61
+ esac
62
+ fi
63
+
64
+ if [ -n "$WORKFLOWS" ]; then
65
+ IFS="," read -ra WF <<< "$WORKFLOWS"
66
+ keep=false
67
+ for w in "${WF[@]}"; do
68
+ if [ "$w" = "$RUN_NAME" ]; then keep=true; break; fi
69
+ done
70
+ if [ "$keep" = false ]; then
71
+ echo "SKIP run $RUN_ID for PR #$PR_NUMBER (workflow $RUN_NAME not in filter)"
72
+ continue
73
+ fi
74
+ fi
75
+
76
+ echo "Approving run $RUN_ID for PR #$PR_NUMBER (workflow: $RUN_NAME)"
77
+ gh api -X POST repos/$REPO/actions/runs/$RUN_ID/approve || echo "approve failed for $RUN_ID"
78
+ APPROVED=$((APPROVED+1))
79
+ done
80
+
81
+ done <<< "$PR_NUMBERS"
82
+
83
+ echo "Total approved runs: $APPROVED"
84
+
85
+ exit 0
@@ -0,0 +1,37 @@
1
+ /* eslint-env node */
2
+ /* global console, process */
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+
6
+ const repoRoot = path.resolve(process.cwd());
7
+ const packageJsonPath = path.join(repoRoot, 'package.json');
8
+ const changelogPath = path.join(repoRoot, 'CHANGELOG.md');
9
+
10
+ if (!fs.existsSync(packageJsonPath)) {
11
+ console.error(`::error::Missing package.json at ${packageJsonPath}`);
12
+ process.exit(1);
13
+ }
14
+
15
+ if (!fs.existsSync(changelogPath)) {
16
+ console.error(`::error::Missing CHANGELOG.md at ${changelogPath}`);
17
+ process.exit(1);
18
+ }
19
+
20
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
21
+ const version = String(pkg.version || '').trim();
22
+
23
+ if (!version) {
24
+ console.error('::error::package.json does not contain a valid "version" field');
25
+ process.exit(1);
26
+ }
27
+
28
+ const changelog = fs.readFileSync(changelogPath, 'utf8');
29
+ const expectedHeadingPrefix = `## [${version}] - `;
30
+
31
+ if (!changelog.includes(expectedHeadingPrefix)) {
32
+ console.error('::error::Release notes missing in CHANGELOG.md');
33
+ console.error(`Expected CHANGELOG.md to contain a heading like: ${expectedHeadingPrefix}YYYY-MM-DD`);
34
+ process.exit(1);
35
+ }
36
+
37
+ console.log(`✅ CHANGELOG.md contains release heading for v${version}`);
@@ -0,0 +1,115 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
6
+ const esmDir = path.join(projectRoot, 'dist', 'esm');
7
+
8
+ const EXTENSIONS = new Set(['.js', '.json', '.node']);
9
+
10
+ function withJsExtension(specifier) {
11
+ if (!specifier.startsWith('.')) return specifier;
12
+
13
+ // Split off query/hash if ever present.
14
+ const match = /^(.*?)([?#].*)?$/.exec(specifier);
15
+ const base = match?.[1] ?? specifier;
16
+ const suffix = match?.[2] ?? '';
17
+
18
+ if (base.endsWith('/')) return specifier;
19
+
20
+ const ext = path.posix.extname(base);
21
+ if (EXTENSIONS.has(ext)) return specifier;
22
+
23
+ return `${base}.js${suffix}`;
24
+ }
25
+
26
+ function rewriteFile(contents) {
27
+ let changed = false;
28
+
29
+ // Rewrite: export * from '...'
30
+ contents = contents.replace(
31
+ /(\bexport\s+\*\s+from\s+)(['"])([^'"]+)(\2)/g,
32
+ (full, prefix, quote, spec, suffixQuote) => {
33
+ const next = withJsExtension(spec);
34
+ if (next === spec) return full;
35
+ changed = true;
36
+ return `${prefix}${quote}${next}${suffixQuote}`;
37
+ },
38
+ );
39
+
40
+ // Rewrite: export { ... } from '...'
41
+ contents = contents.replace(
42
+ /(\bexport\s+\{[^}]*\}\s+from\s+)(['"])([^'"]+)(\2)/g,
43
+ (full, prefix, quote, spec, suffixQuote) => {
44
+ const next = withJsExtension(spec);
45
+ if (next === spec) return full;
46
+ changed = true;
47
+ return `${prefix}${quote}${next}${suffixQuote}`;
48
+ },
49
+ );
50
+
51
+ // Rewrite: import ... from '...'
52
+ contents = contents.replace(
53
+ /(\bimport\s+[^;\n]+\s+from\s+)(['"])([^'"]+)(\2)/g,
54
+ (full, prefix, quote, spec, suffixQuote) => {
55
+ const next = withJsExtension(spec);
56
+ if (next === spec) return full;
57
+ changed = true;
58
+ return `${prefix}${quote}${next}${suffixQuote}`;
59
+ },
60
+ );
61
+
62
+ // Rewrite: import '...'
63
+ contents = contents.replace(
64
+ /(\bimport\s+)(['"])([^'"]+)(\2)/g,
65
+ (full, prefix, quote, spec, suffixQuote) => {
66
+ const next = withJsExtension(spec);
67
+ if (next === spec) return full;
68
+ changed = true;
69
+ return `${prefix}${quote}${next}${suffixQuote}`;
70
+ },
71
+ );
72
+
73
+ return { contents, changed };
74
+ }
75
+
76
+ async function* walk(dir) {
77
+ const entries = await fs.readdir(dir, { withFileTypes: true });
78
+ for (const entry of entries) {
79
+ const fullPath = path.join(dir, entry.name);
80
+ if (entry.isDirectory()) {
81
+ yield* walk(fullPath);
82
+ } else {
83
+ yield fullPath;
84
+ }
85
+ }
86
+ }
87
+
88
+ async function main() {
89
+ try {
90
+ await fs.access(esmDir);
91
+ } catch {
92
+ console.error(`dist/esm not found: ${esmDir}`);
93
+ process.exit(1);
94
+ }
95
+
96
+ let processed = 0;
97
+ let rewritten = 0;
98
+
99
+ for await (const filePath of walk(esmDir)) {
100
+ if (!filePath.endsWith('.js')) continue;
101
+
102
+ const original = await fs.readFile(filePath, 'utf8');
103
+ const { contents, changed } = rewriteFile(original);
104
+
105
+ processed++;
106
+ if (changed) {
107
+ await fs.writeFile(filePath, contents, 'utf8');
108
+ rewritten++;
109
+ }
110
+ }
111
+
112
+ console.log(`fix-esm-imports: processed ${processed} file(s), rewrote ${rewritten} file(s)`);
113
+ }
114
+
115
+ await main();