Skip to content

Commit

Permalink
feat: improve jiti/native compatibility with node and deno
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Sep 19, 2024
1 parent f16a96c commit 7dfa816
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 64 deletions.
20 changes: 12 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ on:
push:
branches:
- main
- v1
pull_request:
branches:
- main
- v1

permissions:
id-token: write
Expand All @@ -20,7 +18,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [18, 20]
node: [18, 20, 22]
fail-fast: false

steps:
Expand All @@ -33,18 +31,24 @@ jobs:
node-version: ${{ matrix.node }}
cache: "pnpm"
- uses: oven-sh/setup-bun@v2
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 20 }}
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 22 }}
with:
bun-version: latest
- uses: denoland/setup-deno@v1
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 22 }}
with:
deno-version: v1.x
- run: pnpm install
- run: pnpm lint
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 20 }}
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 22 }}
- run: pnpm build
- run: pnpm vitest run --coverage
- run: pnpm test:register
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 20 }}
- run: pnpm test:node-register
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 22 }}
- run: pnpm test:bun --coverage
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 20 }}
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 22 }}
- run: pnpm test:native
if: ${{ matrix.os == 'ubuntu-latest' && matrix.node == 22 }}
- name: nightly release
if: |
github.event_name == 'push' &&
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist
*.log*
coverage
.tmp
deno.lock
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ List of modules (within `node_modules`) to always use native require for them.

List of modules (within `node_modules`) to transform them regardless of syntax.

### `importMeta`

Parent module's [`import.meta`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta) context to use for ESM resolution.
### `experimentalBun`
- Type: Boolean
Expand Down
72 changes: 52 additions & 20 deletions lib/jiti-native.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
* @typedef {import('./types').JitiOptions} JitiOptions
*/

const isDeno = "Deno" in globalThis;

/**
* @param {string} parentURL
* @param {JitiOptions} [_opts]
* @param {string|URL} [parentURL]
* @param {JitiOptions} [jitiOptions]
* @returns {Jiti}
*/
export function createJiti(parentURL = "./_.js", _opts) {
if (isDir(parentURL)) {
parentURL += "/_.js";
}
export function createJiti(parentURL, jitiOptions) {
parentURL = normalizeParentURL(parentURL);

/** @type {Jiti} */
function jiti() {
Expand All @@ -26,7 +26,13 @@ export function createJiti(parentURL = "./_.js", _opts) {

jiti.esmResolve = (id, opts) => {
try {
return import.meta.resolve(id, opts?.parentURL || parentURL);
const importMeta = jitiOptions?.importMeta || import.meta;
if (isDeno) {
// Deno throws TypeError: Invalid arguments when passing parentURL
return importMeta.resolve(id);
}
const parent = normalizeParentURL(opts?.parentURL || parentURL);
return importMeta.resolve(id, parent);
} catch (error) {
if (opts?.try) {
return undefined;
Expand All @@ -37,15 +43,38 @@ export function createJiti(parentURL = "./_.js", _opts) {
};

jiti.import = async function (id, opts) {
const resolved = await this.esmResolve(id, opts);
if (!resolved) {
if (opts?.try) {
return undefined;
} else {
throw new Error(`Cannot resolve module '${id}'`);
for (const suffix of ["", "/index"]) {
// prettier-ignore
for (const ext of ["", ".js", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"]) {
try {
const resolved = this.esmResolve(id + suffix + ext, opts);
if (!resolved) {
continue;
}
let importAttrs = undefined
if (resolved.endsWith('.json')) {
importAttrs = { with: { type: 'json'}}
}
return await import(resolved, importAttrs);
} catch (error) {
if (error.code === 'ERR_MODULE_NOT_FOUND' || error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') {
continue
}
if (opts?.try) {
return undefined;
}
throw error;
}
}
}
return import(resolved);
if (!opts?.try) {
const parent = normalizeParentURL(opts?.parentURL || parentURL);
const error = new Error(
`[jiti] [ERR_MODULE_NOT_FOUND] Cannot import '${id}' from '${parent}'.`,
);
error.code = "ERR_MODULE_NOT_FOUND";
throw error;
}
};

jiti.transform = () => {
Expand Down Expand Up @@ -78,12 +107,15 @@ function unsupportedError(message) {
);
}

function isDir(filename) {
if (filename instanceof URL || filename.startsWith("file://")) {
return false;
function normalizeParentURL(input) {
if (!input) {
return "file:///";
}
if (input instanceof URL || input.startsWith("file://")) {
return input;
}
if (filename.endsWith("/")) {
return true;
if (input.endsWith("/")) {
input += "_"; // append a dummy filename
}
return !filename.split("/").pop().includes(".");
return `file://${input}`;
}
5 changes: 5 additions & 0 deletions lib/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ export interface JitiOptions {
*/
transformModules?: string[];

/**
* Parent module's import.meta context to use for ESM resolution.
*/
importMeta?: ImportMeta;

/**
* Enable experimental native Bun support for transformations.
*/
Expand Down
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,19 @@
"build": "pnpm clean && NODE_ENV=production pnpm webpack",
"clean": "rm -rf dist",
"dev": "pnpm clean && pnpm webpack --watch",
"jiti": "JITI_DEBUG=1 ./lib/jiti-cli.mjs",
"jiti": "JITI_DEBUG=1 lib/jiti-cli.mjs",
"lint": "eslint . && prettier -c src lib test stubs",
"lint:fix": "eslint --fix . && prettier -w src lib test stubs",
"prepack": "pnpm build",
"release": "pnpm build && pnpm test && changelogen --release --prerelease --push --publish --publishTag 2x",
"test": "pnpm lint && pnpm test:types && vitest run --coverage && pnpm test:register && pnpm test:bun",
"test:types": "tsc --noEmit",
"test:register": "node ./test/register-test.mjs",
"test:bun": "bun --bun test test/bun"
"test": "pnpm lint && pnpm test:types && vitest run --coverage && pnpm test:node-register && pnpm test:bun && pnpm test:native",
"test:bun": "bun --bun test test/bun",
"test:native": "pnpm test:native:bun && pnpm test:native:node && pnpm test:native:deno",
"test:native:bun": "bun --bun test test/native/bun.test.ts",
"test:native:deno": "deno test -A test/native/deno.test.ts",
"test:native:node": "node --test --experimental-strip-types test/native/node.test.ts",
"test:node-register": "node --test test/node-register.test.mjs",
"test:types": "tsc --noEmit"
},
"devDependencies": {
"@babel/core": "^7.25.2",
Expand Down
26 changes: 0 additions & 26 deletions test/bun-native.test.ts

This file was deleted.

21 changes: 21 additions & 0 deletions test/native/bun.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { readdir } from "node:fs/promises";
// @ts-ignore
import { test } from "bun:test";

import { createJiti } from "../../lib/jiti-native.mjs";

const fixtures = await readdir(new URL("../fixtures", import.meta.url));

const jiti = createJiti(import.meta.url, { importMeta: import.meta });

const ignore = new Set(["error-runtime", "error-parse", "typescript"]);

for (const fixture of fixtures) {
if (ignore.has(fixture)) {
continue;
}

test("fixtures/" + fixture + " (ESM) (Native)", async () => {
await jiti.import("../fixtures/" + fixture);
});
}
36 changes: 36 additions & 0 deletions test/native/deno.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { readdir } from "node:fs/promises";
import { test } from "node:test";

import { createJiti } from "../../lib/jiti-native.mjs";

const fixtures = await readdir(new URL("../fixtures", import.meta.url));

const jiti = createJiti(import.meta.url, { importMeta: import.meta });

// Mostly broken because default type is set to commonjs in fixtures root
const ignore = new Set(
[
"error-runtime",
"error-parse",
"pure-esm-dep",
"proto",
"json",
"esm",
"env",
"typescript",
"top-level-await",
"exotic",
"circular",
"import-map",
].filter(Boolean),
);

for (const fixture of fixtures) {
if (ignore.has(fixture)) {
continue;
}

test("fixtures/" + fixture + " (ESM) (Native)", async () => {
await jiti.import("../fixtures/" + fixture);
});
}
35 changes: 35 additions & 0 deletions test/native/node.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { readdir } from "node:fs/promises";
import { test } from "node:test";

import { createJiti } from "../../lib/jiti-native.mjs";

const fixtures = await readdir(new URL("../fixtures", import.meta.url));

const jiti = createJiti(import.meta.url, { importMeta: import.meta });

// Mostly broken because default type is set to commonjs in fixtures root
const ignore = new Set(
[
"error-runtime",
"error-parse",
"pure-esm-dep",
"proto",
"json",
"esm",
"env",
"typescript",
"top-level-await",
"exotic",
"circular",
].filter(Boolean),
);

for (const fixture of fixtures) {
if (ignore.has(fixture)) {
continue;
}

test("fixtures/" + fixture + " (ESM) (Native)", async () => {
await jiti.import("../fixtures/" + fixture);
});
}
File renamed without changes.
6 changes: 1 addition & 5 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
exclude: [
"**/test.{ts,mjs,cjs,js}",
"node_modules/**/*",
"test/bun*.test.ts",
],
include: ["test/fixturtes.test.ts", "test/utils.test.ts"],
},
});

0 comments on commit 7dfa816

Please sign in to comment.