Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve jiti/native compatibility with node and deno #294

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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. (only used for `jiti/native` import).

### `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}`;
}
7 changes: 7 additions & 0 deletions lib/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ export interface JitiOptions {
*/
transformModules?: string[];

/**
* Parent module's import.meta context to use for ESM resolution.
*
* (Only used for `jiti/native` import)
*/
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"],
},
});