@vdhewei/xlsx-template-lib 1.5.1 → 1.6.1

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/README.md CHANGED
@@ -1,7 +1,1017 @@
1
- ## xlsx-template-lib
1
+ # xlsx-template-lib
2
+
3
+ [中文文档](./README.zh-CN.md)
4
+
5
+ A powerful XLSX template rendering library based on ExcelJS, supporting template-based Excel file generation and data substitution.
6
+
7
+ ## Features
8
+
9
+ - **Template Rendering**: Render Excel files using templates with placeholders
10
+ - **Data Substitution**: Dynamic data substitution with expressions and functions
11
+ - **Rule Configuration**: Configure rendering rules via dedicated rule sheets
12
+ - **Custom Commands**: Extend functionality with custom command functions (e.g., `fn:sum`, `fn:sub`)
13
+ - **ZIP Support**: Batch process multiple Excel files within a ZIP archive
14
+ - **TypeScript Support**: Full TypeScript support with type definitions
15
+ - **CLI Tool**: Command-line interface for quick processing
2
16
 
3
17
  ## Installation
4
18
 
5
19
  ```bash
6
20
  npm install @vdhewei/xlsx-template-lib
7
- ```
21
+ ```
22
+
23
+ ## Template Syntax
24
+
25
+ ### Placeholder Format
26
+
27
+ Use `${variableName}` format in Excel cells for data substitution:
28
+
29
+ | Template (Before) | Rendered (After) |
30
+ |:------------------|:-----------------|
31
+ | `${contract.contractCode}` | `CTR-2024-001` |
32
+ | `${exportData.LRR.mothOrYear}` | `2024-01` |
33
+ | `${contract.contractTitle}` | `Construction Project A` |
34
+
35
+ ### Placeholder Types
36
+
37
+ #### 1. Simple Values (Scalars)
38
+
39
+ Replace a placeholder with a single value.
40
+
41
+ **Excel template:**
42
+ ```
43
+ A B
44
+ 1 Extracted on: ${extractDate}
45
+ ```
46
+
47
+ **Code:**
48
+ ```typescript
49
+ const values = {
50
+ extractDate: new Date('2024-01-15')
51
+ };
52
+ template.substitute(1, values);
53
+ ```
54
+
55
+ **Result:**
56
+ ```
57
+ A B
58
+ 1 Extracted on: Jan-15-2024
59
+ ```
60
+
61
+ **Notes:**
62
+ - Placeholders can be standalone in a cell or part of text: "Total: ${amount}"
63
+ - Excel cell formatting (date, number, currency) is preserved
64
+
65
+ #### 2. Array Indexing
66
+
67
+ Access specific array elements directly in templates.
68
+
69
+ **Excel template:**
70
+ ```
71
+ A B
72
+ 1 First date: ${dates[0]}
73
+ 2 Second date: ${dates[1]}
74
+ ```
75
+
76
+ **Code:**
77
+ ```typescript
78
+ const values = {
79
+ dates: [new Date('2024-01-01'), new Date('2024-02-01')]
80
+ };
81
+ template.substitute(1, values);
82
+ ```
83
+
84
+ **Result:**
85
+ ```
86
+ A B
87
+ 1 First date: Jan-01-2024
88
+ 2 Second date: Feb-01-2024
89
+ ```
90
+
91
+ #### 3. Column Arrays
92
+
93
+ Expand an array horizontally across columns.
94
+
95
+ **Excel template:**
96
+ ```
97
+ A
98
+ 1 ${dates}
99
+ ```
100
+
101
+ **Code:**
102
+ ```typescript
103
+ const values = {
104
+ dates: [
105
+ new Date('2024-01-01'),
106
+ new Date('2024-02-01'),
107
+ new Date('2024-03-01')
108
+ ]
109
+ };
110
+ template.substitute(1, values);
111
+ ```
112
+
113
+ **Result:**
114
+ ```
115
+ A B C
116
+ 1 Jan-01-2024 Feb-01-2024 Mar-01-2024
117
+ ```
118
+
119
+ **Note:** The placeholder must be the only content in its cell
120
+
121
+ #### 4. Table Rows
122
+
123
+ Generate multiple rows from an array of objects.
124
+
125
+ **Excel template:**
126
+ ```
127
+ A B C
128
+ 1 Name Age Department
129
+ 2 ${team.name} ${team.age} ${team.dept}
130
+ ```
131
+
132
+ **Code:**
133
+ ```typescript
134
+ const values = {
135
+ team: [
136
+ { name: 'Alice Johnson', age: 28, dept: 'Engineering' },
137
+ { name: 'Bob Smith', age: 34, dept: 'Marketing' },
138
+ { name: 'Carol White', age: 25, dept: 'Sales' }
139
+ ]
140
+ };
141
+ template.substitute(1, values);
142
+ ```
143
+
144
+ **Result:**
145
+ ```
146
+ A B C
147
+ 1 Name Age Department
148
+ 2 Alice Johnson 28 Engineering
149
+ 3 Bob Smith 34 Marketing
150
+ 4 Carol White 25 Sales
151
+ ```
152
+
153
+ **Syntax:** `${table:arrayName.propertyName}`
154
+ - Each object in the array creates a new row
155
+ - If a property is an array, it expands horizontally
156
+
157
+ #### 5. Images
158
+
159
+ Insert images into cells.
160
+
161
+ **Excel template:**
162
+ ```
163
+ A B
164
+ 1 Logo: ${image:companyLogo}
165
+ ```
166
+
167
+ **Code:**
168
+ ```typescript
169
+ const values = {
170
+ companyLogo: '/path/to/logo.png' // or Base64, Buffer
171
+ };
172
+ template.substitute(1, values);
173
+ ```
174
+
175
+ **Result:**
176
+ ```
177
+ A B
178
+ 1 Logo: 🖼️
179
+ ```
180
+
181
+ **Supported image formats:**
182
+ - File path (absolute or relative): '/path/to/image.png'
183
+ - Base64 string: 'data:image/png;base64,iVBORw0KG...'
184
+ - Buffer: fs.readFileSync('image.png')
185
+
186
+ **Image options:**
187
+ ```typescript
188
+ const template = new XlsxTemplate(data, {
189
+ imageRootPath: '/absolute/path/to/images', // Base path for relative image paths
190
+ imageRatio: 75 // Scale images to 75%
191
+ });
192
+ ```
193
+
194
+ **Table images:**
195
+ ```
196
+ A B
197
+ 1 Product Photo
198
+ 2 ${products.name} ${products.photo:image}
199
+ ```
200
+
201
+ **Code:**
202
+ ```typescript
203
+ const values = {
204
+ products: [
205
+ { name: 'Product 1', photo: 'product1.jpg' },
206
+ { name: 'Product 2', photo: 'product2.jpg' }
207
+ ]
208
+ };
209
+ ```
210
+
211
+ **Result:**
212
+ ```
213
+ A B
214
+ 1 Product Photo
215
+ 2 Product 1 🖼️
216
+ 3 Product 2 🖼️
217
+ ```
218
+
219
+ | Template (Before) | Rendered (After) |
220
+ |:------------------|:-----------------|
221
+ | `${contract.contractCode}` | `CTR-2024-001` |
222
+ | `${exportData.LRR.mothOrYear}` | `2024-01` |
223
+ | `${contract.contractTitle}` | `Construction Project A` |
224
+
225
+ ### Compile Rules Configuration
226
+
227
+ Configure rendering rules in a rule sheet (e.g., `export.metadata.config`) with the following syntax:
228
+
229
+ **⚠️ Important Rule:**
230
+ - **Duplicate rules within the same sheet are invalid and may cause compilation errors**
231
+ - Each rule type must have unique configurations within the same sheet
232
+
233
+ | Rule Type | Syntax | Description |
234
+ |:----------|:-------|:------------|
235
+ | **alias** | `alias: @#key => use aliasKey: @# => @#` | Alias for field mapping |
236
+ | **rowCell** | `G-AP:12=compile GenCell(...)` | Row rule configuration |
237
+ | **mergeCell** | `G-AQ:13-17=sum(...)` | Merge cell calculation |
238
+ | **cell** | `D-7=@#[@D.MY]` | Single cell value assignment |
239
+
240
+ #### Alias Rules
241
+
242
+ Define abbreviations for variable names or variable expression values. Multiple alias configurations are supported.
243
+
244
+ **Syntax:** `alias abbreviation=originalVariable/originalExpression`
245
+
246
+ **Rules:**
247
+ - Alias abbreviations must be unique within the same sheet
248
+ - Aliases can be referenced using `@aliasName` in expressions
249
+
250
+ **Examples:**
251
+
252
+ | Alias Configuration | Description |
253
+ |:--------------------|:------------|
254
+ | `T=template` | Map `T` to `template` |
255
+ | `LLR=exportData.LRR` | Map `LLR` to `exportData.LRR` |
256
+
257
+ **Usage in Expressions:**
258
+
259
+ ```
260
+ Before: ${exportData.LRR.value}
261
+ After: ${@LLR.value}
262
+ ```
263
+
264
+ #### RowCell Rules
265
+
266
+ Configure row rules to assign values to cell ranges. Multiple rowCell configurations are supported.
267
+
268
+ **Syntax:** `columnStartNum-columnEndNum:rowNum=valueExpression`
269
+
270
+ **Range Format:**
271
+ - `columnStartNum-columnEndNum:rowNum`
272
+ - Example: `G-AP:12` (columns G to AP, row 12)
273
+
274
+ **Value Expression:**
275
+ - Typically uses `compile:GenCell` macro replacement or `compile:Macro` expansion
276
+
277
+ **Examples:**
278
+
279
+ | Rule | Description |
280
+ |:-----|:------------|
281
+ | `G-AP:12=compile GenCell(@#item,[compile Macro]#index@0)` | Assign generated values to row 12, columns G-AP |
282
+ | `A-Z:5=compile Macro(@#data,2,5,!!codeKey)` | Assign formatted cell value to row 5, columns A-Z |
283
+
284
+ #### MergeCell Rules
285
+
286
+ Merge cells and apply calculation functions. Multiple mergeCell configurations are supported.
287
+
288
+ **Syntax:** `columnStartNum-columnEndNum:rowStartNum-rowEndNum=functionExpression`
289
+
290
+ **Range Format:**
291
+ - `columnStartNum-columnEndNum:rowStartNum-rowEndNum`
292
+ - Example: `G-AQ:13-17` (columns G to AQ, rows 13 to 17)
293
+
294
+ **Function Expression:**
295
+ - Typically uses `sum` or `sub` functions with macro replacements
296
+
297
+ **Examples:**
298
+
299
+ | Rule | Description |
300
+ |:-----|:------------|
301
+ | `G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)` | Merge and calculate sum for rows 13-17 |
302
+ | `G-AQ:18-35=sub(@LT,[compile:Macro(exprArr,F,18,35,!!codeKey)],compile:Macro(index),0)` | Merge and calculate difference for rows 18-35 |
303
+
304
+ #### Cell Rules
305
+
306
+ Assign values to single cells.
307
+
308
+ **Syntax:** `columnNum:rowNum=valueExpression`
309
+
310
+ **Coordinate Format:**
311
+ - `columnNum:rowNum`
312
+ - Example: `D:7` (column D, row 7)
313
+
314
+ **Value Expression:**
315
+ - `compile:Macro` expansion
316
+ - Variable placeholder: `${variable}`
317
+ - Variable placeholder with alias: `${@alias}`
318
+
319
+ **Examples:**
320
+
321
+ | Rule | Description |
322
+ |:-----|:------------|
323
+ | `D:7=@#[@D.MY]` | Assign value from expression |
324
+ | `A:1=${contractCode}` | Assign from variable placeholder |
325
+ | `B:1=${@LLR.value}` | Assign from aliased variable |
326
+
327
+ #### Calculation Functions
328
+
329
+ **sum Function**
330
+
331
+ Calculate the sum of multiple values.
332
+
333
+ **Syntax:** `sum(valueRoot,[valueItems...],valueSuffix,defaultValue)`
334
+
335
+ **Parameters:**
336
+ - `valueRoot`: Common parent of all value expressions
337
+ - `valueItems`: Array of child value items
338
+ - `valueSuffix`: Suffix for each value
339
+ - `defaultValue`: Value to return when sum equals 0 (undefined will not return default)
340
+
341
+ **Example:**
342
+ ```
343
+ sum(orders,[cat,food,game],1,0)
344
+ // Equivalent to: orders.cat.1 + orders.food.1 + orders.game.1
345
+ ```
346
+
347
+ **sub Function**
348
+
349
+ Calculate the difference of multiple values.
350
+
351
+ **Syntax:** `sub(valueRoot,[valueItems...],valueSuffix,defaultValue)`
352
+
353
+ **Parameters:**
354
+ - `valueRoot`: Common parent of all value expressions
355
+ - `valueItems`: Array of child value items
356
+ - `valueSuffix`: Suffix for each value
357
+ - `defaultValue`: Value to return when difference equals 0 (undefined will not return default)
358
+
359
+ **Example:**
360
+ ```
361
+ sub(orders,[money,food,game],1,0)
362
+ // Equivalent to: orders.money.1 - orders.food.1 - orders.game.1
363
+ ```
364
+
365
+ ### Macro Replacement Rules
366
+
367
+ The library supports powerful macro replacement functions for dynamic cell content generation:
368
+
369
+ #### GenCell Macro
370
+
371
+ Generate cell expressions by concatenating multiple parts:
372
+
373
+ | Syntax | Description | Example | Result |
374
+ |:-------|:------------|:--------|:--------|
375
+ | `compile:GenCell(expr1,expr2,...,exprN)` | Concatenate with default separator `·` | `GenCell(test,1,2)` | `test·1·2` |
376
+ | `compile:GenCell(expr1,expr2,...,exprN,"sep")` | Concatenate with custom separator | `GenCell(test,1,2,"_")` | `test_1_2` |
377
+ | `compile:GenCell(expr1,expr2,...,exprN,"")` | Concatenate without separator | `GenCell(test,1,2,"")` | `test12` |
378
+
379
+ #### Macro Expansion
380
+
381
+ ##### Single Cell Macro
382
+
383
+ Expand to a single cell's value:
384
+
385
+ | Syntax | Description | Example |
386
+ |:-------|:------------|:--------|
387
+ | `compile:Macro(expr,columnNum,rowNum)` | Get value from cell at (columnNum, rowNum) | `Macro(data,2,5)` |
388
+ | `compile:Macro(expr,columnNum,rowNum,MacroFormatter)` | Get formatted value | `Macro(data,2,5,!!codeKey)` |
389
+
390
+ **Parameters:**
391
+ - `expr`: Base expression
392
+ - `columnNum`: Column number (1-indexed)
393
+ - `rowNum`: Row number (1-indexed)
394
+ - `MacroFormatter`: Optional formatter (see below)
395
+
396
+ ##### Multiple Cells Macro
397
+
398
+ Expand to multiple cell values:
399
+
400
+ | Syntax | Description | Example |
401
+ |:-------|:------------|:--------|
402
+ | `compile:Macro(exprArr,columnNum,rowStartNum,rowEndNum)` | Get values from cell range | `Macro(data,1,1,5)` |
403
+ | `compile:Macro(exprArr,columnNum,rowStartNum,rowEndNum,MacroFormatter)` | Get formatted values | `Macro(data,1,1,5,!!number)` |
404
+
405
+ **Parameters:**
406
+ - `exprArr`: Base expression array
407
+ - `columnNum`: Column number (1-indexed)
408
+ - `rowStartNum`: Start row number (1-indexed)
409
+ - `rowEndNum`: End row number (1-indexed)
410
+ - `MacroFormatter`: Optional formatter
411
+
412
+ ##### Index Macro
413
+
414
+ Generate iteration sequence starting from 1:
415
+
416
+ | Syntax | Description | Example Usage | Result |
417
+ |:-------|:------------|:--------------|:--------|
418
+ | `compile:Macro(index)` | Auto-increment index (1-based) | Row 1: `Macro(index)` | `1` |
419
+ | | | Row 2: `Macro(index)` | `2` |
420
+ | | | Row 3: `Macro(index)` | `3` |
421
+
422
+ #### Macro Formatters
423
+
424
+ Format macro output using special formatters starting with `!!`:
425
+
426
+ | Formatter | Description | Input | Output |
427
+ |:----------|:------------|:-------|:--------|
428
+ | `!!codeKey` | Convert special chars (`@-[]{}\/'.`) to `_`, remove extra `__`, trim leading/trailing `_`, convert to uppercase | `test..x` | `TEST_X` |
429
+ | | | `@data-value` | `DATA_VALUE` |
430
+ | | | `[item].name` | `ITEM_NAME` |
431
+ | `!!codeKeyAlias` | Same as `!!codeKey` but adds prefix (default `@`) | `test..x` | `@TEST_X` |
432
+ | | (with default prefix `@`) | `data.value` | `@DATA_VALUE` |
433
+ | `!!number` | Convert to decimal integer, supports `0x` hex prefix | `123` | `123` |
434
+ | | | `0xFF` | `255` |
435
+ | | | `abc` | `abc` (unchanged, NaN) |
436
+
437
+ **CodeKey Conversion Rules:**
438
+ - Special characters replaced: `@`, `-`, space, `[`, `]`, `{`, `}`, `\`, `/`, `'`, `.`
439
+ - Multiple consecutive `__` collapsed to single `_`
440
+ - Leading and trailing `_` removed
441
+ - Final result converted to uppercase
442
+
443
+ #### Macro Examples
444
+
445
+ **Example 1: Generate CodeKey with Row Cell**
446
+
447
+ ```
448
+ Rule: G-AQ:117=compile GenCell(#LT[compile Macro]#err@F118[#codeKey],[compile Macro]#index@0)
449
+ Result: errValue·1, errValue·2, errValue·3, ...
450
+ ```
451
+
452
+ **Example 2: Format Cell Value with CodeKey**
453
+
454
+ ```
455
+ Rule: D-7=compile Macro(@#[@D.MY],5,7,!!codeKey)
456
+ If cell(5,7) = "project-alpha-2024"
457
+ Result: PROJECT_ALPHA_2024
458
+ ```
459
+
460
+ **Example 3: Generate CodeKeyAlias**
461
+
462
+ ```
463
+ Rule: cell F-10=compile Macro(@#key,3,10,!!codeKeyAlias)
464
+ If cell(3,10) = "test..data"
465
+ Result: @TEST_DATA
466
+ ```
467
+
468
+ **Example 4: Number Conversion**
469
+
470
+ ```
471
+ Rule: row-5=compile Macro(@#value,2,5,!!number)
472
+ If cell(2,5) = "42"
473
+ Result: 42
474
+
475
+ Rule: row-6=compile Macro(@#hex,4,6,!!number)
476
+ If cell(4,6) = "0x1A"
477
+ Result: 26
478
+ ```
479
+
480
+ **Example 5: Iteration with Index**
481
+
482
+ ```
483
+ Row 1: Code-${compile:Macro(index)} → Code-1
484
+ Row 2: Code-${compile:Macro(index)} → Code-2
485
+ Row 3: Code-${compile:Macro(index)} → Code-3
486
+ ```
487
+
488
+ #### Complete Rule Configuration Example
489
+
490
+ A complete example of a rule sheet (`export.metadata.config`) with all rule types:
491
+
492
+ ```
493
+ # Alias Rules (define shortcuts for long expressions)
494
+ T=template
495
+ LLR=exportData.LRR
496
+ CTR=contract.contractCode
497
+
498
+ # RowCell Rules (assign values to cell ranges)
499
+ G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)
500
+ A-Z:5=compile Macro(@#data,2,5,!!codeKey)
501
+
502
+ # MergeCell Rules (merge cells and apply calculations)
503
+ G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)
504
+ G-AQ:18-35=sub(@LT,[compile:Macro(exprArr,F,18,35,!!codeKey)],compile:Macro(index),0)
505
+
506
+ # Cell Rules (assign values to single cells)
507
+ D:7=@#[@D.MY]
508
+ A:1=${@CTR}
509
+ B:1=${@LLR.value}
510
+ ```
511
+
512
+ **⚠️ Important Notes:**
513
+ - Each rule type (alias, rowCell, mergeCell, cell) can appear multiple times
514
+ - But **duplicate rules of the same type with the same configuration are invalid**
515
+ - Alias abbreviations must be unique within the sheet
516
+ - Row/column ranges must not overlap in conflicting ways
517
+
518
+ ### Render Functions
519
+
520
+ Built-in and custom functions for data processing:
521
+
522
+ | Function | Syntax | Example |
523
+ |:---------|:-------|:--------|
524
+ | **sum** | `fn:sum(...values)` | `fn:sum(10, 20, 30)` => `60` |
525
+ | **sub** | `fn:sub(a, b)` | `fn:sub(100, 30)` => `70` |
526
+ | **Custom Function** | `fn:customName(...args)` | User-defined logic |
527
+
528
+ ## Quick Start
529
+
530
+ ### Basic Usage
531
+
532
+ ```typescript
533
+ import { ZipXlsxTemplateApp } from '@vdhewei/xlsx-template-lib';
534
+ import * as fs from 'node:fs/promises';
535
+
536
+ // Load template from buffer
537
+ const templateBuffer = await fs.readFile('template.xlsx');
538
+ const app = new ZipXlsxTemplateApp(templateBuffer);
539
+
540
+ // Render with data
541
+ const data = {
542
+ contract: {
543
+ contractCode: 'CTR-2024-001',
544
+ contractTitle: 'Construction Project A'
545
+ },
546
+ exportData: {
547
+ LRR: {
548
+ mothOrYear: '2024-01'
549
+ }
550
+ }
551
+ };
552
+
553
+ await app.substituteAll(data);
554
+
555
+ // Generate output
556
+ const output = await app.generate();
557
+ await fs.writeFile('output.xlsx', output);
558
+ ```
559
+
560
+ ### Using XlsxRender
561
+
562
+ ```typescript
563
+ import { XlsxRender } from '@vdhewei/xlsx-template-lib';
564
+
565
+ const templateBuffer = await fs.readFile('template.xlsx');
566
+ const xlsx = await XlsxRender.create(templateBuffer);
567
+
568
+ // Render a specific sheet
569
+ await xlsx.render({
570
+ contract: { contractCode: 'CTR-001' }
571
+ }, 'Sheet1');
572
+
573
+ // Generate output
574
+ const buffer = await xlsx.generate();
575
+ ```
576
+
577
+ ### Compile and Render with Rules
578
+
579
+ ```typescript
580
+ import {
581
+ ZipXlsxTemplateApp,
582
+ compileAll,
583
+ AddCommand
584
+ } from '@vdhewei/xlsx-template-lib';
585
+
586
+ // Add custom render function
587
+ AddCommand('sum', (obj, args) => {
588
+ return args.groups.reduce((acc, val) => acc + Number(val), 0);
589
+ });
590
+
591
+ // Process with compile options
592
+ const compileOpts = {
593
+ sheetName: 'export.metadata.config', // Rule configuration sheet
594
+ remove: true // Remove rule sheet after compile
595
+ };
596
+
597
+ const zipBuffer = await fs.readFile('template.xlsx');
598
+ const result = await ZipXlsxTemplateApp.compileTo(zipBuffer, {
599
+ checker: async (buf, opts, values, fileName) => {
600
+ // Custom validation logic
601
+ return buf;
602
+ },
603
+ options: compileOpts
604
+ }, renderData);
605
+ ```
606
+
607
+ ### CLI Tool
608
+
609
+ The CLI tool `xlsx-cli` provides command-line interface for quick Excel template processing.
610
+
611
+ #### Installation
612
+
613
+ ```bash
614
+ npm install -g @vdhewei/xlsx-template-lib
615
+ ```
616
+
617
+ Or use directly from `npx`:
618
+
619
+ ```bash
620
+ npx @vdhewei/xlsx-template-lib <command> [options]
621
+ ```
622
+
623
+ #### Commands
624
+
625
+ ##### 1. Compile Command
626
+
627
+ Compile Excel files with rule configurations.
628
+
629
+ ```bash
630
+ xlsx-cli compile <xlsx-file> [options]
631
+ ```
632
+
633
+ **Arguments:**
634
+ - `<xlsx-file>` - Path to the Excel file
635
+
636
+ **Options:**
637
+ - `-s, --save <string>` - Save compiled file to specified directory (default: current directory)
638
+ - `-n, --sheet-name <string>` - Sheet name to compile (default: first sheet)
639
+ - `-r, --remove` - Remove configure rules sheet after compilation (default: false)
640
+
641
+ **Examples:**
642
+
643
+ ```bash
644
+ # Basic compile with default settings
645
+ xlsx-cli compile template.xlsx
646
+
647
+ # Compile and save to specific location
648
+ xlsx-cli compile template.xlsx -s ./output/
649
+
650
+ # Compile specific sheet
651
+ xlsx-cli compile template.xlsx -n Sheet1
652
+
653
+ # Compile and remove config sheet
654
+ xlsx-cli compile template.xlsx -r
655
+
656
+ # Full example
657
+ xlsx-cli compile template.xlsx -s ./output/ -n Sheet1 -r
658
+ ```
659
+
660
+ **Output:**
661
+ - Compiled Excel file saved as `<filename>_<timestamp>.xlsx`
662
+ - Success messages displayed in green
663
+ - Errors displayed in red with process exit code 1
664
+
665
+ ##### 2. Render Command
666
+
667
+ Render Excel templates with data substitution.
668
+
669
+ ```bash
670
+ xlsx-cli render <xlsx-file> [options]
671
+ ```
672
+
673
+ **Arguments:**
674
+ - `<xlsx-file>` - Path to the Excel template file
675
+
676
+ **Options:**
677
+ - `-c, --compile` - Auto-compile rules before rendering (default: false)
678
+ - `-n, --sheet-name <string>` - Sheet name to render (default: first sheet)
679
+ - `-s, --save <string>` - Save rendered file to specified directory (default: current directory)
680
+ - `-d, --data <string>` - Render data source (JSON string, file path, or URL)
681
+
682
+ **Examples:**
683
+
684
+ ```bash
685
+ # Basic render with empty data
686
+ xlsx-cli render template.xlsx
687
+
688
+ # Render with JSON data string
689
+ xlsx-cli render template.xlsx -d '{"name":"John","age":30}'
690
+
691
+ # Render with JSON file
692
+ xlsx-cli render template.xlsx -d ./data.json
693
+
694
+ # Render with remote JSON URL
695
+ xlsx-cli render template.xlsx -d 'https://api.example.com/data.json'
696
+
697
+ # Render with auto-compile
698
+ xlsx-cli render template.xlsx -c -d './data.json'
699
+
700
+ # Render specific sheet
701
+ xlsx-cli render template.xlsx -n Sheet1 -d './data.json'
702
+
703
+ # Full example
704
+ xlsx-cli render template.xlsx -c -n Sheet1 -s ./output/ -d './data.json'
705
+ ```
706
+
707
+ **Data Sources:**
708
+ - **JSON String**: Direct JSON string enclosed in single quotes
709
+ - **Local File**: Path to `.json` file (relative or absolute)
710
+ - **Remote URL**: HTTP/HTTPS URL returning JSON
711
+
712
+ **Output:**
713
+ - Rendered Excel file saved as `<filename>_<timestamp>.xlsx`
714
+ - Validation checks for sheet existence
715
+ - Success/error messages with appropriate colors
716
+
717
+ ##### 3. Rules Command
718
+
719
+ Add rule configurations to Excel files.
720
+
721
+ ```bash
722
+ xlsx-cli rules <xlsx-file> [options]
723
+ ```
724
+
725
+ **Arguments:**
726
+ - `<xlsx-file>` - Path to the Excel file
727
+
728
+ **Options:**
729
+
730
+ **Mode 1: Command Line Rules**
731
+ - `-t, --type <string>` - Rule type: `cell`, `alias`, `rowCell`, `mergeCell` (required when using -r)
732
+ - `-r, --rule <string>` - Rule expression string (can be specified multiple times)
733
+
734
+ **Mode 2: File Rules**
735
+ - `-f, --file <string>` - Read rules from file (format: `<type> ruleExpr` per line)
736
+ - Lines starting with `#` are treated as comments
737
+ - Empty lines are skipped
738
+ - Rule types: `cell`, `alias`, `rowCell`, `mergeCell`
739
+
740
+ **Common Options:**
741
+ - `-s, --save <string>` - Save compiled file to specified directory (default: current directory)
742
+
743
+ **Examples:**
744
+
745
+ **Single Rule (Command Line):**
746
+ ```bash
747
+ # Add alias rule
748
+ xlsx-cli rules template.xlsx -t alias -r 'T=template'
749
+
750
+ # Add cell rule
751
+ xlsx-cli rules template.xlsx -t cell -r 'D:7=${@LLR.value}'
752
+
753
+ # Add rowCell rule
754
+ xlsx-cli rules template.xlsx -t rowCell -r 'G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)'
755
+
756
+ # Add mergeCell rule
757
+ xlsx-cli rules template.xlsx -t mergeCell -r 'G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)'
758
+ ```
759
+
760
+ **Multiple Rules (Command Line):**
761
+ ```bash
762
+ # Add multiple rules with same type
763
+ xlsx-cli rules template.xlsx -t cell -r 'D:7=${@LLR.value}' -r 'A:1=${@T}' -r 'B:1=${@LLR.value}'
764
+ ```
765
+
766
+ **Rules from File:**
767
+ ```bash
768
+ # Read rules from file
769
+ xlsx-cli rules template.xlsx -f rules.txt
770
+
771
+ # Create rules.txt file:
772
+ # This is a comment
773
+ alias T=template
774
+ alias LLR=exportData.LRR
775
+ cell D:7=${@T}
776
+ cell A:1=${@LLR.value}
777
+ rowCell G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)
778
+ mergeCell G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)
779
+ ```
780
+
781
+ **Save to Specific Directory:**
782
+ ```bash
783
+ xlsx-cli rules template.xlsx -f rules.txt -s ./output/
784
+ xlsx-cli rules template.xlsx -t cell -r 'D:7=${@LLR.value}' -s ./output/
785
+ ```
786
+
787
+ **File Format (-f mode):**
788
+ ```bash
789
+ # Format: <type> ruleExpr
790
+ # Comments start with #
791
+ # Valid types: cell, alias, rowCell, mergeCell
792
+
793
+ cell D:7=${@LLR.value}
794
+ alias T=template
795
+ rowCell G-AQ:12=compile GenCell(@#item,[compile Macro]#index@0)
796
+ mergeCell G-AQ:13-17=sum(@LT,[compile:Macro(exprArr,F,13,17,!!codeKey)],compile:Macro(index),0)
797
+ ```
798
+
799
+ **Behavior:**
800
+ - Creates `export_metadata.config` sheet if not exists
801
+ - Adds rule with proper styling: bold + center alignment for type, center alignment for expression
802
+ - Auto-adjusts column widths based on content
803
+ - Each rule type (cell, alias, rowCell, mergeCell) supports up to 4 rules per row
804
+ - If more than 4 rules are added for same type, automatically creates a new row
805
+ - Outputs new file with timestamp
806
+
807
+ #### Common Features
808
+
809
+ **Environment Variables:**
810
+ - CLI automatically loads `.env` file from current directory if present
811
+
812
+ **File Path Resolution:**
813
+ - Supports absolute and relative paths
814
+ - Resolves paths relative to current working directory
815
+ - Validates file existence before processing
816
+
817
+ **Error Handling:**
818
+ - All errors displayed in red using chalk
819
+ - Non-zero exit code on errors
820
+ - Detailed error messages for debugging
821
+
822
+ **Cross-Platform Support:**
823
+ - Works on Windows, Linux, and macOS
824
+ - Uses platform-independent path handling
825
+
826
+ **Output Filename Format:**
827
+ - Default: `<input-filename>_<timestamp>.xlsx`
828
+ - Timestamp in milliseconds since epoch
829
+ - Preserves original file name
830
+
831
+ **Verbose Logging:**
832
+ - Gray informational messages for process steps
833
+ - Green success messages
834
+ - Red error messages
835
+ - Yellow warnings
836
+
837
+ ## Advanced Features
838
+
839
+ ### Custom Commands (Render Functions)
840
+
841
+ ```typescript
842
+ import { AddCommand, generateCommandsXlsxTemplate } from '@vdhewei/xlsx-template-lib';
843
+
844
+ // Add custom command
845
+ AddCommand('multiply', (obj, args) => {
846
+ const values = args.groups.map(g => valueDotGet(obj, g));
847
+ return values.reduce((a, b) => a * b, 1);
848
+ });
849
+
850
+ // Generate template with custom commands
851
+ const buffer = await generateCommandsXlsxTemplate(data, options);
852
+ ```
853
+
854
+ ### Batch Processing
855
+
856
+ ```typescript
857
+ // Process ZIP file containing multiple XLSX files
858
+ const zipBuffer = await fs.readFile('templates.zip');
859
+ const app = new ZipXlsxTemplateApp(zipBuffer);
860
+
861
+ const compileOpts = {
862
+ sheetName: 'export.metadata.config',
863
+ remove: true
864
+ };
865
+
866
+ const renderOpts = {
867
+ // Rendering options
868
+ };
869
+
870
+ await app.substituteAll(renderData, compileOpts, renderOpts);
871
+ const output = await app.generate();
872
+ ```
873
+
874
+ ## API Reference
875
+
876
+ ### ZipXlsxTemplateApp
877
+
878
+ Main class for processing Excel files in ZIP archives.
879
+
880
+ | Method | Description |
881
+ |:-------|:------------|
882
+ | `constructor(data?: Buffer)` | Initialize with ZIP buffer |
883
+ | `loadZipBuffer(data: Buffer)` | Load ZIP buffer |
884
+ | `parse(data: Buffer)` | Parse ZIP and extract XLSX entries |
885
+ | `getEntries()` | Get all XLSX file entries |
886
+ | `substituteAll(renderData, compileOpts?, renderOpts?)` | Substitute all placeholders |
887
+ | `generate(options?)` | Generate output buffer |
888
+ | `static compileAll(files, renderData?, compileOpts?)` | Compile multiple files |
889
+ | `static compileTo(data, opts, values?)` | Compile XLSX in ZIP with custom checker |
890
+
891
+ ### XlsxRender
892
+
893
+ Main class for rendering single Excel files.
894
+
895
+ | Method | Description |
896
+ |:-------|:------------|
897
+ | `static create(data: Buffer, option?)` | Create from buffer |
898
+ | `render(values: Object, sheetName: string)` | Render specific sheet |
899
+ | `getSheets()` | Get all sheet information |
900
+ | `generate(options?)` | Generate output buffer |
901
+
902
+ ### Helper Functions
903
+
904
+ | Function | Description |
905
+ |:---------|:------------|
906
+ | `ExprResolver` | Expression resolver for complex expressions |
907
+ | `compileRuleSheetName` | Default rule sheet name |
908
+ | `generateXlsxTemplate` | Generate XLSX template |
909
+ | `generateCommandsXlsxTemplate` | Generate with custom commands |
910
+ | `AddCommand(name, fn)` | Add custom render function |
911
+
912
+ ## Complete Example
913
+
914
+ ### Template Structure
915
+
916
+ ```
917
+ template.xlsx
918
+ ├── Sheet1 (Data Sheet with placeholders)
919
+ │ ├── A1: ${contract.contractCode}
920
+ │ ├── B1: ${contract.contractTitle}
921
+ │ └── C1: ${exportData.LRR.mothOrYear}
922
+ └── export.metadata.config (Rule Configuration Sheet)
923
+ ├── Row 1: alias @#key => use aliasKey: @# => @#
924
+ ├── Row 2: mergeCell G-AQ(1-17)=sum(...)
925
+ └── Row 3: cell D-7=@#[@D.MY]
926
+ ```
927
+
928
+ ### Compile & Render Flow
929
+
930
+ | Step | Input | Output | Description |
931
+ |:-----|:------|:-------|:------------|
932
+ | 1. Load | `template.xlsx` Buffer | `ZipXlsxTemplateApp` | Load template file |
933
+ | 2. Compile | Rule Config Sheet | Compiled Rules | Parse mergeCell/cell/rowCell rules |
934
+ | 3. Substitute | Data Object | Rendered Sheets | Replace `${...}` placeholders |
935
+ | 4. Generate | - | `output.xlsx` Buffer | Final output file |
936
+
937
+ ```typescript
938
+ import { ZipXlsxTemplateApp, AddCommand } from '@vdhewei/xlsx-template-lib';
939
+ import * as fs from 'node:fs/promises';
940
+
941
+ // Define custom function
942
+ AddCommand('calculateTotal', (obj, args) => {
943
+ const base = Number(args.root);
944
+ const multiplier = args.groups.length > 0 ? Number(args.groups[0]) : 1;
945
+ return base * multiplier;
946
+ });
947
+
948
+ // Main processing
949
+ async function processTemplate() {
950
+ const templateBuffer = await fs.readFile('template.xlsx');
951
+
952
+ const app = new ZipXlsxTemplateApp(templateBuffer);
953
+
954
+ const renderData = {
955
+ contract: {
956
+ contractCode: 'CTR-2024-001',
957
+ contractTitle: 'Monthly Return of Site Labour'
958
+ },
959
+ exportData: {
960
+ LRR: {
961
+ mothOrYear: '2024-01',
962
+ workCode: 'WC-001'
963
+ }
964
+ }
965
+ };
966
+
967
+ const compileOpts = {
968
+ sheetName: 'export.metadata.config',
969
+ remove: true // Remove rule sheet in output
970
+ };
971
+
972
+ await app.substituteAll(renderData, compileOpts);
973
+
974
+ const output = await app.generate();
975
+ await fs.writeFile('output.xlsx', output);
976
+ }
977
+
978
+ processTemplate();
979
+ ```
980
+
981
+ ## Note
982
+
983
+ - The `test_data` directory contains internal test templates and is for private use only
984
+ - Rule configuration sheet supports: `alias`, `mergeCell`, `cell`, `rowCell` rule types
985
+ - Custom functions can be registered via `AddCommand(name, handler)`
986
+
987
+ ## License
988
+
989
+ MIT
990
+
991
+ ## Author
992
+
993
+ VDHewei
994
+
995
+ ## Repository
996
+
997
+ https://github.com/VDHewei/xlsx-template-lib
998
+
999
+ ## Contributing
1000
+
1001
+ Contributions are welcome! Please feel free to submit a Pull Request.
1002
+
1003
+ ## Acknowledgments
1004
+
1005
+ This project was inspired by the excellent open-source project [xlsx-template](https://github.com/optilude/xlsx-template) by optilude.
1006
+
1007
+ **xlsx-template** provides a robust foundation for Excel template-based report generation with dynamic data substitution. Many concepts and design patterns from xlsx-template have influenced this library, including:
1008
+
1009
+ - Template-based Excel file generation
1010
+ - Placeholder substitution syntax
1011
+ - Array and table expansion
1012
+ - Image insertion and positioning
1013
+ - Cell formatting preservation
1014
+
1015
+ We extend our gratitude to the xlsx-template team and contributors for their valuable work in the open-source community.
1016
+
1017
+ **Original xlsx-template repository:** https://github.com/optilude/xlsx-template