Files
bun.sh/test/bake/dev/esm.test.ts

360 lines
8.9 KiB
TypeScript

// ESM tests are about various esm features in development mode.
import { devTest, emptyHtmlFile, minimalFramework } from "../bake-harness";
const liveBindingTest = devTest("live bindings with `var`", {
framework: minimalFramework,
files: {
"state.ts": `
export var value = 0;
export function increment() {
value++;
}
`,
"routes/index.ts": `
import { value, increment } from '../state';
export default function(req, meta) {
increment();
return new Response('State: ' + value);
}
`,
},
async test(dev) {
await dev.fetch("/").equals("State: 1");
await dev.fetch("/").equals("State: 2");
await dev.fetch("/").equals("State: 3");
await dev.patch("routes/index.ts", {
find: "State",
replace: "Value",
});
await dev.fetch("/").equals("Value: 4");
await dev.fetch("/").equals("Value: 5");
await dev.write(
"state.ts",
`
export var value = 0;
export function increment() {
value--;
}
`,
);
await dev.fetch("/").equals("Value: -1");
await dev.fetch("/").equals("Value: -2");
},
});
devTest("live bindings through export clause", {
framework: minimalFramework,
files: {
"state.ts": `
export var value = 0;
export function increment() {
value++;
}
`,
"proxy.ts": `
import { value } from './state';
export { value as live };
`,
"routes/index.ts": `
import { increment } from '../state';
import { live } from '../proxy';
export default function(req, meta) {
increment();
return new Response('State: ' + live);
}
`,
},
test: liveBindingTest.test,
});
devTest("live bindings through export from", {
framework: minimalFramework,
files: {
"state.ts": `
export var value = 0;
export function increment() {
value++;
}
`,
"proxy.ts": `
export { value as live } from './state';
`,
"routes/index.ts": `
import { increment } from '../state';
import { live } from '../proxy';
export default function(req, meta) {
increment();
return new Response('State: ' + live);
}
`,
},
test: liveBindingTest.test,
});
// devTest("live bindings through export star", {
// framework: minimalFramework,
// files: {
// "state.ts": `
// export var value = 0;
// export function increment() {
// value++;
// }
// `,
// "proxy.ts": `
// export * from './state';
// `,
// "routes/index.ts": `
// import { increment } from '../state';
// import { live } from '../proxy';
// export default function(req, meta) {
// increment();
// return new Response('State: ' + live);
// }
// `,
// },
// test: liveBindingTest.test,
// });
devTest("export { x as y }", {
framework: minimalFramework,
files: {
"module.ts": `
function x(value) {
return value + 1;
}
export { x as y };
`,
"routes/index.ts": `
import { y } from '../module';
export default function(req, meta) {
return new Response('Value: ' + y(1));
}
`,
},
async test(dev) {
await dev.fetch("/").equals("Value: 2");
await dev.patch("module.ts", {
find: "1",
replace: "2",
});
await dev.fetch("/").equals("Value: 3");
},
});
devTest("import { x as y }", {
framework: minimalFramework,
files: {
"module.ts": `
export const x = 1;
`,
"routes/index.ts": `
import { x as y } from '../module';
export default function(req, meta) {
return new Response('Value: ' + y);
}
`,
},
async test(dev) {
await dev.fetch("/").equals("Value: 1");
await dev.patch("module.ts", {
find: "1",
replace: "2",
});
await dev.fetch("/").equals("Value: 2");
},
});
devTest("import { default as y }", {
framework: minimalFramework,
files: {
"module.ts": `
export default 1;
`,
"routes/index.ts": `
import { default as y } from '../module';
export default function(req, meta) {
return new Response('Value: ' + y);
}
`,
},
async test(dev) {
await dev.fetch("/").equals("Value: 1");
await dev.patch("module.ts", {
find: "1",
replace: "2",
});
await dev.fetch("/").equals("Value: 2");
},
});
devTest("export { default as y }", {
framework: minimalFramework,
files: {
"module.ts": `
export default 1;
`,
"middle.ts": `
export { default as y } from './module';
`,
"routes/index.ts": `
import { y } from '../middle';
export default function(req, meta) {
return new Response('Value: ' + y);
}
`,
},
async test(dev) {
await dev.fetch("/").equals("Value: 1");
await dev.patch("module.ts", {
find: "1",
replace: "2",
});
await dev.fetch("/").equals("Value: 2");
},
});
devTest("export * as namespace", {
files: {
"index.html": emptyHtmlFile({
scripts: ["index.ts"],
}),
"index.ts": `
import { ns as renamed } from './module';
if (typeof renamed !== 'object') throw new Error('renamed should be an object');
if (renamed.x !== 1) throw new Error('renamed.x should be 1');
if (renamed.y !== 2) throw new Error('renamed.y should be 2');
console.log('PASS');
`,
"module.ts": `
export * as ns from './module2';
`,
"module2.ts": `
export const x = 1;
export const y = 2;
export const ns = "FAIL";
`,
},
async test(dev) {
await using c = await dev.client();
await c.expectMessage("PASS");
},
});
devTest("ESM <-> CJS sync", {
files: {
"index.html": emptyHtmlFile({
scripts: ["index.ts"],
}),
"index.ts": `
const mod = require('./esm');
if (!mod.__esModule) throw new Error('mod.__esModule should be set');
console.log('PASS');
`,
"esm.ts": `
export const x = 1;
`,
},
async test(dev) {
await using c = await dev.client();
await c.expectMessage("PASS");
},
});
devTest("ESM <-> CJS (async)", {
files: {
"index.html": emptyHtmlFile({
scripts: ["index.ts"],
}),
"index.ts": `
const esmImport = await import('./esm'); // TODO: implement sync ESM
const mod = require('./esm');
if (!mod.__esModule) throw new Error('mod.__esModule should be set');
if (esmImport.x !== mod.x) throw new Error('esmImport.x should be equal to mod.x');
if ('__esModule' in esmImport) throw new Error('esmImport.__esModule should be unset');
console.log('PASS');
`,
"esm.ts": `
export const x = 1;
`,
},
async test(dev) {
await using c = await dev.client();
await c.expectMessage("PASS");
},
});
devTest("cannot require a module with top level await", {
files: {
"index.html": emptyHtmlFile({
scripts: ["index.ts"],
}),
"index.ts": `
const mod = require('./esm');
console.log('FAIL');
`,
"esm.ts": `
console.log("FAIL");
import { hello } from './dir';
hello;
`,
"dir/index.ts": `
import './async';
`,
"dir/async.ts": `
console.log("FAIL");
await 1;
`,
},
async test(dev) {
await using c = await dev.client("/", {
errors: [
`error: Cannot require "esm.ts" because "dir/async.ts" uses top-level await, but 'require' is a synchronous operation.`,
],
});
},
});
devTest("function that is assigned to should become a live binding", {
files: {
"index.html": emptyHtmlFile({
scripts: ["index.ts"],
}),
"index.ts": `
// 1. basic test
import { live, change } from "./live.js";
{
if (live() !== 1) throw new Error("live() should be 1");
change();
if (live() !== 2) throw new Error("live() should be 2");
}
// 2. integration test with @babel/runtime
import inheritsLoose from "./inheritsLoose.js";
{
function A() {}
function B() {}
inheritsLoose(B, A);
}
console.log('PASS');
`,
"live.js": `
export function live() {
return 1;
}
export function change() {
live = function() {
return 2;
}
}
`,
"inheritsLoose.js": `
import setPrototypeOf from "./setPrototypeOf.js";
function _inheritsLoose(t, o) {
t.prototype = Object.create(o.prototype), t.prototype.constructor = t, setPrototypeOf(t, o);
}
export { _inheritsLoose as default };
`,
"setPrototypeOf.js": `
function _setPrototypeOf(t, e) {
return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
return t.__proto__ = e, t;
}, _setPrototypeOf(t, e);
}
export { _setPrototypeOf as default };
`,
},
async test(dev) {
await using c = await dev.client();
await c.expectMessage("PASS");
},
});