@akinon/projectzero 2.0.0-beta.22 → 2.0.0-beta.24

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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # @akinon/projectzero
2
2
 
3
+ ## 2.0.0-beta.24
4
+
5
+ ### Patch Changes
6
+
7
+ - 64003f74: ZERO-4337: Add upgrade-to-2 orchestrator codemod for brand projects
8
+ - 2f008a92: ZERO-4337: Add upgrade-to-2 orchestrator codemod
9
+
10
+ ## 2.0.0-beta.23
11
+
12
+ ### Patch Changes
13
+
14
+ - 3fa934ea: ZERO-4338: Rename middleware.ts to proxy.ts for Next 16 convention
15
+
3
16
  ## 2.0.0-beta.22
4
17
 
5
18
  ### Patch Changes
@@ -1,5 +1,69 @@
1
1
  # projectzeronext
2
2
 
3
+ ## 2.0.0-beta.24
4
+
5
+ ### Patch Changes
6
+
7
+ - @akinon/next@2.0.0-beta.24
8
+ - @akinon/pz-akifast@2.0.0-beta.24
9
+ - @akinon/pz-apple-pay@2.0.0-beta.24
10
+ - @akinon/pz-b2b@2.0.0-beta.24
11
+ - @akinon/pz-basket-gift-pack@2.0.0-beta.24
12
+ - @akinon/pz-bkm@2.0.0-beta.24
13
+ - @akinon/pz-checkout-gift-pack@2.0.0-beta.24
14
+ - @akinon/pz-click-collect@2.0.0-beta.24
15
+ - @akinon/pz-credit-payment@2.0.0-beta.24
16
+ - @akinon/pz-cybersource-uc@2.0.0-beta.24
17
+ - @akinon/pz-flow-payment@2.0.0-beta.24
18
+ - @akinon/pz-google-pay@2.0.0-beta.24
19
+ - @akinon/pz-gpay@2.0.0-beta.24
20
+ - @akinon/pz-haso@2.0.0-beta.24
21
+ - @akinon/pz-hepsipay@2.0.0-beta.24
22
+ - @akinon/pz-masterpass@2.0.0-beta.24
23
+ - @akinon/pz-masterpass-rest@2.0.0-beta.24
24
+ - @akinon/pz-multi-basket@2.0.0-beta.24
25
+ - @akinon/pz-one-click-checkout@2.0.0-beta.24
26
+ - @akinon/pz-otp@2.0.0-beta.24
27
+ - @akinon/pz-pay-on-delivery@2.0.0-beta.24
28
+ - @akinon/pz-saved-card@2.0.0-beta.24
29
+ - @akinon/pz-similar-products@2.0.0-beta.24
30
+ - @akinon/pz-tabby-extension@2.0.0-beta.24
31
+ - @akinon/pz-tamara-extension@2.0.0-beta.24
32
+ - @akinon/pz-theme@2.0.0-beta.24
33
+ - @akinon/pz-virtual-try-on@2.0.0-beta.24
34
+
35
+ ## 2.0.0-beta.23
36
+
37
+ ### Patch Changes
38
+
39
+ - @akinon/next@2.0.0-beta.23
40
+ - @akinon/pz-akifast@2.0.0-beta.23
41
+ - @akinon/pz-apple-pay@2.0.0-beta.23
42
+ - @akinon/pz-b2b@2.0.0-beta.23
43
+ - @akinon/pz-basket-gift-pack@2.0.0-beta.23
44
+ - @akinon/pz-bkm@2.0.0-beta.23
45
+ - @akinon/pz-checkout-gift-pack@2.0.0-beta.23
46
+ - @akinon/pz-click-collect@2.0.0-beta.23
47
+ - @akinon/pz-credit-payment@2.0.0-beta.23
48
+ - @akinon/pz-cybersource-uc@2.0.0-beta.23
49
+ - @akinon/pz-flow-payment@2.0.0-beta.23
50
+ - @akinon/pz-google-pay@2.0.0-beta.23
51
+ - @akinon/pz-gpay@2.0.0-beta.23
52
+ - @akinon/pz-haso@2.0.0-beta.23
53
+ - @akinon/pz-hepsipay@2.0.0-beta.23
54
+ - @akinon/pz-masterpass@2.0.0-beta.23
55
+ - @akinon/pz-masterpass-rest@2.0.0-beta.23
56
+ - @akinon/pz-multi-basket@2.0.0-beta.23
57
+ - @akinon/pz-one-click-checkout@2.0.0-beta.23
58
+ - @akinon/pz-otp@2.0.0-beta.23
59
+ - @akinon/pz-pay-on-delivery@2.0.0-beta.23
60
+ - @akinon/pz-saved-card@2.0.0-beta.23
61
+ - @akinon/pz-similar-products@2.0.0-beta.23
62
+ - @akinon/pz-tabby-extension@2.0.0-beta.23
63
+ - @akinon/pz-tamara-extension@2.0.0-beta.23
64
+ - @akinon/pz-theme@2.0.0-beta.23
65
+ - @akinon/pz-virtual-try-on@2.0.0-beta.23
66
+
3
67
  ## 2.0.0-beta.22
4
68
 
5
69
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectzeronext",
3
- "version": "2.0.0-beta.22",
3
+ "version": "2.0.0-beta.24",
4
4
  "private": true,
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -24,33 +24,33 @@
24
24
  "test:middleware": "jest middleware-matcher.test.ts --bail"
25
25
  },
26
26
  "dependencies": {
27
- "@akinon/next": "2.0.0-beta.22",
28
- "@akinon/pz-akifast": "2.0.0-beta.22",
29
- "@akinon/pz-apple-pay": "2.0.0-beta.22",
30
- "@akinon/pz-b2b": "2.0.0-beta.22",
31
- "@akinon/pz-basket-gift-pack": "2.0.0-beta.22",
32
- "@akinon/pz-bkm": "2.0.0-beta.22",
33
- "@akinon/pz-checkout-gift-pack": "2.0.0-beta.22",
34
- "@akinon/pz-click-collect": "2.0.0-beta.22",
35
- "@akinon/pz-credit-payment": "2.0.0-beta.22",
36
- "@akinon/pz-cybersource-uc": "2.0.0-beta.22",
37
- "@akinon/pz-flow-payment": "2.0.0-beta.22",
38
- "@akinon/pz-google-pay": "2.0.0-beta.22",
39
- "@akinon/pz-gpay": "2.0.0-beta.22",
40
- "@akinon/pz-haso": "2.0.0-beta.22",
41
- "@akinon/pz-hepsipay": "2.0.0-beta.22",
42
- "@akinon/pz-masterpass": "2.0.0-beta.22",
43
- "@akinon/pz-masterpass-rest": "2.0.0-beta.22",
44
- "@akinon/pz-multi-basket": "2.0.0-beta.22",
45
- "@akinon/pz-one-click-checkout": "2.0.0-beta.22",
46
- "@akinon/pz-otp": "2.0.0-beta.22",
47
- "@akinon/pz-pay-on-delivery": "2.0.0-beta.22",
48
- "@akinon/pz-saved-card": "2.0.0-beta.22",
49
- "@akinon/pz-similar-products": "2.0.0-beta.22",
50
- "@akinon/pz-tabby-extension": "2.0.0-beta.22",
51
- "@akinon/pz-tamara-extension": "2.0.0-beta.22",
52
- "@akinon/pz-theme": "2.0.0-beta.22",
53
- "@akinon/pz-virtual-try-on": "2.0.0-beta.22",
27
+ "@akinon/next": "2.0.0-beta.24",
28
+ "@akinon/pz-akifast": "2.0.0-beta.24",
29
+ "@akinon/pz-apple-pay": "2.0.0-beta.24",
30
+ "@akinon/pz-b2b": "2.0.0-beta.24",
31
+ "@akinon/pz-basket-gift-pack": "2.0.0-beta.24",
32
+ "@akinon/pz-bkm": "2.0.0-beta.24",
33
+ "@akinon/pz-checkout-gift-pack": "2.0.0-beta.24",
34
+ "@akinon/pz-click-collect": "2.0.0-beta.24",
35
+ "@akinon/pz-credit-payment": "2.0.0-beta.24",
36
+ "@akinon/pz-cybersource-uc": "2.0.0-beta.24",
37
+ "@akinon/pz-flow-payment": "2.0.0-beta.24",
38
+ "@akinon/pz-google-pay": "2.0.0-beta.24",
39
+ "@akinon/pz-gpay": "2.0.0-beta.24",
40
+ "@akinon/pz-haso": "2.0.0-beta.24",
41
+ "@akinon/pz-hepsipay": "2.0.0-beta.24",
42
+ "@akinon/pz-masterpass": "2.0.0-beta.24",
43
+ "@akinon/pz-masterpass-rest": "2.0.0-beta.24",
44
+ "@akinon/pz-multi-basket": "2.0.0-beta.24",
45
+ "@akinon/pz-one-click-checkout": "2.0.0-beta.24",
46
+ "@akinon/pz-otp": "2.0.0-beta.24",
47
+ "@akinon/pz-pay-on-delivery": "2.0.0-beta.24",
48
+ "@akinon/pz-saved-card": "2.0.0-beta.24",
49
+ "@akinon/pz-similar-products": "2.0.0-beta.24",
50
+ "@akinon/pz-tabby-extension": "2.0.0-beta.24",
51
+ "@akinon/pz-tamara-extension": "2.0.0-beta.24",
52
+ "@akinon/pz-theme": "2.0.0-beta.24",
53
+ "@akinon/pz-virtual-try-on": "2.0.0-beta.24",
54
54
  "@hookform/resolvers": "2.9.0",
55
55
  "@next/third-parties": "16.1.6",
56
56
  "@react-google-maps/api": "2.17.1",
@@ -73,7 +73,7 @@
73
73
  "yup": "0.32.11"
74
74
  },
75
75
  "devDependencies": {
76
- "@akinon/eslint-plugin-projectzero": "2.0.0-beta.22",
76
+ "@akinon/eslint-plugin-projectzero": "2.0.0-beta.24",
77
77
  "@semantic-release/changelog": "6.0.2",
78
78
  "@semantic-release/exec": "6.0.3",
79
79
  "@semantic-release/git": "10.0.1",
@@ -3,7 +3,7 @@ import { NextMiddleware, NextResponse } from 'next/server';
3
3
 
4
4
  /**
5
5
  * !IMPORTANT
6
- * Do not remove this file or withPzDefault middleware.
6
+ * Do not remove this file or withPzDefault wrapper.
7
7
  * It is required for the application to work properly.
8
8
  * If you need to add custom matcher, don't remove the existing one.
9
9
  */
@@ -18,7 +18,7 @@ export const config = {
18
18
  ]
19
19
  };
20
20
 
21
- const middleware: NextMiddleware = () =>
21
+ const proxy: NextMiddleware = () =>
22
22
  // req: PzNextRequest,
23
23
  // event: NextFetchEvent
24
24
  {
@@ -32,4 +32,4 @@ const middleware: NextMiddleware = () =>
32
32
  return NextResponse.next();
33
33
  };
34
34
 
35
- export default withPzDefault(middleware);
35
+ export default withPzDefault(proxy);
@@ -0,0 +1,549 @@
1
+ /**
2
+ * upgrade-to-2: orchestrator codemod that chains every step required to move
3
+ * a Project Zero brand project from pz-next 1.x to 2.0 beta.
4
+ *
5
+ * Steps (in order):
6
+ * 1. Preflight checks (git state, package.json, Node.js project)
7
+ * 2. Create a backup git branch (pre-upgrade-2-YYYYMMDD)
8
+ * 3. Bump package.json dependencies to the 2.0 target matrix
9
+ * 4. Run @next/codemod upgrade (Next 16 + React 19)
10
+ * 5. Run migrate-auth-v5 codemod (next-auth v4 -> v5)
11
+ * 6. Run sentry-9 codemod (only when @sentry/nextjs is present)
12
+ * 7. yarn clean && yarn install
13
+ * 8. yarn build (validation)
14
+ * 9. Summary report
15
+ *
16
+ * Out of scope in this first version:
17
+ * - Tailwind v3 -> v4 migration (run manually with @tailwindcss/upgrade)
18
+ * - migrate-segments codemod (separate task)
19
+ *
20
+ * Usage:
21
+ * cd <brand-root>
22
+ * npx @akinon/projectzero codemod --codemod=upgrade-to-2 [flags]
23
+ *
24
+ * Flags:
25
+ * --dry-run Preview only. No package.json writes, no install,
26
+ * codemods invoked in dry-run where supported.
27
+ * --skip-preflight Skip git state / package.json checks
28
+ * --skip-backup Do not create pre-upgrade-2-* branch
29
+ * --skip-bump Do not modify package.json
30
+ * --skip-next-codemod Do not run @next/codemod upgrade
31
+ * --skip-auth Do not run migrate-auth-v5
32
+ * --skip-sentry Do not run sentry-9 (auto-skipped if not needed)
33
+ * --skip-install Do not run yarn install
34
+ * --skip-build Do not run yarn build
35
+ * --interactive Pause for confirmation after each step
36
+ * --verbose Extra logs
37
+ */
38
+
39
+ const path = require('path');
40
+ const fs = require('fs');
41
+ const { execSync, spawnSync } = require('child_process');
42
+ const readline = require('readline');
43
+
44
+ const AUTH_CODEMOD = path.resolve(__dirname, '..', 'migrate-auth-v5');
45
+ const SENTRY_CODEMOD = path.resolve(__dirname, '..', 'sentry-9');
46
+
47
+ const TARGET_MATRIX = {
48
+ dependencies: {
49
+ next: '16.2.4',
50
+ react: '19.2.5',
51
+ 'react-dom': '19.2.5',
52
+ 'next-auth': '5.0.0-beta.25',
53
+ '@akinon/next': 'beta'
54
+ },
55
+ devDependencies: {
56
+ '@types/react': '19.2.14',
57
+ '@types/react-dom': '19.2.3',
58
+ 'eslint-config-next': '16.2.4',
59
+ tailwindcss: '3.4.17',
60
+ '@akinon/projectzero': 'beta',
61
+ '@akinon/eslint-plugin-projectzero': 'beta',
62
+ '@akinon/tsconfig': 'beta'
63
+ },
64
+ conditional: {
65
+ '@sentry/nextjs': '^9.0.0'
66
+ }
67
+ };
68
+
69
+ const STEP_COLUMN_WIDTH = 32;
70
+
71
+ function log(msg) {
72
+ const ts = new Date().toISOString().replace('T', ' ').slice(0, 19);
73
+ console.log(`[${ts}] ${msg}`);
74
+ }
75
+
76
+ function logStep(step, msg) {
77
+ const padded = step.padEnd(STEP_COLUMN_WIDTH, ' ');
78
+ log(`${padded} ${msg}`);
79
+ }
80
+
81
+ function runCommand(cmd, { cwd, dryRun, verbose }) {
82
+ if (dryRun) {
83
+ log(`[DRY] ${cmd}`);
84
+ return { status: 0, dry: true };
85
+ }
86
+ if (verbose) log(`$ ${cmd}`);
87
+ try {
88
+ execSync(cmd, { cwd, stdio: 'inherit' });
89
+ return { status: 0 };
90
+ } catch (err) {
91
+ return { status: err.status || 1, error: err };
92
+ }
93
+ }
94
+
95
+ function confirm(question) {
96
+ return new Promise((resolve) => {
97
+ const rl = readline.createInterface({
98
+ input: process.stdin,
99
+ output: process.stdout
100
+ });
101
+ rl.question(`${question} [Y/n] `, (answer) => {
102
+ rl.close();
103
+ resolve(answer.trim().toLowerCase() !== 'n');
104
+ });
105
+ });
106
+ }
107
+
108
+ function parseArgs(argv) {
109
+ const flags = {
110
+ dryRun: argv.includes('--dry-run'),
111
+ skipPreflight: argv.includes('--skip-preflight'),
112
+ skipBackup: argv.includes('--skip-backup'),
113
+ skipBump: argv.includes('--skip-bump'),
114
+ skipNextCodemod: argv.includes('--skip-next-codemod'),
115
+ skipAuth: argv.includes('--skip-auth'),
116
+ skipSentry: argv.includes('--skip-sentry'),
117
+ skipInstall: argv.includes('--skip-install'),
118
+ skipBuild: argv.includes('--skip-build'),
119
+ interactive: argv.includes('--interactive'),
120
+ verbose: argv.includes('--verbose')
121
+ };
122
+ return flags;
123
+ }
124
+
125
+ function readPackageJson(cwd) {
126
+ const pkgPath = path.join(cwd, 'package.json');
127
+ if (!fs.existsSync(pkgPath)) return null;
128
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
129
+ }
130
+
131
+ function writePackageJson(cwd, pkg) {
132
+ const pkgPath = path.join(cwd, 'package.json');
133
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
134
+ }
135
+
136
+ function stepPreflight(cwd, flags) {
137
+ logStep('[1/9] Preflight', 'starting');
138
+
139
+ const pkg = readPackageJson(cwd);
140
+ if (!pkg) {
141
+ throw new Error('No package.json found in working directory');
142
+ }
143
+
144
+ if (!pkg.dependencies?.next && !pkg.devDependencies?.next) {
145
+ throw new Error('This does not appear to be a Next.js project (no `next` dependency)');
146
+ }
147
+
148
+ const gitStatus = spawnSync('git', ['status', '--porcelain'], {
149
+ cwd,
150
+ encoding: 'utf-8'
151
+ });
152
+ if (gitStatus.status !== 0) {
153
+ throw new Error('git not available or not a git repo');
154
+ }
155
+ const dirty = gitStatus.stdout.trim().length > 0;
156
+ if (dirty && !flags.dryRun) {
157
+ throw new Error(
158
+ 'Working directory has uncommitted changes. Commit or stash before running upgrade-to-2.'
159
+ );
160
+ }
161
+
162
+ const currentNext =
163
+ pkg.dependencies?.next || pkg.devDependencies?.next || 'unknown';
164
+ const currentReact = pkg.dependencies?.react || 'unknown';
165
+ const currentAuth = pkg.dependencies?.['next-auth'] || 'unknown';
166
+ const currentAkinon = pkg.dependencies?.['@akinon/next'] || 'unknown';
167
+
168
+ log(` Current: next@${currentNext} react@${currentReact} next-auth@${currentAuth} @akinon/next@${currentAkinon}`);
169
+ log(` Target: next@${TARGET_MATRIX.dependencies.next} react@${TARGET_MATRIX.dependencies.react} next-auth@${TARGET_MATRIX.dependencies['next-auth']} @akinon/next@beta`);
170
+
171
+ logStep('[1/9] Preflight', 'ok');
172
+ }
173
+
174
+ function stepBackup(cwd, flags) {
175
+ if (flags.skipBackup) {
176
+ logStep('[2/9] Backup branch', 'skipped');
177
+ return null;
178
+ }
179
+ const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
180
+ const branchName = `pre-upgrade-2-${today}`;
181
+
182
+ if (flags.dryRun) {
183
+ logStep('[2/9] Backup branch', `[DRY] git branch ${branchName}`);
184
+ return branchName;
185
+ }
186
+
187
+ const existing = spawnSync(
188
+ 'git',
189
+ ['rev-parse', '--verify', branchName],
190
+ { cwd, encoding: 'utf-8' }
191
+ );
192
+ if (existing.status === 0) {
193
+ logStep('[2/9] Backup branch', `already exists: ${branchName}`);
194
+ return branchName;
195
+ }
196
+
197
+ const result = spawnSync('git', ['branch', branchName], {
198
+ cwd,
199
+ stdio: 'inherit'
200
+ });
201
+ if (result.status !== 0) {
202
+ throw new Error(`Failed to create backup branch ${branchName}`);
203
+ }
204
+ logStep('[2/9] Backup branch', `created ${branchName}`);
205
+ return branchName;
206
+ }
207
+
208
+ function bumpDepsSection(section, matrix) {
209
+ const changes = [];
210
+ if (!section) return { section: section || {}, changes };
211
+ for (const [name, version] of Object.entries(matrix)) {
212
+ if (section[name] !== undefined && section[name] !== version) {
213
+ changes.push(`${name}: ${section[name]} -> ${version}`);
214
+ section[name] = version;
215
+ }
216
+ }
217
+ return { section, changes };
218
+ }
219
+
220
+ function pinNextScriptsToWebpack(pkg) {
221
+ // Next 16 defaults build/dev to Turbopack, which surfaces bundle-time
222
+ // errors for Node APIs used on the edge runtime (e.g. CompressionStream
223
+ // inside @akinon/next/lib/cache.ts). pz-next itself ships with
224
+ // `next dev --webpack` / `next build --webpack` until those are fixed;
225
+ // brand projects need the same flag to build successfully.
226
+ const changes = [];
227
+ if (!pkg.scripts) return changes;
228
+
229
+ ['dev', 'build'].forEach((scriptName) => {
230
+ const current = pkg.scripts[scriptName];
231
+ if (typeof current !== 'string') return;
232
+ if (current.includes('--webpack')) return;
233
+ if (!/\bnext\s+(dev|build)\b/.test(current)) return;
234
+ const updated = current.replace(
235
+ /\bnext\s+(dev|build)\b/,
236
+ 'next $1 --webpack'
237
+ );
238
+ pkg.scripts[scriptName] = updated;
239
+ changes.push(`scripts.${scriptName}: "${current}" -> "${updated}"`);
240
+ });
241
+
242
+ return changes;
243
+ }
244
+
245
+ function stepBump(cwd, flags) {
246
+ if (flags.skipBump) {
247
+ logStep('[3/9] Bump deps', 'skipped');
248
+ return { hasSentry: false, changes: [] };
249
+ }
250
+
251
+ const pkg = readPackageJson(cwd);
252
+ const hasSentry = Boolean(pkg.dependencies?.['@sentry/nextjs']);
253
+
254
+ const allChanges = [];
255
+ const deps = bumpDepsSection(pkg.dependencies, TARGET_MATRIX.dependencies);
256
+ allChanges.push(...deps.changes);
257
+ pkg.dependencies = deps.section;
258
+
259
+ const devDeps = bumpDepsSection(
260
+ pkg.devDependencies,
261
+ TARGET_MATRIX.devDependencies
262
+ );
263
+ allChanges.push(...devDeps.changes);
264
+ pkg.devDependencies = devDeps.section;
265
+
266
+ if (hasSentry) {
267
+ const sentryDeps = bumpDepsSection(pkg.dependencies, {
268
+ '@sentry/nextjs': TARGET_MATRIX.conditional['@sentry/nextjs']
269
+ });
270
+ allChanges.push(...sentryDeps.changes);
271
+ pkg.dependencies = sentryDeps.section;
272
+ }
273
+
274
+ const scriptChanges = pinNextScriptsToWebpack(pkg);
275
+ allChanges.push(...scriptChanges);
276
+
277
+ if (allChanges.length === 0) {
278
+ logStep('[3/9] Bump deps', 'already at target, no changes');
279
+ return { hasSentry, changes: [] };
280
+ }
281
+
282
+ if (flags.dryRun) {
283
+ logStep('[3/9] Bump deps', `[DRY] ${allChanges.length} changes`);
284
+ allChanges.forEach((c) => log(` - ${c}`));
285
+ } else {
286
+ writePackageJson(cwd, pkg);
287
+ logStep('[3/9] Bump deps', `${allChanges.length} changes applied`);
288
+ if (flags.verbose) allChanges.forEach((c) => log(` - ${c}`));
289
+ }
290
+
291
+ return { hasSentry, changes: allChanges };
292
+ }
293
+
294
+ function stepNextCodemod(cwd, flags) {
295
+ if (flags.skipNextCodemod) {
296
+ logStep('[5/9] Next codemod', 'skipped');
297
+ return { ok: true };
298
+ }
299
+ const cmd = flags.dryRun
300
+ ? 'npx --yes @next/codemod@latest upgrade latest --verbose'
301
+ : 'npx --yes @next/codemod@latest upgrade latest';
302
+ logStep('[5/9] Next codemod', `running ${cmd}`);
303
+ const result = runCommand(cmd, { cwd, dryRun: false, verbose: flags.verbose });
304
+ if (result.status !== 0) {
305
+ logStep('[5/9] Next codemod', `FAILED (exit ${result.status})`);
306
+ return { ok: false };
307
+ }
308
+ logStep('[5/9] Next codemod', 'ok');
309
+ return { ok: true };
310
+ }
311
+
312
+ function stepAuthCodemod(cwd, flags) {
313
+ if (flags.skipAuth) {
314
+ logStep('[6/9] Auth codemod', 'skipped');
315
+ return { ok: true };
316
+ }
317
+ logStep('[6/9] Auth codemod', 'running migrate-auth-v5');
318
+ try {
319
+ const prevArgv = process.argv;
320
+ process.argv = [
321
+ process.argv[0],
322
+ process.argv[1],
323
+ ...(flags.dryRun ? ['--dry-run'] : [])
324
+ ];
325
+ const codemod = require(AUTH_CODEMOD);
326
+ const savedCwd = process.cwd();
327
+ process.chdir(cwd);
328
+ const result = codemod.transform();
329
+ const finish = () => {
330
+ process.chdir(savedCwd);
331
+ process.argv = prevArgv;
332
+ };
333
+ if (result && typeof result.then === 'function') {
334
+ return result.then(
335
+ () => {
336
+ finish();
337
+ logStep('[6/9] Auth codemod', 'ok');
338
+ return { ok: true };
339
+ },
340
+ (err) => {
341
+ finish();
342
+ logStep('[6/9] Auth codemod', `FAILED: ${err.message}`);
343
+ return { ok: false };
344
+ }
345
+ );
346
+ }
347
+ finish();
348
+ logStep('[6/9] Auth codemod', 'ok');
349
+ return { ok: true };
350
+ } catch (err) {
351
+ logStep('[6/9] Auth codemod', `FAILED: ${err.message}`);
352
+ return { ok: false };
353
+ }
354
+ }
355
+
356
+ function stepSentryCodemod(cwd, flags, hasSentry) {
357
+ if (flags.skipSentry || !hasSentry) {
358
+ const reason = flags.skipSentry ? 'skipped' : 'no @sentry/nextjs, skipping';
359
+ logStep('[7/9] Sentry codemod', reason);
360
+ return { ok: true };
361
+ }
362
+ if (flags.dryRun) {
363
+ logStep('[7/9] Sentry codemod', '[DRY] would run sentry-9');
364
+ return { ok: true };
365
+ }
366
+ logStep('[7/9] Sentry codemod', 'running sentry-9');
367
+ try {
368
+ const savedCwd = process.cwd();
369
+ process.chdir(cwd);
370
+ const codemod = require(SENTRY_CODEMOD);
371
+ codemod.transform();
372
+ process.chdir(savedCwd);
373
+ logStep('[7/9] Sentry codemod', 'ok');
374
+ return { ok: true };
375
+ } catch (err) {
376
+ logStep('[7/9] Sentry codemod', `FAILED: ${err.message}`);
377
+ return { ok: false };
378
+ }
379
+ }
380
+
381
+ function stepInstall(cwd, flags) {
382
+ if (flags.skipInstall) {
383
+ logStep('[4/9] yarn install', 'skipped');
384
+ return { ok: true };
385
+ }
386
+ if (flags.dryRun) {
387
+ logStep('[4/9] yarn install', '[DRY] yarn clean && yarn');
388
+ return { ok: true };
389
+ }
390
+ logStep('[4/9] yarn install', 'running yarn clean && yarn');
391
+ const clean = runCommand('yarn clean', {
392
+ cwd,
393
+ dryRun: false,
394
+ verbose: flags.verbose
395
+ });
396
+ if (clean.status !== 0) {
397
+ log(' yarn clean failed, continuing with yarn install');
398
+ }
399
+ const install = runCommand('yarn', {
400
+ cwd,
401
+ dryRun: false,
402
+ verbose: flags.verbose
403
+ });
404
+ if (install.status !== 0) {
405
+ logStep('[4/9] yarn install', `FAILED (exit ${install.status})`);
406
+ return { ok: false };
407
+ }
408
+ logStep('[4/9] yarn install', 'ok');
409
+ return { ok: true };
410
+ }
411
+
412
+ function stepBuild(cwd, flags) {
413
+ if (flags.skipBuild) {
414
+ logStep('[8/9] yarn build', 'skipped');
415
+ return { ok: true };
416
+ }
417
+ if (flags.dryRun) {
418
+ logStep('[8/9] yarn build', '[DRY] yarn build');
419
+ return { ok: true };
420
+ }
421
+ logStep('[8/9] yarn build', 'running');
422
+ const result = runCommand('yarn build', {
423
+ cwd,
424
+ dryRun: false,
425
+ verbose: flags.verbose
426
+ });
427
+ if (result.status !== 0) {
428
+ logStep('[8/9] yarn build', `FAILED (exit ${result.status})`);
429
+ return { ok: false };
430
+ }
431
+ logStep('[8/9] yarn build', 'ok');
432
+ return { ok: true };
433
+ }
434
+
435
+ function countTodoMarkers(cwd) {
436
+ const authFile = path.join(cwd, 'src', 'auth.ts');
437
+ if (!fs.existsSync(authFile)) return 0;
438
+ const content = fs.readFileSync(authFile, 'utf-8');
439
+ return (content.match(/TODO:v5/g) || []).length;
440
+ }
441
+
442
+ function stepReport(cwd, results) {
443
+ logStep('[9/9] Report', 'summary');
444
+ const pkg = readPackageJson(cwd) || {};
445
+ const todoCount = countTodoMarkers(cwd);
446
+
447
+ console.log('\n==================== upgrade-to-2 summary ====================');
448
+ console.log(` Preflight: ${results.preflight ? 'ok' : 'failed'}`);
449
+ console.log(` Backup branch: ${results.backupBranch || 'skipped'}`);
450
+ console.log(` Deps changes: ${results.bumpChanges}`);
451
+ console.log(` Install: ${results.install ? 'ok' : 'FAILED'}`);
452
+ console.log(` Next codemod: ${results.nextCodemod ? 'ok' : 'FAILED'}`);
453
+ console.log(` Auth codemod: ${results.authCodemod ? 'ok' : 'FAILED'}`);
454
+ console.log(` Sentry codemod: ${results.sentryCodemod ? 'ok' : results.sentrySkippedReason || 'FAILED'}`);
455
+ console.log(` Build: ${results.build ? 'ok' : 'FAILED'}`);
456
+ console.log('');
457
+ console.log(` Versions after:`);
458
+ console.log(` next: ${pkg.dependencies?.next || '-'}`);
459
+ console.log(` react: ${pkg.dependencies?.react || '-'}`);
460
+ console.log(` next-auth: ${pkg.dependencies?.['next-auth'] || '-'}`);
461
+ console.log(` @akinon/next: ${pkg.dependencies?.['@akinon/next'] || '-'}`);
462
+ if (pkg.dependencies?.['@sentry/nextjs']) {
463
+ console.log(` @sentry/nextjs: ${pkg.dependencies['@sentry/nextjs']}`);
464
+ }
465
+ console.log('');
466
+ if (todoCount > 0) {
467
+ console.log(` Manual work left in src/auth.ts: ${todoCount} TODO:v5 markers`);
468
+ console.log(` grep -n "TODO:v5" src/auth.ts`);
469
+ }
470
+ console.log(' Next steps (manual):');
471
+ console.log(' - Tailwind v4 migration: npx @tailwindcss/upgrade@latest');
472
+ console.log(' - Review backup branch: ' + (results.backupBranch || 'n/a'));
473
+ console.log(' - Run dev server and smoke test auth, cart, checkout');
474
+ console.log('==============================================================\n');
475
+ }
476
+
477
+ async function maybePause(flags, stepLabel) {
478
+ if (!flags.interactive) return true;
479
+ const ok = await confirm(`Continue after ${stepLabel}?`);
480
+ if (!ok) {
481
+ log('Aborted by user.');
482
+ process.exit(1);
483
+ }
484
+ return true;
485
+ }
486
+
487
+ async function transform() {
488
+ const cwd = path.resolve(process.cwd());
489
+ const flags = parseArgs(process.argv);
490
+
491
+ log(`upgrade-to-2 starting in ${cwd}${flags.dryRun ? ' (DRY RUN)' : ''}`);
492
+
493
+ const results = {};
494
+
495
+ try {
496
+ if (!flags.skipPreflight) {
497
+ stepPreflight(cwd, flags);
498
+ results.preflight = true;
499
+ await maybePause(flags, 'preflight');
500
+ } else {
501
+ logStep('[1/9] Preflight', 'skipped');
502
+ results.preflight = true;
503
+ }
504
+
505
+ results.backupBranch = stepBackup(cwd, flags);
506
+ await maybePause(flags, 'backup');
507
+
508
+ const bumpResult = stepBump(cwd, flags);
509
+ results.bumpChanges = bumpResult.changes.length;
510
+ const hasSentry = bumpResult.hasSentry;
511
+ await maybePause(flags, 'bump');
512
+
513
+ results.install = stepInstall(cwd, flags).ok;
514
+ await maybePause(flags, 'install');
515
+
516
+ results.nextCodemod = stepNextCodemod(cwd, flags).ok;
517
+ await maybePause(flags, 'next-codemod');
518
+
519
+ const authResult = stepAuthCodemod(cwd, flags);
520
+ const authAwaited = authResult && typeof authResult.then === 'function'
521
+ ? await authResult
522
+ : authResult;
523
+ results.authCodemod = authAwaited.ok;
524
+ await maybePause(flags, 'auth-codemod');
525
+
526
+ const sentryResult = stepSentryCodemod(cwd, flags, hasSentry);
527
+ results.sentryCodemod = sentryResult.ok;
528
+ if (flags.skipSentry) results.sentrySkippedReason = 'skipped';
529
+ else if (!hasSentry) results.sentrySkippedReason = 'no @sentry/nextjs';
530
+ await maybePause(flags, 'sentry-codemod');
531
+
532
+ results.build = stepBuild(cwd, flags).ok;
533
+ } catch (err) {
534
+ log(`FATAL: ${err.message}`);
535
+ process.exitCode = 1;
536
+ }
537
+
538
+ stepReport(cwd, results);
539
+ log('upgrade-to-2 done');
540
+ }
541
+
542
+ module.exports = { transform };
543
+
544
+ if (require.main === module) {
545
+ transform().catch((err) => {
546
+ console.error(err);
547
+ process.exit(1);
548
+ });
549
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/projectzero",
3
- "version": "2.0.0-beta.22",
3
+ "version": "2.0.0-beta.24",
4
4
  "private": false,
5
5
  "description": "CLI tool to manage your Project Zero Next project",
6
6
  "bin": {