@next/codemod 15.0.0-rc.0 → 15.0.0-rc.1
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 +55 -3
- package/bin/shared.js +7 -0
- package/bin/transform.js +124 -0
- package/bin/upgrade.js +390 -0
- package/lib/cra-to-next/global-css-transform.js +5 -5
- package/lib/cra-to-next/index-to-component.js +5 -5
- package/lib/handle-package.js +110 -0
- package/lib/install.js +2 -3
- package/lib/parser.js +28 -0
- package/lib/run-jscodeshift.js +18 -2
- package/lib/utils.js +115 -0
- package/package.json +13 -6
- package/transforms/add-missing-react-import.js +4 -3
- package/transforms/app-dir-runtime-config-experimental-edge.js +34 -0
- package/transforms/built-in-next-font.js +4 -3
- package/transforms/cra-to-next.js +238 -236
- package/transforms/lib/async-request-api/index.js +16 -0
- package/transforms/lib/async-request-api/next-async-dynamic-api.js +274 -0
- package/transforms/lib/async-request-api/next-async-dynamic-prop.js +715 -0
- package/transforms/lib/async-request-api/utils.js +374 -0
- package/transforms/metadata-to-viewport-export.js +4 -3
- package/transforms/name-default-component.js +6 -6
- package/transforms/new-link.js +9 -7
- package/transforms/next-async-request-api.js +9 -0
- package/transforms/next-dynamic-access-named-export.js +66 -0
- package/transforms/next-image-experimental.js +12 -15
- package/transforms/next-image-to-legacy-image.js +8 -9
- package/transforms/next-og-import.js +4 -3
- package/transforms/next-request-geo-ip.js +339 -0
- package/transforms/url-to-withrouter.js +1 -1
- package/transforms/withamp-to-config.js +1 -1
- package/bin/cli.js +0 -216
- package/lib/uninstall-package.js +0 -32
|
@@ -1,17 +1,9 @@
|
|
|
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
|
};
|
|
14
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = transformer;
|
|
15
7
|
const fs_1 = __importDefault(require("fs"));
|
|
16
8
|
const path_1 = __importDefault(require("path"));
|
|
17
9
|
const execa_1 = __importDefault(require("execa"));
|
|
@@ -32,6 +24,18 @@ const craTransformsPath = path_1.default.join('../lib/cra-to-next');
|
|
|
32
24
|
const globalCssTransformPath = require.resolve(path_1.default.join(craTransformsPath, 'global-css-transform.js'));
|
|
33
25
|
const indexTransformPath = require.resolve(path_1.default.join(craTransformsPath, 'index-to-component.js'));
|
|
34
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;
|
|
35
39
|
constructor(files, flags) {
|
|
36
40
|
this.isDryRun = flags.dry;
|
|
37
41
|
this.jscodeShiftFlags = flags;
|
|
@@ -42,7 +46,7 @@ class CraTransform {
|
|
|
42
46
|
this.pagesDir = this.getPagesDir();
|
|
43
47
|
this.installClient = this.checkForYarn() ? 'yarn' : 'npm';
|
|
44
48
|
const { dependencies, devDependencies } = this.packageJsonData;
|
|
45
|
-
const hasDep = (dep) =>
|
|
49
|
+
const hasDep = (dep) => dependencies?.[dep] || devDependencies?.[dep];
|
|
46
50
|
this.isCra = hasDep('react-scripts');
|
|
47
51
|
this.isVite = !this.isCra && hasDep('vite');
|
|
48
52
|
if (!this.isCra && !this.isVite) {
|
|
@@ -60,45 +64,43 @@ class CraTransform {
|
|
|
60
64
|
fatalMessage('Error: unable to find `src/index`');
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
|
-
transform() {
|
|
64
|
-
|
|
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
|
-
yield this.createPages();
|
|
101
|
-
});
|
|
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();
|
|
102
104
|
}
|
|
103
105
|
checkForYarn() {
|
|
104
106
|
try {
|
|
@@ -129,112 +131,111 @@ class CraTransform {
|
|
|
129
131
|
console.log(...args);
|
|
130
132
|
}
|
|
131
133
|
}
|
|
132
|
-
createPages() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return child.data;
|
|
152
|
-
}
|
|
153
|
-
return '';
|
|
154
|
-
})
|
|
155
|
-
.join('');
|
|
156
|
-
};
|
|
157
|
-
const serializeAttrs = (attrs) => {
|
|
158
|
-
const attrStr = Object.keys(attrs || {})
|
|
159
|
-
.map((name) => {
|
|
160
|
-
const reactName = html_to_react_attributes_1.default[name] || name;
|
|
161
|
-
const value = attrs[name];
|
|
162
|
-
// allow process.env access to work dynamically still
|
|
163
|
-
if (value.match(/%([a-zA-Z0-9_]{0,})%/)) {
|
|
164
|
-
return `${reactName}={\`${value.replace(/%([a-zA-Z0-9_]{0,})%/g, (subStr) => {
|
|
165
|
-
return `\${process.env.${subStr.slice(1, -1)}}`;
|
|
166
|
-
})}\`}`;
|
|
167
|
-
}
|
|
168
|
-
return `${reactName}="${value}"`;
|
|
169
|
-
})
|
|
170
|
-
.join(' ');
|
|
171
|
-
return attrStr.length > 0 ? ` ${attrStr}` : '';
|
|
172
|
-
};
|
|
173
|
-
const serializedHeadTags = [];
|
|
174
|
-
const serializedBodyTags = [];
|
|
175
|
-
headTags.map((_index, element) => {
|
|
176
|
-
if (element.tagName === 'title' ||
|
|
177
|
-
(element.tagName === 'meta' && element.attribs.name === 'viewport')) {
|
|
178
|
-
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;
|
|
179
153
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
+
})}\`}`;
|
|
185
168
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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')) {
|
|
189
179
|
return element;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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') {
|
|
204
194
|
return element;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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'` : ''}
|
|
219
220
|
|
|
220
221
|
export default function MyApp({ Component, pageProps}) {
|
|
221
222
|
${titleTag || metaViewport
|
|
222
|
-
|
|
223
|
+
? `return (
|
|
223
224
|
<>
|
|
224
225
|
<Head>
|
|
225
226
|
${titleTag
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
? `<title${serializeAttrs(titleTag.attribs)}>${gatherTextChildren(titleTag.children)}</title>`
|
|
228
|
+
: ''}
|
|
228
229
|
${metaViewport ? `<meta${serializeAttrs(metaViewport.attribs)} />` : ''}
|
|
229
230
|
</Head>
|
|
230
231
|
|
|
231
232
|
<Component {...pageProps} />
|
|
232
233
|
</>
|
|
233
234
|
)`
|
|
234
|
-
|
|
235
|
+
: 'return <Component {...pageProps} />'}
|
|
235
236
|
}
|
|
236
237
|
`);
|
|
237
|
-
|
|
238
|
+
await fs_1.default.promises.writeFile(path_1.default.join(this.appDir, documentPage), `import Document, { Html, Head, Main, NextScript } from 'next/document'
|
|
238
239
|
|
|
239
240
|
class MyDocument extends Document {
|
|
240
241
|
render() {
|
|
@@ -256,11 +257,11 @@ class MyDocument extends Document {
|
|
|
256
257
|
|
|
257
258
|
export default MyDocument
|
|
258
259
|
`);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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}'
|
|
264
265
|
|
|
265
266
|
// next/dynamic is used to prevent breaking incompatibilities
|
|
266
267
|
// with SSR from window.SOME_VAR usage, if this is not used
|
|
@@ -276,90 +277,95 @@ export default function Page(props) {
|
|
|
276
277
|
return <NextIndexWrapper {...props} />
|
|
277
278
|
}
|
|
278
279
|
`);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
});
|
|
280
|
+
}
|
|
281
|
+
this.logCreate(appPage);
|
|
282
|
+
this.logCreate(documentPage);
|
|
283
|
+
this.logCreate(catchAllPage);
|
|
284
284
|
}
|
|
285
|
-
updatePackageJson() {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
neededDependencies.push(`${dep.name}@${dep.version}`);
|
|
305
|
-
}
|
|
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}`);
|
|
306
304
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if (cur === 'start') {
|
|
324
|
-
prev[cur] = prev[cur].replace('next start', 'next dev');
|
|
325
|
-
prev['start-production'] = 'next start';
|
|
326
|
-
}
|
|
327
|
-
return prev;
|
|
328
|
-
}, {}), dependencies: Object.assign(Object.assign({}, dependencies), packagesToRemove), devDependencies: Object.assign(Object.assign({}, devDependencies), packagesToRemove) }), null, 2));
|
|
329
|
-
yield (0, install_1.install)(this.appDir, neededDependencies, {
|
|
330
|
-
useYarn: this.installClient === 'yarn',
|
|
331
|
-
// do we want to detect offline as well? they might not
|
|
332
|
-
// have next in the local cache already
|
|
333
|
-
isOnline: true,
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
updateGitIgnore() {
|
|
339
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
340
|
-
// add Next.js specific items to .gitignore e.g. '.next'
|
|
341
|
-
const gitignorePath = path_1.default.join(this.appDir, '.gitignore');
|
|
342
|
-
let ignoreContent = yield fs_1.default.promises.readFile(gitignorePath, 'utf8');
|
|
343
|
-
const nextIgnores = (yield fs_1.default.promises.readFile(path_1.default.join(path_1.default.dirname(globalCssTransformPath), 'gitignore'), 'utf8')).split('\n');
|
|
344
|
-
if (!this.isDryRun) {
|
|
345
|
-
for (const ignore of nextIgnores) {
|
|
346
|
-
if (!ignoreContent.includes(ignore)) {
|
|
347
|
-
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;
|
|
348
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}`;
|
|
349
356
|
}
|
|
350
|
-
yield fs_1.default.promises.writeFile(gitignorePath, ignoreContent);
|
|
351
357
|
}
|
|
352
|
-
|
|
353
|
-
}
|
|
358
|
+
await fs_1.default.promises.writeFile(gitignorePath, ignoreContent);
|
|
359
|
+
}
|
|
360
|
+
this.logModify('.gitignore');
|
|
354
361
|
}
|
|
355
|
-
createNextConfig() {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
? `
|
|
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
|
+
? `
|
|
363
369
|
async rewrites() {
|
|
364
370
|
return {
|
|
365
371
|
fallback: [
|
|
@@ -370,7 +376,7 @@ export default function Page(props) {
|
|
|
370
376
|
]
|
|
371
377
|
}
|
|
372
378
|
},`
|
|
373
|
-
|
|
379
|
+
: ''}
|
|
374
380
|
env: {
|
|
375
381
|
PUBLIC_URL: '${homepagePath === '/' ? '' : homepagePath || ''}'
|
|
376
382
|
},
|
|
@@ -384,9 +390,8 @@ export default function Page(props) {
|
|
|
384
390
|
}
|
|
385
391
|
}
|
|
386
392
|
`);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
});
|
|
393
|
+
}
|
|
394
|
+
this.logCreate('next.config.js');
|
|
390
395
|
}
|
|
391
396
|
getPagesDir() {
|
|
392
397
|
// prefer src/pages as CRA uses the src dir by default
|
|
@@ -428,17 +433,14 @@ export default function Page(props) {
|
|
|
428
433
|
return appDir;
|
|
429
434
|
}
|
|
430
435
|
}
|
|
431
|
-
function transformer(files, flags) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
});
|
|
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
|
+
}
|
|
442
445
|
}
|
|
443
|
-
exports.default = transformer;
|
|
444
446
|
//# sourceMappingURL=cra-to-next.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = transform;
|
|
4
|
+
const next_async_dynamic_prop_1 = require("./next-async-dynamic-prop");
|
|
5
|
+
const next_async_dynamic_api_1 = require("./next-async-dynamic-api");
|
|
6
|
+
function transform(file, api) {
|
|
7
|
+
const transforms = [next_async_dynamic_prop_1.transformDynamicProps, next_async_dynamic_api_1.transformDynamicAPI];
|
|
8
|
+
return transforms.reduce((source, transformFn) => {
|
|
9
|
+
const result = transformFn(source, api, file.path);
|
|
10
|
+
if (!result) {
|
|
11
|
+
return source;
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}, file.source);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=index.js.map
|