Open jantimon opened 1 month ago
Does next-yak
generate "use client"
?
Error reporting code is at https://github.com/vercel/next.js/blob/d8884615c9928a243abf9505b04b3ad34f2edf37/packages/next-swc/crates/next-custom-transforms/src/transforms/react_server_components.rs#L738-L749.
It requires is_client_entry
to be true, and is_client_entry
becomes true only by the code at https://github.com/vercel/next.js/blob/d8884615c9928a243abf9505b04b3ad34f2edf37/packages/next-swc/crates/next-custom-transforms/src/transforms/react_server_components.rs#L321-L351
no next-yak
will never inject use-client
the entire solution works well together with server components (as long as you don't add export const metadata
)
here is the transformed page.tsx
code from above (after next-yak):
import { Metadata } from "next";
import { Button } from "./button";
import { styled } from "next-yak";
import __styleYak from "./file.yak.module.css!=!./file?./file.yak.module.css";
export const metadata: Metadata = {
title: '...'
};
const Title =
/*YAK Extracted CSS:
.Title {
color: blue;
}*/
/*#__PURE__*/
styled.h1(__styleYak.Title);
export default function Home() {
return <>
<Title>Hello</Title>
<Button>Click me</Button>
</>;
}
and the magic virtual css module "./file.yak.module.css!=!./file?./file.yak.module.css" becomes:
.Title {
color: blue;
}
maybe the https://yak.js.org/playground helps
I patched dist/build/webpack/loaders/next-swc-loader.js
locally by adding a console.log to the error case:
function swcLoader(inputSource, inputSourceMap) {
const loaderSpan = this.currentTraceSpan.traceChild("next-swc-loader");
const callback = this.async();
loaderSpan.traceAsyncFn(()=>loaderTransform.call(this, loaderSpan, inputSource, inputSourceMap)).then(([transformedSource, outputSourceMap])=>{
if (this.resourcePath.includes("keyframes/page.tsx")) {
console.log("swcLoader succeeded 🙂");
console.dir({
loaders: this.loaders.map((loader) => {
return {
path: loader.path.replace(/.*node_modules\/next/, "next"),
}
}),
userRequest: this._module.userRequest,
resourcePath: this.resourcePath,
inputSource: String(inputSource),
}, { depth: 10 });
}
callback(null, transformedSource, outputSourceMap || inputSourceMap);
}, (err)=>{
console.log("swcLoader failed");
console.dir({
loaders: this.loaders.map((loader) => {
return {
path: loader.path.replace(/.*node_modules\/next/, "next"),
}
}),
userRequest: this._module.userRequest,
resourcePath: this.resourcePath,
inputSource: String(inputSource),
err,
}, { depth: 10 });
callback(err);
});
}
Now this gives me the following log:
swcLoader succeeded 🙂
{
loaders: [
{
path: 'next/dist/build/webpack/loaders/next-flight-loader/index.js'
},
{ path: 'next/dist/build/webpack/loaders/next-swc-loader.js' },
{
path: 'next-yak/packages/next-yak/dist/loaders/ts-loader.cjs'
}
],
userRequest: 'next-yak/packages/example/app/keyframes/page.tsx',
resourcePath: 'next-yak/packages/example/app/keyframes/page.tsx',
inputSource: 'import { Metadata } from "next";\n' +
'import { styled } from "next-yak/internal";\n' +
'import __styleYak from "./page.yak.module.css!=!./page?./page.yak.module.css";\n' +
'export const metadata: Metadata = {\n' +
" title: '..'\n" +
'};\n' +
'const Title =\n' +
'/*YAK Extracted CSS:\n' +
'.Title {\n' +
' color: blue;\n' +
'}*/\n' +
'/*#__PURE__*/\n' +
'styled.h1(__styleYak.Title);\n' +
'export default function Home() {\n' +
' return <>\n' +
' <Title>Hello</Title>\n' +
' </>;\n' +
'}'
}
and directly after that:
swcLoader failed
{
loaders: [
{
path: 'next/dist/compiled/@next/react-refresh-utils/dist/loader.js'
},
{
path: 'next/dist/build/webpack/loaders/next-flight-client-module-loader.js'
},
{ path: 'next/dist/build/webpack/loaders/next-swc-loader.js' },
{
path: 'next-yak/packages/next-yak/dist/loaders/ts-loader.cjs'
}
],
userRequest: 'next-yak/packages/example/app/keyframes/page.tsx?./page.yak.module.css',
resourcePath: 'next-yak/packages/example/app/keyframes/page.tsx',
inputSource: 'import { Metadata } from "next";\n' +
'import { styled } from "next-yak/internal";\n' +
'import __styleYak from "./page.yak.module.css!=!./page?./page.yak.module.css";\n' +
'export const metadata: Metadata = {\n' +
" title: '..'\n" +
'};\n' +
'const Title =\n' +
'/*YAK Extracted CSS:\n' +
'.Title {\n' +
' color: blue;\n' +
'}*/\n' +
'/*#__PURE__*/\n' +
'styled.h1(__styleYak.Title);\n' +
'export default function Home() {\n' +
' return <>\n' +
' <Title>Hello</Title>\n' +
' </>;\n' +
'}',
err: [Error:
× You are attempting to export "metadata" from a component marked with "use client", which is disallowed. Either remove the export, or the "use client" directive. Read more: https://nextjs.org/
│ docs/getting-started/react-essentials#the-use-client-directive
│
│
╭─[next-yak/packages/example/app/keyframes/page.tsx:1:1]
1 │ import { Metadata } from "next";
2 │ import { styled } from "next-yak/internal";
3 │ import __styleYak from "./page.yak.module.css!=!./page?./page.yak.module.css";
4 │ export const metadata: Metadata = {
· ────────
5 │ title: '..'
6 │ };
7 │ const Title =
╰────
] {
code: 'GenericFailure'
}
}
⨯ ./app/keyframes/page.tsx?./page.yak.module.css
@kdy1 as you can see there is no "use client" in the input source for the swc loader
the problem seems to be that it processes ./page.yak.module.css!=!./page?./page.yak.module.css
as typescript file as soon as export const metadata: Metadata
is added
It happens only in the "app-pages-browser"
issuerLayer - and gets injected as entry:
import: 'next-flight-client-entry-loader?modules={"request":"/next-yak/packages/example/app/keyframes/page.tsx?./page.yak.module.css","ids":[]}&modules={"request":"/next-yak/packages/next-yak/dist/context/index.js","ids":["YakThemeProvider"]}&server=false!'
Link to the code that reproduces this issue
https://github.com/jantimon/reproduction-next-server-component-css-error
To Reproduce
Current vs. Expected behavior
I am using a virtual css module (
./page.yak.module.css!=!./page?./page.yak.module.css
).It looks like that this virtual css module is compiled by SWC and fails because it was marked as client component.
I would expect that SWC does not transpile this file at all
Provide environment information
Which area(s) are affected? (Select all that apply)
SWC
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local), Vercel (Deployed), Other (Deployed)
Additional context
In the reproduction repository are two React Server components:
page.tsx
:and
button.tsx
:The bug happens only if
export const metadata
and a yak styled component are in the same file.Removing
export const metadata
orTitle
will not trigger the error.This is surprising as the button is also a yak styled component and can be imported into a server component which uses
export const metadata
.