@next/codemod 15.0.0-canary.2 → 15.0.0-canary.200
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 +458 -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 +15 -7
- 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 +284 -0
- package/transforms/lib/async-request-api/next-async-dynamic-prop.js +680 -0
- package/transforms/lib/async-request-api/utils.js +473 -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,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = transformer;
|
|
3
4
|
const path_1 = require("path");
|
|
4
5
|
const fs_1 = require("fs");
|
|
6
|
+
const parser_1 = require("../lib/parser");
|
|
5
7
|
function findAndReplaceProps(j, root, tagName) {
|
|
6
8
|
const layoutToStyle = {
|
|
7
9
|
intrinsic: { maxWidth: '100%', height: 'auto' },
|
|
@@ -21,14 +23,12 @@ function findAndReplaceProps(j, root, tagName) {
|
|
|
21
23
|
el.value.openingElement.name.type === 'JSXIdentifier' &&
|
|
22
24
|
el.value.openingElement.name.name === tagName)
|
|
23
25
|
.forEach((el) => {
|
|
24
|
-
var _a;
|
|
25
26
|
let layout = 'intrinsic';
|
|
26
27
|
let objectFit = null;
|
|
27
28
|
let objectPosition = null;
|
|
28
29
|
let styleExpProps = [];
|
|
29
30
|
let sizesAttr = null;
|
|
30
|
-
const attributes =
|
|
31
|
-
var _a, _b;
|
|
31
|
+
const attributes = el.node.openingElement.attributes?.filter((a) => {
|
|
32
32
|
if (a.type !== 'JSXAttribute') {
|
|
33
33
|
return true;
|
|
34
34
|
}
|
|
@@ -51,11 +51,11 @@ function findAndReplaceProps(j, root, tagName) {
|
|
|
51
51
|
return false;
|
|
52
52
|
}
|
|
53
53
|
if (a.name.name === 'style') {
|
|
54
|
-
if (
|
|
54
|
+
if (a.value?.type === 'JSXExpressionContainer' &&
|
|
55
55
|
a.value.expression.type === 'ObjectExpression') {
|
|
56
56
|
styleExpProps = a.value.expression.properties;
|
|
57
57
|
}
|
|
58
|
-
else if (
|
|
58
|
+
else if (a.value?.type === 'JSXExpressionContainer' &&
|
|
59
59
|
a.value.expression.type === 'Identifier') {
|
|
60
60
|
styleExpProps = [
|
|
61
61
|
j.spreadElement(j.identifier(a.value.expression.name)),
|
|
@@ -189,8 +189,8 @@ function nextConfigTransformer(j, root, appDir) {
|
|
|
189
189
|
});
|
|
190
190
|
return root;
|
|
191
191
|
}
|
|
192
|
-
function transformer(file,
|
|
193
|
-
const j =
|
|
192
|
+
function transformer(file, _api, options) {
|
|
193
|
+
const j = (0, parser_1.createParserFromPath)(file.path);
|
|
194
194
|
const root = j(file.source);
|
|
195
195
|
const parsed = (0, path_1.parse)(file.path || '/');
|
|
196
196
|
const isConfig = parsed.base === 'next.config.js' ||
|
|
@@ -208,9 +208,8 @@ function transformer(file, api, options) {
|
|
|
208
208
|
source: { value: 'next/legacy/image' },
|
|
209
209
|
})
|
|
210
210
|
.forEach((imageImport) => {
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
const tagName = (_b = defaultSpecifier === null || defaultSpecifier === void 0 ? void 0 : defaultSpecifier.local) === null || _b === void 0 ? void 0 : _b.name;
|
|
211
|
+
const defaultSpecifier = imageImport.node.specifiers?.find((node) => node.type === 'ImportDefaultSpecifier');
|
|
212
|
+
const tagName = defaultSpecifier?.local?.name;
|
|
214
213
|
imageImport.node.source = j.stringLiteral('next/image');
|
|
215
214
|
if (tagName) {
|
|
216
215
|
findAndReplaceProps(j, root, tagName);
|
|
@@ -220,7 +219,7 @@ function transformer(file, api, options) {
|
|
|
220
219
|
// After: const Image = await import("next/image")
|
|
221
220
|
root.find(j.AwaitExpression).forEach((awaitExp) => {
|
|
222
221
|
const arg = awaitExp.value.argument;
|
|
223
|
-
if (
|
|
222
|
+
if (arg?.type === 'CallExpression' && arg.callee.type === 'Import') {
|
|
224
223
|
if (arg.arguments[0].type === 'StringLiteral' &&
|
|
225
224
|
arg.arguments[0].value === 'next/legacy/image') {
|
|
226
225
|
arg.arguments[0] = j.stringLiteral('next/image');
|
|
@@ -230,14 +229,13 @@ function transformer(file, api, options) {
|
|
|
230
229
|
// Before: const Image = require("next/legacy/image")
|
|
231
230
|
// After: const Image = require("next/image")
|
|
232
231
|
root.find(j.CallExpression).forEach((requireExp) => {
|
|
233
|
-
|
|
234
|
-
if (((_b = (_a = requireExp === null || requireExp === void 0 ? void 0 : requireExp.value) === null || _a === void 0 ? void 0 : _a.callee) === null || _b === void 0 ? void 0 : _b.type) === 'Identifier' &&
|
|
232
|
+
if (requireExp?.value?.callee?.type === 'Identifier' &&
|
|
235
233
|
requireExp.value.callee.name === 'require') {
|
|
236
234
|
let firstArg = requireExp.value.arguments[0];
|
|
237
235
|
if (firstArg &&
|
|
238
236
|
firstArg.type === 'StringLiteral' &&
|
|
239
237
|
firstArg.value === 'next/legacy/image') {
|
|
240
|
-
const tagName =
|
|
238
|
+
const tagName = requireExp?.parentPath?.value?.id?.name;
|
|
241
239
|
if (tagName) {
|
|
242
240
|
requireExp.value.arguments[0] = j.stringLiteral('next/image');
|
|
243
241
|
findAndReplaceProps(j, root, tagName);
|
|
@@ -248,5 +246,4 @@ function transformer(file, api, options) {
|
|
|
248
246
|
// TODO: do the same transforms for dynamic imports
|
|
249
247
|
return root.toSource(options);
|
|
250
248
|
}
|
|
251
|
-
exports.default = transformer;
|
|
252
249
|
//# sourceMappingURL=next-image-experimental.js.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
exports.default = transformer;
|
|
4
|
+
const parser_1 = require("../lib/parser");
|
|
5
|
+
function transformer(file, _api, options) {
|
|
6
|
+
const j = (0, parser_1.createParserFromPath)(file.path);
|
|
5
7
|
const root = j(file.source);
|
|
6
8
|
// Before: import Image from "next/image"
|
|
7
9
|
// After: import Image from "next/legacy/image"
|
|
@@ -16,7 +18,7 @@ function transformer(file, api, options) {
|
|
|
16
18
|
// After: const Image = await import("next/legacy/image")
|
|
17
19
|
root.find(j.AwaitExpression).forEach((awaitExp) => {
|
|
18
20
|
const arg = awaitExp.value.argument;
|
|
19
|
-
if (
|
|
21
|
+
if (arg?.type === 'CallExpression' && arg.callee.type === 'Import') {
|
|
20
22
|
if (arg.arguments[0].type === 'StringLiteral' &&
|
|
21
23
|
arg.arguments[0].value === 'next/image') {
|
|
22
24
|
arg.arguments[0] = j.stringLiteral('next/legacy/image');
|
|
@@ -36,7 +38,7 @@ function transformer(file, api, options) {
|
|
|
36
38
|
// After: const Image = await import("next/image")
|
|
37
39
|
root.find(j.AwaitExpression).forEach((awaitExp) => {
|
|
38
40
|
const arg = awaitExp.value.argument;
|
|
39
|
-
if (
|
|
41
|
+
if (arg?.type === 'CallExpression' && arg.callee.type === 'Import') {
|
|
40
42
|
if (arg.arguments[0].type === 'StringLiteral' &&
|
|
41
43
|
arg.arguments[0].value === 'next/future/image') {
|
|
42
44
|
arg.arguments[0] = j.stringLiteral('next/image');
|
|
@@ -46,8 +48,7 @@ function transformer(file, api, options) {
|
|
|
46
48
|
// Before: const Image = require("next/image")
|
|
47
49
|
// After: const Image = require("next/legacy/image")
|
|
48
50
|
root.find(j.CallExpression).forEach((requireExp) => {
|
|
49
|
-
|
|
50
|
-
if (((_b = (_a = requireExp === null || requireExp === void 0 ? void 0 : requireExp.value) === null || _a === void 0 ? void 0 : _a.callee) === null || _b === void 0 ? void 0 : _b.type) === 'Identifier' &&
|
|
51
|
+
if (requireExp?.value?.callee?.type === 'Identifier' &&
|
|
51
52
|
requireExp.value.callee.name === 'require') {
|
|
52
53
|
let firstArg = requireExp.value.arguments[0];
|
|
53
54
|
if (firstArg &&
|
|
@@ -60,8 +61,7 @@ function transformer(file, api, options) {
|
|
|
60
61
|
// Before: const Image = require("next/future/image")
|
|
61
62
|
// After: const Image = require("next/image")
|
|
62
63
|
root.find(j.CallExpression).forEach((requireExp) => {
|
|
63
|
-
|
|
64
|
-
if (((_b = (_a = requireExp === null || requireExp === void 0 ? void 0 : requireExp.value) === null || _a === void 0 ? void 0 : _a.callee) === null || _b === void 0 ? void 0 : _b.type) === 'Identifier' &&
|
|
64
|
+
if (requireExp?.value?.callee?.type === 'Identifier' &&
|
|
65
65
|
requireExp.value.callee.name === 'require') {
|
|
66
66
|
let firstArg = requireExp.value.arguments[0];
|
|
67
67
|
if (firstArg &&
|
|
@@ -75,5 +75,4 @@ function transformer(file, api, options) {
|
|
|
75
75
|
// https://www.codeshiftcommunity.com/docs/import-manipulation/#replacerename-an-import-declaration
|
|
76
76
|
return root.toSource(options);
|
|
77
77
|
}
|
|
78
|
-
exports.default = transformer;
|
|
79
78
|
//# sourceMappingURL=next-image-to-legacy-image.js.map
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = transformer;
|
|
4
|
+
const parser_1 = require("../lib/parser");
|
|
3
5
|
const importToChange = 'ImageResponse';
|
|
4
|
-
function transformer(file,
|
|
5
|
-
const j =
|
|
6
|
+
function transformer(file, _api) {
|
|
7
|
+
const j = (0, parser_1.createParserFromPath)(file.path);
|
|
6
8
|
// Find import declarations that match the pattern
|
|
7
9
|
file.source = j(file.source)
|
|
8
10
|
.find(j.ImportDeclaration, {
|
|
@@ -31,5 +33,4 @@ function transformer(file, api) {
|
|
|
31
33
|
.toSource();
|
|
32
34
|
return file.source;
|
|
33
35
|
}
|
|
34
|
-
exports.default = transformer;
|
|
35
36
|
//# sourceMappingURL=next-og-import.js.map
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = default_1;
|
|
4
|
+
const parser_1 = require("../lib/parser");
|
|
5
|
+
const GEO = 'geo';
|
|
6
|
+
const IP = 'ip';
|
|
7
|
+
const GEOLOCATION = 'geolocation';
|
|
8
|
+
const IP_ADDRESS = 'ipAddress';
|
|
9
|
+
const GEO_TYPE = 'Geo';
|
|
10
|
+
function default_1(file, _api) {
|
|
11
|
+
const j = (0, parser_1.createParserFromPath)(file.path);
|
|
12
|
+
const root = j(file.source);
|
|
13
|
+
if (!root.length) {
|
|
14
|
+
return file.source;
|
|
15
|
+
}
|
|
16
|
+
const nextReqType = root
|
|
17
|
+
.find(j.FunctionDeclaration)
|
|
18
|
+
.find(j.Identifier, (id) => {
|
|
19
|
+
if (id.typeAnnotation?.type !== 'TSTypeAnnotation') {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const typeAnn = id.typeAnnotation.typeAnnotation;
|
|
23
|
+
return (typeAnn.type === 'TSTypeReference' &&
|
|
24
|
+
typeAnn.typeName.type === 'Identifier' &&
|
|
25
|
+
typeAnn.typeName.name === 'NextRequest');
|
|
26
|
+
});
|
|
27
|
+
const vercelFuncImports = root.find(j.ImportDeclaration, {
|
|
28
|
+
source: {
|
|
29
|
+
value: '@vercel/functions',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const vercelFuncImportSpecifiers = vercelFuncImports
|
|
33
|
+
.find(j.ImportSpecifier)
|
|
34
|
+
.nodes();
|
|
35
|
+
const vercelFuncImportNames = new Set(vercelFuncImportSpecifiers.map((node) => node.imported.name));
|
|
36
|
+
const hasGeolocation = vercelFuncImportNames.has(GEOLOCATION);
|
|
37
|
+
const hasIpAddress = vercelFuncImportNames.has(IP_ADDRESS);
|
|
38
|
+
const hasGeoType = vercelFuncImportNames.has(GEO_TYPE);
|
|
39
|
+
let identifierNames = new Set();
|
|
40
|
+
// if all identifiers are already imported, we don't need to create
|
|
41
|
+
// a unique identifier for them, therefore we don't look for all
|
|
42
|
+
// identifier names in the file
|
|
43
|
+
if (!hasGeolocation || !hasIpAddress || !hasGeoType) {
|
|
44
|
+
const allIdentifiers = root.find(j.Identifier).nodes();
|
|
45
|
+
identifierNames = new Set(allIdentifiers.map((node) => node.name));
|
|
46
|
+
}
|
|
47
|
+
let geoIdentifier = hasGeolocation
|
|
48
|
+
? getExistingIdentifier(vercelFuncImportSpecifiers, GEOLOCATION)
|
|
49
|
+
: getUniqueIdentifier(identifierNames, GEOLOCATION);
|
|
50
|
+
let ipIdentifier = hasIpAddress
|
|
51
|
+
? getExistingIdentifier(vercelFuncImportSpecifiers, IP_ADDRESS)
|
|
52
|
+
: getUniqueIdentifier(identifierNames, IP_ADDRESS);
|
|
53
|
+
let geoTypeIdentifier = hasGeoType
|
|
54
|
+
? getExistingIdentifier(vercelFuncImportSpecifiers, GEO_TYPE)
|
|
55
|
+
: getUniqueIdentifier(identifierNames, GEO_TYPE);
|
|
56
|
+
let { needImportGeolocation, needImportIpAddress } = replaceGeoIpValues(j, nextReqType, geoIdentifier, ipIdentifier);
|
|
57
|
+
let { needImportGeoType, hasChangedIpType } = replaceGeoIpTypes(j, root, geoTypeIdentifier);
|
|
58
|
+
let needChanges = needImportGeolocation ||
|
|
59
|
+
needImportIpAddress ||
|
|
60
|
+
needImportGeoType ||
|
|
61
|
+
hasChangedIpType;
|
|
62
|
+
if (!needChanges) {
|
|
63
|
+
return file.source;
|
|
64
|
+
}
|
|
65
|
+
// Even if there was a change above, if there's an existing import,
|
|
66
|
+
// we don't need to import them again.
|
|
67
|
+
needImportGeolocation = !hasGeolocation && needImportGeolocation;
|
|
68
|
+
needImportIpAddress = !hasIpAddress && needImportIpAddress;
|
|
69
|
+
needImportGeoType = !hasGeoType && needImportGeoType;
|
|
70
|
+
insertImportDeclarations(j, root, vercelFuncImports, needImportGeolocation, needImportIpAddress, needImportGeoType, geoIdentifier, ipIdentifier, geoTypeIdentifier);
|
|
71
|
+
return root.toSource();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns an existing identifier from the Vercel functions import declaration.
|
|
75
|
+
*/
|
|
76
|
+
function getExistingIdentifier(vercelFuncImportSpecifiers, identifier) {
|
|
77
|
+
const existingIdentifier = vercelFuncImportSpecifiers.find((node) => node.imported.name === identifier);
|
|
78
|
+
return (existingIdentifier?.local?.name ||
|
|
79
|
+
existingIdentifier.imported.name ||
|
|
80
|
+
identifier);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Returns a unique identifier by adding a suffix to the original identifier
|
|
84
|
+
* if it already exists in the set.
|
|
85
|
+
*/
|
|
86
|
+
function getUniqueIdentifier(identifierNames, identifier) {
|
|
87
|
+
let suffix = 1;
|
|
88
|
+
let uniqueIdentifier = identifier;
|
|
89
|
+
while (identifierNames.has(uniqueIdentifier)) {
|
|
90
|
+
uniqueIdentifier = `${identifier}${suffix}`;
|
|
91
|
+
suffix++;
|
|
92
|
+
}
|
|
93
|
+
return uniqueIdentifier;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Replaces accessors `geo` and `ip` of NextRequest with corresponding
|
|
97
|
+
* function calls from `@vercel/functions`.
|
|
98
|
+
*
|
|
99
|
+
* Creates new variable declarations for destructuring assignments.
|
|
100
|
+
*/
|
|
101
|
+
function replaceGeoIpValues(j, nextReqType, geoIdentifier, ipIdentifier) {
|
|
102
|
+
let needImportGeolocation = false;
|
|
103
|
+
let needImportIpAddress = false;
|
|
104
|
+
for (const nextReqPath of nextReqType.paths()) {
|
|
105
|
+
const fnPath = nextReqPath.parentPath.parentPath;
|
|
106
|
+
const fn = j(fnPath);
|
|
107
|
+
const blockStatement = fn.find(j.BlockStatement);
|
|
108
|
+
const varDeclarators = fn.find(j.VariableDeclarator);
|
|
109
|
+
// req.geo, req.ip
|
|
110
|
+
const geoAccesses = blockStatement.find(j.MemberExpression, (me) => me.object.type === 'Identifier' &&
|
|
111
|
+
me.object.name === nextReqPath.node.name &&
|
|
112
|
+
me.property.type === 'Identifier' &&
|
|
113
|
+
me.property.name === GEO);
|
|
114
|
+
const ipAccesses = blockStatement.find(j.MemberExpression, (me) => me.object.type === 'Identifier' &&
|
|
115
|
+
me.object.name === nextReqPath.node.name &&
|
|
116
|
+
me.property.type === 'Identifier' &&
|
|
117
|
+
me.property.name === IP);
|
|
118
|
+
// var { geo, ip } = req
|
|
119
|
+
const geoDestructuring = varDeclarators.filter((path) => path.node.id.type === 'ObjectPattern' &&
|
|
120
|
+
path.node.id.properties.some((prop) => prop.type === 'ObjectProperty' &&
|
|
121
|
+
prop.key.type === 'Identifier' &&
|
|
122
|
+
prop.key.name === GEO));
|
|
123
|
+
const ipDestructuring = varDeclarators.filter((path) => path.node.id.type === 'ObjectPattern' &&
|
|
124
|
+
path.node.id.properties.some((prop) => prop.type === 'ObjectProperty' &&
|
|
125
|
+
prop.key.type === 'Identifier' &&
|
|
126
|
+
prop.key.name === IP));
|
|
127
|
+
// geolocation(req), ipAddress(req)
|
|
128
|
+
const geoCall = j.callExpression(j.identifier(geoIdentifier), [
|
|
129
|
+
{
|
|
130
|
+
...nextReqPath.node,
|
|
131
|
+
typeAnnotation: null,
|
|
132
|
+
},
|
|
133
|
+
]);
|
|
134
|
+
const ipCall = j.callExpression(j.identifier(ipIdentifier), [
|
|
135
|
+
{
|
|
136
|
+
...nextReqPath.node,
|
|
137
|
+
typeAnnotation: null,
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
geoAccesses.replaceWith(geoCall);
|
|
141
|
+
ipAccesses.replaceWith(ipCall);
|
|
142
|
+
/**
|
|
143
|
+
* For each destructuring assignment, we create a new variable
|
|
144
|
+
* declaration and insert it after the current block statement.
|
|
145
|
+
*
|
|
146
|
+
* Before:
|
|
147
|
+
*
|
|
148
|
+
* ```
|
|
149
|
+
* const { buildId, geo, ip } = req;
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* After:
|
|
153
|
+
*
|
|
154
|
+
* ```
|
|
155
|
+
* const { buildId } = req;
|
|
156
|
+
* const geo = geolocation(req);
|
|
157
|
+
* const ip = ipAddress(req);
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
geoDestructuring.forEach((path) => {
|
|
161
|
+
if (path.node.id.type === 'ObjectPattern') {
|
|
162
|
+
const properties = path.node.id.properties;
|
|
163
|
+
const geoProperty = properties.find((prop) => prop.type === 'ObjectProperty' &&
|
|
164
|
+
prop.key.type === 'Identifier' &&
|
|
165
|
+
prop.key.name === GEO);
|
|
166
|
+
const otherProperties = properties.filter((prop) => prop !== geoProperty);
|
|
167
|
+
const geoDeclaration = j.variableDeclaration('const', [
|
|
168
|
+
j.variableDeclarator(j.identifier(
|
|
169
|
+
// Use alias from destructuring (if present) to retain references:
|
|
170
|
+
// const { geo: geoAlias } = req; -> const geoAlias = geolocation(req);
|
|
171
|
+
// This prevents errors from undeclared variables.
|
|
172
|
+
geoProperty.type === 'ObjectProperty' &&
|
|
173
|
+
geoProperty.value.type === 'Identifier'
|
|
174
|
+
? geoProperty.value.name
|
|
175
|
+
: GEO), geoCall),
|
|
176
|
+
]);
|
|
177
|
+
path.node.id.properties = otherProperties;
|
|
178
|
+
path.parent.insertAfter(geoDeclaration);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
ipDestructuring.forEach((path) => {
|
|
182
|
+
if (path.node.id.type === 'ObjectPattern') {
|
|
183
|
+
const properties = path.node.id.properties;
|
|
184
|
+
const ipProperty = properties.find((prop) => prop.type === 'ObjectProperty' &&
|
|
185
|
+
prop.key.type === 'Identifier' &&
|
|
186
|
+
prop.key.name === IP);
|
|
187
|
+
const otherProperties = properties.filter((prop) => prop !== ipProperty);
|
|
188
|
+
const ipDeclaration = j.variableDeclaration('const', [
|
|
189
|
+
j.variableDeclarator(j.identifier(
|
|
190
|
+
// Use alias from destructuring (if present) to retain references:
|
|
191
|
+
// const { ip: ipAlias } = req; -> const ipAlias = ipAddress(req);
|
|
192
|
+
// This prevents errors from undeclared variables.
|
|
193
|
+
ipProperty.type === 'ObjectProperty' &&
|
|
194
|
+
ipProperty.value.type === 'Identifier'
|
|
195
|
+
? ipProperty.value.name
|
|
196
|
+
: IP), ipCall),
|
|
197
|
+
]);
|
|
198
|
+
path.node.id.properties = otherProperties;
|
|
199
|
+
path.parent.insertAfter(ipDeclaration);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
needImportGeolocation =
|
|
203
|
+
needImportGeolocation ||
|
|
204
|
+
geoAccesses.length > 0 ||
|
|
205
|
+
geoDestructuring.length > 0;
|
|
206
|
+
needImportIpAddress =
|
|
207
|
+
needImportIpAddress || ipAccesses.length > 0 || ipDestructuring.length > 0;
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
needImportGeolocation,
|
|
211
|
+
needImportIpAddress,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Replaces the types of `NextRequest["geo"]` and `NextRequest["ip"]` with
|
|
216
|
+
* corresponding types from `@vercel/functions`.
|
|
217
|
+
*/
|
|
218
|
+
function replaceGeoIpTypes(j, root, geoTypeIdentifier) {
|
|
219
|
+
let needImportGeoType = false;
|
|
220
|
+
let hasChangedIpType = false;
|
|
221
|
+
// get the type of NextRequest that has accessed for ip and geo
|
|
222
|
+
// NextRequest['geo'], NextRequest['ip']
|
|
223
|
+
const nextReqGeoType = root.find(j.TSIndexedAccessType, (tsIndexedAccessType) => {
|
|
224
|
+
return (tsIndexedAccessType.objectType.type === 'TSTypeReference' &&
|
|
225
|
+
tsIndexedAccessType.objectType.typeName.type === 'Identifier' &&
|
|
226
|
+
tsIndexedAccessType.objectType.typeName.name === 'NextRequest' &&
|
|
227
|
+
tsIndexedAccessType.indexType.type === 'TSLiteralType' &&
|
|
228
|
+
tsIndexedAccessType.indexType.literal.type === 'StringLiteral' &&
|
|
229
|
+
tsIndexedAccessType.indexType.literal.value === GEO);
|
|
230
|
+
});
|
|
231
|
+
const nextReqIpType = root.find(j.TSIndexedAccessType, (tsIndexedAccessType) => {
|
|
232
|
+
return (tsIndexedAccessType.objectType.type === 'TSTypeReference' &&
|
|
233
|
+
tsIndexedAccessType.objectType.typeName.type === 'Identifier' &&
|
|
234
|
+
tsIndexedAccessType.objectType.typeName.name === 'NextRequest' &&
|
|
235
|
+
tsIndexedAccessType.indexType.type === 'TSLiteralType' &&
|
|
236
|
+
tsIndexedAccessType.indexType.literal.type === 'StringLiteral' &&
|
|
237
|
+
tsIndexedAccessType.indexType.literal.value === IP);
|
|
238
|
+
});
|
|
239
|
+
if (nextReqGeoType.length > 0) {
|
|
240
|
+
needImportGeoType = true;
|
|
241
|
+
// replace with type Geo
|
|
242
|
+
nextReqGeoType.replaceWith(j.identifier(geoTypeIdentifier));
|
|
243
|
+
}
|
|
244
|
+
if (nextReqIpType.length > 0) {
|
|
245
|
+
hasChangedIpType = true;
|
|
246
|
+
// replace with type string | undefined
|
|
247
|
+
nextReqIpType.replaceWith(j.tsUnionType([j.tsStringKeyword(), j.tsUndefinedKeyword()]));
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
needImportGeoType,
|
|
251
|
+
hasChangedIpType,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Inserts import declarations from `"@vercel/functions"`.
|
|
256
|
+
*
|
|
257
|
+
* For the `Geo` type that needs to be imported to replace `NextRequest["geo"]`,
|
|
258
|
+
* if it is the only import needed, we import it as a type.
|
|
259
|
+
*
|
|
260
|
+
* ```ts
|
|
261
|
+
* import type { Geo } from "@vercel/functions";
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* Otherwise, we import it as an inline type import.
|
|
265
|
+
*
|
|
266
|
+
* ```ts
|
|
267
|
+
* import { type Geo, ... } from "@vercel/functions";
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
function insertImportDeclarations(j, root, vercelFuncImports, needImportGeolocation, needImportIpAddress, needImportGeoType, geoIdentifier, ipIdentifier, geoTypeIdentifier) {
|
|
271
|
+
// No need inserting import.
|
|
272
|
+
if (!needImportGeolocation && !needImportIpAddress && !needImportGeoType) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// As we run this only when `NextRequest` is imported, there should be
|
|
276
|
+
// a "next/server" import.
|
|
277
|
+
const firstNextServerImport = root
|
|
278
|
+
.find(j.ImportDeclaration, { source: { value: 'next/server' } })
|
|
279
|
+
.at(0);
|
|
280
|
+
const firstVercelFuncImport = vercelFuncImports.at(0);
|
|
281
|
+
const hasVercelFuncImport = firstVercelFuncImport.length > 0;
|
|
282
|
+
// If there's no "@vercel/functions" import, and we only need to import
|
|
283
|
+
// `Geo` type, we create a type import to avoid side effect with
|
|
284
|
+
// `verbatimModuleSyntax`.
|
|
285
|
+
// x-ref: https://typescript-eslint.io/rules/no-import-type-side-effects
|
|
286
|
+
if (!hasVercelFuncImport &&
|
|
287
|
+
!needImportGeolocation &&
|
|
288
|
+
!needImportIpAddress &&
|
|
289
|
+
needImportGeoType) {
|
|
290
|
+
const geoTypeImportDeclaration = j.importDeclaration([
|
|
291
|
+
needImportGeoType
|
|
292
|
+
? j.importSpecifier(j.identifier(GEO_TYPE), j.identifier(geoTypeIdentifier))
|
|
293
|
+
: null,
|
|
294
|
+
].filter(Boolean), j.literal('@vercel/functions'),
|
|
295
|
+
// import type { Geo } ...
|
|
296
|
+
'type');
|
|
297
|
+
firstNextServerImport.insertAfter(geoTypeImportDeclaration);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const importDeclaration = j.importDeclaration([
|
|
301
|
+
// If there was a duplicate identifier, we add an
|
|
302
|
+
// incremental number suffix to it and we use alias:
|
|
303
|
+
// `import { geolocation as geolocation1 } from ...`
|
|
304
|
+
needImportGeolocation
|
|
305
|
+
? j.importSpecifier(j.identifier(GEOLOCATION), j.identifier(geoIdentifier))
|
|
306
|
+
: null,
|
|
307
|
+
needImportIpAddress
|
|
308
|
+
? j.importSpecifier(j.identifier(IP_ADDRESS), j.identifier(ipIdentifier))
|
|
309
|
+
: null,
|
|
310
|
+
needImportGeoType
|
|
311
|
+
? j.importSpecifier(j.identifier(GEO_TYPE), j.identifier(geoTypeIdentifier))
|
|
312
|
+
: null,
|
|
313
|
+
].filter(Boolean), j.literal('@vercel/functions'));
|
|
314
|
+
if (hasVercelFuncImport) {
|
|
315
|
+
firstVercelFuncImport
|
|
316
|
+
.get()
|
|
317
|
+
.node.specifiers.push(...importDeclaration.specifiers);
|
|
318
|
+
if (needImportGeoType) {
|
|
319
|
+
const targetGeo = firstVercelFuncImport
|
|
320
|
+
.get()
|
|
321
|
+
.node.specifiers.find((specifier) => specifier.imported.name === GEO_TYPE);
|
|
322
|
+
if (targetGeo) {
|
|
323
|
+
targetGeo.importKind = 'type';
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
if (needImportGeoType) {
|
|
329
|
+
const targetGeo = importDeclaration.specifiers.find((specifier) => specifier.type === 'ImportSpecifier' &&
|
|
330
|
+
specifier.imported.name === GEO_TYPE);
|
|
331
|
+
if (targetGeo) {
|
|
332
|
+
// @ts-expect-error -- Missing types in jscodeshift.
|
|
333
|
+
targetGeo.importKind = 'type';
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
firstNextServerImport.insertAfter(importDeclaration);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
//# sourceMappingURL=next-request-geo-ip.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// One-time usage file. You can delete me after running the codemod!
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.default = transformer;
|
|
4
5
|
function addWithRouterImport(j, root) {
|
|
5
6
|
// We create an import specifier, this is the value of an import, eg:
|
|
6
7
|
// import {withRouter} from 'next/router
|
|
@@ -317,5 +318,4 @@ function transformer(file, api) {
|
|
|
317
318
|
});
|
|
318
319
|
return root.toSource();
|
|
319
320
|
}
|
|
320
|
-
exports.default = transformer;
|
|
321
321
|
//# sourceMappingURL=url-to-withrouter.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// One-time usage file. You can delete me after running the codemod!
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.default = transformer;
|
|
4
5
|
function injectAmp(j, o, desiredAmpValue) {
|
|
5
6
|
const init = o.node.init;
|
|
6
7
|
switch (init.type) {
|
|
@@ -131,5 +132,4 @@ function transformer(file, api) {
|
|
|
131
132
|
}
|
|
132
133
|
return done();
|
|
133
134
|
}
|
|
134
|
-
exports.default = transformer;
|
|
135
135
|
//# sourceMappingURL=withamp-to-config.js.map
|