@prettier/plugin-ruby
Advanced tools
Comparing version 3.2.2 to 4.0.0
@@ -9,2 +9,14 @@ # Changelog | ||
## [4.0.0] - 2023-07-06 | ||
### Added | ||
- [#1283](https://github.com/prettier/plugin-ruby/pull/1283) - oriolgual - Use `process.execPath` instead of `"node"`. | ||
- [#1358](https://github.com/prettier/plugin-ruby/pull/1358) - davidalejandroaguilar - Add the `rubyExecutablePath` option. | ||
- [#1359](https://github.com/prettier/plugin-ruby/pull/1359) - kddnewton - Inherit stderr from the parser process in order to provide better error messages when failing to spawn parser server. This will put out missing gem errors, for example. | ||
### Changed | ||
- [#1360](https://github.com/prettier/plugin-ruby/pull/1360) - kddnewton - Require prettier v3.0.0+. | ||
## [3.2.2] - 2022-09-20 | ||
@@ -63,3 +75,3 @@ | ||
- [#1206](https://github.com/prettier/plugin-ruby/pull/1206) - kddnewton - Switch back to plain JavaScript for development. | ||
- [#1190](https://github.com/prettier/plugin-ruby/pull/1190) - kddnewton - Switch over to using Syntax Tree for the backend. | ||
- [#1190](https://github.com/prettier/plugin-ruby/pull/1190) - kddnewton - Switch over to using Syntax Tree for the backend. This now requires that users install the necessary gems. | ||
@@ -1272,3 +1284,4 @@ ### Removed | ||
[unreleased]: https://github.com/prettier/plugin-ruby/compare/v3.2.2...HEAD | ||
[unreleased]: https://github.com/prettier/plugin-ruby/compare/v4.0.0...HEAD | ||
[4.0.0]: https://github.com/prettier/plugin-ruby/compare/v3.2.2...v4.0.0 | ||
[3.2.2]: https://github.com/prettier/plugin-ruby/compare/v3.2.1...v3.2.2 | ||
@@ -1275,0 +1288,0 @@ [3.2.1]: https://github.com/prettier/plugin-ruby/compare/v3.2.0...v3.2.1 |
{ | ||
"name": "@prettier/plugin-ruby", | ||
"version": "3.2.2", | ||
"version": "4.0.0", | ||
"description": "prettier plugin for the Ruby programming language", | ||
"type": "module", | ||
"main": "src/plugin.js", | ||
@@ -9,3 +10,3 @@ "scripts": { | ||
"lint": "eslint --cache .", | ||
"test": "jest" | ||
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" | ||
}, | ||
@@ -23,9 +24,9 @@ "repository": { | ||
"dependencies": { | ||
"prettier": ">=2.3.0" | ||
"prettier": ">=3.0.0" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^8.15.0", | ||
"eslint-config-prettier": "^8.0.0", | ||
"eslint": "^8.35.0", | ||
"eslint-config-prettier": "^8.7.0", | ||
"husky": "^8.0.1", | ||
"jest": "^29.0.0", | ||
"jest": "^29.5.0", | ||
"pretty-quick": "^3.1.2" | ||
@@ -39,8 +40,11 @@ }, | ||
"env": { | ||
"es6": true, | ||
"jest": true, | ||
"node": true | ||
}, | ||
"parserOptions": { | ||
"ecmaVersion": 2020, | ||
"sourceType": "module" | ||
}, | ||
"rules": { | ||
"no-unused-vars": "off" | ||
"no-undef": "off" | ||
} | ||
@@ -54,3 +58,4 @@ }, | ||
], | ||
"testRegex": ".test.js$" | ||
"testRegex": ".test.js$", | ||
"transform": {} | ||
}, | ||
@@ -65,5 +70,5 @@ "husky": { | ||
"plugins": [ | ||
"." | ||
"./src/plugin.js" | ||
] | ||
} | ||
} |
@@ -12,3 +12,3 @@ <div align="center"> | ||
<a href="https://github.com/prettier/plugin-ruby/actions"> | ||
<img alt="GitHub Actions" src="https://img.shields.io/github/workflow/status/prettier/plugin-ruby/Main?style=flat-square"> | ||
<img alt="GitHub Actions" src="https://img.shields.io/github/actions/workflow/status/prettier/plugin-ruby/main.yml?branch=main&style=flat-square"> | ||
</a> | ||
@@ -72,40 +72,10 @@ <a href="https://www.npmjs.com/package/@prettier/plugin-ruby"> | ||
To run `prettier` with the Ruby plugin, you're going to need [`ruby`](https://www.ruby-lang.org/en/documentation/installation/) (version `2.7.3` or newer) and [`node`](https://nodejs.org/en/download/) (version `8.3` or newer). If you're integrating with a project that is not already using `prettier`, you should use the Ruby gem. Otherwise you can use the `npm` package directly. | ||
The `@prettier/plugin-ruby` plugin for `prettier` is a small wrapper around the [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree) gem that provides a Ruby formatter for `prettier`. It does this by keeping a Ruby server running in that background that `prettier` can communicate with when it needs to format a Ruby file. This means that in order to function, you will need to have both the requisite `node` and `ruby` dependencies installed. Because of this configuration, there are a couple of ways that you can get setup to use this plugin. | ||
Note that currently the editor integrations work best with the `npm` package, as most of the major editor plugins expect a `node_modules` directory. You can get them to work with the Ruby gem, but it requires manually configuring the paths. | ||
- If you're already using `prettier` in your project to format other files in your project and want to install this as a plugin, you can install it using `npm`. | ||
- If you're not using `prettier` yet in your project, then we recommend using the [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree) gem directly instead of using this plugin. | ||
- Note that this plugin also ships a gem named `prettier` which is a wrapper around the `prettier` CLI and includes this plugin by default, but _we no longer recommend its use_. If you're using that gem, you should migrate to using [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree) instead. | ||
This plugin currently supports formatting the following kinds of files: | ||
To run `prettier` with the Ruby plugin as an `npm` package, you're going to need [`ruby`](https://www.ruby-lang.org/en/documentation/installation/) (version `2.7` or newer) and [`node`](https://nodejs.org/en/download/) (version `16` or newer). | ||
- All varieties of Ruby source files (e.g., `*.rb`, `*.gemspec`, `Gemfile`, etc.) | ||
- [RBS type language](https://github.com/ruby/rbs) files - requires having the `rbs` gem in your gem path | ||
- [HAML template language](https://haml.info/) files - requires having the `haml` gem in your gem path | ||
### Ruby gem | ||
Add this line to your application's Gemfile: | ||
```ruby | ||
gem "prettier" | ||
``` | ||
And then execute: | ||
```bash | ||
bundle | ||
``` | ||
Or install it yourself as: | ||
```bash | ||
gem install prettier | ||
``` | ||
The `rbprettier` executable is now installed and ready for use: | ||
```bash | ||
bundle exec rbprettier --write '**/*' | ||
``` | ||
### `npm` package | ||
If you're using the `npm` CLI, then add the plugin by: | ||
@@ -139,10 +109,11 @@ | ||
| API Option | CLI Option | Default | Description | | ||
| --------------- | ------------------ | :-----: | --------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). | | ||
| `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). | | ||
| `rubyPlugins` | `--ruby-plugins` | `""` | The comma-separated list of plugins to require. See [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree#plugins). | | ||
| `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). | | ||
| `singleQuote` | `--single-quote` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#quotes)). | | ||
| `trailingComma` | `--trailing-comma` | `es5` | Almost same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-commas)). Will be on for any value except `none`. | | ||
| API Option | CLI Option | Default | Description | | ||
| -------------------- | --------------------- | :------------------------------------------------: | --------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). | | ||
| `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). | | ||
| `rubyExecutablePath` | `"ruby"` | Allows you to configure your Ruby executable path. | | ||
| `rubyPlugins` | `--ruby-plugins` | `""` | The comma-separated list of plugins to require. See [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree#plugins). | | ||
| `rubySingleQuote` | `--ruby-single-quote` | `false` | Whether or not to default to single quotes for Ruby code. See [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree#plugins). | | ||
| `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). | | ||
| `trailingComma` | `--trailing-comma` | `es5` | Almost same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-commas)). Will be on for any value except `none`. | | ||
@@ -164,20 +135,11 @@ Any of these can be added to your existing [prettier configuration | ||
### Usage with RuboCop | ||
### Ignoring code | ||
RuboCop and Prettier for Ruby serve different purposes, but there is overlap | ||
with some of RuboCop's functionality. | ||
Sometimes you want to leave your formatting in place and have `prettier` not format it, but continue to format the rest of the file. `prettier` has the ability to do this with `prettier-ignore` comments, but because the underlying formatter for this plugin is [Syntax Tree](https://github.com/ruby-syntax-tree/syntax_tree), you instead would use a `stree-ignore` comment. | ||
Prettier provides a RuboCop configuration file to disable the rules which clash. | ||
To enable, add the following config at the top of your project's `.rubocop.yml`: | ||
### Usage with RuboCop | ||
#### Ruby gem | ||
RuboCop and Prettier for Ruby serve different purposes, but there is overlap with some of RuboCop's functionality. Prettier provides a RuboCop configuration file to disable the rules which would clash. To enable this file, add the following configuration at the top of your project's `.rubocop.yml`: | ||
```yaml | ||
inherit_gem: | ||
prettier: rubocop.yml | ||
``` | ||
#### `npm` package | ||
```yaml | ||
inherit_from: | ||
@@ -208,5 +170,7 @@ - node_modules/@prettier/plugin-ruby/rubocop.yml | ||
Check out our [contributing guide](CONTRIBUTING.md). Bug reports and pull requests are welcome on GitHub at https://github.com/prettier/plugin-ruby. | ||
Thanks so much for your interest in contributing! You can contribute in many ways, including: | ||
You can support `prettier/plugin-ruby` [on OpenCollective](https://opencollective.com/prettier-ruby/contribute). Your organization's logo will show up here with a link to your website. | ||
- Contributing code to fix any bugs on [GitHub](https://github.com/prettier/plugin-ruby). | ||
- Reporting issues on [GitHub](https://github.com/prettier/plugin-ruby/issues/new). | ||
- Supporting `prettier/plugin-ruby` on [OpenCollective](https://opencollective.com/prettier-ruby/contribute). Your organization's logo will show up here with a link to your website. | ||
@@ -213,0 +177,0 @@ <!-- prettier-ignore-start --> |
@@ -1,8 +0,188 @@ | ||
const { parseSync } = require("./parseSync"); | ||
import { spawn } from "child_process"; | ||
import fs from "fs"; | ||
import net from "net"; | ||
import os from "os"; | ||
import path from "path"; | ||
import process from "process"; | ||
import url from "url"; | ||
/* | ||
* metadata mostly pulled from linguist and rubocop: | ||
* https://github.com/github/linguist/blob/master/lib/linguist/languages.yml | ||
* https://github.com/rubocop/rubocop/blob/master/spec/rubocop/target_finder_spec.rb | ||
*/ | ||
// In order to properly parse ruby code, we need to tell the ruby process to | ||
// parse using UTF-8. Unfortunately, the way that you accomplish this looks | ||
// differently depending on your platform. | ||
function getLang() { | ||
const { env, platform } = process; | ||
const envValue = env.LC_ALL || env.LC_CTYPE || env.LANG; | ||
// If an env var is set for the locale that already includes UTF-8 in the | ||
// name, then assume we can go with that. | ||
if (envValue && envValue.includes("UTF-8")) { | ||
return envValue; | ||
} | ||
// Otherwise, we're going to guess which encoding to use based on the system. | ||
// This is probably not the best approach in the world, as you could be on | ||
// linux and not have C.UTF-8, but in that case you're probably passing an env | ||
// var for it. This object below represents all of the possible values of | ||
// process.platform per: | ||
// https://nodejs.org/api/process.html#process_process_platform | ||
return { | ||
aix: "C.UTF-8", | ||
android: "C.UTF-8", | ||
cygwin: "C.UTF-8", | ||
darwin: "en_US.UTF-8", | ||
freebsd: "C.UTF-8", | ||
haiku: "C.UTF-8", | ||
linux: "C.UTF-8", | ||
netbsd: "C.UTF-8", | ||
openbsd: "C.UTF-8", | ||
sunos: "C.UTF-8", | ||
win32: ".UTF-8" | ||
}[platform]; | ||
} | ||
// Return the list of plugins that should be passed to the server process. | ||
function getPlugins(opts) { | ||
const plugins = new Set(); | ||
const rubyPlugins = opts.rubyPlugins.trim(); | ||
if (rubyPlugins.length > 0) { | ||
rubyPlugins.split(",").forEach((plugin) => plugins.add(plugin.trim())); | ||
} | ||
if (opts.rubySingleQuote) { | ||
plugins.add("plugin/single_quotes"); | ||
} | ||
if (opts.trailingComma !== "none") { | ||
plugins.add("plugin/trailing_comma"); | ||
} | ||
return Array.from(plugins); | ||
} | ||
// Create a file that will act as a communication mechanism, spawn a parser | ||
// server with that filepath as an argument, then wait for the file to be | ||
// created that will contain the connection information. | ||
export async function spawnServer(opts, killOnExit = true) { | ||
const tmpdir = os.tmpdir(); | ||
const filepath = path.join(tmpdir, `prettier-ruby-parser-${process.pid}.txt`); | ||
const server = spawn( | ||
opts.rubyExecutablePath || "ruby", | ||
[ | ||
url.fileURLToPath(new URL("./server.rb", import.meta.url)), | ||
`--plugins=${getPlugins(opts).join(",")}`, | ||
filepath | ||
], | ||
{ | ||
env: Object.assign({}, process.env, { LANG: getLang() }), | ||
stdio: ["ignore", "ignore", "inherit"], | ||
detached: true | ||
} | ||
); | ||
server.unref(); | ||
if (killOnExit) { | ||
process.on("exit", () => { | ||
if (fs.existsSync(filepath)) { | ||
fs.unlinkSync(filepath); | ||
} | ||
try { | ||
if (server.pid) { | ||
// Kill the server process if it's still running. If we're on windows | ||
// we're going to use the process ID number. If we're not, we're going | ||
// to use the negative process ID to indicate the group. | ||
const pid = process.platform === "win32" ? server.pid : -server.pid; | ||
process.kill(pid); | ||
} | ||
} catch (error) { | ||
// If there's an error killing the process, we're going to ignore it. | ||
} | ||
}); | ||
} | ||
return new Promise((resolve, reject) => { | ||
const interval = setInterval(() => { | ||
if (fs.existsSync(filepath)) { | ||
const connectionJSON = fs.readFileSync(filepath).toString("utf-8"); | ||
resolve({ | ||
serverPID: server.pid, | ||
connectionFilepath: filepath, | ||
connectionOptions: JSON.parse(connectionJSON) | ||
}); | ||
clearTimeout(timeout); | ||
clearInterval(interval); | ||
} else if (server.exitCode) { | ||
reject(new Error("Failed to start parse server.")); | ||
clearTimeout(timeout); | ||
clearInterval(interval); | ||
} | ||
}, 100); | ||
const timeout = setTimeout( | ||
() => { | ||
const message = | ||
"Failed to get connection options from parse server in time. If this happens repeatedly, try increasing the PRETTIER_RUBY_TIMEOUT_MS environment variable beyond 10000."; | ||
clearInterval(interval); | ||
reject(new Error(message)); | ||
}, | ||
parseInt(process.env.PRETTIER_RUBY_TIMEOUT_MS || "10000", 10) | ||
); | ||
}); | ||
} | ||
let connectionOptions; | ||
if (process.env.PRETTIER_RUBY_HOST) { | ||
connectionOptions = JSON.parse(process.env.PRETTIER_RUBY_HOST); | ||
} | ||
// Formats and sends a request to the parser server. We use netcat (or something | ||
// like it) here since Prettier requires the results of `parse` to be | ||
// synchronous and Node.js does not offer a mechanism for synchronous socket | ||
// requests. | ||
async function parse(parser, source, opts) { | ||
if (!connectionOptions) { | ||
const spawnedServer = await spawnServer(opts); | ||
connectionOptions = spawnedServer.connectionOptions; | ||
} | ||
return new Promise((resolve, reject) => { | ||
const socket = new net.Socket(); | ||
socket.on("error", reject); | ||
socket.on("data", (data) => { | ||
const response = JSON.parse(data.toString("utf-8")); | ||
if (response.error) { | ||
const error = new Error(response.error); | ||
if (response.loc) { | ||
error.loc = response.loc; | ||
} | ||
reject(error); | ||
} | ||
resolve(response); | ||
}); | ||
socket.connect(connectionOptions, () => { | ||
socket.end( | ||
JSON.stringify({ | ||
parser, | ||
source, | ||
maxwidth: opts.printWidth, | ||
tabwidth: opts.tabWidth | ||
}) | ||
); | ||
}); | ||
}); | ||
} | ||
// Metadata mostly pulled from linguist and rubocop: | ||
// https://github.com/github/linguist/blob/master/lib/linguist/languages.yml | ||
// https://github.com/rubocop/rubocop/blob/master/spec/rubocop/target_finder_spec.rb | ||
const plugin = { | ||
@@ -84,4 +264,4 @@ languages: [ | ||
ruby: { | ||
parse(text, _parsers, opts) { | ||
return parseSync("ruby", text, opts); | ||
parse(text, opts) { | ||
return parse("ruby", text, opts); | ||
}, | ||
@@ -100,4 +280,4 @@ astFormat: "ruby", | ||
rbs: { | ||
parse(text, _parsers, opts) { | ||
return parseSync("rbs", text, opts); | ||
parse(text, opts) { | ||
return parse("rbs", text, opts); | ||
}, | ||
@@ -116,4 +296,4 @@ astFormat: "rbs", | ||
haml: { | ||
parse(text, _parsers, opts) { | ||
return parseSync("haml", text, opts); | ||
parse(text, opts) { | ||
return parse("haml", text, opts); | ||
}, | ||
@@ -163,4 +343,20 @@ astFormat: "haml", | ||
default: "", | ||
description: "The comma-separated list of plugins to require", | ||
description: "The comma-separated list of plugins to require.", | ||
since: "3.1.0" | ||
}, | ||
rubySingleQuote: { | ||
type: "boolean", | ||
category: "Ruby", | ||
default: false, | ||
description: | ||
"When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals.", | ||
since: "1.0.0" | ||
}, | ||
rubyExecutablePath: { | ||
type: "string", | ||
category: "Ruby", | ||
default: "ruby", | ||
description: | ||
"The path to the Ruby executable to use to run the formatter.", | ||
since: "3.3.0" | ||
} | ||
@@ -176,2 +372,2 @@ }, | ||
module.exports = plugin; | ||
export default plugin; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
5
Yes
131863
10
341
191
3
Updatedprettier@>=3.0.0