@vdhewei/xlsx-template-lib 1.6.6 → 1.6.10
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 +75 -0
- package/README.zh-CN.md +75 -0
- package/dist/bin.js +167 -79
- package/dist/bin.js.map +1 -1
- package/dist/bin.mjs +169 -79
- package/dist/bin.mjs.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -678,6 +678,8 @@ xlsx-cli render <xlsx-file> [options]
|
|
|
678
678
|
- `-n, --sheet-name <string>` - Sheet name to render (default: first sheet)
|
|
679
679
|
- `-s, --save <string>` - Save rendered file to specified directory (default: current directory)
|
|
680
680
|
- `-d, --data <string>` - Render data source (JSON string, file path, or URL)
|
|
681
|
+
- `--header <string>` - HTTP headers for remote data fetch (can be specified multiple times, format: `Key:Value`)
|
|
682
|
+
- `--body <string>` - HTTP request body for POST requests
|
|
681
683
|
|
|
682
684
|
**Examples:**
|
|
683
685
|
|
|
@@ -700,6 +702,15 @@ xlsx-cli render template.xlsx -c -d './data.json'
|
|
|
700
702
|
# Render specific sheet
|
|
701
703
|
xlsx-cli render template.xlsx -n Sheet1 -d './data.json'
|
|
702
704
|
|
|
705
|
+
# Render with custom HTTP headers
|
|
706
|
+
xlsx-cli render template.xlsx -d 'https://api.example.com/data.json' --header 'Authorization:Bearer token123' --header 'Content-Type:application/json'
|
|
707
|
+
|
|
708
|
+
# Render with POST request body
|
|
709
|
+
xlsx-cli render template.xlsx -d 'https://api.example.com/api/query' --body '{"query":"SELECT * FROM users"}' --header 'Content-Type:application/json'
|
|
710
|
+
|
|
711
|
+
# Render with POST method via header
|
|
712
|
+
xlsx-cli render template.xlsx -d 'https://api.example.com/api/create' --body '{"name":"Test"}' --header 'Content-Type:application/json' --header 'method:POST'
|
|
713
|
+
|
|
703
714
|
# Full example
|
|
704
715
|
xlsx-cli render template.xlsx -c -n Sheet1 -s ./output/ -d './data.json'
|
|
705
716
|
```
|
|
@@ -709,6 +720,70 @@ xlsx-cli render template.xlsx -c -n Sheet1 -s ./output/ -d './data.json'
|
|
|
709
720
|
- **Local File**: Path to `.json` file (relative or absolute)
|
|
710
721
|
- **Remote URL**: HTTP/HTTPS URL returning JSON
|
|
711
722
|
|
|
723
|
+
**HTTP Request Options (for Remote URL):**
|
|
724
|
+
- **Headers**: Use `--header` to add custom HTTP headers (can be specified multiple times)
|
|
725
|
+
- Format: `--header 'Key:Value'`
|
|
726
|
+
- Example: `--header 'Authorization:Bearer token123' --header 'Content-Type:application/json'`
|
|
727
|
+
- Special header: `method:POST` can set the HTTP method to POST
|
|
728
|
+
- **Body**: Use `--body` to send request body (typically for POST requests)
|
|
729
|
+
- Format: `--body '{"key":"value"}'`
|
|
730
|
+
- Automatically uses POST method when body is provided
|
|
731
|
+
- **Default Behavior**: GET request with no headers
|
|
732
|
+
|
|
733
|
+
**HTTP Request Examples:**
|
|
734
|
+
|
|
735
|
+
```bash
|
|
736
|
+
# GET request with custom headers
|
|
737
|
+
xlsx-cli render template.xlsx \
|
|
738
|
+
-d 'https://api.example.com/data.json' \
|
|
739
|
+
--header 'Authorization:Bearer your-token' \
|
|
740
|
+
--header 'X-API-Key:api-key-123'
|
|
741
|
+
|
|
742
|
+
# POST request with JSON body
|
|
743
|
+
xlsx-cli render template.xlsx \
|
|
744
|
+
-d 'https://api.example.com/api/query' \
|
|
745
|
+
--body '{"query":"SELECT * FROM users LIMIT 10"}' \
|
|
746
|
+
--header 'Content-Type:application/json'
|
|
747
|
+
|
|
748
|
+
# POST request with method specified in header
|
|
749
|
+
xlsx-cli render template.xlsx \
|
|
750
|
+
-d 'https://api.example.com/api/create' \
|
|
751
|
+
--body '{"name":"New Record","value":100}' \
|
|
752
|
+
--header 'Content-Type:application/json' \
|
|
753
|
+
--header 'method:POST'
|
|
754
|
+
|
|
755
|
+
# Complex example with authentication and query body
|
|
756
|
+
xlsx-cli render template.xlsx \
|
|
757
|
+
-d 'https://api.example.com/v1/export' \
|
|
758
|
+
--header 'Authorization:Bearer eyJhbGc...' \
|
|
759
|
+
--header 'Content-Type:application/json' \
|
|
760
|
+
--body '{"format":"xlsx","filter":{"status":"active"}}' \
|
|
761
|
+
-c -n Sheet1 -s ./output/
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
**HTTP Request Details:**
|
|
765
|
+
|
|
766
|
+
1. **Method Determination**:
|
|
767
|
+
- Default: `GET`
|
|
768
|
+
- With `--body`: Automatically becomes `POST`
|
|
769
|
+
- With `method:POST` header: Explicitly set to `POST`
|
|
770
|
+
- With `method:GET` header: Explicitly set to `GET`
|
|
771
|
+
|
|
772
|
+
2. **Header Format**:
|
|
773
|
+
- Headers are parsed as `Key:Value` pairs
|
|
774
|
+
- Multiple `--header` options can be used
|
|
775
|
+
- Example: `--header 'Accept:application/json' --header 'User-Agent:MyApp/1.0'`
|
|
776
|
+
|
|
777
|
+
3. **Error Handling**:
|
|
778
|
+
- Non-200 status codes return `undefined` and display error message
|
|
779
|
+
- Network errors are caught and displayed in red
|
|
780
|
+
- Missing `node-fetch` (Node.js < 18) displays error message
|
|
781
|
+
|
|
782
|
+
4. **Supported Data Formats**:
|
|
783
|
+
- JSON objects: `{"key":"value"}`
|
|
784
|
+
- JSON arrays: `[{"id":1},{"id":2}]`
|
|
785
|
+
- Nested structures: `{"user":{"name":"John","age":30}}`
|
|
786
|
+
|
|
712
787
|
**Output:**
|
|
713
788
|
- Rendered Excel file saved as `<filename>_<timestamp>.xlsx`
|
|
714
789
|
- Validation checks for sheet existence
|
package/README.zh-CN.md
CHANGED
|
@@ -676,6 +676,8 @@ xlsx-cli render <xlsx-文件> [选项]
|
|
|
676
676
|
- `-n, --sheet-name <string>` - 要渲染的工作表名称(默认:第一个工作表)
|
|
677
677
|
- `-s, --save <string>` - 将渲染后的文件保存到指定目录(默认:当前目录)
|
|
678
678
|
- `-d, --data <string>` - 渲染数据源(JSON 字符串、文件路径或 URL)
|
|
679
|
+
- `--header <string>` - 远程数据获取的 HTTP 请求头(可指定多次,格式:`Key:Value`)
|
|
680
|
+
- `--body <string>` - POST 请求的 HTTP 请求体
|
|
679
681
|
|
|
680
682
|
**示例:**
|
|
681
683
|
|
|
@@ -698,6 +700,15 @@ xlsx-cli render template.xlsx -c -d './data.json'
|
|
|
698
700
|
# 渲染指定工作表
|
|
699
701
|
xlsx-cli render template.xlsx -n Sheet1 -d './data.json'
|
|
700
702
|
|
|
703
|
+
# 使用自定义 HTTP 请求头渲染
|
|
704
|
+
xlsx-cli render template.xlsx -d 'https://api.example.com/data.json' --header 'Authorization:Bearer token123' --header 'Content-Type:application/json'
|
|
705
|
+
|
|
706
|
+
# 使用 POST 请求体渲染
|
|
707
|
+
xlsx-cli render template.xlsx -d 'https://api.example.com/api/query' --body '{"query":"SELECT * FROM users"}' --header 'Content-Type:application/json'
|
|
708
|
+
|
|
709
|
+
# 通过 header 指定 POST 方法
|
|
710
|
+
xlsx-cli render template.xlsx -d 'https://api.example.com/api/create' --body '{"name":"测试"}' --header 'Content-Type:application/json' --header 'method:POST'
|
|
711
|
+
|
|
701
712
|
# 完整示例
|
|
702
713
|
xlsx-cli render template.xlsx -c -n Sheet1 -s ./output/ -d './data.json'
|
|
703
714
|
```
|
|
@@ -707,6 +718,70 @@ xlsx-cli render template.xlsx -c -n Sheet1 -s ./output/ -d './data.json'
|
|
|
707
718
|
- **本地文件**: `.json` 文件的路径(相对或绝对)
|
|
708
719
|
- **远程 URL**: 返回 JSON 的 HTTP/HTTPS URL
|
|
709
720
|
|
|
721
|
+
**HTTP 请求选项(用于远程 URL):**
|
|
722
|
+
- **请求头**: 使用 `--header` 添加自定义 HTTP 请求头(可指定多次)
|
|
723
|
+
- 格式:`--header 'Key:Value'`
|
|
724
|
+
- 示例:`--header 'Authorization:Bearer token123' --header 'Content-Type:application/json'`
|
|
725
|
+
- 特殊请求头:`method:POST` 可设置 HTTP 方法为 POST
|
|
726
|
+
- **请求体**: 使用 `--body` 发送请求体(通常用于 POST 请求)
|
|
727
|
+
- 格式:`--body '{"key":"value"}'`
|
|
728
|
+
- 提供请求体时自动使用 POST 方法
|
|
729
|
+
- **默认行为**: 无请求头的 GET 请求
|
|
730
|
+
|
|
731
|
+
**HTTP 请求示例:**
|
|
732
|
+
|
|
733
|
+
```bash
|
|
734
|
+
# 带自定义请求头的 GET 请求
|
|
735
|
+
xlsx-cli render template.xlsx \
|
|
736
|
+
-d 'https://api.example.com/data.json' \
|
|
737
|
+
--header 'Authorization:Bearer your-token' \
|
|
738
|
+
--header 'X-API-Key:api-key-123'
|
|
739
|
+
|
|
740
|
+
# 带 JSON 请求体的 POST 请求
|
|
741
|
+
xlsx-cli render template.xlsx \
|
|
742
|
+
-d 'https://api.example.com/api/query' \
|
|
743
|
+
--body '{"query":"SELECT * FROM users LIMIT 10"}' \
|
|
744
|
+
--header 'Content-Type:application/json'
|
|
745
|
+
|
|
746
|
+
# 通过 header 指定 POST 方法
|
|
747
|
+
xlsx-cli render template.xlsx \
|
|
748
|
+
-d 'https://api.example.com/api/create' \
|
|
749
|
+
--body '{"name":"新记录","value":100}' \
|
|
750
|
+
--header 'Content-Type:application/json' \
|
|
751
|
+
--header 'method:POST'
|
|
752
|
+
|
|
753
|
+
# 复杂示例:带认证和查询请求体
|
|
754
|
+
xlsx-cli render template.xlsx \
|
|
755
|
+
-d 'https://api.example.com/v1/export' \
|
|
756
|
+
--header 'Authorization:Bearer eyJhbGc...' \
|
|
757
|
+
--header 'Content-Type:application/json' \
|
|
758
|
+
--body '{"format":"xlsx","filter":{"status":"active"}}' \
|
|
759
|
+
-c -n Sheet1 -s ./output/
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
**HTTP 请求详细说明:**
|
|
763
|
+
|
|
764
|
+
1. **HTTP 方法确定**:
|
|
765
|
+
- 默认:`GET`
|
|
766
|
+
- 使用 `--body`:自动变为 `POST`
|
|
767
|
+
- 使用 `method:POST` 请求头:显式设置为 `POST`
|
|
768
|
+
- 使用 `method:GET` 请求头:显式设置为 `GET`
|
|
769
|
+
|
|
770
|
+
2. **请求头格式**:
|
|
771
|
+
- 请求头按 `Key:Value` 格式解析
|
|
772
|
+
- 可以使用多个 `--header` 选项
|
|
773
|
+
- 示例:`--header 'Accept:application/json' --header 'User-Agent:MyApp/1.0'`
|
|
774
|
+
|
|
775
|
+
3. **错误处理**:
|
|
776
|
+
- 非 200 状态码返回 `undefined` 并显示错误消息
|
|
777
|
+
- 网络错误会被捕获并以红色显示
|
|
778
|
+
- 缺少 `node-fetch`(Node.js < 18)会显示错误消息
|
|
779
|
+
|
|
780
|
+
4. **支持的数据格式**:
|
|
781
|
+
- JSON 对象:`{"key":"value"}`
|
|
782
|
+
- JSON 数组:`[{"id":1},{"id":2}]`
|
|
783
|
+
- 嵌套结构:`{"user":{"name":"张三","age":30}}`
|
|
784
|
+
|
|
710
785
|
**输出:**
|
|
711
786
|
- 渲染后的 Excel 文件保存为 `<文件名>_<时间戳>.xlsx`
|
|
712
787
|
- 检查工作表是否存在
|
package/dist/bin.js
CHANGED
|
@@ -4289,11 +4289,9 @@ var XlsxRender = class _XlsxRender extends Workbook {
|
|
|
4289
4289
|
|
|
4290
4290
|
// src/bin-helpers.ts
|
|
4291
4291
|
var path2 = __toESM(require("path"));
|
|
4292
|
-
var url = __toESM(require("url"));
|
|
4293
4292
|
var import_node_fs = require("fs");
|
|
4294
4293
|
var fs3 = __toESM(require("fs/promises"));
|
|
4295
4294
|
var import_chalk = __toESM(require("chalk"));
|
|
4296
|
-
var import_meta = {};
|
|
4297
4295
|
function generateOutputFilename(inputFile) {
|
|
4298
4296
|
const basename3 = path2.basename(inputFile, path2.extname(inputFile));
|
|
4299
4297
|
const timestamp = Date.now();
|
|
@@ -4304,42 +4302,84 @@ async function resolveFilePath(filePath) {
|
|
|
4304
4302
|
if ((0, import_node_fs.existsSync)(resolvedPath)) {
|
|
4305
4303
|
return resolvedPath;
|
|
4306
4304
|
}
|
|
4307
|
-
const scriptDir = path2.dirname(
|
|
4305
|
+
const scriptDir = path2.dirname(process.cwd());
|
|
4308
4306
|
const relativePath = path2.resolve(scriptDir, filePath);
|
|
4309
4307
|
if ((0, import_node_fs.existsSync)(relativePath)) {
|
|
4310
4308
|
return relativePath;
|
|
4311
4309
|
}
|
|
4312
4310
|
throw new Error(`File not found: ${filePath}`);
|
|
4313
4311
|
}
|
|
4314
|
-
|
|
4312
|
+
function isValidJSON(str) {
|
|
4313
|
+
const trimmed = str.trim();
|
|
4314
|
+
if (!trimmed) return false;
|
|
4315
|
+
const firstChar = trimmed[0];
|
|
4316
|
+
const lastChar = trimmed.charAt(trimmed.length - 1);
|
|
4317
|
+
const validStartEndPairs = [
|
|
4318
|
+
["{", "}"],
|
|
4319
|
+
["[", "]"],
|
|
4320
|
+
['"', '"']
|
|
4321
|
+
];
|
|
4322
|
+
const hasValidFormat = validStartEndPairs.some(
|
|
4323
|
+
([start, end]) => firstChar === start && lastChar === end
|
|
4324
|
+
);
|
|
4325
|
+
if (!hasValidFormat && !["true", "false", "null"].includes(trimmed) && !/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(trimmed)) {
|
|
4326
|
+
return false;
|
|
4327
|
+
}
|
|
4328
|
+
try {
|
|
4329
|
+
JSON.parse(str);
|
|
4330
|
+
return true;
|
|
4331
|
+
} catch {
|
|
4332
|
+
return false;
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
async function parseRenderData(dataOption, httpHeaders, body) {
|
|
4315
4336
|
if (!dataOption) {
|
|
4316
4337
|
return {};
|
|
4317
4338
|
}
|
|
4318
4339
|
try {
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
let fetch;
|
|
4340
|
+
if (isValidJSON(dataOption)) {
|
|
4341
|
+
return JSON.parse(dataOption);
|
|
4342
|
+
}
|
|
4343
|
+
if (dataOption.startsWith("http://") || dataOption.startsWith("https://")) {
|
|
4344
|
+
let fetch;
|
|
4345
|
+
try {
|
|
4346
|
+
fetch = globalThis.fetch;
|
|
4347
|
+
} catch (e3) {
|
|
4328
4348
|
try {
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
} catch (e4) {
|
|
4335
|
-
throw new Error("Remote URLs require Node.js 18+ or node-fetch package");
|
|
4336
|
-
}
|
|
4349
|
+
const nodeFetch = await import("node-fetch");
|
|
4350
|
+
fetch = nodeFetch.default;
|
|
4351
|
+
} catch (e4) {
|
|
4352
|
+
console.error(import_chalk.default.red("Remote URLs require Node.js 18+ or node-fetch package"));
|
|
4353
|
+
return void 0;
|
|
4337
4354
|
}
|
|
4338
|
-
const response = await fetch(dataOption);
|
|
4339
|
-
return response.json();
|
|
4340
4355
|
}
|
|
4341
|
-
|
|
4356
|
+
const headers = new Headers();
|
|
4357
|
+
httpHeaders?.forEach((header) => {
|
|
4358
|
+
const [key, value] = header.split(":");
|
|
4359
|
+
headers.append(key, value);
|
|
4360
|
+
});
|
|
4361
|
+
const reqOpts = {
|
|
4362
|
+
headers,
|
|
4363
|
+
method: headers.get(`method`) ? headers.get(`method`).toUpperCase() : "GET"
|
|
4364
|
+
};
|
|
4365
|
+
if (body !== void 0 && body !== "") {
|
|
4366
|
+
reqOpts.body = body;
|
|
4367
|
+
}
|
|
4368
|
+
console.log(import_chalk.default.gray(`Fetching data from: ${dataOption}`));
|
|
4369
|
+
console.log(import_chalk.default.gray(`request.init: ${JSON.stringify(reqOpts)}`));
|
|
4370
|
+
const response = await fetch(dataOption, reqOpts);
|
|
4371
|
+
if (response.status !== 200) {
|
|
4372
|
+
console.error(import_chalk.default.red(`Failed to fetch data from: ${dataOption}, status: ${response.status},${response.text()}`));
|
|
4373
|
+
return void 0;
|
|
4374
|
+
}
|
|
4375
|
+
return response.json();
|
|
4342
4376
|
}
|
|
4377
|
+
const filePath = await resolveFilePath(dataOption);
|
|
4378
|
+
const fileContent = await fs3.readFile(filePath, "utf-8");
|
|
4379
|
+
return JSON.parse(fileContent);
|
|
4380
|
+
} catch (e) {
|
|
4381
|
+
console.error(import_chalk.default.red(`Failed to parse render data from: ${dataOption}`));
|
|
4382
|
+
return void 0;
|
|
4343
4383
|
}
|
|
4344
4384
|
}
|
|
4345
4385
|
function checkSheetAndPlaceholders(xlsx, sheetName) {
|
|
@@ -4349,43 +4389,70 @@ function checkSheetAndPlaceholders(xlsx, sheetName) {
|
|
|
4349
4389
|
throw new Error(`Sheet "${sheetName}" not found in Excel file`);
|
|
4350
4390
|
}
|
|
4351
4391
|
}
|
|
4352
|
-
async function addRuleToSheet(
|
|
4353
|
-
const workbook = await loadWorkbook(xlsxBuffer);
|
|
4392
|
+
async function addRuleToSheet(workbook, ruleType, ruleExpr, sheetName = "export_metadata.config") {
|
|
4354
4393
|
let worksheet = workbook.getWorksheet(sheetName);
|
|
4355
4394
|
if (!worksheet) {
|
|
4356
4395
|
worksheet = workbook.addWorksheet(sheetName);
|
|
4357
4396
|
console.log(import_chalk.default.gray(`Created new sheet: ${sheetName}`));
|
|
4358
4397
|
}
|
|
4359
4398
|
let startRow = 1;
|
|
4360
|
-
let currentRow =
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
let col = 2;
|
|
4370
|
-
while (col <= 4) {
|
|
4371
|
-
const ruleCell = row.getCell(col);
|
|
4372
|
-
if (ruleCell.value) {
|
|
4373
|
-
columnCount++;
|
|
4374
|
-
}
|
|
4375
|
-
col++;
|
|
4399
|
+
let currentRow = NaN;
|
|
4400
|
+
if (worksheet.rowCount > 0) {
|
|
4401
|
+
let stop = false;
|
|
4402
|
+
worksheet.eachRow((row, rowNumber) => {
|
|
4403
|
+
let columnCount = 0;
|
|
4404
|
+
const cell = row.getCell(1);
|
|
4405
|
+
const cellValue = cell.value;
|
|
4406
|
+
if (!cellValue || stop) {
|
|
4407
|
+
return;
|
|
4376
4408
|
}
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4409
|
+
const value = toCellValue(cellValue).trim();
|
|
4410
|
+
if (value.toLowerCase() === ruleType.toLowerCase()) {
|
|
4411
|
+
for (let col = 2; col < 5; col++) {
|
|
4412
|
+
const ruleCell2 = row.getCell(col);
|
|
4413
|
+
if (ruleCell2.value) {
|
|
4414
|
+
columnCount++;
|
|
4415
|
+
} else {
|
|
4416
|
+
break;
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
if (columnCount < 3) {
|
|
4420
|
+
currentRow = rowNumber;
|
|
4421
|
+
startRow = rowNumber;
|
|
4422
|
+
} else {
|
|
4423
|
+
currentRow = rowNumber + 1;
|
|
4424
|
+
}
|
|
4425
|
+
const cell2 = worksheet.getRow(currentRow).getCell(1);
|
|
4426
|
+
if (cell2 === void 0 || cell2.value === null || cell2.value === void 0) {
|
|
4427
|
+
stop = true;
|
|
4428
|
+
return;
|
|
4429
|
+
}
|
|
4430
|
+
if (toCellValue(cell2.value).toLowerCase() !== ruleType.toLowerCase()) {
|
|
4431
|
+
worksheet.insertRow(currentRow, []);
|
|
4432
|
+
currentRow = currentRow + 1;
|
|
4433
|
+
stop = true;
|
|
4434
|
+
return;
|
|
4435
|
+
}
|
|
4436
|
+
if (toCellValue(cell2.value).toLowerCase() === ruleType.toLowerCase() && columnCount < 3) {
|
|
4437
|
+
stop = true;
|
|
4438
|
+
return;
|
|
4439
|
+
}
|
|
4382
4440
|
}
|
|
4383
|
-
}
|
|
4384
|
-
}
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4443
|
+
if (isNaN(currentRow)) {
|
|
4444
|
+
currentRow = worksheet.rowCount + 1;
|
|
4445
|
+
worksheet.insertRow(currentRow, []);
|
|
4446
|
+
}
|
|
4385
4447
|
if (startRow === 1 && currentRow === 1 && worksheet.rowCount === 0) {
|
|
4386
4448
|
currentRow = 1;
|
|
4387
4449
|
}
|
|
4388
4450
|
let targetRow = worksheet.getRow(currentRow);
|
|
4451
|
+
let lastCell = targetRow.getCell(4);
|
|
4452
|
+
if (lastCell !== void 0 && lastCell.value !== void 0 && lastCell.value !== null && lastCell.value !== "") {
|
|
4453
|
+
currentRow = worksheet.rowCount + 1;
|
|
4454
|
+
targetRow = worksheet.insertRow(currentRow, []);
|
|
4455
|
+
}
|
|
4389
4456
|
if (!targetRow.getCell(1).value) {
|
|
4390
4457
|
const typeCell = targetRow.getCell(1);
|
|
4391
4458
|
typeCell.value = ruleType;
|
|
@@ -4395,23 +4462,21 @@ async function addRuleToSheet(xlsxBuffer, ruleType, ruleExpr, sheetName = "expor
|
|
|
4395
4462
|
let ruleCol = 2;
|
|
4396
4463
|
while (ruleCol <= 4) {
|
|
4397
4464
|
const existingCell = targetRow.getCell(ruleCol);
|
|
4398
|
-
if (
|
|
4465
|
+
if (existingCell === void 0 || (existingCell.value === void 0 || existingCell.value === null || existingCell.value === "")) {
|
|
4399
4466
|
break;
|
|
4400
4467
|
}
|
|
4401
4468
|
ruleCol++;
|
|
4402
4469
|
}
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
throw new Error(`Cannot add more than 4 rules for type: ${ruleType}`);
|
|
4470
|
+
const ruleCell = targetRow.getCell(ruleCol);
|
|
4471
|
+
ruleCell.value = ruleExpr;
|
|
4472
|
+
ruleCell.alignment = { vertical: "middle", horizontal: "center" };
|
|
4473
|
+
const column = worksheet.getColumn(ruleCol);
|
|
4474
|
+
column.width = Math.max(column.width || 10, ruleExpr.length + 2);
|
|
4475
|
+
if (currentRow === 1 && ruleCol === 2) {
|
|
4476
|
+
const column2 = worksheet.getColumn(1);
|
|
4477
|
+
column2.width = Math.max(column2.width || 10, ruleExpr.length + 2);
|
|
4412
4478
|
}
|
|
4413
|
-
|
|
4414
|
-
return Buffer.from(buffer);
|
|
4479
|
+
return workbook;
|
|
4415
4480
|
}
|
|
4416
4481
|
async function parseRulesFromFile(filePath) {
|
|
4417
4482
|
const resolvedPath = await resolveFilePath(filePath);
|
|
@@ -4430,8 +4495,17 @@ async function parseRulesFromFile(filePath) {
|
|
|
4430
4495
|
console.log(import_chalk.default.yellow(`\u26A0 Line ${i + 1}: Invalid format. Expected "<type> ruleExpr"`));
|
|
4431
4496
|
continue;
|
|
4432
4497
|
}
|
|
4498
|
+
let items = [];
|
|
4433
4499
|
const type = line.substring(0, spaceIndex).trim();
|
|
4434
|
-
const
|
|
4500
|
+
const values = line.substring(spaceIndex + 1).trim();
|
|
4501
|
+
if (values.indexOf(" ") >= 0) {
|
|
4502
|
+
items = values.split(" ");
|
|
4503
|
+
} else if (values.indexOf(" ") >= 0) {
|
|
4504
|
+
items = values.split(" ");
|
|
4505
|
+
} else {
|
|
4506
|
+
items = [values];
|
|
4507
|
+
}
|
|
4508
|
+
items = items.filter((item) => item.trim() !== "" && item.indexOf("=") > 0);
|
|
4435
4509
|
if (!type) {
|
|
4436
4510
|
console.log(import_chalk.default.yellow(`\u26A0 Line ${i + 1}: Invalid format. Expected "<type> ruleExpr"`));
|
|
4437
4511
|
continue;
|
|
@@ -4445,6 +4519,9 @@ async function parseRulesFromFile(filePath) {
|
|
|
4445
4519
|
}
|
|
4446
4520
|
for (const rule of items) {
|
|
4447
4521
|
let str = rule.trim();
|
|
4522
|
+
if (str === void 0 || str === null || str === "" || str === " ") {
|
|
4523
|
+
continue;
|
|
4524
|
+
}
|
|
4448
4525
|
let key = `${type}:${str}`;
|
|
4449
4526
|
if (validRulesSet.has(key)) {
|
|
4450
4527
|
console.log(import_chalk.default.yellow(`\u26A0 Line ${i + 1}: Duplicate rule "${str}"`));
|
|
@@ -4460,30 +4537,38 @@ async function parseRulesFromFile(filePath) {
|
|
|
4460
4537
|
return rules;
|
|
4461
4538
|
}
|
|
4462
4539
|
async function addMultipleRulesToSheet(xlsxBuffer, rules) {
|
|
4463
|
-
let
|
|
4540
|
+
let workbook = await loadWorkbook(xlsxBuffer);
|
|
4464
4541
|
for (const { type, rule } of rules) {
|
|
4465
|
-
|
|
4466
|
-
buffer = await addRuleToSheet(buffer, type, rule);
|
|
4542
|
+
workbook = await addRuleToSheet(workbook, type, rule);
|
|
4467
4543
|
}
|
|
4468
|
-
|
|
4544
|
+
const buffer = await workbook.xlsx.writeBuffer();
|
|
4545
|
+
return Buffer.from(buffer);
|
|
4469
4546
|
}
|
|
4470
4547
|
|
|
4471
4548
|
// src/bin.ts
|
|
4472
4549
|
async function main() {
|
|
4473
|
-
let version
|
|
4550
|
+
let version;
|
|
4474
4551
|
try {
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
];
|
|
4479
|
-
for (const packagePath of possiblePaths) {
|
|
4480
|
-
if ((0, import_node_fs2.existsSync)(packagePath)) {
|
|
4481
|
-
const packageJson = JSON.parse(await fs4.readFile(packagePath, "utf-8"));
|
|
4482
|
-
version = packageJson.version;
|
|
4483
|
-
break;
|
|
4484
|
-
}
|
|
4552
|
+
version = __VERSION__;
|
|
4553
|
+
if (version.startsWith('"') && version.endsWith('"')) {
|
|
4554
|
+
version = version.slice(1, -1);
|
|
4485
4555
|
}
|
|
4486
4556
|
} catch (e) {
|
|
4557
|
+
version = "1.0.0";
|
|
4558
|
+
try {
|
|
4559
|
+
const possiblePaths = [
|
|
4560
|
+
path3.join(process.cwd(), "package.json"),
|
|
4561
|
+
path3.join(process.cwd(), "..", "package.json")
|
|
4562
|
+
];
|
|
4563
|
+
for (const packagePath of possiblePaths) {
|
|
4564
|
+
if ((0, import_node_fs2.existsSync)(packagePath)) {
|
|
4565
|
+
const packageJson = JSON.parse(await fs4.readFile(packagePath, "utf-8"));
|
|
4566
|
+
version = packageJson.version;
|
|
4567
|
+
break;
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
} catch (e2) {
|
|
4571
|
+
}
|
|
4487
4572
|
}
|
|
4488
4573
|
const envPath = path3.join(process.cwd(), ".env");
|
|
4489
4574
|
if ((0, import_node_fs2.existsSync)(envPath)) {
|
|
@@ -4523,7 +4608,7 @@ async function main() {
|
|
|
4523
4608
|
process.exit(1);
|
|
4524
4609
|
}
|
|
4525
4610
|
});
|
|
4526
|
-
program.command("render").argument("<string>", "xlsx file path").option("-c,--compile", "auto compile flag", false).option("-n,--sheet-name <string>", "render xlsx sheet name when xlsx has multiple sheets").option("-s,--save <string>", "save render xlsx file to user dir").option("-d,--data <string>", "render xlsx file data from").action(async (xlsxFile, options) => {
|
|
4611
|
+
program.command("render").argument("<string>", "xlsx file path").option("-c,--compile", "auto compile flag", false).option("-n,--sheet-name <string>", "render xlsx sheet name when xlsx has multiple sheets").option("-s,--save <string>", "save render xlsx file to user dir").option("-d,--data <string>", "render xlsx file data from").option("--header <string>", "call remote http json data with header", []).option("--body <string>", "call remote http json request with body").action(async (xlsxFile, options) => {
|
|
4527
4612
|
try {
|
|
4528
4613
|
console.log(import_chalk2.default.green("\u{1F4C4} Rendering Excel template..."));
|
|
4529
4614
|
const filePath = await resolveFilePath(xlsxFile);
|
|
@@ -4535,7 +4620,10 @@ async function main() {
|
|
|
4535
4620
|
console.log(import_chalk2.default.gray(`Target sheet: ${sheetName}`));
|
|
4536
4621
|
checkSheetAndPlaceholders(xlsx, sheetName);
|
|
4537
4622
|
console.log(import_chalk2.default.gray("Sheet validation passed"));
|
|
4538
|
-
const renderData = await parseRenderData(options.data);
|
|
4623
|
+
const renderData = await parseRenderData(options.data, options.header, options.body);
|
|
4624
|
+
if (renderData === void 0) {
|
|
4625
|
+
process.exit(1);
|
|
4626
|
+
}
|
|
4539
4627
|
if (Object.keys(renderData).length > 0) {
|
|
4540
4628
|
console.log(import_chalk2.default.gray(`Render data loaded with ${Object.keys(renderData).length} keys`));
|
|
4541
4629
|
}
|