@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.
Files changed (33) hide show
  1. package/bin/next-codemod.js +55 -3
  2. package/bin/shared.js +7 -0
  3. package/bin/transform.js +124 -0
  4. package/bin/upgrade.js +458 -0
  5. package/lib/cra-to-next/global-css-transform.js +5 -5
  6. package/lib/cra-to-next/index-to-component.js +5 -5
  7. package/lib/handle-package.js +110 -0
  8. package/lib/install.js +2 -3
  9. package/lib/parser.js +28 -0
  10. package/lib/run-jscodeshift.js +18 -2
  11. package/lib/utils.js +115 -0
  12. package/package.json +15 -7
  13. package/transforms/add-missing-react-import.js +4 -3
  14. package/transforms/app-dir-runtime-config-experimental-edge.js +34 -0
  15. package/transforms/built-in-next-font.js +4 -3
  16. package/transforms/cra-to-next.js +238 -236
  17. package/transforms/lib/async-request-api/index.js +16 -0
  18. package/transforms/lib/async-request-api/next-async-dynamic-api.js +284 -0
  19. package/transforms/lib/async-request-api/next-async-dynamic-prop.js +680 -0
  20. package/transforms/lib/async-request-api/utils.js +473 -0
  21. package/transforms/metadata-to-viewport-export.js +4 -3
  22. package/transforms/name-default-component.js +6 -6
  23. package/transforms/new-link.js +9 -7
  24. package/transforms/next-async-request-api.js +9 -0
  25. package/transforms/next-dynamic-access-named-export.js +66 -0
  26. package/transforms/next-image-experimental.js +12 -15
  27. package/transforms/next-image-to-legacy-image.js +8 -9
  28. package/transforms/next-og-import.js +4 -3
  29. package/transforms/next-request-geo-ip.js +339 -0
  30. package/transforms/url-to-withrouter.js +1 -1
  31. package/transforms/withamp-to-config.js +1 -1
  32. package/bin/cli.js +0 -216
  33. 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 = (_a = el.node.openingElement.attributes) === null || _a === void 0 ? void 0 : _a.filter((a) => {
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 (((_a = a.value) === null || _a === void 0 ? void 0 : _a.type) === 'JSXExpressionContainer' &&
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 (((_b = a.value) === null || _b === void 0 ? void 0 : _b.type) === 'JSXExpressionContainer' &&
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, api, options) {
193
- const j = api.jscodeshift.withParser('tsx');
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
- var _a, _b;
212
- const defaultSpecifier = (_a = imageImport.node.specifiers) === null || _a === void 0 ? void 0 : _a.find((node) => node.type === 'ImportDefaultSpecifier');
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 ((arg === null || arg === void 0 ? void 0 : arg.type) === 'CallExpression' && arg.callee.type === 'Import') {
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
- var _a, _b, _c, _d, _e;
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 = (_e = (_d = (_c = requireExp === null || requireExp === void 0 ? void 0 : requireExp.parentPath) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.id) === null || _e === void 0 ? void 0 : _e.name;
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
- function transformer(file, api, options) {
4
- const j = api.jscodeshift.withParser('tsx');
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 ((arg === null || arg === void 0 ? void 0 : arg.type) === 'CallExpression' && arg.callee.type === 'Import') {
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 ((arg === null || arg === void 0 ? void 0 : arg.type) === 'CallExpression' && arg.callee.type === 'Import') {
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
- var _a, _b;
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
- var _a, _b;
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, api) {
5
- const j = api.jscodeshift;
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