{"id":42376680,"url":"https://github.com/stylemistake/juke-build","last_synced_at":"2026-01-27T20:50:44.489Z","repository":{"id":57286864,"uuid":"370184932","full_name":"stylemistake/juke-build","owner":"stylemistake","description":"General Purpose Build System with JavaScript DSL for Node.js.","archived":false,"fork":false,"pushed_at":"2022-01-28T07:36:36.000Z","size":3927,"stargazers_count":34,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-19T14:58:54.270Z","etag":null,"topics":["build-automation","build-tool","javascript","juke","make","node","nuke","task-runner","tasks"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stylemistake.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-05-24T00:22:54.000Z","updated_at":"2025-05-21T00:53:40.000Z","dependencies_parsed_at":"2022-08-31T08:51:21.722Z","dependency_job_id":null,"html_url":"https://github.com/stylemistake/juke-build","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/stylemistake/juke-build","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylemistake%2Fjuke-build","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylemistake%2Fjuke-build/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylemistake%2Fjuke-build/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylemistake%2Fjuke-build/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stylemistake","download_url":"https://codeload.github.com/stylemistake/juke-build/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylemistake%2Fjuke-build/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28822136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T18:44:20.126Z","status":"ssl_error","status_checked_at":"2026-01-27T18:44:09.161Z","response_time":168,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build-automation","build-tool","javascript","juke","make","node","nuke","task-runner","tasks"],"created_at":"2026-01-27T20:50:43.768Z","updated_at":"2026-01-27T20:50:44.476Z","avatar_url":"https://github.com/stylemistake.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![JUKE build](https://github.com/stylemistake/juke-build/blob/master/assets/juke-build.png)\n\n\u003e General Purpose Build System with JavaScript DSL for Node.js.\n\u003e Inspired by [NUKE](https://nuke.build/).\n\nThis project is reaching a mature stage, although a lot of features are still in development. Take a look at our [roadmap](https://github.com/stylemistake/juke-build/projects/1).\n\n## Project goals\n\n### Simplicity\n\nEverything should be as simple as possible in all technical aspects. Builds are written in pure JavaScript and provide only the bare minimum for getting the job done. TypeScript is supported, but not required.\n\nCurrently it packs the following:\n\n- A robust dependency model between targets\n- File timestamp checker for inputs/outputs\n- Built-in CLI argument (and environment) parser with a strongly typed Parameter API.\n- Asynchronous execution of external programs via `Juke.exec()`\n\nYou can bring your own tools into the mix, e.g. the glorious [google/zx](https://github.com/google/zx), or native JavaScript tooling, e.g. [webpack](https://webpack.js.org/), with no restrictions imposed by our build framework.\n\n### Minimal dependencies\n\nBuild system should be native to JavaScript and Node.js, and require nothing but the Node.js executable, i.e. no dependency on npm/yarn or TypeScript compiler.\n\n### Strongly typed\n\nStrongly typed API with fully instrospectible build scripts that are written in plain JavaScript, which allows us to parse the build script and generate definition files for tighter integration with other tooling (e.g. CI).\n\n## How to build\n\n```\n./build.mjs\n```\n\n## General usage\n\nCopy contents of the `dist` folder anywhere you want to use Juke (and rename it to `juke`), then create a javascript file for the build script with the following contents (pick one):\n\n**ES modules variant (recommended):**\n\n```ts\n// build.mjs\nimport Juke from './juke/index.js';\n\nJuke.setup({ file: import.meta.url });\n\n// TODO: Declare targets here\nexport const MyTarget = new Juke.Target({\n  // ...\n});\n```\n\n**ES modules with require:**\n\n```ts\n// build.mjs\nimport { createRequire } from 'module';\n\nconst require = createRequire(import.meta.url);\nconst Juke = require('./juke');\n\nJuke.setup({ file: import.meta.url });\n\n// TODO: Declare targets here\nexport const MyTarget = new Juke.Target({\n  // ...\n});\n```\n\n**CommonJS modules variant:**\n\n```ts\n// build.cjs\nconst Juke = require('./juke');\n\nJuke.setup({ file: __filename });\n\n// TODO: Declare targets here\nconst MyTarget = new Juke.Target({\n  // ...\n});\n\n// TODO: Export targets here\nmodule.exports = {\n  MyTarget,\n};\n```\n\nWe recommend using an ES module for the build script, because it allows exporting targets/parameters with a much shorter syntax.\n\n### Create targets\n\nTarget is a simple container for your build script that defines how it should be executed in relation to other targets. It may have dependencies on other targets, and may have various other conditions for executing the target.\n\n```ts\nexport const Target = new Juke.Target({\n  executes: async () =\u003e {\n    console.log('Hello, world!');\n  },\n});\n```\n\n\u003e Notice: When referencing an unexported target, it must have a `name` property, which is used in CLI for specifying (and displaying) the target. If you forget to specify a `name`, it will be displayed as `undefined` during execution.\n\u003e\n\u003e ```ts\n\u003e const Target = new Juke.Target({\n\u003e   name: 'foo',\n\u003e   // ...\n\u003e });\n\u003e ```\n\u003e\n\u003e Normally, name is derived from the name of the exported variable (minus the `Target` suffix).\n\n### Declare dependencies\n\n```ts\nexport const Target = new Juke.Target({\n  dependsOn: [OtherTarget],\n  // ...\n});\n```\n\n### Set a default target\n\nWhen no target is provided via CLI, Juke will execute the default target.\n\n```ts\nexport const Target = new Juke.Target({\n  // ...\n});\n\nexport default Target;\n```\n\n### Declare file inputs and outputs\n\nIf your target consumes and creates files, you can declare them on the target, so it would check whether it actually needs to rebuild.\n\nIf any input file is newer than the output file, target will be rebuilt, and skipped otherwise.\n\nSupports globs.\n\n```ts\nexport const Target = new Juke.Target({\n  inputs: ['package.json', 'src/**/*.js'],\n  outputs: ['dest/bundle.js'],\n  // ...\n});\n```\n\n### Create parameters\n\nAvailable parameter types are: `string`, `number` and `boolean`. You may add a `[]` suffix to the type to make it an array.\n\nTo provide a parameter via CLI, you can either specify it by name (e.g. `--name`), or its alias (e.g. `-N`). If parameter is not a `boolean` type, value will be expected, which you can provide via `--name=value` or `-Nvalue`.\n\nTo fetch the parameter's value, you can use the `get` helper, which is exposed on the target's context.\n\n```ts\nexport const FileParameter = new Juke.Parameter({\n  type: 'string[]',\n  alias: 'f',\n});\n\nexport const Target = new Juke.Target({\n  executes: async ({ get }) =\u003e {\n    const files = get(FileParameter);\n    console.log('Parameter values:', files);\n  },\n  // ...\n});\n```\n\nYou can also dynamically set up target dependencies using binary expressions:\n\n```ts\nexport const Target = new Juke.Target({\n  dependsOn: ({ get }) =\u003e [\n    get(FileParameter).includes('foo') \u0026\u0026 FooTarget,\n  ],\n  // ...\n});\n```\n\nIf you simply need access to arguments passed to the target, you can use the `args` context variable. Note, that you can only pass arguments that begin with `-` or `--`, because all other arguments are normally treated as targets to build.\n\n```ts\nexport const Target = new Juke.Target({\n  executes: async ({ args }) =\u003e {\n    console.log('Passed arguments:', args);\n  },\n});\n```\n\nContext is available on these properties (when using a function syntax):\n\n- `dependsOn`\n- `inputs`\n- `outputs`\n- `onlyWhen`\n- `executes`\n\n\u003e Notice: When referencing an unexported parameter, it must have a `name`, which is used in CLI for specifying the parameter.\n\u003e\n\u003e ```ts\n\u003e const FileParameter = new Juke.Parameter({\n\u003e   name: 'file',\n\u003e });\n\u003e ```\n\u003e\n\u003e Normally, name is derived from the name of the exported variable (minus the `Parameter` suffix, if it exists).\n\n\n### Conditionally run targets\n\nIf you need more control over when the target builds, you can provide a custom condition using `onlyWhen`. Target will build only when the condition is `true`.\n\nFunction can be `async` if it has to be, target will wait for all promises to resolve.\n\n```ts\nexport const Target = new Juke.Target({\n  onlyWhen: ({ get }) =\u003e get(BuildModeParameter) === BUILD_ALL,\n  // ...\n});\n```\n\n### Execute an external program\n\nJuke provides a handy `Juke.exec` helper.\n\n```ts\nexport const Target = new Juke.Target({\n  executes: async () =\u003e {\n    await Juke.exec('yarn', ['install']);\n  },\n});\n```\n\nOn program completion, you get its stdout and stderr. In case, when you need to run a program just to parse its output, you can set a `silent` option to stop it from piping its output to `stdio`.\n\n```ts\nconst { stdout, stderr, combined } = await Juke.exec(command, ...args, {\n  silent: true,\n});\n```\n\nIt throws by default if program has exited with a non-zero exit code (or was killed by a non-EXIT signal). If uncatched, error propagates through Juke and puts dependent targets into a failed state.\n\nYou can disable this behavior via:\n\n```ts\nconst { code } = Juke.exec(command, ...args, {\n  throw: false,\n});\n```\n\nYou can also simulate an exit code by rethrowing it yourself.\n\n```ts\nthrow new Juke.ExitCode(1);\n```\n\n### Run the build\n\nYou can build targets by specifying their names via CLI.\n\nEvery flag that you specify via CLI is transformed into parameters, and their names are canonically written in `--kebab-case`.\n\n```\n./build.js [globalFlags] task-1 [flagsLocalToTask1] task-2 [flagsLocalToTask2]\n```\n\nTo specify an array of parameters, you can simply specify the same flag multiple times:\n\n```\n./build.js task-1 --foo=A --foo=B\n```\n\nYou can also specify parameters via the environment. Environment variable names must be written in `CONSTANT_CASE`. If this parameter is an array, you can use a comma to separate the values.\n\n```\nFOO=A,B ./build.js task-1\n```\n\n### Single target mode\n\nYou can specify that Juke CLI should only accept a single target to run.\n\n```ts\nJuke.setup({\n  file: import.meta.url,\n  singleTarget: true,\n});\n```\n\nThis mode means that all arguments after the first task name are considered as its arguments, regardless of whether they are flags or not.\n\n```\n./build.js [globalFlags] task [argsLocalToTask]\n```\n\n### Various helpers\n\n#### `Juke.chdir(directory: string, relativeTo?: string)`\n\nChanges directory relative to another file or directory. Most commonly used as following:\n\n```ts\nJuke.chdir('..', import.meta.url);\n```\n\n#### `Juke.rm(path: string, options = {})`\n\nRemoves files and directories (synchronously). Supports a small subset of Node 16 options for `fs.rmSync`. Supports globs.\n\n```ts\nJuke.rm('**/node_modules', { recursive: true });\n```\n\n#### `Juke.glob(pattern: string)`\n\nUnix style pathname pattern expansion.\n\nPerforms a search matching a specified pattern according to the rules of the `glob` npm package. Path can be either absolute or relative, and can contain shell-style wildcards. Broken symlinks are included in the results (as in the shell). Whether or not the results are sorted depends on the file system.\n\nReturns a possibly empty list of file paths.\n\n## Examples\n\n[Our own build pipeline](https://github.com/stylemistake/juke-build/blob/master/build.mjs)\n\n[/tg/station13 build pipeline](https://github.com/tgstation/tgstation/blob/master/tools/build/build.js)\n\n\u003cdetails\u003e\n  \u003csummary\u003eScreenshot\u003c/summary\u003e\n  \u003cimg alt=\"image\" src=\"https://user-images.githubusercontent.com/1516236/123164088-26166580-d47b-11eb-9b03-b048274a4499.png\"\u003e\n\u003c/details\u003e\n\n## License\n\nSource code is available under the **MIT** license.\n\nThe Authors retain all copyright to their respective work here submitted.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstylemistake%2Fjuke-build","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstylemistake%2Fjuke-build","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstylemistake%2Fjuke-build/lists"}