npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@foxfriends/embedded-typescript

v0.5.0

Published

Type safe TypeScript templates

Downloads

5

Readme

Embedded TypeScript

Type safe TypeScript templates

What is this? 🧐

A type safe templating system for TypeScript. Templates are compiled to TypeScript files that you then import for type safe string generation.

This templating system draws inspiration from ERB, EJS, handlebars and mustache. This project embraces the "just JavaScript" spirit of ejs and adds some of the helpful white space semantics of mustache.

Checkout the examples or play with embedded-typescript in your browser.

Installation & Usage 📦

  1. Add this package to your project:

    npm install @foxfriends/embedded-typescript or yarn add @foxfriends/embedded-typescript

Motivation

Hello undefined!

When using a typed language, I want my templates to be type checked. For most cases, template literals work well, but template literals quickly become difficult to maintain as the template complexity grows. I can switch to EJS, handlebars, mustache, etc, but then I lose the type safety I had with template literals. Sometimes I want the expressiveness of a templating language without losing type safety. For those cases, I wrote embedded-typescript.

Syntax

| Syntax | Name | Description | | --------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | --- CODE --- | Header | Defines code that should live outside of the generated render function. Use this to define Props and any imports, exports or constants. | | <%= EXPRESSION %> | Expression | Inserts the value of an expression. This value is escaped according to the detected file type mode. | | <%~ EXPRESSION %> | RawExpression | Inserts the value of an expression. | | <%\| EXPRESSION %> | PreserveIndent | Inserts the escaped value of an expression. If the expression generates multiple lines, the indentation level is preserved across all resulting lines. | | <%~\| EXPRESSION %> | RawPreserveIndent | Inserts the value of an expression. If the expression generates multiple lines, the indentation level is preserved across all resulting lines. | | <% CODE %> | Statement | Executes code, but does not insert a value. | | <> | Glue | Glues this line and the next one together by deleting exactly 1 following \n character. | | TEXT | Text | Text literals are inserted as is. All white space is preserved. |

Examples 🚀

Minimal

  1. Write a template file: my-template.ets:
---
interface Props {
  users: {
    name: string;
  }[]
}
---
<% for (const user of props.users) { %><>
Name: <%= user.name %>
<% } %><>
  1. Run the compiler: npx ets. This will compile any files with the .ets extension. my-template.ets.ts will be generated.

  2. Import the generated .ets.ts file wherever you'd like to render your template:

import render from "./my-template.ets";

/* will output:
Name: Alice
Name: Bob
*/

console.log(render({ users: [{ name: "Alice" }, { name: "Bob" }] }));

Note that the arguments to your template function are type checked. You define the arguments to your template function by defining a type or interface named Props.

Partials

Embedded TypeScript preserves the indentation wherever an expression tag (<%= EXPRESSION %>) is used. This means there isn't any special syntax for partials, and ets templates nest as you would expect.

  1. Write a "partial" user-partial.ets:
---
interface Props {
  name: string;
  email: string;
  phone: string;
}
---
Name: <%= props.user.name %>
Email: <%= props.user.email %>
Phone: <%= props.user.phone %>

Note there is nothing special about user-partial.ets, it's just an ets template. We're using the -partial suffix purely for illustration.

  1. Import your "partial" into another ets template my-template-2.ets:
---
import renderUser, { Props as User } from './user-partial.ets';

interface Props {
  users: User[];
}

const example =
`1
2
3
4`;
---
<% if (props.users.length > 0) { %><>
Here is a list of users:

  <% for (const user of props.users) { %><>
  <%| renderUser(user) %>
  <% } %><>

<% } %><>
The indentation level may be preserved for the rendered 'partial'.

There isn't anything special about the 'partial'. Here we used another `.ets` template, but any
expression yeilding a multiline string would be treated the same.

  <%| example %>

The end!
  1. Run the compiler: npx ets.

  2. Import the generated my-template-2.ets.ts file wherever you'd like to render your template:

import render from "./my-template-2.ets";

/* will output:
Here is a list of users:

  Name: Tate
  Email: [email protected]
  Phone: 888-888-8888

  Name: Emily
  Email: [email protected]
  Phone: 777-777-7777

The indentation level is preserved for the rendered 'partial'.

There isn't anything special about the 'partial'. Here we used another `ets` template, but any
expression yielding a multi-line string would be treated the same.

  1
  2
  3
  4

The end!
*/

console.log(
  render({
    users: [
      { name: "Tate", phone: "888-888-8888", email: "[email protected]" },
      { name: "Emily", phone: "777-777-7777", email: "[email protected]" },
    ],
  })
);

Note that indentation was preserved for all lines rendered by user-partial.ets and all lines of the example variable due to the use of the <%| interpolation. Any expression yielding a multi-line string rendered inside an expresssion block (<%| EXPRESSION %>) will apply the indentation across each line.

More Examples

For more examples, take a look at the e2e directory. The *.ets.ts files are generated by the compiler from the *.ets template files. The corresponding *${NAME}.test.ts shows example usage and output.

Async partials

In order to use async/await in a partial, the file extension must be .async.ets. The resulting template function that is generated will be an async function respectively, and will need to be await-ed when used.

HTML mode

When the file extension is .html.ets (or .html.async.ets or .async.html.ets), the template is considered in HTML mode. The only change is to the behaviour of <%= and <%| interpolations: special characters in such interpolated strings will be HTML-escaped.

If you need to output those characters un-changed (e.g. because you're embedding a partial into another partial), use <%~ or <%~|.

Understanding Error Messages

The compiler will output errors when it encounters invalid syntax:

error: Unexpected closing tag '%>'
   --> ./template-1.ets:4:41
    |
4   | <% users.forEach(function(user) { %>%>
    |                                     ^
    |                                     |
...

The first line is a description of the error that was encountered.

The second line is location of the error, in path:line:column notation.

The next 5 lines provide visual context for the error.

Highlights

🎁 Zero run time dependencies

Configuration 🛠

Embedded TypeScript aims to be zero config, but can be configured by creating an ets.config.mjs (or .js or .cjs) file in your project root.

Defaults to the project root.

Example:

Search for .ets files under a directory named src

// ets.config.mjs

```js
/** @type {import('ets').Config} */
export default {
  source: "src",
};
```

Contributing 👫

PR's and issues welcomed! For more guidance check out CONTRIBUTING.md

Licensing 📃

See the project's MIT License.