From 70cdcb2ab96b89cfc8b42d6a6869dceaf74530f2 Mon Sep 17 00:00:00 2001 From: Thada Wangthammang Date: Sat, 11 May 2024 10:59:25 +0700 Subject: [PATCH] feat(cli): dev mode to write host.json --- infra/azure-functions/src/azure-command.ts | 2 +- package.json | 3 ++ packages/main/src/command/build/build.ts | 7 +-- packages/main/src/command/build/bun.ts | 4 +- packages/main/src/command/build/internal.ts | 3 ++ packages/main/src/command/build/nodejs.ts | 16 ++++--- packages/main/src/command/build/types.ts | 2 +- packages/main/src/command/cli.ts | 25 +++------- .../config-loader/types/host-config.ts | 4 +- .../main/src/command/config/config-writer.ts | 46 ------------------ packages/main/src/command/config/config.ts | 47 +++++++++++++++++++ packages/main/src/command/config/index.ts | 2 +- .../main/src/command/config/load-env-vars.ts | 2 +- packages/main/src/command/dev/dev.ts | 25 ++++++++++ packages/main/src/command/dev/index.ts | 1 + packages/main/src/command/index.ts | 1 + 16 files changed, 107 insertions(+), 83 deletions(-) create mode 100644 packages/main/src/command/build/internal.ts delete mode 100644 packages/main/src/command/config/config-writer.ts create mode 100644 packages/main/src/command/config/config.ts create mode 100644 packages/main/src/command/dev/dev.ts create mode 100644 packages/main/src/command/dev/index.ts diff --git a/infra/azure-functions/src/azure-command.ts b/infra/azure-functions/src/azure-command.ts index 5ad57c3..958da99 100644 --- a/infra/azure-functions/src/azure-command.ts +++ b/infra/azure-functions/src/azure-command.ts @@ -68,7 +68,7 @@ export async function assignRoleAssignment(infraConfig: InfraEnvConfig, options: const resourceGroup = `rg-nammatham-${resourceName.prefix}`; const role = 'Contributor'; - const assignee = process.env.AZURE_APPLICATION_ID; + const assignee = process.env.AZURE_APPLICATION_ID; const scope = `/subscriptions/${process.env.AZURE_SUBSCRIPTION_ID}/resourceGroups/${resourceGroup}`; await $`az role assignment create --role ${role} --assignee ${assignee} --scope ${scope}`; } diff --git a/package.json b/package.json index fc37c36..73948d5 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,9 @@ "type": "git", "url": "https://github.com/thaitype/nammatham.git" }, + "peerDependencies": { + "tsx": "^4.7.0" + }, "devDependencies": { "@inquirer/prompts": "^3.3.0", "@types/fs-extra": "^11.0.4", diff --git a/packages/main/src/command/build/build.ts b/packages/main/src/command/build/build.ts index db7a763..98fe35a 100644 --- a/packages/main/src/command/build/build.ts +++ b/packages/main/src/command/build/build.ts @@ -5,14 +5,11 @@ import { system } from 'pkg-fetch'; import type { NammathamConfigs } from '../nammatham-config'; import type { TargetBunOptions, TargetOptions } from './types'; -import { createDebugger } from '../utils'; +import { debug } from './internal'; import { buildExecutableBun } from './bun'; import { findNearestPackageData } from '../packages'; import { buildExecutableNodeJs, buildNodeJs } from './nodejs'; import { constructHostConfig, constructLocalSettings } from '../config'; - -export const debug = createDebugger('nammatham:build'); - /** * Hard code the function.json for the SimpleHttpTrigger */ @@ -55,7 +52,7 @@ export async function buildRuntime(config: NammathamConfigs) { export async function build(config: NammathamConfigs): Promise { const targetPath = path.resolve(config.buildPath ?? '.nmt', 'dist'); fs.mkdirSync(targetPath, { recursive: true }); - await fs.promises.writeFile(path.join(targetPath, 'host.json'), constructHostConfig(config), 'utf-8'); + await fs.promises.writeFile(path.join(targetPath, 'host.json'), constructHostConfig(config, 'build'), 'utf-8'); await fs.promises.writeFile(path.join(targetPath, 'local.settings.json'), constructLocalSettings(), 'utf-8'); if (config.buildOptions?.disabled) { diff --git a/packages/main/src/command/build/bun.ts b/packages/main/src/command/build/bun.ts index 0846958..5fe4587 100644 --- a/packages/main/src/command/build/bun.ts +++ b/packages/main/src/command/build/bun.ts @@ -4,8 +4,8 @@ import path from 'node:path'; import type { NammathamConfigs } from '../nammatham-config'; import type { TargetBunOptions, TargetOptions } from './types'; -import { debug, getDistDirectory, getExecutablePath, getPackageInfo } from './build'; - +import { debug } from './internal'; +import { getDistDirectory, getExecutablePath, getPackageInfo } from './build'; /** * Call bun as a sub-process to build the executable file, for preventing the bundle with tsup */ diff --git a/packages/main/src/command/build/internal.ts b/packages/main/src/command/build/internal.ts new file mode 100644 index 0000000..a7651c1 --- /dev/null +++ b/packages/main/src/command/build/internal.ts @@ -0,0 +1,3 @@ +import { createDebugger } from '../utils'; + +export const debug = createDebugger('nammatham:build'); diff --git a/packages/main/src/command/build/nodejs.ts b/packages/main/src/command/build/nodejs.ts index b7b92ef..2f41235 100644 --- a/packages/main/src/command/build/nodejs.ts +++ b/packages/main/src/command/build/nodejs.ts @@ -6,8 +6,8 @@ import { build as esbuild } from 'esbuild'; import type { NammathamConfigs } from '../nammatham-config'; -import { debug, getDistDirectory, getExecutablePath, getPackageInfo } from './build'; - +import { debug } from './internal'; +import { getDistDirectory, getExecutablePath, getPackageInfo } from './build'; /** * When publish into Azure Functions, the package.json will not be included in the final package. * So, it needs to specify the output file manually wheather it is ESM or CommonJS. for example `main.mjs` or `main.cjs` @@ -69,15 +69,19 @@ export async function buildNodeJs(options: NammathamConfigs): Promise { if (options.buildOptions?.nodeToolChain?.package !== 'pkg') { - throw new Error(`(buildExecutableNodeJs) Unsupported package tool: ${options.buildOptions?.nodeToolChain?.package}`); + throw new Error( + `(buildExecutableNodeJs) Unsupported package tool: ${options.buildOptions?.nodeToolChain?.package}` + ); } const target = options.buildOptions?.target; - if(options.runtime !== 'node'){ + if (options.runtime !== 'node') { throw new Error(`(buildExecutableNodeJs) Unsupported runtime: ${options.runtime}`); } const nodeVersion = options.buildOptions?.nodeToolChain?.version; - if(nodeVersion !== 18){ - throw new Error(`(buildExecutableNodeJs) Only support node.js version 18 due to minimum version of Nammatham Framework.`); + if (nodeVersion !== 18) { + throw new Error( + `(buildExecutableNodeJs) Only support node.js version 18 due to minimum version of Nammatham Framework.` + ); } const runtime = options.runtime + nodeVersion; if (!target) { diff --git a/packages/main/src/command/build/types.ts b/packages/main/src/command/build/types.ts index d52aa8a..1b42050 100644 --- a/packages/main/src/command/build/types.ts +++ b/packages/main/src/command/build/types.ts @@ -50,7 +50,7 @@ export interface BuildOptions { * Disable the build process. This is useful when you want to disable the build process for a specific environment. * You need to manage the build process manually, however, the other Azure Functions configurations will be managed by the framework. * For example, `function.json`, `local.settings.json`, and `host.json` will be managed by the framework. - * + * * @default false */ disabled?: boolean; diff --git a/packages/main/src/command/cli.ts b/packages/main/src/command/cli.ts index 1fa9f97..41515cd 100644 --- a/packages/main/src/command/cli.ts +++ b/packages/main/src/command/cli.ts @@ -1,17 +1,19 @@ import { build } from './build'; +import { writeConfig } from './dev'; import { createDebugger } from './utils'; +import { loadEnvVariables } from './config'; import { loadConfigFromFile } from './config-loader'; -import { loadEnvVariables, writeConfig } from './config'; import { startAzureFunctionHost } from './azure-func-host'; const debug = createDebugger('nammatham:cli'); export async function main() { - console.log(`Start the command`); debug?.(`Working Directory: ${process.cwd()}`); /** * Remove the first two arguments which are the node binary and the script file + * + * TODO: Use a library like yargs or commander to parse the arguments */ const args = process.argv.slice(2); console.log(`Args: ${args}`); @@ -24,26 +26,13 @@ export async function main() { const envVars = loadEnvVariables(config?.envVariablesConfig); debug?.(`Loaded env variables: ${JSON.stringify(envVars)}`); + // debug?.(`Command: ${args[0]}`); if (args[0] === 'dev') { + debug?.(`Starting dev server`); await writeConfig(config, envVars); - /** - * setup host.json for dev - - "watchDirectories": [ - "../src", // Watch the source directory - "." // watch the function.json - ] - - "defaultExecutablePath": "../node_modules/.bin/tsx", - "arguments": [ - "watch", - "../src/main.ts" - ] - */ + debug?.(`Config written`); await startAzureFunctionHost(config); } else if (args[0] === 'build') { - console.log(`Build the code`); await build(config); } - console.log(`End the command`); } diff --git a/packages/main/src/command/config-loader/types/host-config.ts b/packages/main/src/command/config-loader/types/host-config.ts index 1c750ee..4022fdb 100644 --- a/packages/main/src/command/config-loader/types/host-config.ts +++ b/packages/main/src/command/config-loader/types/host-config.ts @@ -168,8 +168,8 @@ export interface ExtensionBundle { id?: string; /** * The version range of the bundle to install. The Functions runtime always picks the maximum permissible version defined by the version range or interval. - * For example, a version value range of `[4.0.0, 5.0.0)` allows all bundle versions from `4.0.0` up to but not including `5.0.0`. For more information, - * see the [interval notation for specifying version ranges](https://learn.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges). + * For example, a version value range of `[4.0.0, 5.0.0)` allows all bundle versions from `4.0.0` up to but not including `5.0.0`. For more information, + * see the [interval notation for specifying version ranges](https://learn.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges). * * The following table lists the currently available version ranges of the default `Microsoft.Azure.Functions.ExtensionBundle` bundles and links to the extensions they include. * diff --git a/packages/main/src/command/config/config-writer.ts b/packages/main/src/command/config/config-writer.ts deleted file mode 100644 index 7632339..0000000 --- a/packages/main/src/command/config/config-writer.ts +++ /dev/null @@ -1,46 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; - -import type { EnvVariables } from './load-env-vars'; -import type { LocalSettings, NammathamConfigs } from '../config-loader'; - -import { getExecutablePath } from '../build'; - -export async function writeConfig(config: NammathamConfigs, envVars: EnvVariables, tmpPath = '') { - const targetPath = path.resolve(config.buildPath ?? './nmt', tmpPath); - await Promise.all([ - fs.promises.writeFile(path.join(targetPath, 'host.json'), constructHostConfig(config), 'utf-8'), - fs.promises.writeFile(path.join(targetPath, 'local.settings.json'), constructLocalSettings(envVars), 'utf-8'), - ]); -} - -export function constructHostConfig(config: NammathamConfigs): string { - return JSON.stringify( - { - customHandler: { - description: { - defaultExecutablePath: getExecutablePath(config.buildOptions?.target), - }, - enableForwardingHttpRequest: true, - }, - ...config.hostConfig, - }, - null, - 2 - ); -} - -export function constructLocalSettings(envVars?: EnvVariables): string { - return JSON.stringify( - { - IsEncrypted: false, - Values: { - ...envVars, - FUNCTIONS_WORKER_RUNTIME: 'custom', - AzureWebJobsStorage: envVars?.AzureWebJobsStorage ?? 'UseDevelopmentStorage=true', - }, - } satisfies LocalSettings, - null, - 2 - ); -} diff --git a/packages/main/src/command/config/config.ts b/packages/main/src/command/config/config.ts new file mode 100644 index 0000000..2b363b2 --- /dev/null +++ b/packages/main/src/command/config/config.ts @@ -0,0 +1,47 @@ +import type { EnvVariables } from './load-env-vars'; +import type { HostConfigV2, LocalSettings, NammathamConfigs } from '../config-loader'; + +import { getExecutablePath } from '../build'; + +export function constructHostConfig(config: NammathamConfigs, mode: 'dev' | 'build'): string { + let description: NonNullable['description'] = {}; + let watchDirectories: HostConfigV2['watchDirectories']; + if (mode === 'build') { + description = { + defaultExecutablePath: getExecutablePath(config.buildOptions?.target), + }; + } else if (mode === 'dev') { + description = { + defaultExecutablePath: '../node_modules/.bin/tsx', + arguments: ['watch', '../src/main.ts'], + }; + watchDirectories = ['../src', '.']; + } + return JSON.stringify( + { + customHandler: { + description, + enableForwardingHttpRequest: true, + }, + watchDirectories, + ...config.hostConfig, + } as HostConfigV2, + null, + 2 + ); +} + +export function constructLocalSettings(envVars?: EnvVariables): string { + return JSON.stringify( + { + IsEncrypted: false, + Values: { + ...envVars, + FUNCTIONS_WORKER_RUNTIME: 'custom', + AzureWebJobsStorage: envVars?.AzureWebJobsStorage ?? 'UseDevelopmentStorage=true', + }, + } satisfies LocalSettings, + null, + 2 + ); +} diff --git a/packages/main/src/command/config/index.ts b/packages/main/src/command/config/index.ts index 84960a6..4b28c87 100644 --- a/packages/main/src/command/config/index.ts +++ b/packages/main/src/command/config/index.ts @@ -1,2 +1,2 @@ export * from './load-env-vars'; -export * from './config-writer'; +export * from './config'; diff --git a/packages/main/src/command/config/load-env-vars.ts b/packages/main/src/command/config/load-env-vars.ts index c742c0a..8fd9389 100644 --- a/packages/main/src/command/config/load-env-vars.ts +++ b/packages/main/src/command/config/load-env-vars.ts @@ -11,7 +11,7 @@ import 'dotenv/config'; export type EnvVariables = Record; -const debug = createDebugger('nammatham:config'); +export const debug = createDebugger('nammatham:config'); export function loadUserDefinedEnvVariables(): EnvVariables { for (const envFile of DEFAULT_ENV_FILEs) { diff --git a/packages/main/src/command/dev/dev.ts b/packages/main/src/command/dev/dev.ts new file mode 100644 index 0000000..d913afa --- /dev/null +++ b/packages/main/src/command/dev/dev.ts @@ -0,0 +1,25 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import type { EnvVariables } from '../config'; +import type { NammathamConfigs } from '../config-loader'; + +import { createDebugger } from '../utils'; +import { constructHostConfig, constructLocalSettings } from '../config'; + +const debug = createDebugger('nammatham:dev'); + +export async function writeConfig(config: NammathamConfigs, envVars: EnvVariables, tmpPath = '') { + fs.mkdirSync(path.resolve(config.buildPath ?? './nmt', tmpPath), { recursive: true }); + const targetPath = path.resolve(config.buildPath ?? './nmt', tmpPath); + const result = await Promise.allSettled([ + fs.promises.writeFile(path.join(targetPath, 'host.json'), constructHostConfig(config, 'dev'), 'utf-8'), + fs.promises.writeFile(path.join(targetPath, 'local.settings.json'), constructLocalSettings(envVars), 'utf-8'), + ]); + result.forEach((r) => { + if (r.status === 'rejected') { + throw new Error(r.reason); + } + }); + +} diff --git a/packages/main/src/command/dev/index.ts b/packages/main/src/command/dev/index.ts new file mode 100644 index 0000000..855d466 --- /dev/null +++ b/packages/main/src/command/dev/index.ts @@ -0,0 +1 @@ +export * from './dev'; diff --git a/packages/main/src/command/index.ts b/packages/main/src/command/index.ts index 53a75dc..20dc8a5 100644 --- a/packages/main/src/command/index.ts +++ b/packages/main/src/command/index.ts @@ -1,2 +1,3 @@ export * from './config-loader'; export * from './build'; +export * from './dev';