Skip to content

Creating a Web Scaffold

Dave Jeffery edited this page Jan 9, 2018 · 9 revisions

From v0.12, Pingy CLI adds support for Web Scaffolding.

Why?

Existing scaffolding tools like Yeoman use the CLI as their exclusive interface. A richer interface can open much more possibilities for a better scaffolding experience. As a user interface, the web browser is one the richest and most accessible interface available, so why not use that instead? There are a ton of examples why it's better but here's an obvious one: Look how easy it is to choose from dozens of different template designs in the gif below! This kind of interaction just isn't possible in a CLI.

Pingy Web Scaffold Animation

How do I use a Web Scaffold.

Run pingy scaffold <name-of-scaffold> and follow the instructions. It's that easy! Check out pingy-scaffold-bootstrap for more specifics about how to use the Bootstrap scaffold. You can also read more about the scaffold command here.

How do I create a Web Scaffold?

So, you want to create a Web Scaffold that others can use to scaffold their own web project? Awesome!

If you get stuck along the way then you can either ping me (@DaveJ) in the Gitter chat channel or create a new issue.

High-level overview

There are really only two things that you need to know about to create your own Web Scaffold, they are:

  1. The pingy-scaffold.json file.
  2. The pingy.scaffold(options) javascript function.

You first create a pingy-scaffold.json that contains your base scaffold options. Then, you call pingy.scaffold(options) which contains options that extend the base scaffold options. So, essentially the two set of scaffold options are merged.

Scaffold Options

The scaffold options object has the following keys:

  • name (string): Will use name in package.json if omitted
  • description (string): Will use description in package.json if omitted
  • web (boolean): true if this is a Web Scaffold
  • dependencies (object)
  • devDependencies (object)
  • pingyJson (object)
  • files (array of objects: [{input, output, vars, includes}])

You will need to read more about the scaffold options object here.

Typical flow

  1. User runs pingy scaffold <your-scaffold-name>

  2. Pingy will clone (or pull) your scaffold from git and cache it locally (in ~/.pingy/scaffolds/<sha-of-git-url>)

  3. Pingy will read pingy-scaffold.json and store your base options

  4. Pingy will launch a local server and inject some JS into the page which makes the pingy.scaffold function available.

  5. You call pingy.scaffold(options), where options is an object containing you pingy scaffold options.

  6. The scaffolding server is destroyed and control is handed back to the terminal. pingy.scaffold(options) (above) returns a promise that is resolved when this completes.

  7. The user continues scaffolding from the terminal.

? You are about to scaffold the following files

  mysite/
  ├─┬ styles/
  │ └── main.scss
  └── index.html

❯ Yes, go ahead
  No, but continue
  No and abort

Example

Examples always help make things a bit more clear 😊.

pingy-scaffold.json

{
  "name": "test",
  "web": true,
  "description": "This is a test scaffold that just includes jQuery",
  "dependencies": {
    "jquery": "^3.2.1"
  },
  "pingyJson": {
    "exclusions": [
      {
        "path": "node_modules/**",
        "action": "exclude",
        "type": "dir"
      },
    ]
  },
  "files": [
    {
      "input": "favicon.ico",
      "output": "favicon.ico"
    }
  ]
}

index.html

<button id="scaffold">Scaffold</button>

<script>
  var btn = document.getElementById('scaffold')

  var options = {
    dependencies: {
      'slick-carousel': '^1.8.1'
    },
    pingyJson: {
      minify: false,
      exclusions: [{
        "path": "!node_modules/{slick-carousel}",
        "action": "exclude",
        "type": "dir"
      }]
    },
    files: [{
      input: 'templates/carousel.html',
      output: 'index.html'
    }]
  }

  btn.addEventListener('click', e => {
    pingy.scaffold(options)
  })
</script>

When the user clicks the scaffold button in the scaffold page (above) then a site will be scaffolded using the following options (pingy-scaffold.jsonpingy.scaffold(options)):

{
  name: 'test',
  web: true,
  description: 'This is a test scaffold that just includes jQuery',
  dependencies: {
    jquery: '^3.2.1',
    'slick-carousel': '^1.8.1'
  },
  pingyJson: {
    minify: false,
    exclusions: [
      {
        path: 'node_modules/**',
        action: 'exclude',
        type: 'dir'
      },
      {
        path: '!node_modules/{slick-carousel}',
        action: 'exclude',
        type: 'dir'
      }
    ]
  },
  files: [
    {
      input: 'favicon.ico',
      output: 'favicon.ico'
    },
    {
      input: 'templates/carousel.html',
      output: 'index.html'
    }
  ]
}

Tips

pingy.scaffold() function returns a promise

pingy.scaffold(options) returns a promise that is resolved when control is handed back to the terminal. The scaffold server is also destroyed at this stage so it is best to notify the user like in the screenshot below:

You may may also decide to close the browser window when it loses focus, something like this:

pingy.scaffold(options).then(() => {
  showFinishModal();
  window.addEventListener("blur", () => window.close());
});

Templating previews

On the Scaffolding server query params are passed into templates as contexts. This is handy for doing a preview before the site is scaffolded. Take the following example:

scripts/main.js

alert('Hello {{ foo }}')

If we call that file as http://localhost:61738/main.js?foo=World then it will be output as:

http://localhost:61738/scripts/main.js?foo=World

alert('Hello World')

Pingy also includes a helper function to allow you to create a templated URLs easily:

pingy.templatedURL('scripts/main.js', { foo: 'John Smith' })
// -> "scripts/main.js?foo=John%20Smith"
⚠️   Notes
Templated URLs do not currently work with includes. Create an issue on the issue tracker and I will add that functionality for anybody who wants it.