I have a Meteor React project, for which I have added Material UI v5 (installation instructions), which comes with Emotion for CSS-in-JS styling:
$ 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/react@11.11.1
+ @emotion/styled@11.11.0
+ @mui/material@5.14.17
added 79 packages from 99 contributors and audited 193 packages in 51.3s
I would like to use the components selector API, so I installed @emotion/babel-plugin
as required:
$ meteor npm install @emotion/babel-plugin
+ @emotion/babel-plugin@11.11.0
updated 1 package and audited 194 packages in 1.643s
...and then I added the configuration, as described in above docs, into my .babelrc
file:
{
"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"]
}
}
}
}
]
]
}
However, now when I start the project, I have this error:
$ 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
Here are my dependencies in 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 release version in .meteor/release
:
METEOR@2.13.3
And Meteor packages versions in .meteor/packages
:
meteor-base@1.5.1 # Packages every Meteor app needs to have
mobile-experience@1.1.0 # Packages for a great mobile UX
mongo@1.16.7 # The database Meteor supports right now
reactive-var@1.0.12 # Reactive variable for tracker
standard-minifier-css@1.9.2 # CSS minifier run for production mode
standard-minifier-js@2.8.1 # JS minifier run for production mode
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers
ecmascript@0.16.7 # Enable ECMAScript2015+ syntax in app code
typescript@4.9.4 # Enable TypeScript syntax in .ts and .tsx modules
shell-server@0.5.0 # Server-side component of the `meteor shell` command
hot-module-replacement@0.5.3 # Update client in development without reloading the page
static-html@1.3.2 # Define static page content in .html files
react-meteor-data # React higher-order component for reactively tracking Meteor data
Note: if I use the --typescript
template (instead of the defaut --react
one as shown above), I have the same issue, but with a slightly different error message:
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
It looks like simply using the full name of the @emotion/babel-plugin
package in the .babelrc
Babel configuration file solves the issue for Meteor build:
{
"plugins": [
[
"@emotion/babel-plugin", // Use full name, instead of just "@emotion"
{
// Etc.
}
]
]
}
In a normal React and Babel stack, the short name @emotion
in the Babel configuration is actually normalized automatically to that full name, hence the Material UI documentation provides only the short name:
Babel has a name normalization phase [which] will automatically add these prefixes when loading [plugin] items.
[...]
babel-plugin
/babel-preset
will be injected as the package name if only the@
-scope name is given.Input:
"@scope"
/ Normalized:"@scope/babel-plugin"
Unfortunately, it looks like the Meteor babel-compiler
wrapper package re-implements this logic, but only the most simple part:
prefixes.push("@babel/plugin-", "babel-plugin-");
// ...
prefixes.push("");
// ...
inputFile.resolve(prefix + id, controlFilePath))
Hence it is safer specifying the actual full name of the Babel plugin, instead of relying on name normalization.
Note: BTW, while the build now works, I still had an issue displaying the web page in the browser, with this message in browser console:
Uncaught Error: Component selectors can only be used in conjunction with @emotion/babel-plugin, the swc Emotion plugin, or another Emotion-aware compiler transform.
I was using the components selector API like this:
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...
}
});
Because of the import of styled
function from the parent package @mui/material
instead of the more specific @mui/material/styles
as mentioned in the Babel config, the latter could not catch the import to transform. So I just needed to add the missing import path to the configuration as well:
{
"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"]
}
}
}
}
]
]
}
...and now everything works!