diff --git a/.github/.gitignore b/.github/.gitignore new file mode 100644 index 00000000..2d19fc76 --- /dev/null +++ b/.github/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 00000000..17b0ce32 --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,31 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + paths: + - "pkg-r/**" + - ".github/workflows/R-CMD-check.yaml" + pull_request: + paths: + - "pkg-r/**" + - ".github/workflows/R-CMD-check.yaml" + +name: R-CMD-check.yaml + +permissions: read-all + +jobs: + routine: + uses: rstudio/shiny-workflows/.github/workflows/routine.yaml@v1 + permissions: + contents: write + with: + working-directory: pkg-r + format-r-code: true + check-js: false + R-CMD-check: + uses: rstudio/shiny-workflows/.github/workflows/R-CMD-check.yaml@v1 + with: + working-directory: pkg-r + extra-check-build-args: '"--compact-vignettes=gs+qpdf"' diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml new file mode 100644 index 00000000..027924a2 --- /dev/null +++ b/.github/workflows/pkgdown.yaml @@ -0,0 +1,66 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + paths: + - 'pkg-r/man/**/*' + - 'pkg-r/vignettes/**/*' + - 'pkg-r/pkgdown/**/*' + - '.github/workflows/pkgdown.yaml' + pull_request: + paths: + - 'pkg-r/man/**/*' + - 'pkg-r/vignettes/**/*' + - 'pkg-r/pkgdown/**/*' + - '.github/workflows/pkgdown.yaml' + release: + types: [published] + workflow_dispatch: + +name: pkgdown.yaml + +permissions: + contents: write + +jobs: + pkgdown: + runs-on: ubuntu-latest + # Only restrict concurrency for non-PR jobs + concurrency: + group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + + # Only run on release events for tags start with "r/v*" + if: github.event_name != 'release' || startsWith(github.ref, 'refs/tags/r/v') + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::pkgdown, local::. + needs: website + working-directory: pkg-r + + - name: Build site + working-directory: pkg-r + shell: Rscript {0} + run: pkgdown::build_site() + + - name: Deploy to GitHub pages ๐Ÿš€ + if: github.event_name != 'pull_request' + uses: JamesIves/github-pages-deploy-action@v4.5.0 + with: + clean: false + branch: gh-pages + folder: docs diff --git a/.github/workflows/py-check.yaml b/.github/workflows/py-check.yaml new file mode 100644 index 00000000..34e173dc --- /dev/null +++ b/.github/workflows/py-check.yaml @@ -0,0 +1,59 @@ +name: py-check.yaml + +on: + push: + branches: [main] + paths: + - 'pkg-py/**/*' + - 'pyproject.toml' + - '.github/workflows/py-check.yaml' + pull_request: + paths: + - 'pkg-py/**/*' + - 'pyproject.toml' + - '.github/workflows/py-check.yaml' + +permissions: + contents: read + +env: + UV_VERSION: "0.9.x" + +jobs: + py-test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.14" + + steps: + - uses: actions/checkout@v4 + + - name: ๐Ÿš€ Install uv + uses: astral-sh/setup-uv@v6.1.0 + with: + version: ${{ env.UV_VERSION }} + + - name: ๐Ÿ Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: ๐Ÿ“ฆ Install the project + run: uv sync --python ${{ matrix.python-version }} --all-extras --all-groups + + - name: ๐Ÿ“œ Show uv.lock + run: cat uv.lock + + - name: ๐Ÿงช Check tests + run: make py-check-tests + + - name: ๐Ÿ“ Check types + run: make py-check-types + + - name: ๐Ÿ“ Check formatting + run: make py-check-format diff --git a/.github/workflows/py-release.yaml b/.github/workflows/py-release.yaml new file mode 100644 index 00000000..ba56b0d9 --- /dev/null +++ b/.github/workflows/py-release.yaml @@ -0,0 +1,54 @@ +name: py-release.yaml + +on: + release: + types: [published] + +env: + UV_VERSION: "0.9.x" + PYTHON_VERSION: 3.13 + +jobs: + py-release: + name: Build and release Python package + runs-on: ubuntu-latest + + # Python release tags start with "py/v*" + if: startsWith(github.ref, 'refs/tags/py/v') + + environment: + name: pypi + url: https://pypi.org/project/shinyjson/ + + permissions: # for trusted publishing + id-token: write + + steps: + - uses: actions/checkout@v4 + + - name: ๐Ÿš€ Install uv + uses: astral-sh/setup-uv@v3 + with: + version: ${{ env.UV_VERSION }} + + - name: ๐Ÿ Set up Python ${{ env.PYTHON_VERSION }} + run: uv python install ${{ env.PYTHON_VERSION }} + + - name: ๐Ÿ“ฆ Install the project + run: uv sync --python ${{ env.PYTHON_VERSION }} --all-extras --all-groups + + - name: ๐Ÿ“ Check types + run: make py-check-types + + - name: ๐Ÿ“ Check formatting + run: make py-check-format + + - name: ๐Ÿงช Check tests + run: make py-check-tests + + - name: ๐Ÿงณ Build package + run: uv build + + # TODO: https://pypi.org/manage/project/shinyjson/settings/publishing/ + - name: ๐Ÿšข Publish release on PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/quartodoc.yaml b/.github/workflows/quartodoc.yaml new file mode 100644 index 00000000..ad416bd3 --- /dev/null +++ b/.github/workflows/quartodoc.yaml @@ -0,0 +1,64 @@ +on: + push: + branches: + - main + paths: + - "docs/**/*" + - "pkg-py/**/*" + - .github/workflows/quartodoc.yaml + workflow_dispatch: + +name: quartodoc.yaml + +permissions: + contents: write + +env: + UV_VERSION: "0.9.x" + PYTHON_VERSION: 3.13 + QUARTO_VERSION: 1.7.31 + +jobs: + docs-publish: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history needed for correct versioning of py pkg + + - name: ๐Ÿ”ต Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + with: + version: ${{ env.QUARTO_VERSION }} + + - name: ๐Ÿš€ Install uv + uses: astral-sh/setup-uv@v6.1.0 + with: + version: ${{ env.UV_VERSION }} + + - name: ๐Ÿ Set up Python ${{ env.PYTHON_VERSION }} + run: uv python install ${{ env.PYTHON_VERSION }} + + - name: ๐Ÿ“ฆ Install shinyjson and dependencies + run: uv sync --python ${{ env.PYTHON_VERSION }} --all-extras --all-groups + + - name: ๐Ÿ”Œ Activate venv + run: | + source .venv/bin/activate + echo "$VIRTUAL_ENV/bin" >> $GITHUB_PATH + echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV + + - name: ๐Ÿญ Update Python docs + run: | + make py-docs-api + quarto render pkg-py/docs + + - name: ๐Ÿšข Deploy to GitHub pages + if: github.event_name != 'pull_request' + uses: JamesIves/github-pages-deploy-action@v4.5.0 + with: + clean: false + folder: docs + branch: gh-pages diff --git a/.github/workflows/verify-js-built.yaml b/.github/workflows/verify-js-built.yaml new file mode 100644 index 00000000..41983b0b --- /dev/null +++ b/.github/workflows/verify-js-built.yaml @@ -0,0 +1,53 @@ +name: verify-js-built.yaml + +on: + push: + branches: ["main", "rc-*"] + paths: + - "js/**/*" + - .github/workflows/verify-js-built.yaml + pull_request: + paths: + - "js/**/*" + - .github/workflows/verify-js-built.yaml + +jobs: + verify_js_built: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: ๐Ÿ”Ž Extract node version from .nvmrc + id: nvm + working-directory: js + run: | + echo "version=$(cat .nvmrc)" >> $GITHUB_OUTPUT + + - name: ๐ŸŸก Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "${{ steps.nvm.outputs.version }}" + + - name: ๐Ÿ†™ Install dependencies + run: make js-setup-ci + env: + NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: ๐Ÿญ Build + run: make js-build + + - name: ๐Ÿง Check for uncommitted changes + run: | + if [[ `git status --porcelain` ]]; then + git diff + echo "Uncommitted changes found. Please commit any changes that result from 'make js-setup-ci js-build'." + exit 1 + else + echo "No uncommitted changes found." + fi + working-directory: js diff --git a/.gitignore b/.gitignore index b7faf403..803b31b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,207 +1,14 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[codz] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py.cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# UV -# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -#uv.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock -#poetry.toml - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. -# https://pdm-project.org/en/latest/usage/project/#working-with-version-control -#pdm.lock -#pdm.toml -.pdm-python -.pdm-build/ - -# pixi -# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. -#pixi.lock -# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one -# in the .venv directory. It is recommended not to include this directory in version control. -.pixi - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.envrc +.Rproj.user +.Rhistory +.RData +.Ruserdata +.Renviron .venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# Abstra -# Abstra is an AI-powered process automation framework. -# Ignore directories containing user credentials, local state, and settings. -# Learn more at https://abstra.io/docs -.abstra/ - -# Visual Studio Code -# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore -# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore -# and can be added to the global gitignore or merged into this file. However, if you prefer, -# you could uncomment the following to ignore the entire vscode folder -# .vscode/ - -# Ruff stuff: -.ruff_cache/ - -# PyPI configuration file -.pypirc - -# Cursor -# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to -# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data -# refer to https://docs.cursor.com/context/ignore-files -.cursorignore -.cursorindexingignore - -# Marimo -marimo/_static/ -marimo/_lsp/ -__marimo__/ +docs/r +docs/py +_dev +uv.lock +.coverage +README.html +README_files +shiny_bookmarks diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..12e7953b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2024 Posit Software, PBC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..3ff7758e --- /dev/null +++ b/Makefile @@ -0,0 +1,236 @@ +# Use qvm to manage quarto +QUARTO_VERSION ?= 1.7.31 +QUARTO_PATH = ~/.local/share/qvm/versions/v${QUARTO_VERSION}/bin/quarto +PATH_PKG_R := pkg-r +PATH_PKG_PY := pkg-py +PATH_PKG_JS := js + +.PHONY: install-quarto +install-quarto: + @echo "๐Ÿ”ต Installing quarto" + @if ! [ -z $(command -v qvm)]; then \ + @echo "Error: qvm is not installed. Please visit https://github.com/dpastoor/qvm/releases/ to install it." >&2 \ + exit 1; \ + fi + qvm install v${QUARTO_VERSION} + @echo "๐Ÿ”น Updating .vscode/settings.json" + @awk -v path="${QUARTO_PATH}" '/"quarto.path":/ {gsub(/"quarto.path": ".*"/, "\"quarto.path\": \"" path "\"")} 1' .vscode/settings.json > .vscode/settings.json.tmp && mv .vscode/settings.json.tmp .vscode/settings.json + @echo "๐Ÿ”น Updating .github/workflows/quartodoc.yaml" + @awk -v ver="${QUARTO_VERSION}" '/QUARTO_VERSION:/ {gsub(/QUARTO_VERSION: .*/, "QUARTO_VERSION: " ver)} 1' .github/workflows/quartodoc.yaml > .github/workflows/quartodoc.yaml.tmp && mv .github/workflows/quartodoc.yaml.tmp .github/workflows/quartodoc.yaml + + +.PHONY: docs +docs: r-docs-render py-docs-render ## [docs] Build the documentation + +.PHONY: docs-preview +docs-preview: ## [docs] Preview the documentation + @npx http-server docs -p 8080 + +.PHONY: js-setup +js-setup: ## [js] Install JS dependencies + @echo "๐Ÿ†™ Setup JS dependencies" + cd $(PATH_PKG_JS) && npm install + +.PHONY: js-setup-ci +js-setup-ci: ## [js] Install JS dependencies as CI + @echo "๐Ÿ†™ Setup JS dependencies" + cd $(PATH_PKG_JS) && npm ci + +.PHONY: js-lint +js-lint: ## [js] Lint JS code + @echo "๐Ÿ“ Linting JS code" + cd $(PATH_PKG_JS) && npm run lint + +.PHONY: js-build +js-build: ## [js] Build JS code + @echo "๐Ÿงณ Building JS code" + cd $(PATH_PKG_JS) && npm run build + +.PHONY: js-build-watch +js-build-watch: ## [js] Build JS code in watch mode + @echo "๐Ÿงณ Building JS code in watch mode" + cd $(PATH_PKG_JS) && npm run watch + +.PHONY: update-dist +update-dist: js-build r-update-dist py-update-dist ## Update shinyjson web assets in all packages + +.PHONY: r-setup +r-setup: ## [r] Install R dependencies + @echo "๐Ÿ†™ Updating R dependencies" + cd $(PATH_PKG_R) && Rscript -e "pak::local_install_dev_deps()" + +.PHONY: r-check +r-check: r-check-format r-check-tests r-check-package ## [r] All R checks + +.PHONY: r-document +r-document: ## [r] Document package + @echo "๐Ÿ“œ Documenting R package" + cd $(PATH_PKG_R) && Rscript -e "devtools::document()" + +.PHONY: r-format +r-format: ## [r] Format R code + air format $(PATH_PKG_R)/ + +.PHONY: r-check-package +r-check-package: ## [r] Check package + @echo "" + @echo "๐Ÿ”„ Running R CMD Check" + cd $(PATH_PKG_R) && Rscript -e "devtools::check(document = FALSE)" + +.PHONY: r-check-tests +r-check-tests: ## [r] Check tests + @echo "" + @echo "๐Ÿงช Running R tests" + cd $(PATH_PKG_R) && Rscript -e "devtools::test()" + +.PHONY: r-check-format +r-check-format: ## [r] Check format + @echo "" + @echo "๐Ÿ“ Checking R format" + air format --check $(PATH_PKG_R)/ + +.PHONY: r-update-dist +r-update-dist: ## [r] Update shinyjson web assets + @echo "" + @echo "๐Ÿ”„ Updating shinyjson web assets" + if [ -d $(PATH_PKG_R)/inst/lib/shiny ]; then \ + rm -rf $(PATH_PKG_R)/inst/lib/shiny; \ + fi + mkdir -p $(PATH_PKG_R)/inst/lib/shiny + cp -r $(PATH_PKG_JS)/dist/shinyjson $(PATH_PKG_R)/inst/lib/shiny/ + (git rev-parse HEAD) > "$(PATH_PKG_R)/inst/lib/shiny/GIT_VERSION" + +.PHONY: r-docs-render +r-docs-render: ## [r] Build R docs + @echo "๐Ÿ“– Rendering R docs with pkgdown" + cd $(PATH_PKG_R) && Rscript -e "pkgdown::build_site()" + +.PHONY: r-docs-preview +r-docs-preview: ## [r] Build R docs + @echo "๐Ÿ“– Rendering R docs with pkgdown" + cd $(PATH_PKG_R) && Rscript -e "pkgdown::preview_site()" + +.PHONY: py-setup +py-setup: ## [py] Setup python environment + uv sync --all-extras --all-groups --upgrade + +.PHONY: py-check +py-check: py-check-format py-check-types py-check-tests ## [py] Run python checks + +.PHONY: py-check-tox +py-check-tox: ## [py] Run python checks across versions with tox + @echo "" + @echo "๐Ÿ”„ Running tests and type checking with tox for Python 3.10--3.14" + uv run tox run-parallel + +.PHONY: py-check-tests +py-check-tests: ## [py] Run python tests + @echo "" + @echo "๐Ÿงช Running tests with pytest" + uv run playwright install + uv run pytest + +.PHONY: py-check-types +py-check-types: ## [py] Run python type checks + @echo "" + @echo "๐Ÿ“ Checking types with pyright" + uv run pyright + +.PHONY: py-check-format +py-check-format: + @echo "" + @echo "๐Ÿ“ Checking format with ruff" + uv run ruff check pkg-py --config pyproject.toml + +.PHONY: py-format +py-format: ## [py] Format python code + uv run ruff check --fix pkg-py --config pyproject.toml + uv run ruff format pkg-py --config pyproject.toml + +.PHONY: py-coverage +py-coverage: ## [py] Generate coverage report + @echo "๐Ÿ“” Generating coverage report" + uv run coverage run -m pytest + uv run coverage report + +.PHONY: py-coverage-report +py-coverage-report: py-coverage ## [py] Generate coverage report and open it in browser + uv run coverage html + @echo "" + @echo "๐Ÿ“ก Serving coverage report at http://localhost:8081/" + @npx http-server htmlcov --silent -p 8081 + +.PHONY: py-update-snaps +py-update-snaps: ## [py] Update python test snapshots + @echo "๐Ÿ“ธ Updating pytest snapshots" + uv run pytest --snapshot-update + +.PHONY: py-docs +py-docs: py-docs-api py-docs-render ## [py] Build python docs + +.PHONY: py-docs-render +py-docs-render: ## [py] Render python docs + @echo "๐Ÿ“– Rendering python docs with quarto" + @$(eval export IN_QUARTODOC=true) + ${QUARTO_PATH} render pkg-py/docs + +.PHONY: py-docs-preview +py-docs-preview: ## [py] Preview python docs + @echo "๐Ÿ“– Rendering python docs with quarto" + @$(eval export IN_QUARTODOC=true) + ${QUARTO_PATH} preview pkg-py/docs + +.PHONY: py-docs-api +py-docs-api: ## [py] Update python API docs + @echo "๐Ÿ“– Generating python docs with quartodoc" + @$(eval export IN_QUARTODOC=true) + cd pkg-py/docs && uv run quartodoc build + cd pkg-py/docs && uv run quartodoc interlinks + +.PHONY: py-docs-api-watch +py-docs-api-watch: ## [py] Update python docs + @echo "๐Ÿ“– Generating python docs with quartodoc" + @$(eval export IN_QUARTODOC=true) + uv run quartodoc build --config pkg-py/docs/_quarto.yml --watch + +.PHONY: py-docs-clean +py-docs-clean: ## [py] Clean python docs + @echo "๐Ÿงน Cleaning python docs" + rm -r pkg-py/docs/api + find pkg-py/docs/py -name '*.quarto_ipynb' -delete + +.PHONY: py-build +py-build: ## [py] Build python package + @echo "๐Ÿงณ Building python package" + @[ -d dist ] && rm -r dist || true + uv build + +.PHONY: py-update-dist +py-update-dist: ## [py] Update shinyjson web assets + @echo "" + @echo "๐Ÿ”„ Updating shinyjson web assets" + if [ -d $(PATH_PKG_PY)/src/shinyjson/www ]; then \ + rm -rf $(PATH_PKG_PY)/src/shinyjson/www; \ + fi + mkdir -p $(PATH_PKG_PY)/src/shinyjson/www + cp -r $(PATH_PKG_JS)/dist/shinyjson $(PATH_PKG_PY)/src/shinyjson/www/ + (git rev-parse HEAD) > "$(PATH_PKG_PY)/src/shinyjson/www/GIT_VERSION" + +.PHONY: help +help: ## Show help messages for make targets + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; { \ + printf "\033[32m%-18s\033[0m", $$1; \ + if ($$2 ~ /^\[docs\]/) { \ + printf "\033[37m[docs]\033[0m%s\n", substr($$2, 7); \ + } else if ($$2 ~ /^\[py\]/) { \ + printf " \033[31m[py]\033[0m%s\n", substr($$2, 5); \ + } else if ($$2 ~ /^\[r\]/) { \ + printf " \033[34m[r]\033[0m%s\n", substr($$2, 4); \ + } else if ($$2 ~ /^\[js\]/) { \ + printf " \033[33m[js]\033[0m%s\n", substr($$2, 5); \ + } else { \ + printf " %s\n", $$2; \ + } \ + }' + +.DEFAULT_GOAL := help diff --git a/README.md b/README.md index 91c6cac0..cc3e67a0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ # shinyjson -Shiny UI using vercel-labs/json-render + + +[![R-CMD-check](https://github.com/posit-dev/shinyjson/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/posit-dev/shinyjson/actions/workflows/R-CMD-check.yaml)
+[![PyPI version](https://badge.fury.io/py/shinyjson.svg)](https://pypi.org/project/shinyjson/) +[![py-check](https://github.com/posit-dev/shinyjson/actions/workflows/py-check.yaml/badge.svg)](https://github.com/posit-dev/shinyjson/actions/workflows/py-check.yaml) + + +Shiny UI using vercel-labs/json-render. + +* ๐Ÿ [shinyjson for Python](https://posit-dev.github.io/shinyjson/py/) + +* ๐Ÿ“˜ [shinyjson for R](https://posit-dev.github.io/shinyjson/r/) diff --git a/js/.eslintrc.js b/js/.eslintrc.js new file mode 100644 index 00000000..7a389616 --- /dev/null +++ b/js/.eslintrc.js @@ -0,0 +1,31 @@ +/* eslint-disable no-undef */ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ], + ignorePatterns: ["dist/*"], + overrides: [], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: ["react", "@typescript-eslint"], + rules: { + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-non-null-assertion": "off", + }, + settings: { + react: { + version: "detect", + }, + }, +} diff --git a/js/.gitignore b/js/.gitignore new file mode 100644 index 00000000..506fcd25 --- /dev/null +++ b/js/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +esbuild-metadata.json diff --git a/js/.nvmrc b/js/.nvmrc new file mode 100644 index 00000000..3f430af8 --- /dev/null +++ b/js/.nvmrc @@ -0,0 +1 @@ +v18 diff --git a/js/.prettierrc b/js/.prettierrc new file mode 100644 index 00000000..6879b154 --- /dev/null +++ b/js/.prettierrc @@ -0,0 +1,14 @@ +{ + "organizeImportsSkipDestructiveCodeActions": true, + "singleQuote": false, + "semi": false, + "trailingComma": "all", + "overrides": [ + { + "files": "**/*.scss", + "options": { + "printWidth": 150 + } + } + ] +} diff --git a/js/build.ts b/js/build.ts new file mode 100644 index 00000000..5b81b2b5 --- /dev/null +++ b/js/build.ts @@ -0,0 +1,143 @@ +import { BuildOptions, build, type Metafile } from "esbuild" +import { sassPlugin } from "esbuild-sass-plugin" +import * as fs from "node:fs/promises" + +// Parse command line arguments +const args = Object.fromEntries( + process.argv + .filter((arg) => arg.startsWith("--")) + .map((arg) => { + const [key, value] = arg.substring(2).split("=") + return [key, value || "true"] + }), +) + +const minify = args.minify !== "false" +const metafile = args.metafile !== "false" + +if (!minify) console.log("Disabling minification") +if (!metafile) console.log("Disabling metafile generation") + +const outDir = "dist" + +const allEsbuildMetadata: Array = [] + +function mergeMetadatas(metadatas: Array): Metafile { + // Merge all the metafile objects together + const mergedMetadata: Metafile = { + inputs: {}, + outputs: {}, + } + + metadatas.forEach((metafile) => { + Object.entries(metafile.inputs).forEach(([key, value]) => { + if ( + mergedMetadata.inputs[key] && + JSON.stringify(mergedMetadata.inputs[key]) !== JSON.stringify(value) + ) { + // It's very possible for multiple MetaFile objects to refer to the same input. + // But if that input has different values in the different Metafile objects, + // that could cause inaccuracies when we merge them. I think it's possible they + // could have different values if tree-shaking is enabled -- this will detect + // those cases and warn the user, and if it happens we can figure out how to + // handle it. + console.error( + `Different values found for key in metadata: ${key}. Overwriting.`, + ) + } + mergedMetadata.inputs[key] = value + }) + Object.entries(metafile.outputs).forEach(([key, value]) => { + if (mergedMetadata.outputs[key]) { + console.error(`Duplicate key found in metadata: ${key}. Overwriting.`) + } + mergedMetadata.outputs[key] = value + }) + }) + + return mergedMetadata +} + +async function bundle_helper( + options: BuildOptions, +): Promise | undefined> { + try { + const result = await build({ + format: "esm", + bundle: true, + minify, + // No need to clean up old source maps, as `minify==false` only during `npm run watch-fast` + // GHA will run `npm run build` which will minify + sourcemap: minify, + metafile, + outdir: outDir, + ...options, + }) + + for (const [output_file_stub] of Object.entries( + options.entryPoints as Record, + )) { + console.log(`Building ${output_file_stub}.js completed successfully!`) + } + + if (result.metafile) { + allEsbuildMetadata.push(result.metafile) + } + + return result + } catch (error) { + console.error("Build failed:", error) + process.exit(1) // Exit with error code to fail CI/CD pipelines + } +} + +interface EntryConfig { + name: string + jsEntry?: string + sassEntry?: string +} + +async function bundleEntry({ + name, + jsEntry, + sassEntry, +}: EntryConfig): Promise { + const tasks = [] + + if (jsEntry) { + tasks.push( + bundle_helper({ + entryPoints: { [name]: jsEntry }, + }), + ) + } + + if (sassEntry) { + tasks.push( + bundle_helper({ + entryPoints: { [name]: sassEntry }, + plugins: [sassPlugin({ type: "css", sourceMap: false })], + }), + ) + } + + await Promise.all(tasks) +} + +const entries: EntryConfig[] = [ + { + name: "shinyjson/shinyjson", + jsEntry: "src/shinyjson/shinyjson.ts", + sassEntry: "src/shinyjson/shinyjson.scss", + }, +] + +;(async () => { + await Promise.all(entries.map(bundleEntry)) + + if (metafile && allEsbuildMetadata.length > 0) { + const mergedMetadata = mergeMetadatas(allEsbuildMetadata) + await fs.writeFile("esbuild-metadata.json", JSON.stringify(mergedMetadata)) + console.log("Metadata file written to esbuild-metadata.json") + } +})() diff --git a/js/eslint.config.js b/js/eslint.config.js new file mode 100644 index 00000000..b4b709fe --- /dev/null +++ b/js/eslint.config.js @@ -0,0 +1,65 @@ +import { defineConfig, globalIgnores } from "eslint/config" +import globals from "globals" +import { fixupConfigRules, fixupPluginRules } from "@eslint/compat" +import tsParser from "@typescript-eslint/parser" +import react from "eslint-plugin-react" +import typescriptEslint from "@typescript-eslint/eslint-plugin" + +import { fileURLToPath } from "url" +import { dirname } from "path" +import { FlatCompat } from "@eslint/eslintrc" +import js from "@eslint/js" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, +}) + +export default defineConfig([ + { + languageOptions: { + globals: { + ...globals.browser, + }, + + parser: tsParser, + ecmaVersion: "latest", + sourceType: "module", + parserOptions: {}, + }, + + extends: fixupConfigRules( + compat.extends( + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + "plugin:prettier/recommended", + "prettier", + ), + ), + + plugins: { + react: fixupPluginRules(react), + "@typescript-eslint": fixupPluginRules(typescriptEslint), + }, + + rules: { + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "prettier/prettier": "error", + }, + + settings: { + react: { + version: "detect", + }, + }, + }, + globalIgnores(["dist/*"]), +]) diff --git a/js/package.json b/js/package.json new file mode 100644 index 00000000..9257699d --- /dev/null +++ b/js/package.json @@ -0,0 +1,46 @@ +{ + "name": "shinyjson", + "private": true, + "version": "0.1.0", + "type": "module", + "description": "Shiny UI using vercel-labs/json-render", + "homepage": "https://posit-dev.github.io/shinyjson/", + "bugs": { + "url": "https://github.com/posit-dev/shinyjson/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/posit-dev/shinyjson.git" + }, + "license": "MIT", + "scripts": { + "lint": "tsc -noEmit && eslint .", + "bundle": "tsx build.ts", + "build": "npm run lint && npm run bundle", + "watch": "npx nodemon --exec 'npm run build' --ext '*' --ignore dist/ --ignore esbuild-metadata.json", + "build-fast": "tsx build.ts --minify=false", + "watch-fast": "npx nodemon --exec 'npm run build-fast' --ext '*' --ignore dist/ --ignore esbuild-metadata.json" + }, + "dependencies": { + "lit": "^3.3.0" + }, + "devDependencies": { + "@eslint/compat": "^1.2.9", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.27.0", + "@types/node": "^22.15.21", + "@types/rstudio-shiny": "git+https://git@github.com/rstudio/shiny.git#v1.10.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", + "esbuild": "^0.25.4", + "esbuild-sass-plugin": "^3.3.1", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-prettier": "^5.4.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "globals": "^16.1.0", + "prettier": "^3.5.3", + "tsx": "^4.19.4", + "typescript": "^5.8.3" + } +} diff --git a/js/src/shinyjson/shinyjson.scss b/js/src/shinyjson/shinyjson.scss new file mode 100644 index 00000000..672dd012 --- /dev/null +++ b/js/src/shinyjson/shinyjson.scss @@ -0,0 +1,4 @@ +// Hello, World! - shinyjson +.shinyjson { + display: block; +} diff --git a/js/src/shinyjson/shinyjson.ts b/js/src/shinyjson/shinyjson.ts new file mode 100644 index 00000000..fd386162 --- /dev/null +++ b/js/src/shinyjson/shinyjson.ts @@ -0,0 +1,4 @@ +// Hello, World! - shinyjson +export function helloWorld(): string { + return "Hello, World!" +} diff --git a/js/tsconfig.json b/js/tsconfig.json new file mode 100644 index 00000000..0721a336 --- /dev/null +++ b/js/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "ES2022", + "lib": ["ES2022", "dom", "dom.iterable"], + "jsx": "react", + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "sourceMap": true, + "moduleResolution": "node", + "noEmit": true, + "esModuleInterop": true, + "skipLibCheck": true, + "target": "ES2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "experimentalDecorators": true, + "useDefineForClassFields": false, + }, + "include": ["**/*.tsx", "**/*.ts"], +} diff --git a/pkg-py/.gitignore b/pkg-py/.gitignore new file mode 100644 index 00000000..1f82c9f6 --- /dev/null +++ b/pkg-py/.gitignore @@ -0,0 +1,3 @@ +src/shinyjson/__version.py + +__pycache__/ diff --git a/pkg-py/CHANGELOG.md b/pkg-py/CHANGELOG.md new file mode 100644 index 00000000..d3c03caa --- /dev/null +++ b/pkg-py/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable changes to the shinyjson Python package will be documented in this file. + +## Unreleased + +- Initial release of shinyjson. diff --git a/pkg-py/README.md b/pkg-py/README.md new file mode 100644 index 00000000..586145d9 --- /dev/null +++ b/pkg-py/README.md @@ -0,0 +1,21 @@ +# shinyjson + +> Shiny UI using vercel-labs/json-render. + +## Installation + +```bash +pip install shinyjson +``` + +## Usage + +```python +from shinyjson import hello_world + +print(hello_world()) +``` + +## Documentation + +- [shinyjson for Python](https://posit-dev.github.io/shinyjson/py/) diff --git a/pkg-py/src/shinyjson/__init__.py b/pkg-py/src/shinyjson/__init__.py new file mode 100644 index 00000000..2380ed66 --- /dev/null +++ b/pkg-py/src/shinyjson/__init__.py @@ -0,0 +1,11 @@ +"""shinyjson: Shiny UI using vercel-labs/json-render.""" + + +def hello_world() -> str: + """Return a Hello, World! greeting.""" + return "Hello, World!" + + +__all__ = [ + "hello_world", +] diff --git a/pkg-py/src/shinyjson/py.typed b/pkg-py/src/shinyjson/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/pkg-r/.Rbuildignore b/pkg-r/.Rbuildignore new file mode 100644 index 00000000..6574a819 --- /dev/null +++ b/pkg-r/.Rbuildignore @@ -0,0 +1,17 @@ +^.*\.Rproj$ +^\.Rproj\.user$ +^LICENSE\.md$ +^scripts$ +^\.github$ +^README\.Rmd$ +^_pkgdown\.yml$ +^docs$ +^pkgdown$ +^vignettes* +^_dev$ +^cran-comments\.md$ +^CRAN-SUBMISSION$ +^_dev$ +^revdep$ +^shiny_bookmarks$ +^vignettes/articles$ diff --git a/pkg-r/DESCRIPTION b/pkg-r/DESCRIPTION new file mode 100644 index 00000000..7b566423 --- /dev/null +++ b/pkg-r/DESCRIPTION @@ -0,0 +1,26 @@ +Package: shinyjson +Title: Shiny UI using vercel-labs/json-render +Version: 0.1.0.9000 +Authors@R: c( + person("Joe", "Cheng", , "joe@posit.co", role = "aut"), + person("Carson", "Sievert", , "carson@posit.co", role = "aut"), + person("Garrick", "Aden-Buie", , "garrick@adenbuie.com", role = c("aut", "cre"), + comment = c(ORCID = "0000-0002-7111-0077")), + person("Barret", "Schloerke", , "barret@posit.co", role = "aut", + comment = c(ORCID = "0000-0001-9986-114X")), + person("Posit Software, PBC", role = c("cph", "fnd"), + comment = c(ROR = "03wc8by49")) + ) +Description: Provides a Shiny UI using vercel-labs/json-render. +License: MIT + file LICENSE +URL: https://posit-dev.github.io/shinyjson/r/, + https://github.com/posit-dev/shinyjson +BugReports: https://github.com/posit-dev/shinyjson/issues +Imports: + htmltools +Suggests: + testthat (>= 3.0.0) +Config/testthat/edition: 3 +Encoding: UTF-8 +Roxygen: list(markdown = TRUE) +RoxygenNote: 7.3.3 diff --git a/pkg-r/LICENSE b/pkg-r/LICENSE new file mode 100644 index 00000000..43f736a4 --- /dev/null +++ b/pkg-r/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2024 +COPYRIGHT HOLDER: Posit Software, PBC diff --git a/pkg-r/LICENSE.md b/pkg-r/LICENSE.md new file mode 100644 index 00000000..12e7953b --- /dev/null +++ b/pkg-r/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2024 Posit Software, PBC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pkg-r/NAMESPACE b/pkg-r/NAMESPACE new file mode 100644 index 00000000..da44dada --- /dev/null +++ b/pkg-r/NAMESPACE @@ -0,0 +1,3 @@ +# Generated by roxygen2: do not edit by hand + +export(hello_world) diff --git a/pkg-r/NEWS.md b/pkg-r/NEWS.md new file mode 100644 index 00000000..c78599c2 --- /dev/null +++ b/pkg-r/NEWS.md @@ -0,0 +1,3 @@ +# shinyjson (development version) + +- Initial release of shinyjson. diff --git a/pkg-r/R/hello_world.R b/pkg-r/R/hello_world.R new file mode 100644 index 00000000..72bc88fc --- /dev/null +++ b/pkg-r/R/hello_world.R @@ -0,0 +1,9 @@ +# Hello, World! - shinyjson + +#' Say Hello, World! +#' +#' @return A character string "Hello, World!" +#' @export +hello_world <- function() { + "Hello, World!" +} diff --git a/pkg-r/R/shinyjson-package.R b/pkg-r/R/shinyjson-package.R new file mode 100644 index 00000000..0cc9debe --- /dev/null +++ b/pkg-r/R/shinyjson-package.R @@ -0,0 +1,2 @@ +#' @keywords internal +"_PACKAGE" diff --git a/pkg-r/README.md b/pkg-r/README.md new file mode 100644 index 00000000..596bb8f8 --- /dev/null +++ b/pkg-r/README.md @@ -0,0 +1,21 @@ +# shinyjson + +> Shiny UI using vercel-labs/json-render. + +## Installation + +```r +pak::pak("posit-dev/shinyjson/pkg-r") +``` + +## Usage + +```r +library(shinyjson) + +hello_world() +``` + +## Documentation + +- [shinyjson for R](https://posit-dev.github.io/shinyjson/r/) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..caf7d0ad --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,168 @@ +[project] +name = "shinyjson" +description = "Shiny UI using vercel-labs/json-render." +readme = "pkg-py/README.md" +requires-python = ">=3.10" +license = { text = "MIT" } +dependencies = [ + "htmltools>=0.6.0", + "shiny>=1.4.0", +] +dynamic = ["version"] +authors = [ + {name = "Joe Cheng", email = "joe@posit.co"}, + {name = "Carson Sievert", email = "carson@posit.co"}, + {name = "Garrick Aden-Buie", email = "garrick@posit.co"} +] + +[project.urls] +Homepage = "https://posit-dev.github.io/shinyjson/" +Documentation = "https://posit-dev.github.io/shinyjson/py/" +Repository = "https://github.com/posit-dev/shinyjson" +Issues = "https://github.com/posit-dev/shinyjson/issues/" +Changelog = "https://github.com/posit-dev/shinyjson/blob/main/pkg-py/CHANGELOG.md" + +[project.optional-dependencies] +providers = [ + "anthropic;python_version>='3.11'", + "chatlas[mcp]>=0.12.0", + "pydantic", + "google-generativeai", + "langchain-core>=1.0.0", + "ollama>=0.4.0", + "openai", + "tokenizers", +] + +[dependency-groups] +test = [ + "coverage>=7.8.2", + "faicons", + "ipyleaflet", + "pandas", + "plotly", + "pyright>=1.1.398", + "pytest>=6.2.4", + "playwright>=1.43.0", + "pytest-playwright>=0.3.0", + "shinylive", + "shinywidgets", + "tox-uv>=1", +] +dev = [ + "quartodoc>=0.10.0", + "ruff>=0.6.5", + {include-group = "test"}, +] + +[tool.uv] +upgrade-package = ["shinyjson"] + +[build-system] +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[tool.hatch.build] +skip-excluded-dirs = true + +[tool.hatch.build.targets.wheel] +packages = ["pkg-py/src/shinyjson"] +include = ["py.typed"] + +[tool.hatch.build.targets.sdist] +include = ["/pkg-py/src/shinyjson"] + +[tool.hatch.version] +source = "vcs" +tag-pattern = "^py/(?:[\\w-]+-)?(?P[vV]?\\d+(?:\\.\\d+){0,2}[^\\+]*)(?:\\+.*)?$" + +[tool.hatch.version.raw-options] +git_describe_command = "git describe --dirty --tags --long --match 'py/v*'" + +[tool.hatch.build.hooks.vcs] +version-file = "pkg-py/src/shinyjson/__version.py" + +[tool.pyright] +include = ["pkg-py"] +exclude = ["pkg-py/_dev"] + +[tool.tox] +legacy_tox_ini = """ +[tox] +env_list = type-py3{10,11,12,13,14}, py3{10,11,12,13,14} +isolated_build = True + +[testenv] +package = wheel +wheel_build_env = .pkg +commands = pytest + +[testenv:type] +deps = + pyright + pytest +commands = pyright +""" + +[tool.ruff] +src = ["pkg-py"] +exclude = [ + "__version.py", + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", + "_dev", +] + +line-length = 80 +indent-width = 4 + +target-version = "py310" + +[tool.ruff.lint] +select = ['E', 'F', 'W', 'A', 'PLC', 'PLE', 'PLW', 'I'] +ignore = [ + "A001", # Variable shadowing Python builtin, e.g. `input` + "A002", # Argument shadowing Python builtin, e.g. `id` + "A004", # Import shadowing Python builtin, e.g. `input` + "E501", + "PLC0415", # Import outside top-level +] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = true +docstring-code-line-length = "dynamic" diff --git a/shinyjson.Rproj b/shinyjson.Rproj new file mode 100644 index 00000000..270314b8 --- /dev/null +++ b/shinyjson.Rproj @@ -0,0 +1,21 @@ +Version: 1.0 + +RestoreWorkspace: Default +SaveWorkspace: Default +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd,collate,namespace