scf

command module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 14, 2024 License: MIT Imports: 2 Imported by: 0

README

SCF - Simple project and file scaffolding

Scaffold out local or remote (from GitHub) project templates.

Examples

Scaffold this repo's contents to ./testing directory.

scf dworthen/scf ./testing

Scaffold subdirectory.

scf dworthen/scf/scripts ./testing

Scaffold specific file to current directory.

scf dworthen/scf/README.md

Scaffold from private repo with GitHub PAT.

scf OWNER/PRIVATE-REPO --gh-token TOKEN...
# Or by setting SCF_GH_TOKEN
SCF_GH_TOKEN=TOKEN scf ...

Scaffold from specific GitHub reference (commit hash, branch name, or tag).

scf OWNER/REPO@HASH|BRANCH_NAME|TAG

Scaffold from local templates

scf ./templates/controller ./controllers/

Features

  • Scaffold repos, directories, and files from GitHub.
  • Scaffold local templates.
  • Scaffold private GitHub repos with the use of GitHub Personal Access Tokens.
  • User prompts.
  • File templating.
  • Commands. Run pre and/or post scaffolding commands, e.g., npm install.
  • Conditional scaffolding.

Inspiration

Inspired by degit with some differences:

  • SCF uses the GitHub API and does not depend on git.
  • Since it uses the GitHub API, SCF does not support other git hosts.
  • Supports private GitHub repos with the use of GitHub PATs.
  • Supports prompts, templating, and conditional scaffolding.
  • Commands. Run pre and/or post scaffolding commands, e.g., npm install.
  • Scaffolding from local sources.

Installation

NPM
npm install @d-dev/scf -g
Binaries
Windows
curl -sSfL https://raw.githubusercontent.com/dworthen/scf/main/scripts/install.ps1 | pwsh -Command -

This will install scf to ~/bin, be sure to export the location in the user or machine $PATH or use the -to flag to specify a download location.

Running the installer with additional flags:

curl -sSfL https://raw.githubusercontent.com/dworthen/scf/main/scripts/install.ps1 -o install.ps1 &&
pwsh -File install.ps1 -force -tag v0.0.1 -to ~/bin &&
rm install.ps1
Linux/Darwin
curl -sSfL https://raw.githubusercontent.com/dworthen/scf/main/scripts/install.sh | bash

This will install scf to ~/bin, be sure to export the location in the user $PATH or use the -to` flag to specify a download location.

Running the installer with additional flags:

curl -sSfL https://raw.githubusercontent.com/dworthen/scf/main/scripts/install.sh | bash -s -- --force --tag v0.0.1 --to ~/bin
From Source with Go
go install github.com/dworthen/scf@latest

Usage

Scaffold from remote GitHub repo.

scf REPO_OWNER/REPO[/OPTIONAL_SUB_DIRECTORY_OR_FILE] ./destination

Or from a local template

scf ./local/templates/some_template_dir_or_file ./destination
Options
  • --gh-token: GitHub Personal Access Token. defaults to $SCF_GH_TOKEN
Help
scf help

GitHub Personal Access Tokens

SCF uses the GitHub API which is rate limited to 60 requests per hour for unauthenticated guests. GitHub personal access tokens can be used to get around the rate limiting or to scaffold from private GitHub repos. A token can be provided through the use of the --gh-token flag or by setting the SCF_GH_TOKEN environment variable. The token needs repository read access.

Creating Project Templates

SCF supports scaffolding out any local directory or remote repo. Nothing special needs to be done to the source directory/files in order to be scaffoldable but SCF does suppot additional features such as user prompts, file templates, commands, and condtional scaffolding. These features are declared in a single config file, scf.config.json. If present in the source project, SCF will read in the config and act accordingly.

An example use case may be a javascript project template that support scaffolding out either a JavaScript project structure or a TypeScript project structure. When scaffolding out the files, SCF may prompt users on whether or not the project should use TypeScript and then conditionally scaffold out the appropriate files.

Prompts

Prompt Type
  • variableName (string) [Required]: The name of the variable for storing the user response.
  • type (string) [Required]: prompt | choose | password | yn | yesno
  • default (string|boolean|number) [Optional]: default value for the prompt. The type should correspond to the type of prompt.
  • message (string) [Required]: The message to prompt the user.
  • options (string[]) [Required if type == choose]: Options to present for the choose type prompt.
  • required (boolean) [Optional]: A boolean to indicate if an answer is required. Defaults to false
Example

REPO_OWNER/REPO/scf.config.json

{
  "prompts": [
    {
      "variableName": "projectName",
      "type": "prompt",
      "message": "Project Name",
      "required": true
    }
  ]
}

Running scf REPO_OWNER/REPO will then prompt the user for a project name.

The scf.config.json may appear in subdirectories. This is helpful if one wishes to house multiple project templates in a single repo. Running scf OWNER/REPO/sub/directory will use the scf.config.json config at that location if one is present. The scf.config.json file itself is never scaffolded out into the destination.

Templates

Prompts on their own are not that useful. SCF supports file templating with handlebars through the use of mailgun/raymond. Any file ending in .hbs will be processed as a handlebars file during scaffolding and made available the prompt responses. The .hbs extension is stripped from the output filename.

Example

Project Structure

|-- Project
    |-- templates
        |-- react-component
            |-- {{componentName}}.jsx.hbs
            |-- scf.config.json
    |-- src
        |-- components

./templates/react-component/scf.config.json

{
  "prompts": [
    {
      "variableName": "componentName",
      "type": "prompt",
      "message": "Component Name",
      "required": true
    }
  ]
}

./templates/react-component/{{componentName}}.jsx

export const {{componentName}} = function {{componentName}}() {
  return <></>
}

Running scf ./templates/react-component ./src/components within the project root will prompt the user for a component name and then use that name to create ./src/components/COMPONENT_NAME.jsx. This example demonstrates using handlebars for file naming as well as file contents. Handlebars can be used anywhere in the file path for dynamically naming directories or files.

This example also demonstrates scaffolding content from local templates. The templates directory could be tracked with source control allowing all devs on a team to scaffold out new files using the same templates.

Be sure to view the full list of built in handlebar helpers made available by mailgun/raymond.

Additional Handlebar Helpers

SCF provides a few additional handlebar helpers for convenience.

tif (ternary if)

This helper acts as a modified ternary if expression. Both the false and true values are optional. If the false value is not provided and the expression evaluates to false then an empty string is returned. If the truth value is not provided then the expression resolves to the variable value if "truthy" else an empty string.

// Resolves to "true" if someVar is truthy else "false"
{{tif someVar "true" "false" }}

// Resolve to "true" if someVar is truthy else ""
{{tif someVar "true"}}

// Resolves to the value of someVar if truthy else ""
{{tif someVar}}
ntif (the negation of tif)

Since handlebar expressions do not support negation, ntif is a convenience to check the negation of a variable.

// Equivalent to !someVar ? "true" : "false"
{{ntif someVar "true" "false"}}

// Like tif, the false and true values are optional
{{ntif someVar "ok"}}
{{ntif someVar}}

View the docs on what is considered "truthy" in handlebars.

Conditional Scaffolding

By default, scf will scaffold out all subdirectories and files present in the source to the destination except for the scf.config.json config file. The config supports an optional files field for specifying which files should be scaffolded and under which conditions.

File Specifier Type
  • files (Array<string|glob>) [Required]: An array of strings or globs specifying which files should be scaffolded.
  • condition (string|handlebars expression) [Optional]: A string or handlebars expression indicating when the associated files should be scaffolded. The files will only be scaffolded if the condition results in the string "true".
  • "workingDirectory" (string) [Optional]: A subdirectory relative to the scf.config.json file where the files list will be evaluated. The working directory itself will not be scaffolded out to the destination.
Example

Project Structure

|-- REPO_OWNER/REPO
    |-- templates
        |-- javascript-project
            |-- typescript
                |-- typescript files to scaffold...
            |-- javascript
                |-- javascript files to scaffold...
            |-- scf.config.json

REPO_OWNER/REPO/javascript-project/scf.config.json

{
  "prompts": [
    {
      "variableName": "useTypescript",
      "type": "yesno",
      "default": false,
      "message": "Use TypeScript?",
      "required": true
    },
  ],
  "files": [
    {
      "condition": "{{tif useTypescript}}",
      "workingDirectory": "typescript",
      "files": [
        "**/*"
      ]
    },
    {
      "condition": "{{ntif useTypescript}}",
      "workingDirectory": "javascript",
      "files": [
        "**/*"
      ]
    }
  ]
}

Running scf REPO_OWNER/REPO/templates/javascript-project ./my-project will scaffold out either the contents of the typescript or javascript directory to ./my-project/ depending on the answer to the prompt. Only the contents are scaffolded out, the typescript or javascript directories themselves will not be scaffolded since the workingDirectory field was provided.

If any of the handlebar expressions within file paths returns an empty string then that path and all subpaths is ignored and those files will not be scaffolded out. We can modify the above project structure example to

Project Structure

|-- REPO_OWNER/REPO
    |-- templates
        |-- javascript-project
            |-- {{tif useTypescript projectName}}
                |-- typescript files to scaffold...
            |-- {{ntif useTypescript projectName}}
                |-- javascript files to scaffold...
            |-- scf.config.json

REPO_OWNER/REPO/javascript-project/scf.config.json

{
  "prompts": [
    {
      "variableName": "projectName",
      "type": "prompt",
      "message": "Project Name",
      "required": true
    },
    {
      "variableName": "useTypescript",
      "type": "yesno",
      "default": false,
      "message": "Use TypeScript?",
      "required": true
    },
  ]
}

Now running scf REPO_ONWER/REPO/templates/javascript-project will create a directory within the current working directory with the name stored in the projectName prompt and either scaffold out the typescript or javascript contents based on the prompt response.

I personally don't recommend this approach for conditionally scaffolding since it can lead to longer, messier file paths and makes it less clear as to which files will be scaffolded out and when. I prefer explicitly specifying how files should be scaffolded out within the config file.

Commands

Commands Type
  • preScaffold (Array<string|handlebars expressions>) [Optional]: A list of commands to run after creating the destination directory but prior to scaffolding out content. Commands will run in the destination directory. Commands can be handlebar expressions, e.g., npm install {{packageName}}.
  • postScaffold (string[]) [Optional]: Same as preScaffold option.
Example

scf.config.json

{
  "prompts": [
    {
      "variableName": "packageName",
      "type": "prompt",
      "message": "Name a package to install",
      "required": true
    },
  ],
  "commands": {
    "postScaffold": [
      "npm install {{packageName}}"
    ]
  }
}

Examples

View the ./examples directory within this repo for more project template examples using prompts, handlebars templating, and conditional scaffolding.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
lib

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL