Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/core/.build/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ import vue from '@vitejs/plugin-vue';
import { libInjectCss } from 'vite-plugin-lib-inject-css';
import autoImportPlugin from './autoImport';
import dtsPlugin from './dts';
import xSenderCssLayerPlugin from './xSenderCssLayer';
// import prismjsPlugin from './prismjs'

const plugins: PluginOption[] = [
vue({
script: {
propsDestructure: true,
},
propsDestructure: true
}
}) as PluginOption,
// prismjsPlugin,
...autoImportPlugin,
dtsPlugin as PluginOption,
libInjectCss() as PluginOption,
xSenderCssLayerPlugin() as PluginOption
];

export default plugins;
22 changes: 22 additions & 0 deletions packages/core/.build/plugins/xSenderCssLayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Plugin } from 'vite';

/**
* 将 x-sender 的 CSS 包裹进 @layer,避免其内部的 .ant 等样式污染用户项目中 ant-design-vue 的全局样式。
*
* @layer 内的样式优先级低于任何非 @layer 的普通样式,因此 ant-design-vue 的样式天然优先,互不干扰。
*/
export default function xSenderCssLayerPlugin(): Plugin {
return {
name: 'vite-plugin-x-sender-css-layer',
transform(code, id) {
if (id.includes('x-sender') && id.endsWith('.css')) {
return {
Comment on lines +11 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Vite plugin transform(code, id), can CSS module idvalues include query suffixes (for example?used, ?inline, or other query parameters)? What is the recommended pattern for reliably matching .css files from a specific package?

💡 Result:

Web search failed: Server error: no LLM provider could handle the message


🏁 Script executed:

# First, let's examine the actual plugin file
find . -name "xSenderCssLayer.ts" -o -name "*xSender*" | head -20

Repository: element-plus-x/Element-Plus-X

Length of output: 1909


🏁 Script executed:

# Look at the plugin file content
cat packages/core/.build/plugins/xSenderCssLayer.ts 2>/dev/null || find . -path "*/.build/plugins/*" -name "*.ts" | head -5

Repository: element-plus-x/Element-Plus-X

Length of output: 1909


🏁 Script executed:

# Search for other Vite plugin transform patterns in the codebase
rg "transform\s*\(\s*code\s*,\s*id" --type ts -A 3 | head -50

Repository: element-plus-x/Element-Plus-X

Length of output: 1909


🏁 Script executed:

# Check if there are any tests or usage examples for this plugin
fd "xSender" -e test.ts -e spec.ts -e test.js -e vite.config.ts

Repository: element-plus-x/Element-Plus-X

Length of output: 1909


🌐 Web query:

Vite plugin transform hook CSS module id query parameters ?used ?inline

💡 Result:

In Vite's CSS plugin, the module ID for CSS files (including CSS modules) can include specific query parameters during the transform hook: - ?used: Internal query parameter automatically appended by Vite's import analysis plugin to CSS imports that use the default export. It signals that the CSS string content should be exported (e.g., export default "css content"), enabling tree-shaking of unused CSS strings by esbuild/Rollup. Without ?used, the module may export an empty string or nothing to avoid unnecessary CSS inclusion. This is used in scenarios like import.meta.glob for CSS or default imports needing the content. Note: Vite 5+ removed default CSS exports, making ?inline required for string access. - ?inline: User-specified query to return processed CSS as a string default export instead of injecting via <style> tag (disables automatic injection). Commonly used for manual CSS handling, e.g., import css from './styles.module.css?inline'. Works with CSS modules but returns the CSS string (losing class exports unless combined with plugins). These appear in the id string in the transform hook filter/handler: transform: [{ filter: /?.(used|inline)/ }, handler(src, id) { / id includes ?used or ?inline */ } ]. They help plugins detect/respond to CSS handling modes, e.g., custom inlining or optimization. ?used is internal (not for direct user use; auto-managed), while ?inline is public. For CSS modules with both needs (classes + inline CSS), use plugins like vite-plugin-css-injected-by-js or vite-css-modules.

Citations:


🌐 Web query:

Vite plugin reliable pattern match CSS files id endsWith

💡 Result:

In Vite plugins, the reliable pattern to match CSS files by their module ID (where the ID ends with '.css') uses the transform hook's filter option with a RegExp. Use this structure: export default function myPlugin { return { name: 'my-css-plugin', transform(code, id) { if (id.endsWith('.css')) { // Your transform logic for CSS files return { code: transformedCode, map: null // or source map } } } } } For newer Vite versions (5+), use the structured transform filter: import { regexFilter } from '@rolldown/pluginutils' // or similar utility export default function myPlugin { return { name: 'my-css-plugin', transform: { filter: regexFilter(/.css$/), handler(code, id) { // Transform logic return { code: transformedCode, map: null } } } } } This matches any module ID ending with '.css'. The id is the full resolved module path/identifier, so endsWith('.css') reliably identifies CSS files. id.endsWith('.css') is safe as id is always a string in Vite plugin hooks.

Citations:


🌐 Web query:

best practice Vite plugin id.split('?') CSS matching third party packages

💡 Result:

In Vite plugins, particularly for handling CSS processing or CSS modules in third-party packages from node_modules, a common best practice is to use conditional filtering in plugin options like include or exclude functions. These functions receive the module id (full path) and parse it using id.split('?') to get the base filename without Vite's internal query parameters (like ?direct or ?html-proxy-cache), then check properties like id.includes('node_modules') to selectively apply transformations. Example from vite-plugin-for-load-css-module for third-party SCSS modules (source 1): include: (id) => (id.endsWith('less') && !id.includes('node_modules')) || (id.endsWith('scss') && id.includes('node_modules')) This pattern ensures: - App CSS (non-node_modules) gets standard processing. - Third-party CSS (node_modules) gets special handling only when needed, avoiding conflicts with relative paths or PostCSS. Vite's CSS pipeline adds query params during processing (sources 14, 19, 22), so splitting isolates the source path for reliable matching. Use enforce: 'pre' for custom CSS plugins to run before Vite's vite:css (source 18). For CSS modules specifically, plugins like vite-css-modules (sources 2,4,27) integrate better with Vite's module graph. Official Vite docs recommend css.modules.include/exclude for scoped processing (vitejs.dev/config/#css-modules-include). Custom plugins follow Rollup/Vite plugin conventions: parse id cleanly before path checks.

Citations:


Handle Vite CSS query suffixes in the matcher.

On line 12, id.endsWith('.css') will miss CSS module IDs with query parameters (e.g., ...XSender.css?used, ...XSender.css?inline), preventing the layer wrapper from applying and reintroducing style pollution. Also, includes('x-sender') is overly broad and can match unrelated paths.

Split the ID on the query string separator first, then apply precise path matching:

🔧 Proposed fix
     transform(code, id) {
-      if (id.includes('x-sender') && id.endsWith('.css')) {
+      const [path] = id.split('?', 1);
+      if (/(^|[\\/])x-sender([\\/]|$)/.test(path) && path.endsWith('.css')) {
         return {
           code: `@layer element-plus-x-third-party {
 ${code}
 }`,
           map: null
         };
       }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
transform(code, id) {
if (id.includes('x-sender') && id.endsWith('.css')) {
return {
transform(code, id) {
const [path] = id.split('?', 1);
if (/(^|[\\/])x-sender([\\/]|$)/.test(path) && path.endsWith('.css')) {
return {
code: `@layer element-plus-x-third-party {
${code}
}`,
map: null
};
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/.build/plugins/xSenderCssLayer.ts` around lines 11 - 13, The
transform hook in xSenderCssLayer.ts currently checks id.includes('x-sender')
and id.endsWith('.css'), which misses Vite query suffixes and overmatches;
update the matcher in transform to first strip the query string (e.g., const
clean = id.split('?')[0]) and then perform a precise path/name check on that
cleaned id (for example using endsWith('XSender.css') or a path-aware test
rather than includes) so CSS module requests like XSender.css?used or ?inline
are matched and unrelated paths are not.

code: `@layer element-plus-x-third-party {
${code}
}`,
map: null
};
}
}
};
}