Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(app-shell): drop Rollup, pre-compile css module and use a macro to load the styles #2018

Merged
merged 13 commits into from
Feb 1, 2021

Conversation

emmenko
Copy link
Member

@emmenko emmenko commented Jan 29, 2021

So I finally figured out a way to bundle the app-shell package using preconstruct.

Until now it was not possible because we loaded CSS modules, and thus babel does not know how to process that import.

Now we don't directly load the .css file anymore. Instead, we use postcss to compile it and load the styles using a macro. This allows the code to be bundled using Babel.

@changeset-bot
Copy link

changeset-bot bot commented Jan 29, 2021

🦋 Changeset detected

Latest commit: 1cf5369

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@commercetools-frontend/mc-scripts Minor
@commercetools-frontend/application-shell Minor
merchant-center-application-template-starter Patch
playground Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Jan 29, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/commercetools/merchant-center-application-kit/mf6ow6bfq
✅ Preview: https://merchant-center-application-kit-git-nm-drop-rollup.commercetools.now.sh

@vercel vercel bot temporarily deployed to Preview January 29, 2021 17:08 Inactive
@vercel vercel bot temporarily deployed to Preview January 29, 2021 19:12 Inactive
@vercel vercel bot temporarily deployed to Preview January 29, 2021 20:28 Inactive
@vercel vercel bot temporarily deployed to Preview January 29, 2021 21:40 Inactive
@vercel vercel bot temporarily deployed to Preview January 31, 2021 13:50 Inactive
compiled-data
dist
dist-tarballs
public
packages/application-shell/test-utils
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test-utils is now a preconstruct entrypoint.

@vercel vercel bot temporarily deployed to Preview January 31, 2021 14:00 Inactive
Comment on lines 31 to 52
getJSON: function (cssFilePath, json) {
const compiledPaths = getCompiledPaths(cssFilePath);

if (!fs.existsSync(compiledPaths.dir)) {
fs.mkdirSync(compiledPaths.dir);
}

// This file contains the mapping between the class names referenced
// in the components and the compiled CSS selectors.
fs.writeFileSync(
`${path.join(compiledPaths.dir, compiledPaths.fileName)}.json`,
JSON.stringify(json),
{ encoding: 'utf8' }
);

// This file provides the type declaration for the JSON file created above.
fs.writeFileSync(
`${path.join(compiledPaths.dir, compiledPaths.fileName)}.json.d.ts`,
`/* eslint-disable prettier/prettier */
declare const styles: ${JSON.stringify(json)};
export default styles;`,
{ encoding: 'utf8' }
);
},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function essentially creates the JSON file that contains the mapping between the class names and the compiled selectors:

image

Comment on lines +72 to +68
fs.writeFileSync(
path.join(compiledPaths.dir, compiledPaths.fileName),
compiled.css,
{ encoding: 'utf8' }
);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here compiled.css is the actual compiled CSS code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will still be fingerprinted etc as it's loaded through webpack, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No really,this script is what we execute independently.

if (isAuthenticated)
return props.children({ isAuthenticated });
<>
<GlobalStyles />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Injected using Emotion Global

@@ -1,4 +1,3 @@
import type { SyntheticEvent } from 'react';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preconstruct doesn't like importing React types separately.

Comment on lines +4 to +9
const compiledStyles = {
global: fs.readFileSync(path.join(__dirname, './compiled/navbar.css'), {
encoding: 'utf8',
}),
jsonMap: require('./compiled/navbar.css.json'),
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the macro. We're simply returning the loaded CSS and JSON map.

Note that in theory we could have run postcss here as well (instead of using a custom script). However, the process is async and it would complicate things, as we want here things to be sync.

// https://babeljs.io/blog/2017/09/11/zero-config-with-babel-macros
import compiledStyles from /* preval */ './navbar.styles';

const styles = compiledStyles.jsonMap;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the code, we reference the class names using the JSON map.

Bonus now is that we also get autocompletion and type checks.

Comment on lines +545 to +549
<Global
styles={css`
${compiledStyles.global}
`}
/>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we pre-compiled the CSS styles, we need to inject them globally here.

@emmenko
Copy link
Member Author

emmenko commented Jan 31, 2021

The styles in the bundle are now like this:

image

Which is actually more or less the same as we had before with Rollup compiling CSS modules.

Copy link
Contributor

@tdeekens tdeekens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I can follow along from a setup perspective. Macros are somewhat magical to me still :)

"build:bundles:watch": "yarn build:bundles -w",
"build:test-utils": "cross-env NODE_ENV=development rollup -c ../../rollup.config.js -i ./src/test-utils/index.ts",
"build:typings": "cross-env tsc -p tsconfig.declarations.json --emitDeclarationOnly --declarationDir dist/typings",
"postbuild:typings": "echo \"export * from '../dist/typings/test-utils';\" > test-utils/index.d.ts"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

Comment on lines +72 to +68
fs.writeFileSync(
path.join(compiledPaths.dir, compiledPaths.fileName),
compiled.css,
{ encoding: 'utf8' }
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will still be fingerprinted etc as it's loaded through webpack, right?

@vercel vercel bot temporarily deployed to Preview January 31, 2021 21:11 Inactive
@adnasa
Copy link
Contributor

adnasa commented Feb 1, 2021

I don't even know what a macro is (except via Sass or Less) 😄

@tdeekens
Copy link
Contributor

tdeekens commented Feb 1, 2021

I don't even know what a macro is (except via Sass or Less) 😄

This article was quite nice: https://kentcdodds.com/blog/write-your-own-code-transform

@vercel vercel bot temporarily deployed to Preview February 1, 2021 11:22 Inactive
@adnasa
Copy link
Contributor

adnasa commented Feb 1, 2021

It took a couple of more rounds of reading, and something I would have liked to see at the PR. but here is what I understood

  1. We pre-compile the css
  2. We expose the css via a macro as compiledStyles
  3. the macro exposes two values, jsonMap and global
  • the jsonMap is a list of references to the compiled css classes (.foo)
  • the global are value that we inline into a global component, hence the the css classes and their rules will be applied

questions

  • question regarding the jsonMap, since we reference the only the css classes, where are their rules injected? (can't find the code that does the injection pointing to a complied css file)
  • when to use what?
  • Fo the moment, the application-provider hosts the global styles, namely reset and grid.
    • If we need to introduce more global styles further down the road, we add them into <GlobalStyles />?

side note

  • is the application-provider the right place to host global styles?

@vercel vercel bot temporarily deployed to Preview February 1, 2021 12:35 Inactive
@emmenko
Copy link
Member Author

emmenko commented Feb 1, 2021

question regarding the jsonMap, since we reference the only the css classes, where are their rules injected? (can't find the code that does the injection pointing to a complied css file)

See #2018 (comment)
The compiled CSS is injected using Global, which is what the global string contains. The code is here.

when to use what?

What what?

PS: note that this is a "temporary" solution, as we want to use Emotion eventually and no CSS modules. See #1884

If we need to introduce more global styles further down the road, we add them into ?

Yes

is the application-provider the right place to host global styles?

Yes, because it's the only component that is always rendered (see our internal auth app, which does not use AppShell).

@adnasa
Copy link
Contributor

adnasa commented Feb 1, 2021

The compiled CSS is injected using Global, which is what the global string contains. The code is here.

aah... nice that is actually elegant.


PS. can't say it's genius, however 🤷🏽‍♂️ :trollface: #slap

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants