@protobase/cli 0.4.1 → 0.5.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/dist/build/config-bundle.js +9 -6
- package/dist/build/deploy-bundle.js +7 -3
- package/dist/build/entry-code.js +3 -2
- package/dist/build/ui-bundle.js +5 -2
- package/dist/build/ui-check.js +30 -0
- package/dist/build/vite-bundle.js +2 -2
- package/dist/dev/entry.js +1 -1
- package/dist/dev/project.js +15 -3
- package/dist/dev/run.js +12 -6
- package/dist/main.js +7 -3
- package/dist/package.js +1 -1
- package/dist/project/extensions.js +46 -0
- package/dist/project/project-ui.js +31 -10
- package/package.json +6 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { hostModuleIds } from "../serve/host-module-ids.js";
|
|
2
|
+
import { extensionImports } from "../project/extensions.js";
|
|
2
3
|
import { bundleEntryCode } from "./entry-code.js";
|
|
3
4
|
import { layoutCheck } from "./layout-check.js";
|
|
4
5
|
import { findPackageDir, packageName } from "./packages/package-dir.js";
|
|
@@ -51,19 +52,21 @@ const servedImports = (native) => {
|
|
|
51
52
|
}
|
|
52
53
|
};
|
|
53
54
|
};
|
|
54
|
-
const buildConfigBundle = async ({ projectDir, outFile, bun }) => {
|
|
55
|
+
const buildConfigBundle = async ({ projectDir, outFile, bun, extensions = [] }) => {
|
|
55
56
|
const file = path.join(projectDir, "protobase-bundle-entry.js");
|
|
56
57
|
const native = /* @__PURE__ */ new Map();
|
|
58
|
+
const plugins = [
|
|
59
|
+
layoutCheck(),
|
|
60
|
+
extensionImports(projectDir, extensions),
|
|
61
|
+
servedImports(native),
|
|
62
|
+
entry(file, bundleEntryCode(projectDir, extensions))
|
|
63
|
+
];
|
|
57
64
|
await viteBundle({
|
|
58
65
|
root: projectDir,
|
|
59
66
|
entry: file,
|
|
60
67
|
outFile,
|
|
61
68
|
bun,
|
|
62
|
-
plugins
|
|
63
|
-
layoutCheck(),
|
|
64
|
-
servedImports(native),
|
|
65
|
-
entry(file, bundleEntryCode(projectDir))
|
|
66
|
-
]
|
|
69
|
+
plugins
|
|
67
70
|
});
|
|
68
71
|
return [...native.values()];
|
|
69
72
|
};
|
|
@@ -3,6 +3,7 @@ import { buildConfigBundle } from "./config-bundle.js";
|
|
|
3
3
|
import { protobaseVersion } from "../version/version.js";
|
|
4
4
|
import { copyPackages } from "./packages/copy-packages.js";
|
|
5
5
|
import { buildUiBundle } from "./ui-bundle.js";
|
|
6
|
+
import { checkUiMerge } from "./ui-check.js";
|
|
6
7
|
import { existsSync } from "node:fs";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -17,17 +18,20 @@ const removeOwnNodeModules = async (outDir) => {
|
|
|
17
18
|
force: true
|
|
18
19
|
});
|
|
19
20
|
};
|
|
20
|
-
const buildDeployBundle = async ({ projectDir, outDir, bun }) => {
|
|
21
|
+
const buildDeployBundle = async ({ projectDir, outDir, bun, extensions = [] }) => {
|
|
21
22
|
await removeOwnNodeModules(outDir);
|
|
23
|
+
if (extensions.some((extension) => extension.ui)) await checkUiMerge(projectDir, extensions);
|
|
22
24
|
const native = await buildConfigBundle({
|
|
23
25
|
projectDir,
|
|
24
26
|
outFile: path.join(outDir, bundleLayout.server),
|
|
25
|
-
bun
|
|
27
|
+
bun,
|
|
28
|
+
extensions
|
|
26
29
|
});
|
|
27
30
|
const packages = native.length > 0 ? await copyPackages(native, path.join(outDir, bundleNodeModules)) : [];
|
|
28
31
|
await buildUiBundle({
|
|
29
32
|
outDir: path.join(outDir, bundleLayout.public),
|
|
30
|
-
projectDir
|
|
33
|
+
projectDir,
|
|
34
|
+
extensions
|
|
31
35
|
});
|
|
32
36
|
const manifest = {
|
|
33
37
|
version: 2,
|
package/dist/build/entry-code.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
//#region src/build/entry-code.ts
|
|
5
5
|
const conventionModule = moduleFile("../project/convention", import.meta.url);
|
|
6
6
|
const uiDirs = (configDir) => readdirSync(configDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync(path.join(configDir, entry.name, "ui.ts"))).map((entry) => entry.name).sort();
|
|
7
|
-
const bundleEntryCode = (projectDir) => {
|
|
7
|
+
const bundleEntryCode = (projectDir, extensions = []) => {
|
|
8
8
|
const custom = path.join(projectDir, "protobase.config.ts");
|
|
9
9
|
const configDir = path.join(projectDir, "config");
|
|
10
10
|
const index = path.join(configDir, "index.ts");
|
|
@@ -16,7 +16,8 @@ const bundleEntryCode = (projectDir) => {
|
|
|
16
16
|
const dirs = uiDirs(configDir);
|
|
17
17
|
lines.push(`import { conventionConfig } from ${JSON.stringify(conventionModule)}`, `import * as index from ${JSON.stringify(index)}`, ...dirs.map((dir, i) => `import * as ui${i} from ${JSON.stringify(path.join(configDir, dir, "ui.ts"))}`), `const convention = conventionConfig(index, [${dirs.map((dir, i) => `[${JSON.stringify(dir)}, ui${i}]`).join(", ")}])`);
|
|
18
18
|
} else lines.push("const convention = undefined");
|
|
19
|
-
|
|
19
|
+
const configs = extensions.flatMap((extension) => extension.config ? [extension.config] : []);
|
|
20
|
+
lines.push(`import { mergeConfig } from '@protobase/server'`, ...configs.map((file, i) => `import * as extension${i} from ${JSON.stringify(file)}`), "const named = (config, file) => ({ ...config, name: config.name ?? file })", `export default mergeConfig(${[...configs.map((file, i) => `named(extension${i}.default ?? {}, ${JSON.stringify(file)})`), `named({ ...project, config: project.config ?? convention }, ${JSON.stringify(hasCustom ? "protobase.config.ts" : "config/index.ts")})`].join(", ")})`);
|
|
20
21
|
return `${lines.join("\n")}\n`;
|
|
21
22
|
};
|
|
22
23
|
//#endregion
|
package/dist/build/ui-bundle.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { adminAppDir } from "../project/admin-app.js";
|
|
2
|
+
import { extensionImports, extensionStyles } from "../project/extensions.js";
|
|
2
3
|
import { projectUiPlugin } from "../project/project-ui.js";
|
|
3
4
|
import { build } from "vite";
|
|
4
5
|
import tailwindcss from "@tailwindcss/vite";
|
|
5
6
|
import react from "@vitejs/plugin-react";
|
|
6
7
|
//#region src/build/ui-bundle.ts
|
|
7
|
-
const buildUiBundle = async ({ outDir, projectDir }) => {
|
|
8
|
+
const buildUiBundle = async ({ outDir, projectDir, extensions = [] }) => {
|
|
8
9
|
const nodeEnv = process.env.NODE_ENV;
|
|
9
10
|
process.env.NODE_ENV = "production";
|
|
10
11
|
await build({
|
|
@@ -15,7 +16,9 @@ const buildUiBundle = async ({ outDir, projectDir }) => {
|
|
|
15
16
|
publicDir: false,
|
|
16
17
|
logLevel: "warn",
|
|
17
18
|
plugins: [
|
|
18
|
-
projectUiPlugin(projectDir),
|
|
19
|
+
projectUiPlugin(projectDir, extensions),
|
|
20
|
+
...projectDir === void 0 ? [] : [extensionImports(projectDir, extensions)],
|
|
21
|
+
extensionStyles(extensions),
|
|
19
22
|
react(),
|
|
20
23
|
tailwindcss()
|
|
21
24
|
],
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { extensionImports } from "../project/extensions.js";
|
|
2
|
+
import { viteBundle } from "./vite-bundle.js";
|
|
3
|
+
import { mergedUiFile, projectUiPlugin } from "../project/project-ui.js";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
//#region src/build/ui-check.ts
|
|
9
|
+
const checkUiMerge = async (projectDir, extensions) => {
|
|
10
|
+
const scratch = await mkdtemp(path.join(tmpdir(), "protobase-ui-check-"));
|
|
11
|
+
const outFile = path.join(scratch, "ui-check.js");
|
|
12
|
+
try {
|
|
13
|
+
const plugins = [projectUiPlugin(projectDir, extensions), extensionImports(projectDir, extensions)];
|
|
14
|
+
await viteBundle({
|
|
15
|
+
root: projectDir,
|
|
16
|
+
entry: mergedUiFile(projectDir),
|
|
17
|
+
outFile,
|
|
18
|
+
plugins,
|
|
19
|
+
logLevel: "error"
|
|
20
|
+
});
|
|
21
|
+
await import(pathToFileURL(outFile).href);
|
|
22
|
+
} finally {
|
|
23
|
+
await rm(scratch, {
|
|
24
|
+
recursive: true,
|
|
25
|
+
force: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { checkUiMerge };
|
|
@@ -4,12 +4,12 @@ import { mkdtemp, rm } from "node:fs/promises";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { build } from "vite";
|
|
6
6
|
//#region src/build/vite-bundle.ts
|
|
7
|
-
const viteBuild = async ({ root, entry, outFile, plugins }) => {
|
|
7
|
+
const viteBuild = async ({ root, entry, outFile, plugins, logLevel = "warn" }) => {
|
|
8
8
|
await build({
|
|
9
9
|
configFile: false,
|
|
10
10
|
root,
|
|
11
11
|
publicDir: false,
|
|
12
|
-
logLevel
|
|
12
|
+
logLevel,
|
|
13
13
|
plugins,
|
|
14
14
|
oxc: { jsx: { development: false } },
|
|
15
15
|
ssr: {
|
package/dist/dev/entry.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Hono } from "hono";
|
|
|
3
3
|
import { configExports, createAdmin, hasUsers } from "@protobase/server";
|
|
4
4
|
//#region src/dev/entry.ts
|
|
5
5
|
const projectDir = process.env.PROTOBASE_PROJECT;
|
|
6
|
-
const project = await loadProject(projectDir);
|
|
6
|
+
const project = await loadProject(projectDir, JSON.parse(process.env.PROTOBASE_EXTENSIONS ?? "[]"));
|
|
7
7
|
const db = projectDb(project, projectDir);
|
|
8
8
|
const { resources, views, pages, userMenu } = configExports(project.exports);
|
|
9
9
|
if (!project.authenticate) throw new Error("No authenticator: export `authenticate` (and `auth`) from protobase.config.ts; see https://docs.protobase.net/reference/auth/");
|
package/dist/dev/project.js
CHANGED
|
@@ -3,6 +3,7 @@ import { sharedDb } from "./create-db.js";
|
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { readdir } from "node:fs/promises";
|
|
6
|
+
import { mergeConfig } from "@protobase/server";
|
|
6
7
|
//#region src/dev/project.ts
|
|
7
8
|
const load = (file) => import(
|
|
8
9
|
/* @vite-ignore */
|
|
@@ -21,12 +22,23 @@ const conventionExports = async (projectDir) => {
|
|
|
21
22
|
}
|
|
22
23
|
return conventionConfig(indexExports, uiModules);
|
|
23
24
|
};
|
|
24
|
-
const
|
|
25
|
+
const named = (config, file) => ({
|
|
26
|
+
...config,
|
|
27
|
+
name: config.name ?? file
|
|
28
|
+
});
|
|
29
|
+
const loadProject = async (projectDir, extensions = []) => {
|
|
25
30
|
const file = path.join(projectDir, "protobase.config.ts");
|
|
26
31
|
const custom = existsSync(file) ? (await load(file)).default ?? {} : {};
|
|
27
|
-
|
|
32
|
+
const extended = [];
|
|
33
|
+
for (const extension of extensions) if (extension.config) extended.push(named((await load(extension.config)).default ?? {}, extension.config));
|
|
34
|
+
const own = named({
|
|
28
35
|
...custom,
|
|
29
|
-
|
|
36
|
+
config: custom.config ?? await conventionExports(projectDir)
|
|
37
|
+
}, existsSync(file) ? "protobase.config.ts" : "config/index.ts");
|
|
38
|
+
const project = mergeConfig(...extended, own);
|
|
39
|
+
return {
|
|
40
|
+
...project,
|
|
41
|
+
exports: project.config ?? {}
|
|
30
42
|
};
|
|
31
43
|
};
|
|
32
44
|
const projectDb = (project, projectDir) => {
|
package/dist/dev/run.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { adminAppDir, adminAppMain, uiPackageDir } from "../project/admin-app.js";
|
|
2
|
+
import { extensionImports, extensionStyles } from "../project/extensions.js";
|
|
1
3
|
import { moduleFile } from "../module-file.js";
|
|
2
4
|
import { layoutCheck } from "../build/layout-check.js";
|
|
3
|
-
import { adminAppDir, adminAppMain, uiPackageDir } from "../project/admin-app.js";
|
|
4
5
|
import { projectUiPlugin } from "../project/project-ui.js";
|
|
5
6
|
import { loadNearestEnvFile } from "./env.js";
|
|
6
7
|
import { checkDatabase } from "./check-database.js";
|
|
@@ -16,10 +17,10 @@ const requireAppFiles = (appDir) => {
|
|
|
16
17
|
const missing = ["index.html", adminAppMain].filter((file) => !existsSync(path.join(appDir, file)));
|
|
17
18
|
if (missing.length > 0) throw new Error(`The admin app is missing ${missing.join(", ")} in ${appDir}; \`protobase dev\` serves the app from there`);
|
|
18
19
|
};
|
|
19
|
-
const reloadOnProjectChange = (
|
|
20
|
+
const reloadOnProjectChange = (dirs) => ({
|
|
20
21
|
name: "protobase-project-reload",
|
|
21
22
|
handleHotUpdate: ({ file, server }) => {
|
|
22
|
-
if (file.startsWith(`${
|
|
23
|
+
if (dirs.some((dir) => file.startsWith(`${dir}${path.sep}`)) && !file.includes(`${path.sep}node_modules${path.sep}`)) server.environments.ssr.moduleGraph.invalidateAll();
|
|
23
24
|
}
|
|
24
25
|
});
|
|
25
26
|
const appDependencies = [
|
|
@@ -42,7 +43,9 @@ const runDev = async (options, out) => {
|
|
|
42
43
|
const appDir = options.appDir ?? adminAppDir;
|
|
43
44
|
requireAppFiles(appDir);
|
|
44
45
|
await checkDatabase(url, options.projectDir);
|
|
46
|
+
const extensions = options.extensions ?? [];
|
|
45
47
|
process.env.PROTOBASE_PROJECT = options.projectDir;
|
|
48
|
+
process.env.PROTOBASE_EXTENSIONS = JSON.stringify(extensions);
|
|
46
49
|
process.env.PROTOBASE_DATABASE_URL = url;
|
|
47
50
|
const server = await createServer({
|
|
48
51
|
root: appDir,
|
|
@@ -56,12 +59,15 @@ const runDev = async (options, out) => {
|
|
|
56
59
|
fs: { allow: [
|
|
57
60
|
searchForWorkspaceRoot(options.projectDir),
|
|
58
61
|
uiPackageDir,
|
|
59
|
-
options.projectDir
|
|
62
|
+
options.projectDir,
|
|
63
|
+
...extensions.map((extension) => extension.dir)
|
|
60
64
|
] }
|
|
61
65
|
},
|
|
62
66
|
plugins: [
|
|
63
|
-
reloadOnProjectChange(options.projectDir),
|
|
64
|
-
projectUiPlugin(options.projectDir),
|
|
67
|
+
reloadOnProjectChange([options.projectDir, ...extensions.map((extension) => extension.dir)]),
|
|
68
|
+
projectUiPlugin(options.projectDir, extensions),
|
|
69
|
+
extensionImports(options.projectDir, extensions),
|
|
70
|
+
extensionStyles(extensions),
|
|
65
71
|
layoutCheck(),
|
|
66
72
|
react(),
|
|
67
73
|
tailwindcss(),
|
package/dist/main.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { manifestFile } from "./bundle/manifest.js";
|
|
2
|
+
import { resolveExtensions } from "./project/extensions.js";
|
|
2
3
|
import { buildDeployBundle } from "./build/deploy-bundle.js";
|
|
3
4
|
import { buildServeRuntime } from "./build/serve-runtime.js";
|
|
4
5
|
import { resolveConnectionUrl } from "./db/connect.js";
|
|
@@ -46,8 +47,9 @@ program.command("doctor").description("Check a config folder against the databas
|
|
|
46
47
|
}, out);
|
|
47
48
|
if (errors > 0) process.exitCode = 1;
|
|
48
49
|
});
|
|
49
|
-
program.command("dev").description("Serve the admin app and API for the project in the current directory").option("--port <port>", "port for the app and API", "5173").option("--env <variable>", "environment variable holding the database URL", "DATABASE_URL").option("--allowed-hosts <hosts>", "extra Host headers to accept, comma separated; a leading dot matches subdomains (.trycloudflare.com)").option("--cache-dir <dir>", "Vite's dependency cache, so parallel dev servers do not share one").action(async (opts) => {
|
|
50
|
+
program.command("dev").description("Serve the admin app and API for the project in the current directory").option("--port <port>", "port for the app and API", "5173").option("--env <variable>", "environment variable holding the database URL", "DATABASE_URL").option("--allowed-hosts <hosts>", "extra Host headers to accept, comma separated; a leading dot matches subdomains (.trycloudflare.com)").option("--cache-dir <dir>", "Vite's dependency cache, so parallel dev servers do not share one").option("--extend <dir>", "merge the protobase.config.ts and protobase.ui.tsx in this folder before the project's, repeatable", collect, []).action(async (opts) => {
|
|
50
51
|
const server = await runDev({
|
|
52
|
+
extensions: resolveExtensions(opts.extend),
|
|
51
53
|
port: Number(opts.port),
|
|
52
54
|
envVar: opts.env,
|
|
53
55
|
projectDir: process.cwd(),
|
|
@@ -57,11 +59,13 @@ program.command("dev").description("Serve the admin app and API for the project
|
|
|
57
59
|
process.once("SIGINT", () => void server.close().then(() => process.exit(0)));
|
|
58
60
|
process.once("SIGTERM", () => void server.close().then(() => process.exit(0)));
|
|
59
61
|
});
|
|
60
|
-
program.command("build").description("Build the deploy bundle of the project in the current directory: its config module, admin UI and manifest").option("--out <dir>", "output folder", "dist").option("--bun", "have Bun's bundler write the config module, for a bundle only Bun serves (needs bun on PATH)", false).action(async (opts) => {
|
|
62
|
+
program.command("build").description("Build the deploy bundle of the project in the current directory: its config module, admin UI and manifest").option("--out <dir>", "output folder", "dist").option("--bun", "have Bun's bundler write the config module, for a bundle only Bun serves (needs bun on PATH)", false).option("--extend <dir>", "merge the protobase.config.ts and protobase.ui.tsx in this folder before the project's, repeatable", collect, []).action(async (opts) => {
|
|
63
|
+
const extensions = resolveExtensions(opts.extend);
|
|
61
64
|
const manifest = await buildDeployBundle({
|
|
62
65
|
projectDir: process.cwd(),
|
|
63
66
|
outDir: path.resolve(opts.out),
|
|
64
|
-
bun: opts.bun
|
|
67
|
+
bun: opts.bun,
|
|
68
|
+
extensions
|
|
65
69
|
});
|
|
66
70
|
const packages = manifest.nodeModules ? `, ${manifest.nodeModules}/` : "";
|
|
67
71
|
out(`built ${opts.out}: ${manifest.server}${packages}, ${manifest.public}/ and ${manifestFile} (api ${manifest.api.join(", ")})\n`);
|
package/dist/package.js
CHANGED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { adminAppDir } from "./admin-app.js";
|
|
2
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/project/extensions.ts
|
|
5
|
+
const extensionConfigFile = "protobase.config.ts";
|
|
6
|
+
const extensionUiFile = "protobase.ui.tsx";
|
|
7
|
+
const resolveExtensions = (dirs) => dirs.map((given) => {
|
|
8
|
+
const resolved = path.resolve(given);
|
|
9
|
+
if (!existsSync(resolved)) throw new Error(`--extend ${given}: there is no folder ${resolved}`);
|
|
10
|
+
const dir = realpathSync(resolved);
|
|
11
|
+
const config = path.join(dir, extensionConfigFile);
|
|
12
|
+
const ui = path.join(dir, extensionUiFile);
|
|
13
|
+
const extension = {
|
|
14
|
+
dir,
|
|
15
|
+
...existsSync(config) && { config },
|
|
16
|
+
...existsSync(ui) && { ui }
|
|
17
|
+
};
|
|
18
|
+
if (!extension.config && !extension.ui) throw new Error(`--extend ${given}: ${dir} has neither ${extensionConfigFile} nor ${extensionUiFile}`);
|
|
19
|
+
return extension;
|
|
20
|
+
});
|
|
21
|
+
const shared = /^(@protobase\/[^/]+|react|react-dom)(\/.*)?$/;
|
|
22
|
+
const inside = (file, dir) => file === dir || file.startsWith(`${dir}${path.sep}`);
|
|
23
|
+
const extensionImports = (projectDir, extensions) => ({
|
|
24
|
+
name: "protobase-extension-imports",
|
|
25
|
+
enforce: "pre",
|
|
26
|
+
async resolveId(id, importer, options) {
|
|
27
|
+
if (!importer || !shared.test(id)) return void 0;
|
|
28
|
+
const from = importer.replace(/^\0/, "").replace(/\?.*$/, "");
|
|
29
|
+
if (!extensions.some((extension) => inside(from, extension.dir))) return void 0;
|
|
30
|
+
return this.resolve(id, path.join(projectDir, extensionConfigFile), {
|
|
31
|
+
...options,
|
|
32
|
+
skipSelf: true
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const appStyles = path.join(path.dirname(adminAppDir), "styles.css");
|
|
37
|
+
const extensionStyles = (extensions) => ({
|
|
38
|
+
name: "protobase-extension-styles",
|
|
39
|
+
enforce: "pre",
|
|
40
|
+
transform: (code, id) => {
|
|
41
|
+
if (extensions.length === 0 || id.replace(/\?.*$/, "") !== appStyles) return void 0;
|
|
42
|
+
return `${code}\n${extensions.map((extension) => `@source ${JSON.stringify(extension.dir)};`).join("\n")}\n`;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
//#endregion
|
|
46
|
+
export { extensionConfigFile, extensionImports, extensionStyles, extensionUiFile, resolveExtensions };
|
|
@@ -4,14 +4,35 @@ import path from "node:path";
|
|
|
4
4
|
const projectUiId = "virtual:protobase-project-ui";
|
|
5
5
|
const empty = `\0${projectUiId}`;
|
|
6
6
|
const projectUiFile = (projectDir) => path.join(projectDir, "protobase.ui.tsx");
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
7
|
+
const mergedUiFile = (projectDir) => path.join(projectDir, "protobase-ui-entry.js");
|
|
8
|
+
const mergedUiCode = (projectDir, extensions) => {
|
|
9
|
+
const files = extensions.flatMap((extension) => extension.ui ? [extension.ui] : []);
|
|
10
|
+
const own = projectUiFile(projectDir);
|
|
11
|
+
const hasOwn = existsSync(own);
|
|
12
|
+
return [
|
|
13
|
+
`import { mergeUi } from '@protobase/ui'`,
|
|
14
|
+
...files.map((file, i) => `import * as extension${i} from ${JSON.stringify(file)}`),
|
|
15
|
+
...hasOwn ? [`import * as project from ${JSON.stringify(own)}`] : [],
|
|
16
|
+
"const named = (ui, file) => ({ ...ui, name: ui.name ?? file })",
|
|
17
|
+
`export default mergeUi(${[...files.map((file, i) => `named(extension${i}.default ?? {}, ${JSON.stringify(file)})`), ...hasOwn ? [`named(project.default ?? {}, "protobase.ui.tsx")`] : []].join(", ")})`,
|
|
18
|
+
""
|
|
19
|
+
].join("\n");
|
|
20
|
+
};
|
|
21
|
+
const projectUiPlugin = (projectDir, extensions = []) => {
|
|
22
|
+
const merged = projectDir !== void 0 && extensions.some((extension) => extension.ui) ? `\0${mergedUiFile(projectDir)}` : void 0;
|
|
23
|
+
return {
|
|
24
|
+
name: "protobase-project-ui",
|
|
25
|
+
resolveId: (id) => {
|
|
26
|
+
if (merged && (id === "virtual:protobase-project-ui" || id === mergedUiFile(projectDir))) return merged;
|
|
27
|
+
if (id !== "virtual:protobase-project-ui") return void 0;
|
|
28
|
+
const file = projectDir === void 0 ? void 0 : projectUiFile(projectDir);
|
|
29
|
+
return file && existsSync(file) ? file : empty;
|
|
30
|
+
},
|
|
31
|
+
load: (id) => {
|
|
32
|
+
if (id === empty) return "export default {}\n";
|
|
33
|
+
if (id === merged) return mergedUiCode(projectDir, extensions);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
};
|
|
16
37
|
//#endregion
|
|
17
|
-
export { projectUiFile, projectUiId, projectUiPlugin };
|
|
38
|
+
export { mergedUiCode, mergedUiFile, projectUiFile, projectUiId, projectUiPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The protobase command: scaffold, doctor, dev, build and serve a Protobase project.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@hono/node-server": "^2.1.3",
|
|
36
36
|
"@hono/vite-dev-server": "^0.26.1",
|
|
37
|
-
"@protobase/layout": "0.
|
|
38
|
-
"@protobase/query": "0.
|
|
39
|
-
"@protobase/schema": "0.
|
|
40
|
-
"@protobase/server": "0.
|
|
41
|
-
"@protobase/ui": "0.
|
|
37
|
+
"@protobase/layout": "0.5.0",
|
|
38
|
+
"@protobase/query": "0.5.0",
|
|
39
|
+
"@protobase/schema": "0.5.0",
|
|
40
|
+
"@protobase/server": "0.5.0",
|
|
41
|
+
"@protobase/ui": "0.5.0",
|
|
42
42
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
43
43
|
"@tailwindcss/vite": "^4.3.3",
|
|
44
44
|
"@vitejs/plugin-react": "^6.1.2",
|