Skip to main content

Dynamic Files

caution

If you not using a version of templates above 1.1.0, please refer to the legacy documentation.

Dynamic files

Dynamic files are files that can be rendered with dynamic data, similar to how template engines work. You use a special syntax that gets replaced with the data you provide. This output is then saved into the file where the instance is being generated.

A dynamic file with these contents:

{{= tps.answers.name }} wants some more cake

will produce something like the following:

lino wants some more cake

Dont worry about the specifics, well touch more on the syntax later on this page.

Template engine

Our template engine was forked from the doT template engine. This provides great flexibility when creating your templates, along with additional features we provide to help with whitespace control.

tip

Keep in mind that all white space in the file will be preserved during render. At times, you may find yourself adding extra whitespace to make your template more readable. However, avoid doing this because the whitespace will remain in your new instance.

Use our playground page to test dynamiclly files without having to generate them.

Our template engine supports two main syntaxes: inline and block.

Inline syntax

Inline syntax is implemented using double curly brackets at the start and end of your expression {{ ... }}. This syntax is more flexible and is recommended when dealing with different control flows inline.

Take this template that is using a inline interpolation statement to render the name of instance as this react components name.

Dot Template
export const {{= tps.name }} = () => {
/* rest of react component code... */
}
Result
null
info

We will be using references to the templates context object like tps.name, tps.answers.*, tps.utils.* and others throughout this document. However, we won't cover this object in detail until later in the guide. For now, keep in mind that tps.name is the name of the instance you are generating, utils is a collection of helper functions designed to make creating dynamic templates easier, and answers is an object that holds the information the user of your template passes to it.

Block syntax

Block syntax is implemented using three curly brackets at both the start and end of your expression {{{ ... }}}. The main difference is that block syntax needs to be placed on a new line, and it will remove all whitespace in front of and behind the brackets, completely removing the line from the output.

Take this example using a block if statement: when css is false notice how there is no trace that anything was there before. No new lines, no spaces, nothing. It was completely wiped.

Dot Template
import React from 'react';
{{{? tps.answers.css }}}
import './component.css';
{{{?}}}
Result
null

Now when css is true notice how the only thing being rendered is whats inside the if statement.

Result
null

You can also indent block syntax and all tabs and spaces will be removed.

Dot Template
const App = () => {
{{{? tps.answers.state }}}
const [state, setState] = useState(null);
{{{?}}}

return (
/* other code ... */
);
};
Result
null
Example: Why is there inline and block?

Block syntax plays a special role in file generation. By removing all existence of white space, your rendered output looks nice and clean. Templates aims to not only make development eaiser but also enforce standards and best practices.

Before block syntax, there was only inline syntax. However there was a lot of issues with the way these tags got parsed that left outputed files with unindented whitespace.

example:

Lets use the example template from above and change block syntax to inline syntax. Now when css & state are false, notice how there is extra whitespace behind.

Dot Template
import React from 'react';
{{? tps.answers.css }}
import './component.css';
{{?}}

const App = () => {
{{? tps.answers.state }}
const [state, setState] = useState(null);
{{?}}

return (
/* other code ... */
);
};
Result
null

Now when css & state are true, you can also see extra lines.

Result
null

In order to get the perfect output, you needed to sacrafice the readability of the template and do something like the following:

Dot Template
import React from 'react';
{{? tps.answers.css }}import './component.css';
{{?}}
const App = () => {
{{? tps.answers.state }}const [state, setState] = useState(null);
{{?}}
return (
/* other code ... */
);
};
Result
null

Now when css & state are true, you can also see extra lines.

Result
null

While this might not look the worst for one answer, imagine 3 or 4. Now lets see how block syntax improves this:

Dot Template
import React from 'react';
{{{? tps.answers.css }}}
import './component.css';
{{{?}}}

const App = () => {
{{{? tps.answers.state }}}
const [state, setState] = useState(null);
{{{?}}}

return (
/* other code ... */
);
};
Result
null

Now when css & state are true,

Result
null

Template Syntaxes

Evaluation

{{{
<expressions...>
}}}

Evaluation are achieved by using inline or block brackets. This allows you to write any arbitrary JavaScript expressions. You can create variables, functions, conditions, the possibilities are endless. This can be helpful for simplifying more complex expressions. The semi-colon at the end of each expression is mandatory!

Dot Template
{{{
const name = "Marcellino Ornelas";
const age = 21;
const ageCategory = age > 18 ? "Adult" : "Child";
}}}
/* rest of code... */
Result
null

Checkout our API docs for more examples on how you can take advantage of evaluations.

Interpolation

{{= <expression> }}

Interpolation is achieved using the equal sign operator = along with inline brackets. This operator allows you to render dynamic output into the files contents.

Take this template that is using a inline interpolation statement to render the name of instance as this react components name.

Dot Template
export const {{= tps.name }} = () => {
/* rest of react component code... */
}
Result
null

Checkout our API docs for more examples on how you can take advantage of interpolations.

Conditionals

{{{? <condition> }}}
if output
{{{?? <else-if-condition> }}}
else if output
{{{??}}}
else output
{{{?}}}

Conditionals are achieved using the question mark operator ?, along with inline or block brackets. The result of the provided expression will be rendered into the output of the file when the expression returns a truthy value.

Take this template below that is using a block if statement to conditionally render an css import when the css answer is true.

Dot Template
import React from 'react';
{{{? tps.answers.css }}}
import "styles.css";
{{{?}}}
Result
null

Now when css is false, no output will be displayed.

Result
null

Else

You can also write else statements by adding an additional inline or block brackets that contains two question mark operators ??.

Take this template that is using a block if-else statement to conditionally render a import statement. If cssLang is modules, it will render an import statement for CSS modules; otherwise, it will render a normal CSS import statement.

Dot Template
import React from 'react';
{{{? tps.answers.cssLang === 'modules'}}}
import styles from './{{= tps.name}}.css';
{{{??}}}
import './{{= tps.name}}.css';
{{{?}}}
Result
null

Now when cssLang is not modules, we will get a normal CSS import statement.

Result
null

Else if

You can also write else-if statements by adding an additional inline or block brackets that contains the two question mark operators ?? with a expression afterwards.

Take this template that is using a block else-if statement to conditonally render a import statement. If cssLang is modules, it will render an import statement for CSS modules. Else if css is true, it will render a CSS import statement. Otherwise it will render nothing.

Dot Template
import React from 'react';
{{{? tps.answers.cssLang === 'modules'}}}
import styles from './{{= tps.name}}.css';
{{{?? tps.answers.css }}}
import './{{= tps.name}}.css';
{{{?}}}
Result
null

Now when cssLang is not modules and css is true, you will get a normal css import statement.

Result
null

Lastly when cssLang is not modules and css is false, you will get no rendered output.

Result
null

Checkout our API docs for more examples on how you can take advantage of conditionals.

Loops

{{{~<array> :<value>:<index>}}}
loop output
{{{~}}}

Loops are achieved using the ~ operator along with inline or block brackets. The contents of the loop will be rendered into the output of the file for each item in the array.

Take this template using a block loop statement to render import statements to the react file when modules is specified.

Dot Template
import React from 'react';
{{{~tps.answers.modules :value}}}
import {{= value}} from "@app/{{= value}}";
{{{~}}}
Result
null

Checkout our API docs for more examples on how you can take advantage of loops.

Defs (Partials)

{{{##def.<name>:<arg>:
def contents
#}}}

Defs, or partials, are achieved using ##def at the beginning and # at the end of inline or block brackets. Defs allow you to create reusable sections of a template. Think of them as smaller templates that can be defined separately and then included within larger templates. This provides modularity and simplifies the process of maintaining and updating your templates.

To render the contents of the def, use #def along with inline brackets.

{{#def.<name>:<arg>}}

Take this template thats creating a complexExpression def that will render a complex expression in both layout options one & two but not in any others. Utilizing a def for this allows you to maintain one version of the logic without duplicating it in your template.

Dot Template
{{{##def.complexExpression:
const someComplexExpression = /* some complex expression */;
#}}}
{{{? tps.answers.layout === "one"}}}
/* layout one code... */

{{#def.complexExpression}}
{{{?? tps.answers.layout === 'two'}}}
/* layout two code... */

{{#def.complexExpression}}
{{{??}}}
/* default layout code... */
{{{?}}}
Result
null

Args

You can also pass one argument to a def. This argument can be any valid javascript value.

Dot Template
{{{##def.complexExpression:useTypescript:
const someComplexExpression{{? useTypescript ?? false }}: string{{?}} = /* some complex expression */;
#}}}
{{#def.complexExpression:true}}
Result
null

If you need to pass more information to the def, you can pass an object as the argument and add all the information to the object. Keep in mind that if you pass an object directly, you need to follow it with a semicolon ; or a space.

Dot Template
{{{##def.someExpression:user:
const name = "{{= user.name}}";
const age = {{= user.age}};
#}}}
{{#def.someExpression:{ name: "lino", age: 24 }; }}
Result
null

Functions

You can also define defs as an javascript function.

Dot Template
{{{##def.renderIntro = function(name) {
return `My name is ${name}`;
}#}}}
{{#def.renderIntro("lino")}}
Result
null

How to utilize the template engine

There are three main ways to use our template engine to make your template dynamic, in file contents, files names, and def files.

File contents

To use our template engine in files, you must add a .tps extension to the end of your file name. When a new instance is rendered, this extension will be removed, leaving any other extension that was before the .tps extension intact.

server.js.tps
const express = require("express");
{{{? tps.answers.security }}}
const helmet = require("helmet");
{{{?}}}

const app = express();

{{{? tps.answers.security }}}
app.use(helmet());

{{{?}}}
/* node code ... */
server.js
null

File names

Every file within your template can utilize the template engine's capabilities directly in its file name. This allows for dynamic naming of files when creating a new instances.

Take this template named react-component, which includes a file named {{=tps.name}}.js:

| - .tps/
| - react-component
| - default
| - {{=tps.name}}.js

Now if you rendered a new instance of this template named Nav:

tps react-component Nav

then you'll end up with:

| - Nav/
| - Nav.js

Any functionality of our template engine can be used in files names, however here are some common use cases that we use when we build templates.

{{= tps.name}}.js

If name was Nav then the result would be:

Nav.js

Def files

Templates allow you to define defs inside files for easier use. Def files gets loaded before any files are rendered so you have access to all defs you define inside your template files. Any file with a .def extension in any of your packages is considered a def file.

| - .tps/
| - react-component
| - default/
| - helpers.def
| - some-other-file.js

If you want your def files to be accessible everywhere, put them inside your default package. If your def file is only used inside a specific package then place it inside that package.

caution

Def files placed inside packages may be usable inside template files inside other packages, however this is discouraged and may not be supported in other versions.

Def files can be used in two ways:

single def file

Single def files will use the entire file contents as the def contents. The name of this def will be the filename. You can access this def inside your template files by using {{#def.<file-name>}}.

Take this react-component template that has a imports.def def file and {{=tps.name}}.jsx.tps file.

| - .tps/
| - react-component/
| - default/
| - imports.def
| - {{=tps.name}}.jsx.tps

Inside the imports.def, we have all the imports statements you will need for your react components.

import React from 'react';
import '{{= tps.name}}.css';

Now inside {{=tps.name}}.jsx.tps we can use this def by the file name.

Dot Template
{{#def.imports}}

export const {{=tps.name}} = () => {
/* react component stuff ... */
};
Result
null

Multi def file

Multi def file is a def file that contains multiple defs. Multi def files uses the defs syntax in order define more than one.

Take this react-component template that has a helper.def def file and {{=tps.name}}.jsx.tps file.

| - .tps/
| - react-component/
| - default/
| - helper.def
| - {{=tps.name}}.jsx.tps

Inside the helpers.def, we have all the imports statements you will need for your react components and the props interface for when typescript is used.

{{{##def.imports:
import React from 'react';
import '{{= tps.name}}.css';
#}}}

{{{##def.propInterface:
interface Props {
/* props */
}
#}}}

Now inside {{=tps.name}}.jsx.tps we can use these defs.

Dot Template
{{#def.imports}}

{{{? tps.answers.typescript}}}
{{#def.propsInterface}}

{{{?}}}
export const {{=tps.name}} = () => {
/* react component stuff ... */
};
Result
null

Templates Context

After converting a file to a dynamic file, you gain access to the template's context object. This object contains information about the template and rendering metadata. The context object can be referenced in any of our template syntaxes.

{{ tps }}

We'll go over the most common properties of this object. For a full list, you can refer to the tps context API.

Name

{{= tps.name }}

Name of new instance you are rendering. When rendering two or more instances at the same time. The same concept as above apply's but for each path you pass in.

If you generate a new instance of a template with no build path. Then tps.name will be null.

Example
cli
tps react-component Nav
Dot Template
const {{= tps.name}} = (props) => {
return (
<div></div>
)
}
Result
null
tip

Dont remember what the template name is? Refresh your mind here.


Answers

{{= tps.answers.<prompt-name> }}

tps.answers is an object that holds the responses your user provided for your prompts. Prompts are a way for template owners to ask users for additional information about how they want their template created. However prompts aren't covered until a bit later in this guide.

Example

when a user creates a new instance of a template:

tps react-component Nav

They will prompted a list of questions. In this case if they "want to use a css file". Template owners can now use the answers object to get the value the user answered.

Dot Template
import React from 'react';
{{{? tps.answers.css }}}
import "styles.css";
{{{?}}}
Result
null

Utils

{{= tps.utils }}

tps.utils is a collection of helper functions to make creating templates easier. We've integrated powerful tools from change-case and inflection to streamline your workflow.

here are a list of some:

To explore all the available functions, check out the tps context utils API.

Example
cli
tps react-component nav
Dot Template
import React from react;

const {{= tps.utils.pascalCase(tps.name)}} = (props) => {
return (
<div></div>
)
}
Result
null