package_json 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.
- checksums.yaml +7 -0
- data/.editorconfig +13 -0
- data/.prettierignore +6 -0
- data/.prettierrc.json +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +243 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +82 -0
- data/LICENSE.txt +21 -0
- data/README.md +284 -0
- data/Rakefile +12 -0
- data/lib/package_json/managers/base.rb +118 -0
- data/lib/package_json/managers/bun_like.rb +79 -0
- data/lib/package_json/managers/npm_like.rb +83 -0
- data/lib/package_json/managers/pnpm_like.rb +84 -0
- data/lib/package_json/managers/yarn_berry_like.rb +81 -0
- data/lib/package_json/managers/yarn_classic_like.rb +95 -0
- data/lib/package_json/version.rb +5 -0
- data/lib/package_json.rb +128 -0
- data/sig/package_json/managers/base.rbs +57 -0
- data/sig/package_json/managers/bun_like.rbs +19 -0
- data/sig/package_json/managers/npm_like.rbs +17 -0
- data/sig/package_json/managers/pnpm_like.rbs +19 -0
- data/sig/package_json/managers/yarn_berry_like.rbs +19 -0
- data/sig/package_json/managers/yarn_classic_like.rbs +21 -0
- data/sig/package_json.rbs +38 -0
- metadata +75 -0
data/README.md
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
# PackageJson
|
2
|
+
|
3
|
+
The missing gem for managing `package.json` files, without having to know about
|
4
|
+
package managers (mostly).
|
5
|
+
|
6
|
+
It provides an interface for easily modifying the properties of `package.json`
|
7
|
+
files, along with a "middle-level" abstraction over JavaScript package mangers
|
8
|
+
to make it easy to manage dependencies without needing to know the specifics of
|
9
|
+
the underlying package manager (and potentially without even knowing the manager
|
10
|
+
itself!).
|
11
|
+
|
12
|
+
This is _not_ meant to provide the exact same functionality and behaviour
|
13
|
+
regardless of what package manager is being used, but rather make it easier to
|
14
|
+
perform common general tasks that are supported by all package managers like
|
15
|
+
adding new dependencies, installing existing ones, and running scripts without
|
16
|
+
having to know the actual command a specific package manager requires for that
|
17
|
+
action (and other such nuances).
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Install the gem and add to the application's Gemfile by executing:
|
22
|
+
|
23
|
+
$ bundle add package_json
|
24
|
+
|
25
|
+
If bundler is not being used to manage dependencies, install the gem by
|
26
|
+
executing:
|
27
|
+
|
28
|
+
$ gem install package_json
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# represents $PWD/package.json, creating it if it does not exist
|
34
|
+
package_json = PackageJson.new
|
35
|
+
|
36
|
+
# adds eslint, eslint-plugin-prettier, and prettier as development dependencies
|
37
|
+
package_json.manager.add(%w[eslint prettier], :dev)
|
38
|
+
|
39
|
+
# adds the "lint" and "format" scripts, preserving any existing scripts
|
40
|
+
package_json.merge! do |pj|
|
41
|
+
{
|
42
|
+
"scripts" => pj.fetch("scripts", {}).merge({
|
43
|
+
"lint" => "eslint . --ext js",
|
44
|
+
"format" => "prettier --check ."
|
45
|
+
})
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# deletes the "babel" property, if it exists
|
50
|
+
package_json.delete!("babel")
|
51
|
+
|
52
|
+
# runs the "lint" script with the "--fix" argument
|
53
|
+
package_json.manager.run("lint", ["--fix"])
|
54
|
+
```
|
55
|
+
|
56
|
+
The `PackageJson` class represents a `package.json` on disk within a directory;
|
57
|
+
because it is expected that the `package.json` might be changed by external
|
58
|
+
sources such as package managers, `PackageJson` reads and writes to and from the
|
59
|
+
`package.json` as needed rather than representing it in memory.
|
60
|
+
|
61
|
+
If you expect the `package.json` to already exist, you can use `read` instead
|
62
|
+
which will raise an error instead of implicitly creating the file if it doesn't
|
63
|
+
exist.
|
64
|
+
|
65
|
+
A `PackageJson` also comes with a `manager` that can be used to manage
|
66
|
+
dependencies and run scripts.
|
67
|
+
|
68
|
+
### Specifying a package manager
|
69
|
+
|
70
|
+
You can specify which package manager should be used with the
|
71
|
+
[`packageManager`](https://nodejs.org/api/packages.html#packagemanager) property
|
72
|
+
in the `package.json`.
|
73
|
+
|
74
|
+
> **Note**
|
75
|
+
>
|
76
|
+
> Only the name of the package manager is used; the version (if present) is
|
77
|
+
> _not_ checked, nor is [`codepack`](https://nodejs.org/api/corepack.html) used
|
78
|
+
> to ensure that the package manager is installed.
|
79
|
+
>
|
80
|
+
> The manager will be invoked by its name in the directory of the
|
81
|
+
> `package.json`, and it is up to the developer to ensure that results in the
|
82
|
+
> desired package manager actually running.
|
83
|
+
|
84
|
+
If the `packageManager` property is not present, then the fallback manager will
|
85
|
+
be used; this defaults to the value of the `PACKAGE_JSON_FALLBACK_MANAGER`
|
86
|
+
environment variable or otherwise `npm`. You can also provide a specific
|
87
|
+
fallback manager:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
PackageJson.read(fallback_manager: :pnpm)
|
91
|
+
PackageJson.new(fallback_manager: :yarn_classic)
|
92
|
+
```
|
93
|
+
|
94
|
+
Supported package managers are `:npm`, `:yarn_berry`, `:yarn_classic`, `:pnpm`,
|
95
|
+
and `:bun`.
|
96
|
+
|
97
|
+
If the `package.json` does not exist, then the `packageManager` property will be
|
98
|
+
included based on this value, but it will _not_ be updated if the file already
|
99
|
+
exists without the property.
|
100
|
+
|
101
|
+
Managers are provided a reference to the `PackageJson` when they're initialized,
|
102
|
+
are run in the same directory as that `PackageJson`.
|
103
|
+
|
104
|
+
### Using the package manager
|
105
|
+
|
106
|
+
Each package manager supports a set of common methods which are covered below.
|
107
|
+
Unless otherwise noted for a particular method, each method:
|
108
|
+
|
109
|
+
- Behaves like `system`, returning either `true`, `false`, or `nil` based on if
|
110
|
+
the package manager exited with a non-zero error code; each method has a
|
111
|
+
bang-equivalent if you wish an exception to be thrown instead
|
112
|
+
- Does not attempt to capture or intercept the output; using `Kernel.system`
|
113
|
+
under the hood, output is sent directly to `stdout` and `stderr`
|
114
|
+
- Will run in the directory of the `package.json`; for methods that generate
|
115
|
+
native commands, it is up to the caller to ensure the working directory is
|
116
|
+
correct
|
117
|
+
|
118
|
+
#### Get the version of the package manager
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
package_json.manager.version
|
122
|
+
```
|
123
|
+
|
124
|
+
This is suitable for checking that the package manager is actually available
|
125
|
+
before performing other operations. Unlike other non-bang methods, this will
|
126
|
+
error if the underlying command exits with a non-zero code.
|
127
|
+
|
128
|
+
#### Installing dependencies
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
# install all dependencies
|
132
|
+
package_json.manager.install
|
133
|
+
|
134
|
+
# install all dependencies, erroring if the lockfile is outdated
|
135
|
+
package_json.manager.install(frozen: true)
|
136
|
+
```
|
137
|
+
|
138
|
+
| Option | Description |
|
139
|
+
| -------- | ---------------------------------------- |
|
140
|
+
| `frozen` | Fail if the lockfile needs to be updated |
|
141
|
+
|
142
|
+
#### Generating the `install` command for native scripts and advanced calls
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
# returns an array of strings that make up the desired operation
|
146
|
+
native_install_command = package_json.manager.native_install_command
|
147
|
+
|
148
|
+
# runs the command with extra environment variables
|
149
|
+
Kernel.system({ "HELLO" => "WORLD" }, *native_install_command)
|
150
|
+
|
151
|
+
append_to_file "bin/ci-run" do
|
152
|
+
<<~CMD
|
153
|
+
echo "* ******************************************************"
|
154
|
+
echo "* Installing JS dependencies"
|
155
|
+
echo "* ******************************************************"
|
156
|
+
#{native_install_command.join(" ")}
|
157
|
+
CMD
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
| Option | Description |
|
162
|
+
| -------- | ---------------------------------------- |
|
163
|
+
| `frozen` | Fail if the lockfile needs to be updated |
|
164
|
+
|
165
|
+
#### Adding dependencies
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# adds axios as a production dependency
|
169
|
+
package_json.manager.add(["axios"])
|
170
|
+
|
171
|
+
# adds eslint and prettier as dev dependencies
|
172
|
+
package_json.manager.add(["eslint", "prettier"], type: :dev)
|
173
|
+
|
174
|
+
# adds dotenv-webpack v6 as a production dependency
|
175
|
+
package_json.manager.add(["dotenv-webpack@^6"])
|
176
|
+
```
|
177
|
+
|
178
|
+
| Option | Description |
|
179
|
+
| ------ | ------------------------------------------------------------------------------------------- |
|
180
|
+
| `type` | The type to add the dependencies as; either `:production` (default), `:dev`, or `:optional` |
|
181
|
+
|
182
|
+
#### Removing dependencies
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# removes the axios package
|
186
|
+
package_json.manager.remove(["axios"])
|
187
|
+
```
|
188
|
+
|
189
|
+
#### Run a script
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
# runs the "test" script
|
193
|
+
package_json.manager.run("test")
|
194
|
+
|
195
|
+
# runs the "test" script, passing it "--coverage path/to/my/test.js" as the argument
|
196
|
+
package_json.manager.run("test", ["--coverage", "path/to/my/test.js"])
|
197
|
+
|
198
|
+
# runs the "lint" script, passing it "--fix" as the argument and telling the package manager to be silent
|
199
|
+
package_json.manager.run("lint", ["--fix"], silent: true)
|
200
|
+
```
|
201
|
+
|
202
|
+
| Option | Description |
|
203
|
+
| -------- | ---------------------------------------- |
|
204
|
+
| `silent` | Suppress output from the package manager |
|
205
|
+
|
206
|
+
#### Generating a `run` command for native scripts and advanced calls
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
native_run_command = package_json.manager.native_run_command("test", ["--coverage"])
|
210
|
+
|
211
|
+
# runs the command with extra environment variables
|
212
|
+
Kernel.system({ "HELLO" => "WORLD" }, *native_run_command)
|
213
|
+
|
214
|
+
append_to_file "bin/ci-run" do
|
215
|
+
<<~CMD
|
216
|
+
echo "* ******************************************************"
|
217
|
+
echo "* Running JS tests"
|
218
|
+
echo "* ******************************************************"
|
219
|
+
#{native_run_command.join(" ")}
|
220
|
+
CMD
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
| Option | Description |
|
225
|
+
| -------- | ---------------------------------------- |
|
226
|
+
| `silent` | Suppress output from the package manager |
|
227
|
+
|
228
|
+
#### Generating a `exec` command for native scripts and advanced calls
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
native_exec_command = package_json.manager.native_exec_command("webpack", ["serve"])
|
232
|
+
|
233
|
+
# runs the command with extra environment variables
|
234
|
+
Kernel.system({ "HELLO" => "WORLD" }, *native_exec_command)
|
235
|
+
|
236
|
+
append_to_file "bin/webpack-webpack" do
|
237
|
+
<<~CMD
|
238
|
+
echo "* ******************************************************"
|
239
|
+
echo "* Serving assets via webpack
|
240
|
+
echo "* ******************************************************"
|
241
|
+
#{native_exec_command.join(" ")}
|
242
|
+
CMD
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
> **Note**
|
247
|
+
>
|
248
|
+
> Since Yarn Classic doesn't provide a native `exec` command, `yarn bin` is used
|
249
|
+
> instead to identify where the package command should be within `node_modules`.
|
250
|
+
>
|
251
|
+
> For other package managers, their native `exec` command is used with the flags
|
252
|
+
> necessary to enforce the package command is only executed if the package is
|
253
|
+
> installed locally.
|
254
|
+
|
255
|
+
## Development
|
256
|
+
|
257
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
258
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
259
|
+
prompt that will allow you to experiment.
|
260
|
+
|
261
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
262
|
+
release a new version, update the version number in `version.rb`, and then run
|
263
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
264
|
+
git commits and the created tag, and push the `.gem` file to
|
265
|
+
[rubygems.org](https://rubygems.org).
|
266
|
+
|
267
|
+
## Contributing
|
268
|
+
|
269
|
+
Bug reports and pull requests are welcome on GitHub at
|
270
|
+
https://github.com/[USERNAME]/package_json. This project is intended to be a
|
271
|
+
safe, welcoming space for collaboration, and contributors are expected to adhere
|
272
|
+
to the
|
273
|
+
[code of conduct](https://github.com/[USERNAME]/package_json/blob/main/CODE_OF_CONDUCT.md).
|
274
|
+
|
275
|
+
## License
|
276
|
+
|
277
|
+
The gem is available as open source under the terms of the
|
278
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
279
|
+
|
280
|
+
## Code of Conduct
|
281
|
+
|
282
|
+
Everyone interacting in the PackageJson project's codebases, issue trackers,
|
283
|
+
chat rooms and mailing lists is expected to follow the
|
284
|
+
[code of conduct](https://github.com/[USERNAME]/package_json/blob/main/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
class PackageJson
|
2
|
+
module Managers
|
3
|
+
class Base
|
4
|
+
# @return [String] the binary to invoke for running the package manager
|
5
|
+
attr_reader :binary
|
6
|
+
|
7
|
+
def initialize(package_json, binary_name:)
|
8
|
+
# @type [PackageJson]
|
9
|
+
@package_json = package_json
|
10
|
+
# @type [String]
|
11
|
+
@binary = binary_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def version
|
15
|
+
require "open3"
|
16
|
+
|
17
|
+
command = "#{binary} --version"
|
18
|
+
stdout, stderr, status = Open3.capture3(command, chdir: @package_json.directory)
|
19
|
+
|
20
|
+
unless status.success?
|
21
|
+
raise PackageJson::Error, "#{command} failed with exit code #{status.exitstatus}: #{stderr}"
|
22
|
+
end
|
23
|
+
|
24
|
+
stdout.chomp
|
25
|
+
end
|
26
|
+
|
27
|
+
# Installs the dependencies specified in the `package.json` file
|
28
|
+
def install(frozen: false)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
# Provides the "native" command for installing dependencies with this package manager for embedding into scripts
|
33
|
+
def native_install_command(frozen: false)
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
|
37
|
+
# Installs the dependencies specified in the `package.json` file
|
38
|
+
def install!(frozen: false)
|
39
|
+
raise_exited_with_non_zero_code_error unless install(frozen: frozen)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Adds the given packages
|
43
|
+
def add(packages, type: :production)
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds the given packages
|
48
|
+
def add!(packages, type: :production)
|
49
|
+
raise_exited_with_non_zero_code_error unless add(packages, type: type)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Removes the given packages
|
53
|
+
def remove(packages)
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
|
57
|
+
# Removes the given packages
|
58
|
+
def remove!(
|
59
|
+
packages
|
60
|
+
)
|
61
|
+
raise_exited_with_non_zero_code_error unless remove(packages)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Runs the script assuming it is defined in the `package.json` file
|
65
|
+
def run(
|
66
|
+
script_name,
|
67
|
+
args = [],
|
68
|
+
silent: false
|
69
|
+
)
|
70
|
+
raise NotImplementedError
|
71
|
+
end
|
72
|
+
|
73
|
+
# Runs the script assuming it is defined in the `package.json` file
|
74
|
+
def run!(
|
75
|
+
script_name,
|
76
|
+
args = [],
|
77
|
+
silent: false
|
78
|
+
)
|
79
|
+
raise_exited_with_non_zero_code_error unless run(
|
80
|
+
script_name,
|
81
|
+
args,
|
82
|
+
silent: silent
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Provides the "native" command for running the script with args for embedding into shell scripts
|
87
|
+
def native_run_command(
|
88
|
+
script_name,
|
89
|
+
args = [],
|
90
|
+
silent: false
|
91
|
+
)
|
92
|
+
raise NotImplementedError
|
93
|
+
end
|
94
|
+
|
95
|
+
# Provides the "native" command for executing a package with args for embedding into shell scripts
|
96
|
+
def native_exec_command(
|
97
|
+
script_name,
|
98
|
+
args = []
|
99
|
+
)
|
100
|
+
raise NotImplementedError
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def raise_exited_with_non_zero_code_error
|
106
|
+
raise Error, "#{binary} exited with non-zero code"
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_full_cmd(sub_cmd, args)
|
110
|
+
[binary, sub_cmd, *args]
|
111
|
+
end
|
112
|
+
|
113
|
+
def raw(sub_cmd, args)
|
114
|
+
Kernel.system(*build_full_cmd(sub_cmd, args), chdir: @package_json.directory)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class PackageJson
|
2
|
+
module Managers
|
3
|
+
class BunLike < Base
|
4
|
+
def initialize(package_json)
|
5
|
+
super(package_json, binary_name: "bun")
|
6
|
+
end
|
7
|
+
|
8
|
+
# Installs the dependencies specified in the `package.json` file
|
9
|
+
def install(frozen: false)
|
10
|
+
raw("install", with_frozen_flag(frozen))
|
11
|
+
end
|
12
|
+
|
13
|
+
# Provides the "native" command for installing dependencies with this package manager for embedding into scripts
|
14
|
+
def native_install_command(frozen: false)
|
15
|
+
build_full_cmd("install", with_frozen_flag(frozen))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds the given packages
|
19
|
+
def add(packages, type: :production)
|
20
|
+
raw("add", [package_type_install_flag(type)].compact + packages)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Removes the given packages
|
24
|
+
def remove(packages)
|
25
|
+
raw("remove", packages)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Runs the script assuming it is defined in the `package.json` file
|
29
|
+
def run(
|
30
|
+
script_name,
|
31
|
+
args = [],
|
32
|
+
silent: false
|
33
|
+
)
|
34
|
+
raw("run", build_run_args(script_name, args, _silent: silent))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Provides the "native" command for running the script with args for embedding into shell scripts
|
38
|
+
def native_run_command(
|
39
|
+
script_name,
|
40
|
+
args = [],
|
41
|
+
silent: false
|
42
|
+
)
|
43
|
+
build_full_cmd("run", build_run_args(script_name, args, _silent: silent))
|
44
|
+
end
|
45
|
+
|
46
|
+
def native_exec_command(
|
47
|
+
script_name,
|
48
|
+
args = []
|
49
|
+
)
|
50
|
+
build_full_cmd("run", build_run_args(script_name, args, _silent: false))
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def build_run_args(script_name, args, _silent:)
|
56
|
+
[script_name, *args]
|
57
|
+
end
|
58
|
+
|
59
|
+
def with_frozen_flag(frozen)
|
60
|
+
return ["--frozen-lockfile"] if frozen
|
61
|
+
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
|
65
|
+
def package_type_install_flag(type)
|
66
|
+
case type
|
67
|
+
when :production
|
68
|
+
nil
|
69
|
+
when :dev
|
70
|
+
"--dev"
|
71
|
+
when :optional
|
72
|
+
"--optional"
|
73
|
+
else
|
74
|
+
raise Error, "unsupported package install type \"#{type}\""
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class PackageJson
|
2
|
+
module Managers
|
3
|
+
class NpmLike < Base
|
4
|
+
def initialize(package_json)
|
5
|
+
super(package_json, binary_name: "npm")
|
6
|
+
end
|
7
|
+
|
8
|
+
# Installs the dependencies specified in the `package.json` file
|
9
|
+
def install(frozen: false)
|
10
|
+
cmd = "install"
|
11
|
+
cmd = "ci" if frozen
|
12
|
+
|
13
|
+
raw(cmd, [])
|
14
|
+
end
|
15
|
+
|
16
|
+
# Provides the "native" command for installing dependencies with this package manager for embedding into scripts
|
17
|
+
def native_install_command(frozen: false)
|
18
|
+
cmd = "install"
|
19
|
+
cmd = "ci" if frozen
|
20
|
+
|
21
|
+
build_full_cmd(cmd, [])
|
22
|
+
end
|
23
|
+
|
24
|
+
# Adds the given packages
|
25
|
+
def add(packages, type: :production)
|
26
|
+
raw("install", [package_type_install_flag(type)] + packages)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Removes the given packages
|
30
|
+
def remove(packages)
|
31
|
+
raw("remove", packages)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Runs the script assuming it is defined in the `package.json` file
|
35
|
+
def run(
|
36
|
+
script_name,
|
37
|
+
args = [],
|
38
|
+
silent: false
|
39
|
+
)
|
40
|
+
raw("run", build_run_args(script_name, args, silent: silent))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Provides the "native" command for running the script with args for embedding into shell scripts
|
44
|
+
def native_run_command(
|
45
|
+
script_name,
|
46
|
+
args = [],
|
47
|
+
silent: false
|
48
|
+
)
|
49
|
+
build_full_cmd("run", build_run_args(script_name, args, silent: silent))
|
50
|
+
end
|
51
|
+
|
52
|
+
def native_exec_command(
|
53
|
+
script_name,
|
54
|
+
args = []
|
55
|
+
)
|
56
|
+
build_full_cmd("exec", ["--no", "--offline"] + build_run_args(script_name, args, silent: false))
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def build_run_args(script_name, args, silent:)
|
62
|
+
# npm assumes flags prefixed with - are for it, unless they come after a "--"
|
63
|
+
args = [script_name, "--", *args]
|
64
|
+
|
65
|
+
args.unshift("--silent") if silent
|
66
|
+
args
|
67
|
+
end
|
68
|
+
|
69
|
+
def package_type_install_flag(type)
|
70
|
+
case type
|
71
|
+
when :production
|
72
|
+
"--save-prod"
|
73
|
+
when :dev
|
74
|
+
"--save-dev"
|
75
|
+
when :optional
|
76
|
+
"--save-optional"
|
77
|
+
else
|
78
|
+
raise Error, "unsupported package install type \"#{type}\""
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class PackageJson
|
2
|
+
module Managers
|
3
|
+
class PnpmLike < Base
|
4
|
+
def initialize(package_json)
|
5
|
+
super(package_json, binary_name: "pnpm")
|
6
|
+
end
|
7
|
+
|
8
|
+
# Installs the dependencies specified in the `package.json` file
|
9
|
+
def install(frozen: false)
|
10
|
+
raw("install", with_frozen_flag(frozen))
|
11
|
+
end
|
12
|
+
|
13
|
+
# Provides the "native" command for installing dependencies with this package manager for embedding into scripts
|
14
|
+
def native_install_command(frozen: false)
|
15
|
+
build_full_cmd("install", with_frozen_flag(frozen))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds the given packages
|
19
|
+
def add(packages, type: :production)
|
20
|
+
raw("add", [package_type_install_flag(type)] + packages)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Removes the given packages
|
24
|
+
def remove(packages)
|
25
|
+
raw("remove", packages)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Runs the script assuming it is defined in the `package.json` file
|
29
|
+
def run(
|
30
|
+
script_name,
|
31
|
+
args = [],
|
32
|
+
silent: false
|
33
|
+
)
|
34
|
+
raw("run", build_run_args(script_name, args, silent: silent))
|
35
|
+
end
|
36
|
+
|
37
|
+
# Provides the "native" command for running the script with args for embedding into shell scripts
|
38
|
+
def native_run_command(
|
39
|
+
script_name,
|
40
|
+
args = [],
|
41
|
+
silent: false
|
42
|
+
)
|
43
|
+
build_full_cmd("run", build_run_args(script_name, args, silent: silent))
|
44
|
+
end
|
45
|
+
|
46
|
+
def native_exec_command(
|
47
|
+
script_name,
|
48
|
+
args = []
|
49
|
+
)
|
50
|
+
build_full_cmd("exec", build_run_args(script_name, args, silent: false))
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def build_run_args(script_name, args, silent:)
|
56
|
+
args = [script_name, *args]
|
57
|
+
|
58
|
+
args.unshift("--silent") if silent
|
59
|
+
args
|
60
|
+
end
|
61
|
+
|
62
|
+
def with_frozen_flag(frozen)
|
63
|
+
return ["--frozen-lockfile"] if frozen
|
64
|
+
|
65
|
+
# we make frozen lockfile behaviour consistent with the other package managers
|
66
|
+
# as pnpm automatically enables frozen lockfile if it detects it's running in CI
|
67
|
+
["--no-frozen-lockfile"]
|
68
|
+
end
|
69
|
+
|
70
|
+
def package_type_install_flag(type)
|
71
|
+
case type
|
72
|
+
when :production
|
73
|
+
"--save-prod"
|
74
|
+
when :dev
|
75
|
+
"--save-dev"
|
76
|
+
when :optional
|
77
|
+
"--save-optional"
|
78
|
+
else
|
79
|
+
raise Error, "unsupported package install type \"#{type}\""
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|