current commit
261015.3 · vfs: unflag
// Flags: --disable-warning=ExperimentalWarning
import '../common/index.mjs';
import assert from 'assert';
import { pathToFileURL } from 'node:url';
import vfs from 'node:vfs';

const vfsImport = (path) => pathToFileURL(path).href;

// ESM imports are cached by URL and unmounting does not clear the V8 module
// cache; each vfs.create() gets its own layer id so mount points are unique.

// Test importing a simple virtual ES module
{
  const myVfs = vfs.create();
  myVfs.writeFileSync('/hello.mjs', 'export const message = "hello from vfs";');
  const mountPoint = myVfs.mount();

  const { message } = await import(vfsImport(`${mountPoint}/hello.mjs`));
  assert.strictEqual(message, 'hello from vfs');

  myVfs.unmount();
}

// Test importing a virtual module with default export
{
  const myVfs = vfs.create();
  myVfs.writeFileSync('/default.mjs', 'export default { name: "test", value: 42 };');
  const mountPoint = myVfs.mount();

  const mod = await import(vfsImport(`${mountPoint}/default.mjs`));
  assert.strictEqual(mod.default.name, 'test');
  assert.strictEqual(mod.default.value, 42);

  myVfs.unmount();
}

// Test importing a virtual module that imports another virtual module.
// Mount first so the embedded absolute specifier can use the mount point.
{
  const myVfs = vfs.create();
  const mountPoint = myVfs.mount();
  myVfs.writeFileSync(`${mountPoint}/utils.mjs`,
                      'export function add(a, b) { return a + b; }');
  myVfs.writeFileSync(`${mountPoint}/main.mjs`, `
    import { add } from ${JSON.stringify(vfsImport(`${mountPoint}/utils.mjs`))};
    export const result = add(10, 20);
  `);

  const { result } = await import(vfsImport(`${mountPoint}/main.mjs`));
  assert.strictEqual(result, 30);

  myVfs.unmount();
}

// Test importing with relative paths
{
  const myVfs = vfs.create();
  myVfs.mkdirSync('/lib', { recursive: true });
  myVfs.writeFileSync('/lib/helper.mjs', 'export const helper = () => "helped";');
  myVfs.writeFileSync('/lib/index.mjs', `
    import { helper } from './helper.mjs';
    export const output = helper();
  `);
  const mountPoint = myVfs.mount();

  const { output } = await import(vfsImport(`${mountPoint}/lib/index.mjs`));
  assert.strictEqual(output, 'helped');

  myVfs.unmount();
}

// Test importing JSON from VFS (with import assertion)
{
  const myVfs = vfs.create();
  myVfs.writeFileSync('/data.json', JSON.stringify({ items: [1, 2, 3], enabled: true }));
  const mountPoint = myVfs.mount();

  const data = await import(vfsImport(`${mountPoint}/data.json`), { with: { type: 'json' } });
  assert.deepStrictEqual(data.default.items, [1, 2, 3]);
  assert.strictEqual(data.default.enabled, true);

  myVfs.unmount();
}

// Test that real modules still work when VFS is mounted
{
  const myVfs = vfs.create();
  myVfs.writeFileSync('/test.mjs', 'export const x = 1;');
  myVfs.mount();

  const assertMod = await import('node:assert');
  assert.strictEqual(typeof assertMod.strictEqual, 'function');

  myVfs.unmount();
}

// Test mixed CJS and ESM - ESM importing from VFS while CJS also works
{
  const myVfs = vfs.create();
  myVfs.writeFileSync('/esm-module.mjs', 'export const esmValue = "esm";');
  myVfs.writeFileSync('/cjs-module.js', 'module.exports = { cjsValue: "cjs" };');
  const mountPoint = myVfs.mount();

  const { esmValue } = await import(vfsImport(`${mountPoint}/esm-module.mjs`));
  assert.strictEqual(esmValue, 'esm');

  const { createRequire } = await import('module');
  const require = createRequire(import.meta.url);
  const { cjsValue } = require(`${mountPoint}/cjs-module.js`);
  assert.strictEqual(cjsValue, 'cjs');

  myVfs.unmount();
}

// Test ESM bare specifier resolution from VFS node_modules.
{
  const myVfs = vfs.create();
  myVfs.mkdirSync('/app/node_modules/my-vfs-pkg', { recursive: true });
  myVfs.writeFileSync('/app/node_modules/my-vfs-pkg/package.json', JSON.stringify({
    name: 'my-vfs-pkg',
    type: 'module',
    exports: { '.': './index.mjs' },
  }));
  myVfs.writeFileSync(
    '/app/node_modules/my-vfs-pkg/index.mjs',
    'export const fromVfs = true;',
  );
  // Importing module must live in the VFS mount so node_modules walk stays inside VFS.
  myVfs.writeFileSync(
    '/app/entry.mjs',
    "export { fromVfs } from 'my-vfs-pkg';",
  );
  const mountPoint = myVfs.mount();

  const { fromVfs } = await import(vfsImport(`${mountPoint}/app/entry.mjs`));
  assert.strictEqual(fromVfs, true);

  myVfs.unmount();
}