在 Meteor 中使用 Babel 配置 MUI 组件选择器 API 在项目启动时出现“TypeError: Cannot read property 'id' of null”

Configuring MUI Components selector API with Babel in Meteor "TypeError: Cannot read property 'id' of null" on project start

提问人:ghybs 提问时间:11/9/2023 最后编辑:ghybs 更新时间:11/16/2023 访问量:158

问:

我有一个 Meteor React 项目,我为此添加了 Material UI v5(安装说明),它带有 CSS-in-JS 样式的 Emotion:

$ meteor create --react meteor-react-mui
Created a new Meteor app in 'meteor-react-mui'.

$ cd meteor-react-mui

$ meteor npm install @mui/material @emotion/react @emotion/styled
+ @emotion/[email protected]
+ @emotion/[email protected]
+ @mui/[email protected]
added 79 packages from 99 contributors and audited 193 packages in 51.3s

我想使用组件选择器 API,因此我根据需要进行了安装:@emotion/babel-plugin

$ meteor npm install @emotion/babel-plugin
+ @emotion/babel-plug[email protected]
updated 1 package and audited 194 packages in 1.643s

...然后我将配置(如上述文档所述)添加到我的文件中:.babelrc

{
  "plugins": [
    [
      "@emotion",
      {
        "importMap": {
          "@mui/system": {
            "styled": {
              "canonicalImport": ["@emotion/styled", "default"],
              "styledBaseImport": ["@mui/system", "styled"]
            }
          },
          "@mui/material/styles": {
            "styled": {
              "canonicalImport": ["@emotion/styled", "default"],
              "styledBaseImport": ["@mui/material/styles", "styled"]
            }
          }
        }
      }
    ]
  ]
}

但是,现在当我启动项目时,我遇到了以下错误:

$ meteor
[[[[[ ~/.../meteor-react-mui ]]]]]

=> Started proxy.                             
=> Started HMR server.                        
/.../.meteor/packages/meteor-tool/.../dev_bundle/lib/node_modules/meteor-promise/promise_server.js:218
      throw error;
      ^

TypeError: Cannot read property 'id' of null
    at InputFile.resolve (/tools/isobuild/compiler-plugin.js:403:61)
    at packages/babel-compiler.js:574:23
    at Array.some (<anonymous>)
    at requireWithPrefixes (packages/babel-compiler.js:569:26)
    at requireWithPath (packages/babel-compiler.js:487:14)
    at resolveHelper (packages/babel-compiler.js:459:24)
    at resolveHelper (packages/babel-compiler.js:450:21)
    at packages/babel-compiler.js:432:19
    at Array.forEach (<anonymous>)
    at walkHelper (packages/babel-compiler.js:431:10)
    at walkBabelRC (packages/babel-compiler.js:417:24)
    at BabelCompiler.BCp._inferHelper (packages/babel-compiler.js:514:17)
    at BabelCompiler.BCp._inferFromBabelRc (packages/babel-compiler.js:359:14)
    at BabelCompiler.BCp.inferExtraBabelOptions (packages/babel-compiler.js:333:10)
    at BabelCompiler.BCp.processOneFileForTarget (packages/babel-compiler.js:209:10)
    at packages/babel-compiler.js:123:25

以下是我的依赖项:package.json

{
  "dependencies": {
    "@babel/runtime": "^7.20.7",
    "@emotion/babel-plugin": "^11.11.0",
    "@emotion/react": "^11.11.1",
    "@emotion/styled": "^11.11.0",
    "@mui/material": "^5.14.17",
    "meteor-node-stubs": "^1.2.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

Meteor 发布版本:.meteor/release

[email protected]

和 Meteor 打包版本:.meteor/packages

[email protected]             # Packages every Meteor app needs to have
[email protected]       # Packages for a great mobile UX
[email protected]                   # The database Meteor supports right now
[email protected]            # Reactive variable for tracker

[email protected]   # CSS minifier run for production mode
[email protected]    # JS minifier run for production mode
[email protected]                # ECMAScript 5 compatibility for older browsers
[email protected]              # Enable ECMAScript2015+ syntax in app code
[email protected]              # Enable TypeScript syntax in .ts and .tsx modules
[email protected]            # Server-side component of the `meteor shell` command
[email protected]  # Update client in development without reloading the page


[email protected]             # Define static page content in .html files
react-meteor-data       # React higher-order component for reactively tracking Meteor data

注意:如果我使用模板(而不是上面所示的模板),我会遇到同样的问题,但错误消息略有不同:--typescript--react

TypeError: Cannot read property 'id' of null
    at InputFile.resolve (/tools/isobuild/compiler-plugin.js:403:61)
    at packages/babel-compiler.js:574:23
    at Array.some (<anonymous>)
    at requireWithPrefixes (packages/babel-compiler.js:569:26)
    at requireWithPath (packages/babel-compiler.js:487:14)
    at resolveHelper (packages/babel-compiler.js:459:24)
    at resolveHelper (packages/babel-compiler.js:450:21)
    at packages/babel-compiler.js:432:19
    at Array.forEach (<anonymous>)
    at walkHelper (packages/babel-compiler.js:431:10)
    at walkBabelRC (packages/babel-compiler.js:417:24)
    at TypeScriptCompiler.BCp._inferHelper (packages/babel-compiler.js:514:17)
    at TypeScriptCompiler.BCp._inferFromBabelRc (packages/babel-compiler.js:359:14)
    at TypeScriptCompiler.BCp.inferExtraBabelOptions (packages/babel-compiler.js:333:10)
    at TypeScriptCompiler.BCp.processOneFileForTarget (packages/babel-compiler.js:209:10)
    at packages/babel-compiler.js:123:25
reactjs meteor material-ui babeljs emotion

评论


答:

1赞 ghybs 11/16/2023 #1

看起来只需在 Babel 配置文件中使用包的全名就可以解决 Meteor 构建的问题:@emotion/babel-plugin.babelrc

{
  "plugins": [
    [
      "@emotion/babel-plugin", // Use full name, instead of just "@emotion"
      {
        // Etc.
      }
    ]
  ]
}

在普通的 React 和 Babel 堆栈中,Babel 配置中的短名称实际上会自动规范化为该全名,因此 Material UI 文档仅提供短名称:@emotion

Babel 有一个名称规范化阶段,[它] 会在加载 [plugin] 项时自动添加这些前缀。

[...]

  • babel-plugin/babel-preset如果仅给出 -scope 名称,则将作为包名称注入。@

输入: / 规范化:"@scope""@scope/babel-plugin"

不幸的是,看起来 Meteor 包装包重新实现了这个逻辑,但只有最简单的部分:babel-compiler

prefixes.push("@babel/plugin-", "babel-plugin-");
// ...
prefixes.push("");
// ...
inputFile.resolve(prefix + id, controlFilePath))

因此,指定 Babel 插件的实际全名比依赖名称规范化更安全。


注意:顺便说一句,虽然构建现在可以工作,但我在浏览器中显示网页时仍然遇到问题,在浏览器控制台中显示以下消息:

未捕获的错误:组件选择器只能与 @emotion/babel-plugin、swc Emotion 插件或其他 Emotion 感知编译器转换结合使用。

我像这样使用组件选择器 API:

import { styled } from "@mui/material"; // Import from MUI instead of from @emotion, e.g. to benefit from theme prop

const StyledDiv = styled("div")({
  // Some style...
});

const StyledParent = styled("div")({
  [StyledDiv]: { // Use the actual component as a selector
    // Some other style...
  }
});

由于从父包导入函数,而不是 Babel 配置中提到的更具体的函数,因此后者无法捕获要转换的导入。因此,我只需要将缺少的导入路径添加到配置中:styled@mui/material@mui/material/styles

{
  "plugins": [
    [
      "@emotion/babel-plugin",
      {
        "importMap": {
          "@mui/system": {
            "styled": {
              "canonicalImport": ["@emotion/styled", "default"],
              "styledBaseImport": ["@mui/system", "styled"]
            }
          },
          "@mui/material/styles": {
            "styled": {
              "canonicalImport": ["@emotion/styled", "default"],
              "styledBaseImport": ["@mui/material/styles", "styled"]
            }
          },
          "@mui/material": { // Add the missing import path
            "styled": {
              "canonicalImport": ["@emotion/styled", "default"],
              "styledBaseImport": ["@mui/material", "styled"]
            }
          }
        }
      }
    ]
  ]
}

...现在一切正常!