@trpc/upgrade 0.0.0-alpha.1 → 0.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +34 -34
- package/dist/transforms/hooksToOptions.cjs +37 -36
- package/dist/transforms/provider.cjs +14 -24
- package/package.json +1 -1
- package/src/bin/cli.ts +66 -47
- package/src/transforms/hooksToOptions.ts +37 -36
- package/src/transforms/provider.ts +17 -33
package/dist/cli.cjs
CHANGED
|
@@ -10,9 +10,9 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
10
10
|
|
|
11
11
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
12
12
|
|
|
13
|
-
var version = "0.0.0-alpha.
|
|
13
|
+
var version = "0.0.0-alpha.2";
|
|
14
14
|
|
|
15
|
-
platform.Command.string(platform.Command.make('git', 'status')).pipe(effect.Effect.filterOrFail(
|
|
15
|
+
const assertCleanGitTree = platform.Command.string(platform.Command.make('git', 'status')).pipe(effect.Effect.filterOrFail(effect.String.includes('nothing to commit'), ()=>'Git tree is not clean, please commit your changes and try again, or run with `--force`'));
|
|
16
16
|
const installPackage = (packageName)=>{
|
|
17
17
|
const packageManager = effect.Match.value(process.env.npm_config_user_agent ?? 'npm').pipe(effect.Match.when(effect.String.startsWith('pnpm'), ()=>'pnpm'), effect.Match.when(effect.String.startsWith('yarn'), ()=>'yarn'), effect.Match.when(effect.String.startsWith('bun'), ()=>'bun'), effect.Match.orElse(()=>'npm'));
|
|
18
18
|
return platform.Command.streamLines(platform.Command.make(packageManager, 'install', packageName)).pipe(effect.Stream.mapEffect(effect.Console.log), effect.Stream.runDrain);
|
|
@@ -28,50 +28,50 @@ const filterIgnored = (files)=>effect.Effect.gen(function*() {
|
|
|
28
28
|
yield* effect.Effect.log('Filtered files:', filteredSourcePaths);
|
|
29
29
|
return filteredSourcePaths;
|
|
30
30
|
});
|
|
31
|
-
const
|
|
31
|
+
const TSProgram = effect.Effect.succeed(typescript.findConfigFile(process.cwd(), typescript.sys.fileExists)).pipe(effect.Effect.filterOrFail(effect.Predicate.isNotNullable, ()=>'No tsconfig found'), effect.Effect.tap((_)=>effect.Effect.logDebug('Using tsconfig', _)), effect.Effect.map((_)=>typescript.readConfigFile(_, typescript.sys.readFile)), effect.Effect.map((_)=>typescript.parseJsonConfigFileContent(_.config, typescript.sys, process.cwd())), effect.Effect.map((_)=>typescript.createProgram({
|
|
32
32
|
options: _.options,
|
|
33
33
|
rootNames: _.fileNames,
|
|
34
34
|
configFileParsingDiagnostics: _.errors
|
|
35
35
|
})));
|
|
36
|
+
// FIXME :: hacky
|
|
36
37
|
const transformPath = (path)=>process.env.DEV ? path : path.replace('../', './').replace('.ts', '.cjs');
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
const force = cli$1.Options.boolean('force').pipe(cli$1.Options.withAlias('f'), cli$1.Options.withDefault(false), cli$1.Options.withDescription('Skip git status check, use with caution'));
|
|
39
|
+
/**
|
|
40
|
+
* TODO: Instead of default values these should be detected automatically from the TS program
|
|
41
|
+
*/ const trpcFile = cli$1.Options.text('trpcFile').pipe(cli$1.Options.withAlias('f'), cli$1.Options.withDefault('~/trpc'), cli$1.Options.withDescription('Path to the trpc import file'));
|
|
42
|
+
const trpcImportName = cli$1.Options.text('trpcImportName').pipe(cli$1.Options.withAlias('i'), cli$1.Options.withDefault('trpc'), cli$1.Options.withDescription('Name of the trpc import'));
|
|
43
|
+
const rootComamnd = cli$1.Command.make('upgrade', {
|
|
44
|
+
force,
|
|
45
|
+
trpcFile,
|
|
46
|
+
trpcImportName
|
|
47
|
+
}, (args)=>effect.Effect.gen(function*() {
|
|
48
|
+
if (!args.force) {
|
|
49
|
+
yield* assertCleanGitTree;
|
|
47
50
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
const transforms = yield* effect.Effect.map(cli$1.Prompt.multiSelect({
|
|
52
|
+
message: 'Select transforms to run',
|
|
53
|
+
choices: [
|
|
54
|
+
{
|
|
55
|
+
title: 'Migrate Hooks to xxxOptions API',
|
|
56
|
+
value: require.resolve(transformPath('../transforms/hooksToOptions.ts'))
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: 'Migrate context provider setup',
|
|
60
|
+
value: require.resolve(transformPath('../transforms/provider.ts'))
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}), // Make sure provider transform runs first if it's selected
|
|
64
|
+
effect.Array.sortWith((a)=>!a.includes('provider.ts'), effect.Order.boolean));
|
|
65
|
+
const program = yield* TSProgram;
|
|
52
66
|
const sourceFiles = program.getSourceFiles();
|
|
53
|
-
// Make sure provider transform runs first if it's selected
|
|
54
|
-
_.sort((a, b)=>a.includes('provider.ts') ? -1 : b.includes('provider.ts') ? 1 : 0);
|
|
55
|
-
/**
|
|
56
|
-
* TODO: Detect these automatically
|
|
57
|
-
*/ const appRouterImportFile = '~/server/routers/_app';
|
|
58
|
-
const appRouterImportName = 'AppRouter';
|
|
59
|
-
const trpcFile = '~/lib/trpc';
|
|
60
|
-
const trpcImportName = 'trpc';
|
|
61
67
|
const commitedFiles = yield* filterIgnored(sourceFiles);
|
|
62
|
-
yield* effect.Effect.forEach(
|
|
63
|
-
return effect.pipe(effect.Effect.log('Running transform', transform), effect.Effect.flatMap(()=>effect.Effect.tryPromise(async ()=>import('jscodeshift/src/Runner.js').then(({ run })=>run(transform.
|
|
64
|
-
appRouterImportFile,
|
|
65
|
-
appRouterImportName,
|
|
66
|
-
trpcFile,
|
|
67
|
-
trpcImportName
|
|
68
|
-
})))), effect.Effect.map((res)=>effect.Effect.log('Transform result', res)));
|
|
68
|
+
yield* effect.Effect.forEach(transforms, (transform)=>{
|
|
69
|
+
return effect.pipe(effect.Effect.log('Running transform', transform), effect.Effect.flatMap(()=>effect.Effect.tryPromise(async ()=>import('jscodeshift/src/Runner.js').then(({ run })=>run(transform, commitedFiles, args)))), effect.Effect.map((_)=>effect.Effect.log('Transform result', _)));
|
|
69
70
|
});
|
|
70
71
|
yield* effect.Effect.log('Installing @trpc/tanstack-react-query');
|
|
71
72
|
yield* installPackage('@trpc/tanstack-react-query');
|
|
72
|
-
// TODO: Format project
|
|
73
73
|
}));
|
|
74
|
-
const cli = cli$1.Command.run(
|
|
74
|
+
const cli = cli$1.Command.run(rootComamnd, {
|
|
75
75
|
name: 'tRPC Upgrade CLI',
|
|
76
76
|
version: `v${version}`
|
|
77
77
|
});
|
|
@@ -43,59 +43,50 @@ const utilMap = {
|
|
|
43
43
|
getInfiniteData: 'getInfiniteQueryData'
|
|
44
44
|
};
|
|
45
45
|
function transform(file, api, options) {
|
|
46
|
-
const {
|
|
47
|
-
if (!
|
|
48
|
-
throw new Error('
|
|
46
|
+
const { trpcImportName } = options;
|
|
47
|
+
if (!trpcImportName) {
|
|
48
|
+
throw new Error('trpcImportName is required');
|
|
49
49
|
}
|
|
50
50
|
const j = api.jscodeshift;
|
|
51
51
|
const root = j(file.source);
|
|
52
52
|
let dirtyFlag = false;
|
|
53
53
|
// Traverse all functions, and _do stuff_
|
|
54
54
|
root.find(j.FunctionDeclaration).forEach((path)=>{
|
|
55
|
-
if (j(path).find(j.Identifier, {
|
|
56
|
-
name: trpcImportName
|
|
57
|
-
}).size() > 0) {
|
|
58
|
-
updateTRPCImport(path);
|
|
59
|
-
}
|
|
60
55
|
replaceHooksWithOptions(path);
|
|
61
56
|
removeSuspenseDestructuring(path);
|
|
62
57
|
migrateUseUtils(path);
|
|
63
58
|
});
|
|
64
59
|
root.find(j.ArrowFunctionExpression).forEach((path)=>{
|
|
65
|
-
if (j(path).find(j.Identifier, {
|
|
66
|
-
name: trpcImportName
|
|
67
|
-
}).size() > 0) {
|
|
68
|
-
updateTRPCImport(path);
|
|
69
|
-
}
|
|
70
60
|
replaceHooksWithOptions(path);
|
|
71
61
|
removeSuspenseDestructuring(path);
|
|
72
62
|
migrateUseUtils(path);
|
|
73
63
|
});
|
|
64
|
+
if (dirtyFlag) {
|
|
65
|
+
updateTRPCImport();
|
|
66
|
+
}
|
|
74
67
|
/**
|
|
75
68
|
* === HELPER FUNCTIONS BELOW ===
|
|
76
|
-
*/ function
|
|
77
|
-
const specifier = root.find(j.ImportDeclaration, {
|
|
78
|
-
source: {
|
|
79
|
-
value: trpcFile
|
|
80
|
-
}
|
|
81
|
-
}).find(j.ImportSpecifier, {
|
|
82
|
-
imported: {
|
|
83
|
-
name: trpcImportName
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
if (specifier.size() === 0) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
specifier.replaceWith(j.importSpecifier(j.identifier('useTRPC')));
|
|
90
|
-
dirtyFlag = true;
|
|
69
|
+
*/ function ensureUseTRPCCall(path) {
|
|
91
70
|
const variableDeclaration = j.variableDeclaration('const', [
|
|
92
71
|
j.variableDeclarator(j.identifier(trpcImportName), j.callExpression(j.identifier('useTRPC'), []))
|
|
93
72
|
]);
|
|
94
73
|
if (j.FunctionDeclaration.check(path.node)) {
|
|
95
|
-
|
|
96
|
-
|
|
74
|
+
path.node.body.body.unshift(variableDeclaration);
|
|
75
|
+
dirtyFlag = true;
|
|
97
76
|
} else if (j.BlockStatement.check(path.node.body)) {
|
|
98
77
|
path.node.body.body.unshift(variableDeclaration);
|
|
78
|
+
dirtyFlag = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function updateTRPCImport() {
|
|
82
|
+
const specifier = root.find(j.ImportSpecifier, {
|
|
83
|
+
imported: {
|
|
84
|
+
name: trpcImportName
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
if (specifier.size() > 0) {
|
|
88
|
+
specifier.replaceWith(j.importSpecifier(j.identifier('useTRPC')));
|
|
89
|
+
dirtyFlag = true;
|
|
99
90
|
}
|
|
100
91
|
}
|
|
101
92
|
function ensureImported(lib, specifier) {
|
|
@@ -114,10 +105,11 @@ function transform(file, api, options) {
|
|
|
114
105
|
dirtyFlag = true;
|
|
115
106
|
}
|
|
116
107
|
}
|
|
117
|
-
function replaceHooksWithOptions(
|
|
108
|
+
function replaceHooksWithOptions(fnPath) {
|
|
118
109
|
// REplace proxy-hooks with useX(options())
|
|
110
|
+
let hasInserted = false;
|
|
119
111
|
for (const [hook, { fn, lib }] of Object.entries(hookToOptions)){
|
|
120
|
-
j(
|
|
112
|
+
j(fnPath).find(j.CallExpression, {
|
|
121
113
|
callee: {
|
|
122
114
|
property: {
|
|
123
115
|
name: hook
|
|
@@ -131,6 +123,10 @@ function transform(file, api, options) {
|
|
|
131
123
|
}
|
|
132
124
|
// Rename the hook to the options function
|
|
133
125
|
memberExpr.property.name = fn;
|
|
126
|
+
if (!hasInserted) {
|
|
127
|
+
ensureUseTRPCCall(fnPath);
|
|
128
|
+
hasInserted = true;
|
|
129
|
+
}
|
|
134
130
|
// Wrap it in the hook call
|
|
135
131
|
j(path).replaceWith(j.callExpression(j.identifier(hook), [
|
|
136
132
|
path.node
|
|
@@ -172,7 +168,7 @@ function transform(file, api, options) {
|
|
|
172
168
|
}
|
|
173
169
|
// Replace util.PATH.proxyMethod() with trpc.PATH.queryFilter()
|
|
174
170
|
const proxyMethod = memberExpr.property.name;
|
|
175
|
-
memberExpr.object.object = j.identifier(
|
|
171
|
+
memberExpr.object.object = j.identifier(trpcImportName);
|
|
176
172
|
memberExpr.property = j.identifier('queryFilter');
|
|
177
173
|
// Wrap it in queryClient.utilMethod()
|
|
178
174
|
j(callExprPath).replaceWith(j.memberExpression(j.identifier('queryClient'), j.callExpression(j.identifier(utilMap[proxyMethod]), [
|
|
@@ -198,11 +194,10 @@ function transform(file, api, options) {
|
|
|
198
194
|
].includes(declarator.init.callee.name)) {
|
|
199
195
|
return;
|
|
200
196
|
}
|
|
201
|
-
console.log(declarator.init.callee.name);
|
|
202
197
|
const tuple = j.ArrayPattern.check(declarator?.id) ? declarator.id : null;
|
|
203
198
|
const dataName = j.Identifier.check(tuple?.elements?.[0]) ? tuple.elements[0].name : null;
|
|
204
199
|
const queryName = j.Identifier.check(tuple?.elements?.[1]) ? tuple.elements[1].name : null;
|
|
205
|
-
if (
|
|
200
|
+
if (queryName) {
|
|
206
201
|
declarator.id = j.identifier(queryName);
|
|
207
202
|
dirtyFlag = true;
|
|
208
203
|
if (dataName) {
|
|
@@ -210,12 +205,18 @@ function transform(file, api, options) {
|
|
|
210
205
|
j.variableDeclarator(j.identifier(dataName), j.memberExpression(declarator.id, j.identifier('data')))
|
|
211
206
|
]));
|
|
212
207
|
}
|
|
208
|
+
} else if (dataName) {
|
|
209
|
+
// const [dataName] = ... => const { data: dataName } = ...
|
|
210
|
+
declarator.id = j.objectPattern([
|
|
211
|
+
j.property('init', j.identifier('data'), j.identifier(dataName))
|
|
212
|
+
]);
|
|
213
|
+
dirtyFlag = true;
|
|
213
214
|
}
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
return dirtyFlag ? root.toSource() : undefined;
|
|
217
218
|
}
|
|
218
|
-
const parser = 'tsx';
|
|
219
|
+
const parser = 'tsx';
|
|
219
220
|
|
|
220
221
|
exports.default = transform;
|
|
221
222
|
exports.parser = parser;
|
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
2
|
|
|
3
3
|
function transform(file, api, options) {
|
|
4
|
-
const {
|
|
4
|
+
const { trpcImportName } = options;
|
|
5
|
+
let routerName = undefined;
|
|
5
6
|
const j = api.jscodeshift;
|
|
6
7
|
const root = j(file.source);
|
|
7
8
|
let dirtyFlag = false;
|
|
8
|
-
function upsertAppRouterImport() {
|
|
9
|
-
if (root.find(j.ImportDeclaration, {
|
|
10
|
-
source: {
|
|
11
|
-
value: appRouterImportFile
|
|
12
|
-
}
|
|
13
|
-
}).size() === 0) {
|
|
14
|
-
root.find(j.ImportDeclaration).at(-1).insertAfter(j.importDeclaration([
|
|
15
|
-
j.importSpecifier(j.identifier(appRouterImportName))
|
|
16
|
-
], j.literal(appRouterImportFile), 'type'));
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
9
|
// Find the variable declaration for `trpc`
|
|
20
10
|
root.find(j.VariableDeclaration).forEach((path)=>{
|
|
21
11
|
const declaration = path.node.declarations[0];
|
|
22
12
|
if (j.Identifier.check(declaration.id) && declaration.id.name === trpcImportName) {
|
|
23
13
|
if (j.CallExpression.check(declaration.init) && j.Identifier.check(declaration.init.callee) && declaration.init.callee.name === 'createTRPCReact') {
|
|
14
|
+
// Get router name ( TODO : should probably get this from the TS compiler along with the import path)
|
|
15
|
+
routerName = declaration.init.original?.typeParameters?.params?.[0]?.typeName?.name;
|
|
24
16
|
// Replace the `createTRPCReact` call with `createTRPCContext`
|
|
25
17
|
declaration.init.callee.name = 'createTRPCContext';
|
|
26
18
|
// Destructure the result into `TRPCProvider` and `useTRPC`
|
|
@@ -57,7 +49,7 @@ function transform(file, api, options) {
|
|
|
57
49
|
});
|
|
58
50
|
});
|
|
59
51
|
}
|
|
60
|
-
// Replace trpc.createClient with createTRPCClient
|
|
52
|
+
// Replace trpc.createClient with createTRPCClient<TRouter>
|
|
61
53
|
root.find(j.CallExpression, {
|
|
62
54
|
callee: {
|
|
63
55
|
object: {
|
|
@@ -69,12 +61,12 @@ function transform(file, api, options) {
|
|
|
69
61
|
}
|
|
70
62
|
}).forEach((path)=>{
|
|
71
63
|
path.node.callee = j.identifier('createTRPCClient');
|
|
72
|
-
// Add the type parameter `<AppRouter>`
|
|
73
|
-
path.node.typeParameters = j.tsTypeParameterInstantiation([
|
|
74
|
-
j.tsTypeReference(j.identifier(appRouterImportName))
|
|
75
|
-
]);
|
|
76
|
-
upsertAppRouterImport();
|
|
77
64
|
dirtyFlag = true;
|
|
65
|
+
if (routerName) {
|
|
66
|
+
path.node.typeParameters = j.tsTypeParameterInstantiation([
|
|
67
|
+
j.tsTypeReference(j.identifier(routerName))
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
78
70
|
});
|
|
79
71
|
// Replace <trpc.Provider client={...} with <TRPCProvider trpcClient={...}
|
|
80
72
|
root.find(j.JSXElement, {
|
|
@@ -112,14 +104,12 @@ function transform(file, api, options) {
|
|
|
112
104
|
path.node.specifiers?.push(createTRPCClientImport);
|
|
113
105
|
});
|
|
114
106
|
// Replace trpc import with TRPCProvider
|
|
115
|
-
root.find(j.
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
root.find(j.ImportSpecifier, {
|
|
108
|
+
imported: {
|
|
109
|
+
name: trpcImportName
|
|
118
110
|
}
|
|
119
111
|
}).forEach((path)=>{
|
|
120
|
-
path.node.
|
|
121
|
-
j.importSpecifier(j.identifier('TRPCProvider'))
|
|
122
|
-
];
|
|
112
|
+
path.node.name = j.identifier('TRPCProvider');
|
|
123
113
|
});
|
|
124
114
|
}
|
|
125
115
|
return dirtyFlag ? root.toSource() : undefined;
|
package/package.json
CHANGED
package/src/bin/cli.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { Command as CLICommand, Prompt } from '@effect/cli';
|
|
3
|
+
import { Command as CLICommand, Options, Prompt } from '@effect/cli';
|
|
4
4
|
import { Command } from '@effect/platform';
|
|
5
5
|
import { NodeContext, NodeRuntime } from '@effect/platform-node';
|
|
6
6
|
import {
|
|
7
|
+
Array,
|
|
7
8
|
Console,
|
|
8
9
|
Effect,
|
|
9
10
|
Match,
|
|
11
|
+
Order,
|
|
10
12
|
pipe,
|
|
11
13
|
Predicate,
|
|
12
14
|
Stream,
|
|
@@ -24,9 +26,9 @@ import { version } from '../../package.json';
|
|
|
24
26
|
|
|
25
27
|
const assertCleanGitTree = Command.string(Command.make('git', 'status')).pipe(
|
|
26
28
|
Effect.filterOrFail(
|
|
27
|
-
|
|
29
|
+
String.includes('nothing to commit'),
|
|
28
30
|
() =>
|
|
29
|
-
'Git tree is not clean, please commit your changes
|
|
31
|
+
'Git tree is not clean, please commit your changes and try again, or run with `--force`',
|
|
30
32
|
),
|
|
31
33
|
);
|
|
32
34
|
|
|
@@ -71,7 +73,7 @@ const filterIgnored = (files: readonly SourceFile[]) =>
|
|
|
71
73
|
return filteredSourcePaths;
|
|
72
74
|
});
|
|
73
75
|
|
|
74
|
-
const
|
|
76
|
+
const TSProgram = Effect.succeed(
|
|
75
77
|
findConfigFile(process.cwd(), sys.fileExists),
|
|
76
78
|
).pipe(
|
|
77
79
|
Effect.filterOrFail(Predicate.isNotNullable, () => 'No tsconfig found'),
|
|
@@ -87,73 +89,90 @@ const Program = Effect.succeed(
|
|
|
87
89
|
),
|
|
88
90
|
);
|
|
89
91
|
|
|
92
|
+
// FIXME :: hacky
|
|
90
93
|
const transformPath = (path: string) =>
|
|
91
94
|
process.env.DEV ? path : path.replace('../', './').replace('.ts', '.cjs');
|
|
92
95
|
|
|
93
|
-
const
|
|
94
|
-
'
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const program = yield* Program;
|
|
114
|
-
const sourceFiles = program.getSourceFiles();
|
|
96
|
+
const force = Options.boolean('force').pipe(
|
|
97
|
+
Options.withAlias('f'),
|
|
98
|
+
Options.withDefault(false),
|
|
99
|
+
Options.withDescription('Skip git status check, use with caution'),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* TODO: Instead of default values these should be detected automatically from the TS program
|
|
104
|
+
*/
|
|
105
|
+
const trpcFile = Options.text('trpcFile').pipe(
|
|
106
|
+
Options.withAlias('f'),
|
|
107
|
+
Options.withDefault('~/trpc'),
|
|
108
|
+
Options.withDescription('Path to the trpc import file'),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const trpcImportName = Options.text('trpcImportName').pipe(
|
|
112
|
+
Options.withAlias('i'),
|
|
113
|
+
Options.withDefault('trpc'),
|
|
114
|
+
Options.withDescription('Name of the trpc import'),
|
|
115
|
+
);
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
const rootComamnd = CLICommand.make(
|
|
118
|
+
'upgrade',
|
|
119
|
+
{
|
|
120
|
+
force,
|
|
121
|
+
trpcFile,
|
|
122
|
+
trpcImportName,
|
|
123
|
+
},
|
|
124
|
+
(args) =>
|
|
125
|
+
Effect.gen(function* () {
|
|
126
|
+
if (!args.force) {
|
|
127
|
+
yield* assertCleanGitTree;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const transforms = yield* Effect.map(
|
|
131
|
+
Prompt.multiSelect({
|
|
132
|
+
message: 'Select transforms to run',
|
|
133
|
+
choices: [
|
|
134
|
+
{
|
|
135
|
+
title: 'Migrate Hooks to xxxOptions API',
|
|
136
|
+
value: require.resolve(
|
|
137
|
+
transformPath('../transforms/hooksToOptions.ts'),
|
|
138
|
+
),
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
title: 'Migrate context provider setup',
|
|
142
|
+
value: require.resolve(
|
|
143
|
+
transformPath('../transforms/provider.ts'),
|
|
144
|
+
),
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
}),
|
|
148
|
+
// Make sure provider transform runs first if it's selected
|
|
149
|
+
Array.sortWith((a) => !a.includes('provider.ts'), Order.boolean),
|
|
119
150
|
);
|
|
120
151
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
*/
|
|
124
|
-
const appRouterImportFile = '~/server/routers/_app';
|
|
125
|
-
const appRouterImportName = 'AppRouter';
|
|
126
|
-
const trpcFile = '~/lib/trpc';
|
|
127
|
-
const trpcImportName = 'trpc';
|
|
152
|
+
const program = yield* TSProgram;
|
|
153
|
+
const sourceFiles = program.getSourceFiles();
|
|
128
154
|
|
|
129
155
|
const commitedFiles = yield* filterIgnored(sourceFiles);
|
|
130
|
-
yield* Effect.forEach(
|
|
156
|
+
yield* Effect.forEach(transforms, (transform) => {
|
|
131
157
|
return pipe(
|
|
132
158
|
Effect.log('Running transform', transform),
|
|
133
159
|
Effect.flatMap(() =>
|
|
134
160
|
Effect.tryPromise(async () =>
|
|
135
161
|
import('jscodeshift/src/Runner.js').then(({ run }) =>
|
|
136
|
-
run(transform
|
|
137
|
-
appRouterImportFile,
|
|
138
|
-
appRouterImportName,
|
|
139
|
-
trpcFile,
|
|
140
|
-
trpcImportName,
|
|
141
|
-
}),
|
|
162
|
+
run(transform, commitedFiles, args),
|
|
142
163
|
),
|
|
143
164
|
),
|
|
144
165
|
),
|
|
145
|
-
Effect.map((
|
|
166
|
+
Effect.map((_) => Effect.log('Transform result', _)),
|
|
146
167
|
);
|
|
147
168
|
});
|
|
148
169
|
|
|
149
170
|
yield* Effect.log('Installing @trpc/tanstack-react-query');
|
|
150
171
|
yield* installPackage('@trpc/tanstack-react-query');
|
|
151
|
-
|
|
152
|
-
// TODO: Format project
|
|
153
172
|
}),
|
|
154
173
|
);
|
|
155
174
|
|
|
156
|
-
const cli = CLICommand.run(
|
|
175
|
+
const cli = CLICommand.run(rootComamnd, {
|
|
157
176
|
name: 'tRPC Upgrade CLI',
|
|
158
177
|
version: `v${version}`,
|
|
159
178
|
});
|
|
@@ -13,7 +13,6 @@ import type {
|
|
|
13
13
|
} from 'jscodeshift';
|
|
14
14
|
|
|
15
15
|
interface TransformOptions extends Options {
|
|
16
|
-
trpcFile?: string;
|
|
17
16
|
trpcImportName?: string;
|
|
18
17
|
}
|
|
19
18
|
|
|
@@ -61,9 +60,9 @@ export default function transform(
|
|
|
61
60
|
api: API,
|
|
62
61
|
options: TransformOptions,
|
|
63
62
|
) {
|
|
64
|
-
const {
|
|
65
|
-
if (!
|
|
66
|
-
throw new Error('
|
|
63
|
+
const { trpcImportName } = options;
|
|
64
|
+
if (!trpcImportName) {
|
|
65
|
+
throw new Error('trpcImportName is required');
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
const j = api.jscodeshift;
|
|
@@ -72,44 +71,27 @@ export default function transform(
|
|
|
72
71
|
|
|
73
72
|
// Traverse all functions, and _do stuff_
|
|
74
73
|
root.find(j.FunctionDeclaration).forEach((path) => {
|
|
75
|
-
if (j(path).find(j.Identifier, { name: trpcImportName }).size() > 0) {
|
|
76
|
-
updateTRPCImport(path);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
74
|
replaceHooksWithOptions(path);
|
|
80
75
|
removeSuspenseDestructuring(path);
|
|
81
76
|
migrateUseUtils(path);
|
|
82
77
|
});
|
|
83
78
|
root.find(j.ArrowFunctionExpression).forEach((path) => {
|
|
84
|
-
if (j(path).find(j.Identifier, { name: trpcImportName }).size() > 0) {
|
|
85
|
-
updateTRPCImport(path);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
79
|
replaceHooksWithOptions(path);
|
|
89
80
|
removeSuspenseDestructuring(path);
|
|
90
81
|
migrateUseUtils(path);
|
|
91
82
|
});
|
|
92
83
|
|
|
84
|
+
if (dirtyFlag) {
|
|
85
|
+
updateTRPCImport();
|
|
86
|
+
}
|
|
87
|
+
|
|
93
88
|
/**
|
|
94
89
|
* === HELPER FUNCTIONS BELOW ===
|
|
95
90
|
*/
|
|
96
91
|
|
|
97
|
-
function
|
|
92
|
+
function ensureUseTRPCCall(
|
|
98
93
|
path: ASTPath<FunctionDeclaration | ArrowFunctionExpression>,
|
|
99
94
|
) {
|
|
100
|
-
const specifier = root
|
|
101
|
-
.find(j.ImportDeclaration, {
|
|
102
|
-
source: { value: trpcFile },
|
|
103
|
-
})
|
|
104
|
-
.find(j.ImportSpecifier, { imported: { name: trpcImportName } });
|
|
105
|
-
|
|
106
|
-
if (specifier.size() === 0) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
specifier.replaceWith(j.importSpecifier(j.identifier('useTRPC')));
|
|
111
|
-
dirtyFlag = true;
|
|
112
|
-
|
|
113
95
|
const variableDeclaration = j.variableDeclaration('const', [
|
|
114
96
|
j.variableDeclarator(
|
|
115
97
|
j.identifier(trpcImportName!),
|
|
@@ -118,10 +100,21 @@ export default function transform(
|
|
|
118
100
|
]);
|
|
119
101
|
|
|
120
102
|
if (j.FunctionDeclaration.check(path.node)) {
|
|
121
|
-
|
|
122
|
-
|
|
103
|
+
path.node.body.body.unshift(variableDeclaration);
|
|
104
|
+
dirtyFlag = true;
|
|
123
105
|
} else if (j.BlockStatement.check(path.node.body)) {
|
|
124
106
|
path.node.body.body.unshift(variableDeclaration);
|
|
107
|
+
dirtyFlag = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function updateTRPCImport() {
|
|
112
|
+
const specifier = root.find(j.ImportSpecifier, {
|
|
113
|
+
imported: { name: trpcImportName },
|
|
114
|
+
});
|
|
115
|
+
if (specifier.size() > 0) {
|
|
116
|
+
specifier.replaceWith(j.importSpecifier(j.identifier('useTRPC')));
|
|
117
|
+
dirtyFlag = true;
|
|
125
118
|
}
|
|
126
119
|
}
|
|
127
120
|
|
|
@@ -148,11 +141,12 @@ export default function transform(
|
|
|
148
141
|
}
|
|
149
142
|
|
|
150
143
|
function replaceHooksWithOptions(
|
|
151
|
-
|
|
144
|
+
fnPath: ASTPath<FunctionDeclaration | ArrowFunctionExpression>,
|
|
152
145
|
) {
|
|
153
146
|
// REplace proxy-hooks with useX(options())
|
|
147
|
+
let hasInserted = false;
|
|
154
148
|
for (const [hook, { fn, lib }] of Object.entries(hookToOptions)) {
|
|
155
|
-
j(
|
|
149
|
+
j(fnPath)
|
|
156
150
|
.find(j.CallExpression, {
|
|
157
151
|
callee: {
|
|
158
152
|
property: { name: hook },
|
|
@@ -171,6 +165,11 @@ export default function transform(
|
|
|
171
165
|
// Rename the hook to the options function
|
|
172
166
|
memberExpr.property.name = fn;
|
|
173
167
|
|
|
168
|
+
if (!hasInserted) {
|
|
169
|
+
ensureUseTRPCCall(fnPath);
|
|
170
|
+
hasInserted = true;
|
|
171
|
+
}
|
|
172
|
+
|
|
174
173
|
// Wrap it in the hook call
|
|
175
174
|
j(path).replaceWith(
|
|
176
175
|
j.callExpression(j.identifier(hook), [path.node]),
|
|
@@ -237,7 +236,7 @@ export default function transform(
|
|
|
237
236
|
|
|
238
237
|
// Replace util.PATH.proxyMethod() with trpc.PATH.queryFilter()
|
|
239
238
|
const proxyMethod = memberExpr.property.name as ProxyMethod;
|
|
240
|
-
memberExpr.object.object = j.identifier(
|
|
239
|
+
memberExpr.object.object = j.identifier(trpcImportName!);
|
|
241
240
|
memberExpr.property = j.identifier('queryFilter');
|
|
242
241
|
|
|
243
242
|
// Wrap it in queryClient.utilMethod()
|
|
@@ -285,8 +284,6 @@ export default function transform(
|
|
|
285
284
|
return;
|
|
286
285
|
}
|
|
287
286
|
|
|
288
|
-
console.log(declarator.init.callee.name);
|
|
289
|
-
|
|
290
287
|
const tuple = j.ArrayPattern.check(declarator?.id)
|
|
291
288
|
? declarator.id
|
|
292
289
|
: null;
|
|
@@ -297,7 +294,7 @@ export default function transform(
|
|
|
297
294
|
? tuple.elements[1].name
|
|
298
295
|
: null;
|
|
299
296
|
|
|
300
|
-
if (
|
|
297
|
+
if (queryName) {
|
|
301
298
|
declarator.id = j.identifier(queryName);
|
|
302
299
|
dirtyFlag = true;
|
|
303
300
|
|
|
@@ -311,6 +308,12 @@ export default function transform(
|
|
|
311
308
|
]),
|
|
312
309
|
);
|
|
313
310
|
}
|
|
311
|
+
} else if (dataName) {
|
|
312
|
+
// const [dataName] = ... => const { data: dataName } = ...
|
|
313
|
+
declarator.id = j.objectPattern([
|
|
314
|
+
j.property('init', j.identifier('data'), j.identifier(dataName)),
|
|
315
|
+
]);
|
|
316
|
+
dirtyFlag = true;
|
|
314
317
|
}
|
|
315
318
|
});
|
|
316
319
|
}
|
|
@@ -319,5 +322,3 @@ export default function transform(
|
|
|
319
322
|
}
|
|
320
323
|
|
|
321
324
|
export const parser = 'tsx';
|
|
322
|
-
|
|
323
|
-
// https://go.codemod.com/ddX54TM
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { API, FileInfo, Options } from 'jscodeshift';
|
|
2
2
|
|
|
3
3
|
interface TransformOptions extends Options {
|
|
4
|
-
trpcFile?: string;
|
|
5
4
|
trpcImportName?: string;
|
|
6
5
|
}
|
|
7
6
|
|
|
@@ -10,32 +9,13 @@ export default function transform(
|
|
|
10
9
|
api: API,
|
|
11
10
|
options: TransformOptions,
|
|
12
11
|
) {
|
|
13
|
-
const {
|
|
14
|
-
|
|
12
|
+
const { trpcImportName } = options;
|
|
13
|
+
let routerName: string | undefined = undefined;
|
|
15
14
|
|
|
16
15
|
const j = api.jscodeshift;
|
|
17
16
|
const root = j(file.source);
|
|
18
17
|
let dirtyFlag = false;
|
|
19
18
|
|
|
20
|
-
function upsertAppRouterImport() {
|
|
21
|
-
if (
|
|
22
|
-
root
|
|
23
|
-
.find(j.ImportDeclaration, { source: { value: appRouterImportFile } })
|
|
24
|
-
.size() === 0
|
|
25
|
-
) {
|
|
26
|
-
root
|
|
27
|
-
.find(j.ImportDeclaration)
|
|
28
|
-
.at(-1)
|
|
29
|
-
.insertAfter(
|
|
30
|
-
j.importDeclaration(
|
|
31
|
-
[j.importSpecifier(j.identifier(appRouterImportName))],
|
|
32
|
-
j.literal(appRouterImportFile),
|
|
33
|
-
'type',
|
|
34
|
-
),
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
19
|
// Find the variable declaration for `trpc`
|
|
40
20
|
root.find(j.VariableDeclaration).forEach((path) => {
|
|
41
21
|
const declaration = path.node.declarations[0];
|
|
@@ -48,6 +28,11 @@ export default function transform(
|
|
|
48
28
|
j.Identifier.check(declaration.init.callee) &&
|
|
49
29
|
declaration.init.callee.name === 'createTRPCReact'
|
|
50
30
|
) {
|
|
31
|
+
// Get router name ( TODO : should probably get this from the TS compiler along with the import path)
|
|
32
|
+
routerName =
|
|
33
|
+
declaration.init.original?.typeParameters?.params?.[0]?.typeName
|
|
34
|
+
?.name;
|
|
35
|
+
|
|
51
36
|
// Replace the `createTRPCReact` call with `createTRPCContext`
|
|
52
37
|
declaration.init.callee.name = 'createTRPCContext';
|
|
53
38
|
|
|
@@ -89,7 +74,7 @@ export default function transform(
|
|
|
89
74
|
});
|
|
90
75
|
}
|
|
91
76
|
|
|
92
|
-
// Replace trpc.createClient with createTRPCClient
|
|
77
|
+
// Replace trpc.createClient with createTRPCClient<TRouter>
|
|
93
78
|
root
|
|
94
79
|
.find(j.CallExpression, {
|
|
95
80
|
callee: {
|
|
@@ -99,12 +84,13 @@ export default function transform(
|
|
|
99
84
|
})
|
|
100
85
|
.forEach((path) => {
|
|
101
86
|
path.node.callee = j.identifier('createTRPCClient');
|
|
102
|
-
// Add the type parameter `<AppRouter>`
|
|
103
|
-
path.node.typeParameters = j.tsTypeParameterInstantiation([
|
|
104
|
-
j.tsTypeReference(j.identifier(appRouterImportName)),
|
|
105
|
-
]);
|
|
106
|
-
upsertAppRouterImport();
|
|
107
87
|
dirtyFlag = true;
|
|
88
|
+
|
|
89
|
+
if (routerName) {
|
|
90
|
+
(path.node as any).typeParameters = j.tsTypeParameterInstantiation([
|
|
91
|
+
j.tsTypeReference(j.identifier(routerName)),
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
108
94
|
});
|
|
109
95
|
|
|
110
96
|
// Replace <trpc.Provider client={...} with <TRPCProvider trpcClient={...}
|
|
@@ -146,13 +132,11 @@ export default function transform(
|
|
|
146
132
|
|
|
147
133
|
// Replace trpc import with TRPCProvider
|
|
148
134
|
root
|
|
149
|
-
.find(j.
|
|
150
|
-
|
|
135
|
+
.find(j.ImportSpecifier, {
|
|
136
|
+
imported: { name: trpcImportName },
|
|
151
137
|
})
|
|
152
138
|
.forEach((path) => {
|
|
153
|
-
path.node.
|
|
154
|
-
j.importSpecifier(j.identifier('TRPCProvider')),
|
|
155
|
-
];
|
|
139
|
+
path.node.name = j.identifier('TRPCProvider');
|
|
156
140
|
});
|
|
157
141
|
}
|
|
158
142
|
|