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

Switch to Vite #319

Merged
merged 86 commits into from
Feb 17, 2024
Merged

Switch to Vite #319

merged 86 commits into from
Feb 17, 2024

Conversation

silviogutierrez
Copy link
Owner

No description provided.

@silviogutierrez
Copy link
Owner Author

@crgwbr : I solved this on joyapp.com by switching the subdomain for static assets. A wildcard subdomain has the release hash in it.

I've always hated all these mechanisms, but understand you need one. I'll think of something, maybe give you an env var.

But I'm curious how you solved it with the prior system / white noise.

Can you post the relevant pseudo code or Python/React areas on esbuild reactivated?

Note: you can already just change STATIC_URL folder to something else per release, say static/<release hash>/dist/ and that'll get prepended to the auto injected file.

For the record, my least favorite part of this conversion is having to detect and inject assets separately between development and production. I like things being explicit. But Vite's ESM module handling requires it.

@crgwbr
Copy link
Contributor

crgwbr commented Feb 15, 2024

But I'm curious how you solved it with the prior system / white noise.
Can you post the relevant pseudo code or Python/React areas on esbuild reactivated?

  1. manage.py build builds the files to static/dist/
  2. Whitenoise builds hashed versions of the files during the collectstatic step.
  3. A template context processor called django.templatetags.static.static to inject hashed file URLs into the context.
  4. The React template then rendered the file urls from context.
class CoreAssets(NamedTuple):
    favicon: str
    css: List[str]
    js: List[str]


class CoreAssetProcessor(NamedTuple):
    core_assets: CoreAssets


def core_assets(request: HttpRequest) -> CoreAssetProcessor:
    """
    Add static-related context variables to the context.
    """
    return {
        "core_assets": {
            "favicon": static("favicon.ico"),
            "css": [
                static("dist/index.css"),
            ],
            "js": [
                static("dist/index.js"),
                static("js/footer.js"),
            ],
        }
    }
export const PageSkeleton = (props: {
    children?: React.ReactNode;
}) => {
    const context = React.useContext(Context);
    return (
        <>
            <Helmet>
                <link
                    rel="icon"
                    sizes="16x16 32x32 64x64"
                    href={context.core_assets.favicon}
                    type="image/x-icon"
                />
                {context.core_assets.css.map((file) => (
                    <link
                        key={file}
                        rel="stylesheet"
                        type="text/css"
                        href={file}
                    />
                ))}
                {context.core_assets.js.map((file) => (
                    <script
                        key={file}
                        defer
                        crossOrigin="anonymous"
                        src={file}
                    />
                ))}
          </Helmet>
          {props.children}
      </>
    );
};

@silviogutierrez
Copy link
Owner Author

@crgwbr that helps.

So if you're doing this in the build process, and presumably you know your release version (say the commit hash), what's stopping you from settings.py in production having something like STATIC_URL = f"static/{os.environ["RELEASE_VERSION"]}/dist/ and dumping your files in there?

Then on your docker file (pseudocode):

ARG RELEASE_VERSION
ENV RELEASE_VERSION=$RELEASE_VERSION
COPY collected static/$RELEASE_VERSION

I could add an ENV variable of some sort, but you'll still need to inject it in docker so your build process changes too.

@crgwbr
Copy link
Contributor

crgwbr commented Feb 15, 2024

Ok, I can make something like that work. 👍

@crgwbr
Copy link
Contributor

crgwbr commented Feb 15, 2024

Vite static path prefix?

I think this is my last pending issue here. Manually setting base: "/static/dist/", in my project's vite.config.ts fixes it. I see there's similar code to do that in vite.mts, but something there must not be working quite as expected.

@silviogutierrez
Copy link
Owner Author

What's the need or use case though? I'm using it only in development in vite.mts so vite doesn't intercept everything, only a few static assets (client/index.tsx). How is it relevant or used in your world?

During production, vite doesn't serve anything at all, only STATIC_URL comes into play.

@crgwbr
Copy link
Contributor

crgwbr commented Feb 15, 2024

Vite always needs to know the static URL so that it knows how to link to it's own assets. Like in my example here: #319 (comment)

A CSS file includes a reference another file (like a font or image):

@font-face {
    font-family: NunitoSans-Regular;
    src: url("../../../static/fonts/NunitoSans-Regular.woff2")
}

During the build process, Vite traverses that path to find the file, and saves is in the dist folder. So now you have:

static/
  - dist/
    - index.js
    - index.css
    - NunitoSans-Regular.woff2

But currently, static/dist/index.css will look like this:

@font-face {
    font-family: NunitoSans-Regular;
    src: url("NunitoSans-Regular.woff2")
}

IOW, during build Vite doesn't know it's URL prefix. By setting base: "/static/dist/" in vite config, the URL is correctly rewritten as:

@font-face {
    font-family: NunitoSans-Regular;
    src: url("/static/dist/NunitoSans-Regular.woff2")
}

@silviogutierrez
Copy link
Owner Author

That helps. Never ran into this because I don't include anything like fonts in the bundle. I just include them directly as a style tag and point to the static URL.

Will investigate, at worst you can just create a custom vite config file. Which is actually a feature: overriding is now as simple as creating a vite config that is automatically picked up and merged. Will even include additional plugins, etc.

@silviogutierrez
Copy link
Owner Author

@crgwbr : I think I see the issue. Vite knows about it during development, but not at build time. Can you confirm the issue happens with build.py and not runserver?

vite.mts is only used during development not at build time (build.client.mts is used for that)

@crgwbr
Copy link
Contributor

crgwbr commented Feb 17, 2024 via email

@silviogutierrez silviogutierrez added the snapshot Triggers a snapshot release to NPM and PyPI label Feb 17, 2024
@github-actions github-actions bot removed the snapshot Triggers a snapshot release to NPM and PyPI label Feb 17, 2024
@silviogutierrez silviogutierrez added the snapshot Triggers a snapshot release to NPM and PyPI label Feb 17, 2024
@github-actions github-actions bot removed the snapshot Triggers a snapshot release to NPM and PyPI label Feb 17, 2024
@silviogutierrez silviogutierrez added the snapshot Triggers a snapshot release to NPM and PyPI label Feb 17, 2024
@github-actions github-actions bot removed the snapshot Triggers a snapshot release to NPM and PyPI label Feb 17, 2024
@silviogutierrez silviogutierrez changed the title Poc/vite again Switch to Vite Feb 17, 2024
@kodiakhq kodiakhq bot merged commit 07321c7 into main Feb 17, 2024
11 checks passed
@kodiakhq kodiakhq bot deleted the poc/vite-again branch February 17, 2024 23:01
@github-actions github-actions bot locked and limited conversation to collaborators Feb 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants