diff --git a/README.md b/README.md index 8f2fb53..1ab5222 100644 --- a/README.md +++ b/README.md @@ -255,12 +255,11 @@ Options: -h, --help display help for command Commands: - dev [options] [src] Run the development server + dev [options] [src] [dest] Run the development server build [options] [src] [dest] Build the project workspace|ws [options] [command] [src] [dest] Work with multiple apps init [options] [path] Initialize a new project clone [account] [dest] Clone a SocialDB repository - pull [account] Pull updates from a SocialDB repository deploy [options] [appName] Deploy the project upload [string] Upload data to SocialDB (not implemented) help [command] display help for command diff --git a/lib/cli.ts b/lib/cli.ts index f53cc39..245f9d2 100644 --- a/lib/cli.ts +++ b/lib/cli.ts @@ -23,6 +23,7 @@ async function run() { .command("dev") .description("Run the development server") .argument("[src]", "path to the app source code", ".") + .argument("[dest]", "destination path", "build") .option("-n, --network ", "network to build for", "mainnet") .option("-l, --loglevel ", "log level (ERROR, WARN, INFO, DEV, BUILD, DEBUG)", "DEV") .option("-p, --port ", "Port to run the server on", "8080") @@ -30,9 +31,9 @@ async function run() { .option("--no-gateway", "Disable the gateway") .option("--no-hot", "Disable hot reloading") .option("--no-open", "Disable opening the browser") - .action((src, opts) => { + .action((src, dest, opts) => { global.log = new Logger(LogLevel[opts.loglevel.toUpperCase() as keyof typeof LogLevel]); - dev(src, opts).catch((e: Error) => { + dev(src, dest, opts).catch((e: Error) => { log.error(e.stack || e.message); }) }); @@ -42,11 +43,10 @@ async function run() { .command("build") .description("Build the project") .argument("[src]", "path to the app source code", ".") - .argument("[dest]", "destination path") + .argument("[dest]", "destination path", "build") .option("-n, --network ", "network to build for", "mainnet") .option("-l, --loglevel ", "log level (ERROR, WARN, INFO, DEV, BUILD, DEBUG)", "BUILD") .action(async (src, dest, opts) => { - dest = dest || path.join(src, "dist") global.log = new Logger(LogLevel[opts.loglevel.toUpperCase() as keyof typeof LogLevel]); await buildApp(src, dest, opts.network).catch((e: Error) => { log.error(e.stack || e.message); @@ -59,7 +59,7 @@ async function run() { .description("Work with multiple apps") .argument("[command]", "command to run") .argument("[src]", "path to the workspace", ".") - .argument("[dest]", "destination path") + .argument("[dest]", "destination path", "build") .option("-n, --network ", "network to build for", "mainnet") .option("-l, --loglevel ", "log level (ERROR, WARN, INFO, DEV, BUILD, DEBUG)") .option("-p, --port ", "Port to run the server on", "8080") @@ -68,7 +68,6 @@ async function run() { .option("--no-hot", "Disable hot reloading") .option("--no-open", "Disable opening the browser") .action(async (command, src, dest, opts) => { - dest = dest || path.join(src, "dist") if (command === "build") { global.log = new Logger(LogLevel?.[opts?.loglevel?.toUpperCase() as keyof typeof LogLevel] || LogLevel.BUILD); await buildWorkspace(src, dest, opts.network).catch((e: Error) => { @@ -76,7 +75,7 @@ async function run() { }); } else if (command === "dev") { global.log = new Logger(LogLevel?.[opts?.loglevel?.toUpperCase() as keyof typeof LogLevel] || LogLevel.DEV); - await devWorkspace(src, opts).catch((e: Error) => { + await devWorkspace(src, dest, opts).catch((e: Error) => { log.error(e.stack || e.message); }); } else { @@ -107,15 +106,6 @@ async function run() { cloneRepository(account, dest); }); - program - .command("pull") - .description("Pull updates from a SocialDB repository") - .argument("[account]", "accountId") - .action(async (account: string | undefined) => { - console.log("not yet supported"); - // pullRepository(account); - }); - program .command("deploy") .description("Deploy the project") diff --git a/lib/dev.ts b/lib/dev.ts index 98ede93..085202b 100644 --- a/lib/dev.ts +++ b/lib/dev.ts @@ -1,9 +1,9 @@ import { readJson, writeJson } from "fs-extra"; +import { Gaze } from "gaze"; import path from "path"; import { Server as IoServer } from "socket.io"; -import { Gaze } from "gaze"; import { buildApp } from "./build"; -import { BaseConfig, loadConfig, readConfig } from "./config"; +import { BaseConfig, loadConfig } from "./config"; import { startDevServer } from "./server"; import { startSocket } from "./socket"; import { Network } from "./types"; @@ -11,8 +11,6 @@ import { loopThroughFiles, readFile } from "./utils/fs"; import { mergeDeep, substractDeep } from "./utils/objects"; import { startFileWatcher } from "./watcher"; -const DEV_DIST_FOLDER = "build"; - var appSrcs = [], appDists = []; var appDevJsons = []; var appDevJsonPath = "bos-loader.json"; @@ -33,12 +31,13 @@ export type DevOptions = { * Build and watch app according to bos.config.json * * @param src path to app source code + * @param dest path to build output * @param opts DevOptions */ -export async function dev(src: string, opts: DevOptions) { - const dist = path.join(src, DEV_DIST_FOLDER); +export async function dev(src: string, dest: string, opts: DevOptions) { + const dist = path.join(src, dest); const devJsonPath = path.join(dist, "bos-loader.json"); - + // Build the app for the first time const config = await loadConfig(src, opts.network); let devJson = await generateApp(src, dist, config, opts); @@ -71,12 +70,12 @@ export async function dev(src: string, opts: DevOptions) { // Watch for changes in the src folder and rebuild the app on changes fileWatcher = startFileWatcher([ - path.join(src, "widget/**/*"), - path.join(src, "module/**/*"), - path.join(src, "ipfs/**/*"), - path.join(src, "bos.config.json"), - path.join(src, "aliases.json") - ], + path.join(src, "widget/**/*"), + path.join(src, "module/**/*"), + path.join(src, "ipfs/**/*"), + path.join(src, "bos.config.json"), + path.join(src, "aliases.json") + ], fileWatcherCallback ); } @@ -86,15 +85,16 @@ export async function dev(src: string, opts: DevOptions) { * * @param root bos.workspace.json root directory * @param srcs apps to build and watch + * @param dest path to build output * @param opts DevOptions */ -export async function devMulti(root: string, srcs: string[], opts: DevOptions) { - const dist = path.join(root, DEV_DIST_FOLDER); +export async function devMulti(root: string, srcs: string[], dest: string, opts: DevOptions) { + const dist = path.join(root, dest); const devJsonPath = path.join(dist, "bos-loader.json"); - + // Build all apps for the first time and merge devJson let appDevJson = { components: {}, data: {} }; - + for (const src of srcs) { const config = await loadConfig(src, opts.network); const devJson = await generateApp(src, path.join(dist, path.relative(root, src)), config, opts); @@ -138,7 +138,7 @@ export async function devMulti(root: string, srcs: string[], opts: DevOptions) { export async function addApps(srcs: string[], dists: string[]) { let appDevJson = await readJson(appDevJsonPath, { throws: false }); - for (let i = 0; i < srcs.length; i ++) { + for (let i = 0; i < srcs.length; i++) { const src = srcs[i]; const dist = dists[i]; @@ -157,19 +157,19 @@ export async function addApps(srcs: string[], dists: string[]) { if (fileWatcher) { fileWatcher.add(srcs.map((src) => [ - path.join(src, "widget/**/*"), - path.join(src, "module/**/*"), - path.join(src, "ipfs/**/*"), - path.join(src, "bos.config.json"), - path.join(src, "aliases.json") - ]).flat() + path.join(src, "widget/**/*"), + path.join(src, "module/**/*"), + path.join(src, "ipfs/**/*"), + path.join(src, "bos.config.json"), + path.join(src, "aliases.json") + ]).flat() ); } } async function fileWatcherCallback(action: string, file: string) { let appDevJson = await readJson(appDevJsonPath, { throws: false }); - + // find which app this file belongs to const index = appSrcs.findIndex((src) => file.includes(path.resolve(src))); if (index == -1) { @@ -178,7 +178,7 @@ async function fileWatcherCallback(action: string, file: string) { const src = appSrcs[index]; const dist = appDists[index]; - + let devJson = appDevJsons[index]; substractDeep(appDevJson, devJson); @@ -186,7 +186,7 @@ async function fileWatcherCallback(action: string, file: string) { log.info(`[${path.relative(src, file)}] changed: rebuilding app...`, LogLevels.DEV); const config = await loadConfig(src, appDevOptions.network); devJson = await generateApp(src, dist, config, appDevOptions); - + // write to redirect map await writeJson(appDevJsonPath, mergeDeep(appDevJson, devJson)); appDevJsons[index] = devJson; diff --git a/lib/workspace.ts b/lib/workspace.ts index ea7fce2..25572ca 100644 --- a/lib/workspace.ts +++ b/lib/workspace.ts @@ -24,14 +24,14 @@ export async function buildWorkspace(src: string, dest: string, network: string loading.finish(`Built workspace ${path.resolve(src)} to ${path.resolve(dest)}`); }; -export async function devWorkspace(src: string, devOpts: DevOptions): Promise { +export async function devWorkspace(src: string, dest: string, devOpts: DevOptions): Promise { const loading = log.loading(`Starting workspace ${src}`, LogLevels.BUILD); const { apps } = await readWorkspace(src); log.info(`Found ${apps.length} apps\n`); - await devMulti(src, apps.map((app) => path.join(src, app)), devOpts); + await devMulti(src, apps.map((app) => path.join(src, app)), dest, devOpts); loading.finish(`Started workspace ${path.resolve(src)}`); } diff --git a/tests/unit/dev.ts b/tests/unit/dev.ts index d380e09..96b7a3d 100644 --- a/tests/unit/dev.ts +++ b/tests/unit/dev.ts @@ -53,12 +53,12 @@ describe("dev", () => { }); it("should call loadConfig with src and opts.network", async () => { - await dev(mockSrc, mockOpts); + await dev(mockSrc, "build", mockOpts); expect(loadConfig).toHaveBeenCalledWith(mockSrc, mockOpts.network); }); it("should call generateApp with src, dist, config, opts, and devJsonPath", async () => { - await dev(mockSrc, mockOpts); + await dev(mockSrc, "build", mockOpts); const mockDist = path.join(mockSrc, 'build'); const mockDevJsonPath = path.join(mockSrc, 'build', 'bos-loader.json'); expect(startDevServer).toHaveBeenCalledWith([mockSrc], [mockDist], mockDevJsonPath, mockOpts); @@ -66,18 +66,18 @@ describe("dev", () => { it("should start the socket server if hot reload is enabled", async () => { const mockOpts: DevOptions = { hot: true }; - await dev(mockSrc, mockOpts); + await dev(mockSrc, "build", mockOpts); expect(startSocket).toHaveBeenCalled(); }); it("should not start the socket server if hot reload is disabled", async () => { const mockOpts: DevOptions = { hot: false }; - await dev(mockSrc, mockOpts); + await dev(mockSrc, "build", mockOpts); expect(startSocket).not.toHaveBeenCalled(); }); it("should call startFileWatcher with correct watch paths", async () => { - await dev(mockSrc, mockOpts); + await dev(mockSrc, "build", mockOpts); const expectedWatchPaths = [ path.join(mockSrc, 'widget', '**', '*'), path.join(mockSrc, 'module', '**', '*'), @@ -92,7 +92,7 @@ describe("dev", () => { const mockedGazeAdd = jest.spyOn(Gaze.prototype, 'add'); const mockOpts: DevOptions = { hot: false }; - await dev(mockSrc, mockOpts); + await dev(mockSrc, "build", mockOpts); const mockSrc2 = "/app_example_2"; vol.fromJSON(app_example_2, mockSrc2);