@ramesesinc/loader 1.0.10
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 +16 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/loaders/ComponentLoader.d.ts +21 -0
- package/dist/loaders/ComponentLoader.js +67 -0
- package/dist/loaders/MenuLoader.d.ts +3 -0
- package/dist/loaders/MenuLoader.js +28 -0
- package/dist/loaders/PageLoader.d.ts +15 -0
- package/dist/loaders/PageLoader.js +68 -0
- package/dist/loaders/TemplateLoader.d.ts +0 -0
- package/dist/loaders/TemplateLoader.js +7 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils/config.d.ts +8 -0
- package/dist/utils/config.js +11 -0
- package/dist/utils/createApi.d.ts +1 -0
- package/dist/utils/createApi.js +12 -0
- package/dist/utils/resolveTenantModule.d.ts +4 -0
- package/dist/utils/resolveTenantModule.js +12 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# rameses-lib
|
|
2
|
+
|
|
3
|
+
A lightweight, dynamic loader library for Next.js projects.
|
|
4
|
+
This library helps you **dynamically load pages, templates, and menus** based on tenant/module configurations.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 📦 Installation
|
|
9
|
+
|
|
10
|
+
Install from npm:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install rameses-lib
|
|
14
|
+
# or
|
|
15
|
+
yarn add rameses-lib
|
|
16
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ComponentLoaders, initComponentLoaders, loadComponent } from "./loaders/ComponentLoader";
|
|
2
|
+
export { loadMenu } from "./loaders/MenuLoader";
|
|
3
|
+
export { initTemplateLoaders, loadPage, TemplateLoaders } from "./loaders/PageLoader";
|
|
4
|
+
export { addRoutes } from "./utils/config";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface ComponentConfig {
|
|
3
|
+
component?: string;
|
|
4
|
+
attr?: Record<string, any>;
|
|
5
|
+
_id?: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
debug?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface LoadComponentOptions {
|
|
10
|
+
tenant?: string;
|
|
11
|
+
module?: string;
|
|
12
|
+
component: string;
|
|
13
|
+
target?: string;
|
|
14
|
+
params?: Record<string, any>;
|
|
15
|
+
collections?: string;
|
|
16
|
+
apiBase?: string;
|
|
17
|
+
}
|
|
18
|
+
export type ComponentLoaders = Record<string, (name: string) => React.ComponentType<any> | null>;
|
|
19
|
+
export declare const initComponentLoaders: (loaders: ComponentLoaders) => void;
|
|
20
|
+
export declare const loadComponent: ({ module, component, target, params, collections, apiBase, }: LoadComponentOptions) => Promise<React.ReactElement>;
|
|
21
|
+
export type { ComponentConfig, LoadComponentOptions };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createAPI } from "../utils/createApi";
|
|
3
|
+
import { resolveTenantModule } from "../utils/resolveTenantModule";
|
|
4
|
+
let globalComponentLoaders = {};
|
|
5
|
+
export const initComponentLoaders = (loaders) => {
|
|
6
|
+
globalComponentLoaders = loaders;
|
|
7
|
+
};
|
|
8
|
+
export const loadComponent = async ({ module, component, target, params = {}, collections, apiBase = "/api/localapi", }) => {
|
|
9
|
+
try {
|
|
10
|
+
let { tenant: tenantID, module: moduleID } = params !== null && params !== void 0 ? params : {};
|
|
11
|
+
if (!tenantID && !moduleID) {
|
|
12
|
+
const resolved = resolveTenantModule();
|
|
13
|
+
tenantID = resolved.tenant;
|
|
14
|
+
moduleID = resolved.module;
|
|
15
|
+
}
|
|
16
|
+
if (!tenantID || !moduleID) {
|
|
17
|
+
throw new Error("Both tenant and module are required from props");
|
|
18
|
+
}
|
|
19
|
+
let config = {};
|
|
20
|
+
const req = {};
|
|
21
|
+
// If component name is provided, try to load its configuration
|
|
22
|
+
if (component) {
|
|
23
|
+
const collection = collections || "components";
|
|
24
|
+
req.path = `/mgmt/${tenantID}/${moduleID}/${collection}/${component}`;
|
|
25
|
+
req.type = "GET";
|
|
26
|
+
req.tenant = tenantID;
|
|
27
|
+
req.module = moduleID;
|
|
28
|
+
req.contextPath = process.env.NEXT_PUBLIC_APP_CONTEXT_PATH;
|
|
29
|
+
req.filePath = `mgmt/${collection}/${component}.json`;
|
|
30
|
+
try {
|
|
31
|
+
const api = createAPI();
|
|
32
|
+
const result = await api.post(`${apiBase}/invoke`, req);
|
|
33
|
+
const data = result.data;
|
|
34
|
+
if (data === null || data === void 0 ? void 0 : data.message) {
|
|
35
|
+
console.log("Data from LoadComponent", { data });
|
|
36
|
+
}
|
|
37
|
+
config = data;
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error("Error loading component config:", err);
|
|
41
|
+
// Continue with default config if loading fails
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new Error("Component name is required");
|
|
46
|
+
}
|
|
47
|
+
const componentName = config.component || component;
|
|
48
|
+
if (!componentName) {
|
|
49
|
+
throw new Error("No component specified in the configuration.");
|
|
50
|
+
}
|
|
51
|
+
const [namespace, componentKey] = componentName.includes(":") ? componentName.split(":") : ["default", componentName];
|
|
52
|
+
const normalizedNamespace = namespace.replace("@", "");
|
|
53
|
+
const importer = globalComponentLoaders[normalizedNamespace];
|
|
54
|
+
if (!importer) {
|
|
55
|
+
throw new Error(`Namespace '${namespace}' not registered in component loaders.`);
|
|
56
|
+
}
|
|
57
|
+
const Component = importer(componentKey);
|
|
58
|
+
if (!Component) {
|
|
59
|
+
throw new Error(`Component '${componentKey}' not found in namespace '${namespace}'.`);
|
|
60
|
+
}
|
|
61
|
+
return _jsx(Component, { attr: config.attr, id: config._id, title: config.title, params: params, target: target, debug: config.debug });
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error("Component loading error:", error);
|
|
65
|
+
return (_jsx("div", { className: "p-4 bg-yellow-100 border border-yellow-400 rounded-lg", children: _jsxs("p", { className: "text-yellow-800 font-medium", children: ["Component Error: ", error.message] }) }));
|
|
66
|
+
}
|
|
67
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createAPI } from "../utils/createApi";
|
|
2
|
+
import { resolveTenantModule } from "../utils/resolveTenantModule";
|
|
3
|
+
export const loadMenu = ({ menuid }) => {
|
|
4
|
+
const resolved = resolveTenantModule();
|
|
5
|
+
const tenant = resolved.tenant || process.env.NEXT_PUBLIC_TENANT_NAME;
|
|
6
|
+
const module = process.env.NEXT_PUBLIC_MODULE_NAME || resolved.module;
|
|
7
|
+
const req = {};
|
|
8
|
+
const fetchData = async () => {
|
|
9
|
+
req.path = `/mgmt/${tenant}/${module}/menus/${menuid}`;
|
|
10
|
+
req.type = "GET";
|
|
11
|
+
req.tenant = tenant;
|
|
12
|
+
req.module = module;
|
|
13
|
+
req.contextPath = process.env.NEXT_PUBLIC_APP_CONTEXT_PATH;
|
|
14
|
+
if (menuid !== "list") {
|
|
15
|
+
req.filePath = `mgmt/menus/${menuid}.json`;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const api = createAPI();
|
|
19
|
+
const result = await api.post("/api/localapi/invoke", req);
|
|
20
|
+
return result.data;
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.error(err);
|
|
24
|
+
return { items: [] };
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
return fetchData();
|
|
28
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface LoadOptions {
|
|
3
|
+
tenant?: string;
|
|
4
|
+
module?: string;
|
|
5
|
+
page?: string;
|
|
6
|
+
component?: string;
|
|
7
|
+
params?: Record<string, any>;
|
|
8
|
+
target: string;
|
|
9
|
+
collections?: string;
|
|
10
|
+
apiBase?: string;
|
|
11
|
+
}
|
|
12
|
+
export type TemplateLoaders = Record<string, (name: string) => React.ComponentType<any> | null>;
|
|
13
|
+
export declare const initTemplateLoaders: (loaders: TemplateLoaders) => void;
|
|
14
|
+
export declare const loadPage: ({ module, page, component, params, target, collections, apiBase }: LoadOptions) => Promise<import("react/jsx-runtime").JSX.Element>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createAPI } from "../utils/createApi";
|
|
3
|
+
import { resolveTenantModule } from "../utils/resolveTenantModule";
|
|
4
|
+
let globalTemplateLoaders = {};
|
|
5
|
+
export const initTemplateLoaders = (loaders) => {
|
|
6
|
+
globalTemplateLoaders = loaders;
|
|
7
|
+
};
|
|
8
|
+
export const loadPage = async ({ module, page, component, params = {}, target, collections, apiBase = "/api/localapi" }) => {
|
|
9
|
+
var _a;
|
|
10
|
+
try {
|
|
11
|
+
let { tenant: tenantID, module: moduleID } = params !== null && params !== void 0 ? params : {};
|
|
12
|
+
if (!tenantID && !moduleID) {
|
|
13
|
+
const resolved = resolveTenantModule();
|
|
14
|
+
const tenantID = resolved.tenant;
|
|
15
|
+
const moduleID = resolved.module;
|
|
16
|
+
}
|
|
17
|
+
if (!tenantID || !moduleID) {
|
|
18
|
+
throw new Error("Both tenant and module are required from props");
|
|
19
|
+
}
|
|
20
|
+
let conf = {};
|
|
21
|
+
const req = {};
|
|
22
|
+
if (page) {
|
|
23
|
+
const collection = !collections ? "pages" : collections;
|
|
24
|
+
req.path = `/mgmt/${tenantID}/${moduleID}/${collection}/${page}`;
|
|
25
|
+
req.type = "GET";
|
|
26
|
+
req.tenant = tenantID;
|
|
27
|
+
req.module = moduleID;
|
|
28
|
+
req.contextPath = process.env.NEXT_PUBLIC_APP_CONTEXT_PATH;
|
|
29
|
+
if (page !== "list") {
|
|
30
|
+
req.filePath = `mgmt/${collection}/${page}.json`;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const api = createAPI();
|
|
34
|
+
// console.log({ path: `${apiBase}/invoke`, req });
|
|
35
|
+
const result = await api.post(`${apiBase}/invoke`, req);
|
|
36
|
+
// console.log("Result", result);
|
|
37
|
+
const data = result.data;
|
|
38
|
+
if (data === null || data === void 0 ? void 0 : data.message) {
|
|
39
|
+
console.log("Data from LoadPage", { data });
|
|
40
|
+
}
|
|
41
|
+
conf = data;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error(err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
conf.template = component;
|
|
49
|
+
conf.attr = {};
|
|
50
|
+
}
|
|
51
|
+
if (!conf.template) {
|
|
52
|
+
throw new Error("No template found in the configuration.");
|
|
53
|
+
}
|
|
54
|
+
const [namespace, xpage] = conf.template.includes(":") ? conf.template.split(":") : ["default", conf.template];
|
|
55
|
+
const normalizedNamespace = namespace.replace("@", "");
|
|
56
|
+
const importer = globalTemplateLoaders[normalizedNamespace];
|
|
57
|
+
if (!importer)
|
|
58
|
+
throw new Error(`Namespace '${namespace}' not registered.`);
|
|
59
|
+
const Template = importer(xpage);
|
|
60
|
+
if (!Template)
|
|
61
|
+
throw new Error(`Template '${xpage}' not found.`);
|
|
62
|
+
// return <Template attr={conf.attr} id={conf._id} title={conf.title} params={params} target={target} debug={conf.debug} />;
|
|
63
|
+
return (_jsx(Template, Object.assign({}, conf.attr, { attr: conf.attr, data: (_a = conf.data) !== null && _a !== void 0 ? _a : {}, id: conf._id, title: conf.title, params: params, target: target, debug: conf.debug })));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return (_jsx("div", { className: "p-4 bg-red-100 border border-red-400 rounded-lg", children: _jsxs("p", { className: "text-red-700 font-medium", children: ["Error: ", error.message] }) }));
|
|
67
|
+
}
|
|
68
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// import dynamic from "next/dynamic";
|
|
3
|
+
// export const templateLoaders: Record<string, (name: string) => React.ComponentType<any> | null> = {
|
|
4
|
+
// seti: (name) => dynamic(() => import(`@/seti/templates/${name}`)),
|
|
5
|
+
// agos: (name) => dynamic(() => import(`@/agos/templates/${name}`)),
|
|
6
|
+
// default: (name) => dynamic(() => import(`@/templates/${name}`)),
|
|
7
|
+
// };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/index.ts","../src/loaders/componentloader.tsx","../src/loaders/menuloader.ts","../src/loaders/pageloader.tsx","../src/loaders/templateloader.ts","../src/utils/config.ts","../src/utils/createapi.ts","../src/utils/resolvetenantmodule.ts"],"version":"5.9.3"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createAPI(): import("axios").AxiosInstance;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
const __LOCAL_API_KEY__ = "4e3b5a2c-9c74-4e10-9c76-3a3deec6d3c2";
|
|
3
|
+
export function createAPI() {
|
|
4
|
+
const api = axios.create();
|
|
5
|
+
api.interceptors.request.use((config) => {
|
|
6
|
+
if (config.headers) {
|
|
7
|
+
config.headers["X-LOCAL_API_KEY"] = __LOCAL_API_KEY__;
|
|
8
|
+
}
|
|
9
|
+
return config;
|
|
10
|
+
});
|
|
11
|
+
return api;
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getRoutePrefixes } from "./config";
|
|
2
|
+
export const resolveTenantModule = () => {
|
|
3
|
+
if (typeof window === "undefined")
|
|
4
|
+
return {};
|
|
5
|
+
const routePrefixes = getRoutePrefixes();
|
|
6
|
+
const pathname = window.location.pathname;
|
|
7
|
+
const [, seg1, seg2, seg3] = pathname.split("/");
|
|
8
|
+
if (seg1 === 'modules' || seg1 === 'sysmgmt') {
|
|
9
|
+
return { tenant: seg2, module: seg3 };
|
|
10
|
+
}
|
|
11
|
+
return {};
|
|
12
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ramesesinc/loader",
|
|
3
|
+
"version": "1.0.10",
|
|
4
|
+
"description": "Reusable loader utilities",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./utils": {
|
|
15
|
+
"import": "./dist/utils/index.js",
|
|
16
|
+
"require": "./dist/utils/index.js",
|
|
17
|
+
"types": "./dist/utils/index.d.ts"
|
|
18
|
+
},
|
|
19
|
+
"./loaders": {
|
|
20
|
+
"import": "./dist/loaders/index.js",
|
|
21
|
+
"require": "./dist/loaders/index.js",
|
|
22
|
+
"types": "./dist/loaders/index.d.ts"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc --build",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"dev": "tsc --watch",
|
|
32
|
+
"prepublishOnly": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"axios": ">=1.0.0",
|
|
39
|
+
"next": ">=13.5.0 <15.0.0",
|
|
40
|
+
"react": ">=18.0.0",
|
|
41
|
+
"react-dom": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^20",
|
|
45
|
+
"@types/react": "^18",
|
|
46
|
+
"@types/react-dom": "^18",
|
|
47
|
+
"typescript": "^5.9.2"
|
|
48
|
+
}
|
|
49
|
+
}
|