Skip to content
This repository has been archived by the owner on Oct 18, 2023. It is now read-only.

Commit

Permalink
feat(typescript): support tsconfig paths (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
privatenumber committed Jun 10, 2022
1 parent a35b8f9 commit cc78627
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 15 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -8,6 +8,7 @@ Node.js `import` hook to instantaneously transform TypeScript to ESM on demand u
- Cached for performance boost
- Supports Node.js v12.20.0+
- Handles `node:` import prefixes
- Resolves `tsconfig.json` [`paths`](https://www.typescriptlang.org/tsconfig#paths)

> **Tip:**
>
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -32,7 +32,7 @@
},
"dependencies": {
"@esbuild-kit/core-utils": "^1.3.1",
"get-tsconfig": "^3.0.1"
"get-tsconfig": "^4.0.0"
},
"devDependencies": {
"@pvtnbr/eslint-config": "^0.22.0",
Expand All @@ -58,6 +58,7 @@
"error",
{
"allow": [
"test",
"describe",
"runTestSuite"
]
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/loaders-deprecated.ts
Expand Up @@ -8,8 +8,8 @@ import {
transformDynamicImport,
} from '@esbuild-kit/core-utils';
import {
tsconfigRaw,
sourcemaps,
tsconfigRaw,
tsExtensionsPattern,
getFormatFromExtension,
type ModuleFormat,
Expand Down
35 changes: 31 additions & 4 deletions src/loaders.ts
@@ -1,12 +1,14 @@
import path from 'path';
import { pathToFileURL } from 'url';
import {
transform,
transformDynamicImport,
resolveTsPath,
} from '@esbuild-kit/core-utils';
import {
tsconfigRaw,
sourcemaps,
tsconfigRaw,
tsconfigPathsMatcher,
tsExtensionsPattern,
getFormatFromExtension,
type ModuleFormat,
Expand Down Expand Up @@ -77,11 +79,14 @@ async function tryDirectory(
}
}

const fileProtocol = 'file://';
const isPathPattern = /^\.{0,2}\//;

export const resolve: resolve = async function (
specifier,
context,
defaultResolve,
resursiveCall,
recursiveCall,
) {
// Added in v12.20.0
// https://nodejs.org/api/esm.html#esm_node_imports
Expand All @@ -94,6 +99,27 @@ export const resolve: resolve = async function (
return await tryDirectory(specifier, context, defaultResolve);
}

const isPath = (
specifier.startsWith(fileProtocol)
|| isPathPattern.test(specifier)
);

if (
tsconfigPathsMatcher
&& !isPath // bare specifier
) {
const possiblePaths = tsconfigPathsMatcher(specifier);
for (const possiblePath of possiblePaths) {
try {
return await resolve(
pathToFileURL(possiblePath).toString(),
context,
defaultResolve,
);
} catch {}
}
}

/**
* Typescript gives .ts, .cts, or .mts priority over actual .js, .cjs, or .mjs extensions
*/
Expand All @@ -117,7 +143,8 @@ export const resolve: resolve = async function (
} catch (error) {
if (
(error instanceof Error)
&& !resursiveCall
&& isPath
&& !recursiveCall
) {
if ((error as any).code === 'ERR_UNSUPPORTED_DIR_IMPORT') {
return await tryDirectory(specifier, context, defaultResolve);
Expand All @@ -140,7 +167,7 @@ export const resolve: resolve = async function (

let { format } = resolved;

if (resolved.url.startsWith('file:')) {
if (resolved.url.startsWith(fileProtocol)) {
format = getFormatFromExtension(resolved.url) ?? format;

if (!format) {
Expand Down
8 changes: 5 additions & 3 deletions src/utils.ts
@@ -1,11 +1,13 @@
import path from 'path';
import { installSourceMapSupport } from '@esbuild-kit/core-utils';
import getTsconfig from 'get-tsconfig';
import { getTsconfig, createPathsMatcher } from 'get-tsconfig';

export const sourcemaps = installSourceMapSupport();

const tsconfig = getTsconfig();
export const tsconfigRaw = tsconfig?.config;

export const sourcemaps = installSourceMapSupport();
export const tsconfigRaw = tsconfig?.config;
export const tsconfigPathsMatcher = tsconfig && createPathsMatcher(tsconfig);

export const tsExtensionsPattern = /\.([cm]?ts|[tj]sx)$/;

Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/package-module/tsconfig/src/base-url.ts
@@ -0,0 +1,3 @@
import value from 'resolve-target';

console.log(value);
@@ -0,0 +1,3 @@
import value from 'paths-exact-match';

console.log(value);
@@ -0,0 +1,3 @@
import value from 'p/nested-resolve-target';

console.log(value);
@@ -0,0 +1,3 @@
import value from 'nested-resolve-target/s';

console.log(value);
@@ -0,0 +1 @@
export default 'resolved';
@@ -0,0 +1 @@
export default 'resolved';
6 changes: 6 additions & 0 deletions tests/fixtures/package-module/tsconfig/tsconfig.json
Expand Up @@ -3,5 +3,11 @@
"jsx": "react",
"jsxFactory": "console.log",
"jsxFragmentFactory": "null",
"baseUrl": "./src",
"paths": {
"paths-exact-match": ["resolve-target"],
"p/*": ["utils/*"],
"*/s": ["utils/*"]
},
}
}
34 changes: 32 additions & 2 deletions tests/specs/typescript/tsconfig.ts
Expand Up @@ -2,12 +2,42 @@ import { testSuite, expect } from 'manten';
import type { NodeApis } from '../../utils/node-with-loader';

export default testSuite(async ({ describe }, node: NodeApis) => {
describe('tsconfig', ({ test }) => {
describe('tsconfig', ({ test, describe }) => {
test('jsxFactory & jsxFragmentFactory', async () => {
const nodeProcess = await node.load('./tsx.tsx', {
const nodeProcess = await node.load('./src/tsx.tsx', {
cwd: './tsconfig',
});
expect(nodeProcess.stdout).toBe('div null hello world\nnull null goodbye world');
});

describe('paths', ({ test }) => {
test('resolves baseUrl', async () => {
const nodeProcess = await node.load('./src/base-url.ts', {
cwd: './tsconfig',
});
expect(nodeProcess.stdout).toBe('resolved');
});

test('resolves paths exact match', async () => {
const nodeProcess = await node.load('./src/paths-exact-match.ts', {
cwd: './tsconfig',
});
expect(nodeProcess.stdout).toBe('resolved');
});

test('resolves paths prefix', async () => {
const nodeProcess = await node.load('./src/paths-prefix-match.ts', {
cwd: './tsconfig',
});
expect(nodeProcess.stdout).toBe('resolved');
});

test('resolves paths suffix', async () => {
const nodeProcess = await node.load('./src/paths-suffix-match.ts', {
cwd: './tsconfig',
});
expect(nodeProcess.stdout).toBe('resolved');
});
});
});
});

0 comments on commit cc78627

Please sign in to comment.