Skip to content

CLI that transforms Sass files to Emotion js files + jscodeshift transform that replaces className with css attribute

Notifications You must be signed in to change notification settings

rackai/sass-to-emotion

 
 

Repository files navigation

sass-to-emotion

There are two parts to this repo, the Sass part and the JavaScript part.

Sass

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.

Sass to JS example

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};
`;

Features

  • 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.
Domain specific (OSS release coming soon)
  • 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.

JavaScript

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

JS to Emotion example

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>
  );
}

Features

  • Coverts single and multiple classes in className, e.g className="foo" and className="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.

Notes

  • 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

About

CLI that transforms Sass files to Emotion js files + jscodeshift transform that replaces className with css attribute

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%