UNPKG

@whook/whook

Version:

Build strong and efficient REST web services.

162 lines (147 loc) 5.5 kB
import { exit, stderr } from 'node:process'; import { readFile, writeFile } from 'node:fs/promises'; import { join, relative } from 'node:path'; import { pathToFileURL } from 'node:url'; import { mkdirp } from 'mkdirp'; import { Knifecycle, constant, initInitializerBuilder } from 'knifecycle'; import initCompiler, { DEFAULT_COMPILER_OPTIONS } from './services/compiler.js'; import initProxiedENV from './services/PROXIED_ENV.js'; import initBuildAutoloader from './services/_buildAutoload.js'; import { printStackTrace } from 'yerror'; import { type BuildInitializer } from 'knifecycle'; import { type WhookCompilerOptions, type WhookCompilerService, } from './services/compiler.js'; import { type LogService } from 'common-services'; import { parseArgs } from './libs/args.js'; export const DEFAULT_BUILD_DIR = 'server'; export const DEFAULT_BUILD_INITIALIZER_PATH_MAP = { $fatalError: 'knifecycle/dist/fatalError.js', BASE_URL: '@whook/whook/dist/services/BASE_URL.js', DEFINITIONS: '@whook/whook/dist/services/DEFINITIONS.js', logger: '@whook/whook/dist/services/logger.js', exit: '@whook/whook/dist/services/exit.js', PORT: '@whook/whook/dist/services/PORT.js', HOST: '@whook/whook/dist/services/HOST.js', WHOOK_RESOLVED_PLUGINS: '@whook/whook/dist/services/WHOOK_RESOLVED_PLUGINS.js', httpRouter: '@whook/whook/dist/services/httpRouter.js', httpTransaction: '@whook/whook/dist/services/httpTransaction.js', httpServer: '@whook/whook/dist/services/httpServer.js', apm: '@whook/whook/dist/services/apm.js', obfuscator: '@whook/whook/dist/services/obfuscator.js', errorHandler: '@whook/whook/dist/services/errorHandler.js', APP_CONFIG: 'application-services/dist/services/APP_CONFIG.js', PROJECT_DIR: 'application-services/dist/services/PROJECT_DIR.js', PROCESS_ENV: 'application-services/dist/services/PROCESS_ENV.js', process: 'application-services/dist/services/process.js', ENV: 'application-services/dist/services/ENV.js', log: 'common-services/dist/services/log.js', time: 'common-services/dist/services/time.js', delay: 'common-services/dist/services/delay.js', random: 'common-services/dist/services/random.js', importer: 'common-services/dist/services/importer.js', resolve: 'common-services/dist/services/resolve.js', }; export async function prepareBuildEnvironment<T extends Knifecycle>( $: T = new Knifecycle() as T, ): Promise<T> { $.register(initBuildAutoloader); $.register(initInitializerBuilder); $.register(initCompiler); $.register(initProxiedENV); $.register( constant('INITIALIZER_PATH_MAP', DEFAULT_BUILD_INITIALIZER_PATH_MAP), ); $.register(constant('args', parseArgs([]))); $.register(constant('BUILD_DIR', DEFAULT_BUILD_DIR)); $.register(constant('COMPILER_OPTIONS', DEFAULT_COMPILER_OPTIONS)); return $; } export async function runBuild( aPrepareBuildEnvironment: typeof prepareBuildEnvironment, ): Promise<void> { try { const $ = await aPrepareBuildEnvironment(); const { APP_ENV, PROJECT_DIR, BUILD_DIR, COMPILER_OPTIONS, compiler, log, buildInitializer, }: { APP_ENV: string; PROJECT_DIR: string; BUILD_DIR: string; COMPILER_OPTIONS: WhookCompilerOptions; compiler: WhookCompilerService; log: LogService; buildInitializer: BuildInitializer; } = await $.run([ 'APP_ENV', 'PROJECT_DIR', 'BUILD_DIR', 'COMPILER_OPTIONS', 'compiler', 'log', 'buildInitializer', 'process', ]); log('info', 'Build environment initialized 🚀🌕'); const distPath = join(PROJECT_DIR, 'dist'); const srcPath = join(PROJECT_DIR, 'src'); const buildPath = join(PROJECT_DIR, 'builds', APP_ENV, BUILD_DIR); const srcRelativePath = relative(buildPath, srcPath); const initializerContent = ( await buildInitializer(['httpServer', 'process']) ).replaceAll(pathToFileURL(srcPath).toString(), srcRelativePath); const indexContent = await buildIndex(); await mkdirp(distPath); await mkdirp(buildPath); await Promise.all([ ensureFile({ log }, join(buildPath, 'initialize.js'), initializerContent), ensureFile({ log }, join(buildPath, 'main.js'), indexContent), ]); const entryPoint = `${buildPath}/main.js`; const { contents, mappings } = await compiler(entryPoint, COMPILER_OPTIONS); await Promise.all([ ensureFile({ log }, `${buildPath}/start.js`, contents), mappings ? ensureFile({ log }, `${buildPath}/start.js.map`, mappings) : Promise.resolve(), ]); } catch (err) { stderr.write( `💀 - Cannot launch the build: ${printStackTrace(err as Error)}`, ); exit(1); } } async function ensureFile( { log }: { log: LogService }, path: string, content: string, ): Promise<void> { try { const oldContent = (await readFile(path)).toString(); if (oldContent === content) { log('debug', `🗀 - Ignore unchanged file: "${path}".`); return; } } catch (err) { log('debug', `🗀 - Write new file: "${path}".`); log('debug-stack', printStackTrace(err as Error)); return await writeFile(path, content); } log('debug', `🗀 - Write changed file: "${path}".`); return await writeFile(path, content); } async function buildIndex(): Promise<string> { return `// Built with \`@whook\`, do not edit in place! import { initialize } from './initialize.js'; await initialize(); `; }