Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions scripts/pack.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ const PACK_FIREFOX = false;

// ============================================================================

// --- utils ---

const MAX_CHUNK_SIZE = 3584 * 1024; // 3.5 MiB

function addFileInChunks(zip, filePath, toDir, baseName, maxChunkSize = MAX_CHUNK_SIZE) {
const buffer = fs.readFileSync(filePath);
let offset = 0;

const chunks = [];
while (offset < buffer.length) {
const end = Math.min(offset + maxChunkSize, buffer.length);
const chunk = buffer.subarray(offset, end);
chunks.push(chunk);
offset = end;
}
const len = chunks.length;

for (let idx = 0; idx < len; idx += 1) {
const chunk = chunks[idx];
// e.g. src/ts.worker.js.part0, src/ts.worker.js.part1, ...
const chunkPath = `${toDir}${baseName}.part${idx}`;
zip.file(chunkPath, chunk);
}
}

const createJSZip = () => {
const currDate = new Date();
const dateWithOffset = new Date(currDate.getTime() - currDate.getTimezoneOffset() * 60000);
Expand All @@ -24,6 +49,8 @@ const createJSZip = () => {
return new JSZip();
};

// --- utils ---

// 判断是否为beta版本
const version = semver.parse(packageInfo.version);
if (version.prerelease.length) {
Expand Down Expand Up @@ -80,7 +107,8 @@ delete chromeManifest.background.scripts;

delete firefoxManifest.background.service_worker;
delete firefoxManifest.sandbox;
// firefoxManifest.content_security_policy = "script-src 'self' blob:; object-src 'self' blob:";
// firefoxManifest.content_security_policy 是为了支持动态组合的 ts.worker.js Blob URL
firefoxManifest.content_security_policy = "script-src 'self' blob:; object-src 'self' blob:";
firefoxManifest.browser_specific_settings = {
gecko: {
id: `{${
Expand Down Expand Up @@ -128,8 +156,14 @@ await Promise.all([
addDir(chrome, "./dist/ext", "", ["manifest.json"]),
addDir(firefox, "./dist/ext", "", ["manifest.json", "ts.worker.js"]),
]);
// 添加ts.worker.js名字为gz
firefox.file("src/ts.worker.js.gz", await fs.readFile("./dist/ext/src/ts.worker.js", { encoding: "utf8" }));

// Now split ts.worker.js into chunks (<4MB each) for Firefox
addFileInChunks(
firefox,
"./dist/ext/src/ts.worker.js", // source file on disk
"src/", // folder path inside zip
"ts.worker.js" // base name for chunked file
);

// 导出zip包
chrome
Expand Down
13 changes: 9 additions & 4 deletions src/pkg/utils/monaco-editor/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { globalCache, systemConfig } from "@App/pages/store/global";
import EventEmitter from "eventemitter3";
import { languages } from "monaco-editor";
import { findGlobalInsertionInfo, updateGlobalCommentLine } from "./utils";
import { findGlobalInsertionInfo, getTsWorkerPromise, updateGlobalCommentLine } from "./utils";

// 注册eslint
const linterWorker = new Worker("/src/linter.worker.js");
const editorWorker = new Worker("/src/editor.worker.js", { type: "module" });
const tsWorkerPromise = getTsWorkerPromise();

const langPromise = systemConfig.getLanguage();

const langs = {
Expand Down Expand Up @@ -456,11 +459,13 @@ type LangEntry = (typeof langs)["zh-CN"];

export default function registerEditor() {
window.MonacoEnvironment = {
getWorkerUrl(moduleId: any, label: any) {
// https://microsoft.github.io/monaco-editor/typedoc/interfaces/Environment.html#getWorker
// Returns Worker | Promise<Worker>
getWorker(workerId: string, label: string) {
if (label === "typescript" || label === "javascript") {
return "/src/ts.worker.js";
return tsWorkerPromise;
}
return "/src/editor.worker.js";
return editorWorker;
},
};
function asLangEntry<T extends keyof typeof langs>(key: T) {
Expand Down
62 changes: 62 additions & 0 deletions src/pkg/utils/monaco-editor/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,67 @@
import type { editor } from "monaco-editor";

const getPartialBlob = (idx: number): Promise<Blob | null> =>
fetch(chrome.runtime.getURL(`/src/ts.worker.js.part${idx}`))
.then((resp) => (resp.ok ? resp.blob() : null))
.catch(() => null);
const combineBlobsToUrl = async (blobs: Blob[], defaultType?: string): Promise<string> => {
const arrayBuffers: ArrayBuffer[] = [];
let totalLength = 0;

// Read all blobs into ArrayBuffers and compute total length
for (const blob of blobs) {
const arrayBuffer = await blob.arrayBuffer();
arrayBuffers.push(arrayBuffer);
totalLength += arrayBuffer.byteLength; // <-- sum, don't overwrite
}

// Allocate a single Uint8Array large enough for everything
const combined = new Uint8Array(totalLength);

// Copy each buffer into the combined array
let offset = 0;
for (const buffer of arrayBuffers) {
combined.set(new Uint8Array(buffer), offset);
offset += buffer.byteLength;
}

// Create a single Blob out of the combined data
const type = defaultType || blobs[0]?.type || "application/octet-stream";
const combinedBlob = new Blob([combined], { type });

// Create a Blob URL
const blobUrl = URL.createObjectURL(combinedBlob);
// 注意:此处生成的 Blob URL 在整个应用生命周期内用于 Worker,不会被释放。
// 如果未来 Worker 支持销毁重建,请在销毁时调用 URL.revokeObjectURL(blobUrl) 释放资源。
return blobUrl;
};
export const getTsWorkerPromise = () =>
fetch(chrome.runtime.getURL("/src/ts.worker.js.part0"))
.then((resp) => {
return resp.ok ? resp.blob() : null;
})
.catch(() => {
return null;
})
.then(async (blob) => {
let worker: Worker;
if (blob) {
// 有分割
const blobs: Blob[] = [];
let idx = 0;
do {
blobs.push(blob);
blob = await getPartialBlob(++idx);
} while (blob);
const url = await combineBlobsToUrl(blobs, "text/javascript");
worker = new Worker(url, { type: "module" });
} else {
// 沒分割
worker = new Worker("/src/ts.worker.js", { type: "module" });
}
return worker;
});

export const findGlobalInsertionInfo = (model: editor.ITextModel) => {
const lineCount = model.getLineCount();

Expand Down
Loading