Skip to content

Commit f8111af

Browse files
authored
fix(build): set executable permissions on CLI bin files (#359)
TypeScript's tsc compiler does not preserve executable permissions on output files, requiring explicit chmod in build scripts. Changes: - Add chmod +x to build scripts for CLI packages (cli, device-profiler, mqtt-bridge) - Set executable permissions on both ESM and CJS bin files - Simplify mqtt-bridge prepublishOnly script (no longer needs separate chmod) - Document CLI package requirements and shebang requirement in package-creation.md - Add comprehensive CLI package section to NEW-PACKAGE.md TypeScript does not support preserving executable permissions (see TypeScript issues #37583, #16257, #26060). The chmod approach in build scripts is the industry-standard solution. Related: #360, #361
1 parent 408e250 commit f8111af

5 files changed

Lines changed: 51 additions & 5 deletions

File tree

docs/NEW-PACKAGE.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,52 @@ This pattern ensures the factory defaults are always valid according to the devi
318318

319319
See `packages/driver-xymd1` for reference implementation.
320320

321+
### CLI Package
322+
323+
CLI packages expose executable commands via the npm `bin` field.
324+
325+
**Required configuration**:
326+
327+
```json
328+
{
329+
"bin": {
330+
"command-name": "./dist/esm/bin/command.js"
331+
}
332+
}
333+
```
334+
335+
**Required structure**:
336+
337+
- `bin/` directory with executable TypeScript source files
338+
- Each bin file MUST start with shebang: `#!/usr/bin/env node`
339+
- TypeScript config MUST use `"rootDir": "."` to include both `src` and `bin`
340+
- Build script MUST set executable permissions after compilation
341+
342+
**Example build script**:
343+
344+
```json
345+
{
346+
"scripts": {
347+
"build": "npm run build:esm && npm run build:cjs && npm run build:package-json && chmod +x dist/esm/bin/*.js dist/cjs/bin/*.js"
348+
}
349+
}
350+
```
351+
352+
**Why chmod is required**: TypeScript's `tsc` compiler does not preserve executable permissions on output files. The build script must explicitly set permissions after compilation.
353+
354+
**Reference implementations**:
355+
356+
- `packages/cli` - Main CLI tool
357+
- `packages/device-profiler` - Profiling CLI
358+
- `packages/mqtt-bridge` - Bridge CLI
359+
360+
See `docs/agents/package-creation.md` for technical details on CLI package structure.
361+
321362
### Utility Package
322363

323364
Utility packages should use scoped naming: `@ya-modbus/<name>`
324365

325-
Examples: `@ya-modbus/driver-types`, `@ya-modbus/cli`
366+
Examples: `@ya-modbus/driver-types`, `@ya-modbus/core`
326367

327368
## Getting Help
328369

docs/agents/package-creation.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,15 @@ ALL packages MUST use consistent tsconfig structure:
4848
- `types`: `"./dist/esm/src/index.d.ts"`
4949
- `exports["."]` points to `dist/{esm,cjs}/src/index.js`
5050
- `bin` (if present): `"./dist/esm/bin/<name>.js"`
51+
- Bin files MUST include shebang (`#!/usr/bin/env node` as first line)
52+
- Build script MUST set executable permissions on bin files (ESM and CJS)
5153

5254
CRITICAL: Using `rootDir: "."` for all packages ensures consistent output structure and prevents configuration errors when adding bin executables.
5355

56+
CRITICAL: TypeScript compilation does not preserve executable permissions. Add `chmod +x` to build script for all bin files.
57+
5458
See: packages/cli for reference package with bin executable
59+
See: packages/cli/package.json (build script) for chmod pattern
5560
See: packages/driver-xymd1 for reference library package
5661

5762
### CommonJS Module Interop

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"license": "GPL-3.0-or-later",
4848
"author": "Geno Roupsky <185580+groupsky@users.noreply.github.com>",
4949
"scripts": {
50-
"build": "npm run build:esm && npm run build:cjs && npm run build:package-json",
50+
"build": "npm run build:esm && npm run build:cjs && npm run build:package-json && chmod +x dist/esm/bin/ya-modbus.js dist/cjs/bin/ya-modbus.js",
5151
"build:esm": "../../node_modules/.bin/tsc -p tsconfig.esm.json",
5252
"build:cjs": "../../node_modules/.bin/tsc -p tsconfig.cjs.json",
5353
"build:package-json": "echo '{\"type\":\"module\"}' > dist/esm/package.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",

packages/device-profiler/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
},
3535
"author": "Geno Roupsky <185580+groupsky@users.noreply.github.com>",
3636
"scripts": {
37-
"build": "npm run build:esm && npm run build:cjs && npm run build:package-json",
37+
"build": "npm run build:esm && npm run build:cjs && npm run build:package-json && chmod +x dist/esm/bin/profile.js dist/cjs/bin/profile.js",
3838
"clean": "rm -rf dist",
3939
"lint": "eslint .",
4040
"test": "jest",

packages/mqtt-bridge/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737
"access": "public"
3838
},
3939
"scripts": {
40-
"build": "npm run build:esm && npm run build:cjs && npm run build:package-json",
40+
"build": "npm run build:esm && npm run build:cjs && npm run build:package-json && chmod +x dist/esm/bin/ya-modbus-bridge.js dist/cjs/bin/ya-modbus-bridge.js",
4141
"clean": "rm -rf dist",
4242
"test": "jest",
4343
"lint": "eslint .",
44-
"prepublishOnly": "npm run build && chmod +x dist/esm/bin/ya-modbus-bridge.js",
44+
"prepublishOnly": "npm run build",
4545
"build:esm": "../../node_modules/.bin/tsc -p tsconfig.esm.json",
4646
"build:cjs": "../../node_modules/.bin/tsc -p tsconfig.cjs.json",
4747
"build:package-json": "echo '{\"type\":\"module\"}' > dist/esm/package.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",

0 commit comments

Comments
 (0)