-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
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
Legacy plugin inlines CSS #2062
Comments
This is somewhat intentional - right now the modern build preloads the js chunk in parallel to the css using a preload directive. In the legacy build, this will have to be done with an XHR request (which I didn't bother to implement). I don't think this breaks anything behavior wise, but it surely can be improved. |
Yeah, it doesn't break anything, but it duplicates all of the CSS that's already included via the |
Would love an option to not have the CSS inlined in the legacy build since I'm integrating it in a backend-based project where I don't have an |
I would be willing to look into this and make a PR adding that option. But so far, I even fail to see where this inlining is happening - it doesn't seem to come from vite or vite-plugin-legacy (unless I missed it). So it seems so happen in one of the dependencies? Any hint would be welcome! Update: Ok, just a few seconds after posting this, I stumbled upon #4448 - this seems related and I know where to look further now. However, I think a fix should be found that covers both the issues, using the legacy plugin or not. |
Same problem here. Backend-based PHP project, enqueuing asset links manually, the inline styles only appear in the I was however able to track down where this is coming from. It's in Vite's core code directly, not the legacy plugin, and is triggered when CSS code-splitting is enabled. The CSS chunk gets produced by these lines: vite/packages/vite/src/node/plugins/css.ts Lines 434 to 454 in 88ded7f
If you set config.build.cssCodeSplit = false, the inlining no longer occurs. It's an unfortunate tradeoff, but until a more direct control is available this is your best workaround. I'm wondering, though... is it actually correct behavior for the whole stylesheet to get inlined here? Shouldn't it only be the aggregated CSS chunks imported in async modules? |
Update: turns out there's a bug that causes CSS files to be omitted from the manifest if build.cssCodeSplit is set to false... which could make this a real pain for backend projects. See #3629 and #6477 . Given that, I can't see a clean workaround. We need an option to turn off the CSS inlining entirely, or only include the async chunks instead of the whole stylesheet. |
It'd be really nice to have the option to remove CSS from the legacy file altogether. I also have a PHP application, WordPress to be spesific.
This inlining results in my legacy bundle being much larger than it otherwise would be. The nomodule attribute ensures that modern browsers won't waste their time so it doesn't even matter, but chasing perfection is really rough with an issue like this. |
Hello @sapphi-red, recently you fix: allow tree-shake glob eager css in js Thanks |
The css-js file compiled by non-modern browsers will write the styles twice, and the volume will increase sharply, which should be a problem. example:
|
I would also like to see this fixed in a way where I can disable legacy output for CSS, since I already use I have written a small plugin that seems to do the trick for me right now, but please be cautious with using it since I do not understand the inner workings of import type { Plugin } from 'vite';
export const removeLegacyCss = (): Plugin => {
return {
name: 'remove-legacy-css',
enforce: 'post',
generateBundle: (_, bundle) => {
const remove = Object.entries(bundle)
.filter(([key, value]) => {
if ('facadeModuleId' in value) {
return key.includes('legacy') && value.facadeModuleId?.endsWith('css');
}
return false;
})
.map(([id]) => {
return id;
});
for (const key of remove) {
delete bundle[key];
}
},
};
}; It is then usable like any normal imported plugin, by adding it to the plugins array: |
I also think adding an option to
would make a lot of sense for cases like these? For backend integration it breaks stuff for me currently. I already run Is one of the options acceptable to the Vite team? I think one of us would surely create a PR if there is confirmation that we are good to go with that. |
I've fixed this by creating a manual chunk for each CSS file I have. This allows me the have more than one stylesheet while also omitting all the CSS in my legacy chunks. It will however generate an extra JS file with just the CSS. But those can just be ignored. manualChunks: ( id ) => {
if ( id.endsWith( 'scss' ) ) {
return path.parse( id ).name;
}
}, |
Being hit by this issue as well. In general I'm not very fond of the approach used by this plugin as essentially having two builds takes twice the time, twice the space and it creates hard-to-track-down bugs "works on my machine" where a user and developer might run different variants of the build. Anyway, this issue feels like a showstopper. If anyone can give some overall pointers I would gladly create a PR to fix the issue by just excluding the CSS from the legacy bundle (as it is still loaded normally using
How did you "ignore" the files? They are still referenced by |
This is how we managed to remove unecessary inlined styles when Vite emited those same styles as separate file. Following plugin call will process only chunks inside function removeLegacyStyleInject({ include }) {
let config;
return {
configResolved(_config) {
config = _config;
},
async renderChunk(code, chunk) {
if (
code.includes("__vite_style__") &&
include(chunk)
) {
const t = babel.types;
const result = await babel.transformAsync(code, {
sourceMaps: config.build.sourcemap,
plugins: [
{
visitor: {
Identifier(path) {
if (path.node.name.includes("__vite_style__")) {
const found = path.findParent((path) => {
return (
path.isVariableDeclaration() ||
path.isExpressionStatement()
);
});
found?.remove();
}
if (path.node.name.includes("exports")) {
const found = path.findParent((path) => {
const args = path.get("arguments");
return (
path.isCallExpression() &&
path.get("callee").node?.name === "exports" &&
args.length === 2 &&
args[1].isStringLiteral()
);
});
found?.get("arguments")[1].replaceWith(t.stringLiteral(""));
}
},
},
},
],
});
return { code: result.code, map: result.map };
}
return null;
},
enforce: "post",
apply: "build",
};
}
// Add this as Vite plugin
removeLegacyStyleInject({
include: (chunk) => {
return (
chunk.facadeModuleId?.includes("/entry-points/") &&
chunk.facadeModuleId?.includes(".scss")
)
}
}) |
it does! I am using the legacy plugin with |
Also getting bit by this. We're trying to use the plugin in an application where a traditional (Java) backend renders the HTML and generates script/link tags manually by reading the manifest to know which static CSS/JS imports are used by a given entrypoint (we don't require modern browser feature support and don't ship ES Modules in production). Due to this behavior, I have to choose between shipping JS that's compatible with our supported browser list or splitting CSS into its own set of chunks. We want to do both, which is possible in Webpack via separate loader chains for scripts/styles, but not via this plugin currently. |
Has this issue been forgotten? It seems like it hasn't been optimized for a long time. |
any update? |
Describe the bug
When attaching
@vitejs/plugin-legacy
, theindex.css
content is inlined into theindex-legacy.[hash].js
output file. This is reproducible with the base Preact template (and presumably others).No changes/issues with the non-legacy output.
Reproduction
https://github.com/lukeed/bug-vite-legacy-inline-css
System Info
vite
version: 2.0.1Logs (Optional if provided reproduction)
Don't see anything useful within
--debug
output. Providing summary instead:The text was updated successfully, but these errors were encountered: