npm package: @skypager/helpers-sketch
Description: skypager sketch design document helper
Author: Jon Soeder
Latest version: 1.3.1

# Sketch Helper 

Turn a designer's `.sketch` files into JavaScript modules.

The Sketch Helper class is an abstract representation of a `.sketch` file produced by Sketch App.  When Skypager's runtime is extended with this helper,
you are able to use it as if it was a module you required.

This helper works in node, and in the browser (provided you use a webpack loader, or the helper itself to generate the sketchtool JSON metadata)

Given a path to a `.sketch` file, the Sketch Helper can load any level of detail about the document's internal object model, provided you can obtain it via the `sketchtool` CLI

It should also be able to export `.png` and `.svg` assets.

With this metadata, it is possible to use `.sketch` files to build an object graph that is organized the way a Designer / Sketch User  
organizes their design.  If the Designer uses naming conventions for their pages, artboards, and layers , this object graph can be easily navigated, making it possible to do all sorts of crazy things!

[API Docs](docs/api/Sketch.md)

## Usage

To enable the sketch helper in the skypager runtime, just `use` it the way you would any other extension

```javascript
const runtime = require('@skypager/node')
  .use(require('@skypager/helpers-sketch'))
```

If you want to build a custom runtime module which automatically has the sketch helper enabled for any consumers

```javascript
import runtime from '@skypager/node'
import Sketch, { attach } from '@skypager/helpers-sketch'

export default runtime.use({ attach })
```

## Example

The following script can run in node, you need `sketchtool` in your path.

```javascript
// using the node runtime, we can read the sketchfiles and use sketchtool directly.
const runtime = require('@skypager/node')
// tell the runtime to use the sketch helper so that we can create modules to represent them
runtime.use(require('@skypager/helpers-sketch'))
// create an instance of the sketch helper by giving us a name for it, and telling us the path
const sketch = runtime.sketch('my-sketch-doc', { path: '/path/to/file.sketch' }) 

main()

async function main() {
  // returns an array of layer objects
  await sketch.listAllLayers().then(layers => console.log({ layers }))
  
  // returns an array of artboard objects
  await sketch.listAllArtboards().then(artboards => console.log({ artboards }))
  
  // if your names follow a naming convention, you can use a route pattern to extract them as attributes 
  await sketch.listAllArtboards({
    namePattern: ':category/:subcategory/:artboardName'
  }) // => [{ category: 'mobile', subcategory: 'darkTheme', artboardName: 'HomeScreen', ...restOfSketchToolArtboardAttributes }]
  
  await sketch.listAllLayers({
    namePattern: ':region/:layerName'
  }) // => [{ pageName: 'HomeScreen', region: 'TopNav', layerName: 'CurrentUser', ...restOfSketchToolArtboardAttributes }]
  
  // get literally everything there is about the document
  await sketch.dump()
}
```

In this example, we know about the path to the sketch file, and we can create it by passing it as an argument.

If you wanted to just discover all of the sketchfiles in your current project folder

```javascript
main()

async function main() {
  await runtime.sketches.discover()
  console.log(runtime.sketches.available)
}
```

then you could access them with something like:

```javascript
const webappSketch = runtime.sketch('test/fixtures/WebApplication')
const styleguideSketch = runtime.sketch('test/fixtures/StyleGuide')
```

### Webpack Loader

[@skypager/helpers-sketch/sketchtool-loader](./sketchtool-loader.js) can be used to process `.sketch` files and turn them
into JSON structures that can be used to hydrate the Sketch helper instances in the browser, with the same metadata we rely on `sketchtool` to provide us with 
directly in the node runtime. 

This will enable you to build, for example, a React App whose state is powered by a Sketch Helper instance.

- TODO: Webpack Config Example

## Subclassing Example

The Sketch Helper class by itself is pretty low-level.  It provides an easier way of navigating the `sketchtool` JSON dumps.

The real value of this helper will come through subclassing it in a way which matches the mental model of the designer who created the `.sketch` file.

For example, say you had a `.sketch` template which consisted of an artboard that had a color palette, and typography display.  

If the designer names and organizes the layers in a way you both agree on, you can create a `Theme` subclass which might generate CSS or less files in your project.

```javascript
import Sketch from '@skypager/helpers-sketch'

export default class ThemeSketch extends Sketch {
  // assuming layer names:
  //  - typography/mainHeading
  //  - typography/subHeading
  //  - typography/copy
  generateTypographyRules() {
    const { filter } = this.lodash
    const typeLayers = filter(this.layers, { category: 'typography' })
    // need to write this function to find the values in the sketch metadata
    return typeLayers.map(findFontNameSizeAndWeight)
  }  

  // assuming layer names:
  //  - color/primary
  //  - color/secondary
  generateColorRules() {
    const { filter } = this.lodash
    const colorLayers = filter(this.layers, { category: 'color' })
    // need to write this function to find the values in the sketch metadata
    return colorLayers.map(findColorNameAndValues)   
  }
}

// now your runtime can load an entire collection of themes 
export function attach(runtime) {
  const { Helper } = runtime
  Helper.registerHelper('theme', () => ThemeSketch)

  Helper.attach(runtime, ThemeSketch, {
    registryProp: 'themes',
    lookupProp: 'theme'
  }) 
}
```

Now let's say you have a few theme sketch files in a folder

```javascript
import runtime from '@skypager/node'
import * as ThemeSketch from './ThemeSketch'

runtime.use(ThemeSketch)

const darkTheme = runtime.theme('DarkTheme', {
  path: '/path/to/dark.theme.sketch' 
})

const lightTheme = runtime.theme('LightTheme', {
  path: '/path/to/light.theme.sketch' 
})

darktheme.generateColorRules().then(writeThemToLessFilesOrSomething)
lightTheme.generateColorRules().then(writeThemToLessFilesOrSomething)
```

## Future Plans

- Sketch Helper instances are observable (i.e. they have a `state` property that is a mobx observable) which means we can keep their state in sync with Sketch App through their plugin interface, or whenever the file is saved (easiest in electron.)