Base path for `require()` - javascript

Note! I went through many answers before asking the question but did not find the answer I want. Please, do not mark it as a duplicate.
In es modules we can set baseUrl in jsconfig.json:
{
"compilerOptions": {
"baseUrl": "/base"
}
}
or
{
"compilerOptions": {
"baseUrl": "."
}
}
Then we can import any files from that base directory throughout the project:
import Grand from 'base/child/grands'
I want to achieve the same thing in commonjs syntax (which uses require() ). I want to be able to import files(modules) like this:
const Grand = require('base/child/grands')
not this way:
const Grand = require('./base/child/grands')
How can I achieve this?
P.S. I am working on a Next.js project which uses 'es modules' for frontend and 'commonjs' for backend.

Related

Built-in nodejs path aliasing and implied index.js module resolution

I want to set up import path aliasing in a typescript/node/express project WITHOUT using other packages like tsconfig-paths and link-module-alias. I would like to do this with built-in nodejs functionality (the Typscript aliasing is already done).
Basically:
// Change
import { myFn } from '../../../utils';
// To
import { myFn } from '#this/utils';
The glimmer of hope I have is with node's subpath imports. Theoretically, I can just add this to my package.json:
"imports": {
"#this/*": "./dist/*"
}
Here's the problem
This works for explicitly importing the *.js files, but it does fails for implied index.js (i.e. Folders as modules)
// Works
import { myFn } from '#this/utils/index.js';
// Does not work
import { myFn } from '#this/utils';
However, I would expect node to resolve the import like this:
if utils is a directory, the import should resolve to */utils/index.js
if utils is a file, the import should resolve to */utils.js
My first thought would be to update the package.json imports to include all possibilities, but this is not supported:
"imports": {
"#this/*": [
"./dist/*",
"./dist/*.js",
"./dist/*/index.js"
]
}
The other option seems to be to define an exports entry for every directory, but that is not scalable.
With --experimental-specifier-resolution=node flag the code below works for me.
index.js:
import { myFn } from '#this';
package.json:
...
"imports": {
"#this": "./utils"
}
...
What worked for me on a typescript node project using esbuild was to define the path twice, but for the second time don't include /*. That will allow you to import the index.ts using #utils while still allowing you to import other files underneath the directory
{
"paths": {
"#src/*": ["src/*"],
"#src": ["src"],
"#utils/*": ["src/utils/*"],
"#utils": ["src/utils"]
}
}

Avoid relative path import hell in react-native?

I'm new to react-native coming from vue background, one thing I hate in react is having to use relative path imports in my components, I find myself doing something like:
import HomeScreen from '../../components/screens/HomeScreen'
If my folder structure changes it means I will have to do a search and replace in all of m components using that component, not cool and prone to be a dev hell.
Is it possible to use absolute paths like with node modules, is there a library where I can use aliases or similar, with vue I could import my components in the app.js like Vue.component('home-screen ...) and I could use them without having to import.
you can add a package.json file with a name key
{
"name": "#components"
}
in any folder and metro will recognize it.
You can then import as #components/screens/HomeScreen
If you are using vscode, you can add a jsconfig.json file to your project root to enable import autocomplete.
Here is mine:
{
"compilerOptions": {
"baseUrl": "",
"paths": {
"#components/*": ["src/components/*"],
"#helper/*": ["src/helper/*"],
"#actions/*": ["src/actions/*"],
"#constants/*": ["src/constants/*"],
"#primitives": ["src/primitives/index.js"],
"#graphql": ["src/graphql/index.js"],
"#services/*": ["src/services/*"],
"#assets/*": ["assets/*"]
}
}
}
The easy method
Add a package.json to important directories with a {"name":"<prefix>"}, similar to a monorepo.
If you are doing this, you can probably also think about making it a full monorepo since there is very little extra work
The incredibly complex method
This method is easier for using things like webpack resolver, etc.
Create a metro.config.js at the root with the following content
module.export = {
resolver: {
resolveRequest: (
context: ResolutionContext,
moduleName: string,
platform: string | null
) => {
//...
return Resolution;
}
}
}
Resolution Context
The resolution context is an object that holds attributes of the running resolution, most importantly originModulePath which is the path of the requiring module.
Resolution
The resolution is an object of either:
{ type: "empty" } for empty sources
{ type: "sourceFile", filePath: string } for JS, JSON, etc. files
{ type: "assetFiles", filePaths: string[] } for any other type (e.g. not metro compilable)
For this method, I would recommend looking at metro-resolver's types since this method is abysmally documented

What is the correct way to import a file in typescript?

historically when importing javascript files, you name the file index.js and then import like so
import something from 'components/path/something' where the last something is a directory with the index.js file in
but with TS I get an error saying: no file or directory when I switch the file name to index.ts
2 solutions so far import something from 'components/path/something/index.ts'
or
import something from 'components/path/something/something'
not a huge fan of either, is there a better approach to this?
There is no correct way:
Imports are usually relative:
import whatever from '../another/folder/'; // will import index
Of course you can adapt this behavior in tsconfig.json:
{
....
"compilerOptions": {
"baseUrl": "src",
"paths": {
"#services/*": ["services/*"],
"#shared/*": ["shared/*"],
"#models/*": ["models/*"],
},
...
}
}
Provides you an "absolute project path":
import WhateverService from '#services/WhateverService';
https://www.typescriptlang.org/docs/handbook/modules.html

Getting 'Module not found' although file exists

I downloaded the angular starter and ran the app and it ran well. Now I Added a new file "people.service.ts" located in "src/app/services/people.service.ts".
When I try to import it, I get an error:
Module not found: Error: Can't resolve 'services/people.service' in
'/path-to-project/angular-starter-master/src/app'
This is the code I use to import it (in src/app/app.module.ts):
import {PeopleService} from 'services/people.service';
I'm sure that there's no typos because the IDE recognizes it. There isn't any TypeScript error in the entire project. The file 'services/people.service' does contain a class named PeopleService.
Any help will be profoundly appreciated. Please let me know if you need any additional information.
The problem is that you're using absolute path instead of a relative path. Change the import to the following:
import {PeopleService} from './services/people.service';
TypeScript 2.0+
In TypeScript 2.0 you can add a baseUrl property in tsconfig.json:
{
"compilerOptions": {
"baseUrl": "."
// etc...
},
// etc...
}
Then you can import everything as if you were in the base directory:
import {PeopleService} from 'services/people.service';
On top of this, you could add a paths property, which allows you to match a pattern then map it out. For example:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"services/*": [
"services/validation/*"
]
}
// etc...
},
// etc...
}
Which would allow you to import it from anywhere like so:
import {PeopleService} from 'services/people.service';
From there, you will need to configure whatever module loader you are using to support these import names as well. Right now the TypeScript compiler doesn't seem to automatically map these out.
You can read more about this in the github issue. There is also a rootDirs property which is useful when using multiple projects.
OR
You can directly change the absolute path to relative path like this:
import {PeopleService} from './services/people.service';

How can I use a config file in React?

Let's say I have 5 jsx files and each file uses some config parameter.
My index.js file imports all of these 5 jsx files.
Instead of having my config data spread accross 5 files, is there a way for my jsx files to get the data from a global JS object which has loaded the data from a config file?
I've seen some examples, but I've not been able to get them to work.
JS6 import function | Example using webpack
Assuming ES6:
config.js
export const myConfig = { importantData: '', apiUrl: '', ... };
Then:
jsxFileOne.js, jsxFileTwo.js, ...
import { myConfig } from 'config.js';
There are other ways to import & export things globally leveraging webpack, but this should get you started.
If your project is built using Webpack, consider using node-env-file.
Example config file snippets:
development.env
API_SERVER_URL=https://www.your-server.com
webpack.config.js
const envFile = require('node-env-file');
...
const appSettingsFile = isDevBuild ? '/settings/development.env' : '/settings/production.env';
try {
envFile(path.join(__dirname + appSettingsFile));
} catch (error) {
console.log("Failed to read env file!: " + __dirname + appSettingsFile);
}
...
plugins: [
new webpack.DefinePlugin({
"process.env": {
API_SERVER_URL: JSON.stringify(process.env.API_SERVER_URL)
}
})
...
]
Inside your js/jsx code, you can now access process.env.API_SERVER_URL variable which will contain the required value.
It seems dotenv package is more popular, you can try this out as well.
Very old problem, that nobody took the time to solve, until now. I leave this for future readers because this is a top search result for configuration in React.
I created wj-config to deal exactly with this. Be sure to pay close attention to the React notes as you will need to enable top-level awaits in webpack, either by ejecting or using the #craco/craco NPM package.
You may also read this blog post that explains its use.

Categories

Resources