Skip to content

Commit d147dc7

Browse files
committed
cli: Add command line interface to combine/split uhex files.
1 parent 0898e2f commit d147dc7

File tree

6 files changed

+216
-7
lines changed

6 files changed

+216
-7
lines changed

config/rollup.config.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { resolve } from 'path';
2+
import builtins from 'builtin-modules';
23
import sourceMaps from 'rollup-plugin-sourcemaps';
34
import nodeResolve from '@rollup/plugin-node-resolve';
45
import commonjs from '@rollup/plugin-commonjs';
@@ -28,7 +29,7 @@ const plugins = /** @type {Plugin[]} */ ([
2829
commonjs(),
2930
// Allow node_modules resolution. Use 'external' to control
3031
// which external modules to include in the bundle
31-
// https://github.com/rollup/rollup-plugin-node-resolve#usage
32+
// https://github.com/rollup/plugins/blob/master/packages/node-resolve/
3233
nodeResolve(),
3334
sourceMaps(),
3435
babel({
@@ -99,4 +100,18 @@ const umdConfigMin = createUmdConfig({
99100
],
100101
});
101102

102-
export default [umdConfig, umdConfigMin];
103+
const cjsConfigCli = {
104+
inlineDynamicImports: true,
105+
external: [...external, ...Object.keys(pkg.dependencies), ...builtins],
106+
input: resolve(dist, 'esm5', 'cli.js'),
107+
output: {
108+
banner: '#!/usr/bin/env node',
109+
file: pkg.bin.uhex,
110+
format: 'cjs',
111+
name: pkg.config.umdName,
112+
sourcemap: true,
113+
},
114+
plugins: [commonjs(), nodeResolve(), sourceMaps()],
115+
};
116+
117+
export default [umdConfig, umdConfigMin, cjsConfigCli];

package-lock.json

Lines changed: 25 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"universal hex",
1010
"uh"
1111
],
12+
"bin": {
13+
"uhex": "./dist/bundles/microbit-uh-cli.cjs.js"
14+
},
1215
"main": "./dist/bundles/microbit-uh.umd.js",
1316
"mainMin": "./dist/bundles/microbit-uh.umd.min.js",
1417
"module": "./dist/esm5/index.js",
@@ -46,6 +49,9 @@
4649
"lint:fix": "npm run lint -- --fix",
4750
"docs": "typedoc --options config/typedoc.json"
4851
},
52+
"dependencies": {
53+
"commander": "^12.0.0"
54+
},
4955
"devDependencies": {
5056
"@babel/core": "^7.23.9",
5157
"@babel/polyfill": "^7.12.1",
@@ -72,4 +78,4 @@
7278
"typedoc-neo-theme": "^1.1.1",
7379
"typescript": "^4.9.5"
7480
}
75-
}
81+
}

src/cli.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import * as fs from 'fs';
2+
import { sep } from 'path';
3+
import * as process from 'process';
4+
import { Command } from 'commander';
5+
import * as microbitUh from './universal-hex';
6+
7+
function combine(
8+
v1IntelHexPath: string,
9+
v2IntelHexPath: string,
10+
universalHexPath: string | undefined,
11+
overwrite: boolean | undefined
12+
) {
13+
console.log('Combining Intel Hex files into Universal Hex');
14+
console.log(`V1 Intel hex file: ${fs.realpathSync(v1IntelHexPath)}`);
15+
console.log(`V2 Intel hex file: ${fs.realpathSync(v2IntelHexPath)}`);
16+
17+
if (!universalHexPath) {
18+
// If the output path is not specified, save it in the current working directory
19+
universalHexPath = `${process.cwd()}${sep}universal.hex`;
20+
}
21+
if (!overwrite && fs.existsSync(universalHexPath)) {
22+
throw new Error(
23+
`Output file already exists: ${fs.realpathSync(universalHexPath)}\n` +
24+
'\tUse "--overwrite" flag to replace it.'
25+
);
26+
}
27+
28+
const v1IntelHexStr = fs.readFileSync(v1IntelHexPath, 'ascii');
29+
const v2IntelHexStr = fs.readFileSync(v2IntelHexPath, 'ascii');
30+
const universalHexStr = microbitUh.createUniversalHex([
31+
{
32+
hex: v1IntelHexStr,
33+
boardId: microbitUh.microbitBoardId.V1,
34+
},
35+
{
36+
hex: v2IntelHexStr,
37+
boardId: microbitUh.microbitBoardId.V2,
38+
},
39+
]);
40+
fs.writeFileSync(universalHexPath, universalHexStr, { encoding: 'ascii' });
41+
42+
console.log(`Universal Hex saved to: ${fs.realpathSync(universalHexPath)}`);
43+
}
44+
45+
function separate(
46+
universalHexPath: string,
47+
v1IntelHexPath: string | undefined,
48+
v2IntelHexPath: string | undefined,
49+
overwrite: boolean | undefined
50+
) {
51+
console.log(
52+
`Splitting Universal Hex file: ${fs.realpathSync(universalHexPath)}`
53+
);
54+
if (!v1IntelHexPath) {
55+
v1IntelHexPath = `${process.cwd()}${sep}v1-intel.hex`;
56+
}
57+
if (!v2IntelHexPath) {
58+
v2IntelHexPath = `${process.cwd()}${sep}v2-intel.hex`;
59+
}
60+
if (!overwrite && fs.existsSync(v1IntelHexPath)) {
61+
throw new Error(
62+
`Output V1 file already exists: ${fs.realpathSync(v1IntelHexPath)}\n` +
63+
'\tUse "--overwrite" flag to replace it.'
64+
);
65+
}
66+
if (!overwrite && fs.existsSync(v2IntelHexPath)) {
67+
throw new Error(
68+
`Output V2 file already exists: ${fs.realpathSync(v2IntelHexPath)}\n` +
69+
'\tUse "--overwrite" flag to replace it.'
70+
);
71+
}
72+
73+
const universalHexStr = fs.readFileSync(universalHexPath, 'ascii');
74+
const separatedHexes = microbitUh.separateUniversalHex(universalHexStr);
75+
if (separatedHexes.length !== 2) {
76+
const boardIds = separatedHexes.map((hexObj) => hexObj.boardId);
77+
const errorMsg =
78+
'Universal Hex should contain only two micro:bit Intel Hexes.\n' +
79+
`Found ${separatedHexes.length}: ${boardIds.join(', ')}`;
80+
throw new Error(errorMsg);
81+
}
82+
83+
let intelHexV1Str = '';
84+
let intelHexV2Str = '';
85+
separatedHexes.forEach((hexObj) => {
86+
if (microbitUh.V1_BOARD_IDS.includes(hexObj.boardId)) {
87+
intelHexV1Str = hexObj.hex;
88+
} else if (microbitUh.V2_BOARD_IDS.includes(hexObj.boardId)) {
89+
intelHexV2Str = hexObj.hex;
90+
}
91+
});
92+
if (!intelHexV1Str || !intelHexV2Str) {
93+
const boardIds = separatedHexes.map((hexObj) => hexObj.boardId);
94+
const errorMsg =
95+
'Universal Hex does not contain both micro:bit Intel Hexes.\n' +
96+
`Found hexes for following board IDs: ${boardIds.join(', ')}`;
97+
throw new Error(errorMsg);
98+
}
99+
fs.writeFileSync(v1IntelHexPath, intelHexV1Str, { encoding: 'ascii' });
100+
fs.writeFileSync(v2IntelHexPath, intelHexV2Str, { encoding: 'ascii' });
101+
102+
console.log(`V1 Intel Hex saved to: ${fs.realpathSync(v1IntelHexPath)}`);
103+
console.log(`V2 Intel Hex saved to: ${fs.realpathSync(v2IntelHexPath)}`);
104+
}
105+
106+
function cli(args: string[]): number {
107+
const uHexCli = new Command();
108+
109+
uHexCli
110+
.command('combine')
111+
.requiredOption('-v1, --v1 <path>', 'Path to micro:bit V1 input Intel Hex')
112+
.requiredOption('-v2, --v2 <path>', 'Path to micro:bit V2 input Intel Hex')
113+
.option('-u, --universal <path>', 'Path to output Universal Hex')
114+
.option('-o, --overwrite', 'Overwrite output file if it exists', false)
115+
.action(
116+
(options: {
117+
v1: string;
118+
v2: string;
119+
universal?: string;
120+
overwrite: boolean;
121+
}) => {
122+
try {
123+
combine(options.v1, options.v2, options.universal, options.overwrite);
124+
} catch (e) {
125+
console.error('Error:', e.message);
126+
process.exit(1);
127+
}
128+
}
129+
);
130+
131+
uHexCli
132+
.command('split')
133+
.requiredOption('-u, --universal <path>', 'Path to input Universal Hex')
134+
.option('-v1, --v1 <path>', 'Path to micro:bit V1 output Intel Hex')
135+
.option('-v2, --v2 <path>', 'Path to micro:bit V2 output Intel Hex')
136+
.option('-o, --overwrite', 'Overwrite output files if they exist', false)
137+
.action(
138+
(options: {
139+
v1?: string;
140+
v2?: string;
141+
universal: string;
142+
overwrite: boolean;
143+
}) => {
144+
try {
145+
separate(
146+
options.universal,
147+
options.v1,
148+
options.v2,
149+
options.overwrite
150+
);
151+
} catch (e) {
152+
console.error('Error:', e.message);
153+
process.exit(1);
154+
}
155+
}
156+
);
157+
158+
uHexCli.parse(args);
159+
160+
return 0;
161+
}
162+
163+
process.exit(cli(process.argv));

src/universal-hex.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import * as ihex from './ihex';
2020

2121
const V1_BOARD_IDS = [0x9900, 0x9901];
22+
const V2_BOARD_IDS = [0x9903, 0x9904, 0x9905, 0x9906];
2223
const BLOCK_SIZE = 512;
2324

2425
/**
@@ -490,6 +491,8 @@ function separateUniversalHex(universalHexStr: string): IndividualHex[] {
490491
}
491492

492493
export {
494+
V1_BOARD_IDS,
495+
V2_BOARD_IDS,
493496
microbitBoardId,
494497
IndividualHex,
495498
iHexToCustomFormatBlocks,

tslint.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"object-literal-sort-keys": false,
1818
"interface-over-type-literal": false,
19+
"no-console": false,
1920
"no-bitwise": false
2021
},
2122
"jsRules": true

0 commit comments

Comments
 (0)