diff --git a/.tools/bun/bin/moon b/.tools/bun/bin/moon new file mode 120000 index 0000000..0810829 --- /dev/null +++ b/.tools/bun/bin/moon @@ -0,0 +1 @@ +../install/global/node_modules/@moonrepo/cli/moon.js \ No newline at end of file diff --git a/.tools/bun/bin/moonx b/.tools/bun/bin/moonx new file mode 120000 index 0000000..4f3b682 --- /dev/null +++ b/.tools/bun/bin/moonx @@ -0,0 +1 @@ +../install/global/node_modules/@moonrepo/cli/moonx.js \ No newline at end of file diff --git a/.tools/bun/install/global/bun.lock b/.tools/bun/install/global/bun.lock new file mode 100644 index 0000000..099dc8a --- /dev/null +++ b/.tools/bun/install/global/bun.lock @@ -0,0 +1,28 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "dependencies": { + "@moonrepo/cli": "^2.0.4", + }, + }, + }, + "packages": { + "@moonrepo/cli": ["@moonrepo/cli@2.0.4", "", { "dependencies": { "detect-libc": "^2.1.2" }, "optionalDependencies": { "@moonrepo/core-linux-arm64-gnu": "2.0.4", "@moonrepo/core-linux-arm64-musl": "2.0.4", "@moonrepo/core-linux-x64-gnu": "2.0.4", "@moonrepo/core-linux-x64-musl": "2.0.4", "@moonrepo/core-macos-arm64": "2.0.4", "@moonrepo/core-windows-x64-msvc": "2.0.4" }, "bin": { "moon": "moon.js", "moonx": "moonx.js" } }, "sha512-cP62Fa7hzToEi0I2i3Gx7zhPPdimVCeW8WqbSlE5y6fhjH38g1N77vZg4A6rcauDEUl/cpA5+vWQyxm4KCsqUA=="], + + "@moonrepo/core-linux-arm64-gnu": ["@moonrepo/core-linux-arm64-gnu@2.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-pvMgTfYUN8ARvhOgVumR1j1XQY0V+59qz+Bi9BBmgOJsaB/QosGGWkCfdV2dChpzfE9AELnjIBgrazUGcOX9KA=="], + + "@moonrepo/core-linux-arm64-musl": ["@moonrepo/core-linux-arm64-musl@2.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-lF+KuN8ymTR+kynI7OU+CN//V6hSGL9eq8+7VUVFY3cRobyzjEulB7xkPgVYH2C81E8TUK2tp1R79Y8nOgEHaA=="], + + "@moonrepo/core-linux-x64-gnu": ["@moonrepo/core-linux-x64-gnu@2.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-0auEy/jyMm5vjIZy/dLdrIoOJ0hnoxcEk+veBGwZatS65dSKEvXdUYrYSGWTRvykbsXDBTAaXFUtj1enGzIXmg=="], + + "@moonrepo/core-linux-x64-musl": ["@moonrepo/core-linux-x64-musl@2.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-h7b7uw8GdhTyZg2R7rHA04mqBIvYvcHtgtVBUXwwZQlh6jprFFtxuvehAdK32PN8sxygwRa+AdTfgq3vZHXFmw=="], + + "@moonrepo/core-macos-arm64": ["@moonrepo/core-macos-arm64@2.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gz2FO0xRHUOXQjzBAz97S4pShL0YbTeNWLdCuRyjX8SNU1l82TX+l20e6OKfyt6jThWvCZeSiolx7W02xDT+iA=="], + + "@moonrepo/core-windows-x64-msvc": ["@moonrepo/core-windows-x64-msvc@2.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-LjwpvjWTeusQjpqLNK7N7tVr/IQSd0W8v0L7fUIKBXNGmuONhDCHwgDqSs3yVzwcQMluiHM2qwu9YSKIEVfTIw=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + } +} diff --git a/.tools/bun/install/global/node_modules/@moonrepo/cli/LICENSE b/.tools/bun/install/global/node_modules/@moonrepo/cli/LICENSE new file mode 100644 index 0000000..54b8e55 --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/cli/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2021 moonrepo, Inc., Miles Johnson + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.tools/bun/install/global/node_modules/@moonrepo/cli/README.md b/.tools/bun/install/global/node_modules/@moonrepo/cli/README.md new file mode 100644 index 0000000..f8ec70a --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/cli/README.md @@ -0,0 +1,104 @@ +# @moonrepo/cli + +The official CLI for [moon](https://moonrepo.dev), a build system and repo management tool for the +web ecosystem, written in Rust! Supports JavaScript, TypeScript, Bash, Rust, Go, and much more! + +- [Documentation](https://moonrepo.dev/docs) +- [Getting started](https://moonrepo.dev/docs/install) +- [Feature comparison](https://moonrepo.dev/docs/comparison) +- [FAQ](https://moonrepo.dev/docs/faq) + +## Installation + +moon can be installed with bash: + +```shell +curl -fsSL https://moonrepo.dev/install/moon.sh | bash +``` + +Or with npm, pnpm, or yarn. + +```shell +yarn add --dev @moonrepo/cli +``` + +Once installed, initialize moon in your repository. + +```shell +moon init +``` + +## Usage + +Once [projects](https://moonrepo.dev/docs/create-project) and +[tasks](https://moonrepo.dev/docs/create-task) have been configured, tasks can be ran with: + +```bash +# Run `lint` in project `app` +moon run app:lint + +# Run `lint` in all projects +moon run :lint +``` + +## Why use moon? + +Working in the JavaScript ecosystem can be very involved, especially when it comes to managing a +repository effectively. Which package manager to use? Which Node.js version to use? How to import +node modules? How to build packages? So on and so forth. moon aims to streamline this entire process +and provide a first-class developer experience. + +- **Increased productivity** - With [Rust](https://www.rust-lang.org/) as our foundation, we can + ensure robust speeds, high performance, and low memory usage. Instead of long builds blocking you, + focus on your work. +- **Exceptional developer experience** - As veterans of the JavaScript ecosystem, we're well aware + of the pain points and frustrations. Our goal is to mitigate and overcome these obstacles. +- **Incremental adoption** - At its core, moon has been designed to be adopted incrementally and is + _not_ an "all at once adoption". Migrate project-by-project, or task-by-task, it's up to you! +- **Reduced scripts confusion** - `package.json` scripts can become unwieldy, very quickly. No more + duplicating the same script into every package, or reverse-engineering which root scripts to use. + With moon, all you need to know is the project name, and a task name. +- **Ensure correct versions** - Whether it's Node.js or npm, ensure the same version of each tool is + the same across _every_ developer's environment. No more wasted hours of debugging. +- **Automation built-in** - When applicable, moon will automatically install `node_modules`, or sync + package dependencies, or even sync TypeScript project references. +- And of course, the amazing list of features below! + +## Features + +> Not all features are currently supported, view the documentation for an accurate list! + +#### Management + +- **Smart hashing** - Collects inputs from multiple sources to ensure builds are deterministic and + reproducible. +- **Remote caching** - Persists builds, hashes, and caches between teammates and CI/CD environments. +- **Integrated toolchain** - Automatically downloads and installs explicit versions of Node.js and + other tools for consistency across the entire workspace or per project. +- **Multi-platform** - Runs on common development platforms: Linux, macOS, and Windows. + +#### Organization + +- **Project graph** - Generates a project graph for dependency and dependent relationships. +- **Code generation** - Easily scaffold new applications, libraries, tooling, and more! +- **Dependency workspaces** - Works alongside package manager workspaces so that projects have + distinct dependency trees. +- **Code ownership** - Declare owners, maintainers, support channels, and more. Generate CODEOWNERS. + +#### Orchestration + +- **Dependency graph** - Generates a dependency graph to increase performance and reduce workloads. +- **Action pipeline** - Executes actions in parallel and in order using a thread pool and our + dependency graph. +- **Action distribution** - Distributes actions across multiple machines to increase throughput. +- **Incremental builds** - With our smart hashing, only rebuild projects that have been changed + since the last build. + +#### Notification + +- **Flakiness detection** - Reduce flaky builds with automatic retries and passthrough settings. +- **Webhook events** - Receive a webhook for every event in the pipeline. Useful for metrics + gathering and insights. +- **Terminal notifications** - Receives notifications in your chosen terminal when builds are + successful... or are not. +- **Git hooks** - Manage Git hooks to enforce workflows and requirements for contributors. diff --git a/.tools/bun/install/global/node_modules/@moonrepo/cli/moon.js b/.tools/bun/install/global/node_modules/@moonrepo/cli/moon.js new file mode 100755 index 0000000..df47a4d --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/cli/moon.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +const cp = require('child_process'); +const { findMoonExe } = require('./utils'); + +const result = cp.spawnSync(findMoonExe(), process.argv.slice(2), { + shell: false, + stdio: 'inherit', +}); + +if (result.error) { + throw result.error; +} + +process.exitCode = result.status; diff --git a/.tools/bun/install/global/node_modules/@moonrepo/cli/moonx.js b/.tools/bun/install/global/node_modules/@moonrepo/cli/moonx.js new file mode 100755 index 0000000..8b9bd7d --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/cli/moonx.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +const cp = require('child_process'); +const { findMoonxExe } = require('./utils'); + +const result = cp.spawnSync(findMoonxExe(), process.argv.slice(2), { + shell: false, + stdio: 'inherit', +}); + +if (result.error) { + throw result.error; +} + +process.exitCode = result.status; diff --git a/.tools/bun/install/global/node_modules/@moonrepo/cli/package.json b/.tools/bun/install/global/node_modules/@moonrepo/cli/package.json new file mode 100644 index 0000000..1c0be21 --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/cli/package.json @@ -0,0 +1,39 @@ +{ + "name": "@moonrepo/cli", + "version": "2.0.4", + "type": "commonjs", + "description": "moon command line and core system.", + "keywords": [ + "moon", + "repo", + "cli", + "core" + ], + "files": [ + "moon.js", + "moonx.js", + "utils.js" + ], + "author": "Miles Johnson", + "license": "MIT", + "bin": { + "moon": "moon.js", + "moonx": "moonx.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/moonrepo/moon", + "directory": "packages/cli" + }, + "dependencies": { + "detect-libc": "^2.1.2" + }, + "optionalDependencies": { + "@moonrepo/core-linux-arm64-gnu": "2.0.4", + "@moonrepo/core-linux-arm64-musl": "2.0.4", + "@moonrepo/core-linux-x64-gnu": "2.0.4", + "@moonrepo/core-linux-x64-musl": "2.0.4", + "@moonrepo/core-macos-arm64": "2.0.4", + "@moonrepo/core-windows-x64-msvc": "2.0.4" + } +} diff --git a/.tools/bun/install/global/node_modules/@moonrepo/cli/utils.js b/.tools/bun/install/global/node_modules/@moonrepo/cli/utils.js new file mode 100644 index 0000000..3dfa7d7 --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/cli/utils.js @@ -0,0 +1,51 @@ +const fs = require('fs'); +const path = require('path'); + +const isLinux = process.platform === 'linux'; +const isMacos = process.platform === 'darwin'; +const isWindows = process.platform === 'win32'; + +const platform = isWindows ? 'windows' : isMacos ? 'macos' : process.platform; +const arch = + process.env['npm_config_user_agent'] && process.env['npm_config_user_agent'].match(/^bun.*arm64$/) + ? 'arm64' + : process.arch; // https://github.com/moonrepo/moon/issues/1103 +const parts = [platform, arch]; + +if (isLinux) { + const { familySync } = require('detect-libc'); + + if (familySync() === 'musl') { + parts.push('musl'); + // } else if (process.arch === 'arm') { + // parts.push('gnueabihf'); + } else { + parts.push('gnu'); + } +} else if (isWindows) { + parts.push('msvc'); +} + +const triple = parts.join('-'); + +function findExe(name) { + const pkgPath = require.resolve(`@moonrepo/core-${triple}/package.json`); + const exePath = path.join(path.dirname(pkgPath), isWindows ? `${name}.exe` : name); + + if (fs.existsSync(exePath)) { + return exePath; + } + + throw new Error(`moon executable "${exePath}" not found!`); +} + +function findMoonExe() { + return findExe('moon'); +} + +function findMoonxExe() { + return findExe('moonx'); +} + +exports.findMoonExe = findMoonExe; +exports.findMoonxExe = findMoonxExe; diff --git a/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/README.md b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/README.md new file mode 100644 index 0000000..4b9938b --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/README.md @@ -0,0 +1,3 @@ +# @moonrepo/core-macos-arm64 + +This is the `aarch64-apple-darwin` binary for `@moonrepo/cli`. diff --git a/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/moon b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/moon new file mode 100755 index 0000000..94e2c4f Binary files /dev/null and b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/moon differ diff --git a/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/moonx b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/moonx new file mode 100755 index 0000000..02030e8 Binary files /dev/null and b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/moonx differ diff --git a/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/package.json b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/package.json new file mode 100644 index 0000000..19e49c4 --- /dev/null +++ b/.tools/bun/install/global/node_modules/@moonrepo/core-macos-arm64/package.json @@ -0,0 +1,32 @@ +{ + "name": "@moonrepo/core-macos-arm64", + "version": "2.0.4", + "description": "macOS ARM64 (Silicon) binary for moon.", + "keywords": [ + "moon", + "repo", + "macos", + "darwin", + "arm64" + ], + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "author": "Miles Johnson", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/moonrepo/moon", + "directory": "packages/core-macos-arm64" + }, + "publishConfig": { + "access": "public", + "executableFiles": [ + "moon", + "moonx" + ] + } +} diff --git a/.tools/bun/install/global/node_modules/detect-libc/LICENSE b/.tools/bun/install/global/node_modules/detect-libc/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.tools/bun/install/global/node_modules/detect-libc/README.md b/.tools/bun/install/global/node_modules/detect-libc/README.md new file mode 100644 index 0000000..23212fd --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/README.md @@ -0,0 +1,163 @@ +# detect-libc + +Node.js module to detect details of the C standard library (libc) +implementation provided by a given Linux system. + +Currently supports detection of GNU glibc and MUSL libc. + +Provides asychronous and synchronous functions for the +family (e.g. `glibc`, `musl`) and version (e.g. `1.23`, `1.2.3`). + +The version numbers of libc implementations +are not guaranteed to be semver-compliant. + +For previous v1.x releases, please see the +[v1](https://github.com/lovell/detect-libc/tree/v1) branch. + +## Install + +```sh +npm install detect-libc +``` + +## API + +### GLIBC + +```ts +const GLIBC: string = 'glibc'; +``` + +A String constant containing the value `glibc`. + +### MUSL + +```ts +const MUSL: string = 'musl'; +``` + +A String constant containing the value `musl`. + +### family + +```ts +function family(): Promise; +``` + +Resolves asychronously with: + +* `glibc` or `musl` when the libc family can be determined +* `null` when the libc family cannot be determined +* `null` when run on a non-Linux platform + +```js +const { family, GLIBC, MUSL } = require('detect-libc'); + +switch (await family()) { + case GLIBC: ... + case MUSL: ... + case null: ... +} +``` + +### familySync + +```ts +function familySync(): string | null; +``` + +Synchronous version of `family()`. + +```js +const { familySync, GLIBC, MUSL } = require('detect-libc'); + +switch (familySync()) { + case GLIBC: ... + case MUSL: ... + case null: ... +} +``` + +### version + +```ts +function version(): Promise; +``` + +Resolves asychronously with: + +* The version when it can be determined +* `null` when the libc family cannot be determined +* `null` when run on a non-Linux platform + +```js +const { version } = require('detect-libc'); + +const v = await version(); +if (v) { + const [major, minor, patch] = v.split('.'); +} +``` + +### versionSync + +```ts +function versionSync(): string | null; +``` + +Synchronous version of `version()`. + +```js +const { versionSync } = require('detect-libc'); + +const v = versionSync(); +if (v) { + const [major, minor, patch] = v.split('.'); +} +``` + +### isNonGlibcLinux + +```ts +function isNonGlibcLinux(): Promise; +``` + +Resolves asychronously with: + +* `false` when the libc family is `glibc` +* `true` when the libc family is not `glibc` +* `false` when run on a non-Linux platform + +```js +const { isNonGlibcLinux } = require('detect-libc'); + +if (await isNonGlibcLinux()) { ... } +``` + +### isNonGlibcLinuxSync + +```ts +function isNonGlibcLinuxSync(): boolean; +``` + +Synchronous version of `isNonGlibcLinux()`. + +```js +const { isNonGlibcLinuxSync } = require('detect-libc'); + +if (isNonGlibcLinuxSync()) { ... } +``` + +## Licensing + +Copyright 2017 Lovell Fuller and others. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html) + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/.tools/bun/install/global/node_modules/detect-libc/index.d.ts b/.tools/bun/install/global/node_modules/detect-libc/index.d.ts new file mode 100644 index 0000000..4c0fb2b --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/index.d.ts @@ -0,0 +1,14 @@ +// Copyright 2017 Lovell Fuller and others. +// SPDX-License-Identifier: Apache-2.0 + +export const GLIBC: 'glibc'; +export const MUSL: 'musl'; + +export function family(): Promise; +export function familySync(): string | null; + +export function isNonGlibcLinux(): Promise; +export function isNonGlibcLinuxSync(): boolean; + +export function version(): Promise; +export function versionSync(): string | null; diff --git a/.tools/bun/install/global/node_modules/detect-libc/lib/detect-libc.js b/.tools/bun/install/global/node_modules/detect-libc/lib/detect-libc.js new file mode 100644 index 0000000..01299b4 --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/lib/detect-libc.js @@ -0,0 +1,313 @@ +// Copyright 2017 Lovell Fuller and others. +// SPDX-License-Identifier: Apache-2.0 + +'use strict'; + +const childProcess = require('child_process'); +const { isLinux, getReport } = require('./process'); +const { LDD_PATH, SELF_PATH, readFile, readFileSync } = require('./filesystem'); +const { interpreterPath } = require('./elf'); + +let cachedFamilyInterpreter; +let cachedFamilyFilesystem; +let cachedVersionFilesystem; + +const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true'; +let commandOut = ''; + +const safeCommand = () => { + if (!commandOut) { + return new Promise((resolve) => { + childProcess.exec(command, (err, out) => { + commandOut = err ? ' ' : out; + resolve(commandOut); + }); + }); + } + return commandOut; +}; + +const safeCommandSync = () => { + if (!commandOut) { + try { + commandOut = childProcess.execSync(command, { encoding: 'utf8' }); + } catch (_err) { + commandOut = ' '; + } + } + return commandOut; +}; + +/** + * A String constant containing the value `glibc`. + * @type {string} + * @public + */ +const GLIBC = 'glibc'; + +/** + * A Regexp constant to get the GLIBC Version. + * @type {string} + */ +const RE_GLIBC_VERSION = /LIBC[a-z0-9 \-).]*?(\d+\.\d+)/i; + +/** + * A String constant containing the value `musl`. + * @type {string} + * @public + */ +const MUSL = 'musl'; + +const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-'); + +const familyFromReport = () => { + const report = getReport(); + if (report.header && report.header.glibcVersionRuntime) { + return GLIBC; + } + if (Array.isArray(report.sharedObjects)) { + if (report.sharedObjects.some(isFileMusl)) { + return MUSL; + } + } + return null; +}; + +const familyFromCommand = (out) => { + const [getconf, ldd1] = out.split(/[\r\n]+/); + if (getconf && getconf.includes(GLIBC)) { + return GLIBC; + } + if (ldd1 && ldd1.includes(MUSL)) { + return MUSL; + } + return null; +}; + +const familyFromInterpreterPath = (path) => { + if (path) { + if (path.includes('/ld-musl-')) { + return MUSL; + } else if (path.includes('/ld-linux-')) { + return GLIBC; + } + } + return null; +}; + +const getFamilyFromLddContent = (content) => { + content = content.toString(); + if (content.includes('musl')) { + return MUSL; + } + if (content.includes('GNU C Library')) { + return GLIBC; + } + return null; +}; + +const familyFromFilesystem = async () => { + if (cachedFamilyFilesystem !== undefined) { + return cachedFamilyFilesystem; + } + cachedFamilyFilesystem = null; + try { + const lddContent = await readFile(LDD_PATH); + cachedFamilyFilesystem = getFamilyFromLddContent(lddContent); + } catch (e) {} + return cachedFamilyFilesystem; +}; + +const familyFromFilesystemSync = () => { + if (cachedFamilyFilesystem !== undefined) { + return cachedFamilyFilesystem; + } + cachedFamilyFilesystem = null; + try { + const lddContent = readFileSync(LDD_PATH); + cachedFamilyFilesystem = getFamilyFromLddContent(lddContent); + } catch (e) {} + return cachedFamilyFilesystem; +}; + +const familyFromInterpreter = async () => { + if (cachedFamilyInterpreter !== undefined) { + return cachedFamilyInterpreter; + } + cachedFamilyInterpreter = null; + try { + const selfContent = await readFile(SELF_PATH); + const path = interpreterPath(selfContent); + cachedFamilyInterpreter = familyFromInterpreterPath(path); + } catch (e) {} + return cachedFamilyInterpreter; +}; + +const familyFromInterpreterSync = () => { + if (cachedFamilyInterpreter !== undefined) { + return cachedFamilyInterpreter; + } + cachedFamilyInterpreter = null; + try { + const selfContent = readFileSync(SELF_PATH); + const path = interpreterPath(selfContent); + cachedFamilyInterpreter = familyFromInterpreterPath(path); + } catch (e) {} + return cachedFamilyInterpreter; +}; + +/** + * Resolves with the libc family when it can be determined, `null` otherwise. + * @returns {Promise} + */ +const family = async () => { + let family = null; + if (isLinux()) { + family = await familyFromInterpreter(); + if (!family) { + family = await familyFromFilesystem(); + if (!family) { + family = familyFromReport(); + } + if (!family) { + const out = await safeCommand(); + family = familyFromCommand(out); + } + } + } + return family; +}; + +/** + * Returns the libc family when it can be determined, `null` otherwise. + * @returns {?string} + */ +const familySync = () => { + let family = null; + if (isLinux()) { + family = familyFromInterpreterSync(); + if (!family) { + family = familyFromFilesystemSync(); + if (!family) { + family = familyFromReport(); + } + if (!family) { + const out = safeCommandSync(); + family = familyFromCommand(out); + } + } + } + return family; +}; + +/** + * Resolves `true` only when the platform is Linux and the libc family is not `glibc`. + * @returns {Promise} + */ +const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC; + +/** + * Returns `true` only when the platform is Linux and the libc family is not `glibc`. + * @returns {boolean} + */ +const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC; + +const versionFromFilesystem = async () => { + if (cachedVersionFilesystem !== undefined) { + return cachedVersionFilesystem; + } + cachedVersionFilesystem = null; + try { + const lddContent = await readFile(LDD_PATH); + const versionMatch = lddContent.match(RE_GLIBC_VERSION); + if (versionMatch) { + cachedVersionFilesystem = versionMatch[1]; + } + } catch (e) {} + return cachedVersionFilesystem; +}; + +const versionFromFilesystemSync = () => { + if (cachedVersionFilesystem !== undefined) { + return cachedVersionFilesystem; + } + cachedVersionFilesystem = null; + try { + const lddContent = readFileSync(LDD_PATH); + const versionMatch = lddContent.match(RE_GLIBC_VERSION); + if (versionMatch) { + cachedVersionFilesystem = versionMatch[1]; + } + } catch (e) {} + return cachedVersionFilesystem; +}; + +const versionFromReport = () => { + const report = getReport(); + if (report.header && report.header.glibcVersionRuntime) { + return report.header.glibcVersionRuntime; + } + return null; +}; + +const versionSuffix = (s) => s.trim().split(/\s+/)[1]; + +const versionFromCommand = (out) => { + const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/); + if (getconf && getconf.includes(GLIBC)) { + return versionSuffix(getconf); + } + if (ldd1 && ldd2 && ldd1.includes(MUSL)) { + return versionSuffix(ldd2); + } + return null; +}; + +/** + * Resolves with the libc version when it can be determined, `null` otherwise. + * @returns {Promise} + */ +const version = async () => { + let version = null; + if (isLinux()) { + version = await versionFromFilesystem(); + if (!version) { + version = versionFromReport(); + } + if (!version) { + const out = await safeCommand(); + version = versionFromCommand(out); + } + } + return version; +}; + +/** + * Returns the libc version when it can be determined, `null` otherwise. + * @returns {?string} + */ +const versionSync = () => { + let version = null; + if (isLinux()) { + version = versionFromFilesystemSync(); + if (!version) { + version = versionFromReport(); + } + if (!version) { + const out = safeCommandSync(); + version = versionFromCommand(out); + } + } + return version; +}; + +module.exports = { + GLIBC, + MUSL, + family, + familySync, + isNonGlibcLinux, + isNonGlibcLinuxSync, + version, + versionSync +}; diff --git a/.tools/bun/install/global/node_modules/detect-libc/lib/elf.js b/.tools/bun/install/global/node_modules/detect-libc/lib/elf.js new file mode 100644 index 0000000..aa166aa --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/lib/elf.js @@ -0,0 +1,39 @@ +// Copyright 2017 Lovell Fuller and others. +// SPDX-License-Identifier: Apache-2.0 + +'use strict'; + +const interpreterPath = (elf) => { + if (elf.length < 64) { + return null; + } + if (elf.readUInt32BE(0) !== 0x7F454C46) { + // Unexpected magic bytes + return null; + } + if (elf.readUInt8(4) !== 2) { + // Not a 64-bit ELF + return null; + } + if (elf.readUInt8(5) !== 1) { + // Not little-endian + return null; + } + const offset = elf.readUInt32LE(32); + const size = elf.readUInt16LE(54); + const count = elf.readUInt16LE(56); + for (let i = 0; i < count; i++) { + const headerOffset = offset + (i * size); + const type = elf.readUInt32LE(headerOffset); + if (type === 3) { + const fileOffset = elf.readUInt32LE(headerOffset + 8); + const fileSize = elf.readUInt32LE(headerOffset + 32); + return elf.subarray(fileOffset, fileOffset + fileSize).toString().replace(/\0.*$/g, ''); + } + } + return null; +}; + +module.exports = { + interpreterPath +}; diff --git a/.tools/bun/install/global/node_modules/detect-libc/lib/filesystem.js b/.tools/bun/install/global/node_modules/detect-libc/lib/filesystem.js new file mode 100644 index 0000000..4c2443c --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/lib/filesystem.js @@ -0,0 +1,51 @@ +// Copyright 2017 Lovell Fuller and others. +// SPDX-License-Identifier: Apache-2.0 + +'use strict'; + +const fs = require('fs'); + +const LDD_PATH = '/usr/bin/ldd'; +const SELF_PATH = '/proc/self/exe'; +const MAX_LENGTH = 2048; + +/** + * Read the content of a file synchronous + * + * @param {string} path + * @returns {Buffer} + */ +const readFileSync = (path) => { + const fd = fs.openSync(path, 'r'); + const buffer = Buffer.alloc(MAX_LENGTH); + const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0); + fs.close(fd, () => {}); + return buffer.subarray(0, bytesRead); +}; + +/** + * Read the content of a file + * + * @param {string} path + * @returns {Promise} + */ +const readFile = (path) => new Promise((resolve, reject) => { + fs.open(path, 'r', (err, fd) => { + if (err) { + reject(err); + } else { + const buffer = Buffer.alloc(MAX_LENGTH); + fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => { + resolve(buffer.subarray(0, bytesRead)); + fs.close(fd, () => {}); + }); + } + }); +}); + +module.exports = { + LDD_PATH, + SELF_PATH, + readFileSync, + readFile +}; diff --git a/.tools/bun/install/global/node_modules/detect-libc/lib/process.js b/.tools/bun/install/global/node_modules/detect-libc/lib/process.js new file mode 100644 index 0000000..ee78ad2 --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/lib/process.js @@ -0,0 +1,24 @@ +// Copyright 2017 Lovell Fuller and others. +// SPDX-License-Identifier: Apache-2.0 + +'use strict'; + +const isLinux = () => process.platform === 'linux'; + +let report = null; +const getReport = () => { + if (!report) { + /* istanbul ignore next */ + if (isLinux() && process.report) { + const orig = process.report.excludeNetwork; + process.report.excludeNetwork = true; + report = process.report.getReport(); + process.report.excludeNetwork = orig; + } else { + report = {}; + } + } + return report; +}; + +module.exports = { isLinux, getReport }; diff --git a/.tools/bun/install/global/node_modules/detect-libc/package.json b/.tools/bun/install/global/node_modules/detect-libc/package.json new file mode 100644 index 0000000..36d0f2b --- /dev/null +++ b/.tools/bun/install/global/node_modules/detect-libc/package.json @@ -0,0 +1,44 @@ +{ + "name": "detect-libc", + "version": "2.1.2", + "description": "Node.js module to detect the C standard library (libc) implementation family and version", + "main": "lib/detect-libc.js", + "files": [ + "lib/", + "index.d.ts" + ], + "scripts": { + "test": "semistandard && nyc --reporter=text --check-coverage --branches=100 ava test/unit.js", + "changelog": "conventional-changelog -i CHANGELOG.md -s", + "bench": "node benchmark/detect-libc", + "bench:calls": "node benchmark/call-familySync.js && sleep 1 && node benchmark/call-isNonGlibcLinuxSync.js && sleep 1 && node benchmark/call-versionSync.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/lovell/detect-libc.git" + }, + "keywords": [ + "libc", + "glibc", + "musl" + ], + "author": "Lovell Fuller ", + "contributors": [ + "Niklas Salmoukas ", + "Vinícius Lourenço " + ], + "license": "Apache-2.0", + "devDependencies": { + "ava": "^2.4.0", + "benchmark": "^2.1.4", + "conventional-changelog-cli": "^5.0.0", + "eslint-config-standard": "^13.0.1", + "nyc": "^15.1.0", + "proxyquire": "^2.1.3", + "semistandard": "^14.2.3" + }, + "engines": { + "node": ">=8" + }, + "types": "index.d.ts" +} diff --git a/.tools/bun/install/global/package.json b/.tools/bun/install/global/package.json new file mode 100644 index 0000000..a9ac006 --- /dev/null +++ b/.tools/bun/install/global/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@moonrepo/cli": "^2.0.4" + } +} \ No newline at end of file diff --git a/README.md b/README.md index c9f8728..05c3b90 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - `mkRepo` for `devShells`, `checks`, `formatter`, and optional `packages.release` - structured tool banners driven from package-backed tool specs - structured release steps (`writeFile`, `replace`, `run`) -- a minimal starter template in [`template/`](/Users/eric/Projects/repo-lib/template) +- a Bun-only Moonrepo + TypeScript + Varlock template in [`template/`](/Users/eric/Projects/repo-lib/template) ## Prerequisites @@ -18,6 +18,15 @@ nix flake new myapp -t 'git+https://git.dgren.dev/eric/nix-flake-lib?ref=refs/tags/v3.4.0#default' --refresh ``` +The generated repo includes: + +- a `repo-lib`-managed Nix flake +- Bun as the only JS runtime and package manager +- Moonrepo root tasks +- shared TypeScript configs adapted from `../moon` +- Varlock with a committed `.env.schema` +- empty `apps/` and `packages/` directories for new projects + ## Use the library Add this flake input: diff --git a/docs/superpowers/plans/2026-03-16-typescript-monorepo-template.md b/docs/superpowers/plans/2026-03-16-typescript-monorepo-template.md new file mode 100644 index 0000000..0b2dbf8 --- /dev/null +++ b/docs/superpowers/plans/2026-03-16-typescript-monorepo-template.md @@ -0,0 +1,99 @@ +# TypeScript Monorepo Template Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the minimal starter template with a Bun-only Moonrepo + TypeScript + Varlock monorepo template exposed through the existing flake template. + +**Architecture:** Expand `template/` into a complete repository skeleton while keeping `repo-lib.lib.mkRepo` as the integration point. Adapt the strict TypeScript config layout and Varlock command pattern from `../moon`, and update release tests so they evaluate the full template contents. + +**Tech Stack:** Nix flakes, repo-lib, Bun, Moonrepo, Varlock, TypeScript + +--- + +## Chunk 1: Documentation Baseline + +### Task 1: Update public template docs + +**Files:** +- Modify: `README.md` + +- [ ] **Step 1: Write the failing expectation mentally against current docs** + +Current docs describe only a minimal starter template and do not mention Bun, Moonrepo, or Varlock. + +- [ ] **Step 2: Update the README to describe the new template** + +Document the generated workspace shape and first-run commands. + +- [ ] **Step 3: Verify the README content is consistent with the template files** + +Check all commands and filenames against the final template layout. + +## Chunk 2: Template Skeleton + +### Task 2: Replace the minimal template with a real monorepo skeleton + +**Files:** +- Modify: `template/flake.nix` +- Create: `template/package.json` +- Create: `template/bunfig.toml` +- Create: `template/moon.yml` +- Create: `template/tsconfig.json` +- Create: `template/tsconfig.options.json` +- Create: `template/tsconfig/browser.json` +- Create: `template/tsconfig/bun.json` +- Create: `template/tsconfig/package.json` +- Create: `template/tsconfig/runtime.json` +- Create: `template/.env.schema` +- Modify: `template/.gitignore` +- Create: `template/README.md` +- Create: `template/apps/.gitkeep` +- Create: `template/packages/.gitkeep` + +- [ ] **Step 1: Add or update template files** + +Use `../moon` as the source for Moonrepo, Varlock, and TypeScript patterns, removing product-specific details. + +- [ ] **Step 2: Verify the template tree is coherent** + +Check that all referenced files exist and that scripts reference only template-safe commands. + +## Chunk 3: Test Coverage + +### Task 3: Update release tests for the full template + +**Files:** +- Modify: `tests/release.sh` + +- [ ] **Step 1: Add a failing test expectation** + +The current template fixture copies only `template/flake.nix`, which is insufficient for the new template layout. + +- [ ] **Step 2: Update fixture creation to copy the full template** + +Rewrite template URL references in copied files as needed for local test evaluation. + +- [ ] **Step 3: Verify the existing template evaluation case now uses the real skeleton** + +Confirm `nix flake show` runs against the expanded template fixture. + +## Chunk 4: Verification + +### Task 4: Run template verification + +**Files:** +- Verify: `README.md` +- Verify: `template/**/*` +- Verify: `tests/release.sh` + +- [ ] **Step 1: Run the release test suite** + +Run: `nix develop -c bash tests/release.sh` + +- [ ] **Step 2: Inspect the template file tree** + +Run: `find template -maxdepth 3 -type f | sort` + +- [ ] **Step 3: Verify the README examples still match the tagged template release pattern** + +Check that versioned `repo-lib` URLs remain in the documented commands and release replacements. diff --git a/docs/superpowers/specs/2026-03-16-typescript-monorepo-template-design.md b/docs/superpowers/specs/2026-03-16-typescript-monorepo-template-design.md new file mode 100644 index 0000000..4cf3d34 --- /dev/null +++ b/docs/superpowers/specs/2026-03-16-typescript-monorepo-template-design.md @@ -0,0 +1,88 @@ +# TypeScript Monorepo Template Design + +## Goal + +Add a new default template to this repository that generates a Bun-only TypeScript monorepo using Moonrepo, Varlock, and the shared TypeScript configuration pattern from `../moon`. + +## Scope + +The generated template should include: + +- a Nix flake wired through `repo-lib.lib.mkRepo` +- Bun-only JavaScript tooling +- Moonrepo root configuration +- strict shared TypeScript configs adapted from `../moon` +- Varlock enabled from day one +- a committed `.env.schema` +- empty `apps/` and `packages/` directories +- minimal documentation for first-run setup + +The template should not include: + +- demo apps or packages +- product-specific environment variables or OpenBao paths from `../moon` +- Node or pnpm support + +## Architecture + +The existing `template/` directory remains the exported flake template. Instead of containing only a starter `flake.nix`, it will become a complete repository skeleton. + +The generated repository will keep the current `repo-lib` integration pattern: + +- `template/flake.nix` calls `repo-lib.lib.mkRepo` +- the shell provisions Bun, Moonrepo CLI, Varlock, and supporting tooling +- repo checks remain driven through `mkRepo` and Lefthook + +Moonrepo and Varlock will be configured at the workspace root. The template will expose root tasks and scripts that work even before any projects are added. + +## Template Contents + +The template should contain: + +- `flake.nix` +- `package.json` +- `bunfig.toml` +- `moon.yml` +- `tsconfig.json` +- `tsconfig.options.json` +- `tsconfig/browser.json` +- `tsconfig/bun.json` +- `tsconfig/package.json` +- `tsconfig/runtime.json` +- `.env.schema` +- `.gitignore` +- `README.md` +- `apps/.gitkeep` +- `packages/.gitkeep` + +It may also keep generic repo support files already useful in templates, such as `.envrc`, `.gitlint`, `.gitleaks.toml`, `.vscode/settings.json`, and `flake.lock`, as long as they remain template-safe. + +## Data And Command Flow + +On first use: + +1. the user creates a repo from the flake template +2. the shell provides Bun, Moonrepo, Varlock, and release support +3. `bun install` installs `@moonrepo/cli`, `varlock`, and TypeScript-related dependencies +4. entering the repo loads `varlock/auto-load` +5. root commands like `bun run env:check`, `bun run env:scan`, and `moon run :typecheck` work without any sample projects + +## Varlock Design + +The template will include a minimal `.env.schema` with: + +- one canonical environment selector +- safe local defaults where practical +- placeholders for OpenBao-backed secrets using generic template paths + +Root scripts in `package.json` will follow the `../moon` pattern for `env:check` and `env:scan`, including `BAO_*` and `OPENBAO_*` compatibility exports. The template will not encode any product-specific namespace names. + +## Testing + +Existing release tests must continue to validate the exported template. The template fixture helper in `tests/release.sh` will need to copy the full template directory, not only `template/flake.nix`, so `nix flake show` exercises the real generated repository structure. + +## Risks + +- Moonrepo root task behavior must remain valid with no projects present. +- Template-safe Varlock defaults must avoid broken first-run behavior while still demonstrating the intended pattern. +- The release test harness must not accidentally preserve upstream URLs inside the copied template. diff --git a/template/.env.schema b/template/.env.schema new file mode 100644 index 0000000..15fc07e --- /dev/null +++ b/template/.env.schema @@ -0,0 +1,18 @@ +# @currentEnv=$REPO_ENVIRONMENT +# --- + +# Canonical repo environment used by Varlock. +# @type=enum(development,ci,production) +REPO_ENVIRONMENT=development + +# Safe starter values for local development. +# @type=string +APP_NAME=typescript-monorepo + +# @type=port +APP_PORT=3000 + +# Optional example secret resolved from OpenBao. +# Replace the namespace and secret path with values for your repo before use. +# @optional @sensitive +EXAMPLE_API_TOKEN=exec('bao kv get -mount=kv -namespace="${BAO_NAMESPACE:+$BAO_NAMESPACE/}template" -field=EXAMPLE_API_TOKEN "$REPO_ENVIRONMENT/shared" 2>/dev/null || true') diff --git a/template/.gitignore b/template/.gitignore index 8e18ed9..cc4cef3 100644 --- a/template/.gitignore +++ b/template/.gitignore @@ -1,9 +1,12 @@ .direnv/ +.moon/cache/ .pre-commit-config.yaml lefthook.yml +.tools/ bazel-* build/ dist/ node_modules/ +.env.sh diff --git a/template/.moon/workspace.yml b/template/.moon/workspace.yml new file mode 100644 index 0000000..c37522a --- /dev/null +++ b/template/.moon/workspace.yml @@ -0,0 +1,11 @@ +$schema: "./cache/schemas/workspace.json" + +projects: + globs: + - "apps/*" + - "packages/*" + sources: + root: "." + +vcs: + defaultBranch: "main" diff --git a/template/README.md b/template/README.md new file mode 100644 index 0000000..dffc537 --- /dev/null +++ b/template/README.md @@ -0,0 +1,32 @@ +# TypeScript Monorepo Template + +This template gives you a Bun-only monorepo with: + +- Moonrepo at the workspace root +- strict shared TypeScript configs +- Varlock with a committed `.env.schema` +- a Nix flake shell built through `repo-lib` + +## First Run + +1. Enter the shell with `direnv allow` or `nix develop`. +2. Install workspace dependencies with `bun install`. +3. Review and customize `.env.schema` for your repo. +4. Run `bun run env:check`. +5. Run `moon run :typecheck`. + +## Layout + +- `apps/` for applications +- `packages/` for shared libraries +- `tsconfig/` for shared TypeScript profiles +- `moon.yml` for root Moonrepo tasks + +## Varlock + +`bunfig.toml` preloads `varlock/auto-load`, and the root scripts expose: + +- `bun run env:check` +- `bun run env:scan` + +If you use OpenBao locally, set `OPENBAO_ADDR`, `OPENBAO_NAMESPACE`, and `OPENBAO_CACERT` in your shell or an ignored `.env.sh` file before running those commands. diff --git a/template/apps/.gitkeep b/template/apps/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/template/apps/.gitkeep @@ -0,0 +1 @@ + diff --git a/template/bunfig.toml b/template/bunfig.toml new file mode 100644 index 0000000..bb76b98 --- /dev/null +++ b/template/bunfig.toml @@ -0,0 +1,2 @@ +env = false +preload = ["varlock/auto-load"] diff --git a/template/flake.lock b/template/flake.lock deleted file mode 100644 index d1f6d78..0000000 --- a/template/flake.lock +++ /dev/null @@ -1,159 +0,0 @@ -{ - "nodes": { - "devshell-lib": { - "inputs": { - "git-hooks": "git-hooks", - "nixpkgs": [ - "nixpkgs" - ], - "treefmt-nix": "treefmt-nix" - }, - "locked": { - "lastModified": 1772603902, - "narHash": "sha256-GN5EC9m0flWDuc6qaB6QoIBD73yFnhl2PBIYXzSTGeQ=", - "ref": "v0.0.2", - "rev": "db4ed150e01e2f9245e668077245447d0089163f", - "revCount": 15, - "type": "git", - "url": "https://git.dgren.dev/eric/nix-flake-lib" - }, - "original": { - "ref": "v0.0.2", - "type": "git", - "url": "https://git.dgren.dev/eric/nix-flake-lib" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1767039857, - "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", - "owner": "NixOS", - "repo": "flake-compat", - "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "flake-compat", - "type": "github" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1772024342, - "narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=", - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "devshell-lib", - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1770073757, - "narHash": "sha256-Vy+G+F+3E/Tl+GMNgiHl9Pah2DgShmIUBJXmbiQPHbI=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "47472570b1e607482890801aeaf29bfb749884f6", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1770107345, - "narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "4533d9293756b63904b7238acb84ac8fe4c8c2c4", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1772542754, - "narHash": "sha256-WGV2hy+VIeQsYXpsLjdr4GvHv5eECMISX1zKLTedhdg=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "8c809a146a140c5c8806f13399592dbcb1bb5dc4", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "devshell-lib": "devshell-lib", - "nixpkgs": "nixpkgs_3" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1770228511, - "narHash": "sha256-wQ6NJSuFqAEmIg2VMnLdCnUc0b7vslUohqqGGD+Fyxk=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "337a4fe074be1042a35086f15481d763b8ddc0e7", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/template/flake.nix b/template/flake.nix index 45fef5f..74287f0 100644 --- a/template/flake.nix +++ b/template/flake.nix @@ -1,6 +1,5 @@ -# flake.nix — product repo template { - description = "my-product"; + description = "typescript-monorepo"; inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; @@ -20,99 +19,61 @@ src = ./.; config = { - # includeStandardPackages = false; - shell = { - env = { - # FOO = "bar"; + banner = { + style = "pretty"; + icon = "☾"; + title = "Moonrepo shell ready"; + titleColor = "GREEN"; + subtitle = "Bun + TypeScript + Varlock"; + subtitleColor = "GRAY"; + borderColor = "BLUE"; }; extraShellText = '' - # any repo-specific shell setup here + export PATH="$PWD/node_modules/.bin:$PATH" ''; - # Impure bootstrap is available as an explicit escape hatch. - # bootstrap = '' - # export GOBIN="$PWD/.tools/bin" - # export PATH="$GOBIN:$PATH" - # ''; - # allowImpureBootstrap = true; + bootstrap = '' + repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" + + export BUN_INSTALL_GLOBAL_DIR="$repo_root/.tools/bun/install/global" + export BUN_INSTALL_BIN="$repo_root/.tools/bun/bin" + export PATH="$BUN_INSTALL_BIN:$PATH" + + mkdir -p "$BUN_INSTALL_GLOBAL_DIR" "$BUN_INSTALL_BIN" + + if [ ! -x "$BUN_INSTALL_BIN/moon" ]; then + bun add -g @moonrepo/cli + fi + ''; + allowImpureBootstrap = true; }; formatting = { - # nixfmt is enabled by default and wired into lefthook. programs = { - # shfmt.enable = true; - # gofmt.enable = true; + oxfmt.enable = true; }; settings = { - # shfmt.options = [ "-i" "2" "-s" "-w" ]; + oxfmt.excludes = [ + "*.css" + "*.graphql" + "*.hbs" + "*.html" + "*.md" + "*.mdx" + "*.mustache" + "*.scss" + "*.vue" + "*.yaml" + "*.yml" + ]; }; }; - # These checks become lefthook commands in the generated `lefthook.yml`. - # repo-lib runs `pre-commit` and `pre-push` hook commands in parallel. - # It also sets `output = [ "failure" "summary" ]` by default. - checks = { - tests = { - command = "echo 'No tests defined yet.'"; - stage = "pre-push"; - passFilenames = false; - }; - - # fmt = { - # command = "nix fmt"; - # stage = "pre-commit"; - # passFilenames = false; - # }; - }; - - # For advanced Lefthook fields like `stage_fixed`, use raw passthrough. - # repo-lib merges this after generated checks. - # lefthook.pre-push.commands.tests.stage_fixed = true; - # lefthook.commit-msg.commands.commitlint = { - # run = "pnpm commitlint --edit {1}"; - # stage_fixed = true; - # }; - - # repo-lib also installs built-in hooks for: - # - treefmt / nixfmt on `pre-commit` - # - gitleaks on `pre-commit` - # - gitlint on `commit-msg` - - # release = null; release = { - steps = [ - # Write a generated version file during release. - # { - # writeFile = { - # path = "src/version.ts"; - # text = '' - # export const APP_VERSION = "$FULL_VERSION" as const; - # ''; - # }; - # } - - # Replace a version string while preserving surrounding captures. - # { - # replace = { - # path = "README.md"; - # regex = ''^(version = ")[^"]*(")$''; - # replacement = ''\1$FULL_VERSION\2''; - # }; - # } - - # Run any extra release step with declared runtime inputs. - # { - # run = { - # runtimeInputs = [ pkgs.git ]; - # script = '' - # git status --short - # ''; - # }; - # } - ]; + steps = [ ]; }; }; @@ -137,37 +98,58 @@ }; }) - # (repo-lib.lib.tools.fromPackage { - # name = "Go"; - # package = pkgs.go; - # version.args = [ "version" ]; - # banner.color = "CYAN"; - # }) + (repo-lib.lib.tools.fromPackage { + name = "Bun"; + package = pkgs.bun; + version.args = [ "--version" ]; + banner = { + color = "YELLOW"; + icon = ""; + }; + }) ]; shell.packages = [ self.packages.${system}.release - # pkgs.go - # pkgs.bun + pkgs.bun + pkgs.openbao + pkgs.oxfmt + pkgs.oxlint ]; - # checks.lint = { - # command = "bun test"; - # stage = "pre-push"; - # passFilenames = false; - # runtimeInputs = [ pkgs.bun ]; - # }; + checks.format = { + command = "oxfmt --check ."; + stage = "pre-commit"; + passFilenames = false; + runtimeInputs = [ pkgs.oxfmt ]; + }; - # checks.generated = { - # command = "git diff --exit-code"; - # stage = "pre-commit"; - # passFilenames = false; - # }; + checks.typecheck = { + command = "bun run typecheck"; + stage = "pre-push"; + passFilenames = false; + runtimeInputs = [ pkgs.bun ]; + }; - # packages.my-tool = pkgs.writeShellApplication { - # name = "my-tool"; - # text = ''echo hello''; - # }; + checks.env-check = { + command = "bun run env:check"; + stage = "pre-push"; + passFilenames = false; + runtimeInputs = [ + pkgs.bun + pkgs.openbao + ]; + }; + + checks.env-scan = { + command = "bun run env:scan"; + stage = "pre-commit"; + passFilenames = false; + runtimeInputs = [ + pkgs.bun + pkgs.openbao + ]; + }; }; }; } diff --git a/template/moon.yml b/template/moon.yml new file mode 100644 index 0000000..4d481d2 --- /dev/null +++ b/template/moon.yml @@ -0,0 +1,45 @@ +$schema: "./.moon/cache/schemas/project.json" + +tasks: + typecheck: + command: "bun" + args: + - "run" + - "typecheck" + inputs: + - "package.json" + - "tsconfig.json" + - "tsconfig.options.json" + - "tsconfig/**/*" + options: + cache: false + runFromWorkspaceRoot: true + + env-check: + command: "bun" + args: + - "run" + - "env:check" + inputs: + - ".env.schema" + - "package.json" + - "bunfig.toml" + toolchains: "system" + options: + cache: false + runFromWorkspaceRoot: true + + env-scan: + command: "bun" + args: + - "run" + - "env:scan" + inputs: + - ".env.schema" + - "package.json" + - "bunfig.toml" + toolchains: "system" + options: + cache: false + runFromWorkspaceRoot: true + runInCI: "skip" diff --git a/template/package.json b/template/package.json new file mode 100644 index 0000000..d7106c6 --- /dev/null +++ b/template/package.json @@ -0,0 +1,23 @@ +{ + "name": "typescript-monorepo", + "private": true, + "workspaces": [ + "apps/*", + "packages/*" + ], + "type": "module", + "scripts": { + "typecheck": "bunx tsc -p tsconfig.json --pretty false", + "env:check": "sh -ec 'export BAO_ADDR=\"${BAO_ADDR:-${OPENBAO_ADDR:-}}\"; export BAO_NAMESPACE=\"${BAO_NAMESPACE:-${OPENBAO_NAMESPACE:-}}\"; export BAO_CACERT=\"${BAO_CACERT:-${OPENBAO_CACERT:-}}\"; exec varlock load --show-all'", + "env:scan": "sh -ec 'export BAO_ADDR=\"${BAO_ADDR:-${OPENBAO_ADDR:-}}\"; export BAO_NAMESPACE=\"${BAO_NAMESPACE:-${OPENBAO_NAMESPACE:-}}\"; export BAO_CACERT=\"${BAO_CACERT:-${OPENBAO_CACERT:-}}\"; exec varlock scan --staged'", + "check": "bun run typecheck && bun run env:check" + }, + "dependencies": { + "@moonrepo/cli": "^2.0.4" + }, + "devDependencies": { + "@types/bun": "latest", + "typescript": "^6.0.0-beta", + "varlock": "0.5.0" + } +} diff --git a/template/packages/.gitkeep b/template/packages/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/template/packages/.gitkeep @@ -0,0 +1 @@ + diff --git a/template/tsconfig.json b/template/tsconfig.json new file mode 100644 index 0000000..bc8375a --- /dev/null +++ b/template/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.options.json", + "files": [] +} diff --git a/template/tsconfig.options.json b/template/tsconfig.options.json new file mode 100644 index 0000000..944c1b1 --- /dev/null +++ b/template/tsconfig.options.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["ESNext"], + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "noEmit": true + } +} diff --git a/template/tsconfig/browser.json b/template/tsconfig/browser.json new file mode 100644 index 0000000..11a4dc1 --- /dev/null +++ b/template/tsconfig/browser.json @@ -0,0 +1,6 @@ +{ + "extends": "./runtime.json", + "compilerOptions": { + "types": ["vite/client"] + } +} diff --git a/template/tsconfig/bun.json b/template/tsconfig/bun.json new file mode 100644 index 0000000..c3b7304 --- /dev/null +++ b/template/tsconfig/bun.json @@ -0,0 +1,6 @@ +{ + "extends": "./runtime.json", + "compilerOptions": { + "types": ["@types/bun"] + } +} diff --git a/template/tsconfig/package.json b/template/tsconfig/package.json new file mode 100644 index 0000000..96e3300 --- /dev/null +++ b/template/tsconfig/package.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "types": [] + }, + "extends": "../tsconfig.options.json" +} diff --git a/template/tsconfig/runtime.json b/template/tsconfig/runtime.json new file mode 100644 index 0000000..9b90c8f --- /dev/null +++ b/template/tsconfig/runtime.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.options.json", + "compilerOptions": { + "lib": ["ESNext", "DOM", "DOM.Iterable"] + } +} diff --git a/tests/release.sh b/tests/release.sh index 3be5f44..23dbe20 100755 --- a/tests/release.sh +++ b/tests/release.sh @@ -484,10 +484,13 @@ EOF write_template_fixture() { local repo_dir="$1" - sed \ + mkdir -p "$repo_dir" + cp -R "$ROOT_DIR/template/." "$repo_dir/" + sed -i.bak \ -e "s|git+https://git.dgren.dev/eric/nix-flake-lib?ref=refs/tags/v[0-9.]*|path:${ROOT_DIR}|" \ -e "s|github:nixos/nixpkgs?ref=nixos-unstable|path:${NIXPKGS_FLAKE_PATH}|" \ - "$ROOT_DIR/template/flake.nix" >"$repo_dir/flake.nix" + "$repo_dir/flake.nix" + rm -f "$repo_dir/flake.nix.bak" } qc_version_cmp() { @@ -1364,6 +1367,13 @@ run_template_eval_case() { write_template_fixture "$repo_dir" CURRENT_LOG="$workdir/template.log" + if [[ ! -f "$repo_dir/package.json" ]]; then + fail "$case_name: template fixture missing package.json" + fi + if [[ ! -f "$repo_dir/.moon/workspace.yml" ]]; then + fail "$case_name: template fixture missing .moon/workspace.yml" + fi + run_capture_ok "$case_name: flake show failed" nix flake show --json "$repo_dir" assert_contains '"lefthook-check"' "$CURRENT_LOG" "$case_name: missing lefthook-check" assert_contains '"release"' "$CURRENT_LOG" "$case_name: missing release package"