Stop compiling DM if compiler outputs are locked. (#60022)

Basically, saves developer's time by yelling that the compiler can't write to dmb/rsc, because they are locked by Dream Daemon.

Added myself as a code owner for /tools/build.
This commit is contained in:
Aleksej Komarov
2021-07-07 01:01:46 +01:00
committed by GitHub
parent 704ff13966
commit 6eacbde24f
13 changed files with 273 additions and 260 deletions
+1 -1
View File
@@ -53,7 +53,6 @@
/code/controllers/subsystem/timer.dm @MrStonedOne
/code/controllers/configuration/entries @MrStonedOne
/config/ @MrStonedOne
/tools/build/ @MrStonedOne
# ninjanomnom
@@ -84,6 +83,7 @@
/icons/ @Twaticus @ShizCalev @Krysonism
/code/controllers/subsystem/air.dm @LemonInTheDark @MrStonedOne
/_maps/ @EOBGames @ShizCalev @Maurukas
/tools/build/ @MrStonedOne @stylemistake
/tools/LinuxOneShot/ @Cyberboss @MrStonedOne
/tools/tgs4_scripts/ @Cyberboss @MrStonedOne
+3 -3
View File
@@ -90,7 +90,7 @@
#define MAX_ATOM_OVERLAYS 100
#if !defined(CBT) && !defined(SPACEMAN_DMM)
#warn "Building with Dream Maker is no longer supported and will result in errors."
#warn "In order to build, run BUILD.bat in the root directory."
#warn "Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build."
#warn Building with Dream Maker is no longer supported and will result in errors.
#warn In order to build, run BUILD.bat in the root directory.
#warn Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build.
#endif
+1 -1
View File
@@ -127,7 +127,7 @@ const DefaultTarget = Juke.createTarget({
* Does not clean them up, as this is intended for TGS which
* clones new copies anyway.
*/
const prependDefines = (...defines) => {
const prependDefines = (...defines) => {
const dmeContents = fs.readFileSync(`${DME_NAME}.dme`);
const textToWrite = defines.map(define => `#define ${define}\n`);
fs.writeFileSync(`${DME_NAME}.dme`, `${textToWrite}\n${dmeContents}`);
+34 -10
View File
@@ -1,4 +1,4 @@
const { exec } = require('../juke');
const Juke = require('../juke');
const { stat } = require('./fs');
const { regQuery } = require('./winreg');
const fs = require('fs');
@@ -15,6 +15,7 @@ let dmPath;
* @param {{ defines?: string[] }} options
*/
const dm = async (dmeFile, options = {}) => {
// Get path to DM compiler
if (!dmPath) {
dmPath = await (async () => {
// Search in array of paths
@@ -60,22 +61,45 @@ const dm = async (dmeFile, options = {}) => {
);
})();
}
const { defines } = options;
// Get project basename
const dmeBaseName = dmeFile.replace(/\.dme$/, '');
// Make sure output files are writable
const testOutputFile = (name) => {
try {
fs.closeSync(fs.openSync(name, 'r+'));
}
catch (err) {
if (err && err.code === 'ENOENT') {
return;
}
if (err && err.code === 'EBUSY') {
Juke.logger.error(`File '${name}' is locked by the DreamDaemon process.`);
Juke.logger.error(`Stop the currently running server and try again.`);
throw new Juke.ExitCode(1);
}
throw err;
}
};
testOutputFile(`${dmeBaseName}.dmb`);
testOutputFile(`${dmeBaseName}.rsc`);
// Compile
const { defines } = options;
if (defines && defines.length > 0) {
const injectedContent = defines
.map(x => `#define ${x}\n`)
.join('');
fs.writeFileSync(`${dmeBaseName}.mdme`, injectedContent)
const dmeContent = fs.readFileSync(`${dmeBaseName}.dme`)
fs.appendFileSync(`${dmeBaseName}.mdme`, dmeContent)
await exec(dmPath, [`${dmeBaseName}.mdme`]);
fs.renameSync(`${dmeBaseName}.mdme.dmb`, `${dmeBaseName}.dmb`)
fs.renameSync(`${dmeBaseName}.mdme.rsc`, `${dmeBaseName}.rsc`)
fs.unlinkSync(`${dmeBaseName}.mdme`)
fs.writeFileSync(`${dmeBaseName}.mdme`, injectedContent);
const dmeContent = fs.readFileSync(`${dmeBaseName}.dme`);
fs.appendFileSync(`${dmeBaseName}.mdme`, dmeContent);
await Juke.exec(dmPath, [`${dmeBaseName}.mdme`]);
fs.writeFileSync(`${dmeBaseName}.dmb`, fs.readFileSync(`${dmeBaseName}.mdme.dmb`));
fs.writeFileSync(`${dmeBaseName}.rsc`, fs.readFileSync(`${dmeBaseName}.mdme.rsc`));
fs.unlinkSync(`${dmeBaseName}.mdme.dmb`);
fs.unlinkSync(`${dmeBaseName}.mdme.rsc`);
fs.unlinkSync(`${dmeBaseName}.mdme`);
}
else {
await exec(dmPath, dmeFile);
await Juke.exec(dmPath, [dmeFile]);
}
};
-18
View File
@@ -1,18 +0,0 @@
import { Parameter, ParameterMap } from './parameter';
declare type TaskArgs = [
/** Task name */
string,
/** Task arguments */
...string[]
];
/**
* Returns global flags and tasks, which is an array of this format:
* `[[taskName, ...taskArgs], ...]`
* @param args List of command line arguments
*/
export declare const prepareArgs: (args: string[]) => {
globalFlags: string[];
taskArgs: TaskArgs[];
};
export declare const parseArgs: (args: string[], parameters: Parameter[]) => ParameterMap;
export {};
-6
View File
@@ -1,6 +0,0 @@
import { SpawnOptionsWithoutStdio } from 'child_process';
export declare class ExitError extends Error {
code: number | null;
signal: string | null;
}
export declare const exec: (executable: string, args?: string[], options?: SpawnOptionsWithoutStdio) => Promise<void>;
-30
View File
@@ -1,30 +0,0 @@
/// <reference types="node" />
import fs from 'fs';
export declare class File {
readonly path: string;
private _stat?;
constructor(path: string);
get stat(): fs.Stats | null;
exists(): boolean;
get mtime(): Date | null;
touch(): void;
}
export declare class Glob {
readonly path: string;
constructor(path: string);
toFiles(): File[];
}
/**
* If true, source is newer than target.
*/
export declare const compareFiles: (sources: File[], targets: File[]) => string | false;
/**
* Returns file stats for the provided path, or null if file is
* not accessible.
*/
export declare const stat: (path: string) => fs.Stats | null;
/**
* Resolves a glob pattern and returns files that are safe
* to call `stat` on.
*/
export declare const resolveGlob: (globPath: string) => string[];
+167 -10
View File
@@ -1,11 +1,166 @@
import chalk from 'chalk';
import glob from 'glob';
import { exec } from './exec';
import { logger } from './logger';
import { createParameter as _createParameter } from './parameter';
import { RunnerConfig } from './runner';
import { createTarget as _createTarget } from './target';
export { exec, chalk, glob, logger };
// Generated by dts-bundle-generator v5.9.0
/// <reference types="node" />
import _chalk from 'chalk';
import { SpawnOptionsWithoutStdio } from 'child_process';
export declare class ExitCode extends Error {
code: number | null;
signal: string | null;
constructor(code: number | null, signal?: string | null);
}
export declare type ExecOptions = SpawnOptionsWithoutStdio & {
/**
* If `true`, this exec call will not pipe its output to stdio.
* @default false
*/
silent?: boolean;
/**
* Throw an exception on non-zero exit code.
* @default true
*/
throw?: boolean;
};
export declare type ExecReturn = {
/** Exit code of the program. */
code: number | null;
/** Signal received by the program which caused it to exit. */
signal: NodeJS.Signals | null;
/** Output collected from `stdout` */
stdout: string;
/** Output collected from `stderr` */
stderr: string;
/** A combined output collected from `stdout` and `stderr`. */
combined: string;
};
export declare const exec: (executable: string, args?: string[], options?: ExecOptions) => Promise<ExecReturn>;
export declare const logger: {
log: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
action: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
info: (...args: unknown[]) => void;
debug: (...args: unknown[]) => void;
};
export declare type ParameterType = (string | string[] | number | number[] | boolean | boolean[]);
export declare type ParameterStringType = ("string" | "string[]" | "number" | "number[]" | "boolean" | "boolean[]");
export declare type ParameterTypeByString<T extends ParameterStringType> = (T extends "string" ? string : T extends "string[]" ? string[] : T extends "number" ? number : T extends "number[]" ? number[] : T extends "boolean" ? boolean : T extends "boolean[]" ? boolean[] : never);
export declare type ParameterConfig<T extends ParameterStringType> = {
/**
* Parameter name, in "camelCase".
*/
readonly name: string;
/**
* Parameter type, one of:
* - `string`
* - `string[]`
* - `number`
* - `number[]`
* - `boolean`
* - `boolean[]`
*/
readonly type: T;
/**
* Short flag for use in CLI, can only be a single character.
*/
readonly alias?: string;
};
export declare type ParameterCreator = <T extends ParameterStringType>(config: ParameterConfig<T>) => Parameter<ParameterTypeByString<T>>;
declare class Parameter<T extends ParameterType = any> {
readonly name: string;
readonly type: ParameterStringType;
readonly alias?: string | undefined;
constructor(name: string, type: ParameterStringType, alias?: string | undefined);
isString(): T extends string | string[] ? true : false;
isNumber(): T extends number | number[] ? true : false;
isBoolean(): T extends boolean | boolean[] ? true : false;
isArray(): T extends Array<unknown> ? true : false;
toKebabCase(): string;
toConstCase(): string;
toCamelCase(): string;
}
export declare type ExecutionContext = {
/** Get parameter value. */
get: <T extends ParameterType>(parameter: Parameter<T>) => (T extends Array<unknown> ? T : T | null);
};
export declare type BooleanLike = boolean | null | undefined;
export declare type WithExecutionContext<R> = (context: ExecutionContext) => R | Promise<R>;
export declare type WithOptionalExecutionContext<R> = R | WithExecutionContext<R>;
export declare type DependsOn = WithOptionalExecutionContext<(Target | BooleanLike)[]>;
export declare type ExecutesFn = WithExecutionContext<unknown>;
export declare type OnlyWhenFn = WithExecutionContext<BooleanLike>;
export declare type FileIo = WithOptionalExecutionContext<(string | BooleanLike)[]>;
export declare type Target = {
name: string;
dependsOn: DependsOn;
executes?: ExecutesFn;
inputs: FileIo;
outputs: FileIo;
parameters: Parameter[];
onlyWhen?: OnlyWhenFn;
};
export declare type TargetConfig = {
/**
* Target name. This parameter is required.
*/
name: string;
/**
* Dependencies for this target. They will be ran before executing this
* target, and may run in parallel.
*/
dependsOn?: DependsOn;
/**
* Function that is delegated to the execution engine for building this
* target. It is normally an async function, which accepts a single
* argument - execution context (contains `get` for interacting with
* parameters).
*
* @example
* executes: async ({ get }) => {
* console.log(get(Parameter));
* },
*/
executes?: ExecutesFn;
/**
* Files that are consumed by this target.
*/
inputs?: FileIo;
/**
* Files that are produced by this target. Additionally, they are also
* touched every time target finishes executing in order to stop
* this target from re-running.
*/
outputs?: FileIo;
/**
* Parameters that are local to this task. Can be retrieved via `get`
* in the executor function.
*/
parameters?: Parameter[];
/**
* Target will run only when this function returns true. It accepts a
* single argument - execution context.
*/
onlyWhen?: OnlyWhenFn;
};
export declare type TargetCreator = (target: TargetConfig) => Target;
export declare type RunnerConfig = {
targets?: Target[];
default?: Target;
parameters?: Parameter[];
};
export declare const chalk: _chalk.Chalk & _chalk.ChalkFunction & {
supportsColor: false | _chalk.ColorSupport;
Level: _chalk.Level;
Color: ("black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright") | ("bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgGrey" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright");
ForegroundColor: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright";
BackgroundColor: "bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgGrey" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright";
Modifiers: "bold" | "reset" | "dim" | "italic" | "underline" | "inverse" | "hidden" | "strikethrough" | "visible";
stderr: _chalk.Chalk & {
supportsColor: false | _chalk.ColorSupport;
};
};
export declare const glob: typeof import("glob");
/**
* Configures Juke Build and starts executing targets.
*
@@ -13,11 +168,13 @@ export { exec, chalk, glob, logger };
* @returns Exit code of the whole runner process.
*/
export declare const setup: (config?: RunnerConfig) => Promise<number>;
export declare const createTarget: typeof _createTarget;
export declare const createParameter: typeof _createParameter;
export declare const createTarget: TargetCreator;
export declare const createParameter: ParameterCreator;
export declare const sleep: (time: number) => Promise<unknown>;
/**
* Resolves a glob pattern and returns files that are safe
* to call `stat` on.
*/
export declare const resolveGlob: (globPath: string) => string[];
export {};
+67 -33
View File
@@ -6173,7 +6173,9 @@ exports.parseArgs = parseArgs;
exports.__esModule = true;
exports.exec = exports.ExitError = void 0;
exports.exec = exports.ExitCode = void 0;
var _chalk = _interopRequireDefault(__webpack_require__(/*! chalk */ "./.yarn/cache/chalk-npm-4.1.1-f1ce6bae57-445c12db7a.zip/node_modules/chalk/source/index.js"));
var _child_process = __webpack_require__(/*! child_process */ "child_process");
@@ -6181,6 +6183,8 @@ var _path = __webpack_require__(/*! path */ "path");
var _fs = __webpack_require__(/*! ./fs */ "./src/fs.ts");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const children = new Set();
const killChildren = () => {
@@ -6238,18 +6242,25 @@ const exceptionHandler = err => {
process.on('unhandledRejection', exceptionHandler);
process.on('uncaughtException', exceptionHandler);
class ExitError extends Error {
constructor(...args) {
super(...args);
class ExitCode extends Error {
constructor(code, signal) {
super('Process exited with code: ' + code);
this.code = null;
this.signal = null;
this.code = code;
this.signal = signal != null ? signal : null;
}
}
exports.ExitError = ExitError;
exports.ExitCode = ExitCode;
const exec = (executable, args = [], options = {}) => {
const {
silent = false,
throw: canThrow = true,
...spawnOptions
} = options;
return new Promise((resolve, reject) => {
// If executable exists relative to the current directory,
// use that executable, otherwise spawn should fall back to
@@ -6258,27 +6269,50 @@ const exec = (executable, args = [], options = {}) => {
executable = (0, _path.resolve)(executable);
}
const child = (0, _child_process.spawn)(executable, args, options);
if (process.env.JUKE_DEBUG) {
console.log(_chalk.default.grey('$', executable, ...args));
}
const child = (0, _child_process.spawn)(executable, args, spawnOptions);
children.add(child);
child.stdout.pipe(process.stdout, {
end: false
let stdout = '';
let stderr = '';
let combined = '';
child.stdout.on('data', data => {
if (!silent) {
process.stdout.write(data);
}
stdout += data;
combined += data;
});
child.stderr.pipe(process.stderr, {
end: false
child.stderr.on('data', data => {
if (!silent) {
process.stderr.write(data);
}
stderr += data;
combined += data;
});
child.stdin.end();
child.on('error', err => reject(err));
child.on('exit', (code, signal) => {
children.delete(child);
if (code !== 0) {
const error = new ExitError('Process exited with code: ' + code);
if (code !== 0 && canThrow) {
const error = new ExitCode(code);
error.code = code;
error.signal = signal;
reject(error);
} else {
resolve();
return;
}
resolve({
code,
signal,
stdout,
stderr,
combined
});
});
});
};
@@ -6503,7 +6537,7 @@ exports.Parameter = exports.createParameter = void 0;
var _stringcase = __webpack_require__(/*! stringcase */ "./.yarn/cache/stringcase-npm-4.3.1-2f1c329337-c81a3a4ab4.zip/node_modules/stringcase/lib/index.js");
const createParameter = options => new Parameter(options.name, options.type, options.alias);
const createParameter = config => new Parameter(config.name, config.type, config.alias);
exports.createParameter = createParameter;
@@ -6863,7 +6897,7 @@ class Worker {
const timeStr = _chalk.default.magenta(time);
if (err instanceof _exec.ExitError) {
if (err instanceof _exec.ExitCode) {
const codeStr = _chalk.default.red(err.code);
_logger.logger.error(`Target '${nameStr}' failed in ${timeStr}, exit code: ${codeStr}`);
@@ -7021,7 +7055,7 @@ module.exports = require("util");;
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
@@ -7035,17 +7069,17 @@ module.exports = require("util");;
/******/ loaded: false,
/******/ exports: {}
/******/ };
/******/
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/************************************************************************/
/******/ /* webpack/runtime/node module decorator */
/******/ (() => {
@@ -7055,7 +7089,7 @@ module.exports = require("util");;
/******/ return module;
/******/ };
/******/ })();
/******/
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
@@ -7067,21 +7101,18 @@ var exports = __webpack_exports__;
exports.__esModule = true;
exports.resolveGlob = exports.sleep = exports.createParameter = exports.createTarget = exports.setup = void 0;
exports.resolveGlob = exports.sleep = exports.createParameter = exports.createTarget = exports.setup = exports.glob = exports.chalk = void 0;
var _chalk = _interopRequireDefault(__webpack_require__(/*! chalk */ "./.yarn/cache/chalk-npm-4.1.1-f1ce6bae57-445c12db7a.zip/node_modules/chalk/source/index.js"));
exports.chalk = _chalk.default;
var _chalk2 = _interopRequireDefault(__webpack_require__(/*! chalk */ "./.yarn/cache/chalk-npm-4.1.1-f1ce6bae57-445c12db7a.zip/node_modules/chalk/source/index.js"));
var _fs = _interopRequireDefault(__webpack_require__(/*! fs */ "fs"));
var _glob = _interopRequireDefault(__webpack_require__(/*! glob */ "./.yarn/cache/glob-npm-7.1.7-5698ad9c48-352f74f082.zip/node_modules/glob/glob.js"));
exports.glob = _glob.default;
var _glob2 = __webpack_require__(/*! glob */ "./.yarn/cache/glob-npm-7.1.7-5698ad9c48-352f74f082.zip/node_modules/glob/glob.js");
var _exec = __webpack_require__(/*! ./exec */ "./src/exec.ts");
exports.exec = _exec.exec;
exports.ExitCode = _exec.ExitCode;
var _logger = __webpack_require__(/*! ./logger */ "./src/logger.ts");
@@ -7095,6 +7126,10 @@ var _target = __webpack_require__(/*! ./target */ "./src/target.ts");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const chalk = _chalk2.default;
exports.chalk = chalk;
const glob = _glob2.glob;
exports.glob = glob;
const autoParameters = [];
const autoTargets = [];
/**
@@ -7149,11 +7184,10 @@ const sleep = time => new Promise(resolve => setTimeout(resolve, time));
exports.sleep = sleep;
const resolveGlob = globPath => {
const unsafePaths = _glob.default.sync(globPath, {
const unsafePaths = glob.sync(globPath, {
strict: false,
silent: true
});
const safePaths = [];
for (let path of unsafePaths) {
@@ -7173,4 +7207,4 @@ exports.resolveGlob = resolveGlob;
/******/ return __webpack_exports__;
/******/ })()
;
});
});
-8
View File
@@ -1,8 +0,0 @@
export declare const logger: {
log: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
action: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
info: (...args: unknown[]) => void;
debug: (...args: unknown[]) => void;
};
-39
View File
@@ -1,39 +0,0 @@
export declare type ParameterType = (string | string[] | number | number[] | boolean | boolean[]);
export declare type ParameterStringType = ('string' | 'string[]' | 'number' | 'number[]' | 'boolean' | 'boolean[]');
declare type ParameterTypeByString<T extends ParameterStringType> = (T extends 'string' ? string : T extends 'string[]' ? string[] : T extends 'number' ? number : T extends 'number[]' ? number[] : T extends 'boolean' ? boolean : T extends 'boolean[]' ? boolean[] : never);
export declare type ParameterMap = Map<Parameter, unknown[]>;
declare type ParameterOptions<T extends ParameterStringType> = {
/**
* Parameter name, in "camelCase".
*/
readonly name: string;
/**
* Parameter type, one of:
* - `string`
* - `string[]`
* - `number`
* - `number[]`
* - `boolean`
* - `boolean[]`
*/
readonly type: T;
/**
* Short flag for use in CLI, can only be a single character.
*/
readonly alias?: string;
};
export declare const createParameter: <T extends ParameterStringType>(options: ParameterOptions<T>) => Parameter<ParameterTypeByString<T>>;
export declare class Parameter<T extends ParameterType = any> {
readonly name: string;
readonly type: ParameterStringType;
readonly alias?: string | undefined;
constructor(name: string, type: ParameterStringType, alias?: string | undefined);
isString(): T extends string | string[] ? true : false;
isNumber(): T extends number | number[] ? true : false;
isBoolean(): T extends boolean | boolean[] ? true : false;
isArray(): T extends Array<unknown> ? true : false;
toKebabCase(): string;
toConstCase(): string;
toCamelCase(): string;
}
export {};
-35
View File
@@ -1,35 +0,0 @@
/// <reference types="node" />
import EventEmitter from 'events';
import { Parameter } from './parameter';
import { ExecutionContext, Target } from './target';
export declare type RunnerConfig = {
targets?: Target[];
default?: Target;
parameters?: Parameter[];
};
export declare const runner: {
defaultTarget?: Target | undefined;
targets: Target[];
parameters: Parameter[];
workers: Worker[];
configure(config: RunnerConfig): void;
start(): Promise<number>;
};
declare class Worker {
readonly target: Target;
readonly context: ExecutionContext;
readonly dependsOn: Target[];
dependencies: Set<Target>;
generator?: AsyncGenerator;
emitter: EventEmitter;
hasFailed: boolean;
constructor(target: Target, context: ExecutionContext, dependsOn: Target[]);
resolveDependency(target: Target): void;
rejectDependency(target: Target): void;
start(): void;
onFinish(fn: () => void): void;
onFail(fn: () => void): void;
private debugLog;
private process;
}
export {};
-66
View File
@@ -1,66 +0,0 @@
import { Parameter, ParameterType } from './parameter';
export declare type ExecutionContext = {
/** Get parameter value. */
get: <T extends ParameterType>(parameter: Parameter<T>) => (T extends Array<unknown> ? T : T | null);
};
declare type BooleanLike = boolean | null | undefined;
declare type WithExecutionContext<R> = (context: ExecutionContext) => R | Promise<R>;
declare type WithOptionalExecutionContext<R> = R | WithExecutionContext<R>;
declare type DependsOn = WithOptionalExecutionContext<(Target | BooleanLike)[]>;
declare type ExecutesFn = WithExecutionContext<unknown>;
declare type OnlyWhenFn = WithExecutionContext<BooleanLike>;
export declare type FileIo = WithOptionalExecutionContext<(string | BooleanLike)[]>;
export declare type Target = {
name: string;
dependsOn: DependsOn;
executes?: ExecutesFn;
inputs: FileIo;
outputs: FileIo;
parameters: Parameter[];
onlyWhen?: OnlyWhenFn;
};
declare type TargetConfig = {
/**
* Target name. This parameter is required.
*/
name: string;
/**
* Dependencies for this target. They will be ran before executing this
* target, and may run in parallel.
*/
dependsOn?: DependsOn;
/**
* Function that is delegated to the execution engine for building this
* target. It is normally an async function, which accepts a single
* argument - execution context (contains `get` for interacting with
* parameters).
*
* @example
* executes: async ({ get }) => {
* console.log(get(Parameter));
* },
*/
executes?: ExecutesFn;
/**
* Files that are consumed by this target.
*/
inputs?: FileIo;
/**
* Files that are produced by this target. Additionally, they are also
* touched every time target finishes executing in order to stop
* this target from re-running.
*/
outputs?: FileIo;
/**
* Parameters that are local to this task. Can be retrieved via `get`
* in the executor function.
*/
parameters?: Parameter[];
/**
* Target will run only when this function returns true. It accepts a
* single argument - execution context.
*/
onlyWhen?: OnlyWhenFn;
};
export declare const createTarget: (target: TargetConfig) => Target;
export {};