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

asset-manifest.json generated by CRA2 is not useful #5513

Closed
pthorn opened this issue Oct 21, 2018 · 16 comments · Fixed by #7721
Closed

asset-manifest.json generated by CRA2 is not useful #5513

pthorn opened this issue Oct 21, 2018 · 16 comments · Fixed by #7721
Assignees
Milestone

Comments

@pthorn
Copy link

pthorn commented Oct 21, 2018

As mentioned in the discussion in #5306, asset-manifest.json generated by CRA2 is less than useful. As an example, this is asset-manifest.json as generated by yarn build for my project:

{
   "main.css": "/static/css/main.7263860d.chunk.css",
   "main.js": "/static/js/main.1d09f064.chunk.js",
   "main.js.map": "/static/js/main.1d09f064.chunk.js.map",
   "static/css/1.a7cf09a2.chunk.css": "/static/css/1.a7cf09a2.chunk.css",
   "static/js/1.f5aeaa31.chunk.js": "/static/js/1.f5aeaa31.chunk.js",
   "static/js/1.f5aeaa31.chunk.js.map": "/static/js/1.f5aeaa31.chunk.js.map",
   "runtime~main.js": "/static/js/runtime~main.229c360f.js",
   "runtime~main.js.map": "/static/js/runtime~main.229c360f.js.map",
   "static/css/1.a7cf09a2.chunk.css.map": "/static/css/1.a7cf09a2.chunk.css.map",
   "static/css/main.7263860d.chunk.css.map": "/static/css/main.7263860d.chunk.css.map",
   "index.html": "/index.html",
   "precache-manifest.981d9ec8b3a8232e0184126d691edb97.js": "/precache-manifest.981d9ec8b3a8232e0184126d691edb97.js",
   "service-worker.js": "/service-worker.js"
 }

It was quite an unpleasant surprise when I upgraded to CRA2. Here are some obvious problems with this file:

  • it contains no information on which files are chunks of the same package
  • there is no way to identify chunks (except the 'main' chunk) by key (random strings in keys? seriously?)
  • there is absolutely no information on the order in which chunks are to be loaded

It can of course still be parsed by pattern matching file names but there are obvious disadvantages:

  • any such parsing will rely on heuristics
  • parsing will break if file naming scheme is ever changed
  • what is the point of having this file at all? you could listdir() the build directory and obtain basically the same information

Here is a suggested asset-manifest.json format that could make it usable:

{
   "main.js": [
       "/static/js/1.f5aeaa31.chunk.js",  // <-- order is significant
       "/static/js/main.1d09f064.chunk.js",
   ],
   "main.js.map": [
       "static/js/1.f5aeaa31.chunk.js.map",   // <-- same order as in "main.js"
       "/static/js/main.1d09f064.chunk.js.map",
    ]
   "runtime~main.js": "/static/js/runtime~main.229c360f.js",
   "runtime~main.js.map": "/static/js/runtime~main.229c360f.js.map",
   "precache-manifest.js": "/precache-manifest.981d9ec8b3a8232e0184126d691edb97.js",
   "service-worker.js": "/service-worker.js"
   "main.css": [
       "/static/css/1.a7cf09a2.chunk.css"
       "/static/css/main.7263860d.chunk.css"
    ],
   "main.css.map": [
       "/static/css/1.a7cf09a2.chunk.css.map"
       "/static/css/main.7263860d.chunk.css.map"
    ],
 }
@gaearon
Copy link
Contributor

gaearon commented Oct 21, 2018

I think we should just change it to only include "initial" scripts in the order they should be in HTML. Since the only purpose of this file is to reconstruct the HTML.

Does that make sense?

@Timer Timer added this to the 2.x milestone Oct 21, 2018
@yordis
Copy link

yordis commented Oct 22, 2018

Definitely, it will be really nice to create a more descriptive manifest file.

I would also group them by type since normally you would group them by type and then try to inject them in order, a better data structure thinking on deployment will be nice to have.

Since the only purpose of this file is to reconstruct the HTML.

Btw, we live inside Rails app right now, so a more descriptive file would be nice for us to deploy

@jbach
Copy link

jbach commented Oct 22, 2018

See also #5225

@ryancogswell
Copy link
Contributor

Having the asset-manifest only include the "initial" scripts would meet our needs. We have two main ways we use our react app: whole-page and partial-page. The "whole-page" use case can just use the index.html from the build (with some dynamic replacements), but in the "partial-page" case (typically a button or icon that opens a modal dialog rendered via the react app) we don't use the index.html at all and instead leverage the asset-manifest to pull in the main js and css files. Now with CRA 2, the index.html is also including a "15..chunk.js" and of course I have no guarantee that this main vendor chunk will still begin with "15" on my next build.

Another possible (and much quicker to implement) way to address this would be to support a boolean environment variable such as process.env.ASYNC_CHUNKS_ONLY to control setting the chunks value in
optimization: { splitChunks: { chunks: 'all'} }
to be "async" instead of "all". This avoids having a separate vendor chunk referenced in index.html (it gets baked into main.js like before). I don't have much of an understanding for the reasons for CRA defaulting to chunks: 'all', but I'd be willing to do a pull request for this alternative approach if it is considered worthwhile.

@ironsweet
Copy link

I see this proposal is removed from 2.x milestone. What's the plan for now? @Timer

@Zemelia
Copy link

Zemelia commented Nov 29, 2018

See #5225 (comment)

@jovinbm
Copy link

jovinbm commented Dec 8, 2018

As a hack, I created the following script to give me all the css and js paths in their correct order. Note that I needed multiple entry points so I have ejected. If you have not, then the entry_key can just be index.

note

  • this script relies on the presence on the html file for each of your entry points. If you have not ejected, then just use index as your entry_key and all will be well. If you have, make sure the webpack configs creates a html file for each of your entry points by just adding more blocks of HtmlWebpackPlugin in your webpack configs for each of your entry points.
  • it uses fs.readFile and regular expressions to parse for the links. I have not analyzed its performance so if you are expecting many pageviews. you might have to reconsider (or run once and cache the results).

code: (written in typescript)

import fs from 'fs';
import path from 'path';
import assetManifest from '<path/to/asset-manifest.json>';

const build_directory = '<path/to/create-react-app/build/directory>';

export const getAssetPathFromCRAHtml = async (
  entry_key: string // something like `admin`, `pages-manage`, `index`. If you have not ejected, then this will just be `index`
): Promise<{
  css_assets: string[];
  js_assets: string[];
}> => {
  const css_assets: string[] = [];
  const js_assets: string[] = [];
  const html_string = await new Promise<string>((resolve, reject) => {
    fs.readFile(
      path.join(build_directory, `${entry_key}.html`),
      (error, data) => {
        if (error) {
          reject(error);
        } else {
          resolve(data.toString('utf8'));
        }
      }
    );
  });
  let css_matches;
  const cssRe = /<link\shref="(\/static\/css\/.+?)"\srel="stylesheet"/g;
  do {
    css_matches = cssRe.exec(html_string);
    if (css_matches) {
      css_assets.push(css_matches[1]);
    }
  } while (css_matches);

  let js_matches;
  const jsRe = /<script\ssrc="(\/static\/js\/.+?)"><\/script>/g;
  do {
    js_matches = jsRe.exec(html_string);
    if (js_matches) {
      js_assets.push(js_matches[1]);
    }
  } while (js_matches);

  const js_runtime = (assetManifest as {
    [key: string]: string;
  })[`runtime~${entry_key}.js`];

  if (js_runtime) {
    js_assets.unshift(js_runtime);
  }
  return {
    css_assets,
    js_assets
  };
};

@ivarne
Copy link

ivarne commented Jan 12, 2019

Sad to see #5955 and similar pull requests and issues being closed by stale bot. Is parsing the generated index.html file the only option forward when including CRA components on a site with a backend?

@mareksuscak
Copy link
Contributor

I'm not sure what are the plans for this feature but what if we swapped out the webpack plugin for this one. Starting with version 3 it appends the entrypoints section.

@iansu
Copy link
Contributor

iansu commented Apr 25, 2019

We are hoping to add an entrypoints key to the asset manifest. That's why we moved the list of files under its own key in 3.0. That plugin looks like it could be a good way to do that. Thanks.

@zastavnitskiy
Copy link

zastavnitskiy commented May 1, 2019

I evaluated Webpack Manifest Plugin and webpack-assets-manifest to understand if it's possible to generate asset manifest that will allow users to reconstruct html.

Both allow customization of generated manifest, either via generate function of webpack-manifest-plugin, or via tranform or customize hooks in case of webpack-assets-manifest.

However, the data exposed in customisation hooks lacks either the information about the order of the chunks, or whether the chunks needs to be included on the page or are asynchronous.

CRA generates index.html using html-webpack-plugin, and that one taps into the emit hook and then builds the list of files for the given entrypoint: by calling compilation.entrypoints.get(entryName).getFiles()

In order to allow users build their custom html based on manifest, we will have to follow similar approach.

@iansu You mentioned adding entrypoints key to the manifest — does it imply that CRA is planning to support multiple entrypoints? Or we can assume a single index.html for now?

Does anyone work on this story already?

@jbach
Copy link

jbach commented May 1, 2019

@zastavnitskiy I think #5955 show how you to reconstruct the HTML?

@zastavnitskiy
Copy link

@jbach, thank you, it does. Do you know why that MR was ignored and eventually closed?

On the other side, at #5513 (comment), it seems that the v3.x direction is to extend existing asset-manifest instead of adding a new one.

@jbach
Copy link

jbach commented May 1, 2019

As far as I understand, enabling to extend the asset-manifest eventually allows adding the content of html-manifest to that singular manifest.

@mdecorte
Copy link

mdecorte commented May 2, 2019

I've created a small script (gist) that you can run in your pipeline or with npm run build && node ./[scriptFileName].js.

It generates something like this, containing only the paths that you need in your html in the correct order.

// build/useful-asset-manifest.json

{
  "css": [
    "/static/css/2.1b02c459.chunk.css",
    "/static/css/main.f8da7d89.chunk.css"
  ],
  "js": [
    "/static/js/runtime~main.3e5a2071.js",
    "/static/js/2.5717aa4d.chunk.js",
    "/static/js/main.8363db78.chunk.js"
  ]
}

Which your backend can then use to generate the link and script tags.

I'm looking forward to a proper v3.x implementation by CRA though :)

@johot
Copy link

johot commented Jun 4, 2019

We also just got this problem when trying to add React components to an existing ASP.NET MVC page.

We solved it similar to @mdecorte. I created a node script that uses node-html-parser to parse the index.html file. It then creates a new json file that we can parse in the backend of our ASP.NET MVC site and basically recreate the index.html file CRA built.

I definitely think there should be an option of building a CRA React app optimized for embedding in existing pages, must be a pretty common scenario when slowly migrating to React.

@bugzpodder bugzpodder changed the title asset-manifest.json generated by CRA2 is pretty useless asset-manifest.json generated by CRA2 is not useful Jun 19, 2019
@lock lock bot locked and limited conversation to collaborators Sep 29, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.