There are two parts to this repo, the Sass part and the JavaScript part.
This contains most of the heavy lifting, give it a glob of .scss files, it will parse them into a
PostCSS AST and generate an Emotion JS file. To use, clone the repo, run npm install
and
execute index.js like so:
../sass-to-emotion/index.js ./src/scss/**/*.scss
Please note yarn install
will not work for users outside of Domain because
it handles the package.json optional
dependencies differently to npm
and will error for a private package we use for internal transforms.
For example an input file foo.scss
:
@mixin ad-exact($width, $height) {
width: $width;
height: $height;
color: $fe-brary-colour-primary-dark;
}
.bar {
color: blue;
@include ad-exact(125px, 700px);
}
%message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.message {
@extend %message-shared;
}
.success {
@extend %message-shared;
border-color: green;
}
.button {
display: flex;
align-items: center;
justify-content: center;
color: $fe-brary-colour-primary-dark;
.foo {
display: block;
font-size: $fe-brary-font-h6-font-size;
}
}
Is turned into a foo.js
:
import { css } from '@emotion/core';
import { variables as vars } from '@domain-group/fe-brary';
function adExact(width, height) {
return css`
width: ${width};
height: ${height};
color: ${vars.colour.primaryDark};
`;
}
export const bar = css`
color: blue;
${adExact('125px', '700px')}
`;
const messageShared = css`
border: 1px solid #ccc;
padding: 10px;
color: #333;
`;
export const message = css`
${messageShared};
`;
export const success = css`
${messageShared};
border-color: green;
`;
export const button = css`
display: flex;
align-items: center;
justify-content: center;
color: ${vars.colour.primaryDark};
`;
export const foo = css`
display: block;
font-size: ${vars.font.h6FontSize};
`;
- Classes become exported Emotion tagged templates css``.
- Nested classes get brought to the top level if they are not nested in an ampersand which could imply state
specific classes e.g
&.is-selected
. - Sass vars not declared in the file are imported from a
../variables
file in the JS. - Handles Sass vars in multi value situations
border-bottom: 1px solid $foo-bar-baz;
. - Sass placeholders become css vars,
@extends
's then references these vars using Emotion composition. - Sass mixins become functions,
@include
's become function calls. - Multi class selectors
.foo, .bar {}
become two exports and use composition. - If multi class selectors are found
.foo.bar
, or a descendant combinator.foo .bar
, it takes the last as precedence. Could be a better way to do this? - Merges decls of multiple class blocks of the same selector.
- If a class selector is referenced inside an & block tree e.g &::hover, it leaves the CSS as is. It adds a FIXME comment above this class if it's also referenced outside an & block.
- If a class, mixin or placeholder is not referenced in a file, it is exported, and vice versa.
- If only one export is generated in the file it will use a
export default
. - Adds FIXME comment when Sass maths is detected so a developer can manually fix.
- Sass vars in rule blocks get moved to the root level and top of the JS file.
- Root level Sass comments become JS comments, comments in blocks stay as is.
- Warns in the CLI for files that need manual FIXME attention.
- Deletes
scss-lint
comments. - Prettier is applied to the JS output.
- fe-brary global vars, mixins and placeholders are imported from fe-brary, if detected on it's export object.
@include media('>=desktop')
uses the new fe-brary#media helper which has the same arg, it becomes${media('>=desktop')}
- Verbose
@media (min-width: $fe-brary-global-tablet-min-width)
media queries are modified to use the helper.
The JS part uses jscodeshift.
Clone this repo and link to the transform at sass-to-emotion/jscodeshift
when using the jscodeshift
CLI.
For example:
npm install -g jscodeshift
jscodeshift --parser flow -t ../sass-to-emotion/jscodeshift.js ./src/js
Changes a BEM like classname from className="baz-whizz__foo-bar"
to css={styles.fooBar}
and adds the JS import.
For example an input file like below is transformed in-place from:
import React from 'react';
export default function Bar() {
return (
<div>
<h1 className="listing-details__shortlist-button-icon">Hello</h1>
<p className="listing-details__shortlist-aasdas-adasda-icon">Foo Bar Baz</p>
</div>
);
}
to:
import React from 'react';
import * as styles from '../../styles/FIXME';
export default function Bar() {
return (
<div>
<h1 css={styles.shortlistButtonIcon}>Hello</h1>
<p css={styles.shortlistAasdasAdasdaIcon}>Foo Bar Baz</p>
</div>
);
}
- Coverts single and multiple classes in
className
, e.gclassName="foo"
andclassName="foo bar"
. - Searches the styles folder for a JS file that exports the correct export, only imports one styles file
import * as styles
, so bare in mind the first one wins. Manual modifications may be required.
- Think about detecting dep overrides
- Is taking the last in
.foo.bar
and.foo .bar
smart - Adding data-testid if class referenced in Enzyme/e2e tests
- Handle
classnames
package in jscodeshift