"Paranormal Activity"! require and import worked async - node.js

I'm using Typescript in my Node.js project. So generally I'm using import * as bla from 'bla';. Our app is served in DigitalOcean and we faced problems in setting up environment variables (tried both ecosystem and .env, none is consistent). Finally we decided to hard code the variables in a env.js file and require it at the beginning of app.ts. After that here is how my app.ts file looks like:
require('./env.js');
.
.
.
import indexRouter from './routes/index';
Interestingly enough, variables defined in env.js were shown undefined in indexRouter. So I've done some console.log. And this is what I got:
here in route
here in env.js
Any idea why env.js was not loaded first? How to solve this?

In TypeScript you can import a module for side-effects only. This is normally discouraged, but seems to match your use case of setting some global state...
Though not recommended practice, some modules set up some global state that can be used by other modules. These modules may not have any exports, or the consumer is not interested in any of their exports. To import these modules, use:
import "./env.js";

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

How to read file without require('fs') - NodeJS

I've got a challenge to read a txt file that located in the same directory as my app.js.
In the challenge I can't use require so I can't import fs.readFileSync to my app.
Looking for other ways to console.log the txt content. Any ideas?
Without using require explicitly, you can use: module.constructor._load or process.mainModule.constructor._load
const fs = module.constructor._load('fs');
console.log(fs.readFileSync('./test.txt'));
Note that process.mainModule will be undefined if there is no entry script. (Not your case)
Of course this shouldn't be used in production code, since it's an undocumented API and may change. But will do for your challenge.
forgot to mention that I have to run it as node app.js
Otherwise you could use ES6 modules too, but that requires an additional flag: node --experimental-modules app.js

how to use node module with es6 import syntax in typescript

I have a typescript project which has uses one of our node modules which normally runs in our front-end. We are now looking to use this module in node on our server.
The module uses es6 import syntax import { props } from 'module/file'
When I include a ref in typescript using either of the following methods
import { props } from 'module/file';
var props = require('module/file');
I get the following error from typescript
unexpected token 'import'
(function (exports, require, module, __filename, __dirname) { import
It's a big job to re-write the module, and I've tried using babel with babel-plugin-dynamic-import-node, as well as SystemJS.
The problem with these systems is that they are all asynchronous, so I can't import the module in the standard fashion, so I would need to do a whole bunch of re-write when we get to the point that I can use import natively in node.js.
I can't be the first person to have this issue, but I can't seem to find a working solution.
--------------- update with set-up -------------
In response to #DanielKhoroshko's response. The original module I am trying to import is normally packaged by webpack in order to use on the front-end. I am now trying to use this same module both server-side and in the front-end (via webpack on the front-end) without re-writing the imports to use require and without running webpack to bundle the js to use on the server.
To be clear, the original module is written in JS, our service which is trying to use this module is written in typescript and transpiled. When the typescript tries to require the old module which uses import, it is at this point that we are running into the issue.
------------------ some progress ---------------------------
I've made some progress by creating a file in my imported module which uses babel in node.js to transpile the es6 code into commonJS modules.
I've done this via
var babel = require("babel-core")
var store = babel.transformFileSync(__dirname + '/store.js', {
plugins: ["transform-es2015-modules-commonjs"]
});
module.exports = {
store: store.code
}
I can now get the store in my new node.js project. However, the submodules within the store.js file are not included in the export.
So where in my module, it says
import activities from './reducers/activities';
I now get an error
Cannot find module './reducers/activities'
How can I get babel to do a deep traversal to include the sub-directories?
unexpected token 'import' means you are running es-modules code in environment that doesn't support import/export commands. If you are writing you code in TypeScript it's important to transpile it first before building for the browser or use ts-node to run it server-side.
If you are using webpack there are loaders ts-loader and awesome-typescript-loader
What is your setup?
To describe the module you would need to create an activities.d.ts file in the same folder where the js-version (I understood it is called activities.js and containers a reducer) resides with the following (approx.):
import { Reducer } from 'redux';
export const activities: Reducer<any>;
#Daniel Khoroshko was right in many ways, I ended up finding #std/esm which lets you import es6 modules and worked find for fetching the included imports as well.
var babel = require('babel-register')({
presets: ["env"]
});
require = require('#std/esm')(module);
var store = require('ayvri-viewer/src/store');
exports.default = {
store: store
}
I had to run babel to get a consistent build from es6 to node compatible es5

next.js and webpack resolve.modules

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';

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.

Resources