joshwcomeau/new-component

Add support for various styling solutions

Open

#4 opened on Sep 3, 2017

View on GitHub
 (2 comments) (0 reactions) (1 assignee)JavaScript (139 forks)github user discovery
enhancementhelp wanted

Repository metrics

Stars
 (759 stars)
PR merge metrics
 (PR metrics pending)

Description

It'd be awesome if new-component could include base styling.

There are two issues here: Coming up with the individual templates for each style solution, as well as the underlying architecture changes to make this possible.

Templates by style solution

styled-components

For styled-components, I spoke with Max on twitter and we think the best solution would be to update the template to be something like this:

// Whatever.js
import React, { Component } from 'react';
import styled from 'styled-components';

class Whatever extends Component {
  render() {
    return <Wrapper />;
  }
}

const Wrapper = styled.div`

`;

export default Whatever;

A near-identical template could be used for emotion (literally just swapping out styled-components for emotion/react, in the import line.

CSS modules

For CSS modules, the JS template would need to be updated to something like this:

// Whatever.js
import React, { Component } from 'react';

import styles from './Whatever.css';

class Whatever extends Component {
  render() {
    return <div className={styles.wrapper} />;
  }
}

export default Whatever;

We'd also need the CSS file, but it can be super minimal:

// Whatever.css
.wrapper {

}

Aphrodite

Finally, given that I work at KA, I'd like to add Aphrodite support! it would look like something of a mix of these earlier two solutions (all defined in the JS file, but using className and a styles object)


Architecture Changes

It actually seems like a pretty hard problem, figuring out how to add these new templates in a sustainable way. Because we support 3 different kinds of components (class, pure, functional), ideally I don't want to have to create 3 * 4 templates, for each possible permutation.

Thinking off the top of my head... One idea is to split the template into "quadrants", little mix-and-match pieces. You'd assemble the final template by picking the pieces you need, based on the type of component and style solution.

I imagine the different "areas" are:

  • React imports
  • Style imports
  • component definition
  • component render contents
  • below-component footer (for stuff like const Wrapper = styled.div)
  • exports (export default Whatever)

Here's a very rough first pass (this could be all wrong, just going with an idea :) )

// templates/react-imports.js
export default {
  functional: "import React from 'react';"
  class: "import React, { Component } from 'react';"
};

// templates/style-imports.js
export default {
  'styled-components': "import styled from 'styled-components';",
};

// templates/render-contents.js
export default {
  'styled-components': "<Wrapper />",
};

// templates/component-definition.js
// NOTE: This one is a bit tricky, since the `render-contents` are within it.
// Maybe something like this?

export default {
  // NOTE: ignore the spaces between ` ` `. Just not sure how to escape in markdown.
  functional = (ComponentName, renderContents) => ` ` `
    const ${ComponentName} = (
      return ${renderContents}
    );
  ` ` `
}

And then you'd just combine them to create the actual template:

import reactImportTemplates from './templates/react-imports';
import styleImportTemplates from './templates/style-imports';
import renderContentTemplates from './templates/render-contents';
import componentDefinitionTemplates from './templates/component-definition';

// Pretend these vars are defined from program input args
const componentName = 'Whatever';
const componentType = 'class';
const style = 'styled-components';

const renderContents = renderContentTemplates[style];

return ` ` `
${reactImportTemplates[componentType]}
${styleImportTemplates[style]}

${componentDefinitionTemplate[componentType](componentName, renderContents)}
` ` `;

This is of course a very rough draft, haven't thought through all the implications. Happy to discuss!

Going forward, I don't anticipate having that much time to spend on this (currently deeply involved in another side project, + my day job), so if anyone wanted to take this on, that'd be awesome. I want this to be a community project! Otherwise, I'll do my best to get to this in the next month or two :)

Contributor guide