diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 83cabbbf..0fe65fce 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -94,12 +94,24 @@ jobs: cd RhinoApp Rscript ../test-lint-js.R + - name: format_js() should format JS scripts + if: always() + run: | + cd RhinoApp + Rscript ../test-format-js.R + - name: lint_sass() should detect lint errors in CSS if: always() run: | cd RhinoApp Rscript ../test-lint-sass.R + - name: format_sass() should format SASS (.scss) scripts + if: always() + run: | + cd RhinoApp + Rscript ../test-format-sass.R + - name: build_js() should build app.min.js if: always() run: | diff --git a/DESCRIPTION b/DESCRIPTION index 3d1888b5..8511f517 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,7 @@ BugReports: https://github.com/Appsilon/rhino/issues License: LGPL-3 Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 Depends: R (>= 2.10) Imports: diff --git a/NAMESPACE b/NAMESPACE index 9a787719..35179f92 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,7 +4,9 @@ export(app) export(build_js) export(build_sass) export(diagnostics) +export(format_js) export(format_r) +export(format_sass) export(init) export(lint_js) export(lint_r) diff --git a/NEWS.md b/NEWS.md index fcfbc9db..7b226386 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,11 @@ -# rhino 1.8.0 +# rhino (development version) + +1. Introduce `format_js()` and `format_sass()` powered by [prettier](https://prettier.io). + * **Note:** `lint_js()` and `lint_sass()` report styling errors. + They _might_ complain about formatting done with `format_js()` and `format_sass()` functions; however, we haven't spotted any issues so far. + If you face any problems with this, then please [raise an issue on GitHub](https://github.com/Appsilon/rhino/issues/new/choose) + +# [rhino 1.8.0](https://github.com/Appsilon/rhino/releases/tag/v1.8.0) 1. All linter functions migrated to `box.linters`. New rhino projects will be configured to use linters from `box.linters`. 2. Updated GitHub Workflow template triggers. diff --git a/R/tools.R b/R/tools.R index 624cd52a..cf2e2718 100644 --- a/R/tools.R +++ b/R/tools.R @@ -233,6 +233,29 @@ lint_js <- function(fix = FALSE) { } } +#' Format JavaScript +#' +#' Runs [prettier](https://prettier.io/) on JavaScript files in `app/js` directory. +#' Requires Node.js installed. +#' +#' You can prevent prettier from formatting a given chunk of your code by adding a special comment: +#' ```js +#' // prettier-ignore +#' ``` +#' Read more about [ignoring code](https://prettier.io/docs/en/ignore). +#' +#' @param fix If `TRUE`, fixes formatting. If FALSE, reports formatting errors without fixing them. +#' @return None. This function is called for side effects. +#' +#' @export +format_js <- function(fix = TRUE) { + if (fix) { + npm("run", "format-js", "--", "--write") + } else { + npm("run", "format-js", "--", "--check") + } +} + #' Build Sass #' #' Builds the `app/styles/main.scss` file into `app/static/css/app.min.css`. @@ -319,6 +342,29 @@ lint_sass <- function(fix = FALSE) { } } +#' Format Sass +#' +#' Runs [prettier](https://prettier.io/) on Sass (.scss) files in `app/styles` directory. +#' Requires Node.js installed. +#' +#' You can prevent prettier from formatting a given chunk of your code by adding a special comment: +#' ```scss +#' // prettier-ignore +#' ``` +#' Read more about [ignoring code](https://prettier.io/docs/en/ignore). +#' +#' @param fix If `TRUE`, fixes formatting. If FALSE, reports formatting errors without fixing them. +#' @return None. This function is called for side effects. +#' +#' @export +format_sass <- function(fix = TRUE) { + if (fix) { + npm("run", "format-sass", "--", "--write") + } else { + npm("run", "format-sass", "--", "--check") + } +} + #' Run Cypress end-to-end tests #' #' Uses [Cypress](https://www.cypress.io/) to run end-to-end tests diff --git a/inst/WORDLIST b/inst/WORDLIST index 05a8facc..8018df59 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,7 +1,6 @@ Addin Addins Appsilon -Boxifying ESLint Init JS @@ -53,6 +52,7 @@ renv roxygen rstudio scalable +scss shinyapps shinymanager shinytest diff --git a/inst/templates/node/package-lock.json b/inst/templates/node/package-lock.json index 7224027d..76b9d655 100644 --- a/inst/templates/node/package-lock.json +++ b/inst/templates/node/package-lock.json @@ -16,6 +16,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-import-resolver-webpack": "^0.13.8", "eslint-plugin-import": "^2.29.1", + "prettier": "^3.3.2", "sass": "^1.69.7", "start-server-and-test": "^2.0.3", "stylelint": "^14.16.1", @@ -7221,6 +7222,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "dev": true, @@ -14186,6 +14202,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true + }, "pretty-bytes": { "version": "5.6.0", "dev": true diff --git a/inst/templates/node/package.json b/inst/templates/node/package.json index 309da471..8d62ba4d 100644 --- a/inst/templates/node/package.json +++ b/inst/templates/node/package.json @@ -5,6 +5,8 @@ "build-sass": "sass --no-source-map --style=compressed ../app/styles/main.scss:../app/static/css/app.min.css", "lint-js": "eslint --config .eslintrc.json ../app/js", "lint-sass": "stylelint ../app/styles", + "format-js": "prettier --config prettier.config.mjs --ignore-path none ../app/js/**/*.js", + "format-sass": "prettier --config prettier.config.mjs --ignore-path none ../app/styles/**/*.scss", "run-app": "cd .. && Rscript -e \"shiny::runApp(port = 3333)\"", "run-cypress": "cypress run --project ../tests", "open-cypress": "cypress open --project ../tests", @@ -22,6 +24,7 @@ "eslint-config-airbnb": "^19.0.4", "eslint-import-resolver-webpack": "^0.13.8", "eslint-plugin-import": "^2.29.1", + "prettier": "^3.3.2", "sass": "^1.69.7", "start-server-and-test": "^2.0.3", "stylelint": "^14.16.1", diff --git a/inst/templates/node/prettier.config.mjs b/inst/templates/node/prettier.config.mjs new file mode 100644 index 00000000..3cbc88eb --- /dev/null +++ b/inst/templates/node/prettier.config.mjs @@ -0,0 +1,11 @@ +/** @type {import("prettier").Config} */ +export default { + overrides: [ + { + files: "../app/js/**/*.js", + options: { + singleQuote: true, + }, + }, + ], +}; diff --git a/man/format_js.Rd b/man/format_js.Rd new file mode 100644 index 00000000..fffe7819 --- /dev/null +++ b/man/format_js.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tools.R +\name{format_js} +\alias{format_js} +\title{Format JavaScript} +\usage{ +format_js(fix = TRUE) +} +\arguments{ +\item{fix}{If \code{TRUE}, fixes formatting. If FALSE, reports formatting errors without fixing them.} +} +\value{ +None. This function is called for side effects. +} +\description{ +Runs \href{https://prettier.io/}{prettier} on JavaScript files in \code{app/js} directory. +Requires Node.js installed. +} +\details{ +You can prevent prettier from formatting a given chunk of your code by adding a special comment: + +\if{html}{\out{