I am new to Web Worker, and right now what I am trying to do is import my function that I have created under the workerCode in Web Worker, but neither "import" nor "importScript" work; only the XLSX library is working fine, but the rest is not.
Could you help me figure out how to do this?
export const consolidateWorkerV2 = () => {
const workerCode = `
/* Load standalone script from CDN */
import * as XLSX from "https://cdn.sheetjs.com/xlsx-0.20.1/package/xlsx.mjs";
import getDataRange from "../shared/util/GetDataRange";
import modifyWorksheet from "../shared/util/modifyWorksheet.js";
import headerStyling from "../shared/util/headerStyling";
self.addEventListener('message', async (e) => {
try {
const files = e.data.files;
const newWorkbook = XLSX.utils.book_new();
console.log("message received from main thread");
for (const [index, item] of files.entries()) {
const file = item.file;
const filename = item.file.name
const data = await item.file.arrayBuffer();
const workbook = XLSX.read(data);
for (const sheetName of workbook.SheetNames) {
if (newWorkbook.SheetNames.includes(journalType)) {
const duplicateSheetName = workbook.SheetNames[0];
const newWorksheet = newWorkbook.Sheets[journalType];
const dupWorksheet = getDataRange(workbook.Sheets[duplicateSheetName]);
const OPTIONS = { raw: false, defval: "", };
const parsedDupWorksheet = XLSX.utils.sheet_to_json(dupWorksheet, OPTIONS);
const updatedWorksheet = modifyWorksheet(dupWorksheet, parsedDupWorksheet, duplicateSheetName, journalType, reportDate);
const newData = updatedWorksheet.map((row) => Object.values(row));
XLSX.utils.sheet_add_aoa(newWorksheet, newData, { origin: -1, skipHeader: true });
} else {
// if no existing sheet name, then add new sheet
for (const sheetName of workbook.SheetNames) {
const worksheet = workbook.Sheets[sheetName];
const worksheetData = getDataRange(worksheet);
const parsedWorksheet = XLSX.utils.sheet_to_json(worksheet, { raw: false, defval: "", });
const updatedWorksheet = modifyWorksheet(worksheetData, parsedWorksheet, sheetName, journalType, reportDate);
const styledSheet = headerStyling(updatedWorksheet);
XLSX.utils.book_append_sheet(newWorkbook, styledSheet, journalType);
}
}
}
}
const consolidateFilename = namingConvention("Consolidated_Final_Report", date);
const excelBuffer = XLSX.write(newWorkbook, { bookType: "xlsx", type: "array", });
const blob = new File([excelBuffer], fileName + ".xlsx");
postMessage({ blob: blob });
} catch(e) {
/* Pass the error message back */
postMessage({ error: String(e.message || e).bold() });
}
}, false);
`;
const blob = new Blob([workerCode], { type: "text/javascript" });
const worker = new Worker(URL.createObjectURL(blob), { type: "module" });
return worker;
};
I see two issues:
You must include the full filename of the module you import via import
or importScripts
when there's no bundler involved; the native web platform doesn't try to guess at file extensions. So you'd need:
import getDataRange from "../shared/util/GetDataRange.js";
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^
...assuming that's the full path of the resource.
I can't find a specific citation for it, but I believe when you load a worker via a blob, you can't use relative imports. Four possible mututally-exclusive solutions for that:
Don't use a Blob. Put the worker code in its own file and use the URL to that file:
const worker = new Worker("./path/to/worker.js", { type: "module" });
This is the simple and easy answer.
Alternatively, use absolute imports as you did with the XLSX
library if you only need to run this code on a single site. For example:
import getDataRange from "https://example.com/shared/util/GetDataRange.js";
Or similar.
Or, modify the workerCode
to replace the relative paths with absolute ones before creating the Blob:
let workerCode = /*...*/;
const shared = new URL("../shared", location.href).toString();
workerCode = workerCode.replace(/from "\.\.\/shared/g, `from ${shared}`);
// ...build the worker
Or use self.origin
to get the origin within the worker (a quick test suggests this works on Chromium, Firefox, and Safari) and use dynamic import.
const getDataRange = (await import(new URL("../shared/util/GetDataRange", self.origin).toString())).default;
I suspect any of the other three solutions are better than this one, but again, it did seem to work on the major browsers.