Skip to content

Commit

Permalink
Documented how to update template-tag components
Browse files Browse the repository at this point in the history
  • Loading branch information
ijlee2 committed Oct 30, 2023
1 parent 52744a1 commit 040cf9d
Show file tree
Hide file tree
Showing 5 changed files with 529 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ npx @codemod-utils/cli --name <your-codemod-name>

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


## Codemods written with @codemod-utils
Expand Down
24 changes: 24 additions & 0 deletions tutorials/template-tag-components/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 not stable. Since its API may change, `@codemod-utils` doesn't provide a utility package yet.
[`<template>`-tag components](https://github.com/ember-template-imports/ember-template-imports) allow Ember developers to write JavaScript or TypeScript in the same file as the template. The new format has the file extension `.gjs` or `.gts`.

This creates interesting problems for codemods, since they need to parse and transform a new file type. 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, it doesn't provide a way to update the file.

Luckily, we can use `@codemod-utils/ast-template` to update template code.


## Table of contents

1. [A simple example](./01-a-simple-example.md)
1. [Create utilities](./02-create-utilities.md)
1. [Conclusion](./03-conclusion.md)
131 changes: 131 additions & 0 deletions tutorials/template-tag-components/01-a-simple-example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# 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.


## Use the CLI

Change the directory to a place where you like to keep projects. Then, run these commands:

```sh
# Create project
npx @codemod-utils/cli --name remove-test-selectors --addon ast-template

# Install dependencies
cd remove-test-selectors
pnpm install

# Install content-tag as a dependency
pnpm install content-tag
```

> [!NOTE]
> Just like in [the main tutorial](../ember-codemod-rename-test-modules/04-step-1-update-acceptance-tests-part-1.md#remove-the-sample-step), remove the sample step, `add-end-of-line`.

## Scaffold step

Create a step called `remove-test-selectors`. It is to read `*.{gjs,gts}` files and write back the file content (a no-op).

<details>

<summary><code>src/steps/remove-test-selectors.ts</code></summary>

For brevity, how `src/index.ts` calls `removeTestSelectors()` is not shown.

```ts
import { readFileSync } from 'node:fs';
import { join } from 'node:path';

import { createFiles, findFiles } from '@codemod-utils/files';

import { Options } from '../types/index.js';

export function removeTestSelectors(options: Options): void {
const { projectRoot } = options;

const filePaths = findFiles('app/components/**/*.{gjs,gts}', {
projectRoot,
});

const fileMap = new Map(
filePaths.map((filePath) => {
const file = readFileSync(join(projectRoot, filePath), 'utf8');

return [filePath, file];
}),
);

createFiles(fileMap, options);
}
```

</details>

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

<details>

<summary><code>tests/fixtures/sample-project/input/app/components/my-component.gjs</code></summary>

The indentations are inconsistent on purpose. We want to know if our codemod will preserve formatting.

```js
import { on } from '@ember/modifier';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import styles from './my-component.css';

const Control =
<template>
<div class={{styles.control}}>
<button
data-test-button="Increment"
type="button"
{{on "click" @onClick}}
>
Increment by 1
</button>
</div>
</template>

const Display =
<template>
<div class={{styles.display}}>
Count:
<p class={{styles.count}} data-test-count ...attributes>
{{@count}}
</p>
</div>
</template>

export default class MyComponent extends Component {
@tracked count = 0;

increment = () => {
this.count++;
}

<template>
<div class={{styles.container}}>
<Control
@onClick={{this.increment}}
/>
<Display @count={{this.count}} data-test-my-count />
</div>
</template>
}
```

</details>


<div align="center">
<div>
Next: <a href="./02-create-utilities.md">Create utilities</a>
</div>
<div>
Previous: <a href="./00-introduction.md">Introduction</a>
</div>
</div>
Loading

0 comments on commit 040cf9d

Please sign in to comment.