@akinon/projectzero 2.0.20-rc.0 → 2.0.20
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/CHANGELOG.md +2 -4
- package/app-template/.env.example +0 -1
- package/app-template/AGENTS.md +0 -8
- package/app-template/CHANGELOG.md +31 -73
- package/app-template/README.md +1 -25
- package/app-template/next.config.mjs +1 -7
- package/app-template/package.json +41 -41
- package/app-template/src/hooks/index.ts +0 -2
- package/app-template/src/plugins.js +0 -1
- package/app-template/src/proxy.ts +1 -2
- package/app-template/src/views/header/search/index.tsx +5 -13
- package/app-template/src/views/product/slider.tsx +38 -85
- package/codemods/fix-login-form-action/index.js +77 -0
- package/codemods/fix-login-form-action/transform.js +43 -0
- package/codemods/fix-template-currenturl/index.js +85 -0
- package/codemods/fix-template-currenturl/transform.js +91 -0
- package/codemods/upgrade-to-2/index.js +166 -50
- package/commands/plugins.ts +0 -4
- package/dist/commands/plugins.js +0 -4
- package/package.json +1 -1
- package/app-template/src/app/global-error.tsx +0 -22
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jscodeshift transform for fix-login-form-action.
|
|
3
|
+
*
|
|
4
|
+
* Replaces every JSXAttribute named `action` whose value is the string
|
|
5
|
+
* "/api/auth/signin/credentials" with a no-op arrow function expression
|
|
6
|
+
* (`action={() => {}}`), and attaches an explanatory leading comment block.
|
|
7
|
+
*
|
|
8
|
+
* Returns null (no source change) when no matching attribute is found.
|
|
9
|
+
*/
|
|
10
|
+
module.exports = function transform(fileInfo, api) {
|
|
11
|
+
const j = api.jscodeshift;
|
|
12
|
+
const root = j(fileInfo.source);
|
|
13
|
+
let changed = false;
|
|
14
|
+
|
|
15
|
+
root.find(j.JSXAttribute, { name: { name: 'action' } }).forEach((p) => {
|
|
16
|
+
const v = p.node.value;
|
|
17
|
+
if (!v) return;
|
|
18
|
+
|
|
19
|
+
// String values can show up as either Literal or StringLiteral depending
|
|
20
|
+
// on the parser version. Match both.
|
|
21
|
+
const isString = v.type === 'Literal' || v.type === 'StringLiteral';
|
|
22
|
+
if (!isString || v.value !== '/api/auth/signin/credentials') return;
|
|
23
|
+
|
|
24
|
+
const arrow = j.arrowFunctionExpression([], j.blockStatement([]));
|
|
25
|
+
p.node.value = j.jsxExpressionContainer(arrow);
|
|
26
|
+
|
|
27
|
+
// Leading block comment explains why the action is a no-op. Attaching
|
|
28
|
+
// to the attribute keeps the comment adjacent in the printed output.
|
|
29
|
+
const comment = j.commentBlock(
|
|
30
|
+
' React 19 belt-and-suspenders: passing a function to `action` makes\n' +
|
|
31
|
+
' React itself call preventDefault on the native submit before any\n' +
|
|
32
|
+
' listener runs, so a hydration race cannot leak credentials into\n' +
|
|
33
|
+
' the URL bar. onSubmit still drives the actual signIn flow. ',
|
|
34
|
+
true,
|
|
35
|
+
false
|
|
36
|
+
);
|
|
37
|
+
p.node.comments = [comment];
|
|
38
|
+
|
|
39
|
+
changed = true;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return changed ? root.toSource({ quote: 'single' }) : null;
|
|
43
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fix-template-currenturl
|
|
3
|
+
*
|
|
4
|
+
* Rewrites the JSON-LD canonical url construction in
|
|
5
|
+
* src/app/** /template.tsx from
|
|
6
|
+
*
|
|
7
|
+
* const currentUrl = URL + pathname + searchParams;
|
|
8
|
+
*
|
|
9
|
+
* to
|
|
10
|
+
*
|
|
11
|
+
* const currentUrl = `${URL}${pathname}`;
|
|
12
|
+
*
|
|
13
|
+
* Why: in the Next 16 / React 19 model `searchParams` is a
|
|
14
|
+
* ReadonlyURLSearchParams that string-coerces differently on the server
|
|
15
|
+
* and the client. The mismatched output lands inside a JSON-LD
|
|
16
|
+
* <script dangerouslySetInnerHTML> tag, breaks hydration of the entire
|
|
17
|
+
* route subtree, and races sibling client component hydration — auth
|
|
18
|
+
* forms in particular have shipped credentials into the URL as native
|
|
19
|
+
* GETs because their onSubmit listener was not yet attached when the
|
|
20
|
+
* user clicked submit.
|
|
21
|
+
*
|
|
22
|
+
* Handles both base forms observed across brands:
|
|
23
|
+
* const currentUrl = URL + pathname + searchParams;
|
|
24
|
+
* const currentUrl = process.env.NEXT_PUBLIC_URL + pathname + searchParams;
|
|
25
|
+
*
|
|
26
|
+
* Files outside src/app/** /template.tsx are skipped. Files that already
|
|
27
|
+
* use the template-literal form are no-ops.
|
|
28
|
+
*
|
|
29
|
+
* Usage:
|
|
30
|
+
* node codemods/fix-template-currenturl/index.js [--dry-run]
|
|
31
|
+
* npx @akinon/projectzero codemod --codemod=fix-template-currenturl [--dry-run]
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const path = require('path');
|
|
35
|
+
const fs = require('fs');
|
|
36
|
+
const jscodeshift = require('jscodeshift/src/Runner');
|
|
37
|
+
|
|
38
|
+
function log(msg) {
|
|
39
|
+
const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
40
|
+
console.log(`[${ts}] ${msg}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const transform = () => {
|
|
44
|
+
const cwd = path.resolve(process.cwd());
|
|
45
|
+
const dryRun = process.argv.includes('--dry-run');
|
|
46
|
+
const transformPath = path.join(__dirname, 'transform.js');
|
|
47
|
+
const srcPath = path.join(cwd, 'src', 'app');
|
|
48
|
+
|
|
49
|
+
if (!fs.existsSync(srcPath)) {
|
|
50
|
+
log(`fix-template-currenturl: no src/app at ${cwd}, skipping`);
|
|
51
|
+
return Promise.resolve();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
log(
|
|
55
|
+
`fix-template-currenturl: scanning ${srcPath}${dryRun ? ' (dry-run)' : ''}`
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return jscodeshift
|
|
59
|
+
.run(transformPath, [srcPath], {
|
|
60
|
+
verbose: 0,
|
|
61
|
+
dry: dryRun,
|
|
62
|
+
print: dryRun,
|
|
63
|
+
extensions: 'tsx',
|
|
64
|
+
parser: 'tsx',
|
|
65
|
+
ignorePattern: '**/node_modules/**',
|
|
66
|
+
silent: true
|
|
67
|
+
})
|
|
68
|
+
.then((res) => {
|
|
69
|
+
log(
|
|
70
|
+
`fix-template-currenturl: ${res.ok} ok, ${res.nochange} unchanged, ${res.skip} skipped, ${res.error} errors`
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
module.exports = { transform };
|
|
76
|
+
|
|
77
|
+
if (require.main === module) {
|
|
78
|
+
const result = transform();
|
|
79
|
+
if (result && typeof result.then === 'function') {
|
|
80
|
+
result.catch((err) => {
|
|
81
|
+
console.error(err);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jscodeshift transform for fix-template-currenturl.
|
|
3
|
+
*
|
|
4
|
+
* Matches:
|
|
5
|
+
* const currentUrl = (URL | process.env.NEXT_PUBLIC_URL) + pathname + searchParams;
|
|
6
|
+
*
|
|
7
|
+
* Replaces the right-hand side with the template literal
|
|
8
|
+
* `${X}${pathname}`
|
|
9
|
+
*
|
|
10
|
+
* Only runs against files whose path ends in `/app/**\/template.tsx`. Returns
|
|
11
|
+
* null (no source change) when no match is found in the file or the
|
|
12
|
+
* declarator already has a non-binary initializer.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
function isUrlBase(node) {
|
|
16
|
+
if (!node) return false;
|
|
17
|
+
if (node.type === 'Identifier' && node.name === 'URL') return true;
|
|
18
|
+
if (
|
|
19
|
+
node.type === 'MemberExpression' &&
|
|
20
|
+
node.object &&
|
|
21
|
+
node.object.type === 'MemberExpression' &&
|
|
22
|
+
node.object.object &&
|
|
23
|
+
node.object.object.type === 'Identifier' &&
|
|
24
|
+
node.object.object.name === 'process' &&
|
|
25
|
+
node.object.property &&
|
|
26
|
+
node.object.property.type === 'Identifier' &&
|
|
27
|
+
node.object.property.name === 'env' &&
|
|
28
|
+
node.property &&
|
|
29
|
+
node.property.type === 'Identifier' &&
|
|
30
|
+
node.property.name === 'NEXT_PUBLIC_URL'
|
|
31
|
+
) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function flattenAdd(node, out) {
|
|
38
|
+
if (
|
|
39
|
+
node &&
|
|
40
|
+
node.type === 'BinaryExpression' &&
|
|
41
|
+
node.operator === '+'
|
|
42
|
+
) {
|
|
43
|
+
flattenAdd(node.left, out);
|
|
44
|
+
flattenAdd(node.right, out);
|
|
45
|
+
} else {
|
|
46
|
+
out.push(node);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = function transform(fileInfo, api) {
|
|
51
|
+
// Restrict to template.tsx under any nested app/ segment. Brand routing
|
|
52
|
+
// depth varies (e.g. [commerce]/[locale]/[currency] vs deeper member-type
|
|
53
|
+
// splits) but the file name is constant.
|
|
54
|
+
if (!/[\\/]app[\\/].*template\.tsx$/.test(fileInfo.path)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const j = api.jscodeshift;
|
|
59
|
+
const root = j(fileInfo.source);
|
|
60
|
+
let changed = false;
|
|
61
|
+
|
|
62
|
+
root
|
|
63
|
+
.find(j.VariableDeclarator, { id: { name: 'currentUrl' } })
|
|
64
|
+
.forEach((p) => {
|
|
65
|
+
const init = p.node.init;
|
|
66
|
+
if (
|
|
67
|
+
!init ||
|
|
68
|
+
init.type !== 'BinaryExpression' ||
|
|
69
|
+
init.operator !== '+'
|
|
70
|
+
) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const parts = [];
|
|
75
|
+
flattenAdd(init, parts);
|
|
76
|
+
if (parts.length !== 3) return;
|
|
77
|
+
|
|
78
|
+
const [base, mid, tail] = parts;
|
|
79
|
+
if (mid.type !== 'Identifier' || mid.name !== 'pathname') return;
|
|
80
|
+
if (tail.type !== 'Identifier' || tail.name !== 'searchParams') return;
|
|
81
|
+
if (!isUrlBase(base)) return;
|
|
82
|
+
|
|
83
|
+
// Build: `${base}${pathname}`
|
|
84
|
+
const empty = () => j.templateElement({ raw: '', cooked: '' }, false);
|
|
85
|
+
const tail2 = j.templateElement({ raw: '', cooked: '' }, true);
|
|
86
|
+
p.node.init = j.templateLiteral([empty(), empty(), tail2], [base, mid]);
|
|
87
|
+
changed = true;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return changed ? root.toSource({ quote: 'single' }) : null;
|
|
91
|
+
};
|
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
* 7. Run sentry-9 codemod (only when @sentry/nextjs is present)
|
|
13
13
|
* 8. Run migrate-eslint codemod (ESLint v9 flat config)
|
|
14
14
|
* 9. Run migrate-page-types codemod (PageProps -> Async/Resolved)
|
|
15
|
-
* 10.
|
|
16
|
-
* 11.
|
|
15
|
+
* 10. Run fix-template-currenturl codemod (template.tsx JSON-LD url)
|
|
16
|
+
* 11. Run fix-login-form-action codemod (login form React 19 hardening)
|
|
17
|
+
* 12. yarn build (validation)
|
|
18
|
+
* 13. Summary report
|
|
17
19
|
*
|
|
18
20
|
* Out of scope in this first version:
|
|
19
21
|
* - Tailwind v3 -> v4 migration (run manually with @tailwindcss/upgrade)
|
|
@@ -34,6 +36,8 @@
|
|
|
34
36
|
* --skip-sentry Do not run sentry-9 (auto-skipped if not needed)
|
|
35
37
|
* --skip-eslint Do not run migrate-eslint
|
|
36
38
|
* --skip-page-types Do not run migrate-page-types
|
|
39
|
+
* --skip-template-url Do not run fix-template-currenturl
|
|
40
|
+
* --skip-login-action Do not run fix-login-form-action
|
|
37
41
|
* --skip-install Do not run yarn install
|
|
38
42
|
* --skip-build Do not run yarn build
|
|
39
43
|
* --interactive Pause for confirmation after each step
|
|
@@ -49,6 +53,8 @@ const AUTH_CODEMOD = path.resolve(__dirname, '..', 'migrate-auth-v5');
|
|
|
49
53
|
const SENTRY_CODEMOD = path.resolve(__dirname, '..', 'sentry-9');
|
|
50
54
|
const ESLINT_CODEMOD = path.resolve(__dirname, '..', 'migrate-eslint');
|
|
51
55
|
const PAGE_TYPES_CODEMOD = path.resolve(__dirname, '..', 'migrate-page-types');
|
|
56
|
+
const TEMPLATE_URL_CODEMOD = path.resolve(__dirname, '..', 'fix-template-currenturl');
|
|
57
|
+
const LOGIN_ACTION_CODEMOD = path.resolve(__dirname, '..', 'fix-login-form-action');
|
|
52
58
|
|
|
53
59
|
const TARGET_MATRIX = {
|
|
54
60
|
dependencies: {
|
|
@@ -56,7 +62,11 @@ const TARGET_MATRIX = {
|
|
|
56
62
|
react: '19.2.5',
|
|
57
63
|
'react-dom': '19.2.5',
|
|
58
64
|
'next-auth': '5.0.0-beta.25',
|
|
59
|
-
'@akinon/next': 'latest'
|
|
65
|
+
'@akinon/next': 'latest',
|
|
66
|
+
// 2.17.1 declared React peer as ^16-^18 and reads the old
|
|
67
|
+
// ReactSharedInternals shape, throwing on the checkout shipping address
|
|
68
|
+
// step under React 19. 2.20.8 is same-major and declares React 19.
|
|
69
|
+
'@react-google-maps/api': '2.20.8'
|
|
60
70
|
},
|
|
61
71
|
devDependencies: {
|
|
62
72
|
'@types/react': '19.2.14',
|
|
@@ -138,6 +148,8 @@ function parseArgs(argv) {
|
|
|
138
148
|
skipSentry: argv.includes('--skip-sentry'),
|
|
139
149
|
skipEslint: argv.includes('--skip-eslint'),
|
|
140
150
|
skipPageTypes: argv.includes('--skip-page-types'),
|
|
151
|
+
skipTemplateUrl: argv.includes('--skip-template-url'),
|
|
152
|
+
skipLoginAction: argv.includes('--skip-login-action'),
|
|
141
153
|
skipInstall: argv.includes('--skip-install'),
|
|
142
154
|
skipBuild: argv.includes('--skip-build'),
|
|
143
155
|
interactive: argv.includes('--interactive'),
|
|
@@ -158,7 +170,7 @@ function writePackageJson(cwd, pkg) {
|
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
function stepPreflight(cwd, flags) {
|
|
161
|
-
logStep('[1/
|
|
173
|
+
logStep('[1/13] Preflight', 'starting');
|
|
162
174
|
|
|
163
175
|
const pkg = readPackageJson(cwd);
|
|
164
176
|
if (!pkg) {
|
|
@@ -192,19 +204,19 @@ function stepPreflight(cwd, flags) {
|
|
|
192
204
|
log(` Current: next@${currentNext} react@${currentReact} next-auth@${currentAuth} @akinon/next@${currentAkinon}`);
|
|
193
205
|
log(` Target: next@${TARGET_MATRIX.dependencies.next} react@${TARGET_MATRIX.dependencies.react} next-auth@${TARGET_MATRIX.dependencies['next-auth']} @akinon/next@${TARGET_MATRIX.dependencies['@akinon/next']}`);
|
|
194
206
|
|
|
195
|
-
logStep('[1/
|
|
207
|
+
logStep('[1/13] Preflight', 'ok');
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
function stepBackup(cwd, flags) {
|
|
199
211
|
if (flags.skipBackup) {
|
|
200
|
-
logStep('[2/
|
|
212
|
+
logStep('[2/13] Backup branch', 'skipped');
|
|
201
213
|
return null;
|
|
202
214
|
}
|
|
203
215
|
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
204
216
|
const branchName = `pre-upgrade-2-${today}`;
|
|
205
217
|
|
|
206
218
|
if (flags.dryRun) {
|
|
207
|
-
logStep('[2/
|
|
219
|
+
logStep('[2/13] Backup branch', `[DRY] git branch ${branchName}`);
|
|
208
220
|
return branchName;
|
|
209
221
|
}
|
|
210
222
|
|
|
@@ -214,7 +226,7 @@ function stepBackup(cwd, flags) {
|
|
|
214
226
|
{ cwd, encoding: 'utf-8' }
|
|
215
227
|
);
|
|
216
228
|
if (existing.status === 0) {
|
|
217
|
-
logStep('[2/
|
|
229
|
+
logStep('[2/13] Backup branch', `already exists: ${branchName}`);
|
|
218
230
|
return branchName;
|
|
219
231
|
}
|
|
220
232
|
|
|
@@ -225,7 +237,7 @@ function stepBackup(cwd, flags) {
|
|
|
225
237
|
if (result.status !== 0) {
|
|
226
238
|
throw new Error(`Failed to create backup branch ${branchName}`);
|
|
227
239
|
}
|
|
228
|
-
logStep('[2/
|
|
240
|
+
logStep('[2/13] Backup branch', `created ${branchName}`);
|
|
229
241
|
return branchName;
|
|
230
242
|
}
|
|
231
243
|
|
|
@@ -323,7 +335,7 @@ function pinNextScriptsToWebpack(pkg) {
|
|
|
323
335
|
|
|
324
336
|
function stepBump(cwd, flags) {
|
|
325
337
|
if (flags.skipBump) {
|
|
326
|
-
logStep('[3/
|
|
338
|
+
logStep('[3/13] Bump deps', 'skipped');
|
|
327
339
|
return { hasSentry: false, changes: [] };
|
|
328
340
|
}
|
|
329
341
|
|
|
@@ -371,16 +383,16 @@ function stepBump(cwd, flags) {
|
|
|
371
383
|
allChanges.push(...scriptChanges);
|
|
372
384
|
|
|
373
385
|
if (allChanges.length === 0) {
|
|
374
|
-
logStep('[3/
|
|
386
|
+
logStep('[3/13] Bump deps', 'already at target, no changes');
|
|
375
387
|
return { hasSentry, changes: [] };
|
|
376
388
|
}
|
|
377
389
|
|
|
378
390
|
if (flags.dryRun) {
|
|
379
|
-
logStep('[3/
|
|
391
|
+
logStep('[3/13] Bump deps', `[DRY] ${allChanges.length} changes`);
|
|
380
392
|
allChanges.forEach((c) => log(` - ${c}`));
|
|
381
393
|
} else {
|
|
382
394
|
writePackageJson(cwd, pkg);
|
|
383
|
-
logStep('[3/
|
|
395
|
+
logStep('[3/13] Bump deps', `${allChanges.length} changes applied`);
|
|
384
396
|
if (flags.verbose) allChanges.forEach((c) => log(` - ${c}`));
|
|
385
397
|
}
|
|
386
398
|
|
|
@@ -389,28 +401,28 @@ function stepBump(cwd, flags) {
|
|
|
389
401
|
|
|
390
402
|
function stepNextCodemod(cwd, flags) {
|
|
391
403
|
if (flags.skipNextCodemod) {
|
|
392
|
-
logStep('[5/
|
|
404
|
+
logStep('[5/13] Next codemod', 'skipped');
|
|
393
405
|
return { ok: true };
|
|
394
406
|
}
|
|
395
407
|
const cmd = flags.dryRun
|
|
396
408
|
? 'npx --yes @next/codemod@latest upgrade latest --verbose'
|
|
397
409
|
: 'npx --yes @next/codemod@latest upgrade latest';
|
|
398
|
-
logStep('[5/
|
|
410
|
+
logStep('[5/13] Next codemod', `running ${cmd}`);
|
|
399
411
|
const result = runCommand(cmd, { cwd, dryRun: false, verbose: flags.verbose });
|
|
400
412
|
if (result.status !== 0) {
|
|
401
|
-
logStep('[5/
|
|
413
|
+
logStep('[5/13] Next codemod', `FAILED (exit ${result.status})`);
|
|
402
414
|
return { ok: false };
|
|
403
415
|
}
|
|
404
|
-
logStep('[5/
|
|
416
|
+
logStep('[5/13] Next codemod', 'ok');
|
|
405
417
|
return { ok: true };
|
|
406
418
|
}
|
|
407
419
|
|
|
408
420
|
function stepAuthCodemod(cwd, flags) {
|
|
409
421
|
if (flags.skipAuth) {
|
|
410
|
-
logStep('[6/
|
|
422
|
+
logStep('[6/13] Auth codemod', 'skipped');
|
|
411
423
|
return { ok: true };
|
|
412
424
|
}
|
|
413
|
-
logStep('[6/
|
|
425
|
+
logStep('[6/13] Auth codemod', 'running migrate-auth-v5');
|
|
414
426
|
try {
|
|
415
427
|
const prevArgv = process.argv;
|
|
416
428
|
process.argv = [
|
|
@@ -430,21 +442,21 @@ function stepAuthCodemod(cwd, flags) {
|
|
|
430
442
|
return result.then(
|
|
431
443
|
() => {
|
|
432
444
|
finish();
|
|
433
|
-
logStep('[6/
|
|
445
|
+
logStep('[6/13] Auth codemod', 'ok');
|
|
434
446
|
return { ok: true };
|
|
435
447
|
},
|
|
436
448
|
(err) => {
|
|
437
449
|
finish();
|
|
438
|
-
logStep('[6/
|
|
450
|
+
logStep('[6/13] Auth codemod', `FAILED: ${err.message}`);
|
|
439
451
|
return { ok: false };
|
|
440
452
|
}
|
|
441
453
|
);
|
|
442
454
|
}
|
|
443
455
|
finish();
|
|
444
|
-
logStep('[6/
|
|
456
|
+
logStep('[6/13] Auth codemod', 'ok');
|
|
445
457
|
return { ok: true };
|
|
446
458
|
} catch (err) {
|
|
447
|
-
logStep('[6/
|
|
459
|
+
logStep('[6/13] Auth codemod', `FAILED: ${err.message}`);
|
|
448
460
|
return { ok: false };
|
|
449
461
|
}
|
|
450
462
|
}
|
|
@@ -452,34 +464,34 @@ function stepAuthCodemod(cwd, flags) {
|
|
|
452
464
|
function stepSentryCodemod(cwd, flags, hasSentry) {
|
|
453
465
|
if (flags.skipSentry || !hasSentry) {
|
|
454
466
|
const reason = flags.skipSentry ? 'skipped' : 'no @sentry/nextjs, skipping';
|
|
455
|
-
logStep('[7/
|
|
467
|
+
logStep('[7/13] Sentry codemod', reason);
|
|
456
468
|
return { ok: true };
|
|
457
469
|
}
|
|
458
470
|
if (flags.dryRun) {
|
|
459
|
-
logStep('[7/
|
|
471
|
+
logStep('[7/13] Sentry codemod', '[DRY] would run sentry-9');
|
|
460
472
|
return { ok: true };
|
|
461
473
|
}
|
|
462
|
-
logStep('[7/
|
|
474
|
+
logStep('[7/13] Sentry codemod', 'running sentry-9');
|
|
463
475
|
try {
|
|
464
476
|
const savedCwd = process.cwd();
|
|
465
477
|
process.chdir(cwd);
|
|
466
478
|
const codemod = require(SENTRY_CODEMOD);
|
|
467
479
|
codemod.transform();
|
|
468
480
|
process.chdir(savedCwd);
|
|
469
|
-
logStep('[7/
|
|
481
|
+
logStep('[7/13] Sentry codemod', 'ok');
|
|
470
482
|
return { ok: true };
|
|
471
483
|
} catch (err) {
|
|
472
|
-
logStep('[7/
|
|
484
|
+
logStep('[7/13] Sentry codemod', `FAILED: ${err.message}`);
|
|
473
485
|
return { ok: false };
|
|
474
486
|
}
|
|
475
487
|
}
|
|
476
488
|
|
|
477
489
|
function stepEslintCodemod(cwd, flags) {
|
|
478
490
|
if (flags.skipEslint) {
|
|
479
|
-
logStep('[8/
|
|
491
|
+
logStep('[8/13] ESLint codemod', 'skipped');
|
|
480
492
|
return { ok: true };
|
|
481
493
|
}
|
|
482
|
-
logStep('[8/
|
|
494
|
+
logStep('[8/13] ESLint codemod', 'running migrate-eslint');
|
|
483
495
|
try {
|
|
484
496
|
const prevArgv = process.argv;
|
|
485
497
|
process.argv = [
|
|
@@ -493,20 +505,20 @@ function stepEslintCodemod(cwd, flags) {
|
|
|
493
505
|
codemod.transform();
|
|
494
506
|
process.chdir(savedCwd);
|
|
495
507
|
process.argv = prevArgv;
|
|
496
|
-
logStep('[8/
|
|
508
|
+
logStep('[8/13] ESLint codemod', 'ok');
|
|
497
509
|
return { ok: true };
|
|
498
510
|
} catch (err) {
|
|
499
|
-
logStep('[8/
|
|
511
|
+
logStep('[8/13] ESLint codemod', `FAILED: ${err.message}`);
|
|
500
512
|
return { ok: false };
|
|
501
513
|
}
|
|
502
514
|
}
|
|
503
515
|
|
|
504
516
|
function stepPageTypesCodemod(cwd, flags) {
|
|
505
517
|
if (flags.skipPageTypes) {
|
|
506
|
-
logStep('[9/
|
|
518
|
+
logStep('[9/13] Page types codemod', 'skipped');
|
|
507
519
|
return { ok: true };
|
|
508
520
|
}
|
|
509
|
-
logStep('[9/
|
|
521
|
+
logStep('[9/13] Page types codemod', 'running migrate-page-types');
|
|
510
522
|
try {
|
|
511
523
|
const prevArgv = process.argv;
|
|
512
524
|
process.argv = [
|
|
@@ -526,35 +538,123 @@ function stepPageTypesCodemod(cwd, flags) {
|
|
|
526
538
|
return result.then(
|
|
527
539
|
() => {
|
|
528
540
|
finish();
|
|
529
|
-
logStep('[9/
|
|
541
|
+
logStep('[9/13] Page types codemod', 'ok');
|
|
530
542
|
return { ok: true };
|
|
531
543
|
},
|
|
532
544
|
(err) => {
|
|
533
545
|
finish();
|
|
534
|
-
logStep('[9/
|
|
546
|
+
logStep('[9/13] Page types codemod', `FAILED: ${err.message}`);
|
|
535
547
|
return { ok: false };
|
|
536
548
|
}
|
|
537
549
|
);
|
|
538
550
|
}
|
|
539
551
|
finish();
|
|
540
|
-
logStep('[9/
|
|
552
|
+
logStep('[9/13] Page types codemod', 'ok');
|
|
541
553
|
return { ok: true };
|
|
542
554
|
} catch (err) {
|
|
543
|
-
logStep('[9/
|
|
555
|
+
logStep('[9/13] Page types codemod', `FAILED: ${err.message}`);
|
|
556
|
+
return { ok: false };
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function stepTemplateUrlCodemod(cwd, flags) {
|
|
561
|
+
if (flags.skipTemplateUrl) {
|
|
562
|
+
logStep('[10/13] Template currentUrl codemod', 'skipped');
|
|
563
|
+
return { ok: true };
|
|
564
|
+
}
|
|
565
|
+
logStep('[10/13] Template currentUrl codemod', 'running fix-template-currenturl');
|
|
566
|
+
try {
|
|
567
|
+
const prevArgv = process.argv;
|
|
568
|
+
process.argv = [
|
|
569
|
+
process.argv[0],
|
|
570
|
+
process.argv[1],
|
|
571
|
+
...(flags.dryRun ? ['--dry-run'] : [])
|
|
572
|
+
];
|
|
573
|
+
const savedCwd = process.cwd();
|
|
574
|
+
process.chdir(cwd);
|
|
575
|
+
const codemod = require(TEMPLATE_URL_CODEMOD);
|
|
576
|
+
const result = codemod.transform();
|
|
577
|
+
const finish = () => {
|
|
578
|
+
process.chdir(savedCwd);
|
|
579
|
+
process.argv = prevArgv;
|
|
580
|
+
};
|
|
581
|
+
if (result && typeof result.then === 'function') {
|
|
582
|
+
return result.then(
|
|
583
|
+
() => {
|
|
584
|
+
finish();
|
|
585
|
+
logStep('[10/13] Template currentUrl codemod', 'ok');
|
|
586
|
+
return { ok: true };
|
|
587
|
+
},
|
|
588
|
+
(err) => {
|
|
589
|
+
finish();
|
|
590
|
+
logStep('[10/13] Template currentUrl codemod', `FAILED: ${err.message}`);
|
|
591
|
+
return { ok: false };
|
|
592
|
+
}
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
finish();
|
|
596
|
+
logStep('[10/13] Template currentUrl codemod', 'ok');
|
|
597
|
+
return { ok: true };
|
|
598
|
+
} catch (err) {
|
|
599
|
+
logStep('[10/13] Template currentUrl codemod', `FAILED: ${err.message}`);
|
|
600
|
+
return { ok: false };
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function stepLoginActionCodemod(cwd, flags) {
|
|
605
|
+
if (flags.skipLoginAction) {
|
|
606
|
+
logStep('[11/13] Login form action codemod', 'skipped');
|
|
607
|
+
return { ok: true };
|
|
608
|
+
}
|
|
609
|
+
logStep('[11/13] Login form action codemod', 'running fix-login-form-action');
|
|
610
|
+
try {
|
|
611
|
+
const prevArgv = process.argv;
|
|
612
|
+
process.argv = [
|
|
613
|
+
process.argv[0],
|
|
614
|
+
process.argv[1],
|
|
615
|
+
...(flags.dryRun ? ['--dry-run'] : [])
|
|
616
|
+
];
|
|
617
|
+
const savedCwd = process.cwd();
|
|
618
|
+
process.chdir(cwd);
|
|
619
|
+
const codemod = require(LOGIN_ACTION_CODEMOD);
|
|
620
|
+
const result = codemod.transform();
|
|
621
|
+
const finish = () => {
|
|
622
|
+
process.chdir(savedCwd);
|
|
623
|
+
process.argv = prevArgv;
|
|
624
|
+
};
|
|
625
|
+
if (result && typeof result.then === 'function') {
|
|
626
|
+
return result.then(
|
|
627
|
+
() => {
|
|
628
|
+
finish();
|
|
629
|
+
logStep('[11/13] Login form action codemod', 'ok');
|
|
630
|
+
return { ok: true };
|
|
631
|
+
},
|
|
632
|
+
(err) => {
|
|
633
|
+
finish();
|
|
634
|
+
logStep('[11/13] Login form action codemod', `FAILED: ${err.message}`);
|
|
635
|
+
return { ok: false };
|
|
636
|
+
}
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
finish();
|
|
640
|
+
logStep('[11/13] Login form action codemod', 'ok');
|
|
641
|
+
return { ok: true };
|
|
642
|
+
} catch (err) {
|
|
643
|
+
logStep('[11/13] Login form action codemod', `FAILED: ${err.message}`);
|
|
544
644
|
return { ok: false };
|
|
545
645
|
}
|
|
546
646
|
}
|
|
547
647
|
|
|
548
648
|
function stepInstall(cwd, flags) {
|
|
549
649
|
if (flags.skipInstall) {
|
|
550
|
-
logStep('[4/
|
|
650
|
+
logStep('[4/13] yarn install', 'skipped');
|
|
551
651
|
return { ok: true };
|
|
552
652
|
}
|
|
553
653
|
if (flags.dryRun) {
|
|
554
|
-
logStep('[4/
|
|
654
|
+
logStep('[4/13] yarn install', '[DRY] yarn clean && yarn');
|
|
555
655
|
return { ok: true };
|
|
556
656
|
}
|
|
557
|
-
logStep('[4/
|
|
657
|
+
logStep('[4/13] yarn install', 'running yarn clean && yarn');
|
|
558
658
|
const clean = runCommand('yarn clean', {
|
|
559
659
|
cwd,
|
|
560
660
|
dryRun: false,
|
|
@@ -569,33 +669,33 @@ function stepInstall(cwd, flags) {
|
|
|
569
669
|
verbose: flags.verbose
|
|
570
670
|
});
|
|
571
671
|
if (install.status !== 0) {
|
|
572
|
-
logStep('[4/
|
|
672
|
+
logStep('[4/13] yarn install', `FAILED (exit ${install.status})`);
|
|
573
673
|
return { ok: false };
|
|
574
674
|
}
|
|
575
|
-
logStep('[4/
|
|
675
|
+
logStep('[4/13] yarn install', 'ok');
|
|
576
676
|
return { ok: true };
|
|
577
677
|
}
|
|
578
678
|
|
|
579
679
|
function stepBuild(cwd, flags) {
|
|
580
680
|
if (flags.skipBuild) {
|
|
581
|
-
logStep('[
|
|
681
|
+
logStep('[12/13] yarn build', 'skipped');
|
|
582
682
|
return { ok: true };
|
|
583
683
|
}
|
|
584
684
|
if (flags.dryRun) {
|
|
585
|
-
logStep('[
|
|
685
|
+
logStep('[12/13] yarn build', '[DRY] yarn build');
|
|
586
686
|
return { ok: true };
|
|
587
687
|
}
|
|
588
|
-
logStep('[
|
|
688
|
+
logStep('[12/13] yarn build', 'running');
|
|
589
689
|
const result = runCommand('yarn build', {
|
|
590
690
|
cwd,
|
|
591
691
|
dryRun: false,
|
|
592
692
|
verbose: flags.verbose
|
|
593
693
|
});
|
|
594
694
|
if (result.status !== 0) {
|
|
595
|
-
logStep('[
|
|
695
|
+
logStep('[12/13] yarn build', `FAILED (exit ${result.status})`);
|
|
596
696
|
return { ok: false };
|
|
597
697
|
}
|
|
598
|
-
logStep('[
|
|
698
|
+
logStep('[12/13] yarn build', 'ok');
|
|
599
699
|
return { ok: true };
|
|
600
700
|
}
|
|
601
701
|
|
|
@@ -607,7 +707,7 @@ function countTodoMarkers(cwd) {
|
|
|
607
707
|
}
|
|
608
708
|
|
|
609
709
|
function stepReport(cwd, results) {
|
|
610
|
-
logStep('[
|
|
710
|
+
logStep('[13/13] Report', 'summary');
|
|
611
711
|
const pkg = readPackageJson(cwd) || {};
|
|
612
712
|
const todoCount = countTodoMarkers(cwd);
|
|
613
713
|
|
|
@@ -667,7 +767,7 @@ async function transform() {
|
|
|
667
767
|
results.preflight = true;
|
|
668
768
|
await maybePause(flags, 'preflight');
|
|
669
769
|
} else {
|
|
670
|
-
logStep('[1/
|
|
770
|
+
logStep('[1/13] Preflight', 'skipped');
|
|
671
771
|
results.preflight = true;
|
|
672
772
|
}
|
|
673
773
|
|
|
@@ -709,6 +809,22 @@ async function transform() {
|
|
|
709
809
|
results.pageTypesCodemod = pageTypesAwaited.ok;
|
|
710
810
|
await maybePause(flags, 'page-types-codemod');
|
|
711
811
|
|
|
812
|
+
const templateUrlResult = stepTemplateUrlCodemod(cwd, flags);
|
|
813
|
+
const templateUrlAwaited =
|
|
814
|
+
templateUrlResult && typeof templateUrlResult.then === 'function'
|
|
815
|
+
? await templateUrlResult
|
|
816
|
+
: templateUrlResult;
|
|
817
|
+
results.templateUrlCodemod = templateUrlAwaited.ok;
|
|
818
|
+
await maybePause(flags, 'template-url-codemod');
|
|
819
|
+
|
|
820
|
+
const loginActionResult = stepLoginActionCodemod(cwd, flags);
|
|
821
|
+
const loginActionAwaited =
|
|
822
|
+
loginActionResult && typeof loginActionResult.then === 'function'
|
|
823
|
+
? await loginActionResult
|
|
824
|
+
: loginActionResult;
|
|
825
|
+
results.loginActionCodemod = loginActionAwaited.ok;
|
|
826
|
+
await maybePause(flags, 'login-action-codemod');
|
|
827
|
+
|
|
712
828
|
results.build = stepBuild(cwd, flags).ok;
|
|
713
829
|
} catch (err) {
|
|
714
830
|
log(`FATAL: ${err.message}`);
|