joshwcomeau / new-component

⚛ ⚡ CLI utility for quickly creating new React components. ⚡ ⚛
MIT License
717 stars 131 forks source link

Add support for various styling solutions #4

Open joshwcomeau opened 7 years ago

joshwcomeau commented 7 years ago

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:

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 :)

morajabi commented 7 years ago

Good approach! I need to think more about it later (maybe at the time I wanted to implement it). Only thing I can think of now is we can use function + switch statement for those 4 generators, so we can group together similar inputs and return identical values for them at once (DRY).

// templates/render-contents.js
export default (style) => {
  switch (style) {
    case 'styled-components':
    case 'glamorous':
      return `<Wrapper />`;
  }
};

BTW, I'd be happy to implement it next week or sooner.

joshwcomeau commented 7 years ago

Yeah, switch statement makes sense to me :)

Awesome to hear you're up for implementing it! I'll assign you to the issue. No pressure of course, just assigning you so that any others know to coordinate if they want to take a stab at it :)