dinou 2.0.1 → 2.0.3
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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [2.0.3]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fixed cross-platform path handling using Node.js `path` module for macOS/Linux compatibility in `get-jsx.js`, `get-error-jsx.js`, and `build-static-pages.js`.
|
|
13
|
+
- Added `awaitWriteFinish` to `chokidar` in `server.js` to avoid parsing incomplete manifest files on macOS.
|
|
14
|
+
|
|
15
|
+
## [2.0.2]
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Watch server components in react manifest plugin.
|
|
20
|
+
|
|
21
|
+
## [2.0.1]
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Use createFromFetch from react-server-dom-esm in client-error.jsx.
|
|
26
|
+
|
|
8
27
|
## [2.0.0]
|
|
9
28
|
|
|
10
29
|
### Changed
|
|
@@ -357,7 +357,7 @@ async function buildStaticPages() {
|
|
|
357
357
|
const updatedSlots = {};
|
|
358
358
|
for (const [slotName, slotElement] of Object.entries(slots)) {
|
|
359
359
|
const slotFolder = path.join(
|
|
360
|
-
|
|
360
|
+
path.dirname(layoutPath),
|
|
361
361
|
`@${slotName}`
|
|
362
362
|
);
|
|
363
363
|
const [slotPath] = getFilePathAndDynamicParams(
|
package/dinou/get-error-jsx.js
CHANGED
|
@@ -58,10 +58,10 @@ function getErrorJSX(reqPath, query, error) {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
const noLayoutErrorPath = path.join(
|
|
61
|
-
|
|
61
|
+
path.dirname(pagePath),
|
|
62
62
|
`no_layout_error`
|
|
63
63
|
);
|
|
64
|
-
if (existsSync(
|
|
64
|
+
if (existsSync(noLayoutErrorPath)) {
|
|
65
65
|
return jsx;
|
|
66
66
|
}
|
|
67
67
|
|
package/dinou/get-jsx.js
CHANGED
|
@@ -55,11 +55,13 @@ async function getJSX(reqPath, query) {
|
|
|
55
55
|
params: dParams ?? {},
|
|
56
56
|
query,
|
|
57
57
|
});
|
|
58
|
+
|
|
59
|
+
const notFoundDir = path.dirname(notFoundPath);
|
|
58
60
|
const noLayoutNotFoundPath = path.join(
|
|
59
|
-
|
|
61
|
+
notFoundDir,
|
|
60
62
|
`no_layout_not_found`
|
|
61
63
|
);
|
|
62
|
-
if (existsSync(
|
|
64
|
+
if (existsSync(noLayoutNotFoundPath)) {
|
|
63
65
|
return jsx;
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -71,7 +73,8 @@ async function getJSX(reqPath, query) {
|
|
|
71
73
|
params: dynamicParams,
|
|
72
74
|
query,
|
|
73
75
|
};
|
|
74
|
-
|
|
76
|
+
|
|
77
|
+
const pageFolder = path.dirname(pagePath);
|
|
75
78
|
const [pageFunctionsPath] = getFilePathAndDynamicParams(
|
|
76
79
|
reqSegments,
|
|
77
80
|
query,
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ function reactClientManifestPlugin({
|
|
|
12
12
|
} = {}) {
|
|
13
13
|
const manifest = {};
|
|
14
14
|
const clientModules = new Set();
|
|
15
|
+
const serverModules = new Set();
|
|
15
16
|
|
|
16
17
|
function parseExports(code) {
|
|
17
18
|
const ast = parser.parse(code, {
|
|
@@ -77,6 +78,102 @@ function reactClientManifestPlugin({
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
function getImports(code, baseFilePath, visited = new Set()) {
|
|
82
|
+
if (visited.has(baseFilePath)) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
visited.add(baseFilePath);
|
|
86
|
+
|
|
87
|
+
const ast = parser.parse(code, {
|
|
88
|
+
sourceType: "module",
|
|
89
|
+
plugins: ["jsx", "typescript"],
|
|
90
|
+
});
|
|
91
|
+
const imports = new Set();
|
|
92
|
+
|
|
93
|
+
traverse(ast, {
|
|
94
|
+
ImportDeclaration(nodePath) {
|
|
95
|
+
const source = nodePath.node.source.value;
|
|
96
|
+
if (source.startsWith(".")) {
|
|
97
|
+
let absImportPath = path.resolve(path.dirname(baseFilePath), source);
|
|
98
|
+
const extensions = [".js", ".jsx", ".ts", ".tsx"];
|
|
99
|
+
if (!extensions.some((ext) => absImportPath.endsWith(ext))) {
|
|
100
|
+
for (const ext of extensions) {
|
|
101
|
+
const potentialPath = absImportPath + ext;
|
|
102
|
+
if (existsSync(potentialPath)) {
|
|
103
|
+
absImportPath = potentialPath;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (existsSync(absImportPath)) {
|
|
109
|
+
imports.add(absImportPath);
|
|
110
|
+
try {
|
|
111
|
+
const importCode = readFileSync(absImportPath, "utf8");
|
|
112
|
+
const nestedImports = getImports(
|
|
113
|
+
importCode,
|
|
114
|
+
absImportPath,
|
|
115
|
+
visited
|
|
116
|
+
);
|
|
117
|
+
nestedImports.forEach((nestedPath) => imports.add(nestedPath));
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.warn(
|
|
120
|
+
`[react-client-manifest] Could not read import: ${absImportPath}`,
|
|
121
|
+
err.message
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
console.warn(
|
|
126
|
+
`[react-client-manifest] Import path not found: ${absImportPath}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return Array.from(imports);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isPageOrLayout(absPath) {
|
|
137
|
+
const fileName = path.basename(absPath);
|
|
138
|
+
return fileName.startsWith("page.") || fileName.startsWith("layout.");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isAsyncDefaultExport(code) {
|
|
142
|
+
const ast = parser.parse(code, {
|
|
143
|
+
sourceType: "module",
|
|
144
|
+
plugins: ["jsx", "typescript"],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
let isAsync = false;
|
|
148
|
+
|
|
149
|
+
traverse(ast, {
|
|
150
|
+
ExportDefaultDeclaration(path) {
|
|
151
|
+
let decl = path.node.declaration;
|
|
152
|
+
|
|
153
|
+
if (decl.type === "Identifier") {
|
|
154
|
+
const binding = path.scope.getBinding(decl.name);
|
|
155
|
+
if (binding && binding.path) {
|
|
156
|
+
decl = binding.path.node;
|
|
157
|
+
if (decl.type === "VariableDeclarator") {
|
|
158
|
+
decl = decl.init;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
decl &&
|
|
165
|
+
(decl.type === "FunctionDeclaration" ||
|
|
166
|
+
decl.type === "ArrowFunctionExpression" ||
|
|
167
|
+
decl.type === "FunctionExpression")
|
|
168
|
+
) {
|
|
169
|
+
isAsync = decl.async;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return isAsync;
|
|
175
|
+
}
|
|
176
|
+
|
|
80
177
|
return {
|
|
81
178
|
name: "react-client-manifest",
|
|
82
179
|
async buildStart() {
|
|
@@ -93,12 +190,23 @@ function reactClientManifestPlugin({
|
|
|
93
190
|
if (isClientModule) {
|
|
94
191
|
clientModules.add(normalizedPath);
|
|
95
192
|
updateManifestForModule(absPath, code, true);
|
|
96
|
-
|
|
97
193
|
this.emitFile({
|
|
98
194
|
type: "chunk",
|
|
99
195
|
id: absPath,
|
|
100
196
|
name: path.basename(absPath, path.extname(absPath)),
|
|
101
197
|
});
|
|
198
|
+
} else if (isPageOrLayout(absPath)) {
|
|
199
|
+
if (!isAsyncDefaultExport(code)) {
|
|
200
|
+
this.warn(
|
|
201
|
+
`[react-client-manifest] The file ${normalizedPath} is a page or layout without "use client" directive, but its default export is not an async function. Add "use client" if it's a client component, or make the default export async if it's a server component.`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
serverModules.add(normalizedPath);
|
|
205
|
+
this.addWatchFile(absPath);
|
|
206
|
+
const imports = getImports(code, absPath);
|
|
207
|
+
for (const importPath of imports) {
|
|
208
|
+
this.addWatchFile(importPath);
|
|
209
|
+
}
|
|
102
210
|
}
|
|
103
211
|
}
|
|
104
212
|
},
|
|
@@ -119,6 +227,7 @@ function reactClientManifestPlugin({
|
|
|
119
227
|
}
|
|
120
228
|
}
|
|
121
229
|
clientModules.delete(normalizedId);
|
|
230
|
+
serverModules.delete(normalizedId);
|
|
122
231
|
return;
|
|
123
232
|
}
|
|
124
233
|
const code = readFileSync(id, "utf8");
|
|
@@ -128,19 +237,33 @@ function reactClientManifestPlugin({
|
|
|
128
237
|
|
|
129
238
|
if (isClientModule) {
|
|
130
239
|
clientModules.add(normalizedId);
|
|
240
|
+
serverModules.delete(normalizedId);
|
|
131
241
|
this.addWatchFile(id);
|
|
132
242
|
} else {
|
|
133
243
|
clientModules.delete(normalizedId);
|
|
244
|
+
if (isPageOrLayout(id)) {
|
|
245
|
+
if (!isAsyncDefaultExport(code)) {
|
|
246
|
+
this.warn(
|
|
247
|
+
`[react-client-manifest] The file ${normalizedId} is a page or layout without "use client" directive, but its default export is not an async function. Add "use client" if it's a client component, or make the default export async if it's a server component.`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
serverModules.add(normalizedId);
|
|
251
|
+
this.addWatchFile(id);
|
|
252
|
+
const imports = getImports(code, id);
|
|
253
|
+
for (const importPath of imports) {
|
|
254
|
+
this.addWatchFile(importPath);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
serverModules.delete(normalizedId);
|
|
258
|
+
}
|
|
134
259
|
}
|
|
135
260
|
},
|
|
136
261
|
generateBundle(outputOptions, bundle) {
|
|
137
262
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
138
263
|
if (chunk.type !== "chunk") continue;
|
|
139
|
-
|
|
140
264
|
for (const modulePath of Object.keys(chunk.modules)) {
|
|
141
265
|
const absModulePath = path.resolve(modulePath);
|
|
142
266
|
const baseFileUrl = pathToFileURL(absModulePath).href;
|
|
143
|
-
|
|
144
267
|
for (const manifestKey in manifest) {
|
|
145
268
|
if (manifestKey.startsWith(baseFileUrl)) {
|
|
146
269
|
manifest[manifestKey].id = "/" + fileName;
|
|
@@ -148,7 +271,6 @@ function reactClientManifestPlugin({
|
|
|
148
271
|
}
|
|
149
272
|
}
|
|
150
273
|
}
|
|
151
|
-
|
|
152
274
|
const serialized = JSON.stringify(manifest, null, 2);
|
|
153
275
|
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
154
276
|
writeFileSync(manifestPath, serialized);
|