diff --git a/src/commands/dev.ts b/src/commands/dev.ts index da3eb9e8..c7601ac3 100644 --- a/src/commands/dev.ts +++ b/src/commands/dev.ts @@ -2,9 +2,11 @@ import {red} from 'kleur'; import {logHelpDevBuild} from '../help/dev.build.help'; import {logHelpDevEject} from '../help/dev.eject.help'; import {logHelpDev} from '../help/dev.help'; +import {logHelpDevStart} from '../help/dev.start.help'; import {build} from '../services/build/build.services'; -import {start, stop} from '../services/docker.services'; import {eject} from '../services/eject/eject.services'; +import {stop} from '../services/start/docker.services'; +import {start} from '../services/start/start.services'; export const dev = async (args?: string[]) => { const [subCommand] = args ?? []; @@ -17,7 +19,7 @@ export const dev = async (args?: string[]) => { await build(args); break; case 'start': - await start(); + await start(args); break; case 'stop': await stop(); @@ -32,6 +34,9 @@ export const helpDev = (args?: string[]) => { const [subCommand] = args ?? []; switch (subCommand) { + case 'start': + logHelpDevStart(args); + break; case 'build': logHelpDevBuild(args); break; diff --git a/src/constants/help.constants.ts b/src/constants/help.constants.ts index a910423a..41e19912 100644 --- a/src/constants/help.constants.ts +++ b/src/constants/help.constants.ts @@ -1,3 +1,5 @@ +import {magenta} from 'kleur'; + export const CLEAR_DESCRIPTION = 'Clear existing dapp code by removing JavaScript, HTML, CSS, and other files from your satellite.'; export const CONFIG_DESCRIPTION = 'Apply configuration to satellite.'; @@ -19,6 +21,12 @@ export const VERSION_DESCRIPTION = 'Check the version of the modules and CLI.'; export const WHOAMI_DESCRIPTION = 'Display your current profile, controller, and links to your satellite.'; +export const DEV_START_DESCRIPTION = 'Start a local Internet Computer network in a container.'; export const DEV_BUILD_DESCRIPTION = 'Build your serverless functions.'; export const DEV_EJECT_DESCRIPTION = 'Generate the required files to begin developing serverless functions in your project.'; + +export const DEV_BUILD_NOTES = `- If no language is provided, the CLI attempts to determine the appropriate build. +- Language can be shortened to ${magenta('rs')} for Rust, ${magenta('ts')} for TypeScript and ${magenta('mjs')} for JavaScript. +- The path option maps to ${magenta('--manifest-path')} for Rust (Cargo) or to the source file for TypeScript and JavaScript (e.g. ${magenta('index.ts')} or ${magenta('index.mjs')}). +- The watch option rebuilds when source files change, with a default debounce delay of 10 seconds; optionally, pass a delay in milliseconds.`; diff --git a/src/help/dev.build.help.ts b/src/help/dev.build.help.ts index 317b88e7..011fab5c 100644 --- a/src/help/dev.build.help.ts +++ b/src/help/dev.build.help.ts @@ -1,5 +1,5 @@ import {cyan, green, magenta, yellow} from 'kleur'; -import {DEV_BUILD_DESCRIPTION} from '../constants/help.constants'; +import {DEV_BUILD_DESCRIPTION, DEV_BUILD_NOTES} from '../constants/help.constants'; import {helpOutput} from './common.help'; import {TITLE} from './help'; @@ -13,10 +13,7 @@ Options: Notes: -- If no language is provided, the CLI attempts to determine the appropriate build. -- Language can be shortened to ${magenta('rs')} for Rust, ${magenta('ts')} for TypeScript and ${magenta('mjs')} for JavaScript. -- The path option maps to ${magenta('--manifest-path')} for Rust (Cargo) or to the source file for TypeScript and JavaScript (e.g. ${magenta('index.ts')} or ${magenta('index.mjs')}). -- The watch option rebuilds when source files change, with a default debounce delay of 10 seconds; optionally, pass a delay in milliseconds.`; +${DEV_BUILD_NOTES}`; const doc = `${DEV_BUILD_DESCRIPTION} diff --git a/src/help/dev.help.ts b/src/help/dev.help.ts index e0f3bb67..e3fccec8 100644 --- a/src/help/dev.help.ts +++ b/src/help/dev.help.ts @@ -1,11 +1,9 @@ import {cyan, green, magenta, yellow} from 'kleur'; -import {DEV_DESCRIPTION} from '../constants/help.constants'; +import {DEV_DESCRIPTION, DEV_START_DESCRIPTION} from '../constants/help.constants'; import {helpOutput} from './common.help'; import {TITLE} from './help'; -const helpDevStart = `${magenta( - 'start' -)} Start a local Internet Computer network in a container.`; +const helpDevStart = `${magenta('start')} ${DEV_START_DESCRIPTION}`; const helpDevBuild = `${magenta('build')} Build your serverless functions. The local server supports live reloading.`; diff --git a/src/help/dev.start.help.ts b/src/help/dev.start.help.ts new file mode 100644 index 00000000..bbacaa57 --- /dev/null +++ b/src/help/dev.start.help.ts @@ -0,0 +1,35 @@ +import {cyan, green, magenta, yellow} from 'kleur'; +import {DEV_BUILD_NOTES, DEV_START_DESCRIPTION} from '../constants/help.constants'; +import {helpOutput} from './common.help'; +import {TITLE} from './help'; + +const usage = `Usage: ${green('juno')} ${cyan('dev')} ${magenta('start')} ${yellow('[options]')} + +Options: + ${yellow('-l, --lang')} Language used when watching for file changes: ${magenta('rust')}, ${magenta('typescript')} or ${magenta('javascript')}. + ${yellow('-p, --path')} Path to the source file or manifest used when watching. + ${yellow('-w, --watch')} Rebuild your functions automatically when source files change. + ${yellow('-h, --help')} Output usage information. + +Notes: + +- The language and path options are only used in combination with watch. +${DEV_BUILD_NOTES}`; + +const doc = `${DEV_START_DESCRIPTION} + +\`\`\` +${usage} +\`\`\` +`; + +const help = `${TITLE} + +${DEV_START_DESCRIPTION} + +${usage} +`; + +export const logHelpDevStart = (args?: string[]) => { + console.log(helpOutput(args) === 'doc' ? doc : help); +}; diff --git a/src/services/build/build.services.ts b/src/services/build/build.services.ts index d76cd1bb..05365159 100644 --- a/src/services/build/build.services.ts +++ b/src/services/build/build.services.ts @@ -1,5 +1,4 @@ import {debounce, nonNullish} from '@dfinity/utils'; -import {hasArgs, nextArg} from '@junobuild/cli-tools'; import chokidar from 'chokidar'; import {red} from 'kleur'; import {existsSync} from 'node:fs'; @@ -12,6 +11,7 @@ import { } from '../../constants/dev.constants'; import {SMALL_TITLE} from '../../help/help'; import {type BuildArgs} from '../../types/build'; +import {buildArgs} from '../../utils/build.utils'; import {buildJavaScript, buildTypeScript} from './build.javascript'; import {buildRust} from './build.rust.services'; @@ -86,7 +86,7 @@ const executeBuild = async ({lang, path}: Omit) => { ); }; -const watchBuild = ({watch, path, ...params}: BuildArgs) => { +export const watchBuild = ({watch, path, ...params}: BuildArgs) => { const doBuild = async () => { console.log(`\n⏱ Rebuilding serverless functions...`); await executeBuild({path, ...params}); @@ -118,38 +118,3 @@ const watchBuild = ({watch, path, ...params}: BuildArgs) => { console.log(red('️‼️ Unexpected error while live reloading:'), err); }); }; - -const buildArgs = (args?: string[]): BuildArgs => { - const path = nextArg({args, option: '-p'}) ?? nextArg({args, option: '--path'}); - - const {lang} = buildLang(args); - - const watch = hasArgs({args, options: ['-w', '--watch']}); - const watchValue = nextArg({args, option: '-w'}) ?? nextArg({args, option: '--watch'}); - - return { - path, - lang, - watch: watchValue ?? watch - }; -}; - -const buildLang = (args?: string[]): Pick => { - const lang = nextArg({args, option: '-l'}) ?? nextArg({args, option: '--lang'}); - - switch (lang?.toLowerCase()) { - case 'rs': - case 'rust': - return {lang: 'rs'}; - case 'ts': - case 'mts': - case 'typescript': - return {lang: 'ts'}; - case 'js': - case 'mjs': - case 'javascript': - return {lang: 'mjs'}; - default: - return {}; - } -}; diff --git a/src/services/docker.services.ts b/src/services/start/docker.services.ts similarity index 86% rename from src/services/docker.services.ts rename to src/services/start/docker.services.ts index 3daefd38..8795084e 100644 --- a/src/services/docker.services.ts +++ b/src/services/start/docker.services.ts @@ -9,17 +9,17 @@ import { detectJunoDevConfigType, junoDevConfigExist, junoDevConfigFile -} from '../configs/juno.dev.config'; -import {JUNO_DEV_CONFIG_FILENAME} from '../constants/constants'; -import {assertDockerRunning, checkDockerVersion} from '../utils/env.utils'; -import {copyTemplateFile, readTemplateFile} from '../utils/fs.utils'; -import {confirmAndExit} from '../utils/prompt.utils'; -import {promptConfigType} from './init.services'; +} from '../../configs/juno.dev.config'; +import {JUNO_DEV_CONFIG_FILENAME} from '../../constants/constants'; +import {assertDockerRunning, checkDockerVersion} from '../../utils/env.utils'; +import {copyTemplateFile, readTemplateFile} from '../../utils/fs.utils'; +import {confirmAndExit} from '../../utils/prompt.utils'; +import {promptConfigType} from '../init.services'; const TEMPLATE_PATH = '../templates/docker'; const DESTINATION_PATH = process.cwd(); -export const start = async () => { +export const startContainer = async () => { const {valid} = await checkDockerVersion(); if (valid === 'error' || !valid) { diff --git a/src/services/start/start.services.ts b/src/services/start/start.services.ts new file mode 100644 index 00000000..e81c2016 --- /dev/null +++ b/src/services/start/start.services.ts @@ -0,0 +1,14 @@ +import {nonNullish} from '@dfinity/utils'; +import {buildArgs} from '../../utils/build.utils'; +import {watchBuild} from '../build/build.services'; +import {startContainer} from './docker.services'; + +export const start = async (args?: string[]) => { + const {watch, ...params} = buildArgs(args); + + if (nonNullish(watch) && watch !== false) { + watchBuild({watch, ...params}); + } + + await startContainer(); +}; diff --git a/src/utils/build.utils.ts b/src/utils/build.utils.ts new file mode 100644 index 00000000..77bc6641 --- /dev/null +++ b/src/utils/build.utils.ts @@ -0,0 +1,37 @@ +import {hasArgs, nextArg} from '@junobuild/cli-tools'; +import type {BuildArgs} from '../types/build'; + +export const buildArgs = (args?: string[]): BuildArgs => { + const path = nextArg({args, option: '-p'}) ?? nextArg({args, option: '--path'}); + + const {lang} = buildLang(args); + + const watch = hasArgs({args, options: ['-w', '--watch']}); + const watchValue = nextArg({args, option: '-w'}) ?? nextArg({args, option: '--watch'}); + + return { + path, + lang, + watch: watchValue ?? watch + }; +}; + +const buildLang = (args?: string[]): Pick => { + const lang = nextArg({args, option: '-l'}) ?? nextArg({args, option: '--lang'}); + + switch (lang?.toLowerCase()) { + case 'rs': + case 'rust': + return {lang: 'rs'}; + case 'ts': + case 'mts': + case 'typescript': + return {lang: 'ts'}; + case 'js': + case 'mjs': + case 'javascript': + return {lang: 'mjs'}; + default: + return {}; + } +};