Can I use commonjs plugins when running esbuild as a module? - es6-modules

I have an esbuild config taken straight from the docs:
import * as esbuild from 'esbuild'
let ctx = await esbuild.context({
entryPoints: ['./js/app.js'],
outdir: 'static',
bundle: true,
minify: true,
sourcemap: true,
plugins: [
postcss,
],
})
await ctx.watch()
let { host, port } = await ctx.serve({
servedir: 'static',
})
But I need to add some plugins, which are all still in commonjs (the esbuild-postcss plugin is a good example).
When I try to add require('esbuild-postcss') I get the error:
ReferenceError: require is not defined in ES module scope, you can use import instead
Which makes sense - but does this mean I can't use any esbuild plugins until they are converted to es modules?
Note: I know I can go back to a commonjs esbuild config, but as far as I can tell I won't be able to use the serve option if I do that.

Related

Invalid option in build() call: "watch"

I am following the example as it is described here:
https://bilalbudhani.com/chokidar-esbuild/
When I do:
node esbuild.config.js --watch
I get the message:
[ERROR] Invalid option in build() call: "watch"
I have no idea why this is happening.
Is "watch" not longer a parameter?
I also did this example:
const path = require('path')
require("esbuild").build({
entryPoints: ["application.js", "client.js"],
bundle: true,
sourcemap: true,
outdir: path.join(process.cwd(), "app/assets/builds"),
absWorkingDir: path.join(process.cwd(), "app/javascript"),
minify: true,
watch: true,
})
.then(() => console.log("⚡Done"))
.catch(() => process.exit(1));
If i remove the line "watch:true", it compiles ok. But if I leave it, I get the same error:
Invalid option in build() call: "watch"
when I do: node esbuild.config.js
Thank you in advance
Summing up from the comments:
esbuild <v0.16 has removed the watch option. Most tutorials and HowTos are pointing to that version. Downgrade your esbuild to that if you want to use it like described there.
Better option is to use esbuild >0.16 which has a built in live reload which combines watch and serve using the newly introduced context

Error: secretOrPrivateKey must have a value

I am using jwt to create token, but when i login via postman I get the error "Error: secretOrPrivateKey must have a value" from my console. I have attached my login code. Please anyone who can help me
exports.login = (req, res, next) => {
User.findOne({
where: {
email: req.body.email
}
})
.then(user => {
if (!user) {
return res.status(401).json({
message:
"Auth failed!! either the account does't exist or you entered a wrong account"
});
}
bcrypt.compare(req.body.password, user.password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed",
token: token
});
}
if (result) {
const token = jwt.sign(
{
email: user.email,
password: user.id
},
process.env.JWT_KEY,
{
expiresIn: "1h"
}
);
res.status(200).json({
message: "Auth granted, welcome!",
token: token
});
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
this is my env.json file
{
"env":{
"MYSQL":"jllgshllWEUJHGHYJkjsfjds90",
"JWT_KEY": "secret"
}
}
It looks like your application can't read the environment variable properly.
I don't know which package you are using to load environment variables but the simplest way is using dotenv package.
After installing it with npm i dotenv, import it as early as possible in your application main file like this:
require("dotenv").config();
Create .env file in your application root folder with this content ( as you see the format is key=value)
MYSQL=jllgshllWEUJHGHYJkjsfjds90
JWT_KEY=secret
Then you can access their values like you already did:
process.env.JWT_KEY
.env file:
Remove the process.env.JWT_SECRET_KEY and do it this way: ${process.env.JWT_SECRET_KEY} wrap it with backtick.
It solved the problem for me.
Had this issue with NestJS when trying to rely on process.env.X. Supposedly #nestjs/config uses dotenv in the background but it doesn't work as expected. I either had to use ConfigService or explicitly configure dotenv in the given files:
jwt.strategy.ts
import * as dotenv from 'dotenv';
dotenv.config();
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
secretOrKey: process.env.JWT_SECRET,
});
}
}
or
#Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(configService: ConfigService) {
super({
secretOrKey: configService.get<string>('JWT_SECRET'),
});
}
}
auth.module.ts
import * as dotenv from 'dotenv';
dotenv.config();
#Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
}),
],
})
or
#Module({
imports: [
JwtModule.registerAsync({
imports: [ConfigModule]
useFactory: async (configService: ConfigService) => {
return {
secret: configService.get<string>('JWT_SECRET'),
};
},
inject: [ConfigService],
}),
],
})
It works for me only if I concatenate it with an empty string like this:
"" + process.env.JWT_KEY
simply remove the process.env.JWT_KEY and replace with "secret key using" ES6 String Literals
${process.env.JWT_SECRET_KEY}
it solves this for me
It was accidentally adding the JWTService as a provider in my AuthModule. That, it seems was overriding the defaults I had set up when registering the JWTModule.
What I had before:
#Module({
imports: [
PassportModule,
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
const authConfig = configService.get<AuthenticationConfig>(
PathNames.AUTH,
);
return {
secret: authConfig.accessToken.secret,
signOptions: {
expiresIn: authConfig.accessToken.expiryTime,
},
};
},
}),
],
providers: [
AuthService,
UsersService,
PrismaService,
AuthResolver,
// this was the problem
JwtService,
JWTStrategy,
],
exports: [AuthService],
})
export class AuthModule {}
Are you sure process.env.JWT_KEY has a valid value? I believe it is undefined or null.
Put the require('dotenv').config() at the top of the server.js file
This solved for me
You can try this, it works for me.
"" + process.env.JWT_KEY
I simply removed process.env.JWT_KEY and replace with "secret" and its working fine.
Just add back quotes `` and use the syntax ${process.env.SECRET_KEY} in them.
I also advise you to use the cross-env package, after installing which you can make two files with environment variables .development.env and .production.env
To configure cross-env, just paste this into a package.json "scripts" field:
"start": "cross-env NODE_ENV = production nest start",
"start:dev": "cross-env NODE_ENV = development nest start --watch"
it is for nestjs
To chime in on this, I found this error was being caused by the contents of the key I was trying to use. I regularly use a "password generator" for such strings, and it had given me the following to use:
<B#LCK$^\7(T^fuaQemeR&6s:##AAwe#?T,c'=+kxT?euCP27R/D=uRm893$=^_h^f={c.)MD#[%zg}$K8_D#D-_/tb2?Q>RFr(}H:Fp#{&yNFt#2Y<K\GB28unz
if the above is placed in between back ticks or speech marks it will not be able to be parsed as a string and will therefore cause this error as described above.
As an extra level of de-bugging make sure you first wrap your key in backticks or parenthesis in order to make sure that an un-parsable string isn't the issue.
You can also try and specify an object with a property the path to the configuration file like
require("dotenv").config({path: '/path_to_env_file'});
I also had the same issue I came to realize that the issue was that I didn't include dotenv package
so
import * as dotenv from 'dotenv';
then put dotenv.config() on top before any other implementation
step 1: install dotenv
step 2: import that in app.js:
const dotenv = require("dotenv");
step 3:
dotenv.config(`${process.env.SECRET_KEY}`);
your issue will solve
Check that process.env.JWT_KEY is defined:
console.log(process.env.JWT_KEY)
I have this issue in backend. To solve I create a init.js file, add my config to process.env and require index.js file
const config = require(`<path of config json file>`)
process.env = { ...process.env, ...config }
require('./index')
then get init.js as entry index in webpack
Do not fix this by prepending an emptystring '' + to your JWT or by wrapping it inside of ${process.env.YOUR_JWT}. This resolves the error but doesn't fix the underlying issue.
The problem is actually that dotenv is being invoked after your module code where you try to read from process.env, therefore process.env.ANYTHING_HERE wont have been populated by dotenv yet.
Probably in your entrypoint file (i.e. main.ts you are calling import on app.module before calling into dotenv or nest's wrapper around it.
The comprehensive fix here is to modify main.ts to call require('dotenv').config({ path: './.env' }) or however you bootstrap your env, and to do this before you import any other file. That way, all your imports will have process.env populated with your environment variables before they run.
If you just fix the issue by following the solutions above then you're going to end up setting your secret key to 'undefined' because what you're actually just doing is you're concating empty string with the env variable for your secret before it exists. It solves the error but clearly wont be the secret you wanted from your config file.
It's also not a good idea to fix it this way because you're not solving this issue for your other environment variables either. They still wont be available to your modules where they need them because they're being included after instead of before, and thus you're highly likely to have other issues in your project where your environment variables are undefined elsewhere too.
This message was occured to me only when I was running E2E tests. In my case I had to explicitly set as an secret option to fix it.
this.jwtService.sign(tokenPayload, { secret: `${process.env.JWT_SECRET}` });
In my case using process.env.JWT_SECRET_KEY.replace(/\\n/g, "\n") worked perfectly fine.
I took this reference from how firebase uses keys stored in .env file to parse it. and they use .replace(/\\n/g, "\n") to parse the key.
IN token REPLACE GRAB THE FOLLOWING IN BACKTICKS. THIS SYNTEX WORKED FOR ME TRY THIS.
${process.env.JWT_SECRET_KEY}
Perhaps you use 2 .env files (for development and for production). For example, you use cross-env NODE_ENV=development in package.json file in "start:dev". If it is true, don't forget add
ConfigModule.forRoot({
envFilePath: `.${process.env.NODE_ENV}.env`
}),
to your module file:
#Module({
imports: [
PassportModule,
ConfigModule.forRoot({
envFilePath: `.${process.env.NODE_ENV}.env`
}),
JwtModule.register({
secret: process.env.JWT_KEY,
signOptions: {
expiresIn: '1h'
},
}),
],
providers: [AuthService, LocalStrategy, JwtStrategy],
exports: [AuthService]
})
export class AuthModule {}
Then you haven't to add something to file with class JwtStrategy and another.
${process.env.JWT_SECRET_KEY}
it worked for me by placing it in auth.module.ts
Use the code above(image), to overcome your problem.
This error is coming because .env files is not accessible in auth.js.
so dont write process.env.JWT_KEY
instead of this just write a random string in single quotes.
eg: 'iamtheprogrammer'
ERROR:
const token = jwt.sign({_id:user._id}, process.env.SECRET)
CORRECT CODE:
const token = jwt.sign({_id:user._id},'iamtryingtoaddsome')

How do I create a "fat" js file with rollup using esm?

I have the following code..
// ui.js (generated by rollup
import Vue from 'vue';
import VueRouter from 'vue-router';
(()=>{
console.log("Wow it actually works");
Vue.use(VueRouter);
const routes = [
{
path: '/',
component: Viewport
}
];
const router = new VueRouter({
mode: "history",
routes: routes
});
window.app = new Vue({ router });
window.app.$mount('#jg-app');
})();
<script src="ui.js" type="module"> </script>
The problem is when I run this I get...
Uncaught TypeError: Failed to resolve module specifier "vue". Relative references must start with either "/", "./", or "../".
This leads me to believe I need a "fat" js that includes dependencies.
I also want to keep everything in es6 modules and avoid introducing say babel.
Is there a way to do this using rollup?
Update
Tried this...
import Vue from "./vue";
But then I get...
Error: Could not resolve './vue' from src/index.js
As far as I can tell this is not possible. I instead had to move the import from the ui project to the server project and create a static js file that looked like this...
//client
import Vue from "./vue"
let app = new Vue(...);
app.$mount('#jg-app');
and import the esm.browser version
// server
app.use('/vue', express.static(__dirname + '/node_modules/vue/dist/vue.esm.browser.js'));
// template
script(src="/main.js" type="module")
Now Vue is working, however, dependencies like Vue-Router appear to not have this es.browser style file.
This is not a solution, it's a workaround
The below rollup config is not esm, it's just a way to create a bundle with dependencies included.
You get one minified browser-compatible JS file.
Here's my working example rollup.config.js (you should replace input: 'src/index.js' with your web app entry point and output.file with a location for the generated bundle):
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import builtins from 'rollup-plugin-node-builtins';
import babel from 'rollup-plugin-babel';
import visualizer from 'rollup-plugin-visualizer';
import { terser } from "rollup-plugin-terser";
const browserPlugins = [
resolve({browser: true}), // so Rollup can properly resolve cuid
babel({
exclude: 'node_modules/**',
babelrc: false,
presets: ['es2015-rollup'],
}),
// builtins(),
commonjs(),
visualizer(),
terser(),
]
export default [
// browser-friendly UMD build
{
// external: Object.keys(globals),
input: 'src/index.js',
output: {
name: 'thinflux',
file: './dist/browser/thinflux.min.js',
format: 'umd'
},
plugins: browserPlugins,
}
];
One more thing: express should statically serve the output.file path, not your source files

bundling CesiumJS using RollupJS

I am trying to bundle CesiumJS with Rollup. I thought I could just do an import like this:
import Cesium from 'cesium/Build/Cesium/Cesium.js'
with the following rollup.config.js file. I am getting a bundle.js but when I run it I get lots errors:
Uncaught TypeError: Cannot read property 'document' of undefined
at bundle.js:formatted:102314
function() {
!function(e) {
var t = this || eval("this")
, r = t.document // it is complaining on this line
, i = t.navigator
, n = t.jQuery
, o = t.JSON;
rollup.config.js
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import uglify from 'rollup-plugin-uglify'
import { minify } from 'uglify-es'
export default {
input: 'scripts/Main.js',
output: {
file: 'dist/bundle.js',
format: 'es',
},
"options": {
sourceMap: 'inline',
output: {
format: 'es'
}
},
plugins: [
resolve({
jsnext: true,
main: true,
browser: true,
}),
commonjs(),
uglify({}, minify)
]
}
ES modules are always in strict mode — by extension, when something is imported into Rollup and converted to an ES module, it also runs in strict mode.
In strict mode, the value of this inside a function is undefined, unless it's a) called as a method, or b) explicitly set with call or apply.
This is expected behaviour with Rollup, and it isn't technically a bug with Cesium, but I would suggest raising an issue with them and seeing if they can use a more modern way of accessing the global variable. There's really no reason to be relying on non-strict behaviour in 2017!
As a last resort you could string-replace this || eval("this") (or this||(0,eval)("this"), as it is in the minified version) with window.
If there are lots of other errors after making that change, it may be impossible to include Cesium in your bundle, and you would need to keep it as an external module.

Configuring app's basename in react-router

I'm struggling a bit with react-router 2.x configuration, specifically app basename.
I've an application which may have different base root throughout its lifecycle. For instance:
/ in development
/users in production
/account in production after migration
The basename comes into play in several places:
static asset compilation in Webpack
react-router main configuration
specifying redirect routes in redux actions
providing something like redirectUrl to API calls
My current solution is to have an ENV variable and make it available both to Webpack and to the app itself by injecting window.defs via an Express server, but I still end up having things like ${defs.APP_BASENAME}/signin in way too many places throughout the app.
How can I abstract the app base, or at least tuck it away in a single location? I should be able to specify the base route in Router's config, and then simply use relative routes somehow, right? Or am I missing something?
You can decorate your history with a basename. You could mix this with a DefinePlugin in your Webpack configuration to specify which basename should be used.
// webpack.config.js
new Webpack.DefinePlugin({
BASENAME: '/users'
})
// somewhere in your application
import { useRouterHistory } from 'react-router'
import { createHistory } from 'history'
const history = useRouterHistory(createHistory)({
basename: BASENAME
})
Given the basename: /users, React Router will ignore the /users at the beginning of the pathname so:
The URL /users is internally matched by the path /
The URL /users/profile matches the path /profile.
Similarly, you do not have to append the basename to the path when you are navigating within your application.
<Link to='/friends'>Friends</Link> will navigate to /friends internally, but the URL in the location bar will be /users/friends.
Today I ran into the same issue:
On my localhost I let an NGINX serve the stuff in the root context, but on my prod server, an Apache serves the stuff from a subdirectory...
Inspired by the answer from Paul S and inspired by the infos here:
https://github.com/ReactTraining/react-router/issues/353
I got the for me working solution:
In the Webpack config file I defined a plugin for my localhost dev env:
plugins: [
new webpack.DefinePlugin({
BASENAME: JSON.stringify("/")
})
],
In the Webpack PROD config file I defined a plugin for my prod env in a subfolder, i.e. www.example.com/users:
plugins: [
new webpack.DefinePlugin({
BASENAME: JSON.stringify("/users/")
}),
And in my react-router definitions I just reference:
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { useBasename } from 'history'
...
<Router history={useBasename(() => browserHistory)({ basename: BASENAME })}>
For me a very elegant solution and easy too. It just cost me around five hours of looking around :-)
In React Router V6.
Edit package.json and add homepage : Directory name key value as follows
"homepage" : "https://blog.sangw.in/react-student-management",
OR
"homepage" : "/react-student-management",
and on Routers BrowserRouter add basename : Directory name as follows
<BrowserRouter basename={'/react-student-management'}>
and you are done.
Visit https://blog.sangw.in/react-student-management and app will be deployed and working.
Try this it will work
import { createBrowserHistory } from 'history';
const history = createBrowserHistory({
basename: 'base-name'
})
<Router history={history}>
</Router>