Open jaydenseric opened 4 years ago
@jaydenseric hey mate I am sorry that you are experiencing this kind of issue! Sounds super frustrating. Before we even try to debug, can you make sure that both the library and the consumer app are transpiled in the same "mode" (either NODE_ENV production or development).
The issue could be that your components library is compiled for development mode and when the Next.js app is deployed to prod it instead uses optimizeForSpeed
which cannot insert styles compiled for dev mode.
I'll check, but if that is the case, how come none of the styles are broken right now at https://deviceagnosticui.com, or https://jaydenseric.com?
it seems that you are not using optimizeForSpeed
there
this article explains how to bundle a library that uses styled-jsx to avoid this issue https://medium.com/@tomaszmularczyk89/guide-to-building-a-react-components-library-with-rollup-and-styled-jsx-694ec66bd2
Thanks for the revelation, although this is a real headache to solve elegantly 😓
Bundling published packages is an antipattern, and you can't have require()
inside ESM for a conditional export. ESM for tree-shaking is an important feature for a published a component library, so I don't want to drop it. Publishing invalid ESM is undesirable because eventually, maybe even in January when conditional package exports are shipped by Node.js, I'll be publishing ESM intended to be run natively.
I think I might publish two builds at device-agnostic-ui
for development and device-agnostic-ui/production
for production, then have Babel/Webpack config to setup in Next.js to rewrite the imports accordingly.
Is optimizeForSpeed
even worth it? What exactly are the benefits?
I might just continue to publish device-agnostic-ui
components and styles transpilled with it false
, and in the Next.js setup instructions explain that the Babel config needs to be configured like this:
{
"presets": [["next/babel", { "styled-jsx": { "optimizeForSpeed": false } }]]
}
optimizeForSpeed
uses the CSSOM apis which are way faster than creating script tags.
As for bundling you have a few options:
1) Ship the source code and let the host app build it
2) You can use conditionals, bundlers will treeshake this https://github.com/giuseppeg/style-sheet/blob/aa125093adcef82ca3bd86b58f5f203ef232a4fa/lib/esm/createElement.js (works only if the bundler replaces process.env.NODE_ENV === 'production'
with a constant, eg. webpack does that)
Here is an example of the difference between transpiling published components without and with NODE_ENV=production
…
For this component:
https://deviceagnosticui.com/components/Blockquote
Here is without:
export var stylesBlockquote = {
styles: React.createElement(
_JSXStyle,
{
id: '3946479178'
},
'.jsx-3946479178,.' +
stylesHtml.className +
' blockquote{margin-top:1.5em;margin-bottom:1.5em;margin-left:1.5em;max-width:22em;line-height:var(--daui-line-height);font-style:italic;font-weight:300;text-rendering:optimizeLegibility;}.jsx-3946479178:first-child,.' +
stylesHtml.className +
':first-child{margin-top:0;}.jsx-3946479178:last-child,.' +
stylesHtml.className +
':last-child{margin-bottom:0;}'
),
className: 'jsx-3946479178'
}
Here is with:
export var stylesBlockquote = {
styles: React.createElement(
_JSXStyle,
{
id: '3946479178'
},
[
'.jsx-3946479178,.' +
stylesHtml.className +
' blockquote{margin-top:1.5em;margin-bottom:1.5em;margin-left:1.5em;max-width:22em;line-height:var(--daui-line-height);font-style:italic;font-weight:300;text-rendering:optimizeLegibility;}',
'.jsx-3946479178:first-child,.' +
stylesHtml.className +
':first-child{margin-top:0;}',
'.jsx-3946479178:last-child,.' +
stylesHtml.className +
':last-child{margin-bottom:0;}'
]
),
className: 'jsx-3946479178'
}
The only difference appears to be the rules are one big string in dev, and an array of a string for each rule for prod.
Could styled-jsx be enhanced to automatically .join()
an array of rules encountered in development? That way component libraries could be published only transpilled for production.
Could styled-jsx be enhanced to automatically .join() an array of rules encountered in development?
@jaydenseric it is not possible to mix up optimizedForSpeed
styles with non optimizedForSpeed
. With optimizedForSpeed
we insert to a single style tag using the CSSOM API, when it is disabled we create multiple style tags.
We can add a warning here though
like if this._optimizeForSpeed
is true but props.children
(the styles) are not an array print a warning
Perhaps you misunderstood the suggestion...
It would be impossible to support importing non optimizedForSpeed
styles for use in a production build, because it is hard to cut up a style string into an array of rules at runtime - that should always happen build time.
But, I can't see why the other way around can't be supported. You should be able to import optimizedForSpeed
styles for use in a development build, because it would be trivial at runtime to detect the styles are an array of rules, and simply array .join()
them together into a single string, just like how non optimizedForSpeed
styles are normally.
With this behavior, to write a component/styles library all you need to do is publish a single optimizedForSpeed
build. This would make it much easier to publish styled-jsx styles and a native ESM build would be possible.
@jaydenseric that is a bit "magic" but it might work, want to put together a PR?
@giuseppeg I got started on a PR a few weeks ago but had to move onto other stuff. Sorry, but I'm now out of time and money to work on OSS, see https://twitter.com/jaydenseric/status/1206800095280128006. I've been working on OSS pro bono full time since August; time to get back into paid work.
@jaydenseric no worries! I get that as I am not paid to maintain any of these OSS projects as well :)
Do you want to request a feature or report a bug?
Bug.
What is the current behavior?
In
optimizeForSpeed
mode, sometimes multiple rules are being incorrectly stuffed into.insertRule()
at once, here:https://github.com/zeit/styled-jsx/blob/343795d3b2fef6d3d085921b0d4cf31b4d9be709/src/lib/stylesheet.js#L129-L139
In production the error is swallowed. This made it exceedingly hard for me to figure out why two seperate Next.js projects were having horrific style issues in production; the SSR page would be styled fine, but on client side route transitions, the styled-jsx styles for many of the components would be simply missing.
Here is a spectrum post from when I first encountered this bug a few weeks ago:
https://spectrum.chat/styled-jsx/general/absolutely-stumped-why-next-js-page-styles-broke~3ad888fa-23fc-4e6c-9948-fffa81a4e956
I found the "fix" at the time was to remove styling from a single component:
https://github.com/jaydenseric/device-agnostic-ui-website/commit/a44fca1f988038d4245054a3b1990eef8d053cbb
Fast forward to now, and I have another project that suddenly started bombing out, here is a Zeit Now deployment:
whimsy-app-hhuy9ally.now.sh
If I put a
console.error(error)
in here, this is an example error:Here is a problematic "rule" that was being inserted:
It was the result of a few weeks of work without a production deployment, so unfortunately I can't say exactly what change pushed the bug to occur. I haven't lucked out on a miraculous "fix" this time though, so after MANY hours of debugging I'm getting pretty desperate!
If the current behavior is a bug, please provide the steps to reproduce and possibly a minimal demo or testcase in the form of a Next.js app, CodeSandbox URL or similar
I'm sorry I only have complicated, real world examples linked above and I haven't been able to isolate a minimal reproduction.
I have discovered that debugging is a lot easier when running
optimizeForSpeed
in dev, so traces aren't minified:What is the expected behavior?
Styles managed via styled-jsx in a Next.js app render correctly on the client after route transitions.
Environment (include versions)
Did this work in previous versions?
Not sure.