Skip to content

Latest commit

 

History

History
308 lines (235 loc) · 11.6 KB

README.md

File metadata and controls

308 lines (235 loc) · 11.6 KB

Module Templates

A flexible VSCode extension for creating (and using) file/folder templates. If you're bored of typing the same boilerplate stuff every time you create a new thing, this might be for you.

There are no templates included with this extension.

Screen capture

Use

The plugin is used in one of two ways:

  • Right click in the file explorer and select New From Template. The template files will be output here.

  • Run New From Template using the command palette. Files will be output relative to the current workspace folder (and the defaultPath option if set)

Templates

Templates are defined in VS Code settings. If your code is shared by many people it can be nice to put templates in workspace settings (.vscode/settings.json) so that they can be used by everyone!

Below is a config example, showing how a template for a React component can be defined. The example template defines a folder, a .jsx file and an .scss file.

{
  "module-templates.engine": "handlebars",
  "module-templates.templates": {
    "react-component": {
      "displayName": "React component",
      "defaultPath": "source/components",
      "folder": "{{kebab name}}",
      "questions": {
        "name": "Component name",
        "className": "HTML class name"
      },
      "files": [
        {
          "name": "{{kebab name}}.jsx",
          "content": [
            "import React from 'react';",
            "",
            "const {{pascal name}} = () =>",
            "  <div className=\"{{kebab className}}\" />",
            "",
            "export default {{pascal name}};"
          ]
        },
        {
          "name": "{{kebab name}}.scss",
          "content": [".{{kebab className}} {}"]
        }
      ]
    }
  }
}

Configuration API

module-templates.engine

Optional. "handlebars".

This option used to support a "legacy" option, which is now deprecated. If you haven't set this option, or set it to "legacy" you'll get a warning each time you use a legacy template. Support for legacy templates will be entirely dropped in the next major version. Set this option to "handlebars" and see below for how to convert legacy template syntax into Handlebars syntax.

module-templates.handlebarsConfig

Optional. string

Path to a javascript file that can be used to configure Handlebars. The javascript file should export a function or an object.

If the file exports an object, it's passed to Handlebars as runtime options. This can f. ex. be used to add custom helpers and partials.

Note that when you edit this file, you might need to reload VSCode in order for the changes to take effect.

module.exports = {
  helpers: {
    myHelper: () => "Hello!",
  },
};

If the file exports a function, that function will be called with Handlebars. This enables configuration of the Handlebars runtime. This can also be used to load some third party helper libraries that register themselves to the Handlebars runtime. The function can also return a runtime options object.

module.exports = handlebars => {
  handlebars.registerPartial("foo", () => "Hello!");

  return {
    helpers: {
      myHelper: () => "Hello!",
    },
  };
};

module-templates.templateFiles

Optional. string[]

A list of file paths to load templates from. The files should be named like <some-name>.module-templates.json (f ex my-project.module-templates.json). The .module-templates part of the file name is optional but strongly recommended, because you get schema validation in VSCode when editing those files.

Paths can either be absolute, relative to the home directory (~) or relative to .vscode/settings.json. Relative paths will not work in user settings.

Example:

{
  "module-templates.templateFiles": ["./my-templates.module-templates.json"]
}

module-templates.templates

An object whose keys are id strings (which can be used in other templates, see extends) and values are template objects. Templates have the following properties:

displayName

Optional. This name is used when selecting a template to create from. If this property is empty, the template will not be shown in the template selector (this can be useful if you create templates that are just for inheritance).

defaultPath

Optional. A path relative to the workspace root. When running the extension from the command palette files will be output to this path. When running from the right-click menu this option has no effect.

extends

Optional. List of template ids (string). When set, the template objects corresponding to the given ids are merged into the current template (overriding left to right, current template last). Questions are also merged, and files are concatenated. In short, you get all properties, files and questions from the inherited templates. See below for example.

folder

Optional. If this is option is set, a folder is created using the name from the option. This field is a template; you can use any syntax supported by the template engine.

files

Required. A list of file templates. File templates are objects with the following properties:

  • name: Required. A name for the file to create (with file extension). Can also be a path (non-existing folders will be created). This field is a template; you can use any syntax supported by the template engine.
  • open: Optional. A boolean that indicates whether this file should be opened after creation or not.
  • content: Required unless contentFile is set. The template for the file to create, given as an array of strings.
  • contentFile: Required unless content is set. Path to a file to read the content template from. The file can have any extension and is read as a string. The path must be absolute, relative to home (~) or relative to the config file you're editing. Note that if you plan on using contentFiles with both user settings and workspace settings, the paths used in user settings must be absolute or relative to ~.

questions

Optional. A dictionary of questions to ask when using the template. The answers are used as data when rendering the template. The aswers are referenced in templates by their key in the questions object.

Question values can be one of three types:

  • string: The value is displayed as a label for the input box
  • object with properties:
    • displayName (string): Displayed as a label for the input box
    • defaultValue (string): A value that is used if no text is input
  • array of object (displayed as a selecttion menu). Properties:
    • displayName (string): Displayed as the name of the option
    • value (any value): The value to use when the value is selected

Note that the legacy engine only supports strings even if the value for array questions can be anything.

{
  "questions": {
    "name": "File name",
    "myQuestion": "Some description",
    "myOtherQuestion": [
      { "displayName": "A", "value": [1, 2] },
      { "displayName": "B", "value": [3, 4] }
    ]
  },
  "folder": "{{name}}",
  "files": [
    {
      "name": "{{name}}.md",
      "content": [
        "{{myQuestion}}", // Outputs the answer from the prompt
        "{{#each myOtherQuestion}}",
        "{{this}}",
        "{{/each}}"
      ]
    }
  ]
}

Composition / inheritance

Templates can be combined to create new ones. The id of a template can be referenced from other templates using extends (see extends option above for more technical details). When referencing a template ids in extends, you inherit all properties, questions and files from that template. You can inherit multiple templates. Inheritance is recursive, so you can inherit other templates that inherit something else and so on.

By omitting displayName from templates, you can create hidden templates that are only used to create other templates. In the example below, only "React component with SCSS" will be available when selecting templates.

{
  "module-templates.engine": "handlebars",
  "module-templates.templates": {
    "jsx-file": {
      "files": [
        {
          "name": "{{kebab name}}.jsx",
          "content": [
            "const {{pascal name}} = () => null;",
            "export default {{pascal name}};"
          ]
        }
      ]
    },
    "scss-file": {
      "files": [
        {
          "name": "{{kebab name}}.scss",
          "content": [".{{kebab name}} {}"]
        }
      ]
    },
    "react-component": {
      "extends": ["jsx-file", "scss-file"],
      "defaultPath": "source/components",
      "displayName": "React component with SCSS",
      "folder": "{{kebab name}}",
      "questions": { "name": "Component name" },
      "files": []
    }
  }
}

Legacy templates

Legacy templates are deprecated. Please enable the handlebars engine and see below for how to convert legacy templates to handlebars templates.

  • {<question-key>.raw} -> {{<question-key>}}
  • {<question-key>.pascal} -> {{pascal <question-key>}}
  • {<question-key>.kebab} -> {{kebab <question-key>}}
  • {<question-key>.camel} -> {{camel <question-key>}}
  • {<question-key>.snake} -> {{snake <question-key>}}

Handlebars templates

Enable Handlebars templates by setting module-templates.engine to "handlebars". See the Handlebars documentation for syntax. The answers object is passed directly to Handlebars as view data.

Helpers

This plugin defines a few Handlebars helpers that you can use in templates. If you want more helpers, consider submitting a PR!

eq

Check whether thing A is equal to thing B. In the following example, Yes! will be output if the answer is yes and No! otherwise.

[
  "{{#eq answer \"yes\"}}Yes!{{else}}No!{{/eq}}",
  "{{#if (eq answer \"yes\")}}Yes!{{else}}No!{{/if}}"
]

Casing helpers

Casing helpers are used to transform answers into a specific casing convention. F. ex. to convert the answer "some name" into "SomeName" (PascalCase):

["{{pascal answer}}"]
Name Input Output
camel some text someText
capital some_text Some Text
constant some text SOME_TEXT
lower SOME_TEXT some_text
kebab some text some-text
pascal some text SomeText
sentence some_text Some text
snake some text some_text
upper some-text SOME-TEXT
words some_text some text

Note that handlebars helpers can be combined, which you can use to create some unsupported casing conventions. F. ex. to ouput text in COBOL-CASE you can use {{upper (kebab input)}}, or to get UPPERCASE WORDS use {{upper (words input)}}.

Context object

For Handlebars templates this extension exposes a context object (in the global scope) that contains some metadata that might be useful. Note that if you have a question with id "context", the answer to that question will replace the context object.

Example:

{{context.template.displayName}}
type Context = {
  // Contents of the clipboard:
  clipboard: string | undefined;
  // The data for the selected template:
  template: Template;
  vscode: {
    // The item that was right-clicked in the file explorer (if any):
    clickedItem: {
      path: string | undefined;
    };
    // The currently open document (tab):
    currentDocument: {
      name: string | undefined;
      path: string | undefined;
    };
    workspace: {
      name: string | undefined;
      path: string | undefined;
    };
  };
};