-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* docs: improve documentation and descriptions * ci: refactor tests to BDD * docs(types): improve inline documentation * feat: introduce `startScript` and `startService` * ci: resolve port conflict on GitHub Actions * ci: debug port conflict on GitHub Actions * ci: debug port conflict on GitHub Actions * ci: debug port conflict on GitHub Actions * ci: forcing process exit on GitHub Actions
- Loading branch information
1 parent
7581a4f
commit bc414bb
Showing
81 changed files
with
1,205 additions
and
1,595 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { Runner } from './runner.js'; | ||
import { Configs } from './poku.js'; | ||
|
||
type BackgroundProcessOptions = { | ||
/** | ||
* - By default, it will resolve in the first console output | ||
* - By setting a string: it will wait for a specifc string on console output to resolve | ||
* - By setting a number: it will wait for time in milliseconds to resolve | ||
* | ||
* @default undefined | ||
*/ | ||
startAfter?: string | number; | ||
/** | ||
* Stops the service after: | ||
* @default 60000 | ||
*/ | ||
timeout?: number; | ||
/** | ||
* Shows the output from service | ||
*/ | ||
verbose?: boolean; | ||
/** | ||
* Specify a target path to start the process | ||
* | ||
* @default "./" | ||
*/ | ||
cwd?: string | undefined; | ||
}; | ||
|
||
export type StartScriptOptions = { | ||
/** | ||
* By default, Poku will use `npm`. Change it as you want. | ||
*/ | ||
readonly runner?: Runner; | ||
} & BackgroundProcessOptions; | ||
|
||
export type StartServiceOptions = { | ||
/** | ||
* By default, Poku will try to identify the actual platform, but you can specify it manually | ||
*/ | ||
readonly platform?: Configs['platform']; | ||
} & BackgroundProcessOptions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type Runner = 'npm' | 'bun' | 'deno' | 'yarn' | 'pnpm'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,40 @@ | ||
import process from 'node:process'; | ||
import path from 'node:path'; | ||
import { getRuntime } from './get-runtime.js'; | ||
import { Configs } from '../@types/poku.js'; | ||
import { Runner } from '../@types/runner.js'; | ||
|
||
const isWindows = process.platform === 'win32'; | ||
|
||
export const runner = (filename: string, configs?: Configs): string[] => { | ||
const runtime = getRuntime(configs); | ||
|
||
// Bun | ||
if (runtime === 'bun') return ['bun']; | ||
|
||
// Deno | ||
if (runtime === 'deno') | ||
return ['deno', 'run', '--allow-read', '--allow-env', '--allow-run']; | ||
return path.extname(filename) === '.ts' ? ['tsx'] : ['node']; | ||
|
||
// Node.js | ||
return path.extname(filename) === '.ts' | ||
? [isWindows ? 'npx.cmd' : 'npx', 'tsx'] | ||
: ['node']; | ||
}; | ||
|
||
export const scriptRunner = (runner: Runner): string[] => { | ||
// Bun | ||
if (runner === 'bun') return ['bun']; | ||
|
||
// Deno | ||
if (runner === 'deno') return ['deno', 'task']; | ||
|
||
// Yarn | ||
if (runner === 'yarn') return ['yarn']; | ||
|
||
// PNPM | ||
if (runner === 'pnpm') return ['pnpm', 'run']; | ||
|
||
// Node.js | ||
return ['npm', 'run']; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import process from 'node:process'; | ||
import { spawn } from 'node:child_process'; | ||
import { runner, scriptRunner } from '../helpers/runner.js'; | ||
import path from 'node:path'; | ||
import { | ||
StartScriptOptions, | ||
StartServiceOptions, | ||
} from '../@types/background-process.js'; | ||
import { sanitizePath } from './list-files.js'; | ||
|
||
const backgroundProcess = ( | ||
runtime: string, | ||
args: string[], | ||
file: string, | ||
options?: StartServiceOptions | ||
): Promise<{ end: () => boolean }> => | ||
new Promise((resolve, reject) => { | ||
let isResolved = false; | ||
|
||
const service = spawn(runtime, args, { | ||
stdio: ['inherit', 'pipe', 'pipe'], | ||
shell: false, | ||
cwd: options?.cwd ? sanitizePath(path.normalize(options.cwd)) : undefined, | ||
env: process.env, | ||
detached: true, | ||
}); | ||
|
||
const end = () => { | ||
process.kill(-service.pid!, 'SIGKILL'); | ||
return true; | ||
}; | ||
|
||
service.stdout.on('data', (data: Buffer) => { | ||
if (!isResolved && typeof options?.startAfter !== 'number') { | ||
const stringData = String(data); | ||
|
||
if ( | ||
typeof options?.startAfter === 'undefined' || | ||
(typeof options?.startAfter === 'string' && | ||
stringData.includes(options.startAfter)) | ||
) { | ||
resolve({ end }); | ||
clearTimeout(timeout); | ||
|
||
isResolved = true; | ||
} | ||
} | ||
|
||
options?.verbose && console.log(String(data)); | ||
}); | ||
|
||
service.stderr.on('data', (data: Buffer) => { | ||
reject(new Error(`Service failed to start: ${data}`)); | ||
}); | ||
|
||
service.on('error', (err) => { | ||
reject(new Error(`Service failed to start: ${err}`)); | ||
}); | ||
|
||
service.on('close', (code) => { | ||
if (code !== 0) reject(new Error(`Service exited with code ${code}`)); | ||
}); | ||
|
||
const timeout = setTimeout(() => { | ||
if (!isResolved) { | ||
service.kill(); | ||
reject(`createService: Timeout\nFile: ${file}`); | ||
} | ||
}, options?.timeout || 10000); | ||
|
||
if (typeof options?.startAfter === 'number') { | ||
setTimeout(() => { | ||
if (!isResolved) { | ||
resolve({ end }); | ||
clearTimeout(timeout); | ||
|
||
isResolved = true; | ||
} | ||
}, options.startAfter); | ||
} | ||
}); | ||
|
||
/** | ||
* | ||
* Starts a file in a background process | ||
* | ||
* Useful for servers, APIs, etc. | ||
*/ | ||
export const startService = async ( | ||
file: string, | ||
options?: StartServiceOptions | ||
): Promise<{ end: () => boolean }> => { | ||
const runtimeOptions = runner(file, { platform: options?.platform }); | ||
const runtime = runtimeOptions.shift()!; | ||
const runtimeArgs = [...runtimeOptions, file]; | ||
|
||
return await backgroundProcess( | ||
runtime, | ||
runtimeArgs, | ||
path.normalize(sanitizePath(file)), | ||
options | ||
); | ||
}; | ||
|
||
/** | ||
* | ||
* Starts a script (package.json) or task (deno.json) in a background process | ||
* | ||
* By default, it uses **npm**, but you can costumize it using the `runner` option. | ||
* | ||
* Useful for servers, APIs, etc. | ||
*/ | ||
export const startScript = async ( | ||
script: string, | ||
options?: StartScriptOptions | ||
): Promise<{ end: () => boolean }> => { | ||
const runtimeOptions = scriptRunner(options?.runner || 'npm'); | ||
const runtime = runtimeOptions.shift()!; | ||
const runtimeArgs = [...runtimeOptions, script]; | ||
|
||
return await backgroundProcess(runtime, runtimeArgs, script, options); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.