Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate build without hashed file names. #821

Closed
lgomez opened this issue Oct 2, 2016 · 24 comments
Closed

Generate build without hashed file names. #821

lgomez opened this issue Oct 2, 2016 · 24 comments

Comments

@lgomez
Copy link

lgomez commented Oct 2, 2016

Hi all,

I'm trying this project out to simplify work on a web app that's to be embedded into a hardware device. For reasons I have no control over, the "embed" team is requesting I commit builds without hashed names. Is there a way to disable hashed names completely for the build without having to eject? This is the only thing I need to override and ejecting for that one reason seems excessive.

In case it matters, I also don't need to distribute sourcemap files in the build.

I'm happy to contribute to the docs if there is a way to do this.

Thank you,

@gaearon
Copy link
Contributor

gaearon commented Oct 2, 2016

This is not supported and it’s unlikely we would support it. Generating hashes is the right default for a vast majority of cases. If for some reason you’d rather not have them generated, you would need to maintain your own Webpack config.

As an alternative to ejecting, you can fork react-scripts: #779.

@gaearon gaearon closed this as completed Oct 2, 2016
@lgomez
Copy link
Author

lgomez commented Oct 2, 2016

Thank you. I wasn't expecting you to add it. Just wanted to know if perhaps it was supported.

Thanks for the suggestions. I'll look into it.

@jrzerr
Copy link

jrzerr commented Jun 7, 2017

A different way to handle this is to do a rename as a part of your build process with a new npm run script so you don't have to do an eject. I used the renamer npm library to do this for me.

npm install --save-dev renamer

Then in package.json scripts section I added some rename helpers:

    "build-rename": "npm run build-rename-js && npm run build-rename-css",
    "build-rename-js": "renamer --regex --find 'main\\.[^\\.]+\\.js' --replace 'main.js' build/static/js/*.js",
    "build-rename-css": "renamer --regex --find 'main\\.[^\\.]+\\.css' --replace 'main.css' build/static/css/*.css",

then you can modify your build script to do the rename post-build like this:

"build": "react-scripts build && npm run build-rename",

Anyways, this is just one way to do it. You will give up source maps unless you do a search and replace inside the js file, but in your specific case of putting on a mobile device that probably doesn't matter.

@gaearon
Copy link
Contributor

gaearon commented Jun 7, 2017

Note that this will break code splitting (i.e import('./module').then(...)) because the hardcoded JS path in the bundle will be wrong.

@ghost
Copy link

ghost commented Sep 13, 2017

Having changing hashes makes working in Visual Studio projects a bit annoying as you can't just update the files, you have to add the new ones and delete the old ones from the project file.

Previously I dealt with cache busting on the chunk files like this in my webpack.config:

    output: {
      filename: 'main.js',
      chunkFilename: 'main.[id].js?v=[hash]',
    },

Then in my HTML template I would add a cache busting query string like so:
<script src="main.js?v<%=LastModified('main.js') %>"></script>

So adding an option to CRA to support something like this seems like a reasonably common use case.

@StefanSchoof
Copy link

Based on @jrzerr workaround, I added the replace inside the js and css file to get source maps working. For the change of the sourcemap url I used replace. I use this on a windows machine:

"scripts": {
    ...
    "build": "react-scripts-ts build&&npm run build-rename",
    "build-rename": "npm run build-rename-js&&npm run build-rename-css&&npm run build-fix-sourcemap",
    "build-rename-js": "renamer --regex --find \"main\\.[^\\.]+\\.\" --replace \"main.\" build\\static\\js\\*",
    "build-rename-css": "renamer --regex --find \"main\\.[^\\.]+\\.\" --replace \"main.\" build\\static\\css\\*",
    "build-fix-sourcemap": "npm run build-fix-sourcemap-js&&npm run build-fix-sourcemap-css",
    "build-fix-sourcemap-js": "replace \"# sourceMappingURL=main..*.js.map\" \"# sourceMappingURL=main.js.map\" build\\static\\js\\main.js",
    "build-fix-sourcemap-css": "replace \"# sourceMappingURL=main..*.map\" \"# sourceMappingURL=main.css.map\" build\\static\\css\\main.css"
  },

@marcoc1712
Copy link

@StefanSchoof 👍
It works, but you should replace css and js also in index.html and service-worker.js in the main directory.

@StefanSchoof
Copy link

@marcoc1712 Thanks for pointing this out. In my setup I embed js in a bigger site and have no need for these files.

@olgeorge
Copy link

@gaearon what's your suggestion on using hashed file names together with cached CDNs?

I'm using S3 to store the app and CloudFront as a CDN. When I deploy a new version, the files with old hashes are removed and new files are added. However, since CloudFront caches index.html, it points to old non-existent files until it refreshes the cache. Thus, I'm breaking the website with each build, and even if I manually refresh the cache, the website still might be down for some time until the cache is refreshed. I could store all prior versions on S3 instead of deleting them, but that doesn't seem like a good long-term solution either.

@Meemaw
Copy link

Meemaw commented Mar 20, 2018

@olgeorge did you solve this?

@aftabnaveed
Copy link

I am using CRA for server side rendering along with Airbnb Hypernova. Because my backend server is not in NodeJS and I need to point to the css file from my backend APP. It is very annoying to keep updating the css paths, therefore, file without hashes should be supported.

@Sofianio
Copy link

Sofianio commented Mar 26, 2018

Here's what i did:
in the file index.html
I get the bundle name from asset-manifest.json
then add the script with the bundle name

<script type="text/javascript">
        var bundle_name = "bundle.js"; // dev mode
        $.getJSON("/asset-manifest.json", function(json){ var bundle_name = json["main.js"]; })
        .always(function(){
            var bundle = document.createElement('script');
            bundle.setAttribute('type', "text/javascript");
            bundle.setAttribute('src', "/static/js/" + bundle_name);
            document.body.appendChild(bundle);
        });
</script>

@lisandrolan
Copy link

@Sofianio that solution helped you? I couldnt do that work! :(

@aftabnaveed
Copy link

I came up with a shell script which I execute instead of just yarn build

Place it in your site root to make it work. In my case the destination is ../../../public/js/main.js you may change it your desired destination.

#!/bin/bash
yarn build
cp -v build/static/js/main.*.js ../../../public/js/main.js
cp -v build/static/css/main.*.css ../../../public/css/main.css

@Sofianio
Copy link

Sofianio commented Apr 10, 2018

@lisandrolan yes it worked for me.

@aftabnaveed your solution is good but it has a downside, if you make an update, the client might not receive it because of browser cache (if file name didn't change ... the browser uses cache)
... there's a reason why they add random numbers to the file name

@chunhui2001
Copy link

chunhui2001 commented Apr 27, 2018

$ yarn build

/# replace hash contents
/# get hash definition in build/asset-manifest.json and replace hashed contents using awk and sed
$ cat build/asset-manifest.json | grep -o -E "\.[0-9a-z]{8}\." |
awk '{system("find ./build/static -exec sed -i \"\" -E \"s/\\" $0 "/./g\" {} \\; 2>/dev/null")}'

/# rename hashed files inside build/static
$ find ./build/static -type f | grep -E "\.[0-9a-z]{8}\." | rename "s/.[a-z0-9]{8}././g"

DONE

@taggartj
Copy link

taggartj commented Jul 3, 2018

"build": "react-scripts build && yarn run build-dist",
"build-dist": "cd build && cp static/js/.js main.js && cp static/css/.css main.css"

@Kopatch
Copy link

Kopatch commented Jul 4, 2018

@taggartj your solution looks great.
Just want to add to solution - for Windows users use
"build-dist": "cd build && copy static\\js\\*.js main.js && copy static\\css\\*.css main.css"

@jamstooks
Copy link

jamstooks commented Jul 24, 2018

Because someone might find this useful...

I opted to do this in the application layer at startup so that I'm not affecting load time. I'm using django, but this could apply to anyone using another application layer (like rails) to serve their react app. I simply added the following to my settings.py:

REACT_BUILD_DIR = os.path.join(BASE_DIR, 'frontend', 'build')

# Pull the js and css filenames from the current build
path = os.path.join(REACT_BUILD_DIR, "asset-manifest.json")
with open(path) as f:
    data = json.load(f)

REACT_CSS_PATH = data['main.css'].replace('static/', '')
REACT_JS_PATH = data['main.js'].replace('static/', '')

I then simply pass REACT_CSS_PATH and REACT_JS_PATH to the template that renders the react app.

@jubalm
Copy link

jubalm commented Aug 27, 2018

In case someone finds this helpful...

Built a component mycomponent that is completely detached from the main application, even styled on it's own with emotionjs so no CSS, so I just created a file postbuild.sh and set it's permission chmod +x postbuild.sh

#!/bin/sh

# copy files | https://tosbourn.com/copy-rename-multiple-files-linux/
for i in ./build/static/js/main.*; do cp $i `echo $i | sed "s/main.*.js/mycomponent.js/g"`; done

# update sourcemap | sed osx backup - https://stackoverflow.com/a/25486705/1072776 
sed -i "" -e "s/sourceMappingURL=main.*.js/sourceMappingURL=mycomponent.js/g" 
./build/static/js/mycomponent.js

Now to fire it automatically after build, i add this to package.json

{
  "scripts": {
    // ... create react app stuff
    "postbuild": "./postbuild.sh"
  }
}

@josefbetancourt
Copy link

josefbetancourt commented Sep 2, 2018

I accomplished this with a Groovy language script. But, I wonder if react-app-rewired could be used to accomplish this rename and fix of map references.

@essar05
Copy link

essar05 commented Sep 12, 2018

As some of the solutions above were a bit outdated and incomplete, I came up with these scripts for package.json in case anyone finds them useful:

{
    "build": "react-scripts build && npm run build-rename",
    "build-rename": "npm run build-rename-js && npm run build-rename-css && npm run build-fix-references",
    "build-rename-js": "renamer --find \"/main\\.[^\\.]+\\./i\" --replace \"main.\" build\\static\\js\\*",
    "build-rename-css": "renamer --find \"/main\\.[^\\.]+\\./i\" --replace \"main.\" build\\static\\css\\*",
    "build-fix-references": "npm run build-fix-sourcemap && npm run build-fix-index && npm run build-fix-serviceworker && npm run build-fix-assetmanifest",
    "build-fix-sourcemap": "npm run build-fix-sourcemap-js && npm run build-fix-sourcemap-css",
    "build-fix-sourcemap-js": "replace-in-file \"/sourceMappingURL=main\\.[^\\.]+\\.js\\.map/i\" \"sourceMappingURL=main.js.map\" build/static/js/main.js --isRegex",
    "build-fix-sourcemap-css": "replace-in-file \"/sourceMappingURL=main\\.[^\\.]+\\.css\\.map/i\" \"sourceMappingURL=main.css.map\" build/static/css/main.css --isRegex",
    "build-fix-index": "npm run build-fix-index-js && npm run build-fix-index-css",
    "build-fix-index-js": "replace-in-file \"/main\\.[^\\.]+\\.js/i\" \"main.js?v=%npm_package_version%\" build/index.html --isRegex",
    "build-fix-index-css": "replace-in-file \"/main\\.[^\\.]+\\.css/i\" \"main.css?v=%npm_package_version%\" build/index.html --isRegex",
    "build-fix-serviceworker": "npm run build-fix-serviceworker-js && npm run build-fix-serviceworker-css",
    "build-fix-serviceworker-js": "replace-in-file \"/main\\.[^\\.]+\\.js/i\" \"main.js\" build/service-worker.js --isRegex",
    "build-fix-serviceworker-css": "replace-in-file \"/main\\.[^\\.]+\\.css/i\" \"main.css\" build/service-worker.js --isRegex",
    "build-fix-assetmanifest": "npm run build-fix-assetmanifest-js && npm run build-fix-assetmanifest-css && npm run build-fix-assetmanifest-js-map && npm run build-fix-assetmanifest-css-map",
    "build-fix-assetmanifest-js": "replace-in-file \"/main\\.[^\\.]+\\.js/i\" \"main.js\" build/asset-manifest.json --isRegex",
    "build-fix-assetmanifest-css": "replace-in-file \"/main\\.[^\\.]+\\.css/i\" \"main.css\" build/asset-manifest.json --isRegex",
    "build-fix-assetmanifest-js-map": "replace-in-file \"/main\\.[^\\.]+\\.js\\.map/i\" \"main.js.map\" build/asset-manifest.json --isRegex",
    "build-fix-assetmanifest-css-map": "replace-in-file \"/main\\.[^\\.]+\\.css\\.map/i\" \"main.css.map\" build/asset-manifest.json --isRegex"
}

Also, don't forget to run

npm i --save-dev renamer
npm i --save-dev replace-in-file

@reactiongears
Copy link

@raizenpk would one need to run all those commands to have a clean build?

@bologer
Copy link

bologer commented Nov 17, 2018

I work on the plugin which allows user to change styles of the component. Meaning that they can choose what was build with react-create-app or change styles of SCSS file and rebuild new CSS so here is what I came-up with (maybe will be useful for someone):

$staticFolder = '/path/to/static/media/';
$assets       = $staticFolder . '*.*';

$fileAssetList = glob( $assets );

if ( ! empty( $fileAssetList ) ) {
	foreach ( $fileAssetList as $key => $assetFullPath ) {
		preg_match( '/\/media\/(.*)\.[a-z0-9]+\.(svg|png|jpg|jpeg|ico|gif)$/m', $assetFullPath, $matches );

		if ( count( $matches ) !== 3 ) {
			continue;
		}

		$fullMatchAndUrl = '/path/to/static' . $matches[0];
		$fileName        = $matches[1];
		$extension       = $matches[2];

		$pattern = "/\.\.\/img\/?([\w-_]*\/)$fileName\.$extension/m";

		if ( preg_match( $pattern, $compiled ) ) {
			$compiled = preg_replace( $pattern, $fullMatchAndUrl, $compiled );
		}
	}
}

@lock lock bot locked and limited conversation to collaborators Jan 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests