@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/index.d.ts
CHANGED
|
@@ -259,10 +259,12 @@ export class Validator {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
export class Lang {
|
|
262
|
-
static
|
|
262
|
+
static setup(server: any): void;
|
|
263
|
+
static get(key: string, params?: Record<string, any>): string;
|
|
263
264
|
static has(key: string): boolean;
|
|
264
265
|
static locale(): string;
|
|
265
266
|
static setLocale(locale: string): void;
|
|
267
|
+
static getFilteredTranslations(locale: string, keys: string[] | null): Record<string, string>;
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
export class DateTime {
|
|
@@ -542,6 +544,24 @@ declare global {
|
|
|
542
544
|
* const tab = request().query.tab || 'general';
|
|
543
545
|
* const id = request().params.id;
|
|
544
546
|
*/
|
|
547
|
+
/**
|
|
548
|
+
* Translates a key using the current locale.
|
|
549
|
+
* Works on both SSR and client-side.
|
|
550
|
+
*
|
|
551
|
+
* @param key - Translation key (dot notation supported)
|
|
552
|
+
* @param params - Optional parameters for replacement (:name) and pluralization (count)
|
|
553
|
+
* @example
|
|
554
|
+
* __("welcome") // "Hoş geldiniz"
|
|
555
|
+
* __("messages.greeting", { name: "Burak" }) // "Merhaba Burak"
|
|
556
|
+
* __("messages.items", { count: 5 }) // "5 öğe"
|
|
557
|
+
*/
|
|
558
|
+
function __(key: string, params?: Record<string, any>): string;
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Alias for __() — translates a key using the current locale.
|
|
562
|
+
*/
|
|
563
|
+
function lang(key: string, params?: Record<string, any>): string;
|
|
564
|
+
|
|
545
565
|
function request(): {
|
|
546
566
|
path: string;
|
|
547
567
|
method: string;
|
|
@@ -551,6 +571,7 @@ declare global {
|
|
|
551
571
|
cookies: Record<string, string>;
|
|
552
572
|
ip: string;
|
|
553
573
|
isAjax: boolean;
|
|
574
|
+
locale: string;
|
|
554
575
|
session: {
|
|
555
576
|
get<T = any>(key: string, defaultValue?: T): T;
|
|
556
577
|
set(key: string, value: any): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitronjs/framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "NitronJS is a modern and extensible Node.js MVC framework built on Fastify. It focuses on clean architecture, modular structure, and developer productivity, offering built-in routing, middleware, configuration management, CLI tooling, and native React integration for scalable full-stack applications.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"njs": "./cli/njs.js"
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"postcss": "^8.5.6",
|
|
36
36
|
"react": "^19.2.3",
|
|
37
37
|
"react-dom": "^19.2.3",
|
|
38
|
-
"react-
|
|
38
|
+
"react-server-dom-webpack": "^19.2.4",
|
|
39
|
+
"redis": "^5.6.0",
|
|
39
40
|
"socket.io": "^4.8.1",
|
|
40
41
|
"tailwindcss": "^4.1.18",
|
|
41
42
|
"typescript": "^5.9.3"
|
package/skeleton/config/app.js
CHANGED
|
@@ -11,6 +11,19 @@ export default {
|
|
|
11
11
|
maxFiles: 10,
|
|
12
12
|
maxParts: 60,
|
|
13
13
|
attachFieldsToBody: true,
|
|
14
|
+
security: {
|
|
15
|
+
verifyMagicBytes: true,
|
|
16
|
+
dangerousExtensions: [
|
|
17
|
+
".exe", ".bat", ".cmd", ".com", ".scr", ".pif", ".msi", ".dll",
|
|
18
|
+
".sh", ".cgi", ".csh", ".ksh",
|
|
19
|
+
".php", ".phtml", ".php5", ".php7",
|
|
20
|
+
".jsp", ".asp", ".aspx",
|
|
21
|
+
".vbs", ".vbe", ".wsf", ".wsh", ".ps1", ".psm1",
|
|
22
|
+
".hta", ".cpl", ".inf", ".reg", ".sct"
|
|
23
|
+
],
|
|
24
|
+
compoundExtensions: [".tar.gz", ".tar.bz2", ".tar.xz", ".tar.zst"],
|
|
25
|
+
allowDoubleExtension: false // WARNING: true enables file.exe.jpg to pass — security risk
|
|
26
|
+
},
|
|
14
27
|
},
|
|
15
28
|
},
|
|
16
29
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const SESSION_LIFETIME = 1000 * 60 * 60 * 2;
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
|
+
// Driver: none | file | memory | redis
|
|
5
|
+
// WARNING: "memory" driver stores sessions in process memory.
|
|
6
|
+
// Sessions are lost on restart and memory usage grows with active users.
|
|
7
|
+
// Use "file" or "redis" in production.
|
|
4
8
|
driver: "file",
|
|
5
9
|
lifetime: SESSION_LIFETIME,
|
|
6
10
|
cookieName: "session",
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import crypto from "crypto";
|
|
4
|
-
import Paths from "../Core/Paths.js";
|
|
5
|
-
import Layout from "../View/Layout.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Sanitizes a name to be a valid JavaScript identifier.
|
|
9
|
-
* @param {string} name - The name to sanitize.
|
|
10
|
-
* @returns {string} A valid JS identifier.
|
|
11
|
-
*/
|
|
12
|
-
function sanitizeName(name) {
|
|
13
|
-
return name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Generates hydration scripts for client-side component rendering.
|
|
18
|
-
* Creates island bundles for React components that need client interactivity.
|
|
19
|
-
*/
|
|
20
|
-
class HydrationBuilder {
|
|
21
|
-
#cache;
|
|
22
|
-
#isDev;
|
|
23
|
-
#templatesPath;
|
|
24
|
-
#analyzer;
|
|
25
|
-
|
|
26
|
-
constructor(cache, isDev, templatesPath, analyzer) {
|
|
27
|
-
this.#cache = cache;
|
|
28
|
-
this.#isDev = isDev;
|
|
29
|
-
this.#templatesPath = templatesPath;
|
|
30
|
-
this.#analyzer = analyzer;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Builds hydration bundles for client components.
|
|
35
|
-
* @param {object} userBundle - User view bundle.
|
|
36
|
-
* @param {object} frameworkBundle - Framework view bundle.
|
|
37
|
-
* @param {object} manifest - Build manifest.
|
|
38
|
-
* @param {Set<string>|null} changedViews - Changed view files or null for full build.
|
|
39
|
-
* @returns {Promise<Array<{input: string, output: string}>>}
|
|
40
|
-
*/
|
|
41
|
-
async build(userBundle, frameworkBundle, manifest, changedViews = null) {
|
|
42
|
-
const hydrationFiles = [];
|
|
43
|
-
const allChangedFiles = new Set();
|
|
44
|
-
|
|
45
|
-
if (changedViews) {
|
|
46
|
-
for (const v of changedViews) allChangedFiles.add(v);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
for (const bundle of [userBundle, frameworkBundle]) {
|
|
50
|
-
if (!bundle?.meta) continue;
|
|
51
|
-
for (const [filePath, fileMeta] of bundle.meta.entries()) {
|
|
52
|
-
if (fileMeta.isClient || fileMeta.needsClient) {
|
|
53
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
54
|
-
const hash = crypto.createHash("md5").update(content).digest("hex");
|
|
55
|
-
const cacheKey = `hydration:${filePath}`;
|
|
56
|
-
const cachedHash = this.#cache.fileHashes.get(cacheKey);
|
|
57
|
-
|
|
58
|
-
if (cachedHash !== hash) {
|
|
59
|
-
this.#cache.fileHashes.set(cacheKey, hash);
|
|
60
|
-
allChangedFiles.add(filePath);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
for (const bundle of [userBundle, frameworkBundle]) {
|
|
67
|
-
if (!bundle?.entries.length) continue;
|
|
68
|
-
|
|
69
|
-
for (const viewPath of bundle.entries) {
|
|
70
|
-
const viewRelative = path.relative(bundle.srcDir, viewPath)
|
|
71
|
-
.replace(/\.tsx$/, "")
|
|
72
|
-
.replace(/\\/g, "/")
|
|
73
|
-
.toLowerCase();
|
|
74
|
-
const manifestKey = `${bundle.namespace}:${viewRelative}`;
|
|
75
|
-
const hydrationScriptPath = `/js/${viewRelative}.js`;
|
|
76
|
-
const hydrationOutputPath = path.join(Paths.publicJs, viewRelative + ".js");
|
|
77
|
-
|
|
78
|
-
const viewMeta = bundle.meta.get(viewPath);
|
|
79
|
-
const clientComponents = new Set();
|
|
80
|
-
|
|
81
|
-
if (!viewMeta?.layoutDisabled) {
|
|
82
|
-
const viewRelativeForLayout = path.relative(bundle.srcDir, viewPath).replace(/\\/g, "/");
|
|
83
|
-
const layoutChain = Layout.resolve(viewRelativeForLayout, bundle.srcDir);
|
|
84
|
-
|
|
85
|
-
for (const layout of layoutChain) {
|
|
86
|
-
const layoutClients = this.#analyzer.findClientComponents(layout.path, bundle.meta, new Set());
|
|
87
|
-
for (const c of layoutClients) {
|
|
88
|
-
clientComponents.add(c);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const viewClients = this.#analyzer.findClientComponents(viewPath, bundle.meta, new Set());
|
|
94
|
-
for (const c of viewClients) {
|
|
95
|
-
clientComponents.add(c);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const needsRebuild = !changedViews ||
|
|
99
|
-
allChangedFiles.has(viewPath) ||
|
|
100
|
-
[...clientComponents].some(c => allChangedFiles.has(c));
|
|
101
|
-
|
|
102
|
-
if (clientComponents.size) {
|
|
103
|
-
if (needsRebuild || !fs.existsSync(hydrationOutputPath)) {
|
|
104
|
-
const hydrationFile = this.#generateHydrationFile(
|
|
105
|
-
viewPath,
|
|
106
|
-
bundle.srcDir,
|
|
107
|
-
clientComponents,
|
|
108
|
-
bundle.meta
|
|
109
|
-
);
|
|
110
|
-
hydrationFiles.push(hydrationFile);
|
|
111
|
-
}
|
|
112
|
-
manifest[manifestKey].hydrationScript = hydrationScriptPath;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return hydrationFiles;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
#generateHydrationFile(viewPath, srcDir, clientComponents, meta) {
|
|
121
|
-
const templateName = this.#isDev ? "page-hydration-dev.tsx" : "page-hydration.tsx";
|
|
122
|
-
const cacheKey = this.#isDev ? "hydrationTemplateDev" : "hydrationTemplate";
|
|
123
|
-
|
|
124
|
-
if (!this.#cache[cacheKey]) {
|
|
125
|
-
const templatePath = path.join(this.#templatesPath, templateName);
|
|
126
|
-
this.#cache[cacheKey] = fs.readFileSync(templatePath, "utf8");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const viewRelative = path.relative(srcDir, viewPath).replace(/\.tsx$/, "").toLowerCase();
|
|
130
|
-
const outputDir = path.join(Paths.project, ".nitron/hydration", path.dirname(viewRelative));
|
|
131
|
-
const outputFile = path.join(outputDir, path.basename(viewRelative) + ".tsx");
|
|
132
|
-
const moduleId = viewRelative.replace(/[\/\\]/g, "_");
|
|
133
|
-
|
|
134
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
135
|
-
|
|
136
|
-
const imports = [];
|
|
137
|
-
const manifestEntries = [];
|
|
138
|
-
const registrations = [];
|
|
139
|
-
let index = 0;
|
|
140
|
-
|
|
141
|
-
for (const componentPath of clientComponents) {
|
|
142
|
-
const componentMeta = meta.get(componentPath);
|
|
143
|
-
if (!componentMeta) continue;
|
|
144
|
-
|
|
145
|
-
const baseName = path.basename(componentPath, ".tsx");
|
|
146
|
-
const relativePath = path.relative(outputDir, componentPath)
|
|
147
|
-
.replace(/\\/g, "/")
|
|
148
|
-
.replace(/\.tsx$/, "");
|
|
149
|
-
|
|
150
|
-
if (componentMeta.hasDefault) {
|
|
151
|
-
const importName = sanitizeName(baseName) + "_" + index++;
|
|
152
|
-
imports.push(`import ${importName} from "${relativePath}";`);
|
|
153
|
-
manifestEntries.push(` "${baseName}": ${importName}`);
|
|
154
|
-
manifestEntries.push(` [${importName}.displayName || ${importName}.name || "${baseName}"]: ${importName}`);
|
|
155
|
-
if (this.#isDev) {
|
|
156
|
-
registrations.push(`if (window.__NITRON_REFRESH__) window.__NITRON_REFRESH__.register(${importName}, "${moduleId}_${importName}");`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
for (const namedExport of componentMeta.named || []) {
|
|
161
|
-
const importName = sanitizeName(namedExport) + "_" + index++;
|
|
162
|
-
imports.push(`import { ${namedExport} as ${importName} } from "${relativePath}";`);
|
|
163
|
-
manifestEntries.push(` "${namedExport}": ${importName}`);
|
|
164
|
-
manifestEntries.push(` [${importName}.displayName || ${importName}.name || "${namedExport}"]: ${importName}`);
|
|
165
|
-
if (this.#isDev) {
|
|
166
|
-
registrations.push(`if (window.__NITRON_REFRESH__) window.__NITRON_REFRESH__.register(${importName}, "${moduleId}_${importName}");`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let code = this.#cache[cacheKey]
|
|
172
|
-
.replace("// __COMPONENT_IMPORTS__", imports.join("\n"))
|
|
173
|
-
.replace(
|
|
174
|
-
"// __COMPONENT_MANIFEST__",
|
|
175
|
-
`Object.assign(componentManifest, {\n${manifestEntries.join(",\n")}\n});`
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
if (this.#isDev) {
|
|
179
|
-
code = code
|
|
180
|
-
.replace("__NITRON_MODULE_ID__", moduleId)
|
|
181
|
-
.replace("// __COMPONENT_REGISTRATIONS__", registrations.join("\n"));
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
fs.writeFileSync(outputFile, code);
|
|
185
|
-
|
|
186
|
-
return outputFile;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export default HydrationBuilder;
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { createRoot } from "react-dom/client";
|
|
3
|
-
|
|
4
|
-
// __COMPONENT_IMPORTS__
|
|
5
|
-
|
|
6
|
-
const componentManifest: Record<string, React.ComponentType<any>> = {};
|
|
7
|
-
|
|
8
|
-
// __COMPONENT_MANIFEST__
|
|
9
|
-
|
|
10
|
-
const w = window as any;
|
|
11
|
-
const prevRefreshReg = w.$RefreshReg$;
|
|
12
|
-
const prevRefreshSig = w.$RefreshSig$;
|
|
13
|
-
|
|
14
|
-
if (typeof w.$RefreshReg$ === "function") {
|
|
15
|
-
const moduleId = "__NITRON_MODULE_ID__";
|
|
16
|
-
w.$RefreshReg$ = (type: any, id: string) => {
|
|
17
|
-
const fullId = moduleId + "_" + id;
|
|
18
|
-
if (prevRefreshReg) prevRefreshReg(type, fullId);
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (typeof w.$RefreshSig$ === "function") {
|
|
23
|
-
w.$RefreshSig$ = prevRefreshSig;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// __COMPONENT_REGISTRATIONS__
|
|
27
|
-
|
|
28
|
-
function mount() {
|
|
29
|
-
const props = (window as any).__NITRON_PROPS__ || {};
|
|
30
|
-
if (!(window as any).__NITRON_ROOTS__) (window as any).__NITRON_ROOTS__ = new Map();
|
|
31
|
-
|
|
32
|
-
const islands = document.querySelectorAll<HTMLElement>("[data-cid]");
|
|
33
|
-
|
|
34
|
-
islands.forEach(element => {
|
|
35
|
-
const componentName = element.dataset.island;
|
|
36
|
-
const componentId = element.dataset.cid;
|
|
37
|
-
|
|
38
|
-
if (!componentName || !componentId) return;
|
|
39
|
-
|
|
40
|
-
const Component = componentManifest[componentName];
|
|
41
|
-
if (!Component) return;
|
|
42
|
-
|
|
43
|
-
const componentProps = props[componentId] || {};
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const roots = (window as any).__NITRON_ROOTS__ as Map<HTMLElement, any>;
|
|
47
|
-
let root = roots.get(element);
|
|
48
|
-
if (!root) {
|
|
49
|
-
root = createRoot(element);
|
|
50
|
-
roots.set(element, root);
|
|
51
|
-
}
|
|
52
|
-
root.render(React.createElement(Component, componentProps));
|
|
53
|
-
} catch {
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
delete (window as any).__NITRON_PROPS__;
|
|
58
|
-
|
|
59
|
-
if ((window as any).__NITRON_REFRESH__) {
|
|
60
|
-
(window as any).__NITRON_REFRESH__.performReactRefresh();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const isHmrUpdate = (window as any).__NITRON_HMR_UPDATE__;
|
|
65
|
-
|
|
66
|
-
if (!isHmrUpdate) {
|
|
67
|
-
if (document.readyState === "loading") {
|
|
68
|
-
document.addEventListener("DOMContentLoaded", mount);
|
|
69
|
-
} else {
|
|
70
|
-
mount();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { createRoot } from "react-dom/client";
|
|
3
|
-
|
|
4
|
-
// __COMPONENT_IMPORTS__
|
|
5
|
-
|
|
6
|
-
declare global {
|
|
7
|
-
interface Window {
|
|
8
|
-
__NITRON_PROPS__?: Record<string, any>;
|
|
9
|
-
__NITRON_ROOTS__?: Map<HTMLElement, any>;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const componentManifest: Record<string, React.ComponentType<any>> = {};
|
|
14
|
-
|
|
15
|
-
// __COMPONENT_MANIFEST__
|
|
16
|
-
|
|
17
|
-
function mount() {
|
|
18
|
-
const props = window.__NITRON_PROPS__ || {};
|
|
19
|
-
if (!window.__NITRON_ROOTS__) window.__NITRON_ROOTS__ = new Map();
|
|
20
|
-
|
|
21
|
-
const islands = document.querySelectorAll<HTMLElement>("[data-cid]");
|
|
22
|
-
|
|
23
|
-
islands.forEach(element => {
|
|
24
|
-
const componentName = element.dataset.island;
|
|
25
|
-
const componentId = element.dataset.cid;
|
|
26
|
-
|
|
27
|
-
if (!componentName || !componentId) return;
|
|
28
|
-
|
|
29
|
-
const Component = componentManifest[componentName];
|
|
30
|
-
if (!Component) return;
|
|
31
|
-
|
|
32
|
-
const componentProps = props[componentId] || {};
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
let root = window.__NITRON_ROOTS__.get(element);
|
|
36
|
-
if (!root) {
|
|
37
|
-
root = createRoot(element);
|
|
38
|
-
window.__NITRON_ROOTS__.set(element, root);
|
|
39
|
-
}
|
|
40
|
-
root.render(React.createElement(Component, componentProps));
|
|
41
|
-
} catch {
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
delete window.__NITRON_PROPS__;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (document.readyState === "loading") {
|
|
49
|
-
document.addEventListener("DOMContentLoaded", mount);
|
|
50
|
-
} else {
|
|
51
|
-
mount();
|
|
52
|
-
}
|
|
53
|
-
|