@nitronjs/framework 0.2.26 → 0.3.0
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/README.md +260 -170
- package/lib/Auth/Auth.js +2 -2
- package/lib/Build/CssBuilder.js +5 -7
- package/lib/Build/EffectivePropUsage.js +174 -0
- package/lib/Build/FactoryTransform.js +1 -21
- package/lib/Build/FileAnalyzer.js +2 -33
- package/lib/Build/Manager.js +390 -58
- package/lib/Build/PropUsageAnalyzer.js +1189 -0
- package/lib/Build/jsxRuntime.js +25 -155
- package/lib/Build/plugins.js +212 -146
- package/lib/Build/propUtils.js +70 -0
- package/lib/Console/Commands/DevCommand.js +30 -10
- package/lib/Console/Commands/MakeCommand.js +8 -1
- package/lib/Console/Output.js +0 -2
- package/lib/Console/Stubs/rsc-consumer.tsx +74 -0
- package/lib/Console/Stubs/vendor-dev.tsx +30 -41
- package/lib/Console/Stubs/vendor.tsx +25 -1
- package/lib/Core/Config.js +0 -6
- package/lib/Core/Paths.js +0 -19
- package/lib/Database/Migration/Checksum.js +0 -3
- package/lib/Database/Migration/MigrationRepository.js +0 -8
- package/lib/Database/Migration/MigrationRunner.js +1 -2
- package/lib/Database/Model.js +19 -11
- package/lib/Database/QueryBuilder.js +25 -4
- package/lib/Database/Schema/Blueprint.js +10 -0
- package/lib/Database/Schema/Manager.js +2 -0
- package/lib/Date/DateTime.js +1 -1
- package/lib/Dev/DevContext.js +44 -0
- package/lib/Dev/DevErrorPage.js +990 -0
- package/lib/Dev/DevIndicator.js +836 -0
- package/lib/HMR/Server.js +16 -37
- package/lib/Http/Server.js +177 -24
- package/lib/Logging/Log.js +34 -2
- package/lib/Mail/Mail.js +41 -10
- package/lib/Route/Router.js +43 -19
- package/lib/Runtime/Entry.js +10 -6
- package/lib/Session/Manager.js +144 -1
- package/lib/Session/Redis.js +117 -0
- package/lib/Session/Session.js +0 -4
- package/lib/Support/Str.js +6 -4
- package/lib/Translation/Lang.js +376 -32
- package/lib/Translation/pluralize.js +81 -0
- package/lib/Validation/MagicBytes.js +120 -0
- package/lib/Validation/Validator.js +46 -29
- package/lib/View/Client/hmr-client.js +100 -90
- package/lib/View/Client/spa.js +121 -50
- package/lib/View/ClientManifest.js +60 -0
- package/lib/View/FlightRenderer.js +100 -0
- package/lib/View/Layout.js +0 -3
- package/lib/View/PropFilter.js +81 -0
- package/lib/View/View.js +230 -495
- package/lib/index.d.ts +22 -1
- package/package.json +3 -2
- package/skeleton/config/app.js +1 -0
- package/skeleton/config/server.js +13 -0
- package/skeleton/config/session.js +4 -0
- package/lib/Build/HydrationBuilder.js +0 -190
- package/lib/Console/Stubs/page-hydration-dev.tsx +0 -72
- package/lib/Console/Stubs/page-hydration.tsx +0 -53
package/lib/Auth/Auth.js
CHANGED
|
@@ -134,7 +134,7 @@ class Auth {
|
|
|
134
134
|
return res.redirect("/");
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
if (path.startsWith("/")
|
|
137
|
+
if (path.startsWith("/")) {
|
|
138
138
|
return res.redirect(path);
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -157,7 +157,7 @@ class Auth {
|
|
|
157
157
|
return res.redirect("/");
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
if (path.startsWith("/")
|
|
160
|
+
if (path.startsWith("/")) {
|
|
161
161
|
return res.redirect(path);
|
|
162
162
|
}
|
|
163
163
|
|
package/lib/Build/CssBuilder.js
CHANGED
|
@@ -10,14 +10,12 @@ import tailwindPostcss from "@tailwindcss/postcss";
|
|
|
10
10
|
*/
|
|
11
11
|
class CssBuilder {
|
|
12
12
|
#cache;
|
|
13
|
-
#isDev;
|
|
14
13
|
#cssInput;
|
|
15
14
|
#cssOutput;
|
|
16
15
|
#hasTailwind = null;
|
|
17
16
|
|
|
18
|
-
constructor(cache,
|
|
17
|
+
constructor(cache, cssInput, cssOutput) {
|
|
19
18
|
this.#cache = cache;
|
|
20
|
-
this.#isDev = isDev;
|
|
21
19
|
this.#cssInput = cssInput;
|
|
22
20
|
this.#cssOutput = cssOutput;
|
|
23
21
|
}
|
|
@@ -27,7 +25,7 @@ class CssBuilder {
|
|
|
27
25
|
* @param {boolean} viewsChanged - Whether views have changed (forces Tailwind rebuild).
|
|
28
26
|
* @returns {Promise<number>} Number of CSS files processed.
|
|
29
27
|
*/
|
|
30
|
-
async build(viewsChanged) {
|
|
28
|
+
async build(viewsChanged, isDev = false) {
|
|
31
29
|
if (!fs.existsSync(this.#cssInput)) {
|
|
32
30
|
return 0;
|
|
33
31
|
}
|
|
@@ -67,12 +65,12 @@ class CssBuilder {
|
|
|
67
65
|
}
|
|
68
66
|
const processor = hasTailwind ? this.#cache.tailwindProcessor : null;
|
|
69
67
|
|
|
70
|
-
await Promise.all(filesToProcess.map(filename => this.#processCss(filename, hasTailwind, processor)));
|
|
68
|
+
await Promise.all(filesToProcess.map(filename => this.#processCss(filename, hasTailwind, processor, isDev)));
|
|
71
69
|
|
|
72
70
|
return filesToProcess.length;
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
async #processCss(filename, hasTailwind, processor) {
|
|
73
|
+
async #processCss(filename, hasTailwind, processor, isDev) {
|
|
76
74
|
const inputPath = path.join(this.#cssInput, filename);
|
|
77
75
|
const outputPath = path.join(this.#cssOutput, filename);
|
|
78
76
|
|
|
@@ -92,7 +90,7 @@ class CssBuilder {
|
|
|
92
90
|
const result = await processor.process(content, {
|
|
93
91
|
from: inputPath,
|
|
94
92
|
to: outputPath,
|
|
95
|
-
map:
|
|
93
|
+
map: isDev ? { inline: false } : false
|
|
96
94
|
});
|
|
97
95
|
|
|
98
96
|
await fs.promises.writeFile(outputPath, result.css);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { mergeUsageTrees, resolveComponentPath } from "./propUtils.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Computes effective prop usage by merging child component prop usage
|
|
6
|
+
* back into parent components through JSX prop forwarding chains.
|
|
7
|
+
*
|
|
8
|
+
* Example: ParentComponent forwards data → ChildComponent.info
|
|
9
|
+
* ChildComponent uses: { title, description }
|
|
10
|
+
* ParentComponent effective data = { title, description }
|
|
11
|
+
* → unused fields are stripped at the server→client boundary
|
|
12
|
+
*/
|
|
13
|
+
class EffectivePropUsage {
|
|
14
|
+
/**
|
|
15
|
+
* @param {Map<string, object|null>} propUsageMap - Basic prop usage per client component (from analyze()).
|
|
16
|
+
* @param {Map<string, {forwards: Array, imports: object}>} forwardMap - Forward info per component.
|
|
17
|
+
* @param {Set<string>} clientPaths - All client component absolute paths.
|
|
18
|
+
* @returns {Map<string, object|null>} Effective prop usage per component.
|
|
19
|
+
*/
|
|
20
|
+
static compute(propUsageMap, forwardMap, clientPaths) {
|
|
21
|
+
const resolvedForwards = resolveForwards(forwardMap, clientPaths);
|
|
22
|
+
const { children, parents } = buildGraph(resolvedForwards);
|
|
23
|
+
const order = topologicalSort(clientPaths, children, parents);
|
|
24
|
+
|
|
25
|
+
const effectiveMap = new Map();
|
|
26
|
+
|
|
27
|
+
for (const [filePath, usage] of propUsageMap) {
|
|
28
|
+
effectiveMap.set(filePath, usage ? structuredClone(usage) : null);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const componentPath of order) {
|
|
32
|
+
const forwards = resolvedForwards.get(componentPath);
|
|
33
|
+
if (!forwards) continue;
|
|
34
|
+
|
|
35
|
+
let effective = effectiveMap.get(componentPath);
|
|
36
|
+
if (effective === null) continue;
|
|
37
|
+
if (!effective) effective = {};
|
|
38
|
+
|
|
39
|
+
for (const { parentProp, childPath, childProp } of forwards) {
|
|
40
|
+
const childEffective = effectiveMap.get(childPath);
|
|
41
|
+
let childPropUsage;
|
|
42
|
+
|
|
43
|
+
if (childEffective === null) {
|
|
44
|
+
childPropUsage = true;
|
|
45
|
+
}
|
|
46
|
+
else if (childEffective && childProp in childEffective) {
|
|
47
|
+
childPropUsage = childEffective[childProp];
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (effective[parentProp] === true) {
|
|
54
|
+
// Basic usage was "whole" (likely from JSX forwarding itself).
|
|
55
|
+
// Replace with child's actual usage — this is the core optimization.
|
|
56
|
+
effective[parentProp] = childPropUsage;
|
|
57
|
+
}
|
|
58
|
+
else if (effective[parentProp] && typeof effective[parentProp] === "object") {
|
|
59
|
+
effective[parentProp] = mergeUsageTrees(effective[parentProp], childPropUsage);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
effective[parentProp] = childPropUsage;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
effectiveMap.set(componentPath, effective);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return effectiveMap;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Resolves relative import paths in forwards to absolute file paths.
|
|
76
|
+
* Only keeps forwards to known client components.
|
|
77
|
+
*/
|
|
78
|
+
function resolveForwards(forwardMap, clientPaths) {
|
|
79
|
+
const resolved = new Map();
|
|
80
|
+
|
|
81
|
+
for (const [filePath, info] of forwardMap) {
|
|
82
|
+
if (!info?.forwards?.length) continue;
|
|
83
|
+
|
|
84
|
+
const dir = path.dirname(filePath);
|
|
85
|
+
const items = [];
|
|
86
|
+
|
|
87
|
+
for (const fwd of info.forwards) {
|
|
88
|
+
const childPath = resolveComponentPath(fwd.childImport, dir);
|
|
89
|
+
|
|
90
|
+
if (childPath && clientPaths.has(childPath)) {
|
|
91
|
+
items.push({
|
|
92
|
+
parentProp: fwd.parentProp,
|
|
93
|
+
childPath,
|
|
94
|
+
childProp: fwd.childProp
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (items.length > 0) {
|
|
100
|
+
resolved.set(filePath, items);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return resolved;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Builds parent→children and child→parents adjacency lists from resolved forwards.
|
|
109
|
+
*/
|
|
110
|
+
function buildGraph(resolvedForwards) {
|
|
111
|
+
const children = new Map();
|
|
112
|
+
const parents = new Map();
|
|
113
|
+
|
|
114
|
+
for (const [parentPath, forwards] of resolvedForwards) {
|
|
115
|
+
for (const fwd of forwards) {
|
|
116
|
+
if (!children.has(parentPath)) children.set(parentPath, new Set());
|
|
117
|
+
children.get(parentPath).add(fwd.childPath);
|
|
118
|
+
|
|
119
|
+
if (!parents.has(fwd.childPath)) parents.set(fwd.childPath, new Set());
|
|
120
|
+
parents.get(fwd.childPath).add(parentPath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { children, parents };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Topological sort — processes leaf components (no forwards) first,
|
|
129
|
+
* then parents, so child effective usage is ready when parent is processed.
|
|
130
|
+
*
|
|
131
|
+
* Uses Kahn's algorithm on the reversed forwarding graph.
|
|
132
|
+
*/
|
|
133
|
+
function topologicalSort(allNodes, children, parents) {
|
|
134
|
+
const order = [];
|
|
135
|
+
const inDeg = new Map();
|
|
136
|
+
|
|
137
|
+
for (const node of allNodes) {
|
|
138
|
+
inDeg.set(node, children.get(node)?.size || 0);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const queue = [];
|
|
142
|
+
|
|
143
|
+
for (const [node, deg] of inDeg) {
|
|
144
|
+
if (deg === 0) queue.push(node);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const visited = new Set();
|
|
148
|
+
let qi = 0;
|
|
149
|
+
|
|
150
|
+
while (qi < queue.length) {
|
|
151
|
+
const node = queue[qi++];
|
|
152
|
+
order.push(node);
|
|
153
|
+
visited.add(node);
|
|
154
|
+
|
|
155
|
+
const parentSet = parents.get(node);
|
|
156
|
+
if (!parentSet) continue;
|
|
157
|
+
|
|
158
|
+
for (const parent of parentSet) {
|
|
159
|
+
const newDeg = inDeg.get(parent) - 1;
|
|
160
|
+
inDeg.set(parent, newDeg);
|
|
161
|
+
|
|
162
|
+
if (newDeg === 0) queue.push(parent);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Nodes not visited = cycles. Add them at the end with basic usage.
|
|
167
|
+
for (const node of allNodes) {
|
|
168
|
+
if (!visited.has(node)) order.push(node);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return order;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export default EffectivePropUsage;
|
|
@@ -109,24 +109,4 @@ function transformFile(filePath) {
|
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
* Transforms all built view files in a directory tree.
|
|
114
|
-
*/
|
|
115
|
-
function transformDirectory(dir) {
|
|
116
|
-
let count = 0;
|
|
117
|
-
|
|
118
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
119
|
-
const full = path.join(dir, entry.name);
|
|
120
|
-
|
|
121
|
-
if (entry.isDirectory()) {
|
|
122
|
-
count += transformDirectory(full);
|
|
123
|
-
}
|
|
124
|
-
else if (entry.name.endsWith(".js") && !entry.name.endsWith(".map")) {
|
|
125
|
-
if (transformFile(full)) count++;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return count;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export { transformFile, transformDirectory };
|
|
112
|
+
export { transformFile };
|
|
@@ -63,7 +63,7 @@ class FileAnalyzer {
|
|
|
63
63
|
|
|
64
64
|
this.#validateGraph(graph, entries, importedBy);
|
|
65
65
|
|
|
66
|
-
return { entries, layouts, meta: graph };
|
|
66
|
+
return { entries, layouts, meta: graph, importedBy };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
#findTsxFiles(dir, result = []) {
|
|
@@ -293,37 +293,6 @@ class FileAnalyzer {
|
|
|
293
293
|
return resolved;
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
findClientComponents(file, meta, seen, depth = 0) {
|
|
297
|
-
const result = new Set();
|
|
298
|
-
|
|
299
|
-
if (depth > MAX_DEPTH || seen.has(file)) {
|
|
300
|
-
return result;
|
|
301
|
-
}
|
|
302
|
-
seen.add(file);
|
|
303
|
-
|
|
304
|
-
const fileMeta = meta.get(file);
|
|
305
|
-
if (!fileMeta) {
|
|
306
|
-
return result;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (fileMeta.isClient) {
|
|
310
|
-
result.add(file);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
for (const importPath of fileMeta.imports) {
|
|
314
|
-
const resolvedPath = this.resolveImport(file, importPath);
|
|
315
|
-
|
|
316
|
-
if (meta.has(resolvedPath)) {
|
|
317
|
-
const childComponents = this.findClientComponents(resolvedPath, meta, seen, depth + 1);
|
|
318
|
-
for (const component of childComponents) {
|
|
319
|
-
result.add(component);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return result;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
296
|
collectCss(file, meta, seen, depth = 0) {
|
|
328
297
|
if (depth > MAX_DEPTH || seen.has(file)) {
|
|
329
298
|
return new Set();
|
|
@@ -366,7 +335,7 @@ class FileAnalyzer {
|
|
|
366
335
|
throw this.#createError("Client Entry Imported", {
|
|
367
336
|
Entry: relativePath(filePath),
|
|
368
337
|
By: relativePath(importers[0]),
|
|
369
|
-
Fix: "
|
|
338
|
+
Fix: "Add \"use client\" directive"
|
|
370
339
|
});
|
|
371
340
|
}
|
|
372
341
|
}
|