Skip to content

Commit c146366

Browse files
author
Joel Denning
authored
Support for typescript (#3)
* Support for typescript * Self review * Fix lint-staged * Fix formatting
1 parent 678e94c commit c146366

12 files changed

+572
-8
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# node-loader-extensionless
22

3-
A node loader that auto-appends .js extensions to imports
3+
A node loader that auto-appends .js or .ts extensions to imports, based on the parentURL.
4+
5+
When paired with [@node-loader/babel](https://github.com/node-loader/node-loader-babel) loader allows for loading typescript via NodeJS loaders
46

57
## Installation
68

babel.config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["@babel/preset-typescript"]
3+
}

lib/node-loader-extensionless.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,50 @@
1+
import { extname, basename } from "node:path";
2+
3+
const typescriptFileExts = [
4+
".ts",
5+
".tsx",
6+
".mts",
7+
".cts",
8+
".d.ts",
9+
".d.mts",
10+
".d.cts",
11+
".js",
12+
];
13+
114
export async function resolve(specifier, context, nextResolve) {
2-
const pathParts = specifier.split("/");
3-
const fileName = pathParts[pathParts.length - 1];
15+
const fileName = basename(specifier);
416
const isRelativePath =
517
specifier.startsWith("./") ?? specifier.startsWith("../");
618

7-
// Only add .ts extension to path-like specifiers, not bare specifiers
19+
let result;
20+
21+
// Only add extensions to path-like specifiers, not bare specifiers
822
if (isRelativePath && !fileName.includes(".")) {
9-
return nextResolve(specifier + ".js", context);
23+
const parentFileExt = context.parentURL
24+
? extname(context.parentURL) || ".js"
25+
: ".js";
26+
27+
const isTypescript = typescriptFileExts.includes(parentFileExt);
28+
// Mimic the typescript file extension substitution algorithm
29+
// Ideally we'd take into account the moduleResolution option, but for now we skip that
30+
// https://www.typescriptlang.org/docs/handbook/modules/reference.html#file-extension-substitution
31+
const fileExtensionsToTry = isTypescript ? typescriptFileExts : [".js"];
32+
33+
for (const ext of fileExtensionsToTry) {
34+
try {
35+
result = await nextResolve(specifier + ext, context);
36+
break;
37+
} catch {}
38+
}
39+
} else {
40+
result = nextResolve(specifier, context);
41+
}
42+
43+
if (!result) {
44+
throw Error(
45+
`Could not resolve specifier '${specifier}' from parent URL ${context.parentURL}`,
46+
);
1047
}
1148

12-
return nextResolve(specifier, context);
49+
return result;
1350
}

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"lib"
1111
],
1212
"scripts": {
13-
"test": "cross-env node --loader ./lib/node-loader-extensionless.js ./test/run-tests.js",
13+
"test": "cross-env node --import=./test/register.js ./test/run-tests.js",
1414
"lint": "eslint .",
1515
"check-format": "prettier --check .",
1616
"prepare": "husky install"
@@ -41,7 +41,10 @@
4141
"access": "public"
4242
},
4343
"devDependencies": {
44+
"@babel/core": "^7.23.7",
45+
"@babel/preset-typescript": "^7.23.3",
4446
"@baseplate-sdk/utils": "^3.2.1",
47+
"@node-loader/babel": "^2.1.0",
4548
"cross-env": "^7.0.3",
4649
"eslint": "^8.56.0",
4750
"eslint-config-node-important-stuff": "^2.0.0",
@@ -54,6 +57,6 @@
5457
},
5558
"lint-staged": {
5659
"*.js": "eslint --cache --fix",
57-
"*.{js,css,md,yml,eslintrc}": "prettier --write"
60+
"*.{js,css,md,yml,eslintrc,ts,tsx,json}": "prettier --write"
5861
}
5962
}

0 commit comments

Comments
 (0)