nickjj / flask-static-digest

Flask extension to help make your static files production ready by md5 tagging and gzipping them.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Flask Static Digest + Create React App

ivadenis opened this issue · comments

Hi,
I'm trying to configure Create React App to work with Flask and stumbled upon this package.

Can you give any hints on how to configure this plugin to consume already compiled manifest? Or maybe you can recommend another approach.

Thanks!

The result of npm build command of CRA looks like this

├── asset-manifest.json
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
├── precache-manifest.b8aaeab7f259072963f46661b376837a.js
├── robots.txt
├── service-worker.js
└── static
    ├── css
    │   ├── main.87c0ef5b.chunk.css
    │   └── main.87c0ef5b.chunk.css.map
    ├── js
    │   ├── 2.856bb494.chunk.js
    │   ├── 2.856bb494.chunk.js.LICENSE.txt
    │   ├── 2.856bb494.chunk.js.map
    │   ├── main.f1dc1e54.chunk.js
    │   ├── main.f1dc1e54.chunk.js.map
    │   ├── runtime-main.a026a1da.js
    │   └── runtime-main.a026a1da.js.map
    └── media
        └── logo.8dad8028.svg

and asset-manifest.json looks like this

{
  "files": {
    "main.css": "/static/css/main.87c0ef5b.chunk.css",
    "main.js": "/static/js/main.f1dc1e54.chunk.js",
    "main.js.map": "/static/js/main.f1dc1e54.chunk.js.map",
    "runtime-main.js": "/static/js/runtime-main.a026a1da.js",
    "runtime-main.js.map": "/static/js/runtime-main.a026a1da.js.map",
    "static/js/2.856bb494.chunk.js": "/static/js/2.856bb494.chunk.js",
    "static/js/2.856bb494.chunk.js.map": "/static/js/2.856bb494.chunk.js.map",
    "index.html": "/index.html",
    "precache-manifest.b8aaeab7f259072963f46661b376837a.js": "/precache-manifest.b8aaeab7f259072963f46661b376837a.js",
    "service-worker.js": "/service-worker.js",
    "static/css/main.87c0ef5b.chunk.css.map": "/static/css/main.87c0ef5b.chunk.css.map",
    "static/js/2.856bb494.chunk.js.LICENSE.txt": "/static/js/2.856bb494.chunk.js.LICENSE.txt",
    "static/media/logo.svg": "/static/media/logo.8dad8028.svg"
  },
  "entrypoints": [
    "static/js/runtime-main.a026a1da.js",
    "static/js/2.856bb494.chunk.js",
    "static/css/main.87c0ef5b.chunk.css",
    "static/js/main.f1dc1e54.chunk.js"
  ]
}

Hi,

What if you modified that starter app to not create a manifest and then used this extension to handle digesting the files and making the manifest?

Creating the manifest with your JS tool of choice is something I used to do 5 years ago but that doesn't work well in practice because you still need to do things on the Flask side to use it. It also limits you to only ever being able to digest files while using a specific JS tool.

But if you're dead set on using your tool to make the manifest, you may want to fork my old Flask-Webpack extension https://github.com/nickjj/flask-webpack instead of using this extension.

Hi Nick,
Thanks for the reply! The Create React App is probably the industry standard starter kit for React apps these days. The beauty of it is that it handles the configuration out of the box. This simplifies updates and maintenance of the codebase. Ejecting webpack configs is a very last resort that is highly not recommended and I was trying to avoid.

I had thought if your plugin would allow specifying the name of a manifest file, then it could pick up the manifest generated by CRA.

You can still use the create react app example app and webpack. You would just end up using this extension to generate the manifest instead of webpack. That would require changing a few lines of code in their webpack config and everything else works the same.

Manifest file creation really doesn't belong at the webpack level because the framework that plans to use the manifest needs to understand how to read it. Suddenly you need to support manifest files being generated from a number of build tools and even specific plugins. I know because I wrote a webpack manifest plugin ~5 years ago and Flask-Webpack to go along with it.

This is why Ruby on Rails, Django, Phoenix and other frameworks all deal with creating their own manifest file (which doesn't require modifying or even using Webpack to make it work), and this extension brings that capability to Flask. It works out of box for everything.

I see your point. The problem with CRA is that you can't just turn off one plugin, one would have to take over the whole config management...

I will close this issue. Thank you for your answers!

P.S.: it would be very awesome if there was an example of CRA + Flask Static Digest.

Technically you could just run this extension's digest against the React app's static/ directory without having to modify anything on the react webpack config file. It would mean you would end up with double digested files but it should work and has no real negative side effects.

And if you wanted to get rid of the double digest, I never used React but chances are it would involve modifying this file: https://github.com/facebook/create-react-app/blob/e89f153224cabd67efb0175103244e0b7f702767/packages/react-scripts/config/webpack.config.js

By removing this plugin: https://github.com/facebook/create-react-app/blob/e89f153224cabd67efb0175103244e0b7f702767/packages/react-scripts/config/webpack.config.js#L658

And then changing any references to [name].[contenthash:8].chunk to be [name] and [name].chunk to [name].

I was just faced with the same problem and here's how I solved it for me without having to eject from create-react-app:

Step 1: Install https://github.com/timarney/react-app-rewired

Step 2: Add this to your config-overrides.js:

module.exports = function override(config, env) {
  // Remove content hashes from js files (let flask-static-digest handle those).
  config.output.filename = config.output.filename.replace('[contenthash:8].', '');
  config.output.chunkFilename = config.output.chunkFilename.replace('[contenthash:8].', '');

  // Remove unneeded plugins.
  const removeThesePlugins = [
    'ManifestPlugin',
  ]
  config.plugins = config.plugins.filter( (plugin) => !removeThesePlugins.includes(plugin.constructor.name))

  // Reconfigure specific plugins.
  for (let plugin of config.plugins) {
    // Remove content hashes from css files.
    if (plugin.constructor.name === 'MiniCssExtractPlugin') {
      plugin.options.filename = plugin.options.filename.replace('[contenthash:8].', '');
      plugin.options.chunkFilename = plugin.options.chunkFilename.replace('[contenthash:8].', '');
    }
  }

  // Create a single entry bundle.
  config.optimization.splitChunks.chunks = 'async';

  return config;
}

Step 3: You can now load the generated main.chunk.js, runtime-main.js and main.chunk.css using static_url_for e.g. in your layout template.

Thanks for the snippet.