You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 09:28:12