@somewhatabstract/x 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +28 -0
- package/.github/workflows/codeql-analysis.yml +29 -29
- package/.github/workflows/dependabot-pr-approval.yml +36 -0
- package/.github/workflows/nodejs.yml +84 -86
- package/.github/workflows/release.yml +4 -7
- package/.vscode/settings.json +19 -0
- package/CHANGELOG.md +23 -0
- package/CONTRIBUTING.md +3 -3
- package/README.md +132 -1
- package/biome.json +39 -0
- package/dist/x.mjs +278 -3
- package/package.json +14 -4
- package/src/__tests__/build-environment.test.ts +285 -0
- package/src/__tests__/discover-packages.test.ts +196 -0
- package/src/__tests__/errors.test.ts +59 -0
- package/src/__tests__/execute-script.test.ts +1042 -0
- package/src/__tests__/find-matching-bins.test.ts +506 -0
- package/src/__tests__/find-workspace-root.test.ts +73 -0
- package/src/__tests__/is-node-executable.test.ts +125 -0
- package/src/__tests__/resolve-bin-path.test.ts +344 -0
- package/src/__tests__/x-impl.test.ts +306 -7
- package/src/__tests__/x.test.ts +236 -0
- package/src/bin/x.ts +55 -1
- package/src/build-environment.ts +98 -0
- package/src/discover-packages.ts +35 -0
- package/src/errors.ts +10 -0
- package/src/execute-script.ts +56 -0
- package/src/find-matching-bins.ts +72 -0
- package/src/find-workspace-root.ts +24 -0
- package/src/is-node-executable.ts +16 -0
- package/src/resolve-bin-path.ts +48 -0
- package/src/x-impl.ts +95 -4
- package/tsconfig-types.json +2 -4
- package/tsconfig.json +5 -13
- package/tsdown.config.ts +1 -1
- package/vitest.config.ts +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: npm
|
|
9
|
+
directory: "/"
|
|
10
|
+
schedule:
|
|
11
|
+
interval: weekly
|
|
12
|
+
day: saturday
|
|
13
|
+
time: "09:00"
|
|
14
|
+
timezone: America/Chicago
|
|
15
|
+
open-pull-requests-limit: 10
|
|
16
|
+
reviewers:
|
|
17
|
+
- somewhatabstract
|
|
18
|
+
|
|
19
|
+
- package-ecosystem: github-actions
|
|
20
|
+
directory: "/"
|
|
21
|
+
schedule:
|
|
22
|
+
interval: weekly
|
|
23
|
+
day: saturday
|
|
24
|
+
time: "09:00"
|
|
25
|
+
timezone: America/Chicago
|
|
26
|
+
open-pull-requests-limit: 10
|
|
27
|
+
reviewers:
|
|
28
|
+
- somewhatabstract
|
|
@@ -13,12 +13,12 @@ name: "CodeQL"
|
|
|
13
13
|
|
|
14
14
|
on:
|
|
15
15
|
push:
|
|
16
|
-
branches: [
|
|
16
|
+
branches: [main]
|
|
17
17
|
pull_request:
|
|
18
18
|
# The branches below must be a subset of the branches above
|
|
19
|
-
branches: [
|
|
19
|
+
branches: [main]
|
|
20
20
|
schedule:
|
|
21
|
-
- cron:
|
|
21
|
+
- cron: "16 7 * * 2"
|
|
22
22
|
|
|
23
23
|
jobs:
|
|
24
24
|
analyze:
|
|
@@ -32,40 +32,40 @@ jobs:
|
|
|
32
32
|
strategy:
|
|
33
33
|
fail-fast: false
|
|
34
34
|
matrix:
|
|
35
|
-
language: [
|
|
35
|
+
language: ["javascript"]
|
|
36
36
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
|
37
37
|
# Learn more:
|
|
38
38
|
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
|
39
39
|
|
|
40
40
|
steps:
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
- name: Checkout repository
|
|
42
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
# Initializes the CodeQL tools for scanning.
|
|
45
|
+
- name: Initialize CodeQL
|
|
46
|
+
uses: github/codeql-action/init@710e2945787622b429f8982cacb154faa182de18
|
|
47
|
+
with:
|
|
48
|
+
languages: ${{ matrix.language }}
|
|
49
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
50
|
+
# By default, queries listed here will override any specified in a config file.
|
|
51
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
52
|
+
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
55
|
+
# If this step fails, then you should remove it and run the build manually (see below)
|
|
56
|
+
- name: Autobuild
|
|
57
|
+
uses: github/codeql-action/autobuild@710e2945787622b429f8982cacb154faa182de18
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
|
60
|
+
# 📚 https://git.io/JvXDl
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
63
|
+
# and modify them (or add more) to build your code if your project
|
|
64
|
+
# uses a compiled language
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
#- run: |
|
|
67
|
+
# make bootstrap
|
|
68
|
+
# make release
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
- name: Perform CodeQL Analysis
|
|
71
|
+
uses: github/codeql-action/analyze@710e2945787622b429f8982cacb154faa182de18
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Dependabot Pull Request Approve and Merge
|
|
2
|
+
|
|
3
|
+
on: pull_request_target
|
|
4
|
+
|
|
5
|
+
permissions:
|
|
6
|
+
pull-requests: write
|
|
7
|
+
contents: write
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
dependabot:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
# Checking the actor will prevent your Action run failing on non-Dependabot
|
|
13
|
+
# PRs but also ensures that it only does work for Dependabot PRs.
|
|
14
|
+
if: ${{ github.actor == 'dependabot[bot]' }}
|
|
15
|
+
steps:
|
|
16
|
+
# This first step will fail if there's no metadata and so the approval
|
|
17
|
+
# will not occur.
|
|
18
|
+
- name: Dependabot metadata
|
|
19
|
+
id: dependabot-metadata
|
|
20
|
+
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a
|
|
21
|
+
with:
|
|
22
|
+
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
23
|
+
# Here the PR gets approved.
|
|
24
|
+
- name: Approve a PR
|
|
25
|
+
run: gh pr review --approve "$PR_URL"
|
|
26
|
+
env:
|
|
27
|
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
28
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
+
# Finally, this sets the PR to allow auto-merging for patch and minor
|
|
30
|
+
# updates if all checks pass
|
|
31
|
+
- name: Enable auto-merge for Dependabot PRs
|
|
32
|
+
if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}
|
|
33
|
+
run: gh pr merge --auto --squash "$PR_URL"
|
|
34
|
+
env:
|
|
35
|
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
36
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -8,7 +8,7 @@ on:
|
|
|
8
8
|
|
|
9
9
|
push:
|
|
10
10
|
branches:
|
|
11
|
-
|
|
11
|
+
- main
|
|
12
12
|
|
|
13
13
|
jobs:
|
|
14
14
|
lint:
|
|
@@ -21,40 +21,39 @@ jobs:
|
|
|
21
21
|
os: [ubuntu-latest]
|
|
22
22
|
node-version: [20.x]
|
|
23
23
|
steps:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
24
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
|
25
|
+
|
|
26
|
+
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
|
|
27
|
+
name: Install pnpm
|
|
28
|
+
with:
|
|
29
|
+
run_install: false
|
|
30
|
+
package_json_file: "package.json"
|
|
31
|
+
|
|
32
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
33
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
|
|
34
|
+
with:
|
|
35
|
+
node-version: ${{ matrix.node-version }}
|
|
36
|
+
cache: "pnpm"
|
|
37
|
+
cache-dependency-path: "pnpm-lock.yaml"
|
|
38
|
+
|
|
39
|
+
- name: Install Dependencies
|
|
40
|
+
shell: bash
|
|
41
|
+
run: pnpm install --frozen-lockfile
|
|
42
|
+
|
|
43
|
+
- name: Lint
|
|
44
|
+
run: pnpm lint
|
|
45
|
+
|
|
46
|
+
- name: Static Types
|
|
47
|
+
run: pnpm typecheck
|
|
48
|
+
|
|
49
|
+
- name: Changesets check
|
|
50
|
+
uses: Khan/actions@973e081efd07f23c4e521e7f4c6b15c9257ee924
|
|
51
|
+
if: |
|
|
52
|
+
github.actor != 'dependabot[bot]' &&
|
|
53
|
+
github.actor != 'dependabot-preview[bot]' &&
|
|
54
|
+
github.event_name == 'pull_request'
|
|
55
|
+
with:
|
|
56
|
+
exclude: .github/,.storybook/
|
|
58
57
|
|
|
59
58
|
coverage:
|
|
60
59
|
name: Update test coverage
|
|
@@ -66,32 +65,31 @@ jobs:
|
|
|
66
65
|
os: [ubuntu-latest]
|
|
67
66
|
node-version: [20.x]
|
|
68
67
|
steps:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
68
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
|
69
|
+
|
|
70
|
+
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
|
|
71
|
+
name: Install pnpm
|
|
72
|
+
with:
|
|
73
|
+
run_install: false
|
|
74
|
+
package_json_file: "package.json"
|
|
75
|
+
|
|
76
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
77
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
|
|
78
|
+
with:
|
|
79
|
+
node-version: ${{ matrix.node-version }}
|
|
80
|
+
cache: "pnpm"
|
|
81
|
+
cache-dependency-path: "pnpm-lock.yaml"
|
|
82
|
+
|
|
83
|
+
- name: Install Dependencies
|
|
84
|
+
shell: bash
|
|
85
|
+
run: pnpm install --frozen-lockfile
|
|
86
|
+
|
|
87
|
+
- name: Run tests with coverage
|
|
88
|
+
run: pnpm coverage
|
|
89
|
+
- name: Upload coverage
|
|
90
|
+
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de
|
|
91
|
+
env:
|
|
92
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
95
93
|
|
|
96
94
|
build:
|
|
97
95
|
needs: [coverage, lint]
|
|
@@ -104,28 +102,28 @@ jobs:
|
|
|
104
102
|
os: [ubuntu-latest]
|
|
105
103
|
node-version: [20.x]
|
|
106
104
|
steps:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
105
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
|
106
|
+
|
|
107
|
+
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
|
|
108
|
+
name: Install pnpm
|
|
109
|
+
with:
|
|
110
|
+
run_install: false
|
|
111
|
+
package_json_file: "package.json"
|
|
112
|
+
|
|
113
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
114
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
|
|
115
|
+
with:
|
|
116
|
+
node-version: ${{ matrix.node-version }}
|
|
117
|
+
cache: "pnpm"
|
|
118
|
+
cache-dependency-path: "pnpm-lock.yaml"
|
|
119
|
+
|
|
120
|
+
- name: Install Dependencies
|
|
121
|
+
shell: bash
|
|
122
|
+
run: pnpm install --frozen-lockfile
|
|
123
|
+
|
|
124
|
+
- name: Run build
|
|
125
|
+
env:
|
|
126
|
+
# We only want to upload bundle analysis for a PR once,
|
|
127
|
+
# so we only provide a token for the ubuntu-latest job.
|
|
128
|
+
CODECOV_TOKEN: ${{ matrix.os == 'ubuntu-latest' && secrets.CODECOV_TOKEN || '' }}
|
|
129
|
+
run: pnpm build
|
|
@@ -35,19 +35,19 @@ jobs:
|
|
|
35
35
|
contents: write # For creating release PRs
|
|
36
36
|
pull-requests: write # For creating release PRs
|
|
37
37
|
steps:
|
|
38
|
-
- uses: actions/checkout@
|
|
38
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
|
39
39
|
with:
|
|
40
40
|
fetch-depth: 0
|
|
41
41
|
persist-credentials: false
|
|
42
42
|
|
|
43
|
-
- uses: pnpm/action-setup@
|
|
43
|
+
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
|
|
44
44
|
name: Install pnpm
|
|
45
45
|
with:
|
|
46
46
|
run_install: false
|
|
47
47
|
package_json_file: package.json
|
|
48
48
|
|
|
49
49
|
- name: Use Node.js 20.x
|
|
50
|
-
uses: actions/setup-node@
|
|
50
|
+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238
|
|
51
51
|
with:
|
|
52
52
|
node-version: 20.x
|
|
53
53
|
cache: pnpm
|
|
@@ -82,7 +82,7 @@ jobs:
|
|
|
82
82
|
|
|
83
83
|
- name: Create Release Pull Request or Publish to npm
|
|
84
84
|
id: changesets
|
|
85
|
-
uses: changesets/action@
|
|
85
|
+
uses: changesets/action@6a0a831ff30acef54f2c6aa1cbbc1096b066edaf
|
|
86
86
|
with:
|
|
87
87
|
publish: pnpm publish:ci
|
|
88
88
|
env:
|
|
@@ -91,8 +91,5 @@ jobs:
|
|
|
91
91
|
# the account of someone with appropriate access levels and given the
|
|
92
92
|
# repo scope.
|
|
93
93
|
GITHUB_TOKEN: ${{ secrets.BOT_PA_TOKEN }}
|
|
94
|
-
# This is the token used to publish to npm when OIDC trusted
|
|
95
|
-
# publishing isn't yet setup
|
|
96
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
97
94
|
# This is used for the bundle analysis
|
|
98
95
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.formatOnSave": true,
|
|
3
|
+
"editor.defaultFormatter": "biomejs.biome",
|
|
4
|
+
"editor.codeActionsOnSave": {
|
|
5
|
+
"source.fixAll.biome": "explicit"
|
|
6
|
+
},
|
|
7
|
+
"[javascript]": {
|
|
8
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
9
|
+
},
|
|
10
|
+
"[typescript]": {
|
|
11
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
12
|
+
},
|
|
13
|
+
"[typescriptreact]": {
|
|
14
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
15
|
+
},
|
|
16
|
+
"[json]": {
|
|
17
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @somewhatabstract/x
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2f64864: Implement monorepo script execution tool with multi-package-manager support
|
|
8
|
+
|
|
9
|
+
- Add support for npm, Yarn, pnpm, Lerna, Bun, and Rush workspaces
|
|
10
|
+
- Implement direct script execution without interpreter detection
|
|
11
|
+
- Add dry-run mode for previewing execution
|
|
12
|
+
- Comprehensive test coverage
|
|
13
|
+
|
|
14
|
+
- 2f64864: Support Node-executable script files (.js, .mjs, .cjs)
|
|
15
|
+
|
|
16
|
+
Bin scripts with `.js`, `.mjs`, or `.cjs` extensions are now automatically
|
|
17
|
+
invoked via the Node executable, matching npm/pnpm behavior. Previously, only
|
|
18
|
+
files with a shebang and executable permissions were supported.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- 2f64864: Add test coverage for all source files including the CLI entry point
|
|
23
|
+
- 2f64864: Add Biome for linting and code formatting.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -27,7 +27,7 @@ To work in the `x` repository, follow these steps:
|
|
|
27
27
|
2. Install `pnpm`
|
|
28
28
|
- `corepack enable pnpm`
|
|
29
29
|
- `corepack prepare pnpm --activate`
|
|
30
|
-
3. Run `pnpm install` to install the dependencies
|
|
30
|
+
3. Run `pnpm install` (or `pnpm i`) to install the dependencies
|
|
31
31
|
|
|
32
32
|
You can now work on `x`. We prefer [🔗Visual Studio Code](https://code.visualstudio.com/) as our development environment (it's cross-platform and awesome), but please use what you feel comfortable with (we'll even forgive you for using vim).
|
|
33
33
|
|
|
@@ -65,6 +65,6 @@ Running the build will execute tests first.
|
|
|
65
65
|
|
|
66
66
|
### Publishing
|
|
67
67
|
|
|
68
|
-
Publishing is automated through our use of [changesets][1]. When a PR is merged
|
|
68
|
+
Publishing is automated through our use of [changesets][1]. When a PR is merged to `main`, a release PR is created that bundles all the changes since the last release. When we are ready to release, this bundled PR is merged to `main` which triggers changesets to publish to npm via trusted publishing.
|
|
69
69
|
|
|
70
|
-
[1]:https://github.com/changesets/changesets/blob/main/README.md#documentation
|
|
70
|
+
[1]:https://github.com/changesets/changesets/blob/main/README.md#documentation
|
package/README.md
CHANGED
|
@@ -1,2 +1,133 @@
|
|
|
1
1
|
# x
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
Execute any bin defined by any package in a monorepo without needing to install that package.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`x` is a tool for monorepos that allows you to execute binary scripts from any package in your workspace without installing them globally or in your current package. It automatically discovers all packages in your workspace and finds the matching bin script.
|
|
8
|
+
|
|
9
|
+
**Supports multiple package managers:**
|
|
10
|
+
- 📦 npm workspaces
|
|
11
|
+
- 🧶 Yarn (classic and modern)
|
|
12
|
+
- 📌 pnpm workspaces
|
|
13
|
+
- 🎯 Lerna
|
|
14
|
+
- 🍞 Bun workspaces
|
|
15
|
+
- 🚀 Rush
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @somewhatabstract/x
|
|
21
|
+
# or
|
|
22
|
+
pnpm add -g @somewhatabstract/x
|
|
23
|
+
# or
|
|
24
|
+
yarn global add @somewhatabstract/x
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Execute a bin script from any package in the workspace
|
|
31
|
+
x <script-name> [...args]
|
|
32
|
+
|
|
33
|
+
# Preview what would be executed (dry-run mode)
|
|
34
|
+
x --dry-run <script-name>
|
|
35
|
+
|
|
36
|
+
# Pass arguments to the script
|
|
37
|
+
x tsc --noEmit
|
|
38
|
+
x eslint src/ --fix
|
|
39
|
+
x jest --watch
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Examples
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Run TypeScript compiler from any package that provides it
|
|
46
|
+
x tsc --noEmit
|
|
47
|
+
|
|
48
|
+
# Run ESLint from any package in the workspace
|
|
49
|
+
x eslint src/
|
|
50
|
+
|
|
51
|
+
# Preview which jest binary would be executed
|
|
52
|
+
x --dry-run jest
|
|
53
|
+
|
|
54
|
+
# Run a custom script with arguments
|
|
55
|
+
x my-custom-script arg1 arg2
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Features
|
|
59
|
+
|
|
60
|
+
- 🔍 **Automatic Discovery**: Finds all packages in your monorepo workspace
|
|
61
|
+
- 🎯 **Smart Matching**: Locates the bin script you want to run
|
|
62
|
+
- 🚀 **No Installation Needed**: Execute bins without installing packages
|
|
63
|
+
- 👁️ **Dry-Run Mode**: Preview what would be executed with `--dry-run`
|
|
64
|
+
- 🔧 **Multi-Language Support**: Works with any executable script (Node.js, Bash, Python, etc.)
|
|
65
|
+
- 📦 **Multi-Package-Manager**: Works with npm, Yarn, pnpm, Lerna, Bun, and Rush
|
|
66
|
+
- ⚡ **Fast**: Efficient package discovery using @manypkg
|
|
67
|
+
- 🛡️ **Type-Safe**: Written in TypeScript with full type safety
|
|
68
|
+
|
|
69
|
+
## How It Works
|
|
70
|
+
|
|
71
|
+
1. **Workspace Detection**: Uses `@manypkg/find-root` to find the workspace root (supports npm, Yarn, pnpm, Lerna, Bun, Rush)
|
|
72
|
+
2. **Package Discovery**: Uses `@manypkg/get-packages` to discover all packages in the workspace
|
|
73
|
+
3. **Bin Matching**: Searches through package.json files to find bins matching your requested script name
|
|
74
|
+
4. **Execution**: Executes the matched script directly via the OS (on Unix-like systems this requires an executable file with a shebang; on Windows the bin must be a directly runnable file such as a `.exe`, `.cmd`, or `.bat`)
|
|
75
|
+
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- Node.js >= 20
|
|
79
|
+
- A monorepo workspace (npm, Yarn, pnpm, Lerna, Bun, or Rush)
|
|
80
|
+
|
|
81
|
+
## CLI Options
|
|
82
|
+
|
|
83
|
+
- `<script-name>` - Name of the bin script to execute (required)
|
|
84
|
+
- `[args...]` - Arguments to pass to the script (optional)
|
|
85
|
+
- `-d, --dry-run` - Show what would be executed without running it
|
|
86
|
+
- `-h, --help` - Show help
|
|
87
|
+
- `-v, --version` - Show version
|
|
88
|
+
|
|
89
|
+
## Error Handling
|
|
90
|
+
|
|
91
|
+
The tool provides clear, user-friendly error messages:
|
|
92
|
+
|
|
93
|
+
- **Not in a workspace**: "Could not find workspace root. Make sure you're in a monorepo workspace."
|
|
94
|
+
- **Script not found**: "No bin script named '<name>' found in any workspace package."
|
|
95
|
+
- **Ambiguous match**: Lists all packages that provide the bin and asks you to be more specific
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Install dependencies
|
|
101
|
+
pnpm install
|
|
102
|
+
|
|
103
|
+
# Run tests
|
|
104
|
+
pnpm test
|
|
105
|
+
|
|
106
|
+
# Type check
|
|
107
|
+
pnpm typecheck
|
|
108
|
+
|
|
109
|
+
# Build
|
|
110
|
+
pnpm build
|
|
111
|
+
|
|
112
|
+
# Run locally
|
|
113
|
+
./dist/x.mjs <script-name>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Architecture
|
|
117
|
+
|
|
118
|
+
The implementation follows a modular design with separate concerns:
|
|
119
|
+
|
|
120
|
+
- `errors.ts` - Custom error types for user-friendly messages
|
|
121
|
+
- `find-workspace-root.ts` - Workspace root detection using @manypkg/find-root
|
|
122
|
+
- `discover-packages.ts` - Package discovery via @manypkg/get-packages
|
|
123
|
+
- `find-matching-bins.ts` - Bin script matching logic
|
|
124
|
+
- `execute-script.ts` - Direct script execution
|
|
125
|
+
- `build-environment.ts` - npm/pnpm environment variable construction
|
|
126
|
+
- `resolve-bin-path.ts` - Bin path resolution with security validation (path traversal protection)
|
|
127
|
+
- `x-impl.ts` - Main orchestration logic
|
|
128
|
+
- `bin/x.ts` - CLI entry point with yargs
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT
|
|
133
|
+
|
package/biome.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.4.3/schema.json",
|
|
3
|
+
"formatter": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"indentStyle": "space",
|
|
6
|
+
"indentWidth": 4,
|
|
7
|
+
"bracketSpacing": false,
|
|
8
|
+
"includes": ["**", "!dist/**", "!coverage/**"]
|
|
9
|
+
},
|
|
10
|
+
"linter": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
"includes": ["**", "!dist/**", "!coverage/**"]
|
|
13
|
+
},
|
|
14
|
+
"assist": {
|
|
15
|
+
"enabled": true,
|
|
16
|
+
"includes": ["**", "!dist/**", "!coverage/**"]
|
|
17
|
+
},
|
|
18
|
+
"overrides": [
|
|
19
|
+
{
|
|
20
|
+
"includes": ["**/*.test.*"],
|
|
21
|
+
"linter": {
|
|
22
|
+
"rules": {
|
|
23
|
+
"suspicious": {
|
|
24
|
+
"noExplicitAny": "off"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"includes": ["*.json", "*.jsonc"],
|
|
31
|
+
"json": {
|
|
32
|
+
"parser": {
|
|
33
|
+
"allowComments": true,
|
|
34
|
+
"allowTrailingCommas": true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|