Absolute path not working in Vite project React TS - reactjs

I'm struggling to get absolute path to work in a Vite react-ts project.
Here's how I created the project
npm init #vitejs/app
npx: installed 6 in 1.883s
√ Project name: ... test-vite
√ Select a framework: » react
√ Select a variant: » react-ts
Then I added baseUrl to tsconfig.json
based on the TS official doc:
{
"compilerOptions": {
"baseUrl": "./src",
...
followed by adding a simple component (T:\test-vite\src\components\Test.tsx)
import React from "react";
const Test = () => <h1>This is a Test.</h1>;
export default Test;
Finally I import the Test component in App.tsx
but it won't let me use absolute path:
import Test from "components/Test";
I get this error
whereas if I use relative path, the app works in dev & build mode without any error:
import Test from "./components/Test";
How can I make absolute path work in the project?

There are two problems here:
Tell typescript how to resolve import path
Tell vite how to build import path
You only tell typescript how to resolve, but vite don't konw how to build. So refer to the official document resolve.alias, maybe this is what you want:
// vite.config.ts
{
resolve: {
alias: [
{ find: '#', replacement: path.resolve(__dirname, 'src') },
],
},
// ...
}
You can import path like this (or any module under ./src):
import Test from "#/components/Test";
import bar from "#/foo/bar"
Moreover, you can use vite plugin vite-tsconfig-paths directly, it makes you don't have to manually configure resolve.alias
Follow the instructions below:
Install vite-tsconfig-paths as dev dependency
Inject vite-tsconfig-paths using the vite.config.ts module
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [tsconfigPaths()],
})

I came here through search results, I was looking for something different, namely, how to do a simple absolute import like import { foo } from 'src/lib/foo.ts'
So if you have a /src directory that contains all code and want to use an absolute import path.
vite.config.ts
export default defineConfig({
...
resolve: {
alias: {
src: path.resolve('src/'),
},
}
})
tsconfig.json
{
"compilerOptions": {
...
"baseUrl": "./"
}
}
Note that this is a trick: src is an alias, so it appears like the path is absolute in Vite. If you have another directory in the root dir, adjacent to /src, you will need to add another alias for that directory.

#Yuns solutions works, but it shows error in vscode. And it was breaking auto-import in vs code.
To make it work in vscode and vite both, I added alias in both tsconfig and vite.config.
// tsconfig.json
{
"paths": {
"#/*": ["src/*"]
}
// ...
}
// vite.config.ts
{
resolve: {
alias: [{ find: '#', replacement: '/src' }],
},
// ...
}
Then, I could import like below (svelte app is in src directory)
import Header from '#/components/Header.svelte

Looking for import {...} from "src/foo/bar";?
I also came here through search results like user Maciej Krawczyk, but the # part also wasn't what I was interested in. That user's answer helped me, but I had trouble with the path.resolve part (ReferenceError because path wasn't defined), so I used a slightly different approach:
vite.config.ts
export default defineConfig({
...
resolve: {
alias: {
src: "/src",
},
},
...
})
Vite's resolver considers the absolute path /src to be from where the server is serving (see GH issue). So if you're running/building from the root of your project with src as a top level directory -- which is pretty common -- this alias points Vite in the right direction.
tsconfig.json
{
"compilerOptions": {
...
"baseUrl": "./",
"paths": {
"src/*": [
"./src/*"
]
}
}
}
This is basically blindly following this StackOverflow answer. TypeScript needs to know that we have special resolving going on as well, otherwise TS will be freaked out about your non-existent src package and not know where it should go looking. (Note: After I changed my TS config, VSCode didn't immediately pick up the change, so I was still getting warnings. I quit, re-opened, and had to wait ~15sec for the warnings to go away.)

1) You need to install these packages:
npm i path
yarn add path
npm i #types/node
yarn add #types/node
npm i vite-tsconfig-paths
yarn add vite-tsconfig-paths
2) Then in the vite.config file:
import { defineConfig } from 'vite';
import react from '#vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
import path from 'path';
export default defineConfig({
base: './',
resolve: {
alias: {
Components: path.resolve(__dirname, './src/components'),
Assets: path.resolve(__dirname, './src/assets'),
},
},
plugins: [react(), tsconfigPaths()],
});
3) And now we have to tell TS those same paths that we defined in the alias:
{
"compilerOptions": {
...,
"baseUrl": "./",
"paths": {
"src/*": [ "./src/*" ],
// We define this path for all files/folders inside
// components folder:
"Components/*": [ "./src/components/*" ],
// We define this path for the index.ts file inside the
// components folder:
"Components": [ "./src/components" ],
"Assets/*": [ "./src/assets/*" ],
"Assets": [ "./src/assets" ]
}
},
...
}
4) reload vscode: As the comment above said, press Fn1 and type "reload with extensions disabled", re-enabling extensions from the popup.
Now try to import
import Test from "components/Test";
it should work.

For anyone looking specifically to add the nice import "#/something-in-src" syntax like Vue has with the latest (as of posting this answer) version of Vite + React + TypeScript, here's how I did it:
Make sure #types/node is installed as a dev dependency. This didn't come with the latest version of Vite for me, and it will make "path" and __dirname throw an undefined error.
vite.config.ts
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: [{ find: "#", replacement: path.resolve(__dirname, "src") }],
},
plugins: [react()],
});
tsconfig.json
Add:
{
"compilerOptions": {
"paths": {
"#/*": ["./src/*"]
}
}
}

For anyone who stucks after all required changes, you need to reload vscode.
My config files:
tsconfig.json
"baseUrl": "./",
"paths": {
"#/*": ["src/*"]
}
vite.config.ts
import { defineConfig } from 'vite';
import react from '#vitejs/plugin-react';
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: { '#': path.resolve(__dirname, './src') },
},
plugins: [react()],
});
In above code you need to have 2 libraries installed:
'path': npm i path
'#types/node': npm i #types/node
After configure your project files you need to reload vscode. To do that press ctrl + P and type ">reload with extensions disabled", after that you will get popUp to activate extensions again click it, and your absoulte path should work
If someone installed vite-tsconfig-paths library, you also need to reload the vscode, remember to import given library to vite.config.ts
export default defineConfig({ plugins: [react(), tsconfigPaths()] });
With package you get default 'components/File' import instead of '#components/File' import.

Related

vite js build file loaded but empty screen

I am building react app using vite.
I run the following command, the preview show everything is working.
npm run build
npm run preview
But when I transfer to the IIS or tomcat server, it show blank page without any error.
Not sure what is happen?
The only different when I deploy to web server I put it into a folder example C:\inetpub\wwwroot\example
Below is my vite.config.ts:
import { defineConfig } from 'vite';
import react from '#vitejs/plugin-react';
import viteTsconfigPaths from 'vite-tsconfig-paths';
import svgrPlugin from 'vite-plugin-svgr';
import { fileURLToPath, URL } from 'url';
// https://vitejs.dev/config/
export default defineConfig({
server: {
port: 3000
},
plugins: [
react(), viteTsconfigPaths(), svgrPlugin()
],
define: {
"global": {},
},
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url)),
},
mainFields: [],
},
css: {
preprocessorOptions: {
scss: {
// example : additionalData: `#import "./src/styles/variables";`
// There is need to include the .scss file extensions.
// additionalData: `#import "./src/styles/variables";`,
},
},
},
base: './',
});
It sounds like your server doesn't know where to find the .html file. Did you configure it to use /wwwroot/example/public as the root?
Try placing the contents of /public (the .js, .css, .html that the build process created) directly inside the /wwwroot folder. So then it would just look like /wwwroot/index.html.

How do I get HMR (Hot Module Replacement) working with a TypeScript React Monorepo in Vite

I've got a React monorepo (build in TypeScript) and I've recently been switching over from Webpack to Vite. However I'm having real difficult in getting HMR working correctly within Vite (I believe because we separately build our packages).
I'm open to options to get this working (although I think I still need to be able to build my packages, for Jest/ESLint performance).
Project Structure
\apps
\main
\packages
\domainA
\foo
\package.json
\build
\src
At the moment each package gets build using tsc "tsc --project tsconfig.lib.json" into the build directory. The package.json defines the following:
"name": "#ig/foo",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"files": [
"/build"
],
I can spin up the main application and if I make a change in /packages/domainA/foo/src/index.ts then it'll build (currently using a watcher) and I get a full page reload.
I'd like to eliminate the page reload and instead use HMR. I don't think changing the entry point to "main": "./src/index.ts" will work for my use-case due to the slowness in the other tools unfortunately. However I'm happy to try and bypass this and re-point Vite to the source files if necessary.
I've tried all sorts of permutations, having looked at a few sample repos. But not managed to get anything working, e.g.
resolve: {
alias: [{
find: '#ig/foo',
replacement: '../packages/domainA/foo/src/index.ts',
},
}
Here is my current Vite config:
import react from '#vitejs/plugin-react';
import fs from 'fs';
import path, { resolve } from 'path';
import { defineConfig } from 'vite';
import mkcert from 'vite-plugin-mkcert';
import svgrPlugin from 'vite-plugin-svgr';
export default defineConfig({
// optimizeDeps: {
// include: ['#infogrid/solution-views-occupancy'],
// },
build: {
outDir: 'build/public',
sourcemap: true,
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
base: resolve(__dirname, 'index_base.html'),
},
}
},
server: {
port: Number(process.env.PORT),
// setting to true allows external ip
host: true,
},
plugins: [
react({ fastRefresh: true }), // Primarily used for HMR
svgrPlugin({ svgrOptions: { icon: true } }), // Turns svgs into react components
mkcert(), // Allows for HTTPS during local development
]
}

Rollup React Library Output Multiple Build Folders?

I have created a React Library with rollup, however, I have a large number of components that get exported so the file size is relatively large.
So in a project where I import the library doing the following;
import { ComponentExampleOne, ComponentExampleTwo } from 'my-react-library';
It imports the whole index file outputted via rollup (including all other components and any 3rd party dependencies), so when a user first hits the page with the import above they need to download the whole file, which is a lot bigger than I would like it to be.
For the likes of lodash where I just want to access a single function and not the entire library, I would do the following;
import isEmpty from 'lodash/isEmpty';
I want to achieve similar functionality with rollup so I can do something like
import { ComponentExampleOne } from 'my-react-library/examples';
import { ButtonRed } from 'my-react-library/buttons';
So I only import what is exported in the index.js file within an examples and buttons folder with this is as my folder structure in my library.
my-react-library/
-src/
--index.js
--examples/
---ComponentExampleOne.js
---ComponentExampleTwo.js
---ComponentExampleThree.js
---index.js
--buttons/
---ButtonRed.js
---ButtonGreen.js
---ButtonBlue.js
---index.js
I have no idea to achieve this with rollup?
This is my current rollup.config.js
import babel from 'rollup-plugin-babel';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import filesize from 'rollup-plugin-filesize';
import localResolve from 'rollup-plugin-local-resolve';
import json from 'rollup-plugin-json';
import pkg from './package.json';
import externals from 'rollup-plugin-node-externals';
import builtins from 'rollup-plugin-node-builtins';
import globals from 'rollup-plugin-node-globals';
import image from 'rollup-plugin-inline-image';
import { terser } from 'rollup-plugin-terser';
const config = {
input: 'src/index.js',
watch: {
chokidar: {
usePolling: true,
paths: 'src/**'
}
},
output: [
{
file: pkg.browser,
format: 'umd',
name: 'Example'
},
{
file: pkg.main,
format: 'cjs',
name: 'Example'
},
{
file: pkg.module,
format: 'es'
},
],
external: Object.keys(pkg.peerDependencies || {}),
plugins: [
globals(),
builtins(),
externals(),
babel({ exclude: 'node_modules/**', presets: ['#babel/env', '#babel/preset-react'] }),
commonjs({
include: "node_modules/**",
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/formik/node_modules/scheduler/index.js': ['unstable_runWithPriority'],
}
}),
peerDepsExternal(),
postcss({ extract: true }),
json({ include: 'node_modules/**' }),
localResolve(),
resolve({
browser: true,
dedupe: ['react', 'react-dom'],
}),
filesize(),
image(),
terser()
]
};
export default config;
Any help would be greatly appreciated.
You don't really need to do that if you use named exports and any modern bundler for building the app.
When Rollup detects you are not using some export it will be removed due to tree-shaking.
If you still want to do it pass an object with the different entries you want to the input option:
// ...
const config = {
input: {
examples: 'examples/entry/file.js',
buttons: 'buttons/entry/file.js'
},
// ...
}

How to avoid using relative path imports (/../../../redux/action/action1) in create-react-app

I've been using create-react-app package for creating a react website. I was using relative paths throughout my app for importing components, resources, redux etc. eg, import action from '../../../redux/action
I have tried using module-alis npm package but with no success. Is there any plugin that I can use to import based on the folder name or alias i.e. an absolute path?
Eg., import action from '#redux/action' or import action from '#resource/css/style.css'
Create a file called .env in the project root and write there:
NODE_PATH=src
Then restart the development server. You should be able to import anything inside src without relative paths.
Note I would not recommend calling your folder src/redux because now it is confusing whether redux import refers to your app or the library. Instead you can call your folder src/app and import things from app/....
We intentionally don't support custom syntax like #redux because it's not compatible with Node resolution algorithm.
The approach in the accepted answer has now been superseded. Create React App now has a different way to set absolute paths as documented here.
To summarise, you can configure your application to support importing modules using absolute paths by doing the following:
Create/Edit your jsconfig.json/tsconfig.json in the root of your project with the following:
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}
Once you have done this you can then import by specifying subdirectories of "src" (in the following example, components is a subdirectory of src) e.g.
import Button from 'components/Button'
We can use webpack 2 resolve property in the webpack config.
Sample webpack config using resolve :
Here component and utils are independent folder containing React components.
resolve: {
modules: ['src/scripts', 'node_modules'],
extensions: ['.jsx', '.js'],
unsafeCache: true,
alias: {
components: path.resolve(__dirname, 'src', 'scripts', 'components'),
utils: path.resolve(__dirname, 'src', 'scripts', 'utils'),
}
}
After that we can import directly in files :
import UiUtils from 'utils/UiUtils';
import TabContent from 'components/TabContent';
Webpack 2 Resolve Reference
After you try Ben Smith's solution above if you find eslint complains about importing absolute path add the following line to your eslint config:
settings: {
'import/resolver': {
node: {
paths: ['src'],
},
},
},
replace 'src' with your folder if you use your own boilerplate with your folder's name.
Feb 2010
Wasted about an hour on this.
An example is below:
Goal: Import App.css in HomePage.js
myapp\src\App.css
myapp\src\pages\HomePage.js
File: jsconfig.json
{
"compilerOptions": {
"baseUrl": "src"
}
}
File: src\pages\HomePage.js
import "App.css";
The alias solution for craco or rewired create-react-app is react-app-alias for systems as: craco, react-app-rewired, customize-cra
According docs of mentioned systems replace react-scripts in package.json and configure next:
react-app-rewired
// config-overrides.js
const {aliasWebpack, aliasJest} = require('react-app-alias')
const options = {} // default is empty for most cases
module.exports = aliasWebpack(options)
module.exports.jest = aliasJest(options)
craco
// craco.config.js
const {CracoAliasPlugin} = require('react-app-alias')
module.exports = {
plugins: [
{
plugin: CracoAliasPlugin,
options: {}
}
]
}
all
Configure aliases in json like this:
// tsconfig.paths.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"example/*": ["example/src/*"],
"#library/*": ["library/src/*"]
}
}
}
And add this file in extends section of main typescript config file:
// tsconfig.json
{
"extends": "./tsconfig.paths.json",
// ...
}
I am using babel-plugin-module-resolver for my project to resolve that problem.
babel-plugin-module-resolver also is the same as module-alis. So I think you should just resolve using module-alis problem.
Because you didn't tell us why using module-alis was fail? So i cant show you how to fix it.
Dont give up your solution while you dont know the reason!
in package.json file,
eject this code in the scripts object like this..
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom",
"eject": "NODE_PATH=src/ react-scripts eject"
},
this will enable the absolute path imports in your app
None of the answers worked for me. Some didn't work at all and others worked but the import was already inside src, for example:
import something from 'path/to/file'.
Whereas I wanted to be able to do:
import something from 'src/path/to/file'
Here is how I solved it:
tsconfig.json
{
"compilerOptions": {
// ...
"baseUrl": ".",
"rootDirs": [
"src"
]
},
"include": [
"src"
]
}

Import react components with absolute path

Here is my test file
// /imports/components/main.test.js
import React from 'react'
import { shallow, mount } from 'enzyme'
import Main from './main'
import TextInput from "/imports/ui/textInput"
...
and the main.js has
// /imports/components/main.js
import { action1 } from "/imports/actions/myAction"
but it throws an error when I run the test, saying
Cannot find module '/imports/actions/myAction' from 'main.js'
If I comment the import './main', same thing happen with importing TextInput. I have no issue with importing modules in node_modules.
How can I tell Jest or webpack to import the component using absolute path from project directory (i.e import Foo from /imports/...)?
Better way to solve relative path import issue, is by creating jsconfig.json file adjacent to package.json file.
{
"compilerOptions": {
"baseUrl": "src"
}
}
then import { action1 } from "actions/myAction"; will work
If you're using Create React App, you can set up the absolute imports path in a jsconfig.json (need to create in a fresh JavaScript template) or tsconfig.json (already created in the TypeScript template) at the root of your project to serve your usage.
Example:
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}
Official docs: https://create-react-app.dev/docs/importing-a-component/#absolute-imports
Another solution is to create an .env file within the root directory of your project.
Within it you will add NODE_PATH=src/
Thats all
Save the file and restart your dev environment in terminal.
Afterwards, you will go through your project and update some import statements accordingly.
My file structure follows exactly the same pattern as yours. To teach Jest into using imports beginning with a /, I use babel-plugin-module-resolver and its handy root option. My .babelrc for Jest looks like this:
{
"presets": ["es2015", "meteor"],
"plugins": [
"transform-class-properties",
"transform-react-constant-elements",
"transform-react-inline-elements",
"transform-react-remove-prop-types",
["module-resolver", {
"root": ["../"],
"alias": {
"react-router-dom": "react-router-dom/umd/react-router-dom.min.js",
"redux": "redux/dist/redux.min.js",
"react-redux": "react-redux/dist/react-redux.min.js"
}
}]
]
}
As I'm using Meteor which customized its root imports, I hide my Jest usage and configuration into a .jest directory at the root of my repository allowing me to have a specific .babelrc without risking conflicts with Meteor's one.
With webpack(v4) and babel, you can provide absolute paths in your directory.
Follow these steps
In your .babelrc file, make this entry for plugins.Make sure you have babel-plugin-root-import in your package.json as well.
"plugins": [
[
"babel-plugin-root-import",
{
"paths": [
{
"rootPathPrefix": "~",
"rootPathSuffix": "./"
},
{
"rootPathPrefix": "#src",
"rootPathSuffix": "src"
},
{
"rootPathPrefix": "#any-other-folder",
"rootPathSuffix": "src/client/../any-other-folder"
}
]
}
]
]
Now if you run into eslint issue, you can add these lines in your eslintrc:
"settings": {
"import/resolver": {
"babel-plugin-root-import": [
{
"rootPathPrefix": "#src",
"rootPathSuffix": "src"
},
{
"rootPathPrefix": "#any-other-folder",
"rootPathSuffix": "src/client/../any-other-folder"
}
]
}
}
Now to make your editor recognize these paths, you can create this entry in jsconfig file.
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["/*"],
"#src/*": ["src/*"],
"#any-other-folder/*": ["src/client/../any-other-folder/*"] ==> absolute path to any other folder
}
},
"exclude": ["node_modules"]
}
Hope these entries help.
I had jest configs in package.json file (under "jest" key). So I just have added there this row:
"modulePaths": ["<rootDir>/src/"]
And this works for me. I hope it can help somebody else.

Resources