I saw some questions about this problem, none of them does not work
I have a nodejs project along with Typescript. I do not like to use a relative path.I get the following error, when I set path in tsconfig :
Cannot find module '#app/controllers/main'
// main.ts
export const fullName = "xxxx";
...
// app.ts
import { fullName } from '#app/controllers/main'
...
This is the structure of my project :
-node_modules
-src
----controllers
---------------main.ts
----app.ts
-package.json
-tsconfig.json
tsconfig:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"baseUrl": ".",
"paths": {
"#app/*": ["src/*"]
},
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
Where is my problem?
Thanks in advance.
Update 2023
Install development dependencies
npm install --save-dev \
ts-patch \
typescript-transform-paths \
tsconfig-paths
ts-patch
Directly patch typescript installation to allow custom transformers (plugins).
The main difference why I prefer ts-patch over ttypescript is that there is no need to change the compiler (ttsc) because (hence the name) tsc is patched.
typescript-transform-paths
Transforms absolute imports to relative from paths in your tsconfig.json.
tsconfig-paths
Load modules whose location is specified in the paths section of tsconfig.json. Both loading at run-time and via API are supported.
Update tsconfig.json
Note: See paths and plugins
{
"compilerOptions":{
/* A series of entries which re-map imports to lookup locations relative to the baseUrl */
"paths":{
"~/*":[
"./src/*"
]
},
/* List of language service plugins */
"plugins":[
/* Transform paths in output .js files */
{
"transform":"typescript-transform-paths"
},
/* Transform paths in output .d.ts files */
{
"transform":"typescript-transform-paths",
"afterDeclarations": true
}
]
}
}
Patch Typescript
Note: This is NOT persistent
npx ts-patch install
Edit/Add prepare script in package.json to patch Typescript persistently
Note: This IS persistent
{
// ...
"scripts": {
"prepare": "npx ts-patch install -s"
}
}
Usage in import
import { hello } from '~/world';
Compile as always
npx tsc
Old Answer
Unfortunately (and I don't know why) the Typescript compiler currently does not support the paths transformation very well.
Here is my solution:
I used the solution with this project.
Install devDependencies
ttypescript -> npm install ttypescript --save-dev -> TTypescript (Transformer TypeScript) solves the problem by patching on the fly the compile module to use transformers from tsconfig.json.
typescript-transform-paths -> npm install typescript-transform-paths --save-dev -> Transforms absolute imports to relative from paths in your tsconfig.json.
tsconfig-paths -> npm install tsconfig-paths --save-dev -> Use this to load modules whose location is specified in the paths section of tsconfig.json. Both loading at run-time and via API are supported.
ts-node-dev -> npm install ts-node-dev --save-dev -> It restarts target node process when any of required files changes (as standard node-dev) but shares Typescript compilation process between restarts
tsconfig.json
Update the tsconfig.json file with the following options:
{
"compilerOptions": {
/* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"paths": {
"#app/*": [
"./src/*"
]
},
/* Advanced Options */
"plugins": [
{
"transform": "typescript-transform-paths"
}
],
}
}
Build
For the compilation phase use ttsc instead of tsc with the configuration file. See the snippet below:
npx ttsc --p ./tsconfig.json
Development mode with autoreload
When you are in dev mode use the following script (put it in the scripts options in package.json) to automatically reload the project with the correct paths. The src/app.ts is the "entry point" of your application located under the src folder.
npx ts-node-dev --prefer-ts true --no-notify -r tsconfig-paths/register --watch src --transpileOnly src/app.ts
PS: Using ts-node-dev increase the speed significantly.
I tweak #Carlo Corradini answer a little bit,
here is my approach to fix this issue.
install required plugins:
npm i -D ttypescript typescript-transform-paths ts-node tsconfig-paths. these are packages that will help us to transform the paths.
ttypescript-> https://www.npmjs.com/package/ttypescript
then, on tsconfig.json, I put:
{
"ts-node": {
"transpileOnly": true,
"require": [ // set this so you dont need to use ts-node -r
"typescript-transform-paths/register",
"tsconfig-paths/register"
]
},
"compilerOptions": {
"composite": true,
"rootDir": ".", // must define
"baseUrl": "src", // must define, the paths will relative to this
"outDir": "lib",
"skipLibCheck": false,
"paths": {
"#app/*": ["./*"],
"#controllers/*": ["./controllers/*"],
},
"plugins": [
{ "transform": "typescript-transform-paths" }
]
}
}
then on your codes you can use:
import myControllers from "#controllers/main.ts"
now you can compile, npx ttsc -p tsconfig.json
and to run on ts-node use npx ts-node -p tsconfig.json src/app.ts
Your syntax is correct.
It appears that, at the time of writing, ts-node-dev does not support the paths entry of tsconfig.json.
This github issue discusses the problem and presents workaround options.
Related
I am trying to import exported members from local nodejs packages. These worked fine before using Vite (standard CRA webpack setup), but ever since switching to Vite, I am getting the following error:
Uncaught SyntaxError:
The requested module '/#fs/Users/...{long_path}.../lib/shared/countries/build/index.js'
does not provide an export named 'countryListAllIsoData'
The Vite project was setup using npm create vite#latest using the typescript, react, swc preset.
These failed imports only happen on local packages (none installed from the npm registery) so I am sure the issue is on my end, but I have not been able to track down what is causing it.
Question 1: How can I make these imports work?
As they did work using Webpack, I'm not quite sure why they don't work using Vite? Vite required my project to setup the project as a module (in package.json - "type": "module"). Does the way imports work in a module break my current code?
Question 2: How can I make my linter display these errors?
I am using VS Code which seems to be perfectly happy with the way the module is imported. No errors are shown and I can go to the definition of the imports without any problems (leading me to believe everything is exported correctly).
Relevant code snippets
I am importing as follows:
import { countryListAllIsoData } from "countries";
The tsconfig.json file is untouched (exactly as npm create vite#latest created). The same applies for the package.json file, except some modules were installed (e.g. the one causing this issue).
The local package causing this issue is named countries and is fairly small and simple. It has the following package.json file:
/// package.json
{
"name": "countries",
"version": "1.0.0",
"description": "",
"main": "./build/index.js",
"exports": {
".": "./build/index.js"
},
"scripts": {
"build": "tsc",
"prepare": "npm run build"
},
"keywords": [],
"author": "Bram Vanbilsen",
"license": "UNLICENSED",
"devDependencies": {
"typescript": "^4.8.4"
}
}
The tsconfig.json file:
{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declarationMap": true, /* Create sourcemaps for d.ts files. */
"outDir": "./build", /* Specify an output folder for all emitted files. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
The index.ts file:
export const countryListAllIsoData = [...] as const;
I'm building Next.js monorepo project with TS, yarn workspace.
For example, I have two packages in yarn workspace, /web and /api. /web is a next.js project and /api is a shared subpackage that is used by /web.
/my-project <-- project root
package.json
/web
src/
package.json
tsconfig.json
next.config.js
/api
src/ <-- rootDir
dist/ <-- outDir
package.json
tsconfig.json
...
// /my-project/package.json
{
"private": true,
"workspaces": [
"web",
"api"
],
}
// /web/packcage.json
{
"dependencies": {
"#api": "workspace:*"
}
}
// /api/packcage.json
{
"name": "#api"
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
}
// /api/tsconfing.json
{
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"module": "ES6",
"moduleResolution": "node",
"target": "ES6",
},
}
From /api's tsconfig, TS creates transpiled result which has es6 module system.("module": "ES6")
As nextjs does not support external packages built with ES module, I expected that /api package does not work in /web project. However, it works well.
Why this can be possible?
When I tried to use some packages which use ES module(built only for browser), I met some errors something like unexpected token: export. At that time, I have to transpile them
manually by using next-transpile-modules and then it resolved the issue. But, in this case, nextjs work with es module package without any issue. Did I misunderstand something about this?
NextJs 11.1+ has an experimental option to support esm. The feedback thread is available here.
/**
* #type {import('next').NextConfig}
*/
const nextConfig = {
experimental: {
// Prefer loading of ES Modules over CommonJS
// #link {https://nextjs.org/blog/next-11-1#es-modules-support|Blog 11.1.0}
// #link {https://github.com/vercel/next.js/discussions/27876|Discussion}
esmExternals: true,
// Experimental monorepo support
// #link {https://github.com/vercel/next.js/pull/22867|Original PR}
// #link {https://github.com/vercel/next.js/discussions/26420|Discussion}
externalDir: true,
}
}
export default nextConfig;
ESM generally requires more than just "modules": "es6" in config... But as you noticed, I realized that some esm packages like '#sindrehorsus' ones where working without the experimental flag.
That said setting the experimental.esmExternals to true will solve most edge cases and should makes next-transpile-modules obsolete.
Note that generally I tend to set "modules" to "esnext" rather than "es6" to be inline to what nextjs recommends.
PS: If it helps, you can have a look this comment: https://stackoverflow.com/a/69554480/5490184 or the https://github.com/belgattitude/nextjs-monorepo-example.
We have a NodeJS app written in typescript. We use modules using relative paths
e.g.
import foo from '#/bar';
We have following paths entry in tsconfig.json
"paths": {
"#/*": [
"./*"
]
}
As typescript does not have path transformation support, we are using ttypescript to compile and override tsconfig.json with tsconfig.build.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"plugins": [
{
"transform": "#zerollup/ts-transform-paths"
}
]
},
"exclude": ["node_modules/**", "tests/**"]
}
and use yarn to build the code
yarn clean && ttsc --project ./tsconfig.build.json
Now this build the code and converts the relative #module paths to absolute.
Next we are using Serverless framework to build and deploy this code as lambda. Serverless uses tsconfig.json to build the code and effectively ignore the custom path transformation.
Question:
is it possible to solve the above problem without using ttypescript and plugin?
Is it possible to configure serverless to use ttypescript and custom tsconfig
Any other way to solve the problem?
Thanks
I have a few premises before starting:
I want a shared package which will be included in three other packages,
typescript,
just one node modules,
multiplatform usage (Windows / Linux),
use dependencies from the shared package,
(live reload will be nice),
not to lose the ability to later publish shared package (the other three packages are web servers, but now is under heavy development),
common scripts build:web build:api build:admin from global package.
My dreamed folder structure is:
/
/node_modules/***
/packages/shared/package.json // Shared dependencies like typeorm, lodash etc..
/packages/shared/src/index.ts
/packages/shared/src/models/User.ts
/packages/web/package.json
/packages/web/tsconfig.json
/packages/web/src/index.ts // first express server (using shared code)
/packages/api/package.json
/packages/api/tsconfig.json
/packages/api/src/index.ts
...
/package.json
/tsconfig.common.json
There are a lot of solutions across the internet - Lerna, typescript project reference, npm / yarn link... but I am not able to find a suitable solution...
For example, this https://github.com/Quramy/lerna-yarn-workspaces-example is cool, but I was not able to setup to run something like this:
// package.json
{
"scripts": {
"debug:web": "concurrently \"(cd packages/shared && tsc -w)\" \"(cd packages/web && tsc -w)\" \"(cd packages/web && nodemon --inspect dist/index.js --watch .. /)\""
...
I want to continuously build the shared package (generate dist/index.js and .ts files) and in nodemon restart server after something changed in ../ (packages folder).
After a few hours I gave up to chance to have code reloading and try that via "easiest-way" I could find - no package in shared but include code directly via:
// shared/index.ts
export const createConnection = () => ....
// web/index.ts
import { createConnection } from 'shared'
it looks like it does not work either:
// tsconfig.json
{
"extends": "../../tsconfig.settings.json",
"compilerOptions": {
"rootDir": "../",
"outDir": "dist",
"baseUrl": "./",
"paths": {
"shared": [ "../shared/src/" ]
}
}
}
the problem is now:
File /shared/src/index.ts is not listed within the file list of the project in tsconfig.json. Projects must list all files or use an 'include' pattern.
Next step is (obviously):
{
"extends": "../../tsconfig.settings.json",
"include": [
"./src/**/*.ts",
"../shared/src/**/*.ts"
],
"compilerOptions": {
"rootDir": "../",
"outDir": "dist",
"baseUrl": "./",
"paths": {
"shared": [ "../shared/src/" ]
}
}
}
This works! No errors, but no files in "dist" also... LOL
I am pretty sure, there is some nice solution, example or it won't work either, but I was not able to find it or build it.
Correct me if I'm wrong, but you need an example where the project is separated to packages in a monorepository, written in Typescript and some of the packages depend on each-other.
If that's the case, I created project like this. I used one of Typescript new features: project references. You can specify other tsconfigs in your config file and you can define a dependency for them. For example: package B depends on package A. Typescript is going to compile B and use its declarations before start compile A.
Here is a small example for the tsconfig
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib"
},
"include": ["src"],
"exclude": ["lib"],
"references": [{ "path": "../util" }, { "path": "../crud-request" }]
}
You can get more informations here: https://www.typescriptlang.org/docs/handbook/projectreferences.html
I used this project as an example to build my own, maybe it'll be useful for you too:
https://github.com/nestjsx/crud
I am writing a node web application using TypeScript and Express.
I managed to get everything working, however the issue I have run into is that my imports don't seem to respect the baseUrl option of my tsconfig.json.
Here is how my tsconfig.json looks like:
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"pretty": true,
"sourceMap": true,
"target": "es6",
"outDir": "./dist",
"baseUrl": "./src"
},
"exclude": [
"node_modules"
]
}
As an example, let's say I have the following files:
- dist/
- a/
- car.js
- b/
- hat.js
In car.js I can easily require hat.js by doing:
import hat from '../../b/hat'; // relative version
This works as expected.
However, I also want to be able to do the following:
import hat from 'b/hat'; // absolute version
This does not generate any issues during compilation or shows any IDE errors, as the tsconfig.json specifies the baseUrl as ./src. Thus the above is perfectly valid TypeScript code.
However, my expectation was that the code will compile down to the relative version:
const hat = require('../../b/hat');
Unfortunatly it compiled down to:
const hat = require('b/hat');
and thus predictably does not work.
Other users have solved this issue by using 3rd party tools such as: https://github.com/s-panferov/awesome-typescript-loader
https://decembersoft.com/posts/say-goodbye-to-relative-paths-in-typescript-imports/
But majority of these tools is designed to work with WebPack, which isn't really suitable for an node back-end application. This is because we are running a long-running server, and thus won't benefit from being bundled into a single file versus several different files (unlike front-end web development).
My question is, how can I compile my TypeScript files, without WebPack, so that absolute imports works correctly.
So the way to do it is to transform back the non-relative imports to relative imports after build so that commonjs will recognize them. And the way to easily do it is the following:
Step 1: Install the transform modules:
npm i -D typescript-transform-paths
npm i -D ttypescript
Step 2: Modify tsconfig.json
"compilerOptions: {
...
"plugins": [
{ "transform": "typescript-transform-paths" },
{ "transform": "typescript-transform-paths", "afterDeclarations": true }
]
}
Step 3: Modify package.json
We will use ttypescript to build the project instead of tsc inorder to use transformers.
"build": "ttsc"
Step 4: Build and Start the project
npm run build
npm start
OP I haven't gotten this to work yet, but this article may provide some guidance: https://levelup.gitconnected.com/get-rid-of-relative-import-path-hell-in-your-typescript-project-9952adec2e84
TL;DR
yarn add link-module-alias
package.json
...
"_moduleAliases": {
"~": "dist"
}
...
My question is, how can I compile my TypeScript files, without WebPack, so that absolute imports works correctly.
My opinion
Don't. In fact don't do it even do it with webpack.
Reason
The node resolution algorithm is complicated enough : https://nodejs.org/api/modules.html#modules_all_together and doesn't need to be complicated further with "and oh, now we have baseurl".
If you still want to do it, here are your options.