derekr / thoughts

Issues repo to track different projects and various thoughts
0 stars 0 forks source link

parse-component #1

Open derekr opened 9 years ago

derekr commented 9 years ago

We're doing this at nerd wallet in a crude way in order to dynamically build a page of react component examples.

screen shot 2015-06-13 at 3 46 57 pm

Would be great to start factoring out some of the logic involved with this in to individual modules. I want to start at the component parsing level and just make that clean.

Given a component module structure like this:

button
  - index.jsx (or button.jsx)
  - index.css (or less or sass or whatevs)
  - /examples
    - button-sizes.jsx
    - button-variants.jsx
  - package.json

Produce an object of file refs and meta data like:

require('parse-component/component')

{
  name: 'button', // parsed from package.json
  pkg: {
    version: '1.0.0', // key fields from package.json, but not the whole thing?
    ...
  },
  path: 'node_modules/button'
}

Then we could parse examples for the component to get an array of these objects:

require('parse-component/examples')

{
  key: 'button-variants',
  name: 'Button Variants', // do some simple manipulation of the key
  path: 'node_modules/button/examples/button-variants.jsx',
  component: 'button'
}

Trying to keep component and examples object small. Adding package.json data would be cool because then we can render things like contributor avatars next to the component… Easy to see who the owners/experts are for a given component… Things like that.

The hard part is rendering the example source, rendering the example and bundling any JS logic (like clicking a button could trigger an alert). We did this in our react-components project, but it slows the build time down (we render the guide as a static site). I have a branch using streams instead of async file reading to keep memory down as example/component source can get big for a large component library. Currently I attach a stream object to an example or component that is just a read stream of the source and a ref object which is the actual component code from require('button') in this case so I can render the example.

Wondering if it's best to make the ref/streaming stuff utilities (separate modules) from this module that takes example and component objects return ref/streaming for actual build time…

var parseExamples = require('parse-component/examples')
var readExample = require('read-component-example')

parseExamples('path/to/component', function (err, examples) {
  if (err) return console.error(err)

  // would iterate over all examples, but simple to explain one
  readExample(examples[0], function (err, exampleRefs) {
    // executes example code that you could pipe (string-to-stream)
    // to the guide
    var html = exampleRefs.ref()

    // pipe the example source code for rendering in to the guide
    exampleRefs.stream.pipe(guideStream)

    // Use something like browserify to get a bundle for executing 
    // the example clientside.
    // Could optimize this by rendering one bundle for all examples per 
    // component. Need a way of passing in the example container element for 
    // correct scoping.
    var bundleSrc = 'var example = require(' + examples[0].path + ')' + 
    'React.render(example, '#example-' + examples[0].key)'
    var b = require('browserify')({ entries: [bundleSrc] })
    b.bundle().pipe(destBundle)
  })
})

Note that a lot of this code is just sketching out concepts… probably tons of errors and definitely won't execute as is.