Skip to content

Commit

Permalink
Updated tutorial for <template> tag (#138)
Browse files Browse the repository at this point in the history
* chore: Removed hyphen between `<template>` and tag

* Updated tutorial

* chore: Generalized use cases

---------

Co-authored-by: ijlee2 <ijlee2@users.noreply.github.com>
  • Loading branch information
ijlee2 and ijlee2 authored Nov 29, 2024
1 parent b1254f1 commit c4da394
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ npx @codemod-utils/cli <your-codemod-name>

- [Main tutorial](./tutorials/ember-codemod-rename-test-modules/00-introduction.md)
- [Blueprints](./tutorials/blueprints-v2-addon/00-introduction.md)
- [`<template>`-tag components](./tutorials/template-tag-components/00-introduction.md)
- [`<template>` tag](./tutorials/template-tag/00-introduction.md)


## Codemods written with @codemod-utils
Expand Down
24 changes: 0 additions & 24 deletions tutorials/template-tag-components/00-introduction.md

This file was deleted.

5 changes: 0 additions & 5 deletions tutorials/template-tag-components/03-conclusion.md

This file was deleted.

24 changes: 24 additions & 0 deletions tutorials/template-tag/00-introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Introduction

> [!IMPORTANT]
> Please complete the [main tutorial](../ember-codemod-rename-test-modules/00-introduction.md) first.
> [!NOTE]
> This tutorial shows how to use [`content-tag`](https://github.com/embroider-build/content-tag#readme) and [`@codemod-utils/ast-template`](../../packages/ast/template#readme) (i.e. `ember-template-recast`) to read and update `*.{gjs,gts}` files.
>
> `content-tag`, in comparison to `ember-template-recast`, is still in development. Its API may change so `@codemod-utils` doesn't provide a utility package yet.
[`<template>` tag](https://github.com/ember-template-imports/ember-template-imports) allows Ember developers to write the template (traditionally, an `*.hbs` file) and the class (`*.{js,ts}`) in the same file. The new format has the file extension `.gjs` or `.gts`.

This creates interesting problems for codemods, because they need to parse and update new file types. By definition, `ember-template-recast` (meant for `*.hbs` files) and `recast` (for `*.{js,ts}`) aren't enough.

`content-tag` helps Node programs understand `*.{gjs,gts}` files. It does so by returning the locations of all `<template>` tags in a file and the template code (the "contents") for each tag. At the time of writing, `content-tag` doesn't provide a way to easily update the file.

Nonetheless, we can already solve 1 specific case: Use `@codemod-utils/ast-template` to update the template.


## Table of contents

1. [A simple example](./01-a-simple-example.md)
1. [Create utilities](./02-create-utilities.md)
1. [Conclusion](./03-conclusion.md)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# A simple example

To illustrate how to read and update `*.{gts,gts}` files, we'll recreate a feature in [`ember-test-selectors`](https://github.com/mainmatter/ember-test-selectors/blob/v6.0.0/strip-data-test-properties-plugin6.js): Remove all data attributes in the template, if the attribute name starts with `data-test`. Our target project is assumed to be an Ember app.
To illustrate how to update templates in `*.{gts,gts}` files, we'll recreate a feature in [`ember-test-selectors`](https://github.com/mainmatter/ember-test-selectors/blob/v6.0.0/strip-data-test-properties-plugin6.js): Remove all data attributes in the template, if the attribute name starts with `data-test`. Our target project is assumed to be an Ember app.


## Use the CLI
Expand Down Expand Up @@ -62,7 +62,7 @@ export function removeTestSelectors(options: Options): void {

</details>

To test the step, we create a component with multiple `<template>`-tags:
To test the step, we create a component with multiple `<template>` tags:

<details>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Create utilities

In [the previous chapter](./01-a-simple-example.md), we read `*.{gjs,gts}` files. We will take small steps to update these files.
In [the previous chapter](./01-a-simple-example.md), we read `*.{gjs,gts}` files. Let's take small steps to update these files.


## Parse file

We avoid directly consuming `content-tag` in our steps, but create **wrappers** instead. Wrapping code helps us minimize the impact of a breaking change in `content-tag`.
Instead of directly consuming `content-tag`, we'll create **wrapper functions**. Wrapping code helps us minimize the impact of a breaking change in `content-tag`.

Until `@codemod-utils` provides an official package, we can create utilities in `src/utils`.
Since `@codemod-utils` doesn't an official package yet, let's create utilities in `src/utils`.

<details>

Expand Down Expand Up @@ -37,7 +37,7 @@ export function parse(file: string) {
return preprocessor.parse(file) as unknown as ContentTag[];
}

export function replaceContents(
export function replaceTemplate(
file: string,
options: {
contents: string;
Expand Down Expand Up @@ -103,7 +103,7 @@ We can run tests to understand what `parse()` returns.

<summary>Expected output</summary>

The fixture file has 3 `<template>`-tags, so the array `contentTags` has 3 elements. The object keys that matter to us are `contents` and `range`.
The fixture file has 3 `<template>` tags, so the array `contentTags` has 3 elements. The object keys that matter to us are `contents` and `range`.

```sh
❯ pnpm test
Expand Down Expand Up @@ -163,7 +163,7 @@ The fixture file has 3 `<template>`-tags, so the array `contentTags` has 3 eleme
</details>
> [!NOTE]
> From `range.start`, we see that `contentTags` is a sorted array. The `<template>`-tag, which appears first in the file, appears first in the array.
> From `range.start`, we see that `contentTags` is a sorted array. The `<template>` tag, which appears first in the file, appears first in the array.
## Create contents
Expand Down Expand Up @@ -230,9 +230,9 @@ export function removeTestSelectors(options: Options): void {
</details>
## Replace contents
## Replace templates
Last but not least, we use `replaceContents()` to replace the contents of each `<template>`-tag. Because a file may have multiple tags, we update the tags in the reverse order.
Last but not least, we use `replaceTemplate()` to replace the contents of each `<template>` tag. Because a file may have multiple tags, we update the tags in the reverse order.
<details>
Expand All @@ -247,7 +247,7 @@ import { createFiles, findFiles } from '@codemod-utils/files';
import { Options } from '../types/index.js';
- import { parse } from '../utils/ast.js';
+ import { parse, replaceContents } from '../utils/ast.js';
+ import { parse, replaceTemplate } from '../utils/ast.js';
function removeDataTestAttributes(file: string): string {
const traverse = AST.traverse();
Expand Down Expand Up @@ -283,7 +283,7 @@ export function removeTestSelectors(options: Options): void {
const contents = removeDataTestAttributes(contentTag.contents);
- console.log(contents);
+ file = replaceContents(file, {
+ file = replaceTemplate(file, {
+ contents,
+ range: contentTag.range,
+ });
Expand Down
19 changes: 19 additions & 0 deletions tutorials/template-tag/03-conclusion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Conclusion

When utilities form the foundation of a codemod, we can experiment with new libraries and write code that stands the test of time.

In this tutorial, we combined `content-tag` with `@codemod-utils/ast-template` so that we can update templates in `*.{gjs,gts}` files—a feature that neither `content-tag` nor `@codemod-utils` provides just yet.

As exercise, see if you can update classes in `*.{gjs,gts}` (maybe for just 1 specific case) by combining `content-tag` with `@codemod-utils/ast-javascript`.

```ts
/* src/utils/ast/template-tag.ts */
import { Preprocessor } from 'content-tag';

export function extractClass(file: string): string {
const preprocessor = new Preprocessor();

// Compiles `<template>` tags to JavaScript
return preprocessor.process(file).code;
}
```

0 comments on commit c4da394

Please sign in to comment.