@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 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
- async function parseRenderData(dataOption) {
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
- return JSON.parse(dataOption);
4318
- } catch (e) {
4319
- try {
4320
- const filePath = await resolveFilePath(dataOption);
4321
- const fileContent = await fs3.readFile(filePath, "utf-8");
4322
- return JSON.parse(fileContent);
4323
- } catch (e2) {
4324
- if (dataOption.startsWith("http://") || dataOption.startsWith("https://")) {
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
- fetch = globalThis.fetch;
4328
- } catch (e3) {
4329
- try {
4330
- const nodeFetch = await import("node-fetch");
4331
- fetch = nodeFetch.default;
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
- throw new Error(`Failed to parse render data from: ${dataOption}`);
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 = "1.0.0";
4550
+ let version;
4509
4551
  try {
4510
- const possiblePaths = [
4511
- path3.join(process.cwd(), "package.json"),
4512
- path3.join(process.cwd(), "..", "package.json")
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);