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

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"]
}
}

Related

Jest test runs - Cannot find module error

I have been working on a React Typescript repo and have been running into an annoying issue where in jest is not able to resolve imports relative to root dir.
Cannot find module '~lib/dates' from 'utils.ts'
And this how the import looks like in the component / utils
import { abc } from '~lib/dates'; // this fails to run
If I change this to a relative path jest test runs works as expected
import { abc } from '../../lib/dates'; // this runs as expected
The same path work for some other directories and I am a bit stumped
import { xyz } from '~components/home/constants'; // jest resolves it
import { abc } from '~lib/dates'; // ERR
I tried including moduleNameWrapper in the jestConfig to see if it jest can resolve the imports correctly but it did not help.
package.json
"jest": {
...
"moduleNameWrapper": {
"^~(.*)$": "<rootDir>/src/$1"
}
}
I could for sure update the VS code setting so that auto imports are resolved relatively to the file and not with the root dir but this has been bugging me for a while. It would be great if anyone has any pointers on how best to resolve this.
I am on a monorepo with the following directory structure
repo
server
client
src
components
lib
utils
package.json
Your implementation looks right. But it looks like the option moduleNameWrapper was the wrong option, it's supposed to be moduleNameMapper.
I also have an example as same as you which also uses babel as transplier, it works fine as I added moduleNameMapper. Here is the my example:
Jest configuration:
https://github.com/tmhao2005/lerna-demo/blob/master/packages/share/jest.config.js
Here is the file for testing:
https://github.com/tmhao2005/lerna-demo/blob/master/packages/helper/src/index.ts
https://github.com/tmhao2005/lerna-demo/blob/master/packages/helper/src/index.test.ts
forget the ~ character;
first define root directory to jest (i.e. src/);
then import your stuff from that root directory; (e.g. import { abc } from 'lib/dates')
by the way you can always import your stuff from default root without any configuration like this: import { abc } from 'src/lib/dates'
further read if you are using create-react-app jest absolute import

How to hide a full path to source file when importing TypeScript symbols from an external library?

I have #ts-stack/mod module which internally exports ColumnsDecoratorFactory, but for public API I want to export only ORM symbol to usage it this way:
import { ORM } from '#ts-stack/mod';
ORM.ColumnsDecoratorFactory // I just want to have access this way
Can direct import ColumnsDecoratorFactory be banned?
// VS Code offers me to import from these places, I don't want to see it:
import { ColumnsDecoratorFactory } from '#ts-stack/mod/dist/modules/orm/decorators/column';
// OR
import { ColumnsDecoratorFactory } from '#ts-stack/mod/dist/modules/orm';
I did the following on the src/index.ts:
import * as ORM from './modules/orm';
export { ORM };
My source files compiles from src to dist folder. In package.json I say:
"main": "dist/index",
"types": "dist/index",

Angular class is not an Angular module

When I build my Angular library, publish it to npm, and use it as a dependency in another project, whenever I try to import on of my module classes into my app.module.ts, and get this error Class TekButtonModule is not an Angular module. I have followed steps from multiple different sites on how to create, build, and publish angular libraries, and I can't figure out why it won't recognize the class as a valid module class.
In my app.module.ts, this is how I am importing the module:
import { TekButtonModule } from "tek-angular-uimodules";
#NgModule({
imports: [
TekButtonModule
]
})
export class AppModule { }
My library project follows the standard directory structure outlined by Angular. I did not change anything with the build, or path settings when I setup the library project. My built library project has a bundles, esm5, esm2015, fesm5, fesm2015, lib (where my custom modules, and components are), as well as a package.json, public-api.d.ts (exports everything in the lib directory), README.md, tek-angular-uimodules.d.ts (exports the public api), and a tek-angular-uimodules.metadata.json file.
Is there some extra configuration that it needed that isn't setup by default to get modules to work correctly?
Here is my button module class:
import { CommonModule } from "#angular/common";
import { NgModule } from "#angular/core";
import { TekButton } from "./button";
#NgModule({
declarations: [
TekButton
],
exports: [
TekButton
],
imports: [
CommonModule
]
})
export class TekButtonModule {}
Here are some of the files that are generated when I build the project:
tek-angular-uimodules.d.ts:
/**
* Generated bundle index. Do not edit.
*/
export * from './public-api';
public-api.ts
export * from "./lib/button";
export * from "./lib/button-group";
export * from "./lib/nav-bar";
./lib/button/button-module.d.ts
export declare class TekButtonModule {
}
If I import the generated javascript module file from the esm5 directory manually in my app.module.ts file, then it works just fine. But I have to manually do that, when it should just work with the standard module import that WebStorm auto imports for me like any other package.
This is the generated module js file under the esm5 directory:
/**
* #fileoverview added by tsickle
* #suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { CommonModule } from "#angular/common";
import { NgModule } from "#angular/core";
import { TekButton } from "./button";
var TekButtonModule = /** #class */ (function () {
function TekButtonModule() {
}
TekButtonModule.decorators = [
{ type: NgModule, args: [{
declarations: [
TekButton
],
exports: [
TekButton
],
imports: [
CommonModule
]
},] }
];
return TekButtonModule;
}());
export { TekButtonModule };
//# sourceMappingURL=data:application/json;base64
Any help is appreciated. I can provide more code, and screenshots if needed.
This is the error I get when trying to import my module:
So I figured out what was causing my specific issue. It was two things. Point two is the direct reason for my issue, point one was making much more confusing to debug.
In order to test my built package, I would run ng build, and then cd into the built project in the dist directory, and run npm pack. I would then install my built package as a file dependency in an entirely separate project to ensure I did everything correctly. What I found out was (or I'm assuming) that there was a caching mechanism going on even when doing local file dependency installations. This caching seemed to be tied to the file name of the tgz file that is generated from npm pack. This caching was causing the very first version to be constantly reinstalled not matter how many changes I made to the built library. The fix, for me, was to simply rename the tgz file to something different each time. You should also change the library version number, and that should also work.
My file structure looked like this:
src
- public-api.ts
- lib
-- button
--- index.ts
--- public-api.ts
--- button.component.ts
--- button.module.ts
-- another-module
-- yet-another-module
The public-api.ts file directly in src looked like this:
export * from './lib/button'
// Other exports
The public-api.ts, and the index.ts file under src\lib\button looked like this:
// public-api.ts
export * from './button.component.ts'
export * from './button.module.ts'
// index.ts
export * from './public-api.ts'
I had assumed that by adding the index.ts files to each directory, the export line in the root public-api.ts would find the index file, and export the files accordingly, but somewhere along the line, this implicit resolution does not work, and, instead, an explicit export is needed. The fix was to change export * from './lib/button' to export * from './lib/button/index.ts' or export * from './lib/button/public-api.ts'.
Point two was the direct reason for my issue, point one just made it really confusing when trying to debug.
So I had the same issue with a different solution, so I thought I would share it here.
I had just updated my library to Angular 9. And everything seemed fine except the strange "Class my-module is not an Angular module" error.
What worked for me was to up tsconfig.lib.ts file with:
...
"angularCompilerOptions": {
...,
"enableIvy": false
}
Don't know if this will help anyone, but for me it was caused by the NgModule import path autocompleting with #angular/core/src/metadata/*, rather than #angular/core.

Set a global service for import in Create React App

Is it possible to somehow declare a jsx file as a global file or a directory for import? For example instead of:
import { Navigation } from '../../../../../../helpers/NavigationService';
do just:
import { Navigation } from 'NavigationService';
I've seen it's possible in webpack config, but I don't see this file in create-react-app. Can I use somehow package.json for that?
There are three ways to solve your issue:
NODE_PATH (<- probably what you're looking for):
You can define an environment variable NODE_PATH=src/ and then you can import your service like so import { Navigation } from 'helpers/NavigationService';. It'll will work but this is not necessary what's best in my opinion.
No deep nesting:
No nesting, means no issue of deep nesting in the first place. You can try having a file hierarchy similar to this:
src/
/helpers
/components/
/componentA/
componentA
relatedComponent
otherRelatedComponent <- no need for nesting
/componentB/
...
The mono repo approach:
Having an internal helper package and have it imported like import { Navigation } from 'myproject-helpers/NavigationService'; may be a good compromise
You could set NODE_PATH='src' in .env file, using global imports instead, here's my solution, without having to eject.
.env:
NODE_PATH='src'
Create a folder src/services, inside it create the NavigationService, my example:
// src/services/NavigationService.js
export class NavigationService {
static runIt() {
console.log("Running");
}
}
In the App.js file, now you can import the navigation service directly, using global import, as follows:
// src/App.js
// ... import React and others
import { NavigationService } from "services/NavigationService";
class App extends Component {
componentDidMount() {
NavigationService.runIt();
}
// ... render method
}
If using VsCode, to get code completion, create jsconfig.json file, with the following:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"services/*": ["./src/services/*"]
}
}
}
I hope it helps!

Auto-completion in Webstorm for my custom npm module (ES6/Babel)

When I use material-ui package I get nice auto-completion in Webstorm (ctrl+space):
I thought it might have something to do with the fact the package includes an index.es.js file:
import _AppBar from './AppBar';
export { _AppBar as AppBar };
import _AutoComplete from './AutoComplete';
export { _AutoComplete as AutoComplete };
import _Avatar from './Avatar';
export { _Avatar as Avatar };
import _Badge from './Badge';
export { _Badge as Badge };
import _BottomNavigation from './BottomNavigation';
...
So I generated my own index.es.js in my custom npm module and put it next to the transpiled index.js:
import {ActionTypes as _ActionTypesElements} from './reducers/elements/elements';
export { _ActionTypesElements as ActionTypes };
import {ActionTypes as _ActionTypesAppState} from './reducers/appState/appState';
export { _ActionTypesAppState as ActionTypesAppState };
import _appStateActions from './reducers/appState/appState_actions';
export { _appStateActions as appStateActions };
...
And yet I get no auto-complete:
Any idea why?
Found the answer:
Had to add a jsnext:main field to the package.json of the npm module:
package.json:
...
"module": "./index.js",
"jsnext:main": "./index.es.js",
...
Webstorm recognizes the package's inner exports.
In WebStorm 2019.3, here are the steps I follow to force Code Completion (including auto-import) to work for a custom, self-published NPM package:
Ensure that the project, itself, has a package.json file at the root of the project, and that package.json includes the desire package in the "dependency" object. For example:
{
"name": "testproject",
"version": "1.0.0",
"dependencies": {
"#yourname/yourpackage": "latest"
}
}
In WebStorm, select File > Invalidate Caches / Restart...
To enable auto-import for package contents, ensure that the JavaScript file in which the package is being used has AT LEAST ONE export statement. For example, in the following code, an export is present, so Code Completion auto-imports the package function isNil():
export function init () {
isNil
}
By comparison, the following code does not contain an export statement, so isNil() is not automatically imported:
function init () {
isNil
}
For me, all three of the preceding steps are necessary for Code Completion to work for my own NPM packages in WebStorm.

Categories

Resources