Giter VIP home page Giter VIP logo

bluprint's Introduction

Reuters


bluprint logo


Dead-easy application scaffolding and CLI.


npm version Reuters open source software

Why this?

Reusing good code is the easiest way to speed up your development time and share solid conventions across your team.

If you've used Yeoman or similar tools to build shareable templates, bluprint has a few advantages:

  • It's far simpler for anyone on your team to create a template regardless of what language they're comfortable working in.
  • Your bluprint actually looks like the boilerplate you'll start from, so it's easy to tell what your bluprint will do at a glance.
  • bluprint's CLI pulls your template directly from GitHub, which means distributing your code is as easy as git push.

bluprint is designed to be a fast, accessible and highly composable tool to build out a library of reusable code.

What's it do?

A bluprint is any GitHub repository you want to use as a template to scaffold out a new project. Just adding a .bluprintrc JSON file to the root of a repo makes it a bluprint.

Register bluprints you use regularly with the CLI.

When you start a new project, the CLI will download the latest tarball of your files from GituHub (public or private repos supported) and scaffold out your local directory. Then it will apply any custom actions defined in your .bluprintrc to transform your files.

bluprint actions can do complex things like move or rename files and folders, execute shell commands, ask users for input, render files through a templating engine to customize them for each project and more.

bluprint parts let you split your template into segments that can help you keep files synced between projects already underway and your bluprint.

Quickstart

Install

yarn global add @reuters-graphics/bluprint

or

npm install -g @reuters-graphics/bluprint

This package supports the latest active versions of node.

Create a new bluprint

Creating a bluprint from existing code is as easy as adding a .bluprintrc JSON file to the root of your project and pushing to GitHub.

Use the new command to create your .bluprintrc in the root of the project you'd like to templatize:

bluprint new

That creates your .bluprintrc.

{
  "bluprint": "^0.0.1",
  "name": "My bluprint",
  "category": "",
  "actions": []
}

Add a category if you like, which will be used in the CLI to group similar bluprints together.

actions can be added to process your bluprint files after scaffolding your directory. Read more in Actions and check out the example bluprint to see what you can do.

Commit your project to GitHub with the .bluprintrc file.

Add a bluprint to your CLI

To use your new bluprint, add it to your CLI using its GitHub repository.

bluprint add <github repo>

Your GitHub repo can be referenced using any of:

  • the URL

    https://github.com/reuters-graphics/my-bluprint

  • the ssh connect string

    [email protected]:reuters-graphics/my-bluprint.git

  • the GitHub user/project shortcut

    reuters-graphics/my-bluprint

If your repository is private, you can make sure the CLI has permission to access it by either:

  1. Exporting a personal access token as the environment variable GITHUB_TOKEN:
export GITHUB_TOKEN=<your personal access token>
  1. Adding your personal access token directly to the CLI:
bluprint token <your token>

Using bluprints with the CLI

Create a fresh directory for your new project.

mkdir my-new-project
cd my-new-project

Scaffold your project from one of your bluprints:

bluprint start

The CLI will ask you to pick a bluprint and will guide you through providing any other information your bluprint needs to finish scaffolding your project.

You can also pass a GitHub repo containing a bluprint directly to this command:

bluprint start <github repo>

Remove a bluprint from your CLI

If you need to remove a bluprint from your CLI, you can:

bluprint remove

Cloning repos

You can also use the CLI to directly clone a GitHub repo:

bluprint clone <github repo>

You can clone any GitHub repo using this command, including private repos (with a GitHub personal access token), regardless of whether the repo has a .bluprintrc config or not.

When using the clone command, all actions will be ignored in your .bluprintrc.

CLI commands

bluprint [command]

Commands:
  bluprint add [repo]           Add a new bluprint to your CLI
  bluprint clone <repo>         Clone a repo with bluprint
  bluprint new [name]           Create a new .bluprintrc file
  bluprint remove [bluprint]    Remove a bluprint from your CLI
  bluprint start [bluprint]     Start a new project from a bluprint
  bluprint token [accessToken]  Add a GitHub personal access token to your CLI

Options:
  --version  Show version number
  --help     Show help

⚙️ Actions

Actions let you orchestrate complex transformations of your files after your repository is pulled down. Each action is an object added to the actions array in your .bluprintrc file.

You can define as many actions as you like and they will be run in sequence when anyone uses your bluprint.

{
  "bluprint": "^0.0.1",
  "name": "My bluprint",
  "category": "",
  "actions": [{
    "action": "prompt"
    // Runs first...
  }, {
    "action": "render"
    // Runs second...
  }, {
    "action": "log"
    // Runs last...
  }]
}

Check out the example bluprint to see what you can do with actions.

Here are the actions the CLI currently supports:

execute

{
  "action": "execute",
  "cmds": [
    ["yarn"],
    ["git", ["init"]]
  ],
  "silent": true
}

This action executes arbitrary commands.

cmds are arrays passed as arguments to a synchronous child process.

In each array, the first item is a string representing the command. The second is an array passed to the command as arguments.

silent is an optional boolean argument that when true will suppress the output of the child process.

log

{
  "action": "log",
  "msg": "Finished creating your project!"
}

This action logs messages to your bluprint's users.

msg is a string.

The message is processed as a chalk tagged template literal so you can easily add a bit of color to your messages:

"Welcome to your {green new} project!"

You can also reference any context your users supplied in a previous prompt action using mustache template syntax:

"Scaffolded your new project named {{ projectName }}!"

move

{
  "action": "move",
  "paths": [
    ["from/", "to/"],
    ["moveme.md", "moved.md"]
  ],
}

This action lets you move or rename files or directories.

paths is an array of arrays. Each inner array represents a move action. The first item in a move action represents the file or directory to be moved and the second, the destination. You can use the answers from a previous prompt action in the destination string with mustache template syntax:

["moveme/code.js", "{{ someAnswer }}/code.js"]

copy

{
  "action": "copy",
  "paths": [
    ["from/", "to/"],
    ["copyme.md", "copied.md"]
  ],
}

This action lets you copy files or directories.

paths is an array of arrays. Each inner array represents a copy action. The first item in a copy action represents the file or directory to be copied and the second, the name of the copied file or directory. You can use the answers from a previous prompt action in the copied name string with mustache template syntax:

["copyme/code.js", "{{ someAnswer }}/code.js"]

prompt

{
  "action": "prompt",
  "questions": [{
    "type": "text",
    "name": "projectName",
    "message": "What should we call this project?"
  }],
}

This action lets you ask your users for more information that is then available to subsequent actions.

questions is an array of prompts.js questions. The name of each question will be available in all actions that use templating syntax, like render, log, move and regexreplace.

regexreplace

{
  "action": "regexreplace",
  "files": [
    "README.md"
  ],
  "replace": [
    ["color", "colour"],
    ["([0-9]{2})\/([0-9]{2})\/([0-9]{4})", "$2.$1.$3", "g"]
  ]
}

This action allows you to make replacements in files using regular expressions. The files array is a list of files in which to replace text. The first item in each replace array is a regular expression string; the second, a replacement string, which can use regex capture groups; and, optionally, a third to override regex flags (defaults to gm). You can also use the answers from a previous prompt action in the replacement string with mustache template syntax:

["^Name: .+$", "Name: {{ userName }}"]

remove

{
  "action": "remove",
  "paths": [
    "dir/*",
    "README.md"
  ],
}

This action removes files or directories.

paths is an array of glob strings relative to the root of your project directory.

render

{
  "action": "render",
  "engine": "mustache",
  "files": ["README.md"],
  "questions": [],
  "context": {
    "copyrightYear": "2020"
  }
}

This action overwrites files after passing them through a templating engine with custom context.

engine is the templating engine to use. Can be either "mustache" or "ejs".

files is an array of files to pass through the templating engine.

questions is an array of prompts.js questions. The name of each question will be available only in this action as context to your template.

context is an object of any additional context to pass to your templates.

Remember, any answers to previous prompt actions are also available as context to your templates. See the docs on mustache and EJS for more information on using their templating syntax in your files.

There are also a few extra utility functions provided to your EJS and mustache templates from the string package: camelize, capitalize, dasherize, humanize, latinise, slugify, titleCase and underscore.

In EJS, you'd use them like:

<%= slugify(myVariable) %>

... and in mustache ...

{{#slugify}}{{myVariable}}{{/slugify}}

Default context

Some actions -- log, move, regexreplace & render -- are given default context variables you can use. These variables include:

  • year: full year at runtime, e.g., 2022
  • month: zero-padded month at runtime, e.g., 02
  • day: zero-padded date at runtime, e.g., 07
  • dirname: the name of the parent directory where bluprint is being executed at runtime, e.g., my-project-folder

You use them in string replacement operations, like:

{
  "action": "regexreplace",
  "files": ["my-file.txt"],
  "replace": [
    ["YYYY", "{{ year }}"],
    ["project-name", "{{ dirname }}"]
  ]
}

Conditioning actions on prompt values

All actions can be conditionally run based on the answer to a previous prompt by adding a condition key to the action object:

[
  {
    "action": "prompt",
    "questions": [{
      "type": "text",
      "name": "userName",
      "message": "What's your name?"
    }]
  },
  {
    "action": "log",
    "msg": "Hi, Jon!" ,
    "condition": ["userName", "Jon"]
  }
]

The first item in the array is the string object path of the prompt variable name you want to test, the second is the value it should be. Any action that fails a condition test will be skipped.

{
  "condition": ["myPromptVar", "some value"]  
}

You may also condition an action on multiple prompt values:

{
  "condition": [
    ["myPromptVar", "some value"],
    ["myOtherVar", true]
  ]
}

📦 Parts

Sometimes it's handy to use just a part of your bluprint. For example, you might want to update a few files in a project to sync up with changes in the upstream bluprint.

Parts make it possible to give your users the option to overwrite some files in a project scaffolded by your bluprint. Just add a parts object to your .bluprintrc. Each key should be the name of a part. The value should be an array of glob strings matching the files in your project that belong to that part.

{
  "bluprint": "^0.0.1",
  "name": "My bluprint",
  "category": "",
  "actions": [],
  "parts": {
    "Config files": [
      "config/*",
      ".tasksrc"
    ],
    "JS components": [
      "src/js/components/**/*",
      "src/scss/component-styles/*"
    ]
  }
}

Now, when a user uses your bluprint, they'll be asked if they want to use the whole bluprint or just a part. If they choose a part, files matching any glob will be copied into the project directory. Those that don't will simply be ignored.

If you need to, you can make any action conditional on the part a user chooses using the bluprintPart context variable.

{
  "condition": ["bluprintPart", "Config files"]  
}

If you want to run an action only when the whole bluprint is used, you can test for null:

{
  "condition": ["bluprintPart", null]  
}

Merge JSON files

You can add a mergeJson flag to your .bluprintrc to attempt to merge (using lodash) any existing JSON files with JSON files in a part that would overwrite them.

{
  "bluprint": "^0.0.1",
  "name": "My bluprint",
  "category": "",
  "actions": [],
  "parts": {},
  "mergeJson": true
}

Properties in the part's JSON file will take precedence over the existing file.

So say you have an existing package.json like:

{
  "dependencies": {
    "react": "14.0",
    "lodash": "3.0"
  }
}

... and your bluprint is updated to React 16 and adds d3 to the dependencies. Running a part that includes package.json with mergeJson set to true would result in these dependencies:

{
  "dependencies": {
    "react": "16.0",
    "lodash": "3.0",
    "d3": "5.0"
  }
}

Developing

See the developing doc.

Credits

The bluprint logo was created by MHD AZMI DWIPRANATA and is part of The Noun Project, available via creative commons license.

bluprint's People

Contributors

dependabot[bot] avatar hobbes7878 avatar palewire avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

doc22940 jarib

bluprint's Issues

Merge JSON files option

Would be good to offer a way to say we should merge some JSON files, for example package.json, which would be most useful in parts.

[Q] can I use bluprints afterward ?

Hi,

Sometimes (most ?) you need template to start a project but later, you may need other files from another bluprint. Can I use a bluprint in my working dir if it is not empty ? if it already used another bluprint previously ?

In those cases, how does it handle conflict if a downloaded file already exists in the working dir ? Is there an option to skip it, overwrite, merge ? I don’t want my readme file be overwritten for instance.


Does "action": "remove" run before copying the files in the working dir or after ?

New feature proposals

Thanks for this project, exactly what I've been looking for 👍

I would like to propose the following features. Would you accept PRs for any of these?

1. Read bluprint from a local directory

Perhaps I've missed something, but it seems any bluprint changes must be pushed to Github before you can test it? This is quite frustrating when developing new bluprints, especially since github.com seems to do a bit of caching that is hard to get around.

It would be nice to run bluprint start ../my-bluprint without having to commit every change.

Alternatively git archive to get a tarball or by adding support for git daemon to the hosted-git-info package.

2. Allow .blueprintrc.js

It would be useful to have the ability to execute JS inside this file. E.g. for my render actions I would like to have the current year in the context:

context: { year: new Date().getFullYear() } 

The only solution to this I've found is to switch from mustache to ejs and execute the JS line as part of the templates.

3. Allow templated execute actions

E.g.:

"cmds": [ ["git", ["remote", "add", "origin", "{{ org }}/{{ year }}-{{ slug }}" ]] ] 

4. Custom actions from the template repo

Not too sure about this one, but the ability to have custom actions in the bluprint repo itself would really make anything possible in terms of customization.

Consider adding some extra default context variables

Right now, we add bluprintPart as a default context variable: https://github.com/reuters-graphics/bluprint/blob/master/lib/actions/index.js#L52

Thinking a few extra defaults would be handy. Specifically, date variables come up a lot like the current year at runtime. Maybe also a variables that reflects the file system, like the parent directory where bluprint is running.

Can feel out what else is useful, but start by adding:

  • year - YYYY
  • month - MM
  • day - DD
  • dirname - parent directory name

Bluprint inheritance

Opening for discussion...

One of the main goals of bluprint is to make it easy to spin off small, reusable customizations. So if I have a set of components I like to use in our graphics rig, I can create a Jon's own-brand graphics rig bluprint. Great! But now updates are happening in the main bluprint I spun off from. How do keep my bluprint up-to-date?

Parts might be an answer. If the updates happened in a part of the parent bluprint, I can run it in my child and pick up that update. That might work for me as a once off, but it doesn't scale terribly well, and it assumes you know you need to update, which cuts against the idea of pushing changes to users that's so nice about bluprint.

Seems like a better system would be to register what bluprint a child inherits from, capturing the version of the parent as a dependency. Maybe we even want to always overwrite specific files in the child with ones from a parent.

Maybe the easier way to think about it is ... How would you write the bluprint config to specify that it inherits from another bluprint?

Any thoughts? @MatthewWeberTR @fcage @chriscanipe

Cannot add bluprints for repositories that don't use `master` as the default branch

This bug can be replicated with the following code. After inspecting the error more closely, I believe it is caused by the bluprint repository having its default branch set to main. It appears that the version of hosted-git-info employed by package.json needs to be upgraded to support the preferred nomenclature, the parlance of our times. I will submit a pull request that attempts to resolve the bug shortly.

$ bluprint add https://github.com/datadesk/baker-example-page-template

bluprint v0.6.6

bluprint:  Added bluprint baker-example-page. Run bluprint start to start a new project.

$ mkdir main-branch-bug
$ cd main-branch-bug                                          
$ bluprint start baker-example-page

bluprint v0.6.6


bluprint: ERROR Connection error attempting to access bluprint repo.

bluprint start [bluprint]

Start a new project from a bluprint

Positionals:
  bluprint  The bluprint to use                                         [string]

Options:
  --version  Show version number                                       [boolean]
  --help     Show help                                                 [boolean]

Error: HTTP error 404: Not Found
    at ClientRequest.<anonymous> (/home/palewire/.nodenv/versions/16.19.0/lib/node_modules/@reuters-graphics/bluprint/dist/index.js:963:13)
    at Object.onceWrapper (node:events:628:26)
    at ClientRequest.emit (node:events:513:28)
    at HTTPParser.parserOnIncomingClient (node:_http_client:693:27)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:128:17)
    at TLSSocket.socketOnData (node:_http_client:534:22)
    at TLSSocket.emit (node:events:513:28)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
    at TLSSocket.Readable.push (node:internal/streams/readable:228:10)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.