@bobfrankston/brother-label 1.0.13 → 1.0.14

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/cli.ts CHANGED
@@ -6,18 +6,21 @@
6
6
 
7
7
  import * as fs from "fs";
8
8
  import * as path from "path";
9
- import { program } from "commander";
10
9
  import {
11
10
  print,
12
11
  render,
12
+ renderSegments,
13
+ printSegments,
13
14
  getConfig,
14
15
  setConfig,
15
16
  getConfigPath,
16
17
  listPrinters,
17
18
  TapeSize,
18
19
  PrintOptions,
20
+ Segment,
19
21
  } from "./api.js";
20
22
 
23
+ const VERSION = "1.0.13";
21
24
  const VALID_TAPES: TapeSize[] = [6, 9, 12, 18, 24];
22
25
 
23
26
  function parseTape(value: string): TapeSize {
@@ -85,53 +88,165 @@ function buildPrintOptions(input: string, opts: { tape?: string; printer?: strin
85
88
  return options;
86
89
  }
87
90
 
88
- program
89
- .name("brother-print")
90
- .description("Print labels on Brother P-touch printers")
91
- .version("1.0.0");
92
-
93
- // Default command - print
94
- program
95
- .argument("[input]", "Text to print, or path to file (auto-detects type)")
96
- .option("-t, --tape <size>", "Tape size: 6, 9, 12, 18, 24 (mm)")
97
- .option("-p, --printer <name>", "Printer name")
98
- .option("-o, --output <file>", "Save to file instead of printing (png, jpg, bmp)")
99
- .option("-a, --aspect <ratio>", "Aspect ratio width:height for HTML (e.g., 3.5:2)")
100
- .option("-H, --height <size>", "Text height: 12mm, .5in, or 50% (of tape height)")
101
- .option("--text", "Force input as literal text")
102
- .option("--html", "Force input as HTML file path")
103
- .option("--image", "Force input as image file path")
104
- .action(async (input: string | undefined, opts) => {
105
- if (!input) {
106
- program.help();
107
- return;
91
+ interface ParsedArgs {
92
+ command: string | null;
93
+ opts: Record<string, string | boolean>;
94
+ segments: Segment[];
95
+ input: string | null;
96
+ }
97
+
98
+ // Valued options: flag name(s) canonical key
99
+ const VALUED_OPTIONS: [string[], string][] = [
100
+ [["-tape"], "tape"],
101
+ [["-p", "-printer"], "printer"],
102
+ [["-o", "-output"], "output"],
103
+ [["-a", "-aspect"], "aspect"],
104
+ [["-H", "-height"], "height"],
105
+ [["-s", "-sp", "-space"], "space"],
106
+ ];
107
+
108
+ // Boolean flags: flag name(s) → canonical key
109
+ const BOOLEAN_FLAGS: [string[], string][] = [
110
+ [["-w", "-html"], "html"],
111
+ [["-i", "-image"], "image"],
112
+ [["-t"], "text"],
113
+ [["-help", "-h"], "help"],
114
+ [["-version", "-v"], "version"],
115
+ ];
116
+
117
+ function parseArgs(argv: string[]): ParsedArgs {
118
+ const args = argv.slice(2).map(a => a.startsWith("--") ? a.slice(1) : a);
119
+ const command: string | null = null;
120
+ const opts: Record<string, string | boolean> = {};
121
+ const segments: Segment[] = [];
122
+ let input: string | null = null;
123
+ let i = 0;
124
+
125
+ while (i < args.length) {
126
+ const arg = args[i];
127
+
128
+ // Segment flags: -text <value>, -qr <value>
129
+ if ((arg === "-text" || arg === "-qr") && i + 1 < args.length) {
130
+ segments.push({ type: arg === "-text" ? "text" : "qr", value: args[i + 1] });
131
+ i += 2;
132
+ continue;
108
133
  }
109
134
 
110
- try {
111
- const options = buildPrintOptions(input, opts);
135
+ // Valued options
136
+ let matched = false;
137
+ for (const [names, key] of VALUED_OPTIONS) {
138
+ if (names.includes(arg) && i + 1 < args.length) {
139
+ opts[key] = args[i + 1];
140
+ i += 2;
141
+ matched = true;
142
+ break;
143
+ }
144
+ }
145
+ if (matched) continue;
112
146
 
113
- if (opts.output) {
114
- // Render only, save to file
115
- const buffer = await render(options);
116
- fs.writeFileSync(opts.output, buffer);
117
- console.log(`Saved to ${opts.output}`);
118
- } else {
119
- // Print
120
- const result = await print(options);
121
- const tape = options.tape ?? getConfig().defaultTape ?? 24;
122
- console.log(`Printed on ${tape}mm tape`);
147
+ // Boolean flags
148
+ for (const [names, key] of BOOLEAN_FLAGS) {
149
+ if (names.includes(arg)) {
150
+ opts[key] = true;
151
+ i++;
152
+ matched = true;
153
+ break;
123
154
  }
124
- } catch (err) {
125
- console.error(`Error: ${(err as Error).message}`);
126
- process.exit(1);
127
155
  }
128
- });
156
+ if (matched) continue;
157
+
158
+ // Subcommands
159
+ if ((arg === "list" || arg === "config") && command === null && input === null) {
160
+ return { ...parseRest(args, i), command: arg };
161
+ }
162
+
163
+ // Bare string → positional input (first one wins)
164
+ if (input === null) {
165
+ input = arg;
166
+ }
167
+ i++;
168
+ }
169
+
170
+ return { command, opts, segments, input };
171
+ }
172
+
173
+ // After recognizing a subcommand, parse the rest for that subcommand's options
174
+ function parseRest(args: string[], startIndex: number): ParsedArgs {
175
+ const opts: Record<string, string | boolean> = {};
176
+ const segments: Segment[] = [];
177
+ let input: string | null = null;
178
+ let i = startIndex + 1;
179
+
180
+ while (i < args.length) {
181
+ const arg = args[i];
182
+ let matched = false;
183
+ for (const [names, key] of VALUED_OPTIONS) {
184
+ if (names.includes(arg) && i + 1 < args.length) {
185
+ opts[key] = args[i + 1];
186
+ i += 2;
187
+ matched = true;
188
+ break;
189
+ }
190
+ }
191
+ if (matched) continue;
192
+
193
+ for (const [names, key] of BOOLEAN_FLAGS) {
194
+ if (names.includes(arg)) {
195
+ opts[key] = true;
196
+ i++;
197
+ matched = true;
198
+ break;
199
+ }
200
+ }
201
+ if (matched) continue;
202
+
203
+ if (input === null) input = arg;
204
+ i++;
205
+ }
206
+
207
+ return { command: null, opts, segments, input };
208
+ }
209
+
210
+ function showHelp(): void {
211
+ console.log(`Brother Label Printer CLI v${VERSION}
212
+
213
+ Usage:
214
+ brother-print [options] <input> Print text, file, or image
215
+ brother-print -text <v> -qr <v> ... Print ordered segments
216
+ brother-print list List available printers
217
+ brother-print config Show/set configuration
218
+
219
+ Options:
220
+ -tape <size> Tape size: 6, 9, 12, 18, 24 (mm)
221
+ -p, -printer <n> Printer name
222
+ -o, -output <file> Save to file instead of printing (png, jpg, bmp)
223
+ -a, -aspect <r> Aspect ratio width:height for HTML (e.g., 3.5:2)
224
+ -H, -height <size> Text height: 12mm, .5in, or 50% (of tape height)
225
+ -s, -space <size> Space between segments: 12px, 1mm, .2in
226
+ -t, -text Force input as literal text (or -text <v> for segments)
227
+ -qr <data> QR code segment
228
+ -w, -html Force input as HTML file path
229
+ -i, -image Force input as image file path
230
+ -help Show this help
231
+ -version Show version`);
232
+ }
233
+
234
+ // --- Main ---
129
235
 
130
- // List printers
131
- program
132
- .command("list")
133
- .description("List available Brother printers")
134
- .action(async () => {
236
+ async function main(): Promise<void> {
237
+ const parsed = parseArgs(process.argv);
238
+
239
+ if (parsed.opts.help) {
240
+ showHelp();
241
+ return;
242
+ }
243
+ if (parsed.opts.version) {
244
+ console.log(VERSION);
245
+ return;
246
+ }
247
+
248
+ // Subcommands
249
+ if (parsed.command === "list") {
135
250
  try {
136
251
  const printers = await listPrinters();
137
252
  if (printers.length === 0) {
@@ -144,18 +259,12 @@ program
144
259
  console.error(`Error: ${(err as Error).message}`);
145
260
  process.exit(1);
146
261
  }
147
- });
148
-
149
- // Config
150
- program
151
- .command("config")
152
- .description("Show or set configuration")
153
- .option("-t, --tape <size>", "Set default tape size: 6, 9, 12, 18, 24 (mm)")
154
- .option("-p, --printer <name>", "Set default printer name")
155
- .action((opts) => {
262
+ return;
263
+ }
264
+
265
+ if (parsed.command === "config") {
156
266
  try {
157
- if (!opts.tape && !opts.printer) {
158
- // Show config
267
+ if (!parsed.opts.tape && !parsed.opts.printer) {
159
268
  const config = getConfig();
160
269
  console.log("Configuration:");
161
270
  console.log(` File: ${getConfigPath()}`);
@@ -164,21 +273,72 @@ program
164
273
  console.log("\nValid tape sizes: 6, 9, 12, 18, 24");
165
274
  return;
166
275
  }
167
-
168
- if (opts.tape) {
169
- const tape = parseTape(opts.tape);
276
+ if (parsed.opts.tape) {
277
+ const tape = parseTape(parsed.opts.tape as string);
170
278
  setConfig({ defaultTape: tape });
171
279
  console.log(`Default tape set to: ${tape}mm`);
172
280
  }
281
+ if (parsed.opts.printer) {
282
+ setConfig({ defaultPrinter: parsed.opts.printer as string });
283
+ console.log(`Default printer set to: ${parsed.opts.printer}`);
284
+ }
285
+ } catch (err) {
286
+ console.error(`Error: ${(err as Error).message}`);
287
+ process.exit(1);
288
+ }
289
+ return;
290
+ }
173
291
 
174
- if (opts.printer) {
175
- setConfig({ defaultPrinter: opts.printer });
176
- console.log(`Default printer set to: ${opts.printer}`);
292
+ // Multi-segment mode
293
+ if (parsed.segments.length > 0) {
294
+ try {
295
+ const tape = parsed.opts.tape ? parseTape(parsed.opts.tape as string) : undefined;
296
+ if (parsed.opts.output) {
297
+ const buffer = await renderSegments(parsed.segments, tape, parsed.opts.height as string | undefined, parsed.opts.space as string | undefined);
298
+ fs.writeFileSync(parsed.opts.output as string, buffer);
299
+ console.log(`Saved to ${parsed.opts.output}`);
300
+ } else {
301
+ await printSegments(parsed.segments, { tape, printer: parsed.opts.printer as string | undefined, textHeight: parsed.opts.height as string | undefined, space: parsed.opts.space as string | undefined });
302
+ const effectiveTape = tape ?? getConfig().defaultTape ?? 24;
303
+ console.log(`Printed ${parsed.segments.length} segments on ${effectiveTape}mm tape`);
177
304
  }
178
305
  } catch (err) {
179
306
  console.error(`Error: ${(err as Error).message}`);
180
307
  process.exit(1);
181
308
  }
182
- });
309
+ return;
310
+ }
311
+
312
+ // Single input mode
313
+ if (!parsed.input) {
314
+ showHelp();
315
+ return;
316
+ }
317
+
318
+ try {
319
+ const options = buildPrintOptions(parsed.input, {
320
+ tape: parsed.opts.tape as string | undefined,
321
+ printer: parsed.opts.printer as string | undefined,
322
+ text: parsed.opts.text as boolean | undefined,
323
+ html: parsed.opts.html as boolean | undefined,
324
+ image: parsed.opts.image as boolean | undefined,
325
+ aspect: parsed.opts.aspect as string | undefined,
326
+ height: parsed.opts.height as string | undefined,
327
+ });
328
+
329
+ if (parsed.opts.output) {
330
+ const buffer = await render(options);
331
+ fs.writeFileSync(parsed.opts.output as string, buffer);
332
+ console.log(`Saved to ${parsed.opts.output}`);
333
+ } else {
334
+ await print(options);
335
+ const tape = options.tape ?? getConfig().defaultTape ?? 24;
336
+ console.log(`Printed on ${tape}mm tape`);
337
+ }
338
+ } catch (err) {
339
+ console.error(`Error: ${(err as Error).message}`);
340
+ process.exit(1);
341
+ }
342
+ }
183
343
 
184
- program.parse();
344
+ main();
package/index.d.ts CHANGED
@@ -2,5 +2,5 @@
2
2
  * Brother Label Printer API
3
3
  * @module @bobfrankston/brother-label
4
4
  */
5
- export { TapeSize, Orientation, PrinterConfig, PrinterInfo, PrintOptions, PrintResult, print, render, getConfig, setConfig, getConfigPath, listPrinters, } from "./api.js";
5
+ export { TapeSize, Orientation, PrinterConfig, PrinterInfo, PrintOptions, PrintResult, Segment, print, render, renderSegments, printSegments, getConfig, setConfig, getConfigPath, listPrinters, } from "./api.js";
6
6
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEH,QAAQ,EACR,WAAW,EACX,aAAa,EACb,WAAW,EACX,YAAY,EACZ,WAAW,EAEX,KAAK,EACL,MAAM,EACN,SAAS,EACT,SAAS,EACT,aAAa,EACb,YAAY,GACf,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEH,QAAQ,EACR,WAAW,EACX,aAAa,EACb,WAAW,EACX,YAAY,EACZ,WAAW,EACX,OAAO,EAEP,KAAK,EACL,MAAM,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,aAAa,EACb,YAAY,GACf,MAAM,UAAU,CAAC"}
package/index.js CHANGED
@@ -4,5 +4,5 @@
4
4
  */
5
5
  export {
6
6
  // Functions
7
- print, render, getConfig, setConfig, getConfigPath, listPrinters, } from "./api.js";
7
+ print, render, renderSegments, printSegments, getConfig, setConfig, getConfigPath, listPrinters, } from "./api.js";
8
8
  //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO;AAQH,YAAY;AACZ,KAAK,EACL,MAAM,EACN,SAAS,EACT,SAAS,EACT,aAAa,EACb,YAAY,GACf,MAAM,UAAU,CAAC","sourcesContent":["/**\r\n * Brother Label Printer API\r\n * @module @bobfrankston/brother-label\r\n */\r\n\r\nexport {\r\n // Types\r\n TapeSize,\r\n Orientation,\r\n PrinterConfig,\r\n PrinterInfo,\r\n PrintOptions,\r\n PrintResult,\r\n // Functions\r\n print,\r\n render,\r\n getConfig,\r\n setConfig,\r\n getConfigPath,\r\n listPrinters,\r\n} from \"./api.js\";\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO;AASH,YAAY;AACZ,KAAK,EACL,MAAM,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,aAAa,EACb,YAAY,GACf,MAAM,UAAU,CAAC","sourcesContent":["/**\r\n * Brother Label Printer API\r\n * @module @bobfrankston/brother-label\r\n */\r\n\r\nexport {\r\n // Types\r\n TapeSize,\r\n Orientation,\r\n PrinterConfig,\r\n PrinterInfo,\r\n PrintOptions,\r\n PrintResult,\r\n Segment,\r\n // Functions\r\n print,\r\n render,\r\n renderSegments,\r\n printSegments,\r\n getConfig,\r\n setConfig,\r\n getConfigPath,\r\n listPrinters,\r\n} from \"./api.js\";\r\n"]}
package/index.ts CHANGED
@@ -11,9 +11,12 @@ export {
11
11
  PrinterInfo,
12
12
  PrintOptions,
13
13
  PrintResult,
14
+ Segment,
14
15
  // Functions
15
16
  print,
16
17
  render,
18
+ renderSegments,
19
+ printSegments,
17
20
  getConfig,
18
21
  setConfig,
19
22
  getConfigPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/brother-label",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "API and CLI for printing labels on Brother P-touch printers",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -27,7 +27,8 @@
27
27
  "scripts": {
28
28
  "build": "tsc",
29
29
  "prepublishOnly": "npm run build",
30
- "start": "node cli.js"
30
+ "start": "node cli.js",
31
+ "release": "npmglobalize"
31
32
  },
32
33
  "keywords": [
33
34
  "brother",
@@ -44,7 +45,6 @@
44
45
  "url": "https://github.com/BobFrankston/brother-label.git"
45
46
  },
46
47
  "dependencies": {
47
- "commander": "^12.0.0",
48
48
  "jimp": "^1.6.0",
49
49
  "puppeteer": "^23.11.1",
50
50
  "qrcode": "^1.5.4"
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=brother-print.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"brother-print.d.ts","sourceRoot":"","sources":["brother-print.ts"],"names":[],"mappings":""}