Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

## Checklist

- [ ] `npm run format` passes (Biome)
- [ ] `pnpm run format` passes (Biome)
- [ ] Example has `start` and `start:e2e` scripts in `package.json`
- [ ] `SERVER_DIR=examples/<name> npm run test` passes (Playwright smoke test)
- [ ] `SERVER_DIR=examples/<name> pnpm run test` passes (Playwright smoke test)
- [ ] `README.md` included with prerequisites, setup, and usage
- [ ] Uses current pinned `@nutrient-sdk/viewer` version
10 changes: 7 additions & 3 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: lts/*
- name: Check pnpm workflows
run: ./tests/pnpm-workflows.test.sh
- name: Install dependencies
run: npm install && npm run install-dependencies
run: pnpm install --frozen-lockfile && pnpm run install-dependencies
- name: Install Playwright Browsers
run: npm exec playwright install chromium --with-deps
run: pnpm exec playwright install chromium --with-deps
- name: Run Playwright tests
run: npm run e2e-tests
run: pnpm run e2e-tests
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ !cancelled() }}
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/update-nutrient-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ jobs:

- name: Setup pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
with:
version: 10

- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
Expand Down Expand Up @@ -158,6 +156,8 @@ jobs:
pull request created by \`GITHUB_TOKEN\` does not trigger the Biome or
Playwright workflows, so the checks tab here will be empty.

- Dependency audit fixes: pnpm examples may include security overrides
in \`pnpm-workspace.yaml\` and the corresponding lockfile updates
- Formatting (\`pnpm run format\`): applied to \`examples/\`
- E2E smoke tests (\`pnpm run e2e-tests\`): ${e2e_result}

Expand Down
4 changes: 2 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
npm run lint-staged
npm run check-biome-version
pnpm run lint-staged
Comment thread
miguelcalderon marked this conversation as resolved.
pnpm run check-biome-version
28 changes: 20 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,31 +71,43 @@ This updates `package.json` in every example directory.
## Commands

```bash
# Install all example dependencies
npm run install-dependencies
# One-time: enable the package-manager shims bundled with Node.js
corepack enable

# Install the root and all example dependencies with the pinned pnpm version
pnpm install --frozen-lockfile
pnpm run install-dependencies

# Format code (Biome)
npm run format
pnpm run format

# Run e2e smoke tests (starts each example, checks PSPDFKit loads)
npm run e2e-tests
pnpm run e2e-tests

# Run Playwright directly for a single example
SERVER_DIR=examples/javascript-vite npm run test
SERVER_DIR=examples/javascript-vite pnpm run test

# Audit and fix vulnerabilities across all examples
npm run audit-fix
pnpm run audit-fix

# Bump Nutrient SDK version in all examples (version is required)
npm run update-nutrient-version -- <version>
pnpm run update-nutrient-version -- <version>
```

The root `packageManager` field pins pnpm, and Corepack applies that pin to every
directory below the root, including the npm-based examples. If you enabled
Corepack's npm shim (`corepack enable npm`), set `COREPACK_ENABLE_STRICT=0` before
running `npm` inside an example by hand; the scripts and the Playwright config set it
for you. Root-level scripts, hooks, and docs use pnpm.

## Adding a New Example

1. Create `examples/<framework-name>/`
2. Add a `package.json` with:
- `start` script — dev server
- `start:e2e` script — dev server on port 3000 (for Playwright)
- If it uses `pnpm-lock.yaml`, a sibling `pnpm-workspace.yaml`; the scripts
reject pnpm examples without one so they cannot silently join the root workspace.
3. The example must call `PSPDFKit.load()` and render `.PSPDFKit-Container`
4. Add a `README.md` following the pattern of existing examples:
- Prerequisites
Expand All @@ -104,7 +116,7 @@ npm run update-nutrient-version -- <version>
5. Use the current pinned `@nutrient-sdk/viewer` version
6. Run the Playwright smoke test against your example:
```bash
SERVER_DIR=examples/<framework-name> npm run test
SERVER_DIR=examples/<framework-name> pnpm run test
```
7. Update the root `README.md` if adding a new framework category

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ This repository includes a check to ensure that the Biome version in `package.js

- During CI/CD in the GitHub workflow
- As a pre-commit hook to prevent commits with mismatched versions
- Manually using `npm run check-biome-version`
- Manually using `pnpm run check-biome-version`

If you update the Biome version in `package.json`, make sure to update it in `.github/workflows/biome.yml` as well.
2 changes: 2 additions & 0 deletions examples/svelte-kit/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
minimumReleaseAge: 0

allowBuilds:
esbuild: true

Expand Down
2 changes: 2 additions & 0 deletions examples/vue-composition-api/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
minimumReleaseAge: 0

allowBuilds:
esbuild: true

Expand Down
19 changes: 2 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"keywords": [],
"author": "",
"license": "MIT",
"packageManager": "pnpm@11.20.0+sha512.9a6f330a95b66446ea088faf1521405a8a01f07fde7124cc9958dfed52d4bb436737e65b08f85f37b46fcba375092558ac51262b816844b22f63406ed166bfee",
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@playwright/test": "^1.56.1",
Expand All @@ -23,28 +24,12 @@
"e2e-tests": "./scripts/e2e-tests.sh",
"lint-staged": "lint-staged",
"test": "playwright test",
"test:pnpm-workflows": "./tests/pnpm-workflows.test.sh",
"audit-fix": "./scripts/audit-dependencies.sh",
"update-nutrient-version": "./scripts/update-nutrient-in-examples.sh",
"check-biome-version": "./scripts/check-biome-version.sh"
},
"lint-staged": {
"*": ["biome check --write --no-errors-on-unmatched"]
},
"pnpm": {
"overrides": {
"ajv": "^8.18.0",
"http-proxy-middleware": "^2.0.10",
"on-headers": "^1.1.0",
"node-forge": "^1.4.0",
"qs": "^6.15.1",
"shell-quote": "^1.9.0",
"fast-uri": "^3.1.4",
"picomatch": "^2.3.2",
"path-to-regexp": "^0.1.13",
"ws": "^8.21.0",
"websocket-driver": "^0.7.5",
"uuid": "^11.1.1"
},
"onlyBuiltDependencies": ["@biomejs/biome"]
}
}
3 changes: 3 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export default defineConfig({
],
webServer: {
command: `cd ${process.env.SERVER_DIR} && npm run start:e2e`,
// The examples are npm projects, but Corepack applies the root pnpm pin to
// them and rejects npm when its strict npm shim is enabled.
env: { COREPACK_ENABLE_STRICT: "0" },
url: "http://localhost:3000",
reuseExistingServer: true,
},
Expand Down
27 changes: 27 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
packages:
- "."
Comment thread
miguelcalderon marked this conversation as resolved.

# pnpm 11 defaults verify-deps-before-run to "install", which would run a
# networked install from `pnpm run` (the pre-commit hook included) whenever
# node_modules is out of date. Warn instead so scripts never mutate the tree.
verifyDepsBeforeRun: warn

allowBuilds:
"@biomejs/biome": true
Comment thread
miguelcalderon marked this conversation as resolved.

onlyBuiltDependencies:
- "@biomejs/biome"
Comment thread
miguelcalderon marked this conversation as resolved.

overrides:
"ajv": "^8.18.0"
"http-proxy-middleware": "^2.0.10"
"on-headers": "^1.1.0"
"node-forge": "^1.4.0"
"qs": "^6.15.1"
"shell-quote": "^1.9.0"
"fast-uri": "^3.1.4"
"picomatch": "^2.3.2"
"path-to-regexp": "^0.1.13"
"ws": "^8.21.0"
"websocket-driver": "^0.7.5"
"uuid": "^11.1.1"
42 changes: 29 additions & 13 deletions scripts/audit-dependencies.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#!/bin/bash
# Runs npm/pnpm audit fix on all examples and outputs the result
# Runs npm/pnpm audit fix on all examples and outputs the result.
# This aggregate report deliberately does not enable errexit: predicates and
# arithmetic expressions below use nonzero statuses as ordinary control flow.

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# shellcheck source=./pnpm-helpers.sh
source "${SCRIPT_DIR}/pnpm-helpers.sh"
Comment thread
miguelcalderon marked this conversation as resolved.

echo -e "\033[37;1mAuditing npm vulnerabilities in examples\033[0m\r"

Expand Down Expand Up @@ -29,17 +33,30 @@ for dir in examples/*; do
result=0
initialresult=0
audit_error=0
audit_error_reason="pnpm/npm command failed or returned invalid data"
has_lockfile=0
initial_json=""
audit_json=""

if [ -f "pnpm-lock.yaml" ]; then
has_lockfile=1
initial_json=$(pnpm audit --json 2>/dev/null)
# pnpm spells this `--fix` (there is no `pnpm audit fix` subcommand),
# and it only writes overrides into package.json — the install is what
# actually applies them to the lockfile and node_modules.
pnpm audit --fix > /dev/null 2>&1
pnpm install --no-frozen-lockfile > /dev/null 2>&1
audit_json=$(pnpm audit --json 2>/dev/null)
if ! require_local_pnpm_workspace "$dir"; then
audit_error=1
audit_error_reason="no sibling pnpm-workspace.yaml"
else
initial_json=$(pnpm audit --json 2>/dev/null)
# pnpm writes override fixes to the local workspace file. The
# install applies them to the lockfile and node_modules. Real
# command failures remain visible and make the audit fail.
if ! run_pnpm_audit_fix; then
audit_error=1
elif ! run_pnpm_install_quietly --no-frozen-lockfile; then
Comment thread
miguelcalderon marked this conversation as resolved.
audit_error=1
audit_error_reason="pnpm install failed; inspect ${dir}/pnpm-workspace.yaml for partial allowBuilds changes"
else
audit_json=$(pnpm audit --json 2>/dev/null)
fi
fi
elif [ -f "package-lock.json" ]; then
has_lockfile=1
initial_json=$(npm audit --json 2>/dev/null)
Expand All @@ -57,7 +74,7 @@ for dir in examples/*; do
fi

if (( audit_error )); then
echo -e " \033[91mAudit could not complete\033[0m (registry endpoint error — result unknown)"
echo -e " \033[91mAudit could not complete\033[0m (${audit_error_reason})"
error_dirs+=("$dir")
elif (( initialresult > 0 )); then
((fixed = initialresult - result))
Expand Down Expand Up @@ -106,9 +123,7 @@ echo -e "\033[37;1m────────────────────

if [ ${#vuln_dirs[@]} -eq 0 ] && [ ${#error_dirs[@]} -eq 0 ]; then
echo -e "\033[92;1mAll examples are vulnerability-free!\033[0m"
elif [ ${#vuln_dirs[@]} -eq 0 ]; then
echo -e "\033[92mNo remaining vulnerabilities found.\033[0m"
else
elif [ ${#vuln_dirs[@]} -gt 0 ]; then
total_remaining=0
for i in "${!vuln_dirs[@]}"; do
echo -e " \033[31m${vuln_dirs[$i]}: ${vuln_counts[$i]} vulnerabilities\033[0m (${vuln_severities[$i]})"
Expand All @@ -118,8 +133,9 @@ else
fi

if [ ${#error_dirs[@]} -gt 0 ]; then
echo -e "\n \033[91;1m${#error_dirs[@]} example(s) could not be audited (registry endpoint error):\033[0m"
echo -e "\n \033[91;1m${#error_dirs[@]} example(s) could not be audited:\033[0m"
Comment thread
miguelcalderon marked this conversation as resolved.
for d in "${error_dirs[@]}"; do
echo -e " \033[91m${d}\033[0m"
done
exit 1
Comment thread
miguelcalderon marked this conversation as resolved.
fi
2 changes: 1 addition & 1 deletion scripts/e2e-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ for dir in examples/*; do
done
if [ "$skip" = false ]; then
echo "Running e2e tests in $dir"
if ! (SERVER_DIR="$dir" npm run test); then
if ! (SERVER_DIR="$dir" pnpm run test); then
failed+=("$dir")
fi
fi
Expand Down
4 changes: 4 additions & 0 deletions scripts/install-dependencies.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/bin/bash
# Runs npm/pnpm install on all examples
set -euo pipefail

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# shellcheck source=./pnpm-helpers.sh
source "${SCRIPT_DIR}/pnpm-helpers.sh"
Comment thread
miguelcalderon marked this conversation as resolved.

for dir in examples/*; do
if [ -d "$dir" ]; then
Expand All @@ -10,6 +13,7 @@ for dir in examples/*; do
pushd "${SCRIPT_DIR}/../${dir}/" > /dev/null

if [ -f "pnpm-lock.yaml" ]; then
require_local_pnpm_workspace "$dir"
pnpm install
elif [ -f "package-lock.json" ]; then
npm install
Expand Down
48 changes: 48 additions & 0 deletions scripts/pnpm-helpers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/bash

# These helpers back scripts that run npm inside the npm-based examples below
# the root pnpm pin. Corepack applies that pin to every directory under the
# root and, with its strict npm shim enabled, would reject those npm calls.
export COREPACK_ENABLE_STRICT=0

# pnpm 11 applies its minimum release age to every lockfile entry during
# install, not only while resolving new versions. All scripts must use the same
# policy so a lockfile written by the audit or update script remains installable
# in CI. The opt-out also avoids tracked minimumReleaseAgeExclude entries.
export pnpm_config_minimum_release_age=0

require_local_pnpm_workspace() {
local example="${1:-The current directory}"

if [ ! -f "pnpm-workspace.yaml" ]; then
echo "${example} has a pnpm-lock.yaml but no sibling pnpm-workspace.yaml; refusing to use an ancestor workspace." >&2
return 1
fi
}

run_pnpm_command_quietly() {
local output
local status=0

output=$(pnpm "$@" 2>&1) || status=$?
if (( status != 0 )); then
printf '%s\n' "$output" >&2
return "$status"
fi
if [ -n "$output" ]; then
printf '%s\n' "$output"
fi
}
Comment thread
miguelcalderon marked this conversation as resolved.

run_pnpm_audit_fix() {
# A successful fix exits zero even when the initial audit found advisories.
# Preserve every real pnpm failure instead of hiding bad flags, registry
# failures, or an unwritable workspace file.
run_pnpm_command_quietly audit --fix=override --ignore-registry-errors
}

run_pnpm_install_quietly() {
# pnpm 11 fails when it encounters an unapproved dependency build. Keep the
# successful output quiet, but print the complete failure before propagating it.
run_pnpm_command_quietly install "$@"
}
10 changes: 8 additions & 2 deletions scripts/update-nutrient-in-examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
set -euo pipefail

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# shellcheck source=./pnpm-helpers.sh
source "${SCRIPT_DIR}/pnpm-helpers.sh"

VERSION="${1:-}"

Expand All @@ -27,11 +29,15 @@ upgrade_npm_in_example() {
echo -e "\n${Green}Upgrading ${Yellow}${directory}${Green} to ${Yellow}${VERSION}${NoColor}"

if [ -f "pnpm-lock.yaml" ]; then
require_local_pnpm_workspace "examples/${directory}"
pnpm install "@nutrient-sdk/viewer@${VERSION}" --save --save-exact

pnpm install > /dev/null
run_pnpm_audit_fix

pnpm audit fix > /dev/null || true
# pnpm 11 treats an unapproved dependency build as an error. Keep this
# fail-fast so the automated bump never continues with only some examples
# updated; stderr remains visible in the workflow log.
run_pnpm_install_quietly --no-frozen-lockfile
elif [ -f "package-lock.json" ]; then
npm install "@nutrient-sdk/viewer@${VERSION}" --save --save-exact

Expand Down
Loading
Loading