Dynamic Files (Legacy)
Dynamic files breakdown
What is a dynamic file
Tps goes beyond simple copy and paste functionality; it enables the existence of a more versatile and powerful system. When working with reusable concepts, the ability to incorporate additional information becomes essential. This is where dynamic files come into play.
Dynamic files reside within your template's package folders, much like any other file. However, dynamic files undergo a different processing procedure. Tps reads the contents of dynamic files, which are then processed by a templating language called doT.
What is doT
DoT is based on JavaScript and uses {{}}
brackets for interpolation. doT is a
templating language similar to popular options like Handlebars, EJS, or Jade. It
provides various types of interpolation, including loops, conditionals, and
more. You can use any valid JavaScript code inside these brackets.
To learn more about doT, you can visit its documentation. However, let's introduce you to the basics here.
It's important to consider that not all JavaScript features may be supported across different computers or versions of Node when running these templates. As a result, certain JavaScript functionality may not work as expected or behave differently.
How to use doT
Like mentioned before doT uses {{}}
brackets. Here are some common ways on how
you can use doT
when it comes to creating complex templates, you may encounter a need to indent the dot syntax to improve readability. However, be aware that this can cause unexpected issues with the appearance of the rendered template. While the indented structure may look good in the template itself, when the dot processes these files, it doesn't recognize or handle the indentation properly. This has been my experience with using it, and you can find more detailed information about this issue in the following link: https://github.com/marcellino-ornelas/templates/issues/7
Evaluation
In doT, evaluation is achieved using {{ <expression>}}
. The expression can be
any valid JavaScript code, enabling the creation of variables, functions, and
various other possibilities. The semi-colon at the end of the expression is
mandatory!
{{const name = "Marcellino Ornelas";}}
null
One limitation of evaluations are that it leaves a empty space where it was placed at. We recommend avoid using as much as possible but could be useful in some cases
Example: Evaluations leaves new line
{{const name = "john doe";}}
hey
null
Interpolation
In doT, interpolation is achieved using {{= <expression>}}
. This syntax allows
the result of the specified expression to replace the brackets in the template.
{{= 2 + 2}}
null
Conditionals
In doT, interpolation is achieved using {{? <expression>}} ... {{?}}
. The
result of the provided expression will be displayed if the expression returns
truthy value
Are you there?
{{? 1 === 1}}
I am here
{{?}}
null
If you dont want the extra new lines, move all the conditional statements to one line
Are you there?
{{? 1 === 1}}I am here{{?}}
null
If you want everthing to flow on one line then just put the condition on the same line
Are you there? {{? 1 === 1}}I am here{{?}}
null
doT also supports else statements.
Are you here? {{? 1 === 2}}I am here {{??}}I am not home {{?}}
null
doT also supports else if statements.
Are you here? {{? 1===2}}I am here {{?? 1 ===1}}I might be home{{??}}I am not home {{?}}
null
One limitation of conditionals is that it leaves a white space behind when the condition is not met
Are you there?
{{? 1 === 2}}I am here{{?}}
how about now?
null
The only way around this right now is placing the condition on the same line but adding a new line to the begining of the value
Are you there?{{? 1 === 1}}{{="\n"}}I am here{{?}}
how about now?{{? 1 === 2}}{{="\n"}}I am here{{?}}
null
Notice how the first condition value still showed up on a new line but the second condition didnt leave a new line
Loops
In doT, loops is achieved using {{~<array> :value:index}}{{= value}}{{~}}
.
{{~[1, 2, 3, 4] :value:index}}
{{= value}}
{{~}}
null
Unfortunately we need to keep all tags on the same line to get a cleaner output.
By indenting {{= value}}
to get a cleaner look will result in indent in the
rendered output
Example: Indent Issues
{{~[1, 2, 3, 4] :value:index}}
{{= value}}
{{~}}
null
If you dont want the extra spacing between each line then you need to move
{{= value}}
on the same line as your loop.
{{~[1,2,3,4] :value:index}}{{= value}}
{{~}}
null
To remove the extra space at the bottom of the output, you can move the end of
the loop to the same line. However, this will cause all your items to be
displayed on a single line. To address this, you can add a \n
character after
each value. To avoid displaying a new line after the last item, you can use a
ternary operator to check if it is the last item.
{{~[1,2,3,4] :value:index}}{{= value + (index !== [1,2,3,4].length - 1 ? "\n" : "")}}{{~}}
null
If you dont need anything to complex, then use join
!
{{= [1,2,3,4].join("\n")}}
null
If you would like to keep output on one line then put all doT brackets on one line
{{~[1, 2, 3, 4] :value:index}}{{= value}}{{~}}
null
Defs (Partials)
DoT supports defs (partials). Defs allow you to create reusable sections of a templates. 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.
{{##def.intro:
This is my intro
#}}
{{#def.intro}}
null
You can also define functions as defs
{{##def.intro = function(name) {
return `This is my intro`;
}#}}
{{#def.intro()}}
null
How to use doT with Templates
Templates make it easy to use the doT template engine inside files. This helps
you add dynamic content to your files. To use doT inside your templates files,
you just need to add the .dot
extension to the file. The .dot
extension can
be added to any file, regardless of its original extension. Once added, the
contents of the file will be processed by doT. The resulting output will then be
incorporated into the rendered file. Your rendered file will no longer have the
.dot
extention
Example
To use doT in a file named server.js
, Rename the file to server.js.dot
. Once
processed and rendered, the resulting file will retain its original name, which
is server.js
.
File contents
Like mentioned above, once you add a .dot
extention to your file name you are
now able to use any dot syntax inside the file and tps will process it.
Example
const express = require("express");
const app = express();
/* node code ... */
{{? tps.answers.security }}app.use(helmet());{{?}}
/* node code ... */
null
File names
File names can also take advantage of doT. This allows file names to be dynamic.
You can use any valid doT syntax inside of file names. Unlike file contents, all
file names get processed with doT so you can use doT syntax in any file name in
your template even if they dont have a .doT
extension.
Example
- Interpolation
- utils
- Defs
{{= tps.name}}.js
If name
was Nav
then the result would be:
Nav.js
{{= tps.utils.camelCase(tps.name)}}.js.dot
If name
was my-nav
then the result would be:
myNav.js
you can utilize a def if your logic gets to complex for your file name
Be aware that adding new lines to the def file will also show up in your file name
{{? tps.answers.capitalize}}{{= tps.utils.capitalize(tps.name)}}{{??}}tps.name{{?}}
{{#def.name}}.js
If name
was nav
and capitalize
was true then the result would be:
Nav.js
if capitalize
was false then the result would be:
nav.js
Def files
Templates allows you to define doT 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.
Def files are files inside your packages that have a .def
extension.
Example
| - .tps/
| - react-component
| - default/
| - helpers.def
If you want your def files to be accessable anywhere put them inside your
default
package. If your def file is only used inside a specific package then
place it inside that package
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
If your def file has no dot syntax then the whole file will be converted to a
def
. You can access this def inside your template files by using
{{#def.<file-name>}}
Example
If you had this template folder structure:
| - .tps/
| - some-template/
| - default/
| - helpers.def
| - index.txt.dot
and inside your helpers.def
you had:
This is my def file
and inside index.txt.dot
you had:
hey there
{{#def.helpers}}
Your rendered index.txt
file would end up like this:
hey there
This is my def file
multiple def functions inside a file
You can define multiple def functions inside a def file by using the def syntax
{{##def.name: ... #}
. These def files can be accessed by the name you give
them in the def.
Example
If you had this template folder structure:
| - .tps/
| - some-template/
| - default/
| - helpers.def
| - index.txt.dot
and inside your helpers.def
you had:
{{##def.helper1:
helper 1
#}}
{{##def.helper2:
helper 2
#}}
and inside index.txt.dot
you had:
hey there
{{#def.helper1}}
{{#def.helper2}}
Your rendered index.txt
file would end up like this:
hey there
helper 1
helper 2
Templates Context
After converting a file to use doT, you gain access to the templates context object, which contains information about the template and rendering metadata. To utilize the context object, you can refer to the tps object within doT tags. Here's an example:
{{ tps }}
Now, let's explore some of the most commonly used properties available in 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 render a template with no build path. Then tps.name
will be null.
Example
tps react-component Nav
const {{= tps.name}} = (props) => {
return (
<div></div>
)
}
null
Dont remember what the template name is? Refresh your mind here
Packages
{{= tps.packages }}
List of packages that were used when rendering your template.
Remember default
package is include by default
When no additional packages are used:
["default"]
when additional packages are used:
["default", "css", "unit-tests"]
Example
- Additional packages
- No additional packages
tps react-component App --packages css
import React from react;
{{? tps.packages.includes("css")}}import "{{= tps.name}}.css";
{{?}}
const {{= tps.name}} = (props) => {
return (
<div></div>
)
}
null
tps react-component App
import React from react;
{{? tps.packages.includes("css")}}import "{{= tps.name}}.css";
{{?}}
const {{= tps.name}} = (props) => {
return (
<div></div>
)
}
null
Don't remember how to use packages? Refresh your mind here
utils
{{= tps.utils }}
Make your template creation journey more enjoyable with our collection of utilities. We've integrated powerful tools from change-case and inflection to streamline your workflow.
here are a list of some:
- camelCase
- capitalCase
- constantCase
- dotCase
- headerCase
- paramCase
- pascalCase
- snakeCase
- pluralize
- singularize
- camelize
- underscore
- humanize
- capitalize
To explore all the available functions, check out the tps context utils API.
Example
tps react-component nav
import React from react;
const {{= tps.utils.capitalize(tps.name)}} = (props) => {
return (
<div></div>
)
}
null