Webpack can't resolve non-js `require`s from within node_modules - javascript

I've got a Next.js project configured to resolve imports that end in .web.js. This works outside of my node_modules directory. I did this by setting resolve.extensions = ['.web.js, '.js', '.jsx'] in my webpack config. I understand that this setting is responsible for resolving imports that don't have an extension, e.g. import _ from './component', when ./component.web.js exists.
I also have some node_modules that make use of this .web.js extension. They're private modules, but the idea stands. Let's say our node_modules looks like this. It may be worth noting that these modules have already been transpiled and as such use require rather than import.
- node_modules
- #foo
- bar.js
- baz.web.js
- baz.native.js
Now let's say that we have the following:
// bar.js
require("./baz");
If I try to import #foo/bar, the app will throw a module not found error on the line require("./baz"); saying that it can't be found. If I change it to require("./baz.web.js") or remove the line altogether then the app runs fine.
Why can webpack make these kind of resolutions outside of node_modules, but not within the directory? And how can I tell webpack to resolve those imports, too?

Depending on your module resolution strategy, you'll either find some files or not. Node.js resolves modules as outlined here. This means for you that require('./baz') is resolved to requesting /path/to/module/baz.js. Since your file is not actually named, it is not found. You can use require('./baz.web') instead.
As to whether Webpack can "automatically" handle which import to use, it probably comes down to using a plugin or having some sort of logic in bar.js to choose between baz.web and baz.native.

Related

VS Code shows module not found even though WebPack build works

My VS Code says that it can't find an import even though my WebPack build still works.
Here is the import...
import * as tf from '#tensorflow/tfjs';
and the message from VS Code:
Cannot find module '#tensorflow/tfjs'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?
I have read something about path aliases which can be set up in the tsconfig.json to shorten long paths to modules. But if this is a path alias and I don't have it configured in my tsconfig.json, how does WebPack know where the module is located?
I also read that the convention for path aliases is to start with an "#" but the folder in the "node_modules" itself is called "#tensorflow", so i don't know if it really is a path alias and if not, maybe WebPack magically knows that it has to search in "node_modules" for this module?
As you can see i'm really confused about this and i would be greatfull if somebody could clear this up for me and explain what i must do to stop VS Code from complaining about the import.
Found the solution on my own.
I only found stuff about defining the aliases in the tsconfig.json expclicitly in the "path" option, but this couldn't be the answer to my problem because in my other Angular projects there is nothing like this defined even though I'm using #Angular imports there a lot without this problem.
But then I found this in my Angular project "moduleResolution": "node".
As stated in othe typescript documentation:
However, resolution for a non-relative module name is performed differently. Node will look for your modules in special folders named node_modules.
And behold, it works. Yes I could have probably tried this earlier, since its written in the message from VS Code from my question, but i though this was something for only node.js specific projects and I didn't read about this anywhere.

How to find an ES6 import module without a relative path?

I have an ES6 import.
import MyAwesomeComponent from 'packageNameOnlyWithoutPath';
I want to inspect the file packageNameOnlyWithoutPath. But I can't find it. I looked in node_modules but I don't see it there. So it might be hiding out elsewhere in the app.
Is there a canonical way to find the path that leads to packageNameOnlyWithoutPath?
you might want to take a look at index.js file in the packageNameOnlyWithoutPath folder inside the node_modules.
Else use text editors which supports goToDefinition plugin
TL;DR: Check resolve aliases in Webpack (or similar bundler) config or .babelrc
There's two places you can check first.
If you are using a bundler like Webpack, resolve aliases can be declared in the Webpack config file (usually webpack.config.js).
But I have also recently started using pure babel and node. The reoslves can also be declared in the .babelrc file (cleaner approach IMHO).
You should find what you're looking for in one of the above.

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 importing video.js returns an empty object

I am trying to use video.js via webpack.
I installed video.js via npm - npm install video.js --save-dev
In webpack I read that video.js should be loaded via script loader else it throws an error.
This is how I am loading video.js through the babel loader
module:
loaders: [
{
test: /video\.js/,
loader: 'script'
}
]
I got this solution from here https://github.com/videojs/video.js/issues/2750
This is my import statement
import videojs from 'video.js';
The issue that I now face is the import is returning an empty object, so when I try to do this:
var vidTag = ReactDOM.findDOMNode(this.refs.html5Video);
this.videojs = videojs(vidTag);
I get this error:
renderer-0.js:8031 Uncaught (in promise) TypeError: (0 , _video2.default) is not a function(…)
Any help will be much appreciated. I am new to ES6 / React / Webpack
Please take a look at the loader's README before copy&pasting some random code. The script-loader is not appropiate here, because it imports scripts into the global scope while skipping the whole module system.
So, if you wanted to use the script-loader, you would just write:
import "script-loader!video.js";
console.log(videojs); // should be an object now
Usually I would not recommend the use of the script-loader because it neglects the whole point of a module system where you import stuff explicitly into the local scope. In the example above, the import happens as a side-effect into the global scope which is effectively the same as just using a <script> tag with all its downsides like name clashes, etc.
There are often better alternatives to it, like the exports-loader, which appends a module.exports at the end of the module, thus turning an old-school global script into a CommonJS module.
In this particular case, however, you don't need a loader at all because video.js is already aware of a CommonJS module system. Just write import videojs from "video.js";.
There is another minor problem, however. If you compile this with webpack, it will print a warning to the console:
WARNING in ../~/video.js/dist/video.js
Critical dependencies:
13:480-487 This seems to be a pre-built javascript file. Though this is possible, it's not recommended. Try to require the original source to get better results.
# ../~/video.js/dist/video.js 13:480-487
This is because webpack detects that this file has already been bundled somehow. Often it's better to include the actual src with all its tiny modules instead of one large dist because this way webpack is able to optimize the bundle in a better way. I've written down an exhaustive explanation about how to import legacy scripts with webpack.
Unfortunately, video.js does not include its src in the version deployed at npm, so you're forced to use the dist. In order to get rid of the error message and to improve webpack's build time, you can instruct webpack to skip video.js when parsing the code for require() statements by setting the module.noParse option in your webpack.config.js:
module: {
noParse: [
/node_modules[\\/]video\.js/
]
}
Usually it's safe to flag all pre-bundled modules (typically those with a dist folder) as noParse because they are already self-contained.
include SDN
<script src="//vjs.zencdn.net/5.11/video.min.js"></script>
webpack config:
config.externals = {
'video.js': 'videojs'
};

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