带有 JavaScript 的 SWC:如何处理 CSS 导入以及如何进行绝对导入?

SWC with JavaScript: How to handle CSS imports and how to absolute imports?

TL;DR

Here is a minimal reproducible example.


上下文

我们刚刚从 Babel 迁移到 SWC. (I asked a 。我正在改进那个问题的答案。)

我们从以下位置迁移了命令:

"test": "NODE_ENV=test riteway -r @babel/register 'src/**/*.test.js' | tap-nirvana",

"test": "SWC_NODE_PROJECT=./jsconfig.json riteway -r @swc-node/register src/**/*.test.js | tap-nirvana",

其中 jsconfig.json 看起来像这样:

{
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": "./src",
    "jsx": "react-jsx"
  }
}

如果我们编写尝试为自包含组件编译测试(没有绝对导入,没有 CSS)它有效:

import { describe } from 'riteway';
import render from 'riteway/render-component';

function HomePageComponent({ user: { email } }) {
  return <p>{email}</p>;
}

describe('home page component', async assert => {
  const user = { email: 'foo' };
  const $ = render(<HomePageComponent user={user} />);

  assert({
    given: 'a user',
    should: 'render its email',
    actual: $('p').text(),
    expected: user.email,
  });
});

测试编译正常。

有了 Babel,我们就有了这样的 .babelrc

{
  "env": {
    "test": {
      "plugins": [
        [
          "module-resolver",
          {
            "root": [
              "."
            ],
            "alias": {
              "components": "./src/components",
              "config": "./src/config",
              "features": "./src/features",
              "hocs": "./src/hocs",
              "hooks": "./src/hooks",
              "pages": "./src/pages",
              "redux": "./src/redux",
              "styles": "./src/styles",
              "tests": "./src/tests",
              "utils": "./src/utils"
            }
          }
        ]
      ]
    }
  },
  "presets": [
    [
      "next/babel",
      {
        "ramda": {}
      }
    ]
  ],
  "plugins": [
    ["styled-components", { "ssr": true }]
  ]
}

样式由 styled-components 处理,绝对导入由 module-resolver 插件定义。 (我们从 styled-components 切换到 CSS 模块,这就是我们从 .module.css CSS 文件导入的原因。无论如何...)

如果我们像这样用他们的实际导入来编写我们想要编写的测试:

import { describe } from 'riteway';
import render from 'riteway/render-component';
import { createPopulatedUserProfile } from 'user-profile/user-profile-factories';

import HomePageComponent from './home-page-component';

describe('home page component', async assert => {
  const user = createPopulatedUserProfile();
  const $ = render(<HomePageComponent user={user} />);

  assert({
    given: 'a user',
    should: 'render its email',
    actual: $('p').text(),
    expected: user.email,
  });
});

它失败了:

$ SWC_NODE_PROJECT=./jsconfig.json riteway -r @swc-node/register src/features/home/home-page-component.test.js | tap-nirvana
/Users/janhesters/dev/my-project/src/features/home/home.module.css:1
(function (exports, require, module, __filename, __dirname) { .container {
                                                              ^

SyntaxError: Unexpected token '.'

当我们离开 CSS 导入时 home-page-component.js,或者:

$ SWC_NODE_PROJECT=./jsconfig.json riteway -r @swc-node/register src/features/home/home-page-component.test.js | tap-nirvana
node:internal/modules/cjs/loader:936
  throw err;
  ^

Error: Cannot find module 'user-profile/user-profile-factories'
Require stack:
- /Users/janhesters/dev/my-project/src/features/home/home-page-component.test.js
- /Users/janhesters/dev/my-project/node_modules/riteway/bin/riteway

分别,当我们去掉 CSS 导入时。

我们如何帮助 SWC 理解 CSS(或模拟 CSS 模块)以及我们如何帮助它理解绝对导入?

我们已经在 jsconfig.json 中设置了 baseUrl ...

  1. 我们如何帮助 SWC 理解 CSS(或模拟 CSS 模块)? - SWC 本身不理解 css,巴别塔也没有。正如您所指出的,当您使用 Babel 时,插件 styled-components 会处理这个问题。您需要对 SWC 执行相同的操作。我找不到执行此操作的现有 SWC 插件,但您可以 roll your own。显然这很痛苦,但这就是使用新工具的成本。
  2. 我们如何帮助 SWC 理解绝对导入? - baseUrlpaths.swrc 选项应该做你想做的,但这也是 seems to have some issues.

直接在 @swc-node GitHub 回购中创建问题可能会更好,但考虑到那里的评论,感觉你可能会 SOL 一段时间。可能 faster/easier 使用 Next supports 开箱即用的库之一重写您的测试。

关于绝对路径

你已经在 jsconfig.json 文件中添加了 baseUrl 但没有添加路径,你应该像我一样修改你的配置文件:

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@screens": ["./screens"],
      "@shared": ["./shared"],
      "@shared/*": ["./shared/*"]
    },

路径是module-resolveralias,我猜你的root不应该是".",应该和你的[=15=一样] 文件,我的意思是 baseUrl 值。

  "plugins": [
    [
      "module-resolver",
      {
        "root": ["./src"],
        "extensions": [".ts", ".tsx", ".js", ".jsx", ".json"],
        "alias": {
          "@screens": "./src/screens",
          "@shared": "./src/shared"
        }
      }
    ]
  ],

如果你有 Webpack,你应该在你的 resolve 关键 Webpack 配置对象中有 alias 配置,就像我的一样:

const resolve = {
  extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
  alias: {
    '@screens': path.join(__dirname, 'src/screens/'),
    '@shared': path.join(__dirname, 'src/shared/'),
  },
  modules: ['src', 'node_modules'],
  descriptionFiles: ['package.json'],
};

关于CSS

实际上,您正在使用 CSS 文件作为 CSS-Modules 而不是 recommended NexJS doc,在文档中开发人员应该像 import './styles.css' 一样导入 CSS 然后使用它作为 JSX 中的 string 就像 <div className="main"

但是

您正在像模块一样导入它 (CSS-Module):

// you did it

import styles from './styles.css';

<div className={styles.main}

如你所知是built-in由this doc支持,NextJS支持,但是SWC看不懂。我花了几个小时来寻找解决方法,但 SWC 似乎还不支持 CSS-Module。您应该为 SWC 创建自己的插件以支持 CSS-Module.

我能够在不编写任何插件的情况下解决所有问题。我把问题的解决方案推到了my example repo

首先,使用官方SWC project's register。这意味着,您必须像这样编译测试:

"test": "riteway -r @swc/register 'src/**/*.test.js' | tap-nirvana",

可以通过运行安装:

yarn add --dev @swc/core @swc/register

我们需要这个版本的寄存器,因为它考虑了一个 .swcrc 文件,而另一个没有。

其次,您需要 "path""baseUrl" 来修复绝对导入,因为出于某种原因,SWC 不会推断仅提供 "baseUrl" 的所有路径。你需要调整你的 .swcrc 来处理 React。

{
  "jsc": {
    "baseUrl": "./src",
    "paths": {
      "*.css": ["utils/identity-object-proxy.js"],
      "utils/*": ["utils/*"]
    },
    "parser": {
      "jsx": true,
      "syntax": "ecmascript"
    },
    "transform": {
      "react": {
        "runtime": "automatic"
      }
    }
  },
  "module": {
    "type": "commonjs"
  }
}

第三,要解决 .css,您需要将所有对 .css 文件的导入重新映射到身份对象代理,您可以在上面的 .swcrc 示例中看到。身份对象代理是一个对象,当您引用任何 属性、returns 您尝试引用的字符串化键时。您可以像这样自己创建一个:

const identityObjectProxy = new Proxy(
  {},
  {
    get: function getter(target, key) {
      if (key === '__esModule') {
        return false;
      }
      return key;
    },
  },
);

export default identityObjectProxy;

要使 .css 重新映射生效,您需要对 .css 文件的所有导入进行绝对导入。 (import styles from './styles.module.css 不行!)