In my React JS project I have configured a jsconfig.json such that I can recursively export nested directories and import a specific export from the base directory as follows:
jsconfig.json:
{
"compilerOptions": {
"jsx": "react",
"baseUrl": "src"
}
}
Project folder structure:
react-app
src
common
index.js
services
ServiceA.js
ServiceB.js
index.js
components
ComponentA.jsx
index.js
pages
pageA
PageA.jsx
index.js
App.jsx
index.js
Now in each index.js I would export all from each file/folder. So for example in common/services/index.js:
export * from 'common/services/ServiceA.js';
export * from 'common/services/ServiceB.js';
And in common/index.js:
export * from 'common/services';
export * from 'common/components';
Now if I require ServiceA exported from ServiceA.js in the PageA.jsx file I could import it as follows:
// PageA.jsx
import {
ServiceA
} from 'common';
// ServiceA.js
export class ServiceA {
doStuff () {
// do stuff
}
}
How can I setup my NodeJS server project to allow for similar exports and imports?
I'd like to do this for consistency between the FE and BE such that I can easily port over any FE code to my BE project without having to make any significant changes to exports and imports.
Edit: I managed to get it working using the answer by Besworks to which I awarded the bounty, however VS Code Intellisense wouldn't navigate to the export definition from the import statement until I added a jsconfig.json in the project root:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"#common" : ["./common/index.js"]
}
}
}
You can map exports in your package.json :
{
"name": "@your-namespace/your-package",
...
"exports": {
".": "./index.js",
"./common": "./common/index.js"
}
}
Then you can refer to the exports by name :
import { YourClass } from '@your-namespace/your-package';
import { AnotherClass } from '@your-namespace/your-package/common';
Alternatively, if your submodules only need to be accessible from within your package, you could map imports instead of exports which would not need your package name prepended but must start with a # character. You don't need to explicitly specify each submodule either, you can map everything from within a folder with wildcard substitution :
{
"name": "your-package",
...
"imports": {
"#common": "./common/index.js",
"#common/services": "./common/services/index.js",
"#common/services/*": "./common/services/*.js",
"#shortcut": "./deeply/nested/path/to/module.js"
}
}
And use them :
import { YourClass } from '#common';
import * from '#common/services';
import { AnotherClass } from '#common/services/ServiceA';
import something from '#shortcut';
In the above example, #common would be a reference to ./common/index.js and #common/services/ServiceA would point to ./common/services/ServiceA.js.
You should never export all packages.
The nodejs module/package import/require takes care of all dependencies and module caching.
Very similar to - https://www.youtube.com/watch?v=-5wpm-gesOY ;)
If your node project is using "type": "module" in package.json, then the filestructure is exactly the same. However, importing from index files in ES modules is still behind an experimental flag:
https://nodejs.org/api/esm.html#customizing-esm-specifier-resolution-algorithm
So you would have to launch it with e.g.:
node --experimental-specifier-resolution=node src/index.js
$ tree src/
src/
├── common
│ ├── index.js
│ ├── ServiceA.js
│ └── ServiceB.js
├── index.js
└── other
├── index.js
├── OtherA.js
└── OtherB.js
src/index.js
import { serviceA, serviceB } from './common';
import { otherA, otherB } from './other';
serviceA();
serviceB();
otherA();
otherB();
src/common/index.js
export * from './ServiceA';
export * from './ServiceB';
`src/common/ServiceA.js'
export const serviceA = () => console.log('***** ServiceA');