Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@ember-data/codemods package #9343

Merged
merged 55 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
bf322ae
Basic project setup
gitKrystan Apr 9, 2024
e926862
Add first test (in jest...sorry)
gitKrystan Apr 9, 2024
04b2b0b
Move codemods tests to own package
gitKrystan Apr 10, 2024
a449a70
Initial implementation
gitKrystan Apr 12, 2024
eaec801
Add test for findRecord with type+id and options
gitKrystan Apr 12, 2024
f38beb1
Add more simple findRecord tests
gitKrystan Apr 12, 2024
b5d2239
Throw if preload option is used
gitKrystan Apr 12, 2024
4bc8981
Support comment preservation
gitKrystan Apr 15, 2024
e80c0f2
Clean up
gitKrystan Apr 15, 2024
c08cd75
Add tests for other builders
gitKrystan Apr 15, 2024
33e438a
Access .content
gitKrystan Apr 15, 2024
926ac7e
Add TS support
gitKrystan Apr 15, 2024
2bb82bf
Switch from jest to node test
gitKrystan Apr 16, 2024
3428224
Add CLI to run codemods
gitKrystan Apr 18, 2024
2533a3e
Handle async nature of store methods
gitKrystan Apr 19, 2024
7f99ae8
Build codemods cli
gitKrystan Apr 19, 2024
ba52708
Fix some issues discovered during dogfooding
gitKrystan Apr 19, 2024
c4ced8d
Handle `this.` and `foo.` cases
gitKrystan Apr 22, 2024
1a35424
Cleanup
gitKrystan Apr 22, 2024
c6615d5
.content only when returned
gitKrystan Apr 22, 2024
950e48a
Add --log-file config
gitKrystan Apr 23, 2024
fdba0ac
Logging improvements
gitKrystan Apr 23, 2024
09b945d
Remove prettier integration
gitKrystan Apr 24, 2024
0757d82
Improve error logging
gitKrystan Apr 24, 2024
13c0acc
Handle yield keyword
gitKrystan Apr 24, 2024
4ea3f9c
Add --dry option
gitKrystan Apr 24, 2024
e224bc1
Add --ignores flag, simplify glob logic
gitKrystan Apr 24, 2024
a2806e3
Clean up
gitKrystan Apr 24, 2024
9caa504
More logger fixes
gitKrystan Apr 24, 2024
05ce2ec
Avoid duplicate specifier errors
gitKrystan Apr 24, 2024
b1cca2f
Clean up
gitKrystan Apr 24, 2024
273182c
Commit build
gitKrystan Apr 24, 2024
0790a49
Add back Glob handling
gitKrystan Apr 24, 2024
419e916
Update README
gitKrystan Apr 24, 2024
247125f
Drop id type annotations when destructuring content
gitKrystan Apr 24, 2024
32bbd9c
Test cleanup
gitKrystan Apr 24, 2024
361ac65
Add idempotency tests
gitKrystan Apr 24, 2024
b837dcb
Commit build
gitKrystan Apr 24, 2024
b30319e
Warn for skips
gitKrystan Apr 25, 2024
03444c4
Update type generics
gitKrystan Apr 25, 2024
838bd13
Commit build
gitKrystan Apr 25, 2024
bf30000
Clean up dependencies/types
gitKrystan Apr 25, 2024
d4d71d9
More package.json cleanup
gitKrystan Apr 25, 2024
28df890
Fix n/no-unpublished-import lint
gitKrystan Apr 26, 2024
5ecae44
Commit build
gitKrystan Apr 26, 2024
63d0f0c
pnpm i
gitKrystan Apr 26, 2024
3300e4c
Merge branch 'main' into codemods
gitKrystan May 2, 2024
1cbfbdc
Update package.json after git merge
gitKrystan May 2, 2024
80e4d75
Add store-names option
gitKrystan May 3, 2024
5827ebd
Add --method option
gitKrystan May 4, 2024
8c5ceb2
Special-case model hook return values to add await
gitKrystan May 7, 2024
5778baf
Add dist to default ignore list
gitKrystan May 8, 2024
317c6f7
Merge branch 'main' into codemods
gitKrystan May 8, 2024
7a78189
Fix lint
gitKrystan May 8, 2024
acf1f4a
Update README
gitKrystan May 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "bun",
"request": "launch",
"name": "Debug Bun",

// The path to a JavaScript or TypeScript file to run.
"program": "${file}",

// The arguments to pass to the program, if any.
"args": [],

// The working directory of the program.
"cwd": "${workspaceFolder}",

// The environment variables to pass to the program.
"env": {},

// If the environment variables should not be inherited from the parent process.
"strictEnv": false,

// If the program should be run in watch mode.
// This is equivalent to passing `--watch` to the `bun` executable.
// You can also set this to "hot" to enable hot reloading using `--hot`.
"watchMode": false,

// If the debugger should stop on the first line of the program.
"stopOnEntry": false,

// If the debugger should be disabled. (for example, breakpoints will not be hit)
"noDebug": false,

// The path to the `bun` executable, defaults to your `PATH` environment variable.
"runtime": "bun",

// The arguments to pass to the `bun` executable, if any.
// Unlike `args`, these are passed to the executable itself, not the program.
"runtimeArgs": []
},
{
"type": "bun",
"request": "attach",
"name": "Attach to Bun",

// The URL of the WebSocket inspector to attach to.
// This value can be retrieved by using `bun --inspect`.
"url": "ws://localhost:6499/"
}
]
}
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
"editor.insertSpaces": false,
"editor.tabSize": 4
},
"typescript.preferences.importModuleSpecifier": "project-relative"
"typescript.preferences.importModuleSpecifier": "project-relative",
"bun.debugTerminal.enabled": true
}
1 change: 1 addition & 0 deletions config/eslint/ignore.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const RULES = [
'node_modules',
'.node_modules.ember-try',
'package.json.ember-try',
'tests/__testfixtures__',
];

function ignoreRules(allowAddon) {
Expand Down
28 changes: 28 additions & 0 deletions packages/codemods/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const base = require('@warp-drive/internal-config/eslint/base.cjs');
const ignore = require('@warp-drive/internal-config/eslint/ignore.cjs');
const imports = require('@warp-drive/internal-config/eslint/imports.cjs');
const isolation = require('@warp-drive/internal-config/eslint/isolation.cjs');
const node = require('@warp-drive/internal-config/eslint/node.cjs');
const parser = require('@warp-drive/internal-config/eslint/parser.cjs');
const typescript = require('@warp-drive/internal-config/eslint/typescript.cjs');

module.exports = {
...parser.defaults(),
...base.settings(),

plugins: [...base.plugins(), ...imports.plugins()],
extends: [...base.extend()],
rules: Object.assign(base.rules(), imports.rules(), isolation.rules(), {}),

ignorePatterns: ignore.ignoreRules(),

overrides: [
node.config(),
node.defaults({
files: ['src/**', 'bin/**'],
}),
typescript.defaults({
rules: { '@typescript-eslint/switch-exhaustiveness-check': 'error' },
}),
],
};
135 changes: 135 additions & 0 deletions packages/codemods/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# @ember-data/codemods

Codemods for EmberData paradigms.

## Usage

### List all available codemod commands

```
npx @ember-data/codemods apply --help
```

The available codemods will be listed under the "Commands" heading.

### Run a codemod

```
npx @ember-data/codemods apply <codemod-name> [codemod-options] <target-glob-pattern...>
```

For example:

```
npx @ember-data/codemods apply legacy-compat-builders ./app/**/*.{js,ts}
```

#### Codemod options

To list the available options for a particular codemod, you can run:

```
npx @ember-data/codemods apply <codemod-name> --help
```

## Codemods

### legacy-compat-builders

```
npx @ember-data/codemods apply legacy-compat-builders --help
Usage: @ember-data/codemods apply legacy-compat-builders [options] <target-glob-pattern...>

Updates legacy store methods to use `store.request` and `@ember-data/legacy-compat/builders` instead.

Arguments:
target-glob-pattern Path to files or glob pattern. If using glob pattern, wrap in single
quotes.

Options:
-d, --dry dry run (no changes are made to files) (default: false)
-v, --verbose <level> Show more information about the transform process (choices: "0", "1",
"2", default: "0")
-l, --log-file [path] Write logs to a file. If option is set but no path is provided, logs are
written to ember-data-codemods.log
-i, --ignore <ignore-glob-pattern...> Ignores the given file or glob pattern. If using glob pattern, wrap in
single quotes.
--store-names <store-name...> Identifier name associated with the store. If overriding, it is
recommended that you include 'store' in your list. (default: ["store"])
--method, --methods <method-name...> Method name(s) to transform. By default, will transform all methods.
(choices: "findAll", "findRecord", "query", "queryRecord", "saveRecord")
-h, --help display help for command
```

### Examples

#### `findAll`

```ts
// before
const posts = await store.findAll<Post>('post');

// after
import { findAll } from '@ember-data/legacy-compat/builders';
const { content: posts } = await store.request<Post[]>(findAll<Post>('post'));
```

#### `findRecord`

```ts
// before
const post = await store.findRecord<Post>({ type: 'post', id: '1' });

// after
import { findRecord } from '@ember-data/legacy-compat/builders';
const { content: post } = await store.request<Post>(findRecord<Post>({ type: 'post', id: '1' }));
```

NOTE: This codemod will not transform `store.findRecord` calls with a 'preload' option set. This option is not supported by the legacy compat builders.

#### `query`

```ts
// before
const posts = await store.query<Post>('post', { id: '1' });

// after
import { query } from '@ember-data/legacy-compat/builders';
const { content: posts } = await store.request<Post[]>(query<Post>('post', { id: '1' }));
```

#### `queryRecord`

```ts
// before
const post = await store.queryRecord<Post>('post', { id: '1' });

// after
import { queryRecord } from '@ember-data/legacy-compat/builders';
const { content: post } = await store.request<Post>(queryRecord<Post>('post', { id: '1' }));
```

#### `saveRecord`

```ts
// before
const post = store.createRecord<Post>('post', { name: 'Krystan rules, you drool' });
const savedPostWithGeneric = await store.saveRecord<Post>(post);
const savedPostNoGeneric = await store.saveRecord(post);

// after
import { saveRecord } from '@ember-data/legacy-compat/builders';
const post = store.createRecord<Post>('post', { name: 'Krystan rules, you drool' });
const { content: savedPostWithGeneric } = await store.request<Post>(saveRecord(post));
const { content: savedPostNoGeneric } = await store.request(saveRecord(post));
```

### Handling of `await`

Calls to legacy store methods that are not currently awaited will not be transformed. In order to provide feature parity with the legacy method, we need to access the `content` property from `Future` returned by `store.request`. In order to do this, we need to `await store.request`, but we can't safely add `await` with a codemod as we don't know if the consuming code will be able to handle the change.

There is one exception to this rule. In the case where a route's `model` hook returns a call to a legacy store method, the codemod will transform the legacy store method and will add the `await` keyword.

## Caveats

GJS and GTS files are not currently supported. PRs welcome! 🧡
Loading
Loading