@8ms/helpers 3.0.22 → 3.0.24
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/dist/_virtual/_rolldown/runtime.cjs +1 -1
- package/dist/adverity/adverity.d.cts +0 -1
- package/dist/adverity/adverity.d.mts +0 -1
- package/dist/adverity/server/getJobs.d.cts +2 -0
- package/dist/adverity/server/getJobs.d.mts +2 -0
- package/dist/api/ApiResponseClass.d.cts +1 -6
- package/dist/api/ApiResponseClass.d.mts +1 -6
- package/dist/api/api.d.cts +0 -1
- package/dist/api/api.d.mts +0 -1
- package/dist/api/deleteRequest.d.cts +0 -1
- package/dist/api/deleteRequest.d.mts +0 -1
- package/dist/api/functions.d.cts +0 -1
- package/dist/api/functions.d.mts +0 -1
- package/dist/api/get.d.cts +0 -1
- package/dist/api/get.d.mts +0 -1
- package/dist/api/post.d.cts +0 -1
- package/dist/api/post.d.mts +0 -1
- package/dist/atInternet/atInternet.d.cts +0 -1
- package/dist/atInternet/atInternet.d.mts +0 -1
- package/dist/aws/cloudWatchLog/server/AwsCloudWatchLog.d.cts +0 -1
- package/dist/aws/cloudWatchLog/server/AwsCloudWatchLog.d.mts +0 -1
- package/dist/aws/cloudWatchLog/server/logEvent.d.cts +1 -0
- package/dist/aws/cloudWatchLog/server/logEvent.d.mts +1 -0
- package/dist/aws/cloudWatchMetric/server/AwsCloudWatchMetric.d.cts +0 -1
- package/dist/aws/cloudWatchMetric/server/AwsCloudWatchMetric.d.mts +0 -1
- package/dist/aws/cloudWatchMetric/server/logMetric.d.cts +1 -1
- package/dist/aws/cloudWatchMetric/server/logMetric.d.mts +1 -1
- package/dist/aws/ec2/server/AwsEc2.d.cts +0 -1
- package/dist/aws/ec2/server/AwsEc2.d.mts +0 -1
- package/dist/aws/ecs/server/AwsEcs.d.cts +0 -1
- package/dist/aws/ecs/server/AwsEcs.d.mts +0 -1
- package/dist/aws/glue/server/AwsGlue.cjs +2 -1
- package/dist/aws/glue/server/AwsGlue.d.cts +0 -1
- package/dist/aws/glue/server/AwsGlue.d.mts +0 -1
- package/dist/aws/glue/server/AwsGlue.mjs +2 -1
- package/dist/aws/lambda/server/AwsLambda.cjs +10 -8
- package/dist/aws/lambda/server/AwsLambda.d.cts +0 -1
- package/dist/aws/lambda/server/AwsLambda.d.mts +0 -1
- package/dist/aws/lambda/server/AwsLambda.mjs +10 -8
- package/dist/aws/s3/server/AwsS3.cjs +3 -2
- package/dist/aws/s3/server/AwsS3.d.cts +1 -1
- package/dist/aws/s3/server/AwsS3.d.mts +1 -1
- package/dist/aws/s3/server/AwsS3.mjs +3 -2
- package/dist/aws/s3/server/payload.d.cts +0 -1
- package/dist/aws/s3/server/payload.d.mts +0 -1
- package/dist/aws/ses/server/AwsSes.cjs +4 -11
- package/dist/aws/ses/server/AwsSes.d.cts +3 -4
- package/dist/aws/ses/server/AwsSes.d.mts +3 -4
- package/dist/aws/ses/server/AwsSes.mjs +4 -11
- package/dist/aws/ses/server/SimpleEmail.cjs +10 -11
- package/dist/aws/ses/server/SimpleEmail.d.cts +3 -12
- package/dist/aws/ses/server/SimpleEmail.d.mts +3 -12
- package/dist/aws/ses/server/SimpleEmail.mjs +10 -11
- package/dist/aws/sqs/server/AwsSqs.d.cts +0 -1
- package/dist/aws/sqs/server/AwsSqs.d.mts +0 -1
- package/dist/aws/ssm/server/AwsSsm.cjs +5 -4
- package/dist/aws/ssm/server/AwsSsm.d.cts +0 -1
- package/dist/aws/ssm/server/AwsSsm.d.mts +0 -1
- package/dist/aws/ssm/server/AwsSsm.mjs +5 -4
- package/dist/brightData/serpApi/server/buildGoogleSerpUrl.cjs +0 -2
- package/dist/brightData/serpApi/server/buildGoogleSerpUrl.mjs +0 -2
- package/dist/brightData/serpApi/server/buildGoogleTrendsUrl.cjs +0 -1
- package/dist/brightData/serpApi/server/buildGoogleTrendsUrl.mjs +0 -1
- package/dist/brightData/serpApi/server/getAsyncRequestId.d.cts +1 -0
- package/dist/brightData/serpApi/server/getAsyncRequestId.d.mts +1 -0
- package/dist/brightData/serpApi/server/getAsyncResults.cjs +7 -4
- package/dist/brightData/serpApi/server/getAsyncResults.d.cts +1 -1
- package/dist/brightData/serpApi/server/getAsyncResults.d.mts +1 -1
- package/dist/brightData/serpApi/server/getAsyncResults.mjs +7 -4
- package/dist/brightData/serpApi/server/getRealtime.d.cts +0 -1
- package/dist/brightData/serpApi/server/getRealtime.d.mts +0 -1
- package/dist/brightData/webScraperIde/server/getBatch.d.cts +0 -1
- package/dist/brightData/webScraperIde/server/getBatch.d.mts +0 -1
- package/dist/brightData/webScraperIde/server/getRealtime.cjs +10 -6
- package/dist/brightData/webScraperIde/server/getRealtime.d.cts +0 -1
- package/dist/brightData/webScraperIde/server/getRealtime.d.mts +0 -1
- package/dist/brightData/webScraperIde/server/getRealtime.mjs +10 -6
- package/dist/date/calculation.cjs +27 -24
- package/dist/date/calculation.d.cts +0 -1
- package/dist/date/calculation.d.mts +0 -1
- package/dist/date/calculation.mjs +27 -24
- package/dist/date/date.d.cts +0 -1
- package/dist/date/date.d.mts +0 -1
- package/dist/date/financialYear.d.cts +0 -1
- package/dist/date/financialYear.d.mts +0 -1
- package/dist/date/format.d.cts +0 -1
- package/dist/date/format.d.mts +0 -1
- package/dist/drizzle/server/Drizzle.d.cts +0 -1
- package/dist/drizzle/server/Drizzle.d.mts +0 -1
- package/dist/emsApi/awsTs/get.cjs +2 -1
- package/dist/emsApi/awsTs/get.d.cts +1 -0
- package/dist/emsApi/awsTs/get.d.mts +1 -0
- package/dist/emsApi/awsTs/get.mjs +2 -1
- package/dist/emsApi/awsTs/post.cjs +2 -1
- package/dist/emsApi/awsTs/post.d.cts +1 -0
- package/dist/emsApi/awsTs/post.d.mts +1 -0
- package/dist/emsApi/awsTs/post.mjs +2 -1
- package/dist/emsApi/vercel/get.cjs +2 -1
- package/dist/emsApi/vercel/get.d.cts +1 -0
- package/dist/emsApi/vercel/get.d.mts +1 -0
- package/dist/emsApi/vercel/get.mjs +2 -1
- package/dist/emsApi/vercel/post.cjs +2 -1
- package/dist/emsApi/vercel/post.d.cts +1 -0
- package/dist/emsApi/vercel/post.d.mts +1 -0
- package/dist/emsApi/vercel/post.mjs +2 -1
- package/dist/eskimi/eskimi.d.cts +0 -1
- package/dist/eskimi/eskimi.d.mts +0 -1
- package/dist/googleAds/keywordPlanner/server/googleAdsKeywordPlanner.d.cts +0 -1
- package/dist/googleAds/keywordPlanner/server/googleAdsKeywordPlanner.d.mts +0 -1
- package/dist/googleAds/server/GoogleAds.d.cts +0 -1
- package/dist/googleAds/server/GoogleAds.d.mts +0 -1
- package/dist/googleCloud/bigquery/server/GoogleCloudBigquery.d.cts +0 -1
- package/dist/googleCloud/bigquery/server/GoogleCloudBigquery.d.mts +0 -1
- package/dist/googleCloud/bigquery/server/loadData.d.cts +1 -7
- package/dist/googleCloud/bigquery/server/loadData.d.mts +1 -7
- package/dist/googleCloud/sheets/server/GoogleCloudSheets.d.cts +1 -2
- package/dist/googleCloud/sheets/server/GoogleCloudSheets.d.mts +1 -2
- package/dist/googleCloud/storage/server/GoogleCloudStorage.d.cts +0 -1
- package/dist/googleCloud/storage/server/GoogleCloudStorage.d.mts +0 -1
- package/dist/googlePageSpeed/server/GooglePageSpeed.cjs +2 -1
- package/dist/googlePageSpeed/server/GooglePageSpeed.d.cts +0 -1
- package/dist/googlePageSpeed/server/GooglePageSpeed.d.mts +0 -1
- package/dist/googlePageSpeed/server/GooglePageSpeed.mjs +2 -1
- package/dist/googleSearchIncidents/server/googleSearchIncidents.d.cts +0 -1
- package/dist/googleSearchIncidents/server/googleSearchIncidents.d.mts +0 -1
- package/dist/greenDomain/server/greenDomain.cjs +2 -1
- package/dist/greenDomain/server/greenDomain.d.cts +0 -1
- package/dist/greenDomain/server/greenDomain.d.mts +0 -1
- package/dist/greenDomain/server/greenDomain.mjs +2 -1
- package/dist/littleWarden/server/LittleWarden.cjs +2 -1
- package/dist/littleWarden/server/LittleWarden.d.cts +0 -1
- package/dist/littleWarden/server/LittleWarden.d.mts +0 -1
- package/dist/littleWarden/server/LittleWarden.mjs +2 -1
- package/dist/littleWarden/server/littleWardenAttrs.d.cts +3 -10
- package/dist/littleWarden/server/littleWardenAttrs.d.mts +3 -10
- package/dist/lumar/api/server/buildRequest.cjs +0 -1
- package/dist/lumar/api/server/buildRequest.d.cts +1 -0
- package/dist/lumar/api/server/buildRequest.d.mts +1 -0
- package/dist/lumar/api/server/buildRequest.mjs +0 -1
- package/dist/lumar/api/server/getData.d.cts +1 -0
- package/dist/lumar/api/server/getData.d.mts +1 -0
- package/dist/lumar/graphql/server/getData.d.cts +1 -0
- package/dist/lumar/graphql/server/getData.d.mts +1 -0
- package/dist/lumar/graphql/server/queries/getCrawls.d.cts +1 -4
- package/dist/lumar/graphql/server/queries/getCrawls.d.mts +1 -4
- package/dist/lumar/graphql/server/queries/getReportDifferences.d.cts +1 -5
- package/dist/lumar/graphql/server/queries/getReportDifferences.d.mts +1 -5
- package/dist/lumar/graphql/server/queries/getRows.d.cts +1 -11
- package/dist/lumar/graphql/server/queries/getRows.d.mts +1 -11
- package/dist/lumar/graphql/server/queries/getTotals.d.cts +1 -7
- package/dist/lumar/graphql/server/queries/getTotals.d.mts +1 -7
- package/dist/myTarget/server/myTarget.d.cts +0 -1
- package/dist/myTarget/server/myTarget.d.mts +0 -1
- package/dist/nextAuth/nextAuth.d.cts +0 -1
- package/dist/nextAuth/nextAuth.d.mts +0 -1
- package/dist/nextJs/client/LazyLoad.d.cts +0 -1
- package/dist/nextJs/client/LazyLoad.d.mts +0 -1
- package/dist/onePassword/server/OnePassword.d.cts +0 -1
- package/dist/onePassword/server/OnePassword.d.mts +0 -1
- package/dist/openAi/server/OpenAi.d.cts +0 -1
- package/dist/openAi/server/OpenAi.d.mts +0 -1
- package/dist/prisma/server/Prisma.d.cts +0 -1
- package/dist/prisma/server/Prisma.d.mts +0 -1
- package/dist/snapchat/server/Snapchat.cjs +3 -2
- package/dist/snapchat/server/Snapchat.d.cts +0 -1
- package/dist/snapchat/server/Snapchat.d.mts +0 -1
- package/dist/snapchat/server/Snapchat.mjs +3 -2
- package/dist/stream/server/sort.d.cts +0 -1
- package/dist/stream/server/sort.d.mts +0 -1
- package/dist/swr/client/swr.d.cts +1 -0
- package/dist/swr/client/swr.d.mts +1 -0
- package/dist/upTimeRobot/server/UpTimeRobot.d.cts +0 -1
- package/dist/upTimeRobot/server/UpTimeRobot.d.mts +0 -1
- package/dist/util/defaultTo.cjs +6 -3
- package/dist/util/defaultTo.mjs +6 -3
- package/dist/util/server/getBrotliCompressed.d.cts +0 -1
- package/dist/util/server/getBrotliCompressed.d.mts +0 -1
- package/dist/util/server/getBrotliDecompressed.d.cts +0 -1
- package/dist/util/server/getBrotliDecompressed.d.mts +0 -1
- package/dist/util/server/getGzipCompressed.d.cts +0 -1
- package/dist/util/server/getGzipCompressed.d.mts +0 -1
- package/dist/util/server/getGzipDecompressed.d.cts +0 -1
- package/dist/util/server/getGzipDecompressed.d.mts +0 -1
- package/dist/xlsx/cell.cjs +102 -0
- package/dist/xlsx/cell.d.cts +38 -0
- package/dist/xlsx/cell.d.mts +38 -0
- package/dist/xlsx/cell.mjs +97 -0
- package/dist/xlsx/column.cjs +37 -0
- package/dist/xlsx/column.d.cts +23 -0
- package/dist/xlsx/column.d.mts +23 -0
- package/dist/xlsx/column.mjs +35 -0
- package/dist/xlsx/createXlsx.cjs +138 -0
- package/dist/xlsx/createXlsx.d.cts +18 -0
- package/dist/xlsx/createXlsx.d.mts +18 -0
- package/dist/xlsx/createXlsx.mjs +136 -0
- package/dist/xlsx/index.cjs +18 -0
- package/dist/xlsx/index.d.cts +6 -0
- package/dist/xlsx/index.d.mts +6 -0
- package/dist/xlsx/index.mjs +6 -0
- package/dist/xlsx/modifyXlsx.cjs +125 -0
- package/dist/xlsx/modifyXlsx.d.cts +24 -0
- package/dist/xlsx/modifyXlsx.d.mts +24 -0
- package/dist/xlsx/modifyXlsx.mjs +124 -0
- package/dist/xlsx/sheet.cjs +36 -0
- package/dist/xlsx/sheet.d.cts +29 -0
- package/dist/xlsx/sheet.d.mts +29 -0
- package/dist/xlsx/sheet.mjs +34 -0
- package/package.json +103 -57
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { XlsxCellValue } from "./cell.mjs";
|
|
2
|
+
//#region src/xlsx/column.d.ts
|
|
3
|
+
type XlsxColumn = {
|
|
4
|
+
id: string;
|
|
5
|
+
displayName: string;
|
|
6
|
+
/** Column width in Excel character units. Derived from the content when omitted. */
|
|
7
|
+
width?: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Convert a 1-based column number to an Excel column letter string.
|
|
11
|
+
*
|
|
12
|
+
* Examples: 1 → "A", 2 → "B", 26 → "Z", 27 → "AA", 28 → "AB".
|
|
13
|
+
*/
|
|
14
|
+
declare const getColumLetter: (colNum: number) => string;
|
|
15
|
+
/**
|
|
16
|
+
* Width in Excel character units, taken from the widest value in the column
|
|
17
|
+
* (header included) and clamped. Heading lines are excluded on purpose: they sit
|
|
18
|
+
* in column A and overflow into the empty cells beside them, so letting them
|
|
19
|
+
* drive the width would stretch the first column for no benefit.
|
|
20
|
+
*/
|
|
21
|
+
declare const getColumnWidth: (column: XlsxColumn, data: Record<string, XlsxCellValue>[]) => number;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { XlsxColumn, getColumLetter, getColumnWidth };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getCellValue } from "./cell.mjs";
|
|
2
|
+
//#region src/xlsx/column.ts
|
|
3
|
+
/** Bounds for widths derived from content, so nothing is cramped or absurd. */
|
|
4
|
+
const COLUMN_WIDTH_MIN = 10;
|
|
5
|
+
const COLUMN_WIDTH_MAX = 60;
|
|
6
|
+
/**
|
|
7
|
+
* Convert a 1-based column number to an Excel column letter string.
|
|
8
|
+
*
|
|
9
|
+
* Examples: 1 → "A", 2 → "B", 26 → "Z", 27 → "AA", 28 → "AB".
|
|
10
|
+
*/
|
|
11
|
+
const getColumLetter = (colNum) => {
|
|
12
|
+
let result = "";
|
|
13
|
+
let remaining = colNum;
|
|
14
|
+
while (remaining > 0) {
|
|
15
|
+
remaining--;
|
|
16
|
+
result = String.fromCharCode(65 + remaining % 26) + result;
|
|
17
|
+
remaining = Math.floor(remaining / 26);
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Width in Excel character units, taken from the widest value in the column
|
|
23
|
+
* (header included) and clamped. Heading lines are excluded on purpose: they sit
|
|
24
|
+
* in column A and overflow into the empty cells beside them, so letting them
|
|
25
|
+
* drive the width would stretch the first column for no benefit.
|
|
26
|
+
*/
|
|
27
|
+
const getColumnWidth = (column, data) => {
|
|
28
|
+
if (column?.width) return column.width;
|
|
29
|
+
const longest = data.reduce((widest, row) => {
|
|
30
|
+
return Math.max(widest, String(getCellValue(row?.[column.id])).length);
|
|
31
|
+
}, column.displayName.length);
|
|
32
|
+
return Math.min(COLUMN_WIDTH_MAX, Math.max(COLUMN_WIDTH_MIN, longest + 2));
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { getColumLetter, getColumnWidth };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_xlsx_column = require("./column.cjs");
|
|
3
|
+
const require_xlsx_cell = require("./cell.cjs");
|
|
4
|
+
const require_xlsx_sheet = require("./sheet.cjs");
|
|
5
|
+
//#region src/xlsx/createXlsx.ts
|
|
6
|
+
/**
|
|
7
|
+
* Builds a complete .xlsx workbook from scratch, using jszip and hand-written
|
|
8
|
+
* OOXML rather than a spreadsheet library.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here touches `fs`, so it runs in the browser as happily as on the
|
|
11
|
+
* server — a page can turn data it has already loaded straight into a download.
|
|
12
|
+
*
|
|
13
|
+
* jszip is an optional peer dependency imported dynamically, so consumers that
|
|
14
|
+
* never build a workbook neither install it nor pay for it in their bundle.
|
|
15
|
+
*/
|
|
16
|
+
/** Style index of the bold row within the cellXfs list built by buildStylesXml. */
|
|
17
|
+
const HEADER_STYLE_INDEX = 1;
|
|
18
|
+
const XML_DECLARATION = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`;
|
|
19
|
+
const CONTENT_TYPE = {
|
|
20
|
+
SHEET: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
|
21
|
+
STYLES: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
|
22
|
+
WORKSHEET: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
|
|
23
|
+
};
|
|
24
|
+
const REL_TYPE = {
|
|
25
|
+
OFFICE_DOCUMENT: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
26
|
+
STYLES: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
27
|
+
WORKSHEET: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
|
28
|
+
};
|
|
29
|
+
const NAMESPACES = {
|
|
30
|
+
MAIN: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
31
|
+
RELATIONSHIP: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
32
|
+
PACKAGE_REL: "http://schemas.openxmlformats.org/package/2006/relationships",
|
|
33
|
+
CONTENT_TYPES: "http://schemas.openxmlformats.org/package/2006/content-types"
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Minimal but valid styles part. Excel requires at least one border, one
|
|
37
|
+
* cellStyleXf and two fills (index 0 "none", index 1 "gray125") even when
|
|
38
|
+
* nothing references them. cellXfs index 1 is the bold header.
|
|
39
|
+
*/
|
|
40
|
+
const buildStylesXml = () => {
|
|
41
|
+
return XML_DECLARATION + `<styleSheet xmlns="${NAMESPACES.MAIN}"><fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><name val="Calibri"/></font></fonts><fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>`;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* One worksheet part: column widths, an optional heading block, a bold header row
|
|
45
|
+
* from the column display names, then one row per data object in column order.
|
|
46
|
+
*
|
|
47
|
+
* <cols> must precede <sheetData> or Excel treats the file as corrupt.
|
|
48
|
+
*/
|
|
49
|
+
const buildWorksheetXml = (sheet) => {
|
|
50
|
+
const heading = sheet?.heading || [];
|
|
51
|
+
const headerRow = 0 === heading.length ? 1 : heading.length + 2;
|
|
52
|
+
const colsXml = sheet.columns.map((column, index) => {
|
|
53
|
+
const position = index + 1;
|
|
54
|
+
return `<col min="${position}" max="${position}" width="${require_xlsx_column.getColumnWidth(column, sheet.data)}" customWidth="1"/>`;
|
|
55
|
+
}).join("");
|
|
56
|
+
const headingXml = heading.map((line, index) => {
|
|
57
|
+
const styleAttr = 0 === index ? ` s="${HEADER_STYLE_INDEX}"` : "";
|
|
58
|
+
return `<row r="${index + 1}">${require_xlsx_cell.buildCellXml(`A${index + 1}`, line, styleAttr)}</row>`;
|
|
59
|
+
}).join("");
|
|
60
|
+
const headerXml = sheet.columns.map((column, index) => {
|
|
61
|
+
return require_xlsx_cell.buildCellXml(`${require_xlsx_column.getColumLetter(index + 1)}${headerRow}`, column.displayName, ` s="${HEADER_STYLE_INDEX}"`);
|
|
62
|
+
}).join("");
|
|
63
|
+
const rowsXml = sheet.data.map((row, rowIndex) => {
|
|
64
|
+
const rowNumber = headerRow + rowIndex + 1;
|
|
65
|
+
return `<row r="${rowNumber}">${sheet.columns.map((column, columnIndex) => {
|
|
66
|
+
return require_xlsx_cell.buildCellXml(`${require_xlsx_column.getColumLetter(columnIndex + 1)}${rowNumber}`, require_xlsx_cell.getCellValue(row?.[column.id]), "");
|
|
67
|
+
}).join("")}</row>`;
|
|
68
|
+
}).join("");
|
|
69
|
+
return XML_DECLARATION + `<worksheet xmlns="${NAMESPACES.MAIN}">` + (colsXml ? `<cols>${colsXml}</cols>` : "") + `<sheetData>${headingXml}<row r="${headerRow}">${headerXml}</row>${rowsXml}</sheetData></worksheet>`;
|
|
70
|
+
};
|
|
71
|
+
const buildContentTypesXml = (sheetCount) => {
|
|
72
|
+
const overrides = Array.from({ length: sheetCount }, (unused, index) => {
|
|
73
|
+
return `<Override PartName="/xl/worksheets/sheet${index + 1}.xml" ContentType="${CONTENT_TYPE.WORKSHEET}"/>`;
|
|
74
|
+
}).join("");
|
|
75
|
+
return XML_DECLARATION + `<Types xmlns="${NAMESPACES.CONTENT_TYPES}"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="${CONTENT_TYPE.SHEET}"/><Override PartName="/xl/styles.xml" ContentType="${CONTENT_TYPE.STYLES}"/>` + overrides + `</Types>`;
|
|
76
|
+
};
|
|
77
|
+
const buildWorkbookXml = (sheetNames) => {
|
|
78
|
+
const sheetsXml = sheetNames.map((name, index) => {
|
|
79
|
+
return `<sheet name="${require_xlsx_cell.getEscapedXml(name)}" sheetId="${index + 1}" r:id="rId${index + 1}"/>`;
|
|
80
|
+
}).join("");
|
|
81
|
+
return XML_DECLARATION + `<workbook xmlns="${NAMESPACES.MAIN}" xmlns:r="${NAMESPACES.RELATIONSHIP}"><sheets>${sheetsXml}</sheets></workbook>`;
|
|
82
|
+
};
|
|
83
|
+
const buildWorkbookRelsXml = (sheetCount) => {
|
|
84
|
+
const sheetRels = Array.from({ length: sheetCount }, (unused, index) => {
|
|
85
|
+
return `<Relationship Id="rId${index + 1}" Type="${REL_TYPE.WORKSHEET}" Target="worksheets/sheet${index + 1}.xml"/>`;
|
|
86
|
+
}).join("");
|
|
87
|
+
return XML_DECLARATION + `<Relationships xmlns="${NAMESPACES.PACKAGE_REL}">` + sheetRels + `<Relationship Id="rId${sheetCount + 1}" Type="${REL_TYPE.STYLES}" Target="styles.xml"/></Relationships>`;
|
|
88
|
+
};
|
|
89
|
+
const buildPackageRelsXml = () => {
|
|
90
|
+
return XML_DECLARATION + `<Relationships xmlns="${NAMESPACES.PACKAGE_REL}"><Relationship Id="rId1" Type="${REL_TYPE.OFFICE_DOCUMENT}" Target="xl/workbook.xml"/></Relationships>`;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Assemble the workbook parts. Shared by the Blob and byte-array entry points so
|
|
94
|
+
* the two can never drift.
|
|
95
|
+
*/
|
|
96
|
+
const buildWorkbook = async (sheets) => {
|
|
97
|
+
if (0 === sheets.length) throw new Error("createXlsx requires at least one sheet.");
|
|
98
|
+
const JSZip = (await import("jszip")).default;
|
|
99
|
+
const zip = new JSZip();
|
|
100
|
+
const names = require_xlsx_sheet.getUniqueSheetNames(sheets);
|
|
101
|
+
zip.file("[Content_Types].xml", buildContentTypesXml(sheets.length));
|
|
102
|
+
zip.file("_rels/.rels", buildPackageRelsXml());
|
|
103
|
+
zip.file("xl/workbook.xml", buildWorkbookXml(names));
|
|
104
|
+
zip.file("xl/_rels/workbook.xml.rels", buildWorkbookRelsXml(sheets.length));
|
|
105
|
+
zip.file("xl/styles.xml", buildStylesXml());
|
|
106
|
+
sheets.forEach((sheet, index) => {
|
|
107
|
+
zip.file(`xl/worksheets/sheet${index + 1}.xml`, buildWorksheetXml(sheet));
|
|
108
|
+
});
|
|
109
|
+
return zip;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Build an .xlsx workbook with one worksheet per entry in `sheets`, returned as a
|
|
113
|
+
* Blob ready to hand to a browser download.
|
|
114
|
+
*
|
|
115
|
+
* Every sheet gets a bold header row from its column display names, and each data
|
|
116
|
+
* row reads values by column `id` — so key order in the source objects does not
|
|
117
|
+
* matter and a missing key becomes an empty cell.
|
|
118
|
+
*/
|
|
119
|
+
const createXlsx = async (sheets) => {
|
|
120
|
+
return (await buildWorkbook(sheets)).generateAsync({
|
|
121
|
+
type: "blob",
|
|
122
|
+
compression: "DEFLATE",
|
|
123
|
+
mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* As createXlsx, but returns raw bytes instead of a Blob — for server-side
|
|
128
|
+
* callers that need to stream or write the workbook rather than download it.
|
|
129
|
+
*/
|
|
130
|
+
const createXlsxBytes = async (sheets) => {
|
|
131
|
+
return (await buildWorkbook(sheets)).generateAsync({
|
|
132
|
+
type: "uint8array",
|
|
133
|
+
compression: "DEFLATE"
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
//#endregion
|
|
137
|
+
exports.createXlsx = createXlsx;
|
|
138
|
+
exports.createXlsxBytes = createXlsxBytes;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { XlsxSheet } from "./sheet.cjs";
|
|
2
|
+
//#region src/xlsx/createXlsx.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Build an .xlsx workbook with one worksheet per entry in `sheets`, returned as a
|
|
5
|
+
* Blob ready to hand to a browser download.
|
|
6
|
+
*
|
|
7
|
+
* Every sheet gets a bold header row from its column display names, and each data
|
|
8
|
+
* row reads values by column `id` — so key order in the source objects does not
|
|
9
|
+
* matter and a missing key becomes an empty cell.
|
|
10
|
+
*/
|
|
11
|
+
declare const createXlsx: (sheets: XlsxSheet[]) => Promise<Blob>;
|
|
12
|
+
/**
|
|
13
|
+
* As createXlsx, but returns raw bytes instead of a Blob — for server-side
|
|
14
|
+
* callers that need to stream or write the workbook rather than download it.
|
|
15
|
+
*/
|
|
16
|
+
declare const createXlsxBytes: (sheets: XlsxSheet[]) => Promise<Uint8Array>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { createXlsx, createXlsxBytes };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { XlsxSheet } from "./sheet.mjs";
|
|
2
|
+
//#region src/xlsx/createXlsx.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Build an .xlsx workbook with one worksheet per entry in `sheets`, returned as a
|
|
5
|
+
* Blob ready to hand to a browser download.
|
|
6
|
+
*
|
|
7
|
+
* Every sheet gets a bold header row from its column display names, and each data
|
|
8
|
+
* row reads values by column `id` — so key order in the source objects does not
|
|
9
|
+
* matter and a missing key becomes an empty cell.
|
|
10
|
+
*/
|
|
11
|
+
declare const createXlsx: (sheets: XlsxSheet[]) => Promise<Blob>;
|
|
12
|
+
/**
|
|
13
|
+
* As createXlsx, but returns raw bytes instead of a Blob — for server-side
|
|
14
|
+
* callers that need to stream or write the workbook rather than download it.
|
|
15
|
+
*/
|
|
16
|
+
declare const createXlsxBytes: (sheets: XlsxSheet[]) => Promise<Uint8Array>;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { createXlsx, createXlsxBytes };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { getColumLetter, getColumnWidth } from "./column.mjs";
|
|
2
|
+
import { buildCellXml, getCellValue, getEscapedXml } from "./cell.mjs";
|
|
3
|
+
import { getUniqueSheetNames } from "./sheet.mjs";
|
|
4
|
+
//#region src/xlsx/createXlsx.ts
|
|
5
|
+
/**
|
|
6
|
+
* Builds a complete .xlsx workbook from scratch, using jszip and hand-written
|
|
7
|
+
* OOXML rather than a spreadsheet library.
|
|
8
|
+
*
|
|
9
|
+
* Nothing here touches `fs`, so it runs in the browser as happily as on the
|
|
10
|
+
* server — a page can turn data it has already loaded straight into a download.
|
|
11
|
+
*
|
|
12
|
+
* jszip is an optional peer dependency imported dynamically, so consumers that
|
|
13
|
+
* never build a workbook neither install it nor pay for it in their bundle.
|
|
14
|
+
*/
|
|
15
|
+
/** Style index of the bold row within the cellXfs list built by buildStylesXml. */
|
|
16
|
+
const HEADER_STYLE_INDEX = 1;
|
|
17
|
+
const XML_DECLARATION = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`;
|
|
18
|
+
const CONTENT_TYPE = {
|
|
19
|
+
SHEET: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
|
20
|
+
STYLES: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
|
21
|
+
WORKSHEET: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"
|
|
22
|
+
};
|
|
23
|
+
const REL_TYPE = {
|
|
24
|
+
OFFICE_DOCUMENT: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
25
|
+
STYLES: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
26
|
+
WORKSHEET: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
|
27
|
+
};
|
|
28
|
+
const NAMESPACES = {
|
|
29
|
+
MAIN: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
30
|
+
RELATIONSHIP: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
31
|
+
PACKAGE_REL: "http://schemas.openxmlformats.org/package/2006/relationships",
|
|
32
|
+
CONTENT_TYPES: "http://schemas.openxmlformats.org/package/2006/content-types"
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Minimal but valid styles part. Excel requires at least one border, one
|
|
36
|
+
* cellStyleXf and two fills (index 0 "none", index 1 "gray125") even when
|
|
37
|
+
* nothing references them. cellXfs index 1 is the bold header.
|
|
38
|
+
*/
|
|
39
|
+
const buildStylesXml = () => {
|
|
40
|
+
return XML_DECLARATION + `<styleSheet xmlns="${NAMESPACES.MAIN}"><fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><name val="Calibri"/></font></fonts><fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>`;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* One worksheet part: column widths, an optional heading block, a bold header row
|
|
44
|
+
* from the column display names, then one row per data object in column order.
|
|
45
|
+
*
|
|
46
|
+
* <cols> must precede <sheetData> or Excel treats the file as corrupt.
|
|
47
|
+
*/
|
|
48
|
+
const buildWorksheetXml = (sheet) => {
|
|
49
|
+
const heading = sheet?.heading || [];
|
|
50
|
+
const headerRow = 0 === heading.length ? 1 : heading.length + 2;
|
|
51
|
+
const colsXml = sheet.columns.map((column, index) => {
|
|
52
|
+
const position = index + 1;
|
|
53
|
+
return `<col min="${position}" max="${position}" width="${getColumnWidth(column, sheet.data)}" customWidth="1"/>`;
|
|
54
|
+
}).join("");
|
|
55
|
+
const headingXml = heading.map((line, index) => {
|
|
56
|
+
const styleAttr = 0 === index ? ` s="${HEADER_STYLE_INDEX}"` : "";
|
|
57
|
+
return `<row r="${index + 1}">${buildCellXml(`A${index + 1}`, line, styleAttr)}</row>`;
|
|
58
|
+
}).join("");
|
|
59
|
+
const headerXml = sheet.columns.map((column, index) => {
|
|
60
|
+
return buildCellXml(`${getColumLetter(index + 1)}${headerRow}`, column.displayName, ` s="${HEADER_STYLE_INDEX}"`);
|
|
61
|
+
}).join("");
|
|
62
|
+
const rowsXml = sheet.data.map((row, rowIndex) => {
|
|
63
|
+
const rowNumber = headerRow + rowIndex + 1;
|
|
64
|
+
return `<row r="${rowNumber}">${sheet.columns.map((column, columnIndex) => {
|
|
65
|
+
return buildCellXml(`${getColumLetter(columnIndex + 1)}${rowNumber}`, getCellValue(row?.[column.id]), "");
|
|
66
|
+
}).join("")}</row>`;
|
|
67
|
+
}).join("");
|
|
68
|
+
return XML_DECLARATION + `<worksheet xmlns="${NAMESPACES.MAIN}">` + (colsXml ? `<cols>${colsXml}</cols>` : "") + `<sheetData>${headingXml}<row r="${headerRow}">${headerXml}</row>${rowsXml}</sheetData></worksheet>`;
|
|
69
|
+
};
|
|
70
|
+
const buildContentTypesXml = (sheetCount) => {
|
|
71
|
+
const overrides = Array.from({ length: sheetCount }, (unused, index) => {
|
|
72
|
+
return `<Override PartName="/xl/worksheets/sheet${index + 1}.xml" ContentType="${CONTENT_TYPE.WORKSHEET}"/>`;
|
|
73
|
+
}).join("");
|
|
74
|
+
return XML_DECLARATION + `<Types xmlns="${NAMESPACES.CONTENT_TYPES}"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="${CONTENT_TYPE.SHEET}"/><Override PartName="/xl/styles.xml" ContentType="${CONTENT_TYPE.STYLES}"/>` + overrides + `</Types>`;
|
|
75
|
+
};
|
|
76
|
+
const buildWorkbookXml = (sheetNames) => {
|
|
77
|
+
const sheetsXml = sheetNames.map((name, index) => {
|
|
78
|
+
return `<sheet name="${getEscapedXml(name)}" sheetId="${index + 1}" r:id="rId${index + 1}"/>`;
|
|
79
|
+
}).join("");
|
|
80
|
+
return XML_DECLARATION + `<workbook xmlns="${NAMESPACES.MAIN}" xmlns:r="${NAMESPACES.RELATIONSHIP}"><sheets>${sheetsXml}</sheets></workbook>`;
|
|
81
|
+
};
|
|
82
|
+
const buildWorkbookRelsXml = (sheetCount) => {
|
|
83
|
+
const sheetRels = Array.from({ length: sheetCount }, (unused, index) => {
|
|
84
|
+
return `<Relationship Id="rId${index + 1}" Type="${REL_TYPE.WORKSHEET}" Target="worksheets/sheet${index + 1}.xml"/>`;
|
|
85
|
+
}).join("");
|
|
86
|
+
return XML_DECLARATION + `<Relationships xmlns="${NAMESPACES.PACKAGE_REL}">` + sheetRels + `<Relationship Id="rId${sheetCount + 1}" Type="${REL_TYPE.STYLES}" Target="styles.xml"/></Relationships>`;
|
|
87
|
+
};
|
|
88
|
+
const buildPackageRelsXml = () => {
|
|
89
|
+
return XML_DECLARATION + `<Relationships xmlns="${NAMESPACES.PACKAGE_REL}"><Relationship Id="rId1" Type="${REL_TYPE.OFFICE_DOCUMENT}" Target="xl/workbook.xml"/></Relationships>`;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Assemble the workbook parts. Shared by the Blob and byte-array entry points so
|
|
93
|
+
* the two can never drift.
|
|
94
|
+
*/
|
|
95
|
+
const buildWorkbook = async (sheets) => {
|
|
96
|
+
if (0 === sheets.length) throw new Error("createXlsx requires at least one sheet.");
|
|
97
|
+
const JSZip = (await import("jszip")).default;
|
|
98
|
+
const zip = new JSZip();
|
|
99
|
+
const names = getUniqueSheetNames(sheets);
|
|
100
|
+
zip.file("[Content_Types].xml", buildContentTypesXml(sheets.length));
|
|
101
|
+
zip.file("_rels/.rels", buildPackageRelsXml());
|
|
102
|
+
zip.file("xl/workbook.xml", buildWorkbookXml(names));
|
|
103
|
+
zip.file("xl/_rels/workbook.xml.rels", buildWorkbookRelsXml(sheets.length));
|
|
104
|
+
zip.file("xl/styles.xml", buildStylesXml());
|
|
105
|
+
sheets.forEach((sheet, index) => {
|
|
106
|
+
zip.file(`xl/worksheets/sheet${index + 1}.xml`, buildWorksheetXml(sheet));
|
|
107
|
+
});
|
|
108
|
+
return zip;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Build an .xlsx workbook with one worksheet per entry in `sheets`, returned as a
|
|
112
|
+
* Blob ready to hand to a browser download.
|
|
113
|
+
*
|
|
114
|
+
* Every sheet gets a bold header row from its column display names, and each data
|
|
115
|
+
* row reads values by column `id` — so key order in the source objects does not
|
|
116
|
+
* matter and a missing key becomes an empty cell.
|
|
117
|
+
*/
|
|
118
|
+
const createXlsx = async (sheets) => {
|
|
119
|
+
return (await buildWorkbook(sheets)).generateAsync({
|
|
120
|
+
type: "blob",
|
|
121
|
+
compression: "DEFLATE",
|
|
122
|
+
mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* As createXlsx, but returns raw bytes instead of a Blob — for server-side
|
|
127
|
+
* callers that need to stream or write the workbook rather than download it.
|
|
128
|
+
*/
|
|
129
|
+
const createXlsxBytes = async (sheets) => {
|
|
130
|
+
return (await buildWorkbook(sheets)).generateAsync({
|
|
131
|
+
type: "uint8array",
|
|
132
|
+
compression: "DEFLATE"
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
//#endregion
|
|
136
|
+
export { createXlsx, createXlsxBytes };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_xlsx_column = require("./column.cjs");
|
|
3
|
+
const require_xlsx_cell = require("./cell.cjs");
|
|
4
|
+
const require_xlsx_sheet = require("./sheet.cjs");
|
|
5
|
+
const require_xlsx_createXlsx = require("./createXlsx.cjs");
|
|
6
|
+
const require_xlsx_modifyXlsx = require("./modifyXlsx.cjs");
|
|
7
|
+
exports.buildCellXml = require_xlsx_cell.buildCellXml;
|
|
8
|
+
exports.createXlsx = require_xlsx_createXlsx.createXlsx;
|
|
9
|
+
exports.createXlsxBytes = require_xlsx_createXlsx.createXlsxBytes;
|
|
10
|
+
exports.getCellColNum = require_xlsx_cell.getCellColNum;
|
|
11
|
+
exports.getCellValue = require_xlsx_cell.getCellValue;
|
|
12
|
+
exports.getColumLetter = require_xlsx_column.getColumLetter;
|
|
13
|
+
exports.getColumnWidth = require_xlsx_column.getColumnWidth;
|
|
14
|
+
exports.getEscapedXml = require_xlsx_cell.getEscapedXml;
|
|
15
|
+
exports.getSanitisedSheetName = require_xlsx_sheet.getSanitisedSheetName;
|
|
16
|
+
exports.getUniqueSheetNames = require_xlsx_sheet.getUniqueSheetNames;
|
|
17
|
+
exports.modifyXlsx = require_xlsx_modifyXlsx.modifyXlsx;
|
|
18
|
+
exports.setCell = require_xlsx_cell.setCell;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { XlsxCellValue, buildCellXml, getCellColNum, getCellValue, getEscapedXml, setCell } from "./cell.cjs";
|
|
2
|
+
import { XlsxColumn, getColumLetter, getColumnWidth } from "./column.cjs";
|
|
3
|
+
import { XlsxSheet, getSanitisedSheetName, getUniqueSheetNames } from "./sheet.cjs";
|
|
4
|
+
import { createXlsx, createXlsxBytes } from "./createXlsx.cjs";
|
|
5
|
+
import { XlsxSheetModifier, modifyXlsx } from "./modifyXlsx.cjs";
|
|
6
|
+
export { XlsxCellValue, XlsxColumn, XlsxSheet, XlsxSheetModifier, buildCellXml, createXlsx, createXlsxBytes, getCellColNum, getCellValue, getColumLetter, getColumnWidth, getEscapedXml, getSanitisedSheetName, getUniqueSheetNames, modifyXlsx, setCell };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { XlsxCellValue, buildCellXml, getCellColNum, getCellValue, getEscapedXml, setCell } from "./cell.mjs";
|
|
2
|
+
import { XlsxColumn, getColumLetter, getColumnWidth } from "./column.mjs";
|
|
3
|
+
import { XlsxSheet, getSanitisedSheetName, getUniqueSheetNames } from "./sheet.mjs";
|
|
4
|
+
import { createXlsx, createXlsxBytes } from "./createXlsx.mjs";
|
|
5
|
+
import { XlsxSheetModifier, modifyXlsx } from "./modifyXlsx.mjs";
|
|
6
|
+
export { XlsxCellValue, XlsxColumn, XlsxSheet, XlsxSheetModifier, buildCellXml, createXlsx, createXlsxBytes, getCellColNum, getCellValue, getColumLetter, getColumnWidth, getEscapedXml, getSanitisedSheetName, getUniqueSheetNames, modifyXlsx, setCell };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { getColumLetter, getColumnWidth } from "./column.mjs";
|
|
2
|
+
import { buildCellXml, getCellColNum, getCellValue, getEscapedXml, setCell } from "./cell.mjs";
|
|
3
|
+
import { getSanitisedSheetName, getUniqueSheetNames } from "./sheet.mjs";
|
|
4
|
+
import { createXlsx, createXlsxBytes } from "./createXlsx.mjs";
|
|
5
|
+
import { modifyXlsx } from "./modifyXlsx.mjs";
|
|
6
|
+
export { buildCellXml, createXlsx, createXlsxBytes, getCellColNum, getCellValue, getColumLetter, getColumnWidth, getEscapedXml, getSanitisedSheetName, getUniqueSheetNames, modifyXlsx, setCell };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_xlsx_cell = require("./cell.cjs");
|
|
3
|
+
//#region src/xlsx/modifyXlsx.ts
|
|
4
|
+
/**
|
|
5
|
+
* Patches an existing .xlsx template: apply per-sheet XML transformations, add
|
|
6
|
+
* sheets that are not in the template, and optionally embed a logo image.
|
|
7
|
+
*
|
|
8
|
+
* Takes and returns bytes rather than file paths, so it has no filesystem
|
|
9
|
+
* dependency and runs anywhere. Callers that work from disk read the template
|
|
10
|
+
* themselves and hand over the buffer.
|
|
11
|
+
*
|
|
12
|
+
* Use this when the output must keep a branded template's styling; use
|
|
13
|
+
* `createXlsx` when building a workbook from nothing.
|
|
14
|
+
*/
|
|
15
|
+
/** 1 screen pixel at 96 DPI equals 9525 English Metric Units in OOXML. */
|
|
16
|
+
const PX_TO_EMU = 9525;
|
|
17
|
+
/** Default logo box, sized for a typical wide wordmark. */
|
|
18
|
+
const LOGO_WIDTH_PX = 500;
|
|
19
|
+
const LOGO_HEIGHT_PX = 77;
|
|
20
|
+
const DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
|
|
21
|
+
const IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
22
|
+
const WORKSHEET_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
|
|
23
|
+
const DRAWING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml";
|
|
24
|
+
/**
|
|
25
|
+
* Minimal valid worksheet XML used when `createIfMissing` adds a sheet.
|
|
26
|
+
*
|
|
27
|
+
* <sheetData></sheetData> must not be self-closing: setCell's new-row path
|
|
28
|
+
* searches for the literal closing tag.
|
|
29
|
+
*/
|
|
30
|
+
const EMPTY_SHEET_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData></sheetData></worksheet>";
|
|
31
|
+
/**
|
|
32
|
+
* Drawing XML for one embedded image, anchored to cell B2 so it sits just below
|
|
33
|
+
* the header row.
|
|
34
|
+
*
|
|
35
|
+
* The image relationship is assumed to be registered as "rId1" in the sibling
|
|
36
|
+
* .rels file.
|
|
37
|
+
*/
|
|
38
|
+
const buildDrawingXml = (widthEmu, heightEmu) => {
|
|
39
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><xdr:oneCellAnchor><xdr:from><xdr:col>1</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>1</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:from><xdr:ext cx="${widthEmu}" cy="${heightEmu}"/><xdr:pic><xdr:nvPicPr><xdr:cNvPr id="2" name="Logo"/><xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr><xdr:blipFill><a:blip r:embed="rId1"/><a:stretch><a:fillRect/></a:stretch></xdr:blipFill><xdr:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${widthEmu}" cy="${heightEmu}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr></xdr:pic><xdr:clientData/></xdr:oneCellAnchor></xdr:wsDr>`;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Apply per-sheet transformations to a template workbook.
|
|
43
|
+
*
|
|
44
|
+
* @param template The .xlsx template as raw bytes.
|
|
45
|
+
* @param sheets One entry per sheet to touch, keyed by its 1-indexed position.
|
|
46
|
+
* @param logo Optional PNG bytes, embedded into sheets with `addLogo`.
|
|
47
|
+
*/
|
|
48
|
+
const modifyXlsx = async (template, sheets, logo) => {
|
|
49
|
+
const zip = await (await import("jszip")).default.loadAsync(template);
|
|
50
|
+
if (logo) {
|
|
51
|
+
zip.file("xl/media/image1.png", logo);
|
|
52
|
+
const contentTypesFile = zip.file("[Content_Types].xml");
|
|
53
|
+
if (contentTypesFile) {
|
|
54
|
+
let contentTypesXml = await contentTypesFile.async("string");
|
|
55
|
+
if (!contentTypesXml.includes(`Extension="png"`)) contentTypesXml = contentTypesXml.replace("</Types>", () => {
|
|
56
|
+
return `<Default Extension="png" ContentType="image/png"/></Types>`;
|
|
57
|
+
});
|
|
58
|
+
zip.file("[Content_Types].xml", contentTypesXml);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
let drawingCounter = 0;
|
|
62
|
+
for (const sheet of sheets) {
|
|
63
|
+
const filename = `xl/worksheets/sheet${sheet.index}.xml`;
|
|
64
|
+
let existing = zip.file(filename);
|
|
65
|
+
if (!existing && sheet.createIfMissing) {
|
|
66
|
+
const name = sheet.sheetName ?? `Sheet${sheet.index}`;
|
|
67
|
+
const rId = `rId${sheet.index + 10}`;
|
|
68
|
+
const workbookFile = zip.file("xl/workbook.xml");
|
|
69
|
+
if (workbookFile) {
|
|
70
|
+
const workbookXml = await workbookFile.async("string");
|
|
71
|
+
zip.file("xl/workbook.xml", workbookXml.replace("</sheets>", () => {
|
|
72
|
+
return `<sheet name="${require_xlsx_cell.getEscapedXml(name)}" sheetId="${sheet.index}" r:id="${rId}"/></sheets>`;
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
const relsFile = zip.file("xl/_rels/workbook.xml.rels");
|
|
76
|
+
if (relsFile) {
|
|
77
|
+
const relsXml = await relsFile.async("string");
|
|
78
|
+
zip.file("xl/_rels/workbook.xml.rels", relsXml.replace("</Relationships>", () => {
|
|
79
|
+
return `<Relationship Id="${rId}" Type="${WORKSHEET_REL_TYPE}" Target="worksheets/sheet${sheet.index}.xml"/></Relationships>`;
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
zip.file(filename, EMPTY_SHEET_XML);
|
|
83
|
+
existing = zip.file(filename);
|
|
84
|
+
}
|
|
85
|
+
if (!existing) continue;
|
|
86
|
+
let xml = sheet.modify(await existing.async("string"));
|
|
87
|
+
if (sheet.addLogo && logo) {
|
|
88
|
+
drawingCounter++;
|
|
89
|
+
const drawingNum = drawingCounter;
|
|
90
|
+
const logoWidthEmu = Math.round((sheet.logoWidthPx ?? LOGO_WIDTH_PX) * PX_TO_EMU);
|
|
91
|
+
const logoHeightEmu = Math.round((sheet.logoHeightPx ?? LOGO_HEIGHT_PX) * PX_TO_EMU);
|
|
92
|
+
const drawingRId = `rIdLogo${drawingNum}`;
|
|
93
|
+
zip.file(`xl/drawings/drawing${drawingNum}.xml`, buildDrawingXml(logoWidthEmu, logoHeightEmu));
|
|
94
|
+
zip.file(`xl/drawings/_rels/drawing${drawingNum}.xml.rels`, `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="${IMAGE_REL_TYPE}" Target="../media/image1.png"/></Relationships>`);
|
|
95
|
+
const contentTypesFile = zip.file("[Content_Types].xml");
|
|
96
|
+
if (contentTypesFile) {
|
|
97
|
+
let contentTypesXml = await contentTypesFile.async("string");
|
|
98
|
+
const drawingPart = `/xl/drawings/drawing${drawingNum}.xml`;
|
|
99
|
+
if (!contentTypesXml.includes(drawingPart)) contentTypesXml = contentTypesXml.replace("</Types>", () => {
|
|
100
|
+
return `<Override PartName="${drawingPart}" ContentType="${DRAWING_CONTENT_TYPE}"/></Types>`;
|
|
101
|
+
});
|
|
102
|
+
zip.file("[Content_Types].xml", contentTypesXml);
|
|
103
|
+
}
|
|
104
|
+
const sheetRelsPath = `xl/worksheets/_rels/sheet${sheet.index}.xml.rels`;
|
|
105
|
+
const sheetRelsFile = zip.file(sheetRelsPath);
|
|
106
|
+
if (sheetRelsFile) {
|
|
107
|
+
const relsXml = await sheetRelsFile.async("string");
|
|
108
|
+
zip.file(sheetRelsPath, relsXml.replace("</Relationships>", () => {
|
|
109
|
+
return `<Relationship Id="${drawingRId}" Type="${DRAWING_REL_TYPE}" Target="../drawings/drawing${drawingNum}.xml"/></Relationships>`;
|
|
110
|
+
}));
|
|
111
|
+
} else zip.file(sheetRelsPath, `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="${drawingRId}" Type="${DRAWING_REL_TYPE}" Target="../drawings/drawing${drawingNum}.xml"/></Relationships>`);
|
|
112
|
+
if (!xml.includes("xmlns:r=")) xml = xml.replace("<worksheet ", `<worksheet xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" `);
|
|
113
|
+
xml = xml.replace("</worksheet>", () => {
|
|
114
|
+
return `<drawing r:id="${drawingRId}"/></worksheet>`;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
zip.file(filename, xml);
|
|
118
|
+
}
|
|
119
|
+
return zip.generateAsync({
|
|
120
|
+
type: "uint8array",
|
|
121
|
+
compression: "DEFLATE"
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
//#endregion
|
|
125
|
+
exports.modifyXlsx = modifyXlsx;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/xlsx/modifyXlsx.d.ts
|
|
2
|
+
type XlsxSheetModifier = {
|
|
3
|
+
/** 1-indexed sheet number inside the ZIP (sheet1.xml, sheet2.xml, …). */
|
|
4
|
+
index: number;
|
|
5
|
+
modify: (xml: string) => string;
|
|
6
|
+
/** Create a blank sheet when the template does not contain this index. */
|
|
7
|
+
createIfMissing?: boolean;
|
|
8
|
+
/** Sheet name used when createIfMissing creates the sheet. */
|
|
9
|
+
sheetName?: string;
|
|
10
|
+
/** Embed the logo into this sheet. Requires a logo to be passed to modifyXlsx. */
|
|
11
|
+
addLogo?: boolean;
|
|
12
|
+
logoHeightPx?: number;
|
|
13
|
+
logoWidthPx?: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Apply per-sheet transformations to a template workbook.
|
|
17
|
+
*
|
|
18
|
+
* @param template The .xlsx template as raw bytes.
|
|
19
|
+
* @param sheets One entry per sheet to touch, keyed by its 1-indexed position.
|
|
20
|
+
* @param logo Optional PNG bytes, embedded into sheets with `addLogo`.
|
|
21
|
+
*/
|
|
22
|
+
declare const modifyXlsx: (template: Uint8Array, sheets: XlsxSheetModifier[], logo?: Uint8Array) => Promise<Uint8Array>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { XlsxSheetModifier, modifyXlsx };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/xlsx/modifyXlsx.d.ts
|
|
2
|
+
type XlsxSheetModifier = {
|
|
3
|
+
/** 1-indexed sheet number inside the ZIP (sheet1.xml, sheet2.xml, …). */
|
|
4
|
+
index: number;
|
|
5
|
+
modify: (xml: string) => string;
|
|
6
|
+
/** Create a blank sheet when the template does not contain this index. */
|
|
7
|
+
createIfMissing?: boolean;
|
|
8
|
+
/** Sheet name used when createIfMissing creates the sheet. */
|
|
9
|
+
sheetName?: string;
|
|
10
|
+
/** Embed the logo into this sheet. Requires a logo to be passed to modifyXlsx. */
|
|
11
|
+
addLogo?: boolean;
|
|
12
|
+
logoHeightPx?: number;
|
|
13
|
+
logoWidthPx?: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Apply per-sheet transformations to a template workbook.
|
|
17
|
+
*
|
|
18
|
+
* @param template The .xlsx template as raw bytes.
|
|
19
|
+
* @param sheets One entry per sheet to touch, keyed by its 1-indexed position.
|
|
20
|
+
* @param logo Optional PNG bytes, embedded into sheets with `addLogo`.
|
|
21
|
+
*/
|
|
22
|
+
declare const modifyXlsx: (template: Uint8Array, sheets: XlsxSheetModifier[], logo?: Uint8Array) => Promise<Uint8Array>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { XlsxSheetModifier, modifyXlsx };
|