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

TODO: chunks and codesplitting #48

Open
nathanjhood opened this issue Oct 8, 2024 · 0 comments
Open

TODO: chunks and codesplitting #48

nathanjhood opened this issue Oct 8, 2024 · 0 comments

Comments

@nathanjhood
Copy link
Owner

nathanjhood commented Oct 8, 2024

Currently:

// config/esbuild.config.ts
export function configFactory(/** ... */) {
  return {
    // ...
    entryNames: isEnvProduction
      ? "static/[ext]/[name].[hash]"
      : isEnvDevelopment && "static/[ext]/bundle",
    // There are also additional JS chunk files if you use code splitting.
    chunkNames: isEnvProduction
      ? "static/[ext]/[name].[hash].chunk"
      : isEnvDevelopment && "static/[ext]/[name].chunk",
    assetNames: "static/media/[name].[hash][ext]",
    // ...
  }
}

This codeblock works beautifully, directly from the react-scripts webpack config into the esbuild one.

Unfortunately though, esbuild doesn't have the standard means of replacing parsed HTML with expanded variables...

... fortunately, I made my own. Adapted from the webpack plugin inside of react-scripts. And it works great!

The below variables containing %PUBLIC_URL% - and any other from the consumer project's .env files - are now being correctly interpolated:

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
  <!-- ... -->
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
  <title>React App</title>
</head>
<!-- ... -->
</html>

The trick next is to figure out how to get the hashes from build output files (and/or metafile) and inject those as well...

Requirement:

The un-processed HTML should look like the below, with the build output's index.html having the appropriate variables - [name] and [hash] and so forth - replaced with those found in the build config (from the 1st codeblock):

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
  <!-- ... -->
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
  <link rel="stylesheet" href="%PUBLIC_URL%/static/css/[name].[hash]" />
  <title>React App</title>
  <script type="module" href="%PUBLIC_URL%/static/js/[name].[hash]" />
</head>
<!-- ... -->
</html>

Caveat:

Currently, I'm doing the HTML file build in a separate step, between copyPublicFolder (which runs first), and esbuild itself.

In order to get the variables that we need to inject into the HTML, we obviously need to run the HTML step after, not before, esbuild finishes each build run.

Thanks to the promise-chaining, there is some scope for placing that function call in exactly the place it needs to happen.

IMPORTANT:

It's worth noting that some implementations out there don't have any stylesheet-importing HTML tag in the template HTML file; it appears that the stylesheet-import gets injected into the HTML on every build run.

Probably a security thing, of course.

Additionally, we must never read and write to the same location at the same time; in fact, I'm going to scrap the "async CLI" idea because it's just bad. With that in mind, a lockfile (just an empty textfile placed at the root of dist) could be useful during build, as a kind of atomic flag; if a process is "holding" that lockfile, then it signals abort() to any other processes that attempt to alter the containing directory - dist - while that lockfile is held.

As long as the end-user isn't hitting build from multiple terminals consecutively (which is on them, not me), then there should be no need for the lockfile...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant