docs(core): update server boundary rules and fix db import paths
- document `.server.js` suffix requirement for node-only imports in DEV.md - add client-safe subentries table and server-only barrel warnings in MODULES.md - fix `crud.js` and `database/index.js` to import from `db.server.js` - replace `createRequire` with `pathToFileURL` in `discover.server.js` for ESM-only modules - update admin navigation and registry to use safe client-compatible imports - bump version to 1.4.132
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Provides convenient methods for Create, Read, Update, Delete operations
|
||||
*/
|
||||
|
||||
import { query, queryOne, queryAll } from './db.js';
|
||||
import { query, queryOne, queryAll } from './db.server.js';
|
||||
|
||||
/**
|
||||
* Filter a data object to only the columns present in allowedColumns.
|
||||
|
||||
@@ -14,7 +14,7 @@ export {
|
||||
closePool,
|
||||
testConnection,
|
||||
tableExists
|
||||
} from './db.js';
|
||||
} from './db.server.js';
|
||||
|
||||
// CRUD helper functions
|
||||
export {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve, join, dirname } from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { info, warn } from '@zen/core/shared/logger';
|
||||
import { registerModule } from './registry.js';
|
||||
|
||||
@@ -132,6 +132,20 @@ export async function findInstalledModuleNames({ cwd = process.cwd() } = {}) {
|
||||
return out.sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Résout l'URL ESM du main entry d'un module à partir de son `package.json`.
|
||||
* On lit `exports['.'].import` puis `main`, sinon `./index.js` par défaut.
|
||||
* Pas de `require.resolve` : un module `@zen/module-*` peut déclarer uniquement
|
||||
* la condition `"import"` (ESM-only) — la résolution CJS échouerait alors avec
|
||||
* "No exports main defined". Comme on a déjà localisé le `package.json` via
|
||||
* `resolveModulePackageJson`, on construit l'URL nous-mêmes.
|
||||
*/
|
||||
function resolveModuleEntryUrl(found) {
|
||||
const pkgDir = dirname(found.path);
|
||||
const main = found.pkg?.exports?.['.']?.import ?? found.pkg?.main ?? './index.js';
|
||||
return pathToFileURL(resolve(pkgDir, main)).href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variante "Node-only" du chargement de modules — utilisée par le CLI
|
||||
* `zen-db init` qui ne passe jamais par un bundler. Charge dynamiquement
|
||||
@@ -145,20 +159,17 @@ export async function findInstalledModuleNames({ cwd = process.cwd() } = {}) {
|
||||
*/
|
||||
export async function loadModulesForCli({ cwd = process.cwd() } = {}) {
|
||||
const names = await findInstalledModuleNames({ cwd });
|
||||
const require = createRequire(join(cwd, 'package.json'));
|
||||
|
||||
for (const name of names) {
|
||||
let entryPath;
|
||||
try {
|
||||
entryPath = require.resolve(name);
|
||||
} catch (err) {
|
||||
warn(`zen-modules: cannot resolve "${name}" — ${err.message}`);
|
||||
const found = resolveModulePackageJson(name, cwd);
|
||||
if (!found) {
|
||||
warn(`zen-modules: cannot find package "${name}" in node_modules`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mod;
|
||||
try {
|
||||
mod = await import(entryPath);
|
||||
mod = await import(resolveModuleEntryUrl(found));
|
||||
} catch (err) {
|
||||
warn(`zen-modules: failed to import "${name}" — ${err.message}`);
|
||||
continue;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
getNavItems,
|
||||
} from './registry.js';
|
||||
import { isDevkitEnabled } from '../../shared/lib/appConfig.js';
|
||||
import { PERMISSIONS } from '@zen/core/users';
|
||||
import { PERMISSIONS } from '@zen/core/users/constants';
|
||||
|
||||
// Sections et items core — enregistrés à l'import de ce module.
|
||||
registerNavSection({ id: 'dashboard', title: 'Tableau de bord', icon: 'DashboardSquare03Icon', order: 10 });
|
||||
|
||||
@@ -7,17 +7,26 @@
|
||||
* - navItem : une entrée de la sidebar admin (section optionnelle pour grouper).
|
||||
* - page : un composant rendu sous /admin/<slug>.
|
||||
*
|
||||
* Les instances de module sont séparées entre le bundle serveur et le bundle
|
||||
* client de Next.js ; c'est attendu : les fetchers vivent côté serveur, les
|
||||
* Composants côté client. Les navItems et les pages sont enregistrés côté
|
||||
* neutre et visibles des deux côtés.
|
||||
* Les Maps sont stockées sur `globalThis` via `Symbol.for` pour survivre :
|
||||
* 1. au hot-reload de Next.js dev (sinon les enregistrements disparaissent).
|
||||
* 2. à la double-instanciation du fichier — l'instrumentation hook tourne en
|
||||
* Node natif (require ESM), tandis que les Server Components passent par
|
||||
* le bundle Turbopack/Webpack. Sans `globalThis`, les nav items poussés
|
||||
* par `register-server.js` au boot ne seraient pas visibles côté Server
|
||||
* Component qui rend la sidebar — la sidebar resterait vide.
|
||||
*/
|
||||
|
||||
const widgetFetchers = new Map(); // id -> async () => data
|
||||
const widgetComponents = new Map(); // id -> { Component, order }
|
||||
const navItems = new Map(); // id -> { id, label, icon, href, order, sectionId }
|
||||
const navSections = new Map(); // id -> { id, title, icon, order }
|
||||
const pages = new Map(); // slug -> { slug, Component, title? }
|
||||
const REGISTRY_KEY = Symbol.for('__ZEN_ADMIN_REGISTRY__');
|
||||
if (!globalThis[REGISTRY_KEY]) {
|
||||
globalThis[REGISTRY_KEY] = {
|
||||
widgetFetchers: new Map(), // id -> async () => data
|
||||
widgetComponents: new Map(), // id -> { Component, order, permission }
|
||||
navItems: new Map(), // id -> { id, label, icon, href, order, sectionId, position, permission }
|
||||
navSections: new Map(), // id -> { id, title, icon, order }
|
||||
pages: new Map(), // slug -> { slug, Component, title?, breadcrumbLabel? }
|
||||
};
|
||||
}
|
||||
const { widgetFetchers, widgetComponents, navItems, navSections, pages } = globalThis[REGISTRY_KEY];
|
||||
|
||||
// ---- Widgets ---------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user