@next/codemod 15.0.0-canary.163 → 15.0.0-canary.164

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/cli.js CHANGED
@@ -147,6 +147,10 @@ const TRANSFORMER_INQUIRER_CHOICES = [
147
147
  name: 'built-in-next-font: Uninstall `@next/font` and transform imports to `next/font`',
148
148
  value: 'built-in-next-font',
149
149
  },
150
+ {
151
+ name: 'next-async-request-api: Transforms usage of Next.js async Request APIs',
152
+ value: 'next-async-request-api',
153
+ },
150
154
  {
151
155
  name: 'next-request-geo-ip: Install `@vercel/functions` to replace `geo` and `ip` properties on `NextRequest`',
152
156
  value: 'next-request-geo-ip',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next/codemod",
3
- "version": "15.0.0-canary.163",
3
+ "version": "15.0.0-canary.164",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "files": [
21
21
  "transforms/*.js",
22
+ "transforms/lib/**/*.js",
22
23
  "bin/*.js",
23
24
  "lib/**/*.js",
24
25
  "lib/cra-to-next/gitignore"
@@ -0,0 +1,21 @@
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 filePath = file.path;
8
+ // if it's node_modules or types file, skip
9
+ if (/node_modules/.test(filePath) || /\.d\.(m|c)?ts$/.test(filePath)) {
10
+ return file.source;
11
+ }
12
+ const transforms = [next_async_dynamic_prop_1.transformDynamicProps, next_async_dynamic_api_1.transformDynamicAPI];
13
+ return transforms.reduce((source, transformFn) => {
14
+ const result = transformFn(source, api, file.path);
15
+ if (!result) {
16
+ return source;
17
+ }
18
+ return result;
19
+ }, file.source);
20
+ }
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformDynamicAPI = transformDynamicAPI;
4
+ const utils_1 = require("./utils");
5
+ function wrapParathnessIfNeeded(hasChainAccess, j, expression) {
6
+ return hasChainAccess ? j.parenthesizedExpression(expression) : expression;
7
+ }
8
+ function transformDynamicAPI(source, api, filePath) {
9
+ const j = api.jscodeshift.withParser('tsx');
10
+ const root = j(source);
11
+ let modified = false;
12
+ // Check if 'use' from 'react' needs to be imported
13
+ let needsReactUseImport = false;
14
+ const insertedTypes = new Set();
15
+ function isImportedInModule(path, functionName) {
16
+ const closestDef = j(path)
17
+ .closestScope()
18
+ .findVariableDeclarators(functionName);
19
+ return closestDef.size() === 0;
20
+ }
21
+ function processAsyncApiCalls(asyncRequestApiName, originRequestApiName) {
22
+ // Process each call to cookies() or headers()
23
+ root
24
+ .find(j.CallExpression, {
25
+ callee: {
26
+ type: 'Identifier',
27
+ name: asyncRequestApiName,
28
+ },
29
+ })
30
+ .forEach((path) => {
31
+ var _a, _b, _c;
32
+ const isImportedTopLevel = isImportedInModule(path, asyncRequestApiName);
33
+ if (!isImportedTopLevel) {
34
+ return;
35
+ }
36
+ const closetScope = j(path).closestScope();
37
+ // First search the closed function of the current path
38
+ let closestFunction;
39
+ closestFunction = j(path).closest(j.FunctionDeclaration);
40
+ if (closestFunction.size() === 0) {
41
+ closestFunction = j(path).closest(j.FunctionExpression);
42
+ }
43
+ if (closestFunction.size() === 0) {
44
+ closestFunction = j(path).closest(j.ArrowFunctionExpression);
45
+ }
46
+ const isAsyncFunction = closestFunction
47
+ .nodes()
48
+ .some((node) => node.async);
49
+ const isCallAwaited = ((_b = (_a = path.parentPath) === null || _a === void 0 ? void 0 : _a.node) === null || _b === void 0 ? void 0 : _b.type) === 'AwaitExpression';
50
+ const hasChainAccess = path.parentPath.value.type === 'MemberExpression' &&
51
+ path.parentPath.value.object === path.node;
52
+ // For cookies/headers API, only transform server and shared components
53
+ if (isAsyncFunction) {
54
+ if (!isCallAwaited) {
55
+ // Add 'await' in front of cookies() call
56
+ const expr = j.awaitExpression(
57
+ // add parentheses to wrap the function call
58
+ j.callExpression(j.identifier(asyncRequestApiName), []));
59
+ j(path).replaceWith(wrapParathnessIfNeeded(hasChainAccess, j, expr));
60
+ modified = true;
61
+ }
62
+ }
63
+ else {
64
+ // Determine if the function is an export
65
+ const closetScopePath = closetScope.get();
66
+ const isFromExport = (0, utils_1.isMatchedFunctionExported)(closetScopePath, j);
67
+ const closestFunctionNode = closetScope.size()
68
+ ? closetScopePath.node
69
+ : null;
70
+ // If it's exporting a function directly, exportFunctionNode is same as exportNode
71
+ // e.g. export default function MyComponent() {}
72
+ // If it's exporting a variable declaration, exportFunctionNode is the function declaration
73
+ // e.g. export const MyComponent = function() {}
74
+ let exportFunctionNode;
75
+ if (isFromExport) {
76
+ if (closestFunctionNode &&
77
+ (0, utils_1.isFunctionType)(closestFunctionNode.type)) {
78
+ exportFunctionNode = closestFunctionNode;
79
+ }
80
+ }
81
+ else {
82
+ // Is normal async function
83
+ exportFunctionNode = closestFunctionNode;
84
+ }
85
+ let canConvertToAsync = false;
86
+ // check if current path is under the default export function
87
+ if (isFromExport) {
88
+ // if default export function is not async, convert it to async, and await the api call
89
+ if (!isCallAwaited) {
90
+ // If the scoped function is async function
91
+ if ((0, utils_1.isFunctionType)(exportFunctionNode.type) &&
92
+ exportFunctionNode.async === false) {
93
+ canConvertToAsync = true;
94
+ exportFunctionNode.async = true;
95
+ }
96
+ if (canConvertToAsync) {
97
+ const expr = j.awaitExpression(j.callExpression(j.identifier(asyncRequestApiName), []));
98
+ j(path).replaceWith(wrapParathnessIfNeeded(hasChainAccess, j, expr));
99
+ (0, utils_1.turnFunctionReturnTypeToAsync)(closetScopePath.node, j);
100
+ modified = true;
101
+ }
102
+ }
103
+ }
104
+ else {
105
+ // if parent is function and it's a hook, which starts with 'use', wrap the api call with 'use()'
106
+ const parentFunction = j(path).closest(j.FunctionDeclaration) ||
107
+ j(path).closest(j.FunctionExpression) ||
108
+ j(path).closest(j.ArrowFunctionExpression);
109
+ if (parentFunction.size() > 0) {
110
+ const parentFunctionName = (_c = parentFunction.get().node.id) === null || _c === void 0 ? void 0 : _c.name;
111
+ const isParentFunctionHook = parentFunctionName === null || parentFunctionName === void 0 ? void 0 : parentFunctionName.startsWith('use');
112
+ if (isParentFunctionHook) {
113
+ j(path).replaceWith(j.callExpression(j.identifier('use'), [
114
+ j.callExpression(j.identifier(asyncRequestApiName), []),
115
+ ]));
116
+ needsReactUseImport = true;
117
+ }
118
+ else {
119
+ castTypesOrAddComment(j, path, originRequestApiName, root, filePath, insertedTypes);
120
+ }
121
+ }
122
+ else {
123
+ castTypesOrAddComment(j, path, originRequestApiName, root, filePath, insertedTypes);
124
+ }
125
+ modified = true;
126
+ }
127
+ }
128
+ });
129
+ }
130
+ const isClientComponent = (0, utils_1.determineClientDirective)(root, j, source);
131
+ const importedNextAsyncRequestApisMapping = findImportMappingFromNextHeaders(root, j);
132
+ // Only transform the valid calls in server or shared components
133
+ if (!isClientComponent) {
134
+ for (const originName in importedNextAsyncRequestApisMapping) {
135
+ const aliasName = importedNextAsyncRequestApisMapping[originName];
136
+ processAsyncApiCalls(aliasName, originName);
137
+ }
138
+ // Add import { use } from 'react' if needed and not already imported
139
+ if (needsReactUseImport) {
140
+ (0, utils_1.insertReactUseImport)(root, j);
141
+ }
142
+ }
143
+ return modified ? root.toSource() : null;
144
+ }
145
+ // cast to unknown first, then the specific type
146
+ const API_CAST_TYPE_MAP = {
147
+ cookies: 'UnsafeUnwrappedCookies',
148
+ headers: 'UnsafeUnwrappedHeaders',
149
+ draftMode: 'UnsafeUnwrappedDraftMode',
150
+ };
151
+ function castTypesOrAddComment(j, path, originRequestApiName, root, filePath, insertedTypes) {
152
+ const isTsFile = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
153
+ if (isTsFile) {
154
+ /* Do type cast for headers, cookies, draftMode
155
+ import {
156
+ type UnsafeUnwrappedHeaders,
157
+ type UnsafeUnwrappedCookies,
158
+ type UnsafeUnwrappedDraftMode
159
+ } from 'next/headers'
160
+
161
+ cookies() as unknown as UnsafeUnwrappedCookies
162
+ headers() as unknown as UnsafeUnwrappedHeaders
163
+ draftMode() as unknown as UnsafeUnwrappedDraftMode
164
+
165
+ e.g. `<path>` is cookies(), convert it to `(<path> as unknown as UnsafeUnwrappedCookies)`
166
+ */
167
+ const targetType = API_CAST_TYPE_MAP[originRequestApiName];
168
+ const newCastExpression = j.tsAsExpression(j.tsAsExpression(path.node, j.tsUnknownKeyword()), j.tsTypeReference(j.identifier(targetType)));
169
+ // Replace the original expression with the new cast expression,
170
+ // also wrap () around the new cast expression.
171
+ j(path).replaceWith(j.parenthesizedExpression(newCastExpression));
172
+ // If cast types are not imported, add them to the import list
173
+ const importDeclaration = root.find(j.ImportDeclaration, {
174
+ source: { value: 'next/headers' },
175
+ });
176
+ if (importDeclaration.size() > 0) {
177
+ const hasImportedType = importDeclaration
178
+ .find(j.TSTypeAliasDeclaration, {
179
+ id: { name: targetType },
180
+ })
181
+ .size() > 0 ||
182
+ importDeclaration
183
+ .find(j.ImportSpecifier, {
184
+ imported: { name: targetType },
185
+ })
186
+ .size() > 0;
187
+ if (!hasImportedType && !insertedTypes.has(targetType)) {
188
+ importDeclaration
189
+ .get()
190
+ .node.specifiers.push(j.importSpecifier(j.identifier(`type ${targetType}`)));
191
+ insertedTypes.add(targetType);
192
+ }
193
+ }
194
+ }
195
+ else {
196
+ // Otherwise for JS file, leave a message to the user to manually handle the transformation
197
+ path.node.comments = [
198
+ j.commentBlock(' TODO: please manually await this call, codemod cannot transform due to undetermined async scope '),
199
+ ...(path.node.comments || []),
200
+ ];
201
+ }
202
+ }
203
+ function findImportMappingFromNextHeaders(root, j) {
204
+ const mappings = {};
205
+ // Find the import declaration from 'next/headers'
206
+ root
207
+ .find(j.ImportDeclaration, { source: { value: 'next/headers' } })
208
+ .forEach((importPath) => {
209
+ const importDeclaration = importPath.node;
210
+ // Iterate over the specifiers and build the mappings
211
+ importDeclaration.specifiers.forEach((specifier) => {
212
+ if (j.ImportSpecifier.check(specifier)) {
213
+ const importedName = specifier.imported.name; // Original name (e.g., cookies)
214
+ const localName = specifier.local.name; // Local name (e.g., myCookies or same as importedName)
215
+ // Add to the mappings
216
+ mappings[importedName] = localName;
217
+ }
218
+ });
219
+ });
220
+ return mappings;
221
+ }
222
+ //# sourceMappingURL=next-async-dynamic-api.js.map
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformDynamicProps = transformDynamicProps;
4
+ const utils_1 = require("./utils");
5
+ const PAGE_PROPS = 'props';
6
+ function isAsyncFunctionDeclaration(path) {
7
+ const decl = path.value.declaration;
8
+ const isAsyncFunction = (decl.type === 'FunctionDeclaration' ||
9
+ decl.type === 'FunctionExpression' ||
10
+ decl.type === 'ArrowFunctionExpression') &&
11
+ decl.async;
12
+ return isAsyncFunction;
13
+ }
14
+ function transformDynamicProps(source, api, _filePath) {
15
+ let modified = false;
16
+ const j = api.jscodeshift.withParser('tsx');
17
+ const root = j(source);
18
+ // Check if 'use' from 'react' needs to be imported
19
+ let needsReactUseImport = false;
20
+ // Based on the prop names
21
+ // e.g. destruct `params` { slug } = params
22
+ // e.g. destruct `searchParams `{ search } = searchParams
23
+ let insertedDestructPropNames = new Set();
24
+ // Rename props to `prop` argument for the function
25
+ let insertedRenamedPropFunctionNames = new Set();
26
+ function processAsyncPropOfEntryFile(isClientComponent) {
27
+ // find `params` and `searchParams` in file, and transform the access to them
28
+ function renameAsyncPropIfExisted(path) {
29
+ var _a;
30
+ const decl = path.value.declaration;
31
+ if (decl.type !== 'FunctionDeclaration' &&
32
+ decl.type !== 'FunctionExpression' &&
33
+ decl.type !== 'ArrowFunctionExpression') {
34
+ return;
35
+ }
36
+ const params = decl.params;
37
+ const propertiesMap = new Map();
38
+ // If there's no first param, return
39
+ if (params.length !== 1) {
40
+ return;
41
+ }
42
+ const propsIdentifier = (0, utils_1.generateUniqueIdentifier)(PAGE_PROPS, path, j);
43
+ const currentParam = params[0];
44
+ // Argument destructuring case
45
+ if (currentParam.type === 'ObjectPattern') {
46
+ // Validate if the properties are not `params` and `searchParams`,
47
+ // if they are, quit the transformation
48
+ for (const prop of currentParam.properties) {
49
+ if ('key' in prop && prop.key.type === 'Identifier') {
50
+ const propName = prop.key.name;
51
+ if (!utils_1.TARGET_PROP_NAMES.has(propName)) {
52
+ return;
53
+ }
54
+ }
55
+ }
56
+ currentParam.properties.forEach((prop) => {
57
+ if (
58
+ // Could be `Property` or `ObjectProperty`
59
+ 'key' in prop &&
60
+ prop.key.type === 'Identifier') {
61
+ const value = 'value' in prop ? prop.value : null;
62
+ propertiesMap.set(prop.key.name, value);
63
+ }
64
+ });
65
+ const paramTypeAnnotation = currentParam.typeAnnotation;
66
+ if (paramTypeAnnotation && paramTypeAnnotation.typeAnnotation) {
67
+ const typeAnnotation = paramTypeAnnotation.typeAnnotation;
68
+ if (typeAnnotation.type === 'TSTypeLiteral') {
69
+ const typeLiteral = typeAnnotation;
70
+ // Find the type property for `params`
71
+ typeLiteral.members.forEach((member) => {
72
+ if (member.type === 'TSPropertySignature' &&
73
+ member.key.type === 'Identifier' &&
74
+ propertiesMap.has(member.key.name)) {
75
+ // if it's already a Promise, don't wrap it again, return
76
+ if (member.typeAnnotation &&
77
+ member.typeAnnotation.typeAnnotation &&
78
+ member.typeAnnotation.typeAnnotation.type ===
79
+ 'TSTypeReference' &&
80
+ member.typeAnnotation.typeAnnotation.typeName.type ===
81
+ 'Identifier' &&
82
+ member.typeAnnotation.typeAnnotation.typeName.name ===
83
+ 'Promise') {
84
+ return;
85
+ }
86
+ // Wrap the `params` type in Promise<>
87
+ if (member.typeAnnotation &&
88
+ member.typeAnnotation.typeAnnotation &&
89
+ j.TSType.check(member.typeAnnotation.typeAnnotation)) {
90
+ member.typeAnnotation.typeAnnotation = j.tsTypeReference(j.identifier('Promise'), j.tsTypeParameterInstantiation([
91
+ // @ts-ignore
92
+ member.typeAnnotation.typeAnnotation,
93
+ ]));
94
+ }
95
+ }
96
+ });
97
+ }
98
+ else if (typeAnnotation.type === 'TSTypeReference') {
99
+ // If typeAnnotation is a type or interface, change the properties to Promise<type of property>
100
+ // e.g. interface PageProps { params: { slug: string } } => interface PageProps { params: Promise<{ slug: string }> }
101
+ const typeReference = typeAnnotation;
102
+ if (typeReference.typeName.type === 'Identifier') {
103
+ // Find the actual type of the type reference
104
+ const foundTypes = findAllTypes(root, j, typeReference.typeName.name);
105
+ // Deal with interfaces
106
+ if (foundTypes.interfaces.length > 0) {
107
+ const interfaceDeclaration = foundTypes.interfaces[0];
108
+ if (interfaceDeclaration.type === 'TSInterfaceDeclaration' &&
109
+ ((_a = interfaceDeclaration.body) === null || _a === void 0 ? void 0 : _a.type) === 'TSInterfaceBody') {
110
+ const typeBody = interfaceDeclaration.body.body;
111
+ // if it's already a Promise, don't wrap it again, return
112
+ // traverse the typeReference's properties, if any is in propNames, wrap it in Promise<> if needed
113
+ typeBody.forEach((member) => {
114
+ var _a, _b, _c;
115
+ if (member.type === 'TSPropertySignature' &&
116
+ member.key.type === 'Identifier' &&
117
+ utils_1.TARGET_PROP_NAMES.has(member.key.name)) {
118
+ // if it's already a Promise, don't wrap it again, return
119
+ if (member.typeAnnotation &&
120
+ member.typeAnnotation.typeAnnotation &&
121
+ ((_c = (_b = (_a = member.typeAnnotation) === null || _a === void 0 ? void 0 : _a.typeAnnotation) === null || _b === void 0 ? void 0 : _b.typeName) === null || _c === void 0 ? void 0 : _c.name) === 'Promise') {
122
+ return;
123
+ }
124
+ // Wrap the prop type in Promise<>
125
+ if (member.typeAnnotation &&
126
+ member.typeAnnotation.typeAnnotation &&
127
+ // check if member name is in propNames
128
+ utils_1.TARGET_PROP_NAMES.has(member.key.name)) {
129
+ member.typeAnnotation.typeAnnotation =
130
+ j.tsTypeReference(j.identifier('Promise'), j.tsTypeParameterInstantiation([
131
+ member.typeAnnotation.typeAnnotation,
132
+ ]));
133
+ }
134
+ }
135
+ });
136
+ }
137
+ }
138
+ }
139
+ }
140
+ propsIdentifier.typeAnnotation = paramTypeAnnotation;
141
+ }
142
+ // Override the first param to `props`
143
+ params[0] = propsIdentifier;
144
+ modified = true;
145
+ }
146
+ if (modified) {
147
+ resolveAsyncProp(path, propertiesMap, propsIdentifier.name);
148
+ }
149
+ }
150
+ function getBodyOfFunctionDeclaration(path) {
151
+ const decl = path.value.declaration;
152
+ let functionBody;
153
+ if (decl.type === 'FunctionDeclaration' ||
154
+ decl.type === 'FunctionExpression' ||
155
+ decl.type === 'ArrowFunctionExpression') {
156
+ if (decl.body && decl.body.type === 'BlockStatement') {
157
+ functionBody = decl.body.body;
158
+ }
159
+ }
160
+ return functionBody;
161
+ }
162
+ // Helper function to insert `const params = await asyncParams;` at the beginning of the function body
163
+ function resolveAsyncProp(path, propertiesMap, propsIdentifierName) {
164
+ var _a;
165
+ const isDefaultExport = path.value.type === 'ExportDefaultDeclaration';
166
+ // If it's sync default export, and it's also server component, make the function async
167
+ if (isDefaultExport &&
168
+ !isClientComponent &&
169
+ !isAsyncFunctionDeclaration(path)) {
170
+ if ('async' in path.value.declaration) {
171
+ path.value.declaration.async = true;
172
+ (0, utils_1.turnFunctionReturnTypeToAsync)(path.value.declaration, j);
173
+ }
174
+ }
175
+ const isAsyncFunc = isAsyncFunctionDeclaration(path);
176
+ // @ts-ignore quick way to check if it's a function and it has a name
177
+ const functionName = ((_a = path.value.declaration.id) === null || _a === void 0 ? void 0 : _a.name) || 'default';
178
+ const functionBody = getBodyOfFunctionDeclaration(path);
179
+ for (const [propName, paramsProperty] of propertiesMap) {
180
+ const propNameIdentifier = j.identifier(propName);
181
+ const propsIdentifier = j.identifier(propsIdentifierName);
182
+ const accessedPropId = j.memberExpression(propsIdentifier, propNameIdentifier);
183
+ // Check param property value, if it's destructed, we need to destruct it as well
184
+ // e.g.
185
+ // input: Page({ params: { slug } })
186
+ // output: const { slug } = await props.params; rather than const props = await props.params;
187
+ const uid = functionName + ':' + propName;
188
+ if ((paramsProperty === null || paramsProperty === void 0 ? void 0 : paramsProperty.type) === 'ObjectPattern') {
189
+ const objectPattern = paramsProperty;
190
+ const objectPatternProperties = objectPattern.properties;
191
+ // destruct the object pattern, e.g. { slug } => const { slug } = params;
192
+ const destructedObjectPattern = j.variableDeclaration('const', [
193
+ j.variableDeclarator(j.objectPattern(objectPatternProperties.map((prop) => {
194
+ if (prop.type === 'Property' &&
195
+ prop.key.type === 'Identifier') {
196
+ return j.objectProperty(j.identifier(prop.key.name), j.identifier(prop.key.name));
197
+ }
198
+ return prop;
199
+ })), propNameIdentifier),
200
+ ]);
201
+ if (!insertedDestructPropNames.has(uid) && functionBody) {
202
+ functionBody.unshift(destructedObjectPattern);
203
+ insertedDestructPropNames.add(uid);
204
+ }
205
+ }
206
+ if (isAsyncFunc) {
207
+ // If it's async function, add await to the async props.<propName>
208
+ const paramAssignment = j.variableDeclaration('const', [
209
+ j.variableDeclarator(j.identifier(propName), j.awaitExpression(accessedPropId)),
210
+ ]);
211
+ if (!insertedRenamedPropFunctionNames.has(uid) && functionBody) {
212
+ functionBody.unshift(paramAssignment);
213
+ insertedRenamedPropFunctionNames.add(uid);
214
+ }
215
+ }
216
+ else {
217
+ const isFromExport = path.value.type === 'ExportNamedDeclaration';
218
+ if (isFromExport) {
219
+ // If it's export function, populate the function to async
220
+ if ((0, utils_1.isFunctionType)(path.value.declaration.type) &&
221
+ // Make TS happy
222
+ 'async' in path.value.declaration) {
223
+ path.value.declaration.async = true;
224
+ (0, utils_1.turnFunctionReturnTypeToAsync)(path.value.declaration, j);
225
+ // Insert `const <propName> = await props.<propName>;` at the beginning of the function body
226
+ const paramAssignment = j.variableDeclaration('const', [
227
+ j.variableDeclarator(j.identifier(propName), j.awaitExpression(accessedPropId)),
228
+ ]);
229
+ if (!insertedRenamedPropFunctionNames.has(uid) && functionBody) {
230
+ functionBody.unshift(paramAssignment);
231
+ insertedRenamedPropFunctionNames.add(uid);
232
+ }
233
+ }
234
+ }
235
+ else {
236
+ const paramAssignment = j.variableDeclaration('const', [
237
+ j.variableDeclarator(j.identifier(propName), j.callExpression(j.identifier('use'), [accessedPropId])),
238
+ ]);
239
+ if (!insertedRenamedPropFunctionNames.has(uid) && functionBody) {
240
+ needsReactUseImport = true;
241
+ functionBody.unshift(paramAssignment);
242
+ insertedRenamedPropFunctionNames.add(uid);
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ // Process Function Declarations
249
+ // Matching: default export function XXX(...) { ... }
250
+ const defaultExportFunctionDeclarations = root.find(j.ExportDefaultDeclaration, {
251
+ declaration: {
252
+ type: (type) => type === 'FunctionDeclaration' ||
253
+ type === 'FunctionExpression' ||
254
+ type === 'ArrowFunctionExpression',
255
+ },
256
+ });
257
+ defaultExportFunctionDeclarations.forEach((path) => {
258
+ renameAsyncPropIfExisted(path);
259
+ });
260
+ // Matching Next.js functional named export of route entry:
261
+ // - export function <named>(...) { ... }
262
+ // - export const <named> = ...
263
+ const targetNamedExportDeclarations = root.find(j.ExportNamedDeclaration,
264
+ // Filter the name is in TARGET_NAMED_EXPORTS
265
+ {
266
+ declaration: {
267
+ id: {
268
+ name: (idName) => {
269
+ return utils_1.TARGET_NAMED_EXPORTS.has(idName);
270
+ },
271
+ },
272
+ },
273
+ });
274
+ targetNamedExportDeclarations.forEach((path) => {
275
+ renameAsyncPropIfExisted(path);
276
+ });
277
+ // TDOO: handle targetNamedDeclarators
278
+ // const targetNamedDeclarators = root.find(
279
+ // j.VariableDeclarator,
280
+ // (node) =>
281
+ // node.id.type === 'Identifier' &&
282
+ // TARGET_NAMED_EXPORTS.has(node.id.name)
283
+ // )
284
+ }
285
+ const isClientComponent = (0, utils_1.determineClientDirective)(root, j, source);
286
+ // Apply to `params` and `searchParams`
287
+ processAsyncPropOfEntryFile(isClientComponent);
288
+ // Add import { use } from 'react' if needed and not already imported
289
+ if (needsReactUseImport) {
290
+ (0, utils_1.insertReactUseImport)(root, j);
291
+ }
292
+ return modified ? root.toSource() : null;
293
+ }
294
+ function findAllTypes(root, j, typeName) {
295
+ const types = {
296
+ interfaces: [],
297
+ typeAliases: [],
298
+ imports: [],
299
+ references: [],
300
+ };
301
+ // Step 1: Find all interface declarations with the specified name
302
+ root
303
+ .find(j.TSInterfaceDeclaration, {
304
+ id: {
305
+ type: 'Identifier',
306
+ name: typeName,
307
+ },
308
+ })
309
+ .forEach((path) => {
310
+ types.interfaces.push(path.node);
311
+ });
312
+ // Step 2: Find all type alias declarations with the specified name
313
+ root
314
+ .find(j.TSTypeAliasDeclaration, {
315
+ id: {
316
+ type: 'Identifier',
317
+ name: typeName,
318
+ },
319
+ })
320
+ .forEach((path) => {
321
+ types.typeAliases.push(path.node);
322
+ });
323
+ // Step 3: Find all imported types with the specified name
324
+ root
325
+ .find(j.ImportSpecifier, {
326
+ imported: {
327
+ type: 'Identifier',
328
+ name: typeName,
329
+ },
330
+ })
331
+ .forEach((path) => {
332
+ types.imports.push(path.node);
333
+ });
334
+ // Step 4: Find all references to the specified type
335
+ root
336
+ .find(j.TSTypeReference, {
337
+ typeName: {
338
+ type: 'Identifier',
339
+ name: typeName,
340
+ },
341
+ })
342
+ .forEach((path) => {
343
+ types.references.push(path.node);
344
+ });
345
+ return types;
346
+ }
347
+ //# sourceMappingURL=next-async-dynamic-prop.js.map
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TARGET_PROP_NAMES = exports.TARGET_NAMED_EXPORTS = void 0;
4
+ exports.isFunctionType = isFunctionType;
5
+ exports.isMatchedFunctionExported = isMatchedFunctionExported;
6
+ exports.determineClientDirective = determineClientDirective;
7
+ exports.isPromiseType = isPromiseType;
8
+ exports.turnFunctionReturnTypeToAsync = turnFunctionReturnTypeToAsync;
9
+ exports.insertReactUseImport = insertReactUseImport;
10
+ exports.generateUniqueIdentifier = generateUniqueIdentifier;
11
+ exports.TARGET_NAMED_EXPORTS = new Set([
12
+ // For custom route
13
+ 'GET',
14
+ 'HEAD',
15
+ 'POST',
16
+ 'PUT',
17
+ 'DELETE',
18
+ 'PATCH',
19
+ 'OPTIONS',
20
+ // For page and layout
21
+ 'generateMetadata',
22
+ ]);
23
+ exports.TARGET_PROP_NAMES = new Set(['params', 'searchParams']);
24
+ function isFunctionType(type) {
25
+ return (type === 'FunctionDeclaration' ||
26
+ type === 'FunctionExpression' ||
27
+ type === 'ArrowFunctionExpression');
28
+ }
29
+ function isMatchedFunctionExported(path, j) {
30
+ const matchedFunctionNameFilter = (idName) => exports.TARGET_NAMED_EXPORTS.has(idName);
31
+ const directNamedExport = j(path).closest(j.ExportNamedDeclaration, {
32
+ declaration: {
33
+ type: 'FunctionDeclaration',
34
+ id: {
35
+ name: matchedFunctionNameFilter,
36
+ },
37
+ },
38
+ });
39
+ if (directNamedExport.size() > 0) {
40
+ return true;
41
+ }
42
+ // Check for default export (`export default function() {}`)
43
+ const isDefaultExport = j(path).closest(j.ExportDefaultDeclaration).size() > 0;
44
+ if (isDefaultExport) {
45
+ return true;
46
+ }
47
+ // Look for named export elsewhere in the file (`export { <named> }`)
48
+ const root = j(path).closestScope().closest(j.Program);
49
+ const isNamedExport = root
50
+ .find(j.ExportNamedDeclaration, {
51
+ specifiers: [
52
+ {
53
+ type: 'ExportSpecifier',
54
+ exported: {
55
+ name: matchedFunctionNameFilter,
56
+ },
57
+ },
58
+ ],
59
+ })
60
+ .size() > 0;
61
+ // Look for variable export but still function, e.g. `export const <named> = function() {}`,
62
+ // also check if variable is a function or arrow function
63
+ const isVariableExport = root
64
+ .find(j.ExportNamedDeclaration, {
65
+ declaration: {
66
+ declarations: [
67
+ {
68
+ type: 'VariableDeclarator',
69
+ id: {
70
+ type: 'Identifier',
71
+ name: matchedFunctionNameFilter,
72
+ },
73
+ init: {
74
+ type: isFunctionType,
75
+ },
76
+ },
77
+ ],
78
+ },
79
+ })
80
+ .size() > 0;
81
+ if (isVariableExport)
82
+ return true;
83
+ return isNamedExport;
84
+ }
85
+ function determineClientDirective(root, j, source) {
86
+ const hasStringDirective = root
87
+ .find(j.Literal)
88
+ .filter((path) => {
89
+ const expr = path.node;
90
+ return (expr.value === 'use client' && path.parentPath.node.type === 'Program');
91
+ })
92
+ .size() > 0;
93
+ // 'use client';
94
+ const hasStringDirectiveWithSemicolon = root
95
+ .find(j.StringLiteral)
96
+ .filter((path) => {
97
+ const expr = path.node;
98
+ return (expr.type === 'StringLiteral' &&
99
+ expr.value === 'use client' &&
100
+ path.parentPath.node.type === 'Program');
101
+ })
102
+ .size() > 0;
103
+ if (hasStringDirective || hasStringDirectiveWithSemicolon)
104
+ return true;
105
+ // Since the client detection is not reliable with AST in jscodeshift,
106
+ // determine if 'use client' or "use client" is leading in the source code.
107
+ const trimmedSource = source.trim();
108
+ const containsClientDirective = /^'use client'/.test(trimmedSource) || /^"use client"/g.test(trimmedSource);
109
+ return containsClientDirective;
110
+ }
111
+ function isPromiseType(typeAnnotation) {
112
+ return (typeAnnotation.type === 'TSTypeReference' &&
113
+ typeAnnotation.typeName.name === 'Promise');
114
+ }
115
+ function turnFunctionReturnTypeToAsync(node, j) {
116
+ if (j.FunctionDeclaration.check(node) ||
117
+ j.FunctionExpression.check(node) ||
118
+ j.ArrowFunctionExpression.check(node)) {
119
+ if (node.returnType) {
120
+ const returnTypeAnnotation = node.returnType.typeAnnotation;
121
+ const isReturnTypePromise = isPromiseType(returnTypeAnnotation);
122
+ // Turn <return type> to Promise<return type>
123
+ // e.g. () => { slug: string } to () => Promise<{ slug: string }>
124
+ // e.g. Anything to Promise<Anything>
125
+ if (!isReturnTypePromise) {
126
+ if (node.returnType &&
127
+ j.TSTypeAnnotation.check(node.returnType) &&
128
+ (j.TSTypeReference.check(node.returnType.typeAnnotation) ||
129
+ j.TSUnionType.check(node.returnType.typeAnnotation) ||
130
+ j.TSTypePredicate.check(node.returnType.typeAnnotation))) {
131
+ // Change the return type to Promise<void>
132
+ node.returnType.typeAnnotation = j.tsTypeReference(j.identifier('Promise'),
133
+ // @ts-ignore ignore the super strict type checking on the type annotation
134
+ j.tsTypeParameterInstantiation([returnTypeAnnotation]));
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+ function insertReactUseImport(root, j) {
141
+ const hasReactUseImport = root
142
+ .find(j.ImportSpecifier, {
143
+ imported: {
144
+ type: 'Identifier',
145
+ name: 'use',
146
+ },
147
+ })
148
+ .size() > 0;
149
+ if (!hasReactUseImport) {
150
+ const reactImportDeclaration = root.find(j.ImportDeclaration, {
151
+ source: {
152
+ type: 'Literal',
153
+ value: 'react',
154
+ },
155
+ });
156
+ if (reactImportDeclaration.size() > 0) {
157
+ // Add 'use' to existing 'react' import declaration
158
+ reactImportDeclaration
159
+ .get()
160
+ .node.specifiers.push(j.importSpecifier(j.identifier('use')));
161
+ }
162
+ else {
163
+ // Final all type imports to 'react'
164
+ const reactImport = root.find(j.ImportDeclaration, {
165
+ source: {
166
+ type: 'Literal',
167
+ value: 'react',
168
+ },
169
+ });
170
+ if (reactImport.size() > 0) {
171
+ reactImport
172
+ .get()
173
+ .node.specifiers.push(j.importSpecifier(j.identifier('use')));
174
+ }
175
+ else {
176
+ // Add new import declaration for 'react' and 'use'
177
+ root
178
+ .get()
179
+ .node.program.body.unshift(j.importDeclaration([j.importSpecifier(j.identifier('use'))], j.literal('react')));
180
+ }
181
+ }
182
+ }
183
+ }
184
+ function findSubScopeArgumentIdentifier(path, j, argName) {
185
+ const defCount = j(path).find(j.Identifier, { name: argName }).size();
186
+ return defCount > 0;
187
+ }
188
+ function generateUniqueIdentifier(defaultIdName, path, j) {
189
+ let idName = defaultIdName;
190
+ let idNameSuffix = 0;
191
+ while (findSubScopeArgumentIdentifier(path, j, idName)) {
192
+ idName = defaultIdName + idNameSuffix;
193
+ idNameSuffix++;
194
+ }
195
+ const propsIdentifier = j.identifier(idName);
196
+ return propsIdentifier;
197
+ }
198
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = void 0;
7
+ var index_1 = require("./lib/async-request-api/index");
8
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(index_1).default; } });
9
+ //# sourceMappingURL=next-async-request-api.js.map