@vdhewei/xlsx-template-lib 1.6.7 → 1.6.11
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 +88 -35
- package/dist/bin.js.map +1 -1
- package/dist/bin.mjs +88 -35
- package/dist/bin.mjs.map +1 -1
- package/package.json +3 -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
|
@@ -4309,35 +4309,77 @@ async function resolveFilePath(filePath) {
|
|
|
4309
4309
|
}
|
|
4310
4310
|
throw new Error(`File not found: ${filePath}`);
|
|
4311
4311
|
}
|
|
4312
|
-
|
|
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) {
|
|
4313
4336
|
if (!dataOption) {
|
|
4314
4337
|
return {};
|
|
4315
4338
|
}
|
|
4316
4339
|
try {
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
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) {
|
|
4326
4348
|
try {
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
} catch (e4) {
|
|
4333
|
-
throw new Error("Remote URLs require Node.js 18+ or node-fetch package");
|
|
4334
|
-
}
|
|
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;
|
|
4335
4354
|
}
|
|
4336
|
-
const response = await fetch(dataOption);
|
|
4337
|
-
return response.json();
|
|
4338
4355
|
}
|
|
4339
|
-
|
|
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();
|
|
4340
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;
|
|
4341
4383
|
}
|
|
4342
4384
|
}
|
|
4343
4385
|
function checkSheetAndPlaceholders(xlsx, sheetName) {
|
|
@@ -4505,20 +4547,28 @@ async function addMultipleRulesToSheet(xlsxBuffer, rules) {
|
|
|
4505
4547
|
|
|
4506
4548
|
// src/bin.ts
|
|
4507
4549
|
async function main() {
|
|
4508
|
-
let version
|
|
4550
|
+
let version;
|
|
4509
4551
|
try {
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
];
|
|
4514
|
-
for (const packagePath of possiblePaths) {
|
|
4515
|
-
if ((0, import_node_fs2.existsSync)(packagePath)) {
|
|
4516
|
-
const packageJson = JSON.parse(await fs4.readFile(packagePath, "utf-8"));
|
|
4517
|
-
version = packageJson.version;
|
|
4518
|
-
break;
|
|
4519
|
-
}
|
|
4552
|
+
version = __VERSION__;
|
|
4553
|
+
if (version.startsWith('"') && version.endsWith('"')) {
|
|
4554
|
+
version = version.slice(1, -1);
|
|
4520
4555
|
}
|
|
4521
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
|
+
}
|
|
4522
4572
|
}
|
|
4523
4573
|
const envPath = path3.join(process.cwd(), ".env");
|
|
4524
4574
|
if ((0, import_node_fs2.existsSync)(envPath)) {
|
|
@@ -4558,7 +4608,7 @@ async function main() {
|
|
|
4558
4608
|
process.exit(1);
|
|
4559
4609
|
}
|
|
4560
4610
|
});
|
|
4561
|
-
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").option("-r,--remove", "remove configure rules sheet", false).action(async (xlsxFile, options) => {
|
|
4562
4612
|
try {
|
|
4563
4613
|
console.log(import_chalk2.default.green("\u{1F4C4} Rendering Excel template..."));
|
|
4564
4614
|
const filePath = await resolveFilePath(xlsxFile);
|
|
@@ -4570,7 +4620,10 @@ async function main() {
|
|
|
4570
4620
|
console.log(import_chalk2.default.gray(`Target sheet: ${sheetName}`));
|
|
4571
4621
|
checkSheetAndPlaceholders(xlsx, sheetName);
|
|
4572
4622
|
console.log(import_chalk2.default.gray("Sheet validation passed"));
|
|
4573
|
-
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
|
+
}
|
|
4574
4627
|
if (Object.keys(renderData).length > 0) {
|
|
4575
4628
|
console.log(import_chalk2.default.gray(`Render data loaded with ${Object.keys(renderData).length} keys`));
|
|
4576
4629
|
}
|
|
@@ -4579,7 +4632,7 @@ async function main() {
|
|
|
4579
4632
|
const ruleSheetName = options.sheetName || compileRuleSheetName;
|
|
4580
4633
|
const opts = new RuleMapOptions();
|
|
4581
4634
|
opts.sheetName = ruleSheetName;
|
|
4582
|
-
opts.remove = false;
|
|
4635
|
+
opts.remove = options.remove || false;
|
|
4583
4636
|
const compiledResult = await compileAll(buffer, opts);
|
|
4584
4637
|
buffer = Buffer.from(compiledResult);
|
|
4585
4638
|
xlsx = await XlsxRender.create(buffer);
|