Reputation: 3716
I'm currently writing a node CLI tool and using webpack to bundle all of my assets. The entry point for this application is the js file where I actually parse process.argv
and run a command (For reference, I'm using tj/commander). This way, once the bundling is complete, I can enter ./<outputFile>
and it will run my application. The entry file looks like this:
import cli from './cli';
cli.parse(process.argv);
// If nothing was supplied
if (!process.argv.slice(2).length) {
cli.outputHelp();
}
The bundling works fine but I can't get webpack to output the file as an executable. Once I run chmod +x <outputFile>
, everything works perfectly. Is there a way that I can tell webpack what permissions to grant an output file?
Upvotes: 7
Views: 6521
Reputation: 7558
This is how I did it with Webpack 5:
import { promises as fs } from 'fs';
plugins: [
new webpack.BannerPlugin({
banner: '#!/usr/bin/env node',
raw: true,
entryOnly: true
}),
function() {
this.hooks.done.tapPromise('Make executable', async () => {
await fs.chmod(`${__dirname}/dist/app.js`, '755');
});
}
]
Upvotes: 1
Reputation: 5367
I'm surprised no one said a thing about webpack's BannerPlugin
. I do something similar than @oklas, but using BannerPlugin
to add the specific node shebang:
{
plugins: [
new webpack.BannerPlugin({
banner: '#!/usr/bin/env node',
raw: true,
}),
],
}
Then I simply add the execution permissions just adding chmod
to my package.json
file:
"scripts": {
"build": "webpack && chmod +x dist/mycommand"
}
Anyway, if you'd like to just use webpack you can use the WebpackShellPlugin
, as said by oklas (note that using this forces you to add a new dependency, that's why I avoid using this approach):
const WebpackShellPlugin = require('webpack-shell-plugin')
{
// [...]
plugins: [
new WebpackShellPlugin({
onBuildEnd:['chmod +x dist/mycommand'],
}),
],
}
If you want to avoid including WebpackShellPlugin
as a dependency, you can try to define a custom plugin based on fs
, as said by @taylorc93
Upvotes: 9
Reputation: 2754
You'll need to append #!/usr/bin/env node
on top of the file.
I ended up with this webpack plugin using shelljs
plugins: [
// ...plugins,
function () {
this.plugin('done', () => {
shell
.echo('#!/usr/bin/env node\n')
.cat(`${__dirname}/build/outputfile.js`)
.to(`${__dirname}/commandname`)
shell.chmod(755, `${__dirname}/commandname`)
})
},
]
Upvotes: 3
Reputation: 8220
One simple way is to use npm. Do you have an package.json
in your project?
Add "build": "webpack && chmod +x outputFile"
to the scripts
section of your package.json
and build your project by running npm run build
.
Another way is to add one of these solutions to your webpack.config.js
:
simple plugin from this answer which has pre
and post
build handlers
use on-build-webpack plugin, which executes js code at the end of the webpack build process
Whatever you choose, you'll need to add this piece of code:
var chmod = require('chmod');
chmod("outputFile", 500);
Upvotes: 6
Reputation: 3716
Although @oklas's solution worked perfectly for me, I really wanted to try and keep all of this within webpack. I realized after a little more thought that this could all be done by a very simple plugin:
plugins: [
// ...plugins,
function() {
this.plugin('done', () => {
fs.chmodSync('bin/program-name.js', '755');
// When the webpack output doesn't have a .js extension, minification fails :(
fs.renameSync('bin/program-name.js', 'bin/program-name');
})
},
]
Use whichever way suits your needs!
Upvotes: 2