package_json 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|