Skip to content

Commit 8cf771c

Browse files
committed
fix vitest node wasm export
1 parent 2c80226 commit 8cf771c

7 files changed

Lines changed: 75 additions & 79 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"loro-crdt": patch
3+
---
4+
5+
Fix Node and Vitest bare imports by resolving the package root to the Node.js
6+
WASM entry under the `node` export condition.

crates/loro-wasm/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"development": "./bundler/index.js",
2626
"default": "./browser/index.js"
2727
},
28+
"node": "./nodejs/index.js",
2829
"import": "./bundler/index.js",
2930
"require": "./nodejs/index.js",
3031
"default": "./bundler/index.js"

examples/bundler-smoke-tests/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ LORO_SMOKE_PACKAGE=loro-crdt@1.12.1 pnpm --dir examples/bundler-smoke-tests run
5959

6060
- `vite5`, `vite6`, `vite7`, `vite8`: Vite production builds with bare
6161
`import "loro-crdt"`.
62+
- `vitest-node`: Vitest Node runtime with bare `import "loro-crdt"`, covering
63+
package conditional exports as seen by test runners.
6264
- `vite5-dev`, `vite6-dev`, `vite7-dev`, `vite8-dev`: Vite dev servers with
6365
bare `import "loro-crdt"` and WASM/top-level-await plugins enabled.
6466
- `vite5-web-mirror-dev`: `import "loro-crdt/web"` plus `loro-mirror`, which

examples/bundler-smoke-tests/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"pretest:dev": "playwright install chromium",
1111
"test:dev": "LORO_SMOKE_MODE=json node ./scripts/run.mjs vite5-dev vite6-dev vite7-dev vite8-dev rolldown-vite-dev webpack5-dev rsbuild2-dev rspack2-dev parcel2-dev next16-turbopack-dev next16-webpack-dev vite5-web-mirror-dev && LORO_SMOKE_MODE=json node ./scripts/dev-runtime.mjs",
1212
"test:smoke": "pnpm run test:browser && pnpm run test:dev",
13-
"test:fast": "node ./scripts/run.mjs vite5 vite6 vite7 vite8 rolldown-vite webpack5 rsbuild2 rspack2 esbuild-default-copy esbuild-base64 rollup-default-copy rollup-base64 parcel2",
13+
"test:fast": "node ./scripts/run.mjs vitest-node vite5 vite6 vite7 vite8 rolldown-vite webpack5 rsbuild2 rspack2 esbuild-default-copy esbuild-base64 rollup-default-copy rollup-base64 parcel2",
1414
"test:next": "node ./scripts/run.mjs next16-turbopack next16-webpack",
1515
"list": "node ./scripts/run.mjs --list"
1616
},

examples/bundler-smoke-tests/scripts/browser-runtime.mjs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { chromium } from "playwright";
99
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1010
const packageDir = path.resolve(__dirname, "..");
1111
const tmpRoot = path.join(packageDir, ".tmp");
12+
const expectedSmokeJson = { map: { text: "mergeable-smoke" } };
13+
const expectedSmokeJsonLiteral = JSON.stringify(expectedSmokeJson);
1214

1315
const defaultCases = [
1416
"vite5",
@@ -228,11 +230,11 @@ async function verifyPage(browser, name, url) {
228230
}
229231

230232
await page.waitForFunction(
231-
() => {
233+
(expected) => {
232234
const value = globalThis.__LORO_JSON_SMOKE__;
233-
return value?.t === "hi" && Object.keys(value).length === 1;
235+
return JSON.stringify(value) === JSON.stringify(expected);
234236
},
235-
null,
237+
expectedSmokeJson,
236238
{ timeout: 60_000 },
237239
);
238240
await page.waitForTimeout(250);
@@ -254,17 +256,15 @@ async function verifyPage(browser, name, url) {
254256
);
255257
}
256258

257-
if (JSON.stringify(jsonSmokeValue) !== JSON.stringify({ t: "hi" })) {
259+
if (JSON.stringify(jsonSmokeValue) !== expectedSmokeJsonLiteral) {
258260
throw new Error(
259261
`${name}: unexpected JSON smoke value ${JSON.stringify(jsonSmokeValue)}`,
260262
);
261263
}
262264

263-
if (!bodyText.includes('{"t":"hi"}')) {
265+
if (!bodyText.includes(expectedSmokeJsonLiteral)) {
264266
throw new Error(
265-
`${name}: expected body to include {"t":"hi"}, got ${JSON.stringify(
266-
bodyText,
267-
)}`,
267+
`${name}: expected body to include ${expectedSmokeJsonLiteral}, got ${JSON.stringify(bodyText)}`,
268268
);
269269
}
270270

examples/bundler-smoke-tests/scripts/dev-runtime.mjs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { chromium } from "playwright";
88
const __dirname = path.dirname(fileURLToPath(import.meta.url));
99
const packageDir = path.resolve(__dirname, "..");
1010
const tmpRoot = path.join(packageDir, ".tmp");
11+
const expectedSmokeJson = { map: { text: "mergeable-smoke" } };
12+
const expectedSmokeJsonLiteral = JSON.stringify(expectedSmokeJson);
1113

1214
const defaultCases = [
1315
"vite5-dev",
@@ -177,11 +179,11 @@ async function verifyPage(browser, name, url) {
177179
}
178180

179181
await page.waitForFunction(
180-
() => {
182+
(expected) => {
181183
const value = globalThis.__LORO_JSON_SMOKE__;
182-
return value?.t === "hi" && Object.keys(value).length === 1;
184+
return JSON.stringify(value) === JSON.stringify(expected);
183185
},
184-
null,
186+
expectedSmokeJson,
185187
{ timeout: 10_000 },
186188
);
187189
await page.waitForTimeout(250);
@@ -199,7 +201,7 @@ async function verifyPage(browser, name, url) {
199201
const jsonSmokeValue = await page.evaluate(
200202
() => globalThis.__LORO_JSON_SMOKE__ ?? null,
201203
);
202-
if (JSON.stringify(jsonSmokeValue) !== JSON.stringify({ t: "hi" })) {
204+
if (JSON.stringify(jsonSmokeValue) !== expectedSmokeJsonLiteral) {
203205
throw new Error(
204206
`${name}: unexpected JSON smoke value ${JSON.stringify(jsonSmokeValue)}`,
205207
);

examples/bundler-smoke-tests/scripts/run.mjs

Lines changed: 51 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const localLoroPackage = path.join(repoRoot, "crates/loro-wasm");
2121
const loroPackageSpec = normalizeLoroPackageSpec(
2222
process.env.LORO_SMOKE_PACKAGE ?? `file:${localLoroPackage}`,
2323
);
24-
const smokeMode = process.env.LORO_SMOKE_MODE ?? "default";
24+
const expectedSmokeJson = { map: { text: "mergeable-smoke" } };
25+
const expectedSmokeJsonLiteral = JSON.stringify(expectedSmokeJson);
2526

2627
function normalizeLoroPackageSpec(spec) {
2728
if (spec === "loro-crdt" || spec.startsWith("loro-crdt@")) {
@@ -31,43 +32,33 @@ function normalizeLoroPackageSpec(spec) {
3132
return spec;
3233
}
3334

34-
const sharedApp = (importPath) => {
35-
if (smokeMode === "json") {
36-
return `import { LoroDoc } from "${importPath}";
37-
38-
const doc = new LoroDoc();
39-
doc.getText("t").insert(0, "hi");
35+
function loroSmokeBody({ renderToDom = true } = {}) {
36+
return `const doc = new Loro();
37+
const map = doc.getMap("map");
38+
const text = map.ensureMergeableText("text");
39+
text.insert(0, "mergeable-smoke");
4040
const value = doc.toJSON();
41+
const expected = ${expectedSmokeJsonLiteral};
4142
42-
if (value.t !== "hi" || Object.keys(value).length !== 1) {
43+
if (JSON.stringify(value) !== JSON.stringify(expected)) {
4344
throw new Error(\`Unexpected Loro JSON: \${JSON.stringify(value)}\`);
4445
}
4546
46-
console.log(value);
4747
globalThis.__LORO_JSON_SMOKE__ = value;
48-
const app = document.getElementById("app");
48+
globalThis.__LORO_SMOKE__ = value.map.text;
49+
${renderToDom
50+
? `const app = document.getElementById("app");
4951
if (app) {
5052
app.textContent = JSON.stringify(value);
5153
}
52-
`;
53-
}
54-
55-
return `import { LoroDoc } from "${importPath}";
56-
57-
const doc = new LoroDoc();
58-
const text = doc.getText("text");
59-
text.insert(0, "bundler-smoke");
60-
61-
const value = doc.toJSON().text;
62-
if (value !== "bundler-smoke") {
63-
throw new Error(\`Unexpected Loro value: \${value}\`);
54+
`
55+
: ""}`;
6456
}
6557

66-
globalThis.__LORO_SMOKE__ = value;
67-
const app = document.getElementById("app");
68-
if (app) {
69-
app.textContent = value;
70-
}
58+
const sharedApp = (importPath) => {
59+
return `import { Loro } from "${importPath}";
60+
61+
${loroSmokeBody()}
7162
`;
7263
};
7364

@@ -85,6 +76,15 @@ const html = `<!doctype html>
8576
`;
8677

8778
const cases = {
79+
"vitest-node": {
80+
description: "Vitest Node runtime with bare loro-crdt import",
81+
dependencies: {},
82+
devDependencies: {
83+
vitest: "3.2.6",
84+
},
85+
setup: setupVitestNode,
86+
command: ["pnpm", "exec", "vitest", "run"],
87+
},
8888
vite5: viteCase("vite5", "vite", "^5.4.21"),
8989
vite6: viteCase("vite6", "vite", "^6.4.2"),
9090
vite7: viteCase("vite7", "vite", "^7.3.2"),
@@ -356,6 +356,22 @@ async function setupBasic(dir, importPath) {
356356
await writeFile(path.join(dir, "src/main.js"), sharedApp(importPath));
357357
}
358358

359+
async function setupVitestNode(dir) {
360+
await writeFile(
361+
path.join(dir, "loro.test.ts"),
362+
`import { describe, expect, it } from "vitest";
363+
import { Loro } from "loro-crdt";
364+
365+
describe("loro-crdt", () => {
366+
it("runs the mergeable text smoke test", () => {
367+
${loroSmokeBody({ renderToDom: false }).replaceAll("\n", "\n ")}
368+
expect(value).toStrictEqual(${expectedSmokeJsonLiteral});
369+
});
370+
});
371+
`,
372+
);
373+
}
374+
359375
async function setupVite(dir) {
360376
await setupBasic(dir, "loro-crdt");
361377
await writeFile(
@@ -374,25 +390,13 @@ async function setupViteWebMirror(dir) {
374390
await writeFile(path.join(dir, "index.html"), html);
375391
await writeFile(
376392
path.join(dir, "src/main.js"),
377-
`import init, { LoroDoc } from "loro-crdt/web";
393+
`import init, { Loro } from "loro-crdt/web";
378394
import wasmUrl from "loro-crdt/web/loro_wasm_bg.wasm?url";
379395
import * as mirror from "loro-mirror";
380396
381397
await init(wasmUrl);
382-
const doc = new LoroDoc();
383-
doc.getText("t").insert(0, "hi");
384-
const value = doc.toJSON();
385-
386-
if (value.t !== "hi" || Object.keys(value).length !== 1) {
387-
throw new Error(\`Unexpected Loro JSON: \${JSON.stringify(value)}\`);
388-
}
389-
390-
globalThis.__LORO_JSON_SMOKE__ = value;
398+
${loroSmokeBody()}
391399
globalThis.__LORO_MIRROR_KEYS__ = Object.keys(mirror);
392-
const app = document.getElementById("app");
393-
if (app) {
394-
app.textContent = JSON.stringify(value);
395-
}
396400
`,
397401
);
398402
await writeViteWasmConfig(dir);
@@ -545,35 +549,14 @@ export default function Page() {
545549
);
546550
await writeFile(
547551
path.join(dir, "components/Smoke.jsx"),
548-
smokeMode === "json"
549-
? `"use client";
552+
`"use client";
550553
551-
import { LoroDoc } from "${importPath}";
554+
import { Loro } from "${importPath}";
552555
553556
export default function Smoke() {
554-
const doc = new LoroDoc();
555-
doc.getText("t").insert(0, "hi");
556-
const value = doc.toJSON();
557-
558-
if (value.t !== "hi" || Object.keys(value).length !== 1) {
559-
throw new Error(\`Unexpected Loro JSON: \${JSON.stringify(value)}\`);
560-
}
561-
562-
console.log(value);
563-
globalThis.__LORO_JSON_SMOKE__ = value;
557+
${loroSmokeBody({ renderToDom: false }).replaceAll("\n", "\n ")}
564558
return <main>{JSON.stringify(value)}</main>;
565559
}
566-
`
567-
: `"use client";
568-
569-
import { LoroDoc } from "${importPath}";
570-
571-
export default function Smoke() {
572-
const doc = new LoroDoc();
573-
const text = doc.getText("text");
574-
text.insert(0, "bundler-smoke");
575-
return <main>{doc.toJSON().text}</main>;
576-
}
577560
`,
578561
);
579562
await writeFile(
@@ -661,7 +644,9 @@ async function runCase(name, testCase) {
661644
if (testCase.afterBuild) {
662645
await testCase.afterBuild(dir);
663646
}
664-
await inspectOutput(dir, testCase.inspect);
647+
if (testCase.inspect) {
648+
await inspectOutput(dir, testCase.inspect);
649+
}
665650
console.log(`[${name}] ok`);
666651
}
667652

0 commit comments

Comments
 (0)