使用ESBuild编译浏览器JS时导入CSS遇Unexpected "."错误求助
问题分析
报错Unexpected "."是因为esbuild默认将CSS文件当作JavaScript解析,遇到CSS类选择器的.时无法识别。你的插件逻辑没被触发,大概率是插件优先级问题或者路径匹配未生效,同时插件代码里存在语法错误导致逻辑执行失败。
修复步骤
1. 修复插件中的语法错误
你的escapedData函数里,替换单引号的代码用了HTML实体",这在JavaScript里会导致语法错误,直接换成原生字符串处理:
const escapedData = (data: string) => { return data .replace(/\n/g, '') .replace(/"/g, '\\"') .replace(/'/g, "\\'"); };
2. 添加onResolve拦截CSS文件导入
在插件的setup里添加onResolve钩子,专门拦截.css后缀的文件,标记专属命名空间,确保它们被插件优先处理,而不是走esbuild默认加载流程:
build.onResolve({ filter: /\.css$/ }, async (args) => { // 指定该文件由当前插件处理 return { path: args.path, namespace: 'css-plugin' }; });
同时拆分onLoad钩子,分别处理入口文件、CSS文件和其他JS/JSX文件,避免匹配冲突:
- 单独匹配
index.js处理入口代码 - 针对
css-plugin命名空间处理CSS转换 - 处理剩余JS/JSX文件
3. 完整修复后的插件代码
import axios from 'axios'; import * as esbuild from 'esbuild-wasm'; import localforage from 'localforage'; const fileCache = localforage.createInstance({ name: 'fileCache', }); export const outputCodeFetcher = (inputCode: string) => { return { name: 'output-code-fetch', setup(build: esbuild.PluginBuild) { // 处理入口文件index.js build.onLoad({ filter: /^index\.js$/ }, async () => { return { loader: 'jsx', contents: inputCode, }; }); // 拦截CSS文件导入,标记专属命名空间 build.onResolve({ filter: /\.css$/ }, async (args) => { return { path: args.path, namespace: 'css-plugin' }; }); // 处理CSS文件,转换为JS代码 build.onLoad({ filter: /.*/, namespace: 'css-plugin' }, async (args) => { const cachedResult = await fileCache.getItem(args.path); if (cachedResult) { return cachedResult; } const { data, request } = await axios.get(args.path); const escapedData = (data: string) => { return data .replace(/\n/g, '') .replace(/"/g, '\\"') .replace(/'/g, "\\'"); }; const contents = ` const style = document.createElement('style'); style.innerText = '${escapedData(data)}'; document.head.appendChild(style); `; const axiosResult = { loader: 'jsx', contents: contents, resolveDir: new URL('./', request.responseURL).pathname, }; await fileCache.setItem(args.path, axiosResult); return axiosResult; }); // 处理其他JS/JSX文件 build.onLoad({ filter: /.*/ }, async (args) => { const cachedResult = await fileCache.getItem(args.path); if (cachedResult) { return cachedResult; } const { data, request } = await axios.get(args.path); const axiosResult = { loader: 'jsx', contents: data, resolveDir: new URL('./', request.responseURL).pathname, }; await fileCache.setItem(args.path, axiosResult); return axiosResult; }); }, }; };
4. 验证ESBuild配置
确保build配置中bundle为true(你已开启),插件顺序正确——如果inputCodePathResolver是处理路径解析的插件,需放在outputCodeFetcher之前。
关键原理
- 通过
onResolve拦截CSS文件并标记命名空间,强制esbuild跳过默认解析规则,交给插件处理。 - 修复语法错误后,插件能正常将CSS转换为动态创建
<style>标签的JS代码,让esbuild可以正确解析打包。
内容的提问来源于stack exchange,提问作者Nick Murdaugh
相关产品推荐
相关产品推荐

