next.js and webpack resolve.modules - node.js

I am building a React app using next.js, and I am playing around with the webpack config, in the next.config.js file.
Specifically, I'd like to have nicer imports using webpack's resolve.modules config.
However, when I add the following line in the next.config.js file :
config.resolve.modules
.concat(['styles','static','components','imports'])
and then
import FooBar from 'components/index/FooBar", for example in a pages/index.js file, it still won't work. FooBar is not found.
The component exists, and the import works fine if I use a relative path. However I'd like to have nicer imports, and I know it is possible with webpack (see react-boilerplate for example).
Am I doing something wrong with webpack ? Maybe it's a real bug ?

Check the NextJS example with-absolute-imports
const path = require('path')
module.exports = {
webpack (config, options) {
config.resolve.alias['components'] = path.join(__dirname, 'components')
return config
}
}
Alternatively, should work by adding this to next.config.js file:
config.resolve.modules.push(path.resolve('./'));
(and it doesn't require any babel plugin)

resolve.modules will look into the directories you configured for the modules you import. So when import components/index/FooBar it will look in:
styles/components/index/FooBar
static/components/index/FooBar
components/components/index/FooBar
imports/components/index/FooBar
A relative path looks further, but that's not relevant here and the path remains the same, just climbing up the directory tree (see resolve.modules).
Presumably none of these paths match your component. To get component/index/FooBar you need to import just index/FooBar.
import FooBar from 'index/FooBar';

Related

Importing dotenv files with ES Modules syntax will not allow custom path to file

So basically I'm using ES modules in node and everything is working fine until I tried to moved my .env file from the root of the folder to a config folder and now node can't locate it, even if I specify the path within the dotenv.config() options, like so:
import { config } from "dotenv";
config({path: './config/.env'})
my console throws this:
Error: Expected token to be set for this request, but none was presentat RequestManager.resolveRequest (file:///C:/Users/macfi/OneDrive/Documents/GitHub/new_discord_bot/node_modules/#discordjs/rest/dist/index.mjs:734:15)at RequestManager.queueRequest (file:///C:/Users/macfi/OneDrive/Documents/GitHub/new_discord_bot/node_modules/#discordjs/rest/dist/index.mjs:706:46)at REST.raw (file:///C:/Users/macfi/OneDrive/Documents/GitHub/new_discord_bot/node_modules/#discordjs/rest/dist/index.mjs:861:32)at REST.request (file:///C:/Users/macfi/OneDrive/Documents/GitHub/new_discord_bot/node_modules/#discordjs/rest/dist/index.mjs:857:33)at REST.put (file:///C:/Users/macfi/OneDrive/Documents/GitHub/new_discord_bot/node_modules/#discordjs/rest/dist/index.mjs:851:17)at main (file:///C:/Users/macfi/OneDrive/Documents/GitHub/new_discord_bot/src/index.js:101:16)at file:///C:/Users/macfi/OneDrive/Documents/GitHub/new_discord_bot/src/index.js:110:1at ModuleJob.run (node:internal/modules/esm/module_job:198:25)at async Promise.all (index 0)at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
Just so it's clear, if I keep the .env file in the root, everything works, like this:
dotenv.config()
Can't really find anything that helpful online about it, the dotenv docs only display a custom path example using CommonJS so yeah. Appreciate it if anyone has any insight to share.
I tried using the node path module and the __dirname but apparently those get disabled if you enable ES Modules

What the "#" stands for in paths like "#/assets/xyz"?

Sometimes I see paths written like "#/assets/XXX", and I reckon it refers to the root maybe (in Nodejs)? But i guess it's a syntax that doesn't apply everywhere because when I want to refer to the root folder and try to use it, it sometimes break. I am not sure the implications of it.
The "#" is often used as an alias for a frequently used path (like src/) in webpack environments. You have to define it in your configuration file so the "#" can be resolved in the build-process.
If you work in an ES6 environment and import a component several times, it can be handy to create an alias for the component path.
Example (source: webpack documentation):
resolve.alias
object
Create aliases to import or require certain modules more easily. For example, to alias a bunch of commonly used src/ folders:
webpack.config.js
module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
}
};
Now, instead of using relative paths when importing like so:
import Utility from '../../utilities/utility';
you can use the alias:
import Utility from 'Utilities/utility';
You can find a similar answer here: https://stackoverflow.com/a/42753045/10764912

How to use Webpack loaders in a Node app?

Is there a way to use Webpack loaders in a Node app / Run a Node app in a Webpack environment?
For instance I've got a webpack config that has a style-loader. In my Node app I do the following:
import style from 'style.css'
console.log(style.someClass)
I wanna run it like $ node app.js
I've got an idea that might work, based on the Webpack NodeJS API. What if we put the code that we want to be able to use the Webpack environment (with the configured module loaders) into a module:
appModule.js:
import style from 'style.css'
console.log(style.someClass)
And require it with the following:
app.js:
import Webpack from 'webpack'
import MemoryFS from 'memory-fs'
...
webpackConfig.entry = 'appModule.js'
webpackConfig.output = 'appModule-out.js'
let compiler = Webpack(webpackConfig)
let mfs = new MemoryFS()
compiler.outputFileSystem = mfs
compiler.run(function (err, stats) {
require(webpackConfig.output)
})
Probably it won't work because the require looks for the output on the physical FS... Can we require from the memory FS? I have not tried it yet - Any idea?
Webpack loaders aren't transpilers or interpreters, they simple gather assets that are then handled off to something like SASS or a text concatenator; within the confines of Webpacks environment.
Thus it is not possible to reuse them in the way you want, because while you can of course import and call them (they're still just functions + classes), they don't convert CSS to JSON objects (they don't do this) as you have written in your desired example.
It looks like you just need a JS implementation of a css parser - have a look at https://github.com/reworkcss/css
You should be able to create a compilation targeting the node environment which you can ultimately run by simply calling node output.js and this will immediately execute the entry point module.
Be aware, in case that you're using a newer version of Node.js, that Webpack doesn't support the ES2015 module syntax, so you'll have to configure Babel for Node.js as well to transform the modules.

How to have path alias in nodejs?

Let's say i have following codes:
var mod1 = require('../../../../ok/mod1');
var mod2 = require('../../../info/mod2');
It's not pretty coding like above, i am wondering if there is a way to configure the root resolver just like webpack-resolve-root in nodejs?
So far as i know, the NODE_PATH can be used to replace the root of node_modules, but that's not what i want. I'd like to have the resolver to resolve multiple folders in order.
Updated answer for 2021.
nodejs subpath imports have been added in: v14.6.0, v12.19.0
This allows for you to add the following to package.json
"imports": {
"#ok/*": "./some-path/ok/*"
"#info/*": "./some-other-path/info/*"
},
and in your .js
import mod1 from '#ok/mod1';
import mod2 from '#info/mod2';
There is an npm package called module-alias that may do what you are looking for.
The best way to approach this would be to use a global (config) container.
In most cases you will have a config file in your application. In this config you can add a property which will be an object containing all absolute paths to files/folders.
Because config files are used at the start of you application, you just do the following:
var config = require("./config.js");
//config = {... , path: {"someModule": "/absolute/path/to", "someModule2": "/absolute/path/to"...}}
global.CONFIG_CONTAINER = config
Later on in your application you can just use
var myModule = require(CONFIG_CONTAINER.path.someModule)
// + concat if you are looking for a file
In case you have some complex paths and you need a more dynamic system, you can always implement a function inside the config that will build paths for you. ( config.makePath = function(){...} )
That should take care of it in a nutshell.

Relative paths using requirejs in combination with Typescript and AMD

There are several Javascript files, organized in folders Scripts/folder1, Scripts/folder2, ...
With requirejs.config.baseUrl a folder is defined as the default, for example Scripts/folder1. Then in requirejs.config.paths some files are addressed with just the filename, and some are addressed with a relative path (like ../folder2/blabla).
When coding the Typescipt file folder2/blabla.ts we need the module "math" from folder1. So we write
import MOD1 = module("../folder1/math");
Regarding Typescript, anything is fine with that. It can find the module. However, with requirejs there is a problem. It does not know the module "../folder1/math", it only knows "math".
The problem seems to be that the import statement expects a filename, being adressed by starting from the current directory. However, this isn't the module id that requirejs knows about.
Using absolute paths anywhere, both in the requirejs configuration and the import statement in Typescript, solves the problem.
Am I doing this wrong? Or are absolute paths the way to go?
Specify a baseUrl to be equivalent to the root folder of your Typescript files:
require.config({
baseUrl: './scripts',
}
)
Then when you use relative paths starting from the scripts folder you can just do import like you normally do in typescript and requirejs will be using the same base path.
Update: This presentation should should answer all your url / using js from Typescript questions: http://www.youtube.com/watch?v=4AGQpv0MKsA with code : https://github.com/basarat/typescript-amd/blob/master/README.md
In you require configuration specify paths for each module. That should solve paths problem:
require.config({
paths: {
jquery: 'libs/jquery-1.7.1.min',
jqueryui: 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min'
// Other modules...
}
});

Resources