chore: add wasm-base64 build target

This commit is contained in:
Zixuan Chen 2024-11-16 19:18:29 +08:00
parent 35e7ea5f54
commit 70c4942fad
No known key found for this signature in database
11 changed files with 156 additions and 35 deletions

View file

@ -0,0 +1,5 @@
---
"loro-crdt": patch
---
Add base64 build target

View file

@ -3,4 +3,5 @@ npm/
nodejs/ nodejs/
bundler/ bundler/
web/ web/
docs/ docs/
base64/

View file

@ -213,6 +213,7 @@
"npm:@rollup/plugin-alias@^5.1.1", "npm:@rollup/plugin-alias@^5.1.1",
"npm:@rollup/plugin-node-resolve@^15.0.1", "npm:@rollup/plugin-node-resolve@^15.0.1",
"npm:@rollup/plugin-typescript@^12.1.1", "npm:@rollup/plugin-typescript@^12.1.1",
"npm:@rollup/plugin-wasm@^6.2.2",
"npm:@typescript-eslint/parser@^6.2.0", "npm:@typescript-eslint/parser@^6.2.0",
"npm:@vitest/ui@^1.0.4", "npm:@vitest/ui@^1.0.4",
"npm:esbuild@~0.18.20", "npm:esbuild@~0.18.20",

View file

@ -21,6 +21,7 @@
"./bundler", "./bundler",
"./nodejs", "./nodejs",
"./web", "./web",
"./base64",
"CHANGELOG.md", "CHANGELOG.md",
"README.md", "README.md",
"LICENSE", "LICENSE",
@ -38,21 +39,22 @@
"@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-typescript": "^12.1.1", "@rollup/plugin-typescript": "^12.1.1",
"rollup": "^3.20.1", "@rollup/plugin-wasm": "^6.2.2",
"tslib": "^2.8.0",
"typescript": "^5.6.3",
"vite-plugin-top-level-await": "^1.2.2",
"vite-plugin-wasm": "^3.1.0",
"@typescript-eslint/parser": "^6.2.0", "@typescript-eslint/parser": "^6.2.0",
"@vitest/ui": "^1.0.4", "@vitest/ui": "^1.0.4",
"esbuild": "^0.18.20", "esbuild": "^0.18.20",
"eslint": "^8.46.0", "eslint": "^8.46.0",
"loro-crdt-old": "npm:loro-crdt@=0.16.0",
"loro-crdt-alpha-4": "npm:loro-crdt@=1.0.0-alpha.4", "loro-crdt-alpha-4": "npm:loro-crdt@=1.0.0-alpha.4",
"loro-crdt-old": "npm:loro-crdt@=0.16.0",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"rollup": "^3.20.1",
"rollup-plugin-dts": "^5.3.0", "rollup-plugin-dts": "^5.3.0",
"rollup-plugin-esbuild": "^5.0.0", "rollup-plugin-esbuild": "^5.0.0",
"tslib": "^2.8.0",
"typescript": "^5.6.3",
"vite": "^4.2.1", "vite": "^4.2.1",
"vite-plugin-top-level-await": "^1.2.2",
"vite-plugin-wasm": "^3.1.0",
"vitest": "^1.4.0" "vitest": "^1.4.0"
} }
} }

View file

@ -0,0 +1,33 @@
import typescript from '@rollup/plugin-typescript';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import { wasm } from '@rollup/plugin-wasm';
const base64Config = {
input: {
'index': 'bundler/index.js',
},
output: {
dir: 'base64',
format: 'es',
sourcemap: false,
entryFileNames: '[name].js',
},
plugins: [
typescript({
tsconfig: 'tsconfig.json',
compilerOptions: {
target: 'ES2020',
declaration: true,
outDir: 'base64',
},
exclude: ['tests/**/*', 'vite.config.*']
}),
nodeResolve(),
wasm({
maxFileSize: 1024 * 1024 * 10,
sync: ["*", "loro_wasm_bg.wasm", "bundler/loro_wasm_bg.wasm"]
}),
]
};
export default [base64Config];

View file

@ -1,5 +1,6 @@
import typescript from '@rollup/plugin-typescript'; import typescript from '@rollup/plugin-typescript';
import { nodeResolve } from '@rollup/plugin-node-resolve'; import { nodeResolve } from '@rollup/plugin-node-resolve';
const createConfig = (format, tsTarget, outputDir) => ({ const createConfig = (format, tsTarget, outputDir) => ({
input: { input: {
'index': 'index.ts', 'index': 'index.ts',
@ -18,7 +19,7 @@ const createConfig = (format, tsTarget, outputDir) => ({
declaration: true, declaration: true,
outDir: outputDir, outDir: outputDir,
}, },
exclude: ['tests/**/*'] exclude: ['tests/**/*', 'vite.config.*']
}), }),
nodeResolve() nodeResolve()
], ],

View file

@ -0,0 +1,3 @@
{
"deno.enable": true
}

View file

@ -1,4 +1,4 @@
import { walk } from "https://deno.land/std/fs/mod.ts"; import { walk } from "https://deno.land/std@0.224.0/fs/mod.ts";
const DIRS_TO_SCAN = ["./nodejs", "./bundler", "./web"]; const DIRS_TO_SCAN = ["./nodejs", "./bundler", "./web"];
const FILES_TO_PROCESS = ["index.js", "index.d.ts"]; const FILES_TO_PROCESS = ["index.js", "index.d.ts"];
@ -8,20 +8,21 @@ async function replaceInFile(filePath: string) {
let content = await Deno.readTextFile(filePath); let content = await Deno.readTextFile(filePath);
// Replace various import/require patterns for 'loro-wasm' // Replace various import/require patterns for 'loro-wasm'
const isWebIndexJs = filePath.includes("web") && filePath.endsWith("index.js"); const isWebIndexJs = filePath.includes("web") &&
filePath.endsWith("index.js");
const target = isWebIndexJs ? "./loro_wasm.js" : "./loro_wasm"; const target = isWebIndexJs ? "./loro_wasm.js" : "./loro_wasm";
content = content.replace( content = content.replace(
/from ["']loro-wasm["']/g, /from ["']loro-wasm["']/g,
`from "${target}"` `from "${target}"`,
); );
content = content.replace( content = content.replace(
/require\(["']loro-wasm["']\)/g, /require\(["']loro-wasm["']\)/g,
`require("${target}")` `require("${target}")`,
); );
content = content.replace( content = content.replace(
/import\(["']loro-wasm["']\)/g, /import\(["']loro-wasm["']\)/g,
`import("${target}")` `import("${target}")`,
); );
if (isWebIndexJs) { if (isWebIndexJs) {
@ -35,23 +36,79 @@ async function replaceInFile(filePath: string) {
} }
} }
async function main() { async function transform(dir: string) {
for (const dir of DIRS_TO_SCAN) { try {
try { for await (
for await (const entry of walk(dir, { const entry of walk(dir, {
includeDirs: false, includeDirs: false,
match: [/index\.(js|d\.ts)$/], match: [/index\.(js|d\.ts)$/],
})) { })
if (FILES_TO_PROCESS.includes(entry.name)) { ) {
await replaceInFile(entry.path); if (FILES_TO_PROCESS.includes(entry.name)) {
} await replaceInFile(entry.path);
} }
} catch (error) {
console.error(`Error scanning directory ${dir}:`, error);
} }
} catch (error) {
console.error(`Error scanning directory ${dir}:`, error);
} }
} }
async function rollupBase64() {
const command = new Deno.Command("rollup", {
args: ["--config", "./rollup.base64.config.mjs"],
});
try {
const { code, stdout, stderr } = await command.output();
if (code === 0) {
console.log("✓ Rollup base64 build completed successfully");
} else {
console.error("Error running rollup base64 build:");
console.error(new TextDecoder().decode(stdout));
console.error(new TextDecoder().decode(stderr));
}
} catch (error) {
console.error("Failed to execute rollup command:", error);
}
const toReplaceFrom = `{
const wkmod = await import('./loro_wasm_bg-b2849b85.js');
const instance = new WebAssembly.Instance(wkmod.default, {
"./loro_wasm_bg.js": imports,
});
__wbg_set_wasm(instance.exports);
}`;
const toReplaceTarget = `
import loro_wasm_bg_js from './loro_wasm_bg-b2849b85.js';
const instance = new WebAssembly.Instance(loro_wasm_bg_js(), {
"./loro_wasm_bg.js": imports,
});
__wbg_set_wasm(instance.exports);
`;
const base64IndexPath = "./base64/index.js";
const content = await Deno.readTextFile(base64IndexPath);
if (!content.includes(toReplaceFrom)) {
throw new Error(
`Could not find string to replace in ${base64IndexPath}`,
);
}
await Deno.writeTextFile(
base64IndexPath,
content.replace(toReplaceFrom, toReplaceTarget),
);
await Deno.copyFile("./bundler/loro_wasm.d.ts", "./base64/loro_wasm.d.ts");
}
async function main() {
for (const dir of DIRS_TO_SCAN) {
await transform(dir);
}
await rollupBase64();
transform("./base64");
}
if (import.meta.main) { if (import.meta.main) {
main(); main();
} }

View file

@ -5,21 +5,21 @@
import "quill/dist/quill.bubble.css"; import "quill/dist/quill.bubble.css";
import "quill/dist/quill.snow.css"; import "quill/dist/quill.snow.css";
import { QuillBinding } from "./binding"; import { QuillBinding } from "./binding";
import { Loro } from "loro-crdt"; import { LoroDoc } from "loro-crdt/base64";
const editor1 = ref<null | HTMLDivElement>(null); const editor1 = ref<null | HTMLDivElement>(null);
const editor2 = ref<null | HTMLDivElement>(null); const editor2 = ref<null | HTMLDivElement>(null);
const editor3 = ref<null | HTMLDivElement>(null); const editor3 = ref<null | HTMLDivElement>(null);
const editor4 = ref<null | HTMLDivElement>(null); const editor4 = ref<null | HTMLDivElement>(null);
const binds: QuillBinding[] = []; const binds: QuillBinding[] = [];
const texts: Loro[] = []; const texts: LoroDoc[] = [];
const editors = [editor1, editor2, editor3, editor4]; const editors = [editor1, editor2, editor3, editor4];
const editorVersions = reactive(["", "", "", ""]); const editorVersions = reactive(["", "", "", ""]);
const online = reactive([true, true, true, true]); const online = reactive([true, true, true, true]);
onMounted(() => { onMounted(() => {
let index = 0; let index = 0;
for (const editor of editors) { for (const editor of editors) {
const text = new Loro(); const text = new LoroDoc();
text.setPeerId(BigInt(index)); text.setPeerId(BigInt(index));
texts.push(text); texts.push(text);
const quill = new Quill(editor.value!, { const quill = new Quill(editor.value!, {

View file

@ -2,7 +2,7 @@
* The skeleton of this binding is learned from https://github.com/yjs/y-quill * The skeleton of this binding is learned from https://github.com/yjs/y-quill
*/ */
import { Delta, Loro, LoroText } from "loro-crdt"; import { Delta, LoroDoc, LoroText } from "loro-crdt/base64";
import Quill, { DeltaOperation, DeltaStatic, Sources } from "quill"; import Quill, { DeltaOperation, DeltaStatic, Sources } from "quill";
// @ts-ignore // @ts-ignore
import isEqual from "is-equal"; import isEqual from "is-equal";
@ -12,18 +12,18 @@ const Delta = Quill.import("delta");
// setDebug("*"); // setDebug("*");
const EXPAND_CONFIG: { [key in string]: "before" | "after" | "both" | "none" } = const EXPAND_CONFIG: { [key in string]: "before" | "after" | "both" | "none" } =
{ {
bold: "after", bold: "after",
italic: "after", italic: "after",
underline: "after", underline: "after",
link: "none", link: "none",
header: "none", header: "none",
}; };
export class QuillBinding { export class QuillBinding {
private richtext: LoroText; private richtext: LoroText;
constructor( constructor(
public doc: Loro, public doc: LoroDoc,
public quill: Quill, public quill: Quill,
) { ) {
doc.configTextStyle({ doc.configTextStyle({

View file

@ -26,6 +26,9 @@ importers:
'@rollup/plugin-typescript': '@rollup/plugin-typescript':
specifier: ^12.1.1 specifier: ^12.1.1
version: 12.1.1(rollup@3.29.4)(tslib@2.8.0)(typescript@5.6.3) version: 12.1.1(rollup@3.29.4)(tslib@2.8.0)(typescript@5.6.3)
'@rollup/plugin-wasm':
specifier: ^6.2.2
version: 6.2.2(rollup@3.29.4)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: ^6.2.0 specifier: ^6.2.0
version: 6.21.0(eslint@8.57.0)(typescript@5.6.3) version: 6.21.0(eslint@8.57.0)(typescript@5.6.3)
@ -564,6 +567,15 @@ packages:
rollup: rollup:
optional: true optional: true
'@rollup/plugin-wasm@6.2.2':
resolution: {integrity: sha512-gpC4R1G9Ni92ZIRTexqbhX7U+9estZrbhP+9SRb0DW9xpB9g7j34r+J2hqrcW/lRI7dJaU84MxZM0Rt82tqYPQ==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@5.1.0': '@rollup/pluginutils@5.1.0':
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@ -2964,6 +2976,12 @@ snapshots:
optionalDependencies: optionalDependencies:
rollup: 4.17.2 rollup: 4.17.2
'@rollup/plugin-wasm@6.2.2(rollup@3.29.4)':
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@3.29.4)
optionalDependencies:
rollup: 3.29.4
'@rollup/pluginutils@5.1.0(rollup@3.29.4)': '@rollup/pluginutils@5.1.0(rollup@3.29.4)':
dependencies: dependencies:
'@types/estree': 1.0.5 '@types/estree': 1.0.5