How to prevent Webpack from removing other files in output path? - javascript

Let's say I compile some JS assets to dist/static/js:
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
},
Before running npm run build I create one file in /dist/, /static/ and /js/.
After running npm run build this file was removed. The one created in /static/ and /js/ is gone. How can I prevent it?
I'm using Vue.js/Webpack boilerplate:
https://github.com/vuejs-templates/webpack

If you look here:
https://github.com/vuejs-templates/webpack/blob/17ed63b1b3a0eaaebd3f593c08c32107a7cb7e01/template/build/build.js
You can see that a package called rimraf is being imported:
const rm = require('rimraf')
This package is responsible for clearing out your assetsRoot and assetsSubDirectory. This is a good thing because usually when you re-run your build process from nothing, you want a clean slate to begin with.
I would advise you not to disable this but rather put your file in another directory or let your Javascript generate your file since the removal takes places before the compilation.

Related

How to change production build static folder path in a React project?

I have a React project on which I ran npm run build and it made a production build for me. The problem is that it gives me the following stylesheet injected into the index.html
<script src="./static/js/5.a4bfdba9.chunk.js"></script>
As you can see, the path set is ./static/js/
But I want the path to be set to ./static/dashboard/js/5.a4bfdba9.chunk.js
I can't figure out where or what to change to ensure every time I run the build, it takes the path specified by me instead of the default path?
Note: I looked at "homepage": "." attribute in package.json but changing this will only append a path before /static/js/
Update:
You will need to update the webpack config to achieve this. There are multiple ways to do that:
react-scripts eject (not recommended)
patch-package
react-app-rewired
I am providing steps for patch-package.
You need to make changes to the file config/webpack.config.js of react-scripts package. Here is a git diff of changes you need to make:
diff --git a/node_modules/react-scripts/config/webpack.config.js b/node_modules/react-scripts/config/webpack.config.js
index 26c2a65..ad29fbd 100644
--- a/node_modules/react-scripts/config/webpack.config.js
+++ b/node_modules/react-scripts/config/webpack.config.js
## -212,13 +212,13 ## module.exports = function (webpackEnv) {
// There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files.
filename: isEnvProduction
- ? 'static/js/[name].[contenthash:8].js'
+ ? 'static/dashboard/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
// TODO: remove this when upgrading to webpack 5
futureEmitAssets: true,
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
- ? 'static/js/[name].[contenthash:8].chunk.js'
+ ? 'static/dashboard/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
// webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
## -676,8 +676,8 ## module.exports = function (webpackEnv) {
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
- filename: 'static/css/[name].[contenthash:8].css',
- chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
+ filename: 'static/dashboard/css/[name].[contenthash:8].css',
+ chunkFilename: 'static/dashboard/css/[name].[contenthash:8].chunk.css',
}),
// Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding
Now if you make these changes to node_modules/react-scripts/config/webpack.config.js you will get the files outputted to your desired folder and index.html will have correct paths too.
But the changes in node_modules will be overwritten if you install/uninstall any package. To persist those changes you can use patch-package, which will write your changes in node_modules after install.
Here are the steps to setup patch-package, you can read their readme file for more details:
yarn add patch-package postinstall-postinstall
Add this in scripts section of package.json:
"postinstall": "patch-package"
run patch-package to create a .patch file:
npx patch-package some-package
commit the patch file to share the fix with your team
git add patches/react-scripts+4.0.3.patch
git commit -m "change output dir structure in react-scripts"
I have created a git repository too for your reference.
Old Answer:
Warning: won't work for moving contents of build folder. Can move the whole build folder only. eg: mv build/ dist/
Changing settings of create react app so that it outputs to different folder will be very complicated. But you can move the files after build completes, which is much simple.
In your package.json you have:
"scripts": {
"build": "react-scripts build"
You can add another script called postbuild that does something after the build and will be called by npm/Yarn automatically.
"postbuild": "mv build/ dist/"
This should move the files to different folder.
In case anyone else runs into a similar problem. My issue was that the project name was being prepended to the static path.
So the path was /{project-name}/static/5.a4bfdba9.chunk.js instead of /static/5.a4bfdba9.chunk.js
Following the solution provided I came across a note specifying the "public path" was inferred from homepage. In the package.json my homepage url included the project name.
Wrong
"homepage": "https://{URL}.io/project-name"
Correct
"homepage": "https://{URL}.io/project-name"
Removing the project name from this url resolved the issue.

Copy build files to another project using webpack

I have two project folders in the same parent folder. One is for front end files (JS, CSS, images etc.) and another is for the backend files. The frontend project uses webpack to build files into a dist folder. The backend project is the one which gets deployed to the server (or run on localhost).
So everytime I make change to a JS or CSS file, I run webpack build, copy the build files from frontend-project/dist folder to backend/frontend/js or backend/frontend/css folder and rerun the backend project.
This is really counter-productive. I want to copy the dist files automatically after build to the backend-project. Is there any way to do it in webpack using a plugin or without using one? I have used gulp for such kind of tasks before, but want to rely solely on webpack for now.
I tried the copy-webpack-plugin, but it doesn't run after the build, thus is not useful for me.
I see several ways how to reach your purposes:
You could specify your backend/frontend/js folder as an output folder for your bundles.
module.exports = {
//...
output: {
path: path.resolve(__dirname, '../backend/frontend')
}
};
If you need two copies of your bundles (one in frontend folder and another in the backend), you can use FileManagerPlugin to copy your bundle files to the backend after finishing of the build.
module.exports = {
//...
plugins: [
new FileManagerPlugin({
events: {
onEnd: {
copy: [
{
source: path.join(__dirname, 'dist'),
destination: path.join(__dirname, '../backend/frontend')
}
]
}
}
})
]
};
If you run build manually after every code change, I think this is unproductive. You can use webpack-dev-server to run build automatically when you develop. It doesn't store bundles in the file system, it keeps them in memory.

How to use node_modules on a "traditional website"?

I have used node to manage dependencies on React apps and the like, in those you use package.json to keep track of libs and use them in your scripts using ES6 import module syntax.
But now I'm working on a legacy code base that uses a bunch of jQuery plugins (downloaded manually and placed in a "libs" folder) and links them directly in the markup using script tags.
I want to use npm to manage these dependencies. Is my only option:
run npm init
install all plugins through npm and have them in package.json
link to the scripts in the node_modules folder directly from the markup:
<script src="./node_modules/lodash/lodash.js"></script>
or is there a better way?
Check out this tutorial for going from using script tags to bundling with Webpack. You will want to do the following: (Do steps 1 and 2 as you mentioned in your question then your step 3 will change to the following 3 steps)
Download webpack with npm: npm install webpack --save-dev
Create a webpack.config.js file specifying your entry file and output file. Your entry file will contain any custom JS components your app is using. You will also need to specify to include your node_modules within your generated Javascript bundle. Your output file will be the resulting Javascript bundle that Webpack will create for you and it will contain all the necessary Javascript your app needs to run. A simple example webpack.config.js would be the following:
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
resolve: {
alias: {
'node_modules': path.join(__dirname, 'node_modules'),
}
}
};
Lastly, add a <script> tag within your main HTML page pointing to your newly generated Javascript bundle:
<script src="dist/my-first-webpack.bundle.js"></script>
Now your web application should work the same as before your refactoring journey.
Cheers
I recommend Parcel js.
Then you only need:
Run npm init
Install dependency, for example npm install jquery
Import with ES6 syntax: import $ from "jquery";
And run with parcel

How do I include a .gitignore file as part of my npm module?

I am building an npm module that will generate a specific project template for certain software projects. As such, when a developer installs my npm module and runs it, I would like the program to create files and folders in a certain way.
One such file I would like to include in the project template is a .gitignore file because the software project is going to assume it will be tracked via git. However, when I call "npm install" on my module, npm renames all my .gitignore files to .npmignore files. How can I ensure that my .gitignore files are not tampered with by npm when I distribute my module?
Currently npm doesn't allow .gitignore files to be included as part of an npm package and will instead rename it to .npmignore.
A common workaround is to rename the .gitignore to gitignore before publishing. Then as part of an init script, rename the gitignore file to .gitignore. This approach is used in Create React App
Here's how to do it in Node, code from Create React App init script
const gitignoreExists = fs.existsSync(path.join(appPath, '.gitignore'));
if (gitignoreExists) {
// Append if there's already a `.gitignore` file there
const data = fs.readFileSync(path.join(appPath, 'gitignore'));
fs.appendFileSync(path.join(appPath, '.gitignore'), data);
fs.unlinkSync(path.join(appPath, 'gitignore'));
} else {
// Rename gitignore after the fact to prevent npm from renaming it to .npmignore
// See: https://github.com/npm/npm/issues/1862
fs.moveSync(
path.join(appPath, 'gitignore'),
path.join(appPath, '.gitignore'),
[]
);
}
https://github.com/npm/npm/issues/1862
Looks like this is a known issue. The answer at the bottom seems like the recommended approach. In case the issue or link ever gets destroyed:
For us, I think a better solution is going to be clear documentation that if authors wish to use .gitignore in their generators, they will need to name their files .##gitignore##, which will be a value gitignore set to the same string gitignore.
In this way, the file that gets published as a template file to npm is called .##gitignore## and won't get caught by npm, but then later it gets replaced with the string to be .gitignore.
You can see multiple commits dealing with npm issue 1862:
this project adds a rename.json:
lib/init-template/rename.json
{
".npmignore": ".gitignore",
}
this one renames the .gitignore:
templates/default/.gitignore → templates/default/{%=gitignore%}
index.js
## -114,6 +114,10 ## generator._pkgData = function (pkg) {
+ // npm will rename .gitignore to .npmignore:
+ // [ref](https://github.com/npm/npm/issues/1862)
+ pkg.gitignore = '.gitignore';
Edit: even though this answer causes .gitignore to be included in the published package (proven by unpkg), upon running npm install the .gitignore file is simply removed! So even getting NPM to include the file is not enough.
Another solution (simpler imo):
Include both .gitignore and .npmignore in the repo. Add the following to your .npmignore file:
!.gitignore
!.npmignore
Now that .npmignore will already exist in the published package, NPM won't rename .gitignore.

Working project structure that uses grunt.js to combine JavaScript files using RequireJS?

I have some projects that use RequireJS to load individual JavaScript modules in the browser, but I haven't optimized them yet. In both development and production, the app makes a separate request for each JavaScript file, and now I would like to fix that using Grunt.
I have tried to put together a simple project structure to no avail, so I'm wondering if someone can provide a working example for me. My goals are the following:
In development mode, everything works in the browser by issuing a separate request for each required module. No grunt tasks or concatenation are required in development mode.
When I'm ready, I can run a grunt task to optimize (combine) all of the JavaScript files using r.js and test that out locally. Once I'm convinced the optimized application runs correctly, I can deploy it.
Here's a sample structure for the sake of this conversation:
grunt-requirejs-example/
grunt.js
main.js (application entry point)
index.html (references main.js)
lib/ (stuff that main.js depends on)
a.js
b.js
requirejs/
require.js
text.js
build/ (optimized app goes here)
node_modules/ (necessary grunt tasks live here)
Specifically, I'm looking for a working project structure that I can start from. My main questions are:
If this project structure is flawed, what do you recommend?
What exactly needs to be in my grunt.js file, especially to get the r.js optimizer working?
If all of this isn't worth the work and there's a way to use the grunt watch task to automatically build everything in development mode every time I save a file, then I'm all ears. I want to avoid anything that slows down the loop from making a change to seeing it in the browser.
I use the grunt-contrib-requirejs task to build project based on require.js. Install it inside your project directory with:
npm install grunt-contrib-requirejs --save-dev
BTW: --save-dev will add the package to your development dependencies in your package.json. If you're not using a package.json in your project, ignore it.
Load the task in your grunt file with:
grunt.loadNpmTasks('grunt-contrib-requirejs');
And add the configuration to your grunt.initConfig
requirejs: {
production: {
options: {
baseUrl: "path/to/base",
mainConfigFile: "path/to/config.js",
out: "path/to/optimized.js"
}
}
}
Now you're able to build your require.js stuff into a single file that will be minimized with uglifyjs by running grunt requirejs
You can bundle a set of different tasks into some sort of main task, by adding this to your grunt file
grunt.registerTask('default', ['lint', 'requirejs']);
With this, you can simply type grunt and grunt will automatically run the default task with the two 'subtasks': lint and requirejs.
If you need a special production task: define it like the above
grunt.registerTask('production', ['lint', 'requirejs', 'less', 'copy']);
and run it with
grunt production
If you need different behaviors for 'production' and 'development' inside i.e. the requirejs task, you can use so called targets. In the configuration example above it's already defined as production. You can add another target if you need (BTW, you can define a global config for all targets by adding a options object on the same level)
requirejs: {
// global config
options: {
baseUrl: "path/to/base",
mainConfigFile: "path/to/config.js"
},
production: {
// overwrites the default config above
options: {
out: "path/to/production.js"
}
},
development: {
// overwrites the default config above
options: {
out: "path/to/development.js",
optimize: none // no minification
}
}
}
Now you can run them both at the same time with grunt requirejs or individually with grunt requirejs:production, or you define them in the different tasks with:
grunt.registerTask('production', ['lint', 'requirejs:production']);
grunt.registerTask('development', ['lint', 'requirejs:development']);
Now to answer your questions:
I would definitely use a subfolder in your project. In my case I use a 'src' folder for development that is build into a 'htdocs' folder for production. The project layout I prefere is:
project/
src/
js/
libs/
jquery.js
...
appname/
a.js
b.js
...
main.js // require.js starter
index.html
...
build/
... //some tmp folder for the build process
htdocs/
... // production build
node_modules/
...
.gitignore
grunt.js
package.json
see above
You can do so, but I wouldn't recommend to add requirejs to the watch task, it's a resource hungry task and it will slow down your machine noticeable.
Last but not least: Be very cautious when playing around with r.js. Especially when you want to optimize the whole project with r.js by adding a modules directive to your config. R.js will delete the output directory without asking. If it happens that it is accidentally configured to be your system root, r.js will erase your HDD. Be warned, I erased my whole htdocs folder permanently some time ago while setting up my grunt task... Always add keepBuildDir:true to your options when playing around with the r.js config.

Categories