@next/codemod 16.3.0-preview.6 → 16.3.0-preview.7
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/lib/utils.js
CHANGED
|
@@ -137,5 +137,10 @@ exports.TRANSFORMER_INQUIRER_CHOICES = [
|
|
|
137
137
|
value: 'cache-components-instant-false',
|
|
138
138
|
version: '16.3.0',
|
|
139
139
|
},
|
|
140
|
+
{
|
|
141
|
+
title: "Remove `export const prefetch = 'partial'` Route Segment Config from App Router pages and layouts after enabling `partialPrefetching` globally",
|
|
142
|
+
value: 'remove-partial-prefetch',
|
|
143
|
+
version: '16.3.0',
|
|
144
|
+
},
|
|
140
145
|
];
|
|
141
146
|
//# sourceMappingURL=utils.js.map
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ const utils_1 = require("./lib/utils");
|
|
|
18
18
|
// Properties that need to be moved to experimental.turbopack*
|
|
19
19
|
const RENAMED_EXPERIMENTAL_PROPERTIES = {
|
|
20
20
|
minify: 'turbopackMinify',
|
|
21
|
-
treeShaking: '
|
|
21
|
+
treeShaking: 'turbopackModuleFragments',
|
|
22
22
|
sourceMaps: 'turbopackSourceMaps',
|
|
23
23
|
};
|
|
24
24
|
// `memoryLimit` is no longer supported and is removed entirely. We drop it under
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = transformer;
|
|
4
|
+
const parser_1 = require("../lib/parser");
|
|
5
|
+
// Route Segment Config name and the only value this codemod strips.
|
|
6
|
+
const CONFIG_NAME = 'prefetch';
|
|
7
|
+
const TARGET_VALUE = 'partial';
|
|
8
|
+
// Unwrap `'partial' as const` / `'partial' satisfies T` so the value guard
|
|
9
|
+
// still matches when the export is annotated.
|
|
10
|
+
function unwrap(j, node) {
|
|
11
|
+
if ((j.TSAsExpression && j.TSAsExpression.check(node)) ||
|
|
12
|
+
(j.TSSatisfiesExpression && j.TSSatisfiesExpression.check(node))) {
|
|
13
|
+
return node.expression;
|
|
14
|
+
}
|
|
15
|
+
return node;
|
|
16
|
+
}
|
|
17
|
+
// Only `prefetch = 'partial'` matches. A different value such as
|
|
18
|
+
// `prefetch = 'allow-runtime'` is a legitimate config and is left untouched.
|
|
19
|
+
function isTargetPrefetch(j, decl) {
|
|
20
|
+
if (!j.VariableDeclarator.check(decl) || !j.Identifier.check(decl.id)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (decl.id.name !== CONFIG_NAME || !decl.init) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const init = unwrap(j, decl.init);
|
|
27
|
+
return ((j.StringLiteral.check(init) && init.value === TARGET_VALUE) ||
|
|
28
|
+
(j.Literal.check(init) && init.value === TARGET_VALUE));
|
|
29
|
+
}
|
|
30
|
+
// Drop only the `prefetch = 'partial'` declarator, leaving any sibling
|
|
31
|
+
// declarators (e.g. `export const runtime = 'edge', prefetch = 'partial'`)
|
|
32
|
+
// intact. Returns the number of declarators left so the caller can remove the
|
|
33
|
+
// whole statement when it becomes empty.
|
|
34
|
+
function stripTargetDeclarators(j, declaration) {
|
|
35
|
+
declaration.declarations = declaration.declarations.filter((decl) => !isTargetPrefetch(j, decl));
|
|
36
|
+
return declaration.declarations.length;
|
|
37
|
+
}
|
|
38
|
+
// Removing a statement also removes the comments attached above it — which
|
|
39
|
+
// may be a user's note or a deliberate `// TODO(...)` marker. Reattach the
|
|
40
|
+
// leading comments to the next statement (or the previous one when the
|
|
41
|
+
// removed statement is last) so they survive the removal.
|
|
42
|
+
function preserveLeadingComments(path) {
|
|
43
|
+
const comments = path.node.comments?.filter((comment) => comment.leading);
|
|
44
|
+
if (!comments?.length) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const body = path.parent.node.body;
|
|
48
|
+
if (!Array.isArray(body)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const index = body.indexOf(path.node);
|
|
52
|
+
const next = body[index + 1];
|
|
53
|
+
const prev = body[index - 1];
|
|
54
|
+
if (next) {
|
|
55
|
+
next.comments = [...comments, ...(next.comments ?? [])];
|
|
56
|
+
}
|
|
57
|
+
else if (prev) {
|
|
58
|
+
for (const comment of comments) {
|
|
59
|
+
comment.leading = false;
|
|
60
|
+
comment.trailing = true;
|
|
61
|
+
}
|
|
62
|
+
prev.comments = [...(prev.comments ?? []), ...comments];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function transformer(file, _api) {
|
|
66
|
+
// Run on App Router page/layout files, except for test environment. The
|
|
67
|
+
// `prefetch` Route Segment Config only applies to pages and layouts, so
|
|
68
|
+
// route handlers are intentionally excluded.
|
|
69
|
+
// `(^|[/\\])app` matches both an absolute path and a relative `app/...` path
|
|
70
|
+
// (what `npx @next/codemod ... ./app` passes), so top-level app files aren't
|
|
71
|
+
// silently skipped.
|
|
72
|
+
if (process.env.NODE_ENV !== 'test' &&
|
|
73
|
+
!/(^|[/\\])app[/\\](?:.*[/\\])?(page|layout)(\.[^/\\]*)?$/.test(file.path)) {
|
|
74
|
+
return file.source;
|
|
75
|
+
}
|
|
76
|
+
const j = (0, parser_1.createParserFromPath)(file.path);
|
|
77
|
+
const root = j(file.source);
|
|
78
|
+
let hasChanges = false;
|
|
79
|
+
// `export const prefetch = 'partial'` (possibly alongside other configs in
|
|
80
|
+
// the same statement).
|
|
81
|
+
root
|
|
82
|
+
.find(j.ExportNamedDeclaration, {
|
|
83
|
+
declaration: { type: 'VariableDeclaration' },
|
|
84
|
+
})
|
|
85
|
+
.filter((path) => {
|
|
86
|
+
const declaration = path.node.declaration;
|
|
87
|
+
return (j.VariableDeclaration.check(declaration) &&
|
|
88
|
+
declaration.declarations.some((decl) => isTargetPrefetch(j, decl)));
|
|
89
|
+
})
|
|
90
|
+
.forEach((path) => {
|
|
91
|
+
const declaration = path.node.declaration;
|
|
92
|
+
const remaining = stripTargetDeclarators(j, declaration);
|
|
93
|
+
// Remove the whole export only when nothing else was declared with it.
|
|
94
|
+
if (remaining === 0) {
|
|
95
|
+
preserveLeadingComments(path);
|
|
96
|
+
j(path).remove();
|
|
97
|
+
}
|
|
98
|
+
hasChanges = true;
|
|
99
|
+
});
|
|
100
|
+
// Bare `const prefetch = 'partial'` declarations are only Route Segment
|
|
101
|
+
// Configs when the file also exports them as `prefetch` via a local
|
|
102
|
+
// `export { prefetch }`. Re-exports (`export { prefetch } from '...'`) bind
|
|
103
|
+
// another module's value, and aliased exports export a different name
|
|
104
|
+
// (`export { prefetch as other }`) or a different binding
|
|
105
|
+
// (`export { other as prefetch }`), so neither counts. When an aliased
|
|
106
|
+
// export shares the `prefetch` binding, removing the declaration would
|
|
107
|
+
// break it, so the whole file is left untouched.
|
|
108
|
+
let hasPlainPrefetchExportSpecifier = false;
|
|
109
|
+
let hasAliasedPrefetchBinding = false;
|
|
110
|
+
root
|
|
111
|
+
.find(j.ExportNamedDeclaration)
|
|
112
|
+
.filter((path) => !path.node.source)
|
|
113
|
+
.forEach((path) => {
|
|
114
|
+
for (const spec of path.node.specifiers ?? []) {
|
|
115
|
+
if (!j.ExportSpecifier.check(spec) ||
|
|
116
|
+
!j.Identifier.check(spec.local) ||
|
|
117
|
+
spec.local.name !== CONFIG_NAME) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (j.Identifier.check(spec.exported) &&
|
|
121
|
+
spec.exported.name === CONFIG_NAME) {
|
|
122
|
+
hasPlainPrefetchExportSpecifier = true;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
hasAliasedPrefetchBinding = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
const hasLocalPrefetchExportSpecifier = hasPlainPrefetchExportSpecifier && !hasAliasedPrefetchBinding;
|
|
130
|
+
// Track that we removed a bare declaration so we only drop the matching
|
|
131
|
+
// export specifier below.
|
|
132
|
+
let removedBareDeclaration = false;
|
|
133
|
+
if (hasLocalPrefetchExportSpecifier) {
|
|
134
|
+
root
|
|
135
|
+
.find(j.VariableDeclaration)
|
|
136
|
+
.filter((path) => {
|
|
137
|
+
// `export const prefetch` is handled above; skip it here.
|
|
138
|
+
if (j.ExportNamedDeclaration.check(path.parent.node)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
// Only top-level declarations can be the exported config. A local
|
|
142
|
+
// `const prefetch` inside a function or block is unrelated code.
|
|
143
|
+
if (!j.Program.check(path.parent.node)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return path.node.declarations.some((decl) => isTargetPrefetch(j, decl));
|
|
147
|
+
})
|
|
148
|
+
.forEach((path) => {
|
|
149
|
+
const remaining = stripTargetDeclarators(j, path.node);
|
|
150
|
+
if (remaining === 0) {
|
|
151
|
+
preserveLeadingComments(path);
|
|
152
|
+
j(path).remove();
|
|
153
|
+
}
|
|
154
|
+
removedBareDeclaration = true;
|
|
155
|
+
hasChanges = true;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// Handle `export { prefetch }` and `export { prefetch, other }`, but only
|
|
159
|
+
// when the paired declaration was the `'partial'` one we removed above.
|
|
160
|
+
if (removedBareDeclaration) {
|
|
161
|
+
root
|
|
162
|
+
.find(j.ExportNamedDeclaration)
|
|
163
|
+
// Skip re-exports (`export { prefetch } from '...'`): their specifiers
|
|
164
|
+
// reference another module's binding, not the declaration we removed.
|
|
165
|
+
.filter((path) => !path.node.source && Boolean(path.node.specifiers?.length))
|
|
166
|
+
.forEach((path) => {
|
|
167
|
+
const specifiers = path.node.specifiers;
|
|
168
|
+
if (!specifiers)
|
|
169
|
+
return;
|
|
170
|
+
const filteredSpecifiers = specifiers.filter((spec) => {
|
|
171
|
+
// Remove only the plain `export { prefetch }` specifier. Aliased
|
|
172
|
+
// specifiers export a different name or bind a different value, so
|
|
173
|
+
// they aren't the Route Segment Config.
|
|
174
|
+
if (j.ExportSpecifier.check(spec) &&
|
|
175
|
+
j.Identifier.check(spec.local) &&
|
|
176
|
+
j.Identifier.check(spec.exported)) {
|
|
177
|
+
return !(spec.local.name === CONFIG_NAME &&
|
|
178
|
+
spec.exported.name === CONFIG_NAME);
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
});
|
|
182
|
+
if (filteredSpecifiers.length !== specifiers.length) {
|
|
183
|
+
hasChanges = true;
|
|
184
|
+
if (filteredSpecifiers.length === 0) {
|
|
185
|
+
preserveLeadingComments(path);
|
|
186
|
+
j(path).remove();
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
path.node.specifiers = filteredSpecifiers;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (hasChanges) {
|
|
195
|
+
return root.toSource();
|
|
196
|
+
}
|
|
197
|
+
return file.source;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=remove-partial-prefetch.js.map
|