webspresso 0.0.110 → 0.0.112
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/core/client/adapter/datastar.js +190 -51
- package/core/client/api.js +23 -21
- package/core/client/controller.js +407 -40
- package/core/client/debug.js +12 -12
- package/core/client/devtool.js +124 -19
- package/core/client/forms.js +163 -29
- package/core/client/fragments.js +616 -118
- package/core/client/host.js +369 -0
- package/core/client/index.d.ts +18 -0
- package/core/client/index.js +154 -23
- package/core/client/lite.js +101 -4
- package/core/client/server-fragments.js +142 -22
- package/core/client/services.js +588 -0
- package/core/client/utils.js +188 -2
- package/index.d.ts +334 -29
- package/index.js +3 -0
- package/package.json +1 -1
- package/src/bundle/index.js +175 -14
- package/src/dev/banner.js +4 -1
- package/src/dev-reload/index.js +29 -1
- package/src/server.js +28 -0
package/src/bundle/index.js
CHANGED
|
@@ -53,7 +53,30 @@ const SERVER_ONLY_PACKAGES = new Set([
|
|
|
53
53
|
]);
|
|
54
54
|
|
|
55
55
|
const SERVER_ONLY_INTERNAL_PATTERN =
|
|
56
|
-
/(?:^|\/)(?:core\/(?:orm|services|auth|secrets|queue|mail|http|metrics|health|rate-limit)|src\/(?:server|file-router|api|pages)
|
|
56
|
+
/(?:^(?:src\/)?(?:models|services|jobs|migrations|pages\/api)|(?:^|\/)(?:core\/(?:orm|services|auth|secrets|queue|mail|http|metrics|health|rate-limit)|src\/(?:server|file-router|api|pages)))(?:\/|$)/;
|
|
57
|
+
|
|
58
|
+
function isClientServiceFile(resolvedBase) {
|
|
59
|
+
const candidates = [
|
|
60
|
+
resolvedBase,
|
|
61
|
+
`${resolvedBase}.ts`,
|
|
62
|
+
`${resolvedBase}.js`,
|
|
63
|
+
`${resolvedBase}.mjs`,
|
|
64
|
+
`${resolvedBase}.tsx`,
|
|
65
|
+
`${resolvedBase}.jsx`,
|
|
66
|
+
];
|
|
67
|
+
for (const cand of candidates) {
|
|
68
|
+
if (cand.includes('.client.')) return true;
|
|
69
|
+
if (fs.existsSync(cand) && fs.statSync(cand).isFile()) {
|
|
70
|
+
try {
|
|
71
|
+
const content = fs.readFileSync(cand, 'utf8');
|
|
72
|
+
if (/\bdefineClientService\b/.test(content)) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
} catch (_) {}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
57
80
|
|
|
58
81
|
const DEFAULT_ENTRYPOINTS = [
|
|
59
82
|
'src/client/main.js',
|
|
@@ -290,6 +313,9 @@ function checkServerImportViolation(specifier, importerPath, cwd = process.cwd()
|
|
|
290
313
|
const relToRoot = path.relative(cwd, resolved).replace(/\\/g, '/');
|
|
291
314
|
|
|
292
315
|
if (SERVER_ONLY_INTERNAL_PATTERN.test(relToRoot)) {
|
|
316
|
+
if (/^(?:src\/)?services(?:\/|$)/.test(relToRoot) && isClientServiceFile(resolved)) {
|
|
317
|
+
return { forbidden: false };
|
|
318
|
+
}
|
|
293
319
|
return {
|
|
294
320
|
forbidden: true,
|
|
295
321
|
reason: `Server module "${relToRoot}" (imported as "${raw}" in "${path.relative(cwd, importerPath)}") cannot be bundled into browser code.`,
|
|
@@ -700,6 +726,8 @@ function createDevBundler(options = {}, devReloadHub = null) {
|
|
|
700
726
|
validateClientBundleBoundaries(entryPoints, cwd);
|
|
701
727
|
|
|
702
728
|
const outbase = options.outbase || (fs.existsSync(path.resolve(cwd, 'src/client')) ? path.resolve(cwd, 'src/client') : undefined);
|
|
729
|
+
const splitting = options.splitting !== undefined ? Boolean(options.splitting) : format === 'esm';
|
|
730
|
+
const chunkNames = options.chunkNames || 'chunks/[name]-[hash]';
|
|
703
731
|
const esbuildConfig = {
|
|
704
732
|
entryPoints,
|
|
705
733
|
outdir,
|
|
@@ -708,6 +736,12 @@ function createDevBundler(options = {}, devReloadHub = null) {
|
|
|
708
736
|
minify,
|
|
709
737
|
target,
|
|
710
738
|
format,
|
|
739
|
+
splitting,
|
|
740
|
+
...(splitting ? { chunkNames } : {}),
|
|
741
|
+
define: {
|
|
742
|
+
'process.env.NODE_ENV': '"development"',
|
|
743
|
+
...(options.define || {}),
|
|
744
|
+
},
|
|
711
745
|
metafile,
|
|
712
746
|
outbase,
|
|
713
747
|
plugins: [createServerBoundaryPlugin(cwd), devReloadPlugin, ...userPlugins],
|
|
@@ -804,6 +838,7 @@ async function buildBundle(options = {}) {
|
|
|
804
838
|
publicDir,
|
|
805
839
|
manifestPath,
|
|
806
840
|
hash: options.hash !== false,
|
|
841
|
+
splitting: options.splitting !== false && (options.format || 'esm') === 'esm',
|
|
807
842
|
});
|
|
808
843
|
}
|
|
809
844
|
|
|
@@ -812,8 +847,10 @@ async function buildBundle(options = {}) {
|
|
|
812
847
|
const sourcemap = options.sourcemap !== undefined ? options.sourcemap : true;
|
|
813
848
|
const target = options.target || ['es2020'];
|
|
814
849
|
const format = options.format || 'esm';
|
|
850
|
+
const splitting = options.splitting !== undefined ? Boolean(options.splitting) : format === 'esm';
|
|
815
851
|
const outbase = options.outbase || (fs.existsSync(path.resolve(cwd, 'src/client')) ? path.resolve(cwd, 'src/client') : undefined);
|
|
816
852
|
const entryNames = options.entryNames || (options.hash !== false ? '[dir]/[name].[hash]' : '[dir]/[name]');
|
|
853
|
+
const chunkNames = options.chunkNames || (options.hash !== false ? 'chunks/[name].[hash]' : 'chunks/[name]');
|
|
817
854
|
const userPlugins = Array.isArray(options.plugins) ? options.plugins : [];
|
|
818
855
|
|
|
819
856
|
const manifestPlugin = createManifestPlugin({
|
|
@@ -838,6 +875,8 @@ async function buildBundle(options = {}) {
|
|
|
838
875
|
sourcemap,
|
|
839
876
|
target,
|
|
840
877
|
format,
|
|
878
|
+
splitting,
|
|
879
|
+
...(splitting ? { chunkNames } : {}),
|
|
841
880
|
entryNames,
|
|
842
881
|
outbase,
|
|
843
882
|
metafile: true,
|
|
@@ -858,12 +897,34 @@ async function buildBundle(options = {}) {
|
|
|
858
897
|
};
|
|
859
898
|
}
|
|
860
899
|
|
|
900
|
+
function resolveLocalModuleFile(importerAbsPath, specifier) {
|
|
901
|
+
if (!specifier || !specifier.startsWith('.')) return null;
|
|
902
|
+
const baseResolved = path.resolve(path.dirname(importerAbsPath), specifier);
|
|
903
|
+
const candidates = [
|
|
904
|
+
baseResolved,
|
|
905
|
+
`${baseResolved}.ts`,
|
|
906
|
+
`${baseResolved}.js`,
|
|
907
|
+
`${baseResolved}.mjs`,
|
|
908
|
+
`${baseResolved}.tsx`,
|
|
909
|
+
`${baseResolved}.jsx`,
|
|
910
|
+
`${baseResolved}.css`,
|
|
911
|
+
path.join(baseResolved, 'index.ts'),
|
|
912
|
+
path.join(baseResolved, 'index.js'),
|
|
913
|
+
];
|
|
914
|
+
for (const cand of candidates) {
|
|
915
|
+
if (fs.existsSync(cand) && fs.statSync(cand).isFile()) {
|
|
916
|
+
return cand;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
|
|
861
922
|
/**
|
|
862
923
|
* Built-in fallback asset builder when esbuild is not installed in node_modules.
|
|
863
|
-
* Compiles `.ts` / `.js` client entrypoints,
|
|
864
|
-
* and writes `manifest.json`.
|
|
924
|
+
* Compiles `.ts` / `.js` client entrypoints, splits dynamic `import()` modules
|
|
925
|
+
* and shared chunks into `chunks/`, hashes filenames for cache-busting, and writes `manifest.json`.
|
|
865
926
|
*/
|
|
866
|
-
function buildBundleFallback({ cwd, entryPoints, outdir, publicDir, manifestPath, hash = true }) {
|
|
927
|
+
function buildBundleFallback({ cwd, entryPoints, outdir, publicDir, manifestPath, hash = true, splitting = true }) {
|
|
867
928
|
const crypto = require('crypto');
|
|
868
929
|
let ts = null;
|
|
869
930
|
try {
|
|
@@ -880,22 +941,116 @@ function buildBundleFallback({ cwd, entryPoints, outdir, publicDir, manifestPath
|
|
|
880
941
|
: entryPoints;
|
|
881
942
|
|
|
882
943
|
const metafile = { inputs: {}, outputs: {} };
|
|
944
|
+
const emittedChunksBySource = new Map(); // absSourcePath -> { absOut, relFromOutdir, relOutput }
|
|
883
945
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
if (!fs.existsSync(absIn)) continue;
|
|
887
|
-
const rawCode = fs.readFileSync(absIn, 'utf8');
|
|
888
|
-
let outCode = rawCode;
|
|
889
|
-
|
|
890
|
-
if ((absIn.endsWith('.ts') || absIn.endsWith('.tsx')) && ts) {
|
|
946
|
+
function transpileSource(absFile, rawCode) {
|
|
947
|
+
if ((absFile.endsWith('.ts') || absFile.endsWith('.tsx')) && ts) {
|
|
891
948
|
const transpiled = ts.transpileModule(rawCode, {
|
|
892
949
|
compilerOptions: {
|
|
893
950
|
module: ts.ModuleKind.ESNext,
|
|
894
951
|
target: ts.ScriptTarget.ES2020,
|
|
895
952
|
},
|
|
896
|
-
fileName:
|
|
953
|
+
fileName: absFile,
|
|
897
954
|
});
|
|
898
|
-
|
|
955
|
+
return transpiled.outputText;
|
|
956
|
+
}
|
|
957
|
+
return rawCode;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function emitChunkModule(absModulePath) {
|
|
961
|
+
if (emittedChunksBySource.has(absModulePath)) {
|
|
962
|
+
return emittedChunksBySource.get(absModulePath);
|
|
963
|
+
}
|
|
964
|
+
if (!fs.existsSync(absModulePath)) return null;
|
|
965
|
+
|
|
966
|
+
const rawCode = fs.readFileSync(absModulePath, 'utf8');
|
|
967
|
+
const isCss = absModulePath.endsWith('.css');
|
|
968
|
+
let outCode = isCss ? rawCode : transpileSource(absModulePath, rawCode);
|
|
969
|
+
|
|
970
|
+
// Reserve placeholder to prevent infinite recursion on circular imports
|
|
971
|
+
const baseName = path.basename(absModulePath, path.extname(absModulePath)).replace(/\.client$/i, '');
|
|
972
|
+
const placeholder = { absOut: '', relFromOutdir: '', relOutput: '' };
|
|
973
|
+
emittedChunksBySource.set(absModulePath, placeholder);
|
|
974
|
+
|
|
975
|
+
const chunkImports = [];
|
|
976
|
+
if (!isCss && splitting) {
|
|
977
|
+
const placeholderOutDir = path.join(outdir, 'chunks');
|
|
978
|
+
outCode = rewriteModuleImports(absModulePath, outCode, placeholderOutDir, chunkImports);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
const digest = hash
|
|
982
|
+
? '.' + crypto.createHash('sha256').update(outCode).digest('hex').slice(0, 8).toUpperCase()
|
|
983
|
+
: '';
|
|
984
|
+
const ext = isCss ? '.css' : '.js';
|
|
985
|
+
const relFromOutdir = `chunks/${baseName}${digest}${ext}`;
|
|
986
|
+
const absOut = path.join(outdir, relFromOutdir);
|
|
987
|
+
fs.mkdirSync(path.dirname(absOut), { recursive: true });
|
|
988
|
+
fs.writeFileSync(absOut, outCode, 'utf8');
|
|
989
|
+
|
|
990
|
+
const relInput = path.relative(cwd, absModulePath).replace(/\\/g, '/');
|
|
991
|
+
const relOutput = path.relative(cwd, absOut).replace(/\\/g, '/');
|
|
992
|
+
metafile.inputs[relInput] = { bytes: Buffer.byteLength(rawCode, 'utf8') };
|
|
993
|
+
metafile.outputs[relOutput] = {
|
|
994
|
+
bytes: Buffer.byteLength(outCode, 'utf8'),
|
|
995
|
+
imports: chunkImports,
|
|
996
|
+
inputs: {
|
|
997
|
+
[relInput]: { bytesInOutput: Buffer.byteLength(outCode, 'utf8') },
|
|
998
|
+
},
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
placeholder.absOut = absOut;
|
|
1002
|
+
placeholder.relFromOutdir = relFromOutdir;
|
|
1003
|
+
placeholder.relOutput = relOutput;
|
|
1004
|
+
return placeholder;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function rewriteModuleImports(importerAbsPath, code, emitterOutDir, collectedImports) {
|
|
1008
|
+
// 1. Rewrite dynamic `import('./...')`
|
|
1009
|
+
let rewritten = code.replace(
|
|
1010
|
+
/\bimport\s*\(\s*(['"])(\.[^'"]+)\1\s*\)/g,
|
|
1011
|
+
(fullMatch, quote, spec) => {
|
|
1012
|
+
const resolved = resolveLocalModuleFile(importerAbsPath, spec);
|
|
1013
|
+
if (!resolved) return fullMatch;
|
|
1014
|
+
const chunkInfo = emitChunkModule(resolved);
|
|
1015
|
+
if (!chunkInfo || !chunkInfo.absOut) return fullMatch;
|
|
1016
|
+
let relSpec = path.relative(emitterOutDir, chunkInfo.absOut).replace(/\\/g, '/');
|
|
1017
|
+
if (!relSpec.startsWith('.')) relSpec = './' + relSpec;
|
|
1018
|
+
collectedImports.push({ path: chunkInfo.relOutput, kind: 'dynamic-import' });
|
|
1019
|
+
return `import(${quote}${relSpec}${quote})`;
|
|
1020
|
+
}
|
|
1021
|
+
);
|
|
1022
|
+
|
|
1023
|
+
// 2. Rewrite static relative `import ... from './...'` / `export ... from './...'`
|
|
1024
|
+
rewritten = rewritten.replace(
|
|
1025
|
+
/(\b(?:import|export)\s+(?:[\s\S]*?\bfrom\s+)?)(['"])(\.[^'"]+)\2/g,
|
|
1026
|
+
(fullMatch, prefix, quote, spec) => {
|
|
1027
|
+
const resolved = resolveLocalModuleFile(importerAbsPath, spec);
|
|
1028
|
+
if (!resolved) return fullMatch;
|
|
1029
|
+
const chunkInfo = emitChunkModule(resolved);
|
|
1030
|
+
if (!chunkInfo || !chunkInfo.absOut) return fullMatch;
|
|
1031
|
+
let relSpec = path.relative(emitterOutDir, chunkInfo.absOut).replace(/\\/g, '/');
|
|
1032
|
+
if (!relSpec.startsWith('.')) relSpec = './' + relSpec;
|
|
1033
|
+
collectedImports.push({ path: chunkInfo.relOutput, kind: 'import-statement' });
|
|
1034
|
+
if (resolved.endsWith('.css')) {
|
|
1035
|
+
return `/* css: ${relSpec} */`;
|
|
1036
|
+
}
|
|
1037
|
+
return `${prefix}${quote}${relSpec}${quote}`;
|
|
1038
|
+
}
|
|
1039
|
+
);
|
|
1040
|
+
|
|
1041
|
+
return rewritten;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
for (const [entryKey, rawFilePath] of Object.entries(entriesMap)) {
|
|
1045
|
+
const absIn = path.resolve(cwd, rawFilePath);
|
|
1046
|
+
if (!fs.existsSync(absIn)) continue;
|
|
1047
|
+
const rawCode = fs.readFileSync(absIn, 'utf8');
|
|
1048
|
+
let outCode = transpileSource(absIn, rawCode);
|
|
1049
|
+
|
|
1050
|
+
const entryOutDir = path.dirname(path.join(outdir, `${entryKey}.js`));
|
|
1051
|
+
const entryImports = [];
|
|
1052
|
+
if (splitting) {
|
|
1053
|
+
outCode = rewriteModuleImports(absIn, outCode, entryOutDir, entryImports);
|
|
899
1054
|
}
|
|
900
1055
|
|
|
901
1056
|
const digest = hash
|
|
@@ -912,10 +1067,16 @@ function buildBundleFallback({ cwd, entryPoints, outdir, publicDir, manifestPath
|
|
|
912
1067
|
metafile.outputs[relOutput] = {
|
|
913
1068
|
entryPoint: relInput,
|
|
914
1069
|
bytes: Buffer.byteLength(outCode, 'utf8'),
|
|
1070
|
+
imports: entryImports,
|
|
1071
|
+
inputs: {
|
|
1072
|
+
[relInput]: { bytesInOutput: Buffer.byteLength(outCode, 'utf8') },
|
|
1073
|
+
},
|
|
915
1074
|
};
|
|
916
1075
|
}
|
|
917
1076
|
|
|
918
|
-
const
|
|
1077
|
+
const relOutdir = path.relative(cwd, outdir).replace(/\\/g, '/') || 'public/dist';
|
|
1078
|
+
const relPublicDir = path.relative(cwd, publicDir).replace(/\\/g, '/') || 'public';
|
|
1079
|
+
const manifest = generateManifest(metafile, { outdir: relOutdir, publicDir: relPublicDir });
|
|
919
1080
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
920
1081
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
921
1082
|
|
package/src/dev/banner.js
CHANGED
|
@@ -154,7 +154,10 @@ function collectBannerSummary(context = {}) {
|
|
|
154
154
|
hasAdmin = Boolean(context.hasAdmin);
|
|
155
155
|
} else if (pluginManager && pluginManager.plugins instanceof Map) {
|
|
156
156
|
pluginsList = Array.from(pluginManager.plugins.keys());
|
|
157
|
-
hasAdmin =
|
|
157
|
+
hasAdmin =
|
|
158
|
+
pluginManager.plugins.has('admin-panel') ||
|
|
159
|
+
pluginManager.plugins.has('adminPanel') ||
|
|
160
|
+
pluginManager.plugins.has('admin');
|
|
158
161
|
} else {
|
|
159
162
|
hasAdmin = true;
|
|
160
163
|
}
|
package/src/dev-reload/index.js
CHANGED
|
@@ -315,6 +315,21 @@ function createDevReloadHub(config = {}) {
|
|
|
315
315
|
|
|
316
316
|
let activeNunjucksEnv = nunjucksEnv;
|
|
317
317
|
let onTemplateChangeCallback = onTemplateChange;
|
|
318
|
+
let getDevMetaCallback = typeof config.getDevMeta === 'function' ? config.getDevMeta : null;
|
|
319
|
+
|
|
320
|
+
function resolveDevMetaJson() {
|
|
321
|
+
if (typeof getDevMetaCallback !== 'function') return null;
|
|
322
|
+
try {
|
|
323
|
+
const meta = getDevMetaCallback();
|
|
324
|
+
if (!meta || typeof meta !== 'object') return null;
|
|
325
|
+
return JSON.stringify(meta)
|
|
326
|
+
.replace(/</g, '\\u003c')
|
|
327
|
+
.replace(/>/g, '\\u003e')
|
|
328
|
+
.replace(/&/g, '\\u0026');
|
|
329
|
+
} catch (_) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
318
333
|
|
|
319
334
|
function broadcast(type, payload = {}) {
|
|
320
335
|
if (isClosed) return;
|
|
@@ -375,6 +390,9 @@ function createDevReloadHub(config = {}) {
|
|
|
375
390
|
setOnTemplateChange: (fn) => {
|
|
376
391
|
onTemplateChangeCallback = fn;
|
|
377
392
|
},
|
|
393
|
+
setDevMetaProvider: (fn) => {
|
|
394
|
+
getDevMetaCallback = fn;
|
|
395
|
+
},
|
|
378
396
|
invalidateTemplateCache,
|
|
379
397
|
triggerTemplateReload: (filename, reason = 'template') => {
|
|
380
398
|
onTemplateFileChanged(filename, reason);
|
|
@@ -418,6 +436,12 @@ function createDevReloadHub(config = {}) {
|
|
|
418
436
|
// Client JS route handler
|
|
419
437
|
function handleScript(req, res) {
|
|
420
438
|
res.type('application/javascript');
|
|
439
|
+
const metaJson = resolveDevMetaJson();
|
|
440
|
+
if (metaJson) {
|
|
441
|
+
const metaPrefix = `(function(){if(typeof window!=='undefined'){window.__WEBSPRESSO_DEV_META__=${metaJson};if(window.__WEBSPRESSO_DEVTOOLS__&&typeof window.__WEBSPRESSO_DEVTOOLS__.setMeta==='function'){window.__WEBSPRESSO_DEVTOOLS__.setMeta(window.__WEBSPRESSO_DEV_META__);}}})();\n`;
|
|
442
|
+
res.send(metaPrefix + CLIENT_RUNTIME_SCRIPT);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
421
445
|
res.send(CLIENT_RUNTIME_SCRIPT);
|
|
422
446
|
}
|
|
423
447
|
|
|
@@ -429,7 +453,11 @@ function createDevReloadHub(config = {}) {
|
|
|
429
453
|
const origSend = res.send.bind(res);
|
|
430
454
|
res.send = function(body) {
|
|
431
455
|
if (typeof body === 'string' && body.includes('</body>')) {
|
|
432
|
-
const
|
|
456
|
+
const metaJson = resolveDevMetaJson();
|
|
457
|
+
const metaTag = metaJson
|
|
458
|
+
? `<script>window.__WEBSPRESSO_DEV_META__=${metaJson};</script>\n`
|
|
459
|
+
: '';
|
|
460
|
+
const scriptTag = `${metaTag}<script defer src="${CLIENT_SCRIPT_PATH}"></script>`;
|
|
433
461
|
if (!body.includes(CLIENT_SCRIPT_PATH)) {
|
|
434
462
|
body = body.replace('</body>', scriptTag + '\n</body>');
|
|
435
463
|
}
|
package/src/server.js
CHANGED
|
@@ -1517,6 +1517,34 @@ function createApp(options = {}) {
|
|
|
1517
1517
|
options: typeof devReloadOption === 'object' ? devReloadOption : {},
|
|
1518
1518
|
enabled: devReloadEnabled,
|
|
1519
1519
|
logger: logging ? console : null,
|
|
1520
|
+
getDevMeta: () => {
|
|
1521
|
+
const pluginsList =
|
|
1522
|
+
pluginManager && pluginManager.plugins instanceof Map
|
|
1523
|
+
? Array.from(pluginManager.plugins.keys())
|
|
1524
|
+
: [];
|
|
1525
|
+
const hasDb = Boolean(options.db || core.db || (app.locals && app.locals.db));
|
|
1526
|
+
const hasStudio = Boolean(
|
|
1527
|
+
hasDb ||
|
|
1528
|
+
options.studio ||
|
|
1529
|
+
pluginsList.includes('studio') ||
|
|
1530
|
+
pluginsList.includes('db-studio')
|
|
1531
|
+
);
|
|
1532
|
+
const hasAdmin = Boolean(
|
|
1533
|
+
pluginsList.includes('admin-panel') ||
|
|
1534
|
+
pluginsList.includes('adminPanel') ||
|
|
1535
|
+
pluginsList.includes('admin') ||
|
|
1536
|
+
app.adminApi ||
|
|
1537
|
+
(app.locals && app.locals.adminRegistry)
|
|
1538
|
+
);
|
|
1539
|
+
const adminPath = (app.locals && app.locals.adminBasePath) || '/_admin';
|
|
1540
|
+
return {
|
|
1541
|
+
hasDb,
|
|
1542
|
+
hasStudio,
|
|
1543
|
+
hasAdmin,
|
|
1544
|
+
adminPath,
|
|
1545
|
+
plugins: pluginsList,
|
|
1546
|
+
};
|
|
1547
|
+
},
|
|
1520
1548
|
});
|
|
1521
1549
|
|
|
1522
1550
|
if (devReloadHub) {
|