NextJS module resolution error with multiple paths in my path resolution - javascript

Hi everyone :) I was hoping I could get some fresh eyes on a problem I'm running into on my project:
I have a NextJS (12.1.4) project using absolute imports, I also have an NPM package where I directly import the source files in my project (since it's a library for shared components, internal use only). This package is called bs-components here.
Here is my jsconfig.json file
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#bscom/*": ["node_modules/#company/bs-components/*"],
"*": ["node_modules/#company/bs-components/*", "*"]
}
}
}
I will explain the intent behind my paths config here:
The first entry for #bscom/* will allow me to import modules from my library using a shorthand.
The second entry for * is intended so that the module resolution goes like this:
Look in the component library first for a module, so that absolute imports in the component library are respected.
If module is not found in the library, then resolve using the baseUrl as the starting point for resolution.
Unfortunately when using this config, absolute imports within the component library fail with the following error:
./node_modules/#company/bs-components/styling/resets.js:1:0
Module not found: Can't resolve 'constants/colors'
This happens in my NextJS _document.js file, so during the server render step.
There are no modules with that path in my main project which could cause a conflict.
Any suggestions would be greatly appreciated, thanks :)

Related

How to do module resolution when an npm package contains multiple namespaces/submodules

A project I am working with is using this package: https://www.npmjs.com/package/#ethersproject/abstract-provider
within an SDK we are building..
After installation, the SDK node_modules folder contains the #ethersproject directory which contains an abstract-provider directory as well as multiple other directories, each their own namespace with separate package.json files.
We use the code in the sdk as follows:
import { Provider } from '#ethersproject/abstract-provider';
and this works fine for webpack based projects which import our SDK (such as a React app using create-react-app).
When I want to use our SDK in a project which DOESN'T use a bundler such as webpack (such as a very simple frontend pulling in our sdk with importmap as here: https://github.com/unegma/rainprotocol__verify-gating), we are getting an error:
Failed to resolve module specifier "#ethersproject/abstract-provider". Relative references must start with either "/", "./", or "../".
I'm assuming this is because our SDK doesn't know how to resolve #ethersproject/abstract-provider because abstract-provider is a submodule of #ethersproject (which isn't even a module itself).
What is the way to solve this issue? Do we need to create a decs.d.ts file in our SDK root which contains something like: declare module "#ethersproject/abstract-provider; or is there a different way to do this?

How to avoid relative imports in modules intended to be bundled?

I'm developing a module that doesn't have a build that the user imports. Instead, he imports individual components and then bundles them along with his code. However, those components share utilities and I want to import them without going through relative path hell.
I know that's a pretty common question and I did some research. Suppose I have module/components/foo/bar/baz/index.js that wants to import module/utils/helper.js
Option 1
Just use relative paths and do:
import helper from '../../../../utils/helper'
Option 2
Use the module-alias package and have:
import helper from '#utils/helper'
This would work in Node.js because modules are resolved at runtime. However, let's say the module user has Webpack and imports the module:
import component from 'module/components/foo/bar/baz'
Webpack wouldn't be able to resolve #utils unless the user specifies that alias in his own Webpack configuration. That would be pretty annoying.
Option 3
Use Webpack aliases in webpack.config.js:
module.exports = {
resolve: {
alias: {
'#utils': path.join(__dirname, 'utils')
}
}
}
This would work fine if the module was pre-bundled. But as I've previously mentioned, I want the library to be usable with ES6 imports so that users can bundle only the parts they need.
Option 4
I could use the module name in the module's own source code:
import helper from 'module/utils/helper'
This appears to solve the problem, but I think it's a pretty bad solution. For development, you'd have to create a symlink node_modules/module -> module. I'm sure this hides many potential issues and collaborators would have to manually do it as well.
Is there a way to avoid relative paths while allowing the library to be used with ES6 imports?

How can I resolve the following compilation error when running Jest for testing?

Although I am able to start the npm project using npm start without any issues with webpack or babel, once I run npm test, I find the following error related to testing App.js using App.test.js (where App.js imports ApolloClient):
TypeError: Cannot assign to read only property '__esModule' of object '[object Object]'
| import ApolloClient from 'apollo-boost';
| ^
at node_modules/apollo-boost/lib/bundle.cjs.js:127:74
at Array.forEach (<anonymous>)
at Object.<anonymous> (node_modules/apollo-boost/lib/bundle.cjs.js:127:36)
Essentially, I'm confused as to why I get an error when running the test but not when starting the project.
I've tried adding in a number of babel plugins to both .babelrc and in my webpack config file:
#babel/plugin-transform-object-assign
#babel/plugin-transform-modules-commonjs
babel-plugin-transform-es2015-modules-commonjs
However, I haven't been able to resolve the issue. My thinking was that this is related to the fact that the file that fails to compile was originally CommonJS.
I was only able to find something relatively similar here, https://github.com/ReactTraining/react-router/pull/6758, but I didn't find a solution.
Is there something that I'm missing specifically related to running tests? I should also mention I've tried frameworks other than Jest and ran into the same issue.
EDIT:
I removed everything from App.test.js except the imports to isolate the issue so it just contains the following:
import React from 'react';
import { shallow } from 'enzyme/build';
import App from './App';
UPDATE:
I was able to resolve the initial error by upgrading apollo-boost from version 0.3.1 to 0.4.2. However, I now have a different error that is similarly frustrating. I am using Babel 7 and have added the plugin #babel/plugin-syntax-dynamic-import to both my .babelrc and to my webpack.config.js files. Despite this, I get the following error related to the use of a dynamic import in App.js when running the Jest to test App.test.js:
SyntaxError: Support for the experimental syntax 'dynamicImport' isn't currently enabled
Add #babel/plugin-syntax-dynamic-import (https://git.io/vb4Sv) to the 'plugins' section of your Babel config to enable parsing.
I'm not sure if there is a parsing error or something else, but I've tried numerous things that have not worked. The closest discussion I could find related to this problem is, https://github.com/facebook/jest/issues/5920, however, the proposed solutions don't work for me.
UPDATE:
One thing that I'm trying is to avoid duplication of the babel options as right now they're both in .babelrc and in the babel-loader options within webpack.config.js. From what I found online (Whats the difference when configuring webpack babel-loader vs configuring it within package.json?), the way to make webpack use the settings in .babelrc is to not specify options. However, doing so results in the same error described above showing up only this time when running npm start. I will add that the project that was originally created using create-react-app, however, in order to support multiple pages, I needed to customize webpack's configuration and so ejected from it. I'm not sure why this is so convoluted.
its probably a babel configuration issue, I'm pretty sure jest needs to be compiled to work with create-react-app...
did you specify a setup file in package.json:
"jest": {
"setupFiles": [
"/setupTests.js"
]
}
and in setupTests.js:
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
It turns out that one of the components in the project's src directory had its own local package.json file even though it wasn't being used and was not installed as a local dependency in the top level package.json (instead imports were done using relative urls). For some reason, the existence of this file changed the behavior of webpack and other tools when starting and testing the project such that none of the top level configurations were used for files within directories with separate package.json files. Once I removed these local package.json files from the components sub-directory, all the prior issues were resolved. One hallmark of this problem is that compilation errors were not showing up for JavaScript files that weren't nested under an alternate package.json file.
Hopefully this is useful for anyone that encounters similar errors as I don't think the cause can be directly determined from the compiler messages alone.

Typescript - Transform imports for npm distribution

I am working on an NPM package written in Typescript, and I am having trouble wrapping my head around module resolution when compiling the library to publish.
Throughout the project, I have been using non-relative imports to avoid the annoyance of ../../../. However, I read in the typescript documentation that relative imports should be used within a project.
A relative import is resolved relative to the importing file and cannot resolve to an ambient module declaration. You should use relative imports for your own modules that are guaranteed to maintain their relative location at runtime.
A non-relative import can be resolved relative to baseUrl, or through path mapping, which we’ll cover below. They can also resolve to ambient module declarations. Use non-relative paths when importing any of your external dependencies.
I would like to not have to sacrifice the nice, neat imports in favor of relative imports, but I am not sure how to set up the compiler settings in order to get this to work. When running tests, I specify NODE_PATH in order to resolve the modules, but this isn't working for post-compilation.
I would like to be able to write files using non-relative imports, but have them transformed in some way so that the dist/ files can resolve the imports.
The project is hosted on github here.
The relevant issue is that I end up with an index.d.ts file in my dist/ folder that looks like this:
import { Emitter } from 'emitter';
import { Schema } from 'migrations';
import { Model, model, relation } from 'model';
import { Builder } from 'query';
export { Builder, Emitter, Model, model, relation, Schema };
But all the modules have errors that the module cannot be resolved. How can I keep these imports in their current form, but transform them in some way when building so that when I publish the npm package, the modules can be correctly resolved.
I would follow the advice in the official Typescript docs:
https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html
Basically, the suggestion is to build your library just before publishing to npm. You will have two files, in output; let's call them main.js and main.d.ts.
The critical point, here, is that by tsc-ing your source files you resolve the dependencies before npm is involved at all, so you can keep your references as you wish.
In your package.json, include two lines (or change them accordingly, if you have them already):
{
...
"main": "./lib/main.js",
"types": "./lib/main.d.ts"
...
}
In this way, any consuming project doesn't need to know about the internals of your library: they can just use the compiled output, referencing the generated typings file.

Webpack resolve.root and TypeScript loader

Our project is using the webpack resolve.root option to import modules with absolute paths. (avoiding something like ../../../module)
In its current state the project is using babel-loader which works perfectly fine.
My task is to migrate the app to Angular 2.
Therefor I am currently in the process of transitioning to TypeScript.
Somehow it seems like the ts-loader does not work in combination with the resolve.root option of the webpack config.
Example of the webpack.config.js
resolve: {
root: [
path.resolve('./node_modules'),
path.resolve('./app'),
path.resolve('./app/lib'),
]
},
Example of a module import
import AbstractListState from 'states/abstract_list_state';
The states directory is inside the app/lib directory.
Error when executing webpack
ERROR in ./app/mainViews/panel/panel.controller.ts
Module not found: Error: Cannot resolve module 'states/abstract_list_state' in C:\Users\...\Project\app\mainViews\panel
# ./app/mainViews/panel/panel.controller.ts 4:28-65
Pre version 2.0 TypeScript will try to load modules with an absolute path from the node_modules directory. This is because TypeScript's module resultion is per default set to "node". Which means it works like node's require method. So, even if you're using webpack to build your app, TypeScript (and its compiler) will still want to load the files.
In order to let webpack import your modules with absolute path you have to go back and use the require method. This way TypeScript will let webpack import stuff. But of course you will not get any type-inference, autocomplete, ...
Or, you update to the TypeScript 2.0 beta and give this a try: https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#module-resolution-enhancements-baseurl-path-mapping-rootdirs-and-tracing

Categories