@trpc/upgrade 11.0.0-rc.800 → 11.0.0-rc.802
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/bin.js +232 -0
- package/package.json +6 -7
- package/src/bin/index.ts +86 -233
- package/src/lib/ast/scanners.ts +49 -26
- package/src/lib/execa.ts +4 -0
- package/src/lib/git.ts +44 -0
- package/src/lib/pkgmgr.ts +38 -0
- package/dist/bin.cjs +0 -178
package/dist/bin.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { basename, extname } from 'node:path';
|
|
3
|
+
import { parse } from '@bomb.sh/args';
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
5
|
+
import { cancel, log, intro, multiselect, isCancel, outro } from '@clack/prompts';
|
|
6
|
+
import * as ts from 'typescript';
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
import { exec } from 'node:child_process';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
import __node_cjsModule from 'node:module';
|
|
11
|
+
|
|
12
|
+
var version = "11.0.0-rc.802+1eccc1171";
|
|
13
|
+
|
|
14
|
+
function getProgram() {
|
|
15
|
+
const configFile = ts.findConfigFile(process.cwd(), (filepath)=>ts.sys.fileExists(filepath));
|
|
16
|
+
if (!configFile) {
|
|
17
|
+
p.log.error('No tsconfig found');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (process.env.VERBOSE) {
|
|
21
|
+
p.log.info(`Using tsconfig: ${configFile}`);
|
|
22
|
+
}
|
|
23
|
+
const { config } = ts.readConfigFile(configFile, (filepath)=>ts.sys.readFile(filepath));
|
|
24
|
+
const parsedConfig = ts.parseJsonConfigFileContent(config, ts.sys, process.cwd());
|
|
25
|
+
const program = ts.createProgram({
|
|
26
|
+
options: parsedConfig.options,
|
|
27
|
+
rootNames: parsedConfig.fileNames,
|
|
28
|
+
configFileParsingDiagnostics: parsedConfig.errors
|
|
29
|
+
});
|
|
30
|
+
return program;
|
|
31
|
+
}
|
|
32
|
+
function findSourceAndImportName(program) {
|
|
33
|
+
const files = program.getSourceFiles().filter((sourceFile)=>{
|
|
34
|
+
if (sourceFile.isDeclarationFile) return false;
|
|
35
|
+
let found = false;
|
|
36
|
+
ts.forEachChild(sourceFile, (node)=>{
|
|
37
|
+
if (!found && ts.isImportDeclaration(node)) {
|
|
38
|
+
const { moduleSpecifier } = node;
|
|
39
|
+
if (ts.isStringLiteral(moduleSpecifier) && moduleSpecifier.text.includes('@trpc/react-query')) {
|
|
40
|
+
found = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return found;
|
|
45
|
+
});
|
|
46
|
+
let importName = 'trpc';
|
|
47
|
+
files.forEach((sourceFile)=>{
|
|
48
|
+
ts.forEachChild(sourceFile, (node)=>{
|
|
49
|
+
if (ts.isVariableStatement(node) && node.modifiers?.some((mod)=>mod.getText(sourceFile) === 'export')) {
|
|
50
|
+
node.declarationList.declarations.forEach((declaration)=>{
|
|
51
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer && ts.isCallExpression(declaration.initializer) && ts.isIdentifier(declaration.initializer.expression) && declaration.initializer.expression.getText(sourceFile) === 'createTRPCReact') {
|
|
52
|
+
importName = declaration.name.getText(sourceFile);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
files: files.map((d)=>d.fileName),
|
|
60
|
+
importName
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function findTRPCImportReferences(program) {
|
|
64
|
+
const { files: filesImportingTRPC, importName } = findSourceAndImportName(program);
|
|
65
|
+
const trpcReferenceSpecifiers = new Map();
|
|
66
|
+
program.getSourceFiles().forEach((sourceFile)=>{
|
|
67
|
+
if (sourceFile.isDeclarationFile) return;
|
|
68
|
+
ts.forEachChild(sourceFile, (node)=>{
|
|
69
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
70
|
+
const resolved = ts.resolveModuleName(node.moduleSpecifier.text, sourceFile.fileName, program.getCompilerOptions(), ts.sys);
|
|
71
|
+
if (resolved.resolvedModule && filesImportingTRPC.includes(resolved.resolvedModule.resolvedFileName)) {
|
|
72
|
+
trpcReferenceSpecifiers.set(resolved.resolvedModule.resolvedFileName, node.moduleSpecifier.text);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
const counts = {};
|
|
78
|
+
let currentMax = 0;
|
|
79
|
+
const mostUsed = {
|
|
80
|
+
file: ''
|
|
81
|
+
};
|
|
82
|
+
[
|
|
83
|
+
...trpcReferenceSpecifiers.values()
|
|
84
|
+
].forEach((specifier)=>{
|
|
85
|
+
counts[specifier] = (counts[specifier] || 0) + 1;
|
|
86
|
+
if (counts[specifier] > currentMax) {
|
|
87
|
+
currentMax = counts[specifier];
|
|
88
|
+
mostUsed.file = specifier;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
importName,
|
|
93
|
+
mostUsed,
|
|
94
|
+
all: Object.fromEntries(trpcReferenceSpecifiers.entries())
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const execa = promisify(exec);
|
|
99
|
+
|
|
100
|
+
async function assertCleanGitTree() {
|
|
101
|
+
const { stdout } = await execa('git status');
|
|
102
|
+
if (!stdout.includes('nothing to commit')) {
|
|
103
|
+
cancel('Git tree is not clean, please commit your changes and try again, or run with `--force`');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function filterIgnored(files) {
|
|
108
|
+
const { stdout } = await execa('git check-ignore **/*');
|
|
109
|
+
const ignores = stdout.split('\n');
|
|
110
|
+
if (process.env.VERBOSE) {
|
|
111
|
+
log.info(`cwd: ${process.cwd()}`);
|
|
112
|
+
log.info(`All files in program: ${files.map((file)=>file.fileName).join(', ')}`);
|
|
113
|
+
log.info(`Ignored files: ${ignores.join(', ')}`);
|
|
114
|
+
}
|
|
115
|
+
// Ignore "common files"
|
|
116
|
+
const filteredSourcePaths = files.filter((source)=>source.fileName.startsWith(resolve()) && // only look ahead of current directory
|
|
117
|
+
!source.fileName.includes('/trpc/packages/') && // relative paths when running codemod locally
|
|
118
|
+
!source.fileName.includes('/node_modules/') && // always ignore node_modules
|
|
119
|
+
!ignores.includes(source.fileName)).map((source)=>source.fileName);
|
|
120
|
+
if (process.env.VERBOSE) {
|
|
121
|
+
log.info(`Filtered files: ${filteredSourcePaths.join(', ')}`);
|
|
122
|
+
}
|
|
123
|
+
return filteredSourcePaths;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function getPackageManager() {
|
|
127
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
128
|
+
if (userAgent?.startsWith('pnpm')) return 'pnpm';
|
|
129
|
+
if (userAgent?.startsWith('yarn')) return 'yarn';
|
|
130
|
+
if (userAgent?.startsWith('bun')) return 'bun';
|
|
131
|
+
return 'npm';
|
|
132
|
+
}
|
|
133
|
+
async function installPackage(packageName) {
|
|
134
|
+
const packageManager = getPackageManager();
|
|
135
|
+
const installCmd = packageManager === 'yarn' ? 'add' : 'install';
|
|
136
|
+
const { stdout, stderr } = await execa(`${packageManager} ${installCmd} ${packageName}`);
|
|
137
|
+
if (stderr) {
|
|
138
|
+
log.error(stderr);
|
|
139
|
+
}
|
|
140
|
+
if (process.env.VERBOSE) {
|
|
141
|
+
log.info(stdout);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function uninstallPackage(packageName) {
|
|
145
|
+
const packageManager = getPackageManager();
|
|
146
|
+
const uninstallCmd = packageManager === 'yarn' ? 'remove' : 'uninstall';
|
|
147
|
+
const { stdout, stderr } = await execa(`${packageManager} ${uninstallCmd} ${packageName}`);
|
|
148
|
+
if (stderr) {
|
|
149
|
+
log.error(stderr);
|
|
150
|
+
}
|
|
151
|
+
if (process.env.VERBOSE) {
|
|
152
|
+
log.info(stdout);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const require = __node_cjsModule.createRequire(import.meta.url);
|
|
157
|
+
|
|
158
|
+
const args = parse(process.argv.slice(2), {
|
|
159
|
+
default: {
|
|
160
|
+
force: false,
|
|
161
|
+
skipTanstackQuery: false,
|
|
162
|
+
verbose: false
|
|
163
|
+
},
|
|
164
|
+
alias: {
|
|
165
|
+
f: 'force',
|
|
166
|
+
h: 'help',
|
|
167
|
+
v: 'verbose',
|
|
168
|
+
q: 'skipTanstackQuery'
|
|
169
|
+
},
|
|
170
|
+
boolean: true
|
|
171
|
+
});
|
|
172
|
+
if (args.verbose) process.env.VERBOSE = '1';
|
|
173
|
+
intro(`tRPC Upgrade CLI v${version}`);
|
|
174
|
+
if (args.help) {
|
|
175
|
+
log.info(`
|
|
176
|
+
Usage: upgrade [options]
|
|
177
|
+
|
|
178
|
+
Options:
|
|
179
|
+
-f, --force Skip git status check, use with caution
|
|
180
|
+
-q, --skipTanstackQuery Skip installing @trpc/tanstack-react-query package
|
|
181
|
+
-v, --verbose Enable verbose logging
|
|
182
|
+
-h, --help Show help
|
|
183
|
+
`.trim());
|
|
184
|
+
process.exit(0);
|
|
185
|
+
}
|
|
186
|
+
if (args.verbose) {
|
|
187
|
+
log.info(`Running upgrade with args: ${JSON.stringify(args, null, 2)}`);
|
|
188
|
+
}
|
|
189
|
+
if (!args.force) {
|
|
190
|
+
await assertCleanGitTree();
|
|
191
|
+
}
|
|
192
|
+
const transforms = await multiselect({
|
|
193
|
+
message: 'Select transforms to run',
|
|
194
|
+
options: [
|
|
195
|
+
{
|
|
196
|
+
value: require.resolve('@trpc/upgrade/transforms/hooksToOptions'),
|
|
197
|
+
label: 'Migrate Hooks to xxxOptions API'
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
value: require.resolve('@trpc/upgrade/transforms/provider'),
|
|
201
|
+
label: 'Migrate context provider setup'
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
});
|
|
205
|
+
if (isCancel(transforms)) process.exit(0);
|
|
206
|
+
// Make sure provider transform runs first if it's selected
|
|
207
|
+
const sortedTransforms = transforms.sort((a)=>a.includes('provider') ? -1 : 1);
|
|
208
|
+
const program = getProgram();
|
|
209
|
+
const sourceFiles = program.getSourceFiles();
|
|
210
|
+
const possibleReferences = findTRPCImportReferences(program);
|
|
211
|
+
const trpcFile = possibleReferences.mostUsed.file;
|
|
212
|
+
const trpcImportName = possibleReferences.importName;
|
|
213
|
+
const commitedFiles = await filterIgnored(sourceFiles);
|
|
214
|
+
for (const transform of sortedTransforms){
|
|
215
|
+
log.step(`Running transform: ${basename(transform, extname(transform))}`);
|
|
216
|
+
const { run } = await import('jscodeshift/src/Runner.js');
|
|
217
|
+
await run(transform, commitedFiles, {
|
|
218
|
+
...args,
|
|
219
|
+
trpcFile,
|
|
220
|
+
trpcImportName
|
|
221
|
+
});
|
|
222
|
+
log.success(`Transform completed`);
|
|
223
|
+
}
|
|
224
|
+
if (!args.skipTanstackQuery) {
|
|
225
|
+
log.info('Installing @trpc/tanstack-react-query');
|
|
226
|
+
await installPackage('@trpc/tanstack-react-query');
|
|
227
|
+
log.success('@trpc/tanstack-react-query installed');
|
|
228
|
+
log.info('Uninstalling @trpc/react-query');
|
|
229
|
+
await uninstallPackage('@trpc/react-query');
|
|
230
|
+
log.success('@trpc/react-query uninstalled');
|
|
231
|
+
}
|
|
232
|
+
outro('Upgrade complete! 🎉');
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trpc/upgrade",
|
|
3
|
-
"version": "11.0.0-rc.
|
|
3
|
+
"version": "11.0.0-rc.802+1eccc1171",
|
|
4
4
|
"description": "Upgrade scripts for tRPC",
|
|
5
5
|
"author": "juliusmarminge",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": "./dist/bin.js",
|
|
8
9
|
"homepage": "https://trpc.io",
|
|
9
10
|
"repository": {
|
|
10
11
|
"type": "git",
|
|
@@ -32,10 +33,8 @@
|
|
|
32
33
|
"!**/__tests__"
|
|
33
34
|
],
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"@
|
|
36
|
-
"@
|
|
37
|
-
"@effect/platform-node": "0.73.2",
|
|
38
|
-
"effect": "3.13.2",
|
|
36
|
+
"@bomb.sh/args": "0.3.0",
|
|
37
|
+
"@clack/prompts": "0.10.0",
|
|
39
38
|
"jscodeshift": "17.1.1",
|
|
40
39
|
"typescript": "^5.6.2"
|
|
41
40
|
},
|
|
@@ -52,5 +51,5 @@
|
|
|
52
51
|
"funding": [
|
|
53
52
|
"https://trpc.io/sponsor"
|
|
54
53
|
],
|
|
55
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "1eccc1171fba8b1bdb4d8acd83ab37d1e0a98b22"
|
|
56
55
|
}
|
package/src/bin/index.ts
CHANGED
|
@@ -1,247 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { NodeContext, NodeRuntime } from '@effect/platform-node';
|
|
6
|
-
import {
|
|
7
|
-
Array,
|
|
8
|
-
Console,
|
|
9
|
-
Effect,
|
|
10
|
-
Logger,
|
|
11
|
-
LogLevel,
|
|
12
|
-
Match,
|
|
13
|
-
Order,
|
|
14
|
-
pipe,
|
|
15
|
-
Predicate,
|
|
16
|
-
Stream,
|
|
17
|
-
String,
|
|
18
|
-
} from 'effect';
|
|
19
|
-
import type { SourceFile } from 'typescript';
|
|
20
|
-
import {
|
|
21
|
-
createProgram,
|
|
22
|
-
findConfigFile,
|
|
23
|
-
parseJsonConfigFileContent,
|
|
24
|
-
readConfigFile,
|
|
25
|
-
sys,
|
|
26
|
-
} from 'typescript';
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { basename, extname } from 'node:path';
|
|
3
|
+
import { parse } from '@bomb.sh/args';
|
|
4
|
+
import { intro, isCancel, log, multiselect, outro } from '@clack/prompts';
|
|
27
5
|
import { version } from '../../package.json';
|
|
28
|
-
import { findTRPCImportReferences } from '../lib/ast/scanners';
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Match.value(process.env.npm_config_user_agent ?? 'npm').pipe(
|
|
48
|
-
Match.when(String.startsWith('pnpm'), () => 'pnpm'),
|
|
49
|
-
Match.when(String.startsWith('yarn'), () => 'yarn'),
|
|
50
|
-
Match.when(String.startsWith('bun'), () => 'bun'),
|
|
51
|
-
Match.orElse(() => 'npm'),
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
const installPackage = (packageName: string) => {
|
|
55
|
-
const packageManager = getPackageManager();
|
|
56
|
-
return Command.streamLines(
|
|
57
|
-
MakeCommand(packageManager, 'install', packageName),
|
|
58
|
-
).pipe(Stream.mapEffect(Console.log), Stream.runDrain);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const uninstallPackage = (packageName: string) => {
|
|
62
|
-
const packageManager = getPackageManager();
|
|
63
|
-
const uninstallCmd = packageManager === 'yarn' ? 'remove' : 'uninstall';
|
|
64
|
-
return Command.streamLines(
|
|
65
|
-
MakeCommand(packageManager, uninstallCmd, packageName),
|
|
66
|
-
).pipe(Stream.mapEffect(Console.log), Stream.runDrain);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const filterIgnored = (files: readonly SourceFile[]) =>
|
|
70
|
-
Effect.gen(function* () {
|
|
71
|
-
const ignores = yield* Command.string(
|
|
72
|
-
MakeCommand('git', 'check-ignore', '**/*').pipe(Command.runInShell(true)),
|
|
73
|
-
).pipe(
|
|
74
|
-
Effect.tap((_) => Effect.logDebug('Ignored files output:', _)),
|
|
75
|
-
Effect.map((_) => _.split('\n')),
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
yield* Effect.logDebug('cwd:', process.cwd());
|
|
79
|
-
|
|
80
|
-
yield* Effect.logDebug(
|
|
81
|
-
'All files in program:',
|
|
82
|
-
files.map((_) => _.fileName),
|
|
83
|
-
);
|
|
84
|
-
yield* Effect.logDebug('Ignored files:', ignores);
|
|
85
|
-
|
|
86
|
-
// Ignore "common files"
|
|
87
|
-
const filteredSourcePaths = files
|
|
88
|
-
.filter(
|
|
89
|
-
(source) =>
|
|
90
|
-
source.fileName.startsWith(path.resolve()) && // only look ahead of current directory
|
|
91
|
-
!source.fileName.includes('/trpc/packages/') && // relative paths when running codemod locally
|
|
92
|
-
!source.fileName.includes('/node_modules/') && // always ignore node_modules
|
|
93
|
-
!ignores.includes(source.fileName), // ignored files
|
|
94
|
-
)
|
|
95
|
-
.map((source) => source.fileName);
|
|
96
|
-
|
|
97
|
-
yield* Effect.logDebug('Filtered files:', filteredSourcePaths);
|
|
98
|
-
|
|
99
|
-
return filteredSourcePaths;
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
const TSProgram = Effect.succeed(
|
|
103
|
-
findConfigFile(process.cwd(), sys.fileExists),
|
|
104
|
-
).pipe(
|
|
105
|
-
Effect.filterOrFail(Predicate.isNotNullable, () => 'No tsconfig found'),
|
|
106
|
-
Effect.tap((_) => Effect.logDebug('Using tsconfig', _)),
|
|
107
|
-
Effect.map((_) => readConfigFile(_, sys.readFile)),
|
|
108
|
-
Effect.map((_) => parseJsonConfigFileContent(_.config, sys, process.cwd())),
|
|
109
|
-
Effect.map((_) =>
|
|
110
|
-
createProgram({
|
|
111
|
-
options: _.options,
|
|
112
|
-
rootNames: _.fileNames,
|
|
113
|
-
configFileParsingDiagnostics: _.errors,
|
|
114
|
-
}),
|
|
115
|
-
),
|
|
116
|
-
);
|
|
6
|
+
import { findTRPCImportReferences, getProgram } from '../lib/ast/scanners';
|
|
7
|
+
import { assertCleanGitTree, filterIgnored } from '../lib/git';
|
|
8
|
+
import { installPackage, uninstallPackage } from '../lib/pkgmgr';
|
|
9
|
+
|
|
10
|
+
const args = parse(process.argv.slice(2), {
|
|
11
|
+
default: {
|
|
12
|
+
force: false,
|
|
13
|
+
skipTanstackQuery: false,
|
|
14
|
+
verbose: false,
|
|
15
|
+
},
|
|
16
|
+
alias: {
|
|
17
|
+
f: 'force',
|
|
18
|
+
h: 'help',
|
|
19
|
+
v: 'verbose',
|
|
20
|
+
q: 'skipTanstackQuery',
|
|
21
|
+
},
|
|
22
|
+
boolean: true,
|
|
23
|
+
});
|
|
24
|
+
if (args.verbose) process.env.VERBOSE = '1';
|
|
117
25
|
|
|
118
|
-
|
|
119
|
-
const transformPath = (path: string) =>
|
|
120
|
-
process.env.DEV ? path : path.replace('../', './').replace('.ts', '.cjs');
|
|
26
|
+
intro(`tRPC Upgrade CLI v${version}`);
|
|
121
27
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
);
|
|
28
|
+
if (args.help) {
|
|
29
|
+
log.info(
|
|
30
|
+
`
|
|
31
|
+
Usage: upgrade [options]
|
|
127
32
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
);
|
|
33
|
+
Options:
|
|
34
|
+
-f, --force Skip git status check, use with caution
|
|
35
|
+
-q, --skipTanstackQuery Skip installing @trpc/tanstack-react-query package
|
|
36
|
+
-v, --verbose Enable verbose logging
|
|
37
|
+
-h, --help Show help
|
|
38
|
+
`.trim(),
|
|
39
|
+
);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (args.verbose) {
|
|
44
|
+
log.info(`Running upgrade with args: ${JSON.stringify(args, null, 2)}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!args.force) {
|
|
48
|
+
await assertCleanGitTree();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const transforms = await multiselect({
|
|
52
|
+
message: 'Select transforms to run',
|
|
53
|
+
options: [
|
|
54
|
+
{
|
|
55
|
+
value: require.resolve('@trpc/upgrade/transforms/hooksToOptions'),
|
|
56
|
+
label: 'Migrate Hooks to xxxOptions API',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
value: require.resolve('@trpc/upgrade/transforms/provider'),
|
|
60
|
+
label: 'Migrate context provider setup',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
if (isCancel(transforms)) process.exit(0);
|
|
136
65
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
Options.withDescription('Name of the trpc import'),
|
|
66
|
+
// Make sure provider transform runs first if it's selected
|
|
67
|
+
const sortedTransforms = transforms.sort((a) =>
|
|
68
|
+
a.includes('provider') ? -1 : 1,
|
|
141
69
|
);
|
|
142
70
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
71
|
+
const program = getProgram();
|
|
72
|
+
const sourceFiles = program.getSourceFiles();
|
|
73
|
+
const possibleReferences = findTRPCImportReferences(program);
|
|
74
|
+
const trpcFile = possibleReferences.mostUsed.file;
|
|
75
|
+
const trpcImportName = possibleReferences.importName;
|
|
148
76
|
|
|
149
|
-
const
|
|
150
|
-
Options.withAlias('v'),
|
|
151
|
-
Options.withDefault(false),
|
|
152
|
-
Options.withDescription('Enable verbose logging'),
|
|
153
|
-
);
|
|
77
|
+
const commitedFiles = await filterIgnored(sourceFiles);
|
|
154
78
|
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
|
|
79
|
+
for (const transform of sortedTransforms) {
|
|
80
|
+
log.step(`Running transform: ${basename(transform, extname(transform))}`);
|
|
81
|
+
const { run } = await import('jscodeshift/src/Runner.js');
|
|
82
|
+
await run(transform, commitedFiles, {
|
|
83
|
+
...args,
|
|
159
84
|
trpcFile,
|
|
160
85
|
trpcImportName,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
(args) =>
|
|
165
|
-
Effect.gen(function* () {
|
|
166
|
-
if (args.verbose) {
|
|
167
|
-
yield* Effect.log('Running upgrade with args:', args);
|
|
168
|
-
}
|
|
169
|
-
if (!args.force) {
|
|
170
|
-
yield* assertCleanGitTree;
|
|
171
|
-
}
|
|
172
|
-
const transforms = yield* pipe(
|
|
173
|
-
Prompt.multiSelect({
|
|
174
|
-
message: 'Select transforms to run',
|
|
175
|
-
choices: [
|
|
176
|
-
{
|
|
177
|
-
title: 'Migrate Hooks to xxxOptions API',
|
|
178
|
-
value: require.resolve(
|
|
179
|
-
transformPath('../transforms/hooksToOptions.ts'),
|
|
180
|
-
),
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
title: 'Migrate context provider setup',
|
|
184
|
-
value: require.resolve(
|
|
185
|
-
transformPath('../transforms/provider.ts'),
|
|
186
|
-
),
|
|
187
|
-
},
|
|
188
|
-
],
|
|
189
|
-
}),
|
|
190
|
-
Effect.flatMap((selected) => {
|
|
191
|
-
if (selected.length === 0) {
|
|
192
|
-
return Effect.fail(
|
|
193
|
-
new Error('Please select at least one transform to run'),
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
return Effect.succeed(selected);
|
|
197
|
-
}),
|
|
198
|
-
Effect.map(
|
|
199
|
-
// Make sure provider transform runs first if it's selected
|
|
200
|
-
Array.sortWith((a) => !a.includes('provider.ts'), Order.boolean),
|
|
201
|
-
),
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
const program = yield* TSProgram;
|
|
205
|
-
const sourceFiles = program.getSourceFiles();
|
|
206
|
-
|
|
207
|
-
const possibleReferences = findTRPCImportReferences(program);
|
|
208
|
-
const trpcFile = possibleReferences.mostUsed.file;
|
|
209
|
-
const trpcImportName = possibleReferences.importName;
|
|
210
|
-
|
|
211
|
-
const commitedFiles = yield* filterIgnored(sourceFiles);
|
|
212
|
-
yield* Effect.forEach(transforms, (transform) => {
|
|
213
|
-
return pipe(
|
|
214
|
-
Effect.log('Running transform', transform),
|
|
215
|
-
Effect.flatMap(() =>
|
|
216
|
-
Effect.tryPromise(async () =>
|
|
217
|
-
import('jscodeshift/src/Runner.js').then(({ run }) =>
|
|
218
|
-
run(transform, commitedFiles, {
|
|
219
|
-
...args,
|
|
220
|
-
trpcFile: trpcFile,
|
|
221
|
-
trpcImportName: trpcImportName,
|
|
222
|
-
}),
|
|
223
|
-
),
|
|
224
|
-
),
|
|
225
|
-
),
|
|
226
|
-
Effect.map((_) => Effect.log('Transform result', _)),
|
|
227
|
-
);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
if (!args.skipTanstackQuery) {
|
|
231
|
-
yield* Effect.log('Installing @trpc/tanstack-react-query');
|
|
232
|
-
yield* installPackage('@trpc/tanstack-react-query');
|
|
86
|
+
});
|
|
87
|
+
log.success(`Transform completed`);
|
|
88
|
+
}
|
|
233
89
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
Logger.withMinimumLogLevel(args.verbose ? LogLevel.Debug : LogLevel.Info),
|
|
239
|
-
),
|
|
240
|
-
);
|
|
90
|
+
if (!args.skipTanstackQuery) {
|
|
91
|
+
log.info('Installing @trpc/tanstack-react-query');
|
|
92
|
+
await installPackage('@trpc/tanstack-react-query');
|
|
93
|
+
log.success('@trpc/tanstack-react-query installed');
|
|
241
94
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
95
|
+
log.info('Uninstalling @trpc/react-query');
|
|
96
|
+
await uninstallPackage('@trpc/react-query');
|
|
97
|
+
log.success('@trpc/react-query uninstalled');
|
|
98
|
+
}
|
|
246
99
|
|
|
247
|
-
|
|
100
|
+
outro('Upgrade complete! 🎉');
|
package/src/lib/ast/scanners.ts
CHANGED
|
@@ -1,25 +1,45 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
isCallExpression,
|
|
4
|
-
isIdentifier,
|
|
5
|
-
isImportDeclaration,
|
|
6
|
-
isStringLiteral,
|
|
7
|
-
isVariableDeclaration,
|
|
8
|
-
isVariableStatement,
|
|
9
|
-
resolveModuleName,
|
|
10
|
-
sys,
|
|
11
|
-
type Program,
|
|
12
|
-
} from 'typescript';
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import * as ts from 'typescript';
|
|
13
3
|
|
|
14
|
-
export function
|
|
4
|
+
export function getProgram() {
|
|
5
|
+
const configFile = ts.findConfigFile(process.cwd(), (filepath) =>
|
|
6
|
+
ts.sys.fileExists(filepath),
|
|
7
|
+
);
|
|
8
|
+
if (!configFile) {
|
|
9
|
+
p.log.error('No tsconfig found');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (process.env.VERBOSE) {
|
|
14
|
+
p.log.info(`Using tsconfig: ${configFile}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { config } = ts.readConfigFile(configFile, (filepath) =>
|
|
18
|
+
ts.sys.readFile(filepath),
|
|
19
|
+
);
|
|
20
|
+
const parsedConfig = ts.parseJsonConfigFileContent(
|
|
21
|
+
config,
|
|
22
|
+
ts.sys,
|
|
23
|
+
process.cwd(),
|
|
24
|
+
);
|
|
25
|
+
const program = ts.createProgram({
|
|
26
|
+
options: parsedConfig.options,
|
|
27
|
+
rootNames: parsedConfig.fileNames,
|
|
28
|
+
configFileParsingDiagnostics: parsedConfig.errors,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return program;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function findSourceAndImportName(program: ts.Program) {
|
|
15
35
|
const files = program.getSourceFiles().filter((sourceFile) => {
|
|
16
36
|
if (sourceFile.isDeclarationFile) return false;
|
|
17
37
|
let found = false;
|
|
18
|
-
forEachChild(sourceFile, (node) => {
|
|
19
|
-
if (!found && isImportDeclaration(node)) {
|
|
38
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
39
|
+
if (!found && ts.isImportDeclaration(node)) {
|
|
20
40
|
const { moduleSpecifier } = node;
|
|
21
41
|
if (
|
|
22
|
-
isStringLiteral(moduleSpecifier) &&
|
|
42
|
+
ts.isStringLiteral(moduleSpecifier) &&
|
|
23
43
|
moduleSpecifier.text.includes('@trpc/react-query')
|
|
24
44
|
) {
|
|
25
45
|
found = true;
|
|
@@ -31,17 +51,17 @@ export function findSourceAndImportName(program: Program) {
|
|
|
31
51
|
|
|
32
52
|
let importName = 'trpc';
|
|
33
53
|
files.forEach((sourceFile) => {
|
|
34
|
-
forEachChild(sourceFile, (node) => {
|
|
54
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
35
55
|
if (
|
|
36
|
-
isVariableStatement(node) &&
|
|
56
|
+
ts.isVariableStatement(node) &&
|
|
37
57
|
node.modifiers?.some((mod) => mod.getText(sourceFile) === 'export')
|
|
38
58
|
) {
|
|
39
59
|
node.declarationList.declarations.forEach((declaration) => {
|
|
40
60
|
if (
|
|
41
|
-
isVariableDeclaration(declaration) &&
|
|
61
|
+
ts.isVariableDeclaration(declaration) &&
|
|
42
62
|
declaration.initializer &&
|
|
43
|
-
isCallExpression(declaration.initializer) &&
|
|
44
|
-
isIdentifier(declaration.initializer.expression) &&
|
|
63
|
+
ts.isCallExpression(declaration.initializer) &&
|
|
64
|
+
ts.isIdentifier(declaration.initializer.expression) &&
|
|
45
65
|
declaration.initializer.expression.getText(sourceFile) ===
|
|
46
66
|
'createTRPCReact'
|
|
47
67
|
) {
|
|
@@ -58,20 +78,23 @@ export function findSourceAndImportName(program: Program) {
|
|
|
58
78
|
};
|
|
59
79
|
}
|
|
60
80
|
|
|
61
|
-
export function findTRPCImportReferences(program: Program) {
|
|
81
|
+
export function findTRPCImportReferences(program: ts.Program) {
|
|
62
82
|
const { files: filesImportingTRPC, importName } =
|
|
63
83
|
findSourceAndImportName(program);
|
|
64
84
|
const trpcReferenceSpecifiers = new Map<string, string>();
|
|
65
85
|
|
|
66
86
|
program.getSourceFiles().forEach((sourceFile) => {
|
|
67
87
|
if (sourceFile.isDeclarationFile) return;
|
|
68
|
-
forEachChild(sourceFile, (node) => {
|
|
69
|
-
if (
|
|
70
|
-
|
|
88
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
89
|
+
if (
|
|
90
|
+
ts.isImportDeclaration(node) &&
|
|
91
|
+
ts.isStringLiteral(node.moduleSpecifier)
|
|
92
|
+
) {
|
|
93
|
+
const resolved = ts.resolveModuleName(
|
|
71
94
|
node.moduleSpecifier.text,
|
|
72
95
|
sourceFile.fileName,
|
|
73
96
|
program.getCompilerOptions(),
|
|
74
|
-
sys,
|
|
97
|
+
ts.sys,
|
|
75
98
|
);
|
|
76
99
|
if (
|
|
77
100
|
resolved.resolvedModule &&
|
package/src/lib/execa.ts
ADDED
package/src/lib/git.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { cancel, log } from '@clack/prompts';
|
|
3
|
+
import type { SourceFile } from 'typescript';
|
|
4
|
+
import { execa } from './execa';
|
|
5
|
+
|
|
6
|
+
export async function assertCleanGitTree() {
|
|
7
|
+
const { stdout } = await execa('git status');
|
|
8
|
+
if (!stdout.includes('nothing to commit')) {
|
|
9
|
+
cancel(
|
|
10
|
+
'Git tree is not clean, please commit your changes and try again, or run with `--force`',
|
|
11
|
+
);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function filterIgnored(files: readonly SourceFile[]) {
|
|
17
|
+
const { stdout } = await execa('git check-ignore **/*');
|
|
18
|
+
const ignores = stdout.split('\n');
|
|
19
|
+
|
|
20
|
+
if (process.env.VERBOSE) {
|
|
21
|
+
log.info(`cwd: ${process.cwd()}`);
|
|
22
|
+
log.info(
|
|
23
|
+
`All files in program: ${files.map((file) => file.fileName).join(', ')}`,
|
|
24
|
+
);
|
|
25
|
+
log.info(`Ignored files: ${ignores.join(', ')}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Ignore "common files"
|
|
29
|
+
const filteredSourcePaths = files
|
|
30
|
+
.filter(
|
|
31
|
+
(source) =>
|
|
32
|
+
source.fileName.startsWith(resolve()) && // only look ahead of current directory
|
|
33
|
+
!source.fileName.includes('/trpc/packages/') && // relative paths when running codemod locally
|
|
34
|
+
!source.fileName.includes('/node_modules/') && // always ignore node_modules
|
|
35
|
+
!ignores.includes(source.fileName), // ignored files
|
|
36
|
+
)
|
|
37
|
+
.map((source) => source.fileName);
|
|
38
|
+
|
|
39
|
+
if (process.env.VERBOSE) {
|
|
40
|
+
log.info(`Filtered files: ${filteredSourcePaths.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return filteredSourcePaths;
|
|
44
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { log } from '@clack/prompts';
|
|
2
|
+
import { execa } from './execa';
|
|
3
|
+
|
|
4
|
+
function getPackageManager() {
|
|
5
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
6
|
+
if (userAgent?.startsWith('pnpm')) return 'pnpm';
|
|
7
|
+
if (userAgent?.startsWith('yarn')) return 'yarn';
|
|
8
|
+
if (userAgent?.startsWith('bun')) return 'bun';
|
|
9
|
+
return 'npm';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function installPackage(packageName: string) {
|
|
13
|
+
const packageManager = getPackageManager();
|
|
14
|
+
const installCmd = packageManager === 'yarn' ? 'add' : 'install';
|
|
15
|
+
const { stdout, stderr } = await execa(
|
|
16
|
+
`${packageManager} ${installCmd} ${packageName}`,
|
|
17
|
+
);
|
|
18
|
+
if (stderr) {
|
|
19
|
+
log.error(stderr);
|
|
20
|
+
}
|
|
21
|
+
if (process.env.VERBOSE) {
|
|
22
|
+
log.info(stdout);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function uninstallPackage(packageName: string) {
|
|
27
|
+
const packageManager = getPackageManager();
|
|
28
|
+
const uninstallCmd = packageManager === 'yarn' ? 'remove' : 'uninstall';
|
|
29
|
+
const { stdout, stderr } = await execa(
|
|
30
|
+
`${packageManager} ${uninstallCmd} ${packageName}`,
|
|
31
|
+
);
|
|
32
|
+
if (stderr) {
|
|
33
|
+
log.error(stderr);
|
|
34
|
+
}
|
|
35
|
+
if (process.env.VERBOSE) {
|
|
36
|
+
log.info(stdout);
|
|
37
|
+
}
|
|
38
|
+
}
|
package/dist/bin.cjs
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
var path = require('node:path');
|
|
3
|
-
var cli$1 = require('@effect/cli');
|
|
4
|
-
var platform = require('@effect/platform');
|
|
5
|
-
var platformNode = require('@effect/platform-node');
|
|
6
|
-
var effect = require('effect');
|
|
7
|
-
var typescript = require('typescript');
|
|
8
|
-
|
|
9
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
-
|
|
11
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
12
|
-
|
|
13
|
-
var version = "11.0.0-rc.800+892471d1b";
|
|
14
|
-
|
|
15
|
-
function findSourceAndImportName(program) {
|
|
16
|
-
const files = program.getSourceFiles().filter((sourceFile)=>{
|
|
17
|
-
if (sourceFile.isDeclarationFile) return false;
|
|
18
|
-
let found = false;
|
|
19
|
-
typescript.forEachChild(sourceFile, (node)=>{
|
|
20
|
-
if (!found && typescript.isImportDeclaration(node)) {
|
|
21
|
-
const { moduleSpecifier } = node;
|
|
22
|
-
if (typescript.isStringLiteral(moduleSpecifier) && moduleSpecifier.text.includes('@trpc/react-query')) {
|
|
23
|
-
found = true;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
return found;
|
|
28
|
-
});
|
|
29
|
-
let importName = 'trpc';
|
|
30
|
-
files.forEach((sourceFile)=>{
|
|
31
|
-
typescript.forEachChild(sourceFile, (node)=>{
|
|
32
|
-
if (typescript.isVariableStatement(node) && node.modifiers?.some((mod)=>mod.getText(sourceFile) === 'export')) {
|
|
33
|
-
node.declarationList.declarations.forEach((declaration)=>{
|
|
34
|
-
if (typescript.isVariableDeclaration(declaration) && declaration.initializer && typescript.isCallExpression(declaration.initializer) && typescript.isIdentifier(declaration.initializer.expression) && declaration.initializer.expression.getText(sourceFile) === 'createTRPCReact') {
|
|
35
|
-
importName = declaration.name.getText(sourceFile);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
return {
|
|
42
|
-
files: files.map((d)=>d.fileName),
|
|
43
|
-
importName
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
function findTRPCImportReferences(program) {
|
|
47
|
-
const { files: filesImportingTRPC, importName } = findSourceAndImportName(program);
|
|
48
|
-
const trpcReferenceSpecifiers = new Map();
|
|
49
|
-
program.getSourceFiles().forEach((sourceFile)=>{
|
|
50
|
-
if (sourceFile.isDeclarationFile) return;
|
|
51
|
-
typescript.forEachChild(sourceFile, (node)=>{
|
|
52
|
-
if (typescript.isImportDeclaration(node) && typescript.isStringLiteral(node.moduleSpecifier)) {
|
|
53
|
-
const resolved = typescript.resolveModuleName(node.moduleSpecifier.text, sourceFile.fileName, program.getCompilerOptions(), typescript.sys);
|
|
54
|
-
if (resolved.resolvedModule && filesImportingTRPC.includes(resolved.resolvedModule.resolvedFileName)) {
|
|
55
|
-
trpcReferenceSpecifiers.set(resolved.resolvedModule.resolvedFileName, node.moduleSpecifier.text);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
const counts = {};
|
|
61
|
-
let currentMax = 0;
|
|
62
|
-
const mostUsed = {
|
|
63
|
-
file: ''
|
|
64
|
-
};
|
|
65
|
-
[
|
|
66
|
-
...trpcReferenceSpecifiers.values()
|
|
67
|
-
].forEach((specifier)=>{
|
|
68
|
-
counts[specifier] = (counts[specifier] || 0) + 1;
|
|
69
|
-
if (counts[specifier] > currentMax) {
|
|
70
|
-
currentMax = counts[specifier];
|
|
71
|
-
mostUsed.file = specifier;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
return {
|
|
75
|
-
importName,
|
|
76
|
-
mostUsed,
|
|
77
|
-
all: Object.fromEntries(trpcReferenceSpecifiers.entries())
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const MakeCommand = (command, ...args)=>{
|
|
82
|
-
return platform.Command.make(command, ...args).pipe(platform.Command.workingDirectory(process.cwd()), platform.Command.runInShell(true));
|
|
83
|
-
};
|
|
84
|
-
const assertCleanGitTree = platform.Command.string(MakeCommand('git', 'status')).pipe().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`'));
|
|
85
|
-
const getPackageManager = ()=>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'));
|
|
86
|
-
const installPackage = (packageName)=>{
|
|
87
|
-
const packageManager = getPackageManager();
|
|
88
|
-
return platform.Command.streamLines(MakeCommand(packageManager, 'install', packageName)).pipe(effect.Stream.mapEffect(effect.Console.log), effect.Stream.runDrain);
|
|
89
|
-
};
|
|
90
|
-
const uninstallPackage = (packageName)=>{
|
|
91
|
-
const packageManager = getPackageManager();
|
|
92
|
-
const uninstallCmd = packageManager === 'yarn' ? 'remove' : 'uninstall';
|
|
93
|
-
return platform.Command.streamLines(MakeCommand(packageManager, uninstallCmd, packageName)).pipe(effect.Stream.mapEffect(effect.Console.log), effect.Stream.runDrain);
|
|
94
|
-
};
|
|
95
|
-
const filterIgnored = (files)=>effect.Effect.gen(function*() {
|
|
96
|
-
const ignores = yield* platform.Command.string(MakeCommand('git', 'check-ignore', '**/*').pipe(platform.Command.runInShell(true))).pipe(effect.Effect.tap((_)=>effect.Effect.logDebug('Ignored files output:', _)), effect.Effect.map((_)=>_.split('\n')));
|
|
97
|
-
yield* effect.Effect.logDebug('cwd:', process.cwd());
|
|
98
|
-
yield* effect.Effect.logDebug('All files in program:', files.map((_)=>_.fileName));
|
|
99
|
-
yield* effect.Effect.logDebug('Ignored files:', ignores);
|
|
100
|
-
// Ignore "common files"
|
|
101
|
-
const filteredSourcePaths = files.filter((source)=>source.fileName.startsWith(path__default.default.resolve()) && // only look ahead of current directory
|
|
102
|
-
!source.fileName.includes('/trpc/packages/') && // relative paths when running codemod locally
|
|
103
|
-
!source.fileName.includes('/node_modules/') && // always ignore node_modules
|
|
104
|
-
!ignores.includes(source.fileName)).map((source)=>source.fileName);
|
|
105
|
-
yield* effect.Effect.logDebug('Filtered files:', filteredSourcePaths);
|
|
106
|
-
return filteredSourcePaths;
|
|
107
|
-
});
|
|
108
|
-
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({
|
|
109
|
-
options: _.options,
|
|
110
|
-
rootNames: _.fileNames,
|
|
111
|
-
configFileParsingDiagnostics: _.errors
|
|
112
|
-
})));
|
|
113
|
-
// FIXME :: hacky
|
|
114
|
-
const transformPath = (path)=>process.env.DEV ? path : path.replace('../', './').replace('.ts', '.cjs');
|
|
115
|
-
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'));
|
|
116
|
-
/**
|
|
117
|
-
* TODO: Instead of default values these should be detected automatically from the TS program
|
|
118
|
-
*/ 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'));
|
|
119
|
-
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'));
|
|
120
|
-
const skipTanstackQuery = cli$1.Options.boolean('skipTanstackQuery').pipe(cli$1.Options.withAlias('q'), cli$1.Options.withDefault(false), cli$1.Options.withDescription('Skip installing @trpc/tanstack-react-query package'));
|
|
121
|
-
const verbose = cli$1.Options.boolean('verbose').pipe(cli$1.Options.withAlias('v'), cli$1.Options.withDefault(false), cli$1.Options.withDescription('Enable verbose logging'));
|
|
122
|
-
const rootComamnd = cli$1.Command.make('upgrade', {
|
|
123
|
-
force,
|
|
124
|
-
trpcFile,
|
|
125
|
-
trpcImportName,
|
|
126
|
-
skipTanstackQuery,
|
|
127
|
-
verbose
|
|
128
|
-
}, (args)=>effect.Effect.gen(function*() {
|
|
129
|
-
if (args.verbose) {
|
|
130
|
-
yield* effect.Effect.log('Running upgrade with args:', args);
|
|
131
|
-
}
|
|
132
|
-
if (!args.force) {
|
|
133
|
-
yield* assertCleanGitTree;
|
|
134
|
-
}
|
|
135
|
-
const transforms = yield* effect.pipe(cli$1.Prompt.multiSelect({
|
|
136
|
-
message: 'Select transforms to run',
|
|
137
|
-
choices: [
|
|
138
|
-
{
|
|
139
|
-
title: 'Migrate Hooks to xxxOptions API',
|
|
140
|
-
value: require.resolve(transformPath('../transforms/hooksToOptions.ts'))
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
title: 'Migrate context provider setup',
|
|
144
|
-
value: require.resolve(transformPath('../transforms/provider.ts'))
|
|
145
|
-
}
|
|
146
|
-
]
|
|
147
|
-
}), effect.Effect.flatMap((selected)=>{
|
|
148
|
-
if (selected.length === 0) {
|
|
149
|
-
return effect.Effect.fail(new Error('Please select at least one transform to run'));
|
|
150
|
-
}
|
|
151
|
-
return effect.Effect.succeed(selected);
|
|
152
|
-
}), effect.Effect.map(// Make sure provider transform runs first if it's selected
|
|
153
|
-
effect.Array.sortWith((a)=>!a.includes('provider.ts'), effect.Order.boolean)));
|
|
154
|
-
const program = yield* TSProgram;
|
|
155
|
-
const sourceFiles = program.getSourceFiles();
|
|
156
|
-
const possibleReferences = findTRPCImportReferences(program);
|
|
157
|
-
const trpcFile = possibleReferences.mostUsed.file;
|
|
158
|
-
const trpcImportName = possibleReferences.importName;
|
|
159
|
-
const commitedFiles = yield* filterIgnored(sourceFiles);
|
|
160
|
-
yield* effect.Effect.forEach(transforms, (transform)=>{
|
|
161
|
-
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, {
|
|
162
|
-
...args,
|
|
163
|
-
trpcFile: trpcFile,
|
|
164
|
-
trpcImportName: trpcImportName
|
|
165
|
-
})))), effect.Effect.map((_)=>effect.Effect.log('Transform result', _)));
|
|
166
|
-
});
|
|
167
|
-
if (!args.skipTanstackQuery) {
|
|
168
|
-
yield* effect.Effect.log('Installing @trpc/tanstack-react-query');
|
|
169
|
-
yield* installPackage('@trpc/tanstack-react-query');
|
|
170
|
-
yield* effect.Effect.log('Uninstalling @trpc/react-query');
|
|
171
|
-
yield* uninstallPackage('@trpc/react-query');
|
|
172
|
-
}
|
|
173
|
-
}).pipe(effect.Logger.withMinimumLogLevel(args.verbose ? effect.LogLevel.Debug : effect.LogLevel.Info)));
|
|
174
|
-
const cli = cli$1.Command.run(rootComamnd, {
|
|
175
|
-
name: 'tRPC Upgrade CLI',
|
|
176
|
-
version: `v${version}`
|
|
177
|
-
});
|
|
178
|
-
cli(process.argv).pipe(effect.Effect.provide(platformNode.NodeContext.layer), platformNode.NodeRuntime.runMain);
|