@next/codemod 15.0.0-canary.167 → 15.0.0-canary.169
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/bin/next-codemod.js +27 -3
- package/bin/transform.js +104 -0
- package/bin/upgrade.js +180 -202
- package/lib/cra-to-next/global-css-transform.js +1 -2
- package/lib/cra-to-next/index-to-component.js +1 -2
- package/lib/handle-package.js +5 -5
- package/lib/install.js +1 -1
- package/lib/run-jscodeshift.js +8 -1
- package/lib/utils.js +92 -0
- package/package.json +2 -3
- package/transforms/cra-to-next.js +237 -235
- package/transforms/lib/async-request-api/next-async-dynamic-api.js +3 -4
- package/transforms/lib/async-request-api/next-async-dynamic-prop.js +5 -7
- package/transforms/name-default-component.js +2 -3
- package/transforms/new-link.js +2 -3
- package/transforms/next-dynamic-access-named-export.js +2 -4
- package/transforms/next-image-experimental.js +8 -12
- package/transforms/next-image-to-legacy-image.js +4 -6
- package/transforms/next-request-geo-ip.js +10 -6
- package/bin/cli.js +0 -237
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -33,6 +24,18 @@ const craTransformsPath = path_1.default.join('../lib/cra-to-next');
|
|
|
33
24
|
const globalCssTransformPath = require.resolve(path_1.default.join(craTransformsPath, 'global-css-transform.js'));
|
|
34
25
|
const indexTransformPath = require.resolve(path_1.default.join(craTransformsPath, 'index-to-component.js'));
|
|
35
26
|
class CraTransform {
|
|
27
|
+
appDir;
|
|
28
|
+
pagesDir;
|
|
29
|
+
isVite;
|
|
30
|
+
isCra;
|
|
31
|
+
isDryRun;
|
|
32
|
+
indexPage;
|
|
33
|
+
installClient;
|
|
34
|
+
shouldLogInfo;
|
|
35
|
+
packageJsonPath;
|
|
36
|
+
shouldUseTypeScript;
|
|
37
|
+
packageJsonData;
|
|
38
|
+
jscodeShiftFlags;
|
|
36
39
|
constructor(files, flags) {
|
|
37
40
|
this.isDryRun = flags.dry;
|
|
38
41
|
this.jscodeShiftFlags = flags;
|
|
@@ -43,7 +46,7 @@ class CraTransform {
|
|
|
43
46
|
this.pagesDir = this.getPagesDir();
|
|
44
47
|
this.installClient = this.checkForYarn() ? 'yarn' : 'npm';
|
|
45
48
|
const { dependencies, devDependencies } = this.packageJsonData;
|
|
46
|
-
const hasDep = (dep) =>
|
|
49
|
+
const hasDep = (dep) => dependencies?.[dep] || devDependencies?.[dep];
|
|
47
50
|
this.isCra = hasDep('react-scripts');
|
|
48
51
|
this.isVite = !this.isCra && hasDep('vite');
|
|
49
52
|
if (!this.isCra && !this.isVite) {
|
|
@@ -61,45 +64,43 @@ class CraTransform {
|
|
|
61
64
|
fatalMessage('Error: unable to find `src/index`');
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
|
-
transform() {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
yield this.createPages();
|
|
102
|
-
});
|
|
67
|
+
async transform() {
|
|
68
|
+
console.log('Transforming CRA project at:', this.appDir);
|
|
69
|
+
// convert src/index.js to a react component to render
|
|
70
|
+
// inside of Next.js instead of the custom render root
|
|
71
|
+
const indexTransformRes = await (0, run_jscodeshift_1.default)(indexTransformPath, { ...this.jscodeShiftFlags, silent: true, verbose: 0 }, [path_1.default.join(this.appDir, 'src', this.indexPage)]);
|
|
72
|
+
if (indexTransformRes.error > 0) {
|
|
73
|
+
fatalMessage(`Error: failed to apply transforms for src/${this.indexPage}, please check for syntax errors to continue`);
|
|
74
|
+
}
|
|
75
|
+
if (index_to_component_1.indexContext.multipleRenderRoots) {
|
|
76
|
+
fatalMessage(`Error: multiple ReactDOM.render roots in src/${this.indexPage}, migrate additional render roots to use portals instead to continue.\n` +
|
|
77
|
+
`See here for more info: https://react.dev/reference/react-dom/createPortal`);
|
|
78
|
+
}
|
|
79
|
+
if (index_to_component_1.indexContext.nestedRender) {
|
|
80
|
+
fatalMessage(`Error: nested ReactDOM.render found in src/${this.indexPage}, please migrate this to a top-level render (no wrapping functions) to continue`);
|
|
81
|
+
}
|
|
82
|
+
// comment out global style imports and collect them
|
|
83
|
+
// so that we can add them to _app
|
|
84
|
+
const globalCssRes = await (0, run_jscodeshift_1.default)(globalCssTransformPath, { ...this.jscodeShiftFlags }, [this.appDir]);
|
|
85
|
+
if (globalCssRes.error > 0) {
|
|
86
|
+
fatalMessage(`Error: failed to apply transforms for src/${this.indexPage}, please check for syntax errors to continue`);
|
|
87
|
+
}
|
|
88
|
+
if (!this.isDryRun) {
|
|
89
|
+
await fs_1.default.promises.mkdir(path_1.default.join(this.appDir, this.pagesDir));
|
|
90
|
+
}
|
|
91
|
+
this.logCreate(this.pagesDir);
|
|
92
|
+
if (global_css_transform_1.globalCssContext.reactSvgImports.size > 0) {
|
|
93
|
+
// This de-opts webpack 5 since svg/webpack doesn't support webpack 5 yet,
|
|
94
|
+
// so we don't support this automatically
|
|
95
|
+
fatalMessage(`Error: import {ReactComponent} from './logo.svg' is not supported, please use normal SVG imports to continue.\n` +
|
|
96
|
+
`React SVG imports found in:\n${[
|
|
97
|
+
...global_css_transform_1.globalCssContext.reactSvgImports,
|
|
98
|
+
].join('\n')}`);
|
|
99
|
+
}
|
|
100
|
+
await this.updatePackageJson();
|
|
101
|
+
await this.createNextConfig();
|
|
102
|
+
await this.updateGitIgnore();
|
|
103
|
+
await this.createPages();
|
|
103
104
|
}
|
|
104
105
|
checkForYarn() {
|
|
105
106
|
try {
|
|
@@ -130,112 +131,111 @@ class CraTransform {
|
|
|
130
131
|
console.log(...args);
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
|
-
createPages() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return child.data;
|
|
153
|
-
}
|
|
154
|
-
return '';
|
|
155
|
-
})
|
|
156
|
-
.join('');
|
|
157
|
-
};
|
|
158
|
-
const serializeAttrs = (attrs) => {
|
|
159
|
-
const attrStr = Object.keys(attrs || {})
|
|
160
|
-
.map((name) => {
|
|
161
|
-
const reactName = html_to_react_attributes_1.default[name] || name;
|
|
162
|
-
const value = attrs[name];
|
|
163
|
-
// allow process.env access to work dynamically still
|
|
164
|
-
if (value.match(/%([a-zA-Z0-9_]{0,})%/)) {
|
|
165
|
-
return `${reactName}={\`${value.replace(/%([a-zA-Z0-9_]{0,})%/g, (subStr) => {
|
|
166
|
-
return `\${process.env.${subStr.slice(1, -1)}}`;
|
|
167
|
-
})}\`}`;
|
|
168
|
-
}
|
|
169
|
-
return `${reactName}="${value}"`;
|
|
170
|
-
})
|
|
171
|
-
.join(' ');
|
|
172
|
-
return attrStr.length > 0 ? ` ${attrStr}` : '';
|
|
173
|
-
};
|
|
174
|
-
const serializedHeadTags = [];
|
|
175
|
-
const serializedBodyTags = [];
|
|
176
|
-
headTags.map((_index, element) => {
|
|
177
|
-
if (element.tagName === 'title' ||
|
|
178
|
-
(element.tagName === 'meta' && element.attribs.name === 'viewport')) {
|
|
179
|
-
return element;
|
|
134
|
+
async createPages() {
|
|
135
|
+
// load public/index.html and add tags to _document
|
|
136
|
+
const htmlContent = await fs_1.default.promises.readFile(path_1.default.join(this.appDir, `${this.isCra ? 'public/' : ''}index.html`), 'utf8');
|
|
137
|
+
const $ = cheerio_1.default.load(htmlContent);
|
|
138
|
+
// note: title tag and meta[viewport] needs to be placed in _app
|
|
139
|
+
// not _document
|
|
140
|
+
const titleTag = $('title')[0];
|
|
141
|
+
const metaViewport = $('meta[name="viewport"]')[0];
|
|
142
|
+
const headTags = $('head').children();
|
|
143
|
+
const bodyTags = $('body').children();
|
|
144
|
+
const pageExt = this.shouldUseTypeScript ? 'tsx' : 'js';
|
|
145
|
+
const appPage = path_1.default.join(this.pagesDir, `_app.${pageExt}`);
|
|
146
|
+
const documentPage = path_1.default.join(this.pagesDir, `_document.${pageExt}`);
|
|
147
|
+
const catchAllPage = path_1.default.join(this.pagesDir, `[[...slug]].${pageExt}`);
|
|
148
|
+
const gatherTextChildren = (children) => {
|
|
149
|
+
return children
|
|
150
|
+
.map((child) => {
|
|
151
|
+
if (child.type === 'text') {
|
|
152
|
+
return child.data;
|
|
180
153
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
154
|
+
return '';
|
|
155
|
+
})
|
|
156
|
+
.join('');
|
|
157
|
+
};
|
|
158
|
+
const serializeAttrs = (attrs) => {
|
|
159
|
+
const attrStr = Object.keys(attrs || {})
|
|
160
|
+
.map((name) => {
|
|
161
|
+
const reactName = html_to_react_attributes_1.default[name] || name;
|
|
162
|
+
const value = attrs[name];
|
|
163
|
+
// allow process.env access to work dynamically still
|
|
164
|
+
if (value.match(/%([a-zA-Z0-9_]{0,})%/)) {
|
|
165
|
+
return `${reactName}={\`${value.replace(/%([a-zA-Z0-9_]{0,})%/g, (subStr) => {
|
|
166
|
+
return `\${process.env.${subStr.slice(1, -1)}}`;
|
|
167
|
+
})}\`}`;
|
|
186
168
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
169
|
+
return `${reactName}="${value}"`;
|
|
170
|
+
})
|
|
171
|
+
.join(' ');
|
|
172
|
+
return attrStr.length > 0 ? ` ${attrStr}` : '';
|
|
173
|
+
};
|
|
174
|
+
const serializedHeadTags = [];
|
|
175
|
+
const serializedBodyTags = [];
|
|
176
|
+
headTags.map((_index, element) => {
|
|
177
|
+
if (element.tagName === 'title' ||
|
|
178
|
+
(element.tagName === 'meta' && element.attribs.name === 'viewport')) {
|
|
190
179
|
return element;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
180
|
+
}
|
|
181
|
+
let hasChildren = element.children.length > 0;
|
|
182
|
+
let serializedAttrs = serializeAttrs(element.attribs);
|
|
183
|
+
if (element.tagName === 'script' || element.tagName === 'style') {
|
|
184
|
+
hasChildren = false;
|
|
185
|
+
serializedAttrs += ` dangerouslySetInnerHTML={{ __html: \`${gatherTextChildren(element.children).replace(/`/g, '\\`')}\` }}`;
|
|
186
|
+
}
|
|
187
|
+
serializedHeadTags.push(hasChildren
|
|
188
|
+
? `<${element.tagName}${serializedAttrs}>${gatherTextChildren(element.children)}</${element.tagName}>`
|
|
189
|
+
: `<${element.tagName}${serializedAttrs} />`);
|
|
190
|
+
return element;
|
|
191
|
+
});
|
|
192
|
+
bodyTags.map((_index, element) => {
|
|
193
|
+
if (element.tagName === 'div' && element.attribs.id === 'root') {
|
|
205
194
|
return element;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
195
|
+
}
|
|
196
|
+
let hasChildren = element.children.length > 0;
|
|
197
|
+
let serializedAttrs = serializeAttrs(element.attribs);
|
|
198
|
+
if (element.tagName === 'script' || element.tagName === 'style') {
|
|
199
|
+
hasChildren = false;
|
|
200
|
+
serializedAttrs += ` dangerouslySetInnerHTML={{ __html: \`${gatherTextChildren(element.children).replace(/`/g, '\\`')}\` }}`;
|
|
201
|
+
}
|
|
202
|
+
serializedHeadTags.push(hasChildren
|
|
203
|
+
? `<${element.tagName}${serializedAttrs}>${gatherTextChildren(element.children)}</${element.tagName}>`
|
|
204
|
+
: `<${element.tagName}${serializedAttrs} />`);
|
|
205
|
+
return element;
|
|
206
|
+
});
|
|
207
|
+
if (!this.isDryRun) {
|
|
208
|
+
await fs_1.default.promises.writeFile(path_1.default.join(this.appDir, appPage), `${global_css_transform_1.globalCssContext.cssImports.size === 0
|
|
209
|
+
? ''
|
|
210
|
+
: [...global_css_transform_1.globalCssContext.cssImports]
|
|
211
|
+
.map((file) => {
|
|
212
|
+
if (!this.isCra) {
|
|
213
|
+
file = file.startsWith('/') ? file.slice(1) : file;
|
|
214
|
+
}
|
|
215
|
+
return `import '${file.startsWith('/')
|
|
216
|
+
? path_1.default.relative(path_1.default.join(this.appDir, this.pagesDir), file)
|
|
217
|
+
: file}'`;
|
|
218
|
+
})
|
|
219
|
+
.join('\n') + '\n'}${titleTag ? `import Head from 'next/head'` : ''}
|
|
220
220
|
|
|
221
221
|
export default function MyApp({ Component, pageProps}) {
|
|
222
222
|
${titleTag || metaViewport
|
|
223
|
-
|
|
223
|
+
? `return (
|
|
224
224
|
<>
|
|
225
225
|
<Head>
|
|
226
226
|
${titleTag
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
? `<title${serializeAttrs(titleTag.attribs)}>${gatherTextChildren(titleTag.children)}</title>`
|
|
228
|
+
: ''}
|
|
229
229
|
${metaViewport ? `<meta${serializeAttrs(metaViewport.attribs)} />` : ''}
|
|
230
230
|
</Head>
|
|
231
231
|
|
|
232
232
|
<Component {...pageProps} />
|
|
233
233
|
</>
|
|
234
234
|
)`
|
|
235
|
-
|
|
235
|
+
: 'return <Component {...pageProps} />'}
|
|
236
236
|
}
|
|
237
237
|
`);
|
|
238
|
-
|
|
238
|
+
await fs_1.default.promises.writeFile(path_1.default.join(this.appDir, documentPage), `import Document, { Html, Head, Main, NextScript } from 'next/document'
|
|
239
239
|
|
|
240
240
|
class MyDocument extends Document {
|
|
241
241
|
render() {
|
|
@@ -257,11 +257,11 @@ class MyDocument extends Document {
|
|
|
257
257
|
|
|
258
258
|
export default MyDocument
|
|
259
259
|
`);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
const relativeIndexPath = path_1.default.relative(path_1.default.join(this.appDir, this.pagesDir), path_1.default.join(this.appDir, 'src', this.isCra ? '' : 'main'));
|
|
261
|
+
// TODO: should we default to ssr: true below and recommend they
|
|
262
|
+
// set it to false if they encounter errors or prefer the more safe
|
|
263
|
+
// option to prevent their first start from having any errors?
|
|
264
|
+
await fs_1.default.promises.writeFile(path_1.default.join(this.appDir, catchAllPage), `// import NextIndexWrapper from '${relativeIndexPath}'
|
|
265
265
|
|
|
266
266
|
// next/dynamic is used to prevent breaking incompatibilities
|
|
267
267
|
// with SSR from window.SOME_VAR usage, if this is not used
|
|
@@ -277,90 +277,95 @@ export default function Page(props) {
|
|
|
277
277
|
return <NextIndexWrapper {...props} />
|
|
278
278
|
}
|
|
279
279
|
`);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
});
|
|
280
|
+
}
|
|
281
|
+
this.logCreate(appPage);
|
|
282
|
+
this.logCreate(documentPage);
|
|
283
|
+
this.logCreate(catchAllPage);
|
|
285
284
|
}
|
|
286
|
-
updatePackageJson() {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
neededDependencies.push(`${dep.name}@${dep.version}`);
|
|
306
|
-
}
|
|
285
|
+
async updatePackageJson() {
|
|
286
|
+
// rename react-scripts -> next and react-scripts test -> jest
|
|
287
|
+
// add needed dependencies for webpack compatibility
|
|
288
|
+
const newDependencies = [
|
|
289
|
+
// TODO: do we want to install jest automatically?
|
|
290
|
+
{
|
|
291
|
+
name: 'next',
|
|
292
|
+
version: 'latest',
|
|
293
|
+
},
|
|
294
|
+
];
|
|
295
|
+
const packageName = this.isCra ? 'react-scripts' : 'vite';
|
|
296
|
+
const packagesToRemove = {
|
|
297
|
+
[packageName]: undefined,
|
|
298
|
+
};
|
|
299
|
+
const neededDependencies = [];
|
|
300
|
+
const { devDependencies, dependencies, scripts } = this.packageJsonData;
|
|
301
|
+
for (const dep of newDependencies) {
|
|
302
|
+
if (!devDependencies?.[dep.name] && !dependencies?.[dep.name]) {
|
|
303
|
+
neededDependencies.push(`${dep.name}@${dep.version}`);
|
|
307
304
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (cur === 'start') {
|
|
325
|
-
prev[cur] = prev[cur].replace('next start', 'next dev');
|
|
326
|
-
prev['start-production'] = 'next start';
|
|
327
|
-
}
|
|
328
|
-
return prev;
|
|
329
|
-
}, {}), dependencies: Object.assign(Object.assign({}, dependencies), packagesToRemove), devDependencies: Object.assign(Object.assign({}, devDependencies), packagesToRemove) }), null, 2));
|
|
330
|
-
yield (0, install_1.install)(this.appDir, neededDependencies, {
|
|
331
|
-
useYarn: this.installClient === 'yarn',
|
|
332
|
-
// do we want to detect offline as well? they might not
|
|
333
|
-
// have next in the local cache already
|
|
334
|
-
isOnline: true,
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
updateGitIgnore() {
|
|
340
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
341
|
-
// add Next.js specific items to .gitignore e.g. '.next'
|
|
342
|
-
const gitignorePath = path_1.default.join(this.appDir, '.gitignore');
|
|
343
|
-
let ignoreContent = yield fs_1.default.promises.readFile(gitignorePath, 'utf8');
|
|
344
|
-
const nextIgnores = (yield fs_1.default.promises.readFile(path_1.default.join(path_1.default.dirname(globalCssTransformPath), 'gitignore'), 'utf8')).split('\n');
|
|
345
|
-
if (!this.isDryRun) {
|
|
346
|
-
for (const ignore of nextIgnores) {
|
|
347
|
-
if (!ignoreContent.includes(ignore)) {
|
|
348
|
-
ignoreContent += `\n${ignore}`;
|
|
305
|
+
}
|
|
306
|
+
this.logInfo(`Installing ${neededDependencies.join(' ')} with ${this.installClient}`);
|
|
307
|
+
if (!this.isDryRun) {
|
|
308
|
+
await fs_1.default.promises.writeFile(this.packageJsonPath, JSON.stringify({
|
|
309
|
+
...this.packageJsonData,
|
|
310
|
+
scripts: Object.keys(scripts).reduce((prev, cur) => {
|
|
311
|
+
const command = scripts[cur];
|
|
312
|
+
prev[cur] = command;
|
|
313
|
+
if (command === packageName) {
|
|
314
|
+
prev[cur] = 'next dev';
|
|
315
|
+
}
|
|
316
|
+
if (command.includes(`${packageName} `)) {
|
|
317
|
+
prev[cur] = command.replace(`${packageName} `, command.includes(`${packageName} test`) ? 'jest ' : 'next ');
|
|
318
|
+
}
|
|
319
|
+
if (cur === 'eject') {
|
|
320
|
+
prev[cur] = undefined;
|
|
349
321
|
}
|
|
322
|
+
// TODO: do we want to map start -> next start instead of CRA's
|
|
323
|
+
// default of mapping starting to dev mode?
|
|
324
|
+
if (cur === 'start') {
|
|
325
|
+
prev[cur] = prev[cur].replace('next start', 'next dev');
|
|
326
|
+
prev['start-production'] = 'next start';
|
|
327
|
+
}
|
|
328
|
+
return prev;
|
|
329
|
+
}, {}),
|
|
330
|
+
dependencies: {
|
|
331
|
+
...dependencies,
|
|
332
|
+
...packagesToRemove,
|
|
333
|
+
},
|
|
334
|
+
devDependencies: {
|
|
335
|
+
...devDependencies,
|
|
336
|
+
...packagesToRemove,
|
|
337
|
+
},
|
|
338
|
+
}, null, 2));
|
|
339
|
+
await (0, install_1.install)(this.appDir, neededDependencies, {
|
|
340
|
+
useYarn: this.installClient === 'yarn',
|
|
341
|
+
// do we want to detect offline as well? they might not
|
|
342
|
+
// have next in the local cache already
|
|
343
|
+
isOnline: true,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async updateGitIgnore() {
|
|
348
|
+
// add Next.js specific items to .gitignore e.g. '.next'
|
|
349
|
+
const gitignorePath = path_1.default.join(this.appDir, '.gitignore');
|
|
350
|
+
let ignoreContent = await fs_1.default.promises.readFile(gitignorePath, 'utf8');
|
|
351
|
+
const nextIgnores = (await fs_1.default.promises.readFile(path_1.default.join(path_1.default.dirname(globalCssTransformPath), 'gitignore'), 'utf8')).split('\n');
|
|
352
|
+
if (!this.isDryRun) {
|
|
353
|
+
for (const ignore of nextIgnores) {
|
|
354
|
+
if (!ignoreContent.includes(ignore)) {
|
|
355
|
+
ignoreContent += `\n${ignore}`;
|
|
350
356
|
}
|
|
351
|
-
yield fs_1.default.promises.writeFile(gitignorePath, ignoreContent);
|
|
352
357
|
}
|
|
353
|
-
|
|
354
|
-
}
|
|
358
|
+
await fs_1.default.promises.writeFile(gitignorePath, ignoreContent);
|
|
359
|
+
}
|
|
360
|
+
this.logModify('.gitignore');
|
|
355
361
|
}
|
|
356
|
-
createNextConfig() {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
? `
|
|
362
|
+
async createNextConfig() {
|
|
363
|
+
if (!this.isDryRun) {
|
|
364
|
+
const { proxy, homepage } = this.packageJsonData;
|
|
365
|
+
const homepagePath = new URL(homepage || '/', 'http://example.com')
|
|
366
|
+
.pathname;
|
|
367
|
+
await fs_1.default.promises.writeFile(path_1.default.join(this.appDir, 'next.config.js'), `module.exports = {${proxy
|
|
368
|
+
? `
|
|
364
369
|
async rewrites() {
|
|
365
370
|
return {
|
|
366
371
|
fallback: [
|
|
@@ -371,7 +376,7 @@ export default function Page(props) {
|
|
|
371
376
|
]
|
|
372
377
|
}
|
|
373
378
|
},`
|
|
374
|
-
|
|
379
|
+
: ''}
|
|
375
380
|
env: {
|
|
376
381
|
PUBLIC_URL: '${homepagePath === '/' ? '' : homepagePath || ''}'
|
|
377
382
|
},
|
|
@@ -385,9 +390,8 @@ export default function Page(props) {
|
|
|
385
390
|
}
|
|
386
391
|
}
|
|
387
392
|
`);
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
});
|
|
393
|
+
}
|
|
394
|
+
this.logCreate('next.config.js');
|
|
391
395
|
}
|
|
392
396
|
getPagesDir() {
|
|
393
397
|
// prefer src/pages as CRA uses the src dir by default
|
|
@@ -429,16 +433,14 @@ export default function Page(props) {
|
|
|
429
433
|
return appDir;
|
|
430
434
|
}
|
|
431
435
|
}
|
|
432
|
-
function transformer(files, flags) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
});
|
|
436
|
+
async function transformer(files, flags) {
|
|
437
|
+
try {
|
|
438
|
+
const craTransform = new CraTransform(files, flags);
|
|
439
|
+
await craTransform.transform();
|
|
440
|
+
console.log(`CRA to Next.js migration complete`, `\n${feedbackMessage}`);
|
|
441
|
+
}
|
|
442
|
+
catch (err) {
|
|
443
|
+
fatalMessage(`Error: failed to complete transform`, err);
|
|
444
|
+
}
|
|
443
445
|
}
|
|
444
446
|
//# sourceMappingURL=cra-to-next.js.map
|
|
@@ -28,7 +28,6 @@ function transformDynamicAPI(source, api, filePath) {
|
|
|
28
28
|
},
|
|
29
29
|
})
|
|
30
30
|
.forEach((path) => {
|
|
31
|
-
var _a, _b, _c;
|
|
32
31
|
const isImportedTopLevel = isImportedInModule(path, asyncRequestApiName);
|
|
33
32
|
if (!isImportedTopLevel) {
|
|
34
33
|
return;
|
|
@@ -46,7 +45,7 @@ function transformDynamicAPI(source, api, filePath) {
|
|
|
46
45
|
const isAsyncFunction = closestFunction
|
|
47
46
|
.nodes()
|
|
48
47
|
.some((node) => node.async);
|
|
49
|
-
const isCallAwaited =
|
|
48
|
+
const isCallAwaited = path.parentPath?.node?.type === 'AwaitExpression';
|
|
50
49
|
const hasChainAccess = path.parentPath.value.type === 'MemberExpression' &&
|
|
51
50
|
path.parentPath.value.object === path.node;
|
|
52
51
|
// For cookies/headers API, only transform server and shared components
|
|
@@ -107,8 +106,8 @@ function transformDynamicAPI(source, api, filePath) {
|
|
|
107
106
|
j(path).closest(j.FunctionExpression) ||
|
|
108
107
|
j(path).closest(j.ArrowFunctionExpression);
|
|
109
108
|
if (parentFunction.size() > 0) {
|
|
110
|
-
const parentFunctionName =
|
|
111
|
-
const isParentFunctionHook = parentFunctionName
|
|
109
|
+
const parentFunctionName = parentFunction.get().node.id?.name;
|
|
110
|
+
const isParentFunctionHook = parentFunctionName?.startsWith('use');
|
|
112
111
|
if (isParentFunctionHook) {
|
|
113
112
|
j(path).replaceWith(j.callExpression(j.identifier('use'), [
|
|
114
113
|
j.callExpression(j.identifier(asyncRequestApiName), []),
|