Skip to content

Commit

Permalink
Add tutorial for blueprints (Part 2) (#75)
Browse files Browse the repository at this point in the history
Explained how to create files from blueprints (static)

Co-authored-by: ijlee2 <ijlee2@users.noreply.github.com>
  • Loading branch information
ijlee2 and ijlee2 authored Sep 17, 2023
1 parent 111a0e9 commit 94e3646
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 12 deletions.
10 changes: 6 additions & 4 deletions tutorials/blueprint-for-v2-addon/00-introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ We will partially implement `blueprint-for-v2-addon`, a codemod that helps [CLAR
- Generating files is simpler and faster.

```sh
# Average time for @embroider/addon-blueprint
time EMBER_CLI_PNPM=true ember addon "@clark-ui/button" --addon-location "ui/button" --blueprint "@embroider/addon-blueprint" --pnpm --skip-npm --typescript
# Average for @embroider/addon-blueprint
time EMBER_CLI_PNPM=true ember addon "@my-orgs-ui/button" --addon-location "ui/button" --blueprint "@embroider/addon-blueprint" --pnpm --skip-npm --typescript

1.87s user 1.18s system 126% cpu 2.409 total
```

```sh
# Average time for blueprint-for-v2-addon
time pnpm generate-addon --addon-name "@clark-ui/button" --addon-location "ui/button"
# Average for blueprint-for-v2-addon
time pnpm generate-addon --addon-name "@my-orgs-ui/button" --addon-location "ui/button"

1.86s user 0.23s system 133% cpu 1.565 total
```
Expand All @@ -30,3 +30,5 @@ We will partially implement `blueprint-for-v2-addon`, a codemod that helps [CLAR
## Table of contents

1. [Create a project](./01-create-a-project.md)
1. [Sketch out the solution](./02-sketch-out-the-solution.md)
1. [Define codemod options](./03-define-codemod-options.md)
16 changes: 8 additions & 8 deletions tutorials/blueprint-for-v2-addon/01-create-a-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,28 @@ cd blueprint-for-v2-addon
pnpm install
```

> [!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), you can remove the sample step, `add-end-of-line`.

## Folder structure

Let's take a look at how `blueprint-for-v2-addon` is structured as a tree. For simplicity, the tree only shows what's important for the tutorial.
Let's take a look at how `blueprint-for-v2-addon` is structured as a tree. For simplicity, the tree only shows what's new, compared to that from [the main tutorial](ember-codemod-rename-test-modules/02-understand-the-folder-structure.md#folder-structure).

```sh
blueprint-for-v2-addon
└── src
├── blueprints
├── steps
├── types
├── utils
│ └── blueprints.ts
└── index.ts
└── utils
└── blueprints.ts
```

We see that the CLI has scaffolded `src/blueprints` and `src/utils`.


### blueprints

The `blueprints` directory contains files that we want end-developers (our users) to have.
The `blueprints` folder contains files that we want end-developers (our users) to have.

For the most part, the folder structure and file names will match what end-developers will see in their project. At runtime, it is possible to change the file path (e.g. rename `__gitignore__` to `.gitignore`) or exclude the file (e.g. `tsconfig.json` for JavaScript projects).

Expand All @@ -56,7 +56,7 @@ In short, we can write and test our codemod as usual, without worrying about whe

<div align="center">
<div>
Next:
Next: <a href="./02-sketch-out-the-solution.md">Sketch out the solution</a>
</div>
<div>
Previous: <a href="./00-introduction.md">Introduction</a>
Expand Down
193 changes: 193 additions & 0 deletions tutorials/blueprint-for-v2-addon/02-sketch-out-the-solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Sketch out the solution

Our codemod will support **workspaces**. For us, this means, a project that includes 1 addon and 1 test app, or many addons and equally many test apps.

```sh
workspace-root
├── packages
│ ├── addon-1
│ ├── addon-2
│ ├── ...
│ └── addon-n
└── tests
├── test-app-for-addon-1
├── test-app-for-addon-2
├── ...
└── test-app-for-addon-n
```

We learned in [the previous chapter](./01-create-a-project.md) that,

- The `blueprints` folder contains files that our users will have.
- The `blueprintsRoot` variable represents where the blueprint files will be saved on their machine.

Let's look at how we can read and write these files to their project.

Goals:

- Take small steps
- Read and write blueprint files


## Add blueprint files

Let's start out simple by creating 1 file for the addon and 1 file for the test app. Both files are **static**; there's nothing **dynamic** (e.g. string interpolations, conditional statements, for-loops) that would complicate our first step.

Copy-paste the following starter code:

<details>

<summary><code>src/blueprints/__addonLocation__/package.json</code></summary>

```json
{
"name": "addon-1",
"version": "0.0.0"
}
```

</details>

<details>

<summary><code>src/blueprints/__testAppLocation__/package.json</code></summary>

```json
{
"name": "test-app-for-addon-1",
"version": "0.0.0"
}
```

</details>

Then, scaffold a step called `create-files-from-blueprints`. Use `findFiles()` from `@codemod-utils/files` to find the blueprint files, then log the file paths.

> [!NOTE]
> Need a refresher on [`findFiles()`](../ember-codemod-rename-test-modules/04-step-1-update-acceptance-tests-part-1.md#find-files)? Don't forget to run tests to check your code.
>
> ```sh
> ❯ pnpm test
>
> [
> '.gitkeep',
> '__addonLocation__/package.json',
> '__testAppLocation__/package.json'
> ]
> ```
<details>
<summary>Solution: <code>src/steps/create-files-from-blueprints.ts</code></summary>
Note, the project root for `findFiles()` points to `blueprintsRoot`, not `options.projectRoot`.
```ts
import { findFiles } from '@codemod-utils/files';
import type { Options } from '../types/index.js';
import { blueprintsRoot } from '../utils/blueprints.js';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function createFilesFromBlueprints(options: Options): void {
const blueprintFilePaths = findFiles('**/*', {
projectRoot: blueprintsRoot,
});
console.log(blueprintFilePaths);
}
```
</details>
<details>
<summary>Solution: <code>src/steps/index.ts</code></summary>
```diff
+ export * from './create-files-from-blueprints.js';
export * from './create-options.js';
```
</details>
<details>
<summary>Solution: <code>src/index.ts</code></summary>
```diff
- import { createOptions } from './steps/index.js';
+ import { createFilesFromBlueprints, createOptions } from './steps/index.js';
import type { CodemodOptions } from './types/index.js';
export function runCodemod(codemodOptions: CodemodOptions): void {
const options = createOptions(codemodOptions);
- // ...
+ createFilesFromBlueprints(options);
}
```
</details>
## Read and write blueprint files
Unlike in [the main tutorial](../ember-codemod-rename-test-modules/04-step-1-update-acceptance-tests-part-1.md#read-and-write-files), we won't use `writeFileSync()` from Node.js to create files. The reason is, the folders where files will be created (i.e. folders named `__addonLocation__` and `__testAppLocation__`) don't exist on the user's machine. We'd need to write extra code to handle this edge case.
Luckily, `@codemod-utils/files` provides [`createFiles()`](../../packages/files/README.md#createfiles). It creates multiple files at once and creates folders as needed. We just need to provide this function a `Map`, which maps a blueprint's file path to its file content.
<details>
<summary>Solution: <code>src/steps/create-files-from-blueprints.ts</code></summary>
```ts
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { createFiles, findFiles } from '@codemod-utils/files';
import type { Options } from '../types/index.js';
import { blueprintsRoot } from '../utils/blueprints.js';
export function createFilesFromBlueprints(options: Options): void {
const blueprintFilePaths = findFiles('**/*', {
projectRoot: blueprintsRoot,
});
const fileMap = new Map(
blueprintFilePaths.map((blueprintFilePath) => {
const blueprintFile = readFileSync(
join(blueprintsRoot, blueprintFilePath),
'utf8',
);
return [blueprintFilePath, blueprintFile];
}),
);
createFiles(fileMap, options);
}
```
</details>
Run `./codemod-test-fixtures.sh`. From the `sample-project`'s `output` folder, we see that we're a few steps closer to creating the addon and the test app. ✨
```sh
workspace-root
├── __addonLocation__
│ └── package.json
└── __testAppLocation__
└── package.json
```
<div align="center">
<div>
Next: <a href="./03-define-codemod-options.md">Define codemod options</a>
</div>
<div>
Previous: <a href="./01-create-a-project.md">Create a project</a>
</div>
</div>
11 changes: 11 additions & 0 deletions tutorials/blueprint-for-v2-addon/03-define-codemod-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Define codemod options


<div align="center">
<div>
Next:
</div>
<div>
Previous: <a href="./02-sketch-out-the-solution.md">Sketch out the solution</a>
</div>
</div>

0 comments on commit 94e3646

Please sign in to comment.