Skip to content

Latest commit

 

History

4,178 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

paranext-core

Extensible Bible translation software

Platform icon

Build Status CodeQL Github Tag

Summary

Platform.Bible is extensible Bible translation software. Its functionality is provided almost completely by extensions in order to be very powerful and flexible, giving developers the freedom to create and to share their desired Bible translation experience.

This repository contains the core Platform.Bible software (Electron client, extension host including the Platform API (PAPI), and .NET library) and the extensions that come bundled with it. There are many other repositories containing additional extensions.

Users

This repository does not distribute a built application. The application released to users is Paratext 10, which is built from this source in paranext/paratext-10-studio; see LICENSING.md for how the source and the released binary are licensed. To try Platform.Bible itself, build it from source with the developer install instructions below.

Linux Users

To install a locally created snap package, run the following commands:

sudo snap install <path to snap file> --dangerous
sudo snap connect platform-bible:dot-platform-bible

Some users may find that not everything works properly in Linux without some additional setup. Please see How to set up Platform.Bible on Linux for more information.

Mac Users

If you download and run the ARM release of Platform.Bible from a computer running Apple Silicon, you will likely encounter a warning from Apple's Gatekeeper stating that "Platform.Bible is an app downloaded from the Internet. Are you sure you want to open it?":

mac-arm-downloaded-internet-warning

Don't be alarmed! This is a typical warning, seen when downloading most apps from the Internet. It also says "Apple checked it for malicious software and none was detected." If you trust Platform.Bible and would like to run it select "Open."

Once the app is opened, you may see a dialog that says "Platform.Bible wants to use your confidential information stored in 'Platform.Bible Safe Storage' in your keychain."

mac-arm-keychain-access-warning

We use encryption to keep your data safe, allowing access to keychain provides encryption services with papi.dataProtection so extensions can store user data securely. If you approve of this, please allow keychain access.

JavaScript Tool Manager

We strongly recommend you use Volta with this repo to automatically use the right version of tools such as node, npm, and pnpm for each relevant repo.

If you don't use Volta, you need to look at the volta property in the package.json of this repository and each development package repository to see the right tool versions to install in your preferred way.

Developer Install

Set up pre-requisites, build, and run:

  1. Install the version of Node.js that matches the version specified in package.json at volta.node and any other necessary package managers. We strongly recommend using Volta to detect and install all necessary JavaScript package managers automatically. If you would like to install them manually, see the JavaScript Tool Manager section for more information on how to determine which JavaScript package managers are required and at which versions.

  2. Install .NET 8 SDK.

    To check if dotnet is installed run:

    dotnet --version
    # 8.0.412 [/usr/local/share/dotnet/sdk] (or some other directory)
    dotnet --list-sdks
    # 8.0.412 (or similar 8.* version)
  3. Ruby >= 3.2 and Bundler - only needed to regenerate THIRD-PARTY-NOTICES.md, which is done on Linux (see Third-party notices). Not needed to build or run the application. On Ubuntu 22.04: sudo snap install ruby --classic --channel=3.4/stable. On 24.04+: sudo apt install ruby-full. Then:

    gem install --user-install bundler
    bundle install   # installs licensee, pinned in Gemfile.lock
  4. Install gitleaks. The pre-commit hook runs gitleaks on staged files to block accidental secret commits — without it, every git commit fails.

    • macOS: brew install gitleaks
    • Windows: winget install Gitleaks.Gitleaks
    • Linux: download a prebuilt binary from the releases page and place it on your PATH (e.g., ~/.local/bin/gitleaks), or sudo apt install gitleaks on Ubuntu 24.04+.

    To verify:

    gitleaks version
  5. Prerequisites for macOS or Linux (below).

  6. Clone, install, build, and run (below).

Linux Development Pre-requisites

  1. Add the system libraries needed for Electron, Build Instructions (Linux). While those are the official instructions they aren't as clear as they used to be - adding these libraries work (this list taken from stagePackages):

    # Ubuntu-22/Debian
    sudo apt-get install libappindicator3-1 libasound2 libdrm2 libdrm-nouveau2 libgbm1 libgl1 libgtk-3-0 libnspr4 libnss3 libsecret-1-0 libtinfo5 libxss1
  2. If you want to build the app package locally (i.e. by running npm run package) also run these commands.

If you are developing on Ubuntu 24.04 with AppArmor, you will need to modify package.json locally to add the --no-sandbox flag to electronmon. For example:

{
  "scripts": {
    "start:main": "concurrently -k -P \"cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --watch --config ./.erb/configs/webpack.config.main.dev.ts\" \"electronmon --no-sandbox . {@}\" --"
  }
}

This should be a temporary workaround until we update to newer versions of electron and electron-builder.

Handling platform-bible: URIs during Linux development

If you want your development Platform.Bible to handle the opening of platform-bible:// URIs, such as for completing authentication from a web browser, do the following to install a development Platform.Bible .desktop launcher.

  1. From the root of the repository, use these commands to copy the launcher into your local applications launchers list, replacing REPO_PATH with the path to your source repository, and update:
    perl -p -e "s#REPO_PATH#$(pwd)#g" platform.bible-dev.desktop > \
      ~/.local/share/applications/platform.bible-dev.desktop
    update-desktop-database ~/.local/share/applications/
  2. Set the newly installed .desktop file as the handler for platform-bible:
    xdg-mime default platform.bible-dev.desktop x-scheme-handler/platform-bible
  3. Optionally check that the handler is set:
    xdg-mime query default x-scheme-handler/platform-bible
  4. After starting Platform.Bible, you can manually check how it handles the links by running:
    xdg-open platform-bible://test

This allows platform-bible:// handling by an existing Platform.Bible process. More work may be needed to successfully handle platform-bible:// if Platform.Bible is not already launched.

macOS Development Pre-requisites

macOS doesn't come preinstalled with all the icu4c libraries. They must be installed separately to provide Unicode support to our .NET code. Platform.Bible is configured to expect those libraries to be installed using MacPorts. The icu package on MacPorts has the icu4c libraries needed for icu.net to run properly.

The build processes are configured to automatically download and package icu4c libraries with the application, but for development this has to be done manually.

The .NET data provider is configured to automatically copy the icu4c dylibs into its build output directory. If for some reason you need to disable that, you will need to set an environment variable for the OS to find them. For example:

export DYLD_FALLBACK_LIBRARY_PATH="$HOME/lib:/usr/local/lib:/usr/lib:/opt/local/lib"

If you need to set environment variables like the above, consider adding them to your .zprofile so you don't have to remember to do it manually.

Cloning and installing dependencies (all platforms)

Cloning and checking out the right version

Clone the repo:

git clone https://github.com/paranext/paranext-core.git
cd paranext-core

If you want to develop an extension for Platform.Bible, check out the Git reference (usually a tag) corresponding to the version for which you want to develop your extension. We recommend you develop for the latest released version, not main. Consult the version table for more information. For example, if you want to develop your extension for version 0.3.0, run the following:

git checkout v0.3.0

Linking local development packages (automatic)

If you are developing paranext-core itself or developing extensions based on a development version of paranext-core like main (as opposed to developing based on a specific tag), you must also have some local development packages set up. npm install in paranext-core should do this automatically for you; you can skip this section if you do not have particular requirements for these local development packages. We develop paranext-core based on specific branches of some dependent repositories, so npm install makes them available for paranext-core to find if they are not already in the expected locations on your computer. If you are developing extensions based on a specific release of Platform.Bible, no further action is necessary; you can skip to Install and Build.

paranext-core looks for local development packages first as sub-directories of the dev-packages folder (e.g. paranext-core/dev-packages/scripture-editors). If it cannot find the appropriate directories there, it looks in the repo's parent folder (e.g. paranext-core/../scripture-editors).

npm install will clone and install the following repositories in dev-packages at the revision listed in dev-packages.json if they are not in one of the expected directories as mentioned above:

Running npm install in paranext-core builds development versions of the npm packages @eten-tech-foundation/platform-editor and @eten-tech-foundation/scripture-utilities from scripture-editors and stages them into dev-packages/staging/. This repo's package.json files depend on those staged folders with file: specifiers, so npm install reads each staged package's own manifest and installs its dependencies into this repo's tree. The staged copies are symlinked into node_modules, so a rebuild is visible immediately without reinstalling.

scripture-editors commits its built dist/, so this is a copy rather than a build: you do not need its toolchain — no pnpm, no nx — to run Platform.Bible. A build only happens if you are editing the editor yourself (npm run build:editor) or your checkout predates the committed build.

This means scripture-editors is free to add, bump, or drop its own dependencies without this repo restating them, and nothing here resolves the editor from the npm registry.

Fresh clones just work: on the very first npm install, the staged packages are created during the install itself — after npm has already resolved the dependency tree — so the install automatically runs a second pass to pick up their dependencies. You will see a message about this; no action is needed. (npm ci doesn't need the second pass at all: it installs the staged packages' dependencies from package-lock.json.) Repeat installs skip the editor build entirely when the staged copy is already current, so this cost is paid only when scripture-editors actually changed.

When scripture-editors' own dependencies change (its package.json, not ours), this repo's package-lock.json must be refreshed to match: run npm install here and commit the lockfile change. Until that lands, npm ci and CI builds fail with a message pointing at exactly this paragraph's fix — loudly, not with stale packages. The check on scripture-editors' platform-yalc branch watches for this so the person updating that branch is reminded to open the core PR.

Note: npm install will check out the revision of scripture-editors specified in dev-packages.json (it will throw and ask you to do something with your working changes if you have any so your changes don't get messed up). However, the general expectation is that this revision for the scripture-editors repository is the branch named platform-yalc when this repo is on main and release-prep when this repo is on release-prep. platform-yalc exists so a breaking change can land on scripture-editors' main before build servers pick it up; move it forward only once this repo is ready for it.

To make your local development and build servers use changes from scripture-editors/main, use that repo's own script rather than moving the branch by hand:

# from a clone of scripture-editors, with platform-yalc checked out and a clean tree
npm run move-platform-yalc

It resets your local branch to origin's state, rebases it onto origin/main, checks that this repo's package-lock.json still matches the editor's dependencies, and force-pushes only if that passes — so a move that would break every core build stops before the push rather than after it. -- --dry-run stops short of pushing; -- --skip-verify is the emergency hatch. Give it a token first (export GITHUB_TOKEN=$(gh auth token)) — the check reads this repo through the GitHub API and the unauthenticated budget is 60 requests an hour.

Moving the branch by hand skips all of that — most importantly the lockfile check, which is the one failure that breaks every build here rather than only the mover's.

To manually set up scripture-editors to be staged locally (this should all be done automatically during npm install):

  1. Clone scripture-editors as a sibling to this repo (alternatively, clone it in paranext-core/dev-packages):

    # from paranext-core
    cd ..
    git clone https://github.com/paranext/scripture-editors.git scripture-editors
    # optionally checkout the platform-yalc branch to match the build servers
    cd scripture-editors
    git checkout platform-yalc
    # go back to paranext-core
    cd ../paranext-core
  2. Run npm install in this repo. The preinstall script stages each package from scripture-editors into dev-packages/staging/, building it only when the pinned revision has no committed dist/ to copy.

    Only that build needs scripture-editors' own dependencies, so install them only if you are editing the editor (if you are using Volta, you must set the environment variable VOLTA_FEATURE_PNPM to 1):

    # from paranext-core
    cd ../scripture-editors
    pnpm install
    cd ../paranext-core

A sibling checkout is used and moved, not just read. When ../scripture-editors exists, npm install here stages from it rather than from its own clone under dev-packages/ — and staging brings that checkout to the pinned revision, fetching and switching branches in it. It refuses to touch a checkout with uncommitted changes, and leaves one alone with a warning when it is on a branch of your own or parked on a detached commit; a clean checkout on main or on the pinned branch is moved. If you would rather keep your own clone out of this entirely, move it somewhere that is not a sibling of this repo and let npm install manage its own under dev-packages/scripture-editors.

Getting your scripture-editors changes into this repo

Pick by what you need the change to show up in:

You want to Run, in this repo
see it in the running dev app npm run build:editor
run tests, typecheck, or a production build npm run stage-dev-packages -- --local
pick up a change to the editor's own dependencies commit in scripture-editors, then npm install

--local is the whole difference between the first two and a normal install: it stages your working tree, uncommitted changes included, instead of the revision dev-packages.json pins. npm run build:editor is that plus a webpack DLL rebuild, and the dev app needs both halves — the dev renderer serves @eten-tech-foundation/platform-editor out of the DLL, so staging alone leaves the app running the old editor while your tests and lint pass against the new one. Restart the dev server afterwards.

The dependency case is the odd one out because npm, not this repo, has to install whatever the editor now declares, and that only happens on a full npm install — which stages a committed revision and refuses to run against a checkout with uncommitted changes. So commit in scripture-editors first; committing on a branch of your own is enough. (A lockfile built from uncommitted editor work would record a dependency closure nobody else can reproduce, which the pre-commit hook blocks you from committing anyway.)

npm install leaves your editor checkout alone unless it is somewhere nothing is being kept — a clean checkout on main, or on the pinned branch itself, which is force-pushed by design. On a branch of your own, or parked on a detached commit, it warns and stages what it finds rather than moving you. So developing both repos at once works the way you would expect: keep your editor branch checked out and this repo builds against it.

Where it does move your checkout, it never discards commits. Anything on the branch that the remote does not have is parked on a refs/stage-rescue/… ref first, and the install prints how to get it back — at the end of its own output as well as when it happens, so it is not lost in the middle of a build. Nothing deletes those refs, so the notice repeats on every install until you deal with the commits and git update-ref -d the ref.

Checking that what is staged matches this repo's lockfile

npm run verify:dev-packages

package-lock.json records each staged package's version and dependencies, but the packages themselves come from the branch dev-packages.json pins, which moves independently of this repo's commits. A checkout of this repo can therefore stage an editor its own lockfile does not describe.

A plain npm install here makes this check on its own and repairs what it can. This command is the check by itself, for the case that cannot: a consumer repo. Every repo that builds against this one installs it with npm ci --ignore-scripts — required, because this repo's postinstall builds an Electron DLL they have no use for — and that skips the check along with everything else. Run it in this repo's directory right after that install:

# in the paranext-core checkout, after: npm ci --ignore-scripts
npm run verify:dev-packages

It reads the tree and reports; it never installs or repairs anything. It needs Node 22.18 or later, like stage-dev-packages — below that, invoke it the same way you invoke that one (node --experimental-strip-types .erb/scripts/postinstall.ts --check).

Install and build

Install dependencies:

npm install

To build, run the following:

npm run build

Starting Development

Start the app in the dev environment:

npm start

After you run npm start (or, in VSCode, launch Debug Platform), you can edit the code, and the relevant processes will hot reload.

Dev builds are cached under node_modules/.cache. If you ever suspect a stale bundle, npm run clean:build-cache clears every cache in that directory (Storybook's and the extensions' included) and the next build repopulates them.

Starting without the .NET watcher

npm start runs the .NET data provider under dotnet watch, which restores and builds the project before the provider's Main() runs — 15-24 seconds of dev startup, depending on how warm the MSBuild and Roslyn servers are (measured on one machine; reproduce with the Startup performance timing tooling below). If you are not editing C#, you can skip it:

npm run start:no-dotnet-watch

This runs the already-built assembly instead, which takes roughly 3 seconds off the time to a fully loaded app and about 15 seconds off how long the data provider takes to become ready.

The script is a thin wrapper that sets PT_DOTNET_NO_WATCH=true. The value must be exactly true; any other value (including other truthy-looking strings) is ignored and the watcher runs as normal. If you start the app some other way, set the variable directly — this is the only handle you have when the npm script is out of reach:

PT_DOTNET_NO_WATCH=true npm start

In VSCode, use the Debug Platform (no .NET watcher) compound instead of Debug Platform — it is the same configuration with PT_DOTNET_NO_WATCH already set. (Debug Platform Backend runs npm run start with a fixed env block, so it cannot pick up the npm script, which is why this ships as its own entry rather than something you edit.)

The trade-off is that C# changes are no longer picked up. After editing C# you have to run npm run build:data yourself and then restart the app — the provider is started once and never re-spawned in place, so a rebuild alone will not reach it. Otherwise the app keeps running the previous build: it starts and behaves normally, just against older C# code, which is why the startup log says explicitly when this mode is active. You also need to have built the provider at least once before the script will work at all.

Developing Extensions

Platform.Bible core extensions are found in the extensions folder. Please follow the instructions in extensions/README.md to develop core extensions.

Please see the Extension Template wiki for guides on developing additional extensions that are not part of the Platform.Bible core. Once you have packaged an extension, it can be distributed for Platform.Bible users to install. See Running your extension in an app for installation information.

Startup performance timing

Platform.Bible can emit startup timing marks across all four processes (main, renderer, extension host, and the .NET data provider) so you can profile where cold-start time goes. Marks are off by default - when disabled, each mark call is a single boolean check, so calls are safe to leave in the code.

To capture a run, set the PT_STARTUP_MARKS=true environment variable when launching. The main process forwards it to the renderer automatically, and the .NET provider reads the same variable:

# Dev (quick, but see the caveat below - dotnet watch distorts the .NET timings)
PT_STARTUP_MARKS=true npm start

# Packaged build (representative numbers - use these for comparisons)
PT_STARTUP_MARKS=true ./release/build/linux-unpacked/platform-bible \
  --no-sandbox --remote-debugging-port=9223

Every process writes its marks to the Electron main.log. Render them as an ordered waterfall from the repo root:

npm run startup-waterfall                              # reads the default main.log for your platform
npm run startup-waterfall -- --log=/path/to/main.log   # or point at a specific capture
npm run startup-waterfall -- --help                    # usage

Notes:

  • Use a packaged build for numbers you intend to compare. In dev, the .NET provider runs under dotnet watch, which inflates the C# portion of the timeline and is not representative.
  • main.log accumulates across launches; the tool shows only the latest run and warns when it drops older ones. For a clean capture, delete (or copy aside) the log between runs.

Analytics test-environment override

Platform.Bible's analytics abstraction (src/extension-host/services/analytics.service.ts) normally decides whether events target the "test" or "production" analytics audience by checking whether the build is packaged and which Send/Receive server it's configured against. Developers and testers who need to force "test" targeting regardless of build/server configuration — so their activity never lands in production analytics — can set the PT_ANALYTICS_TEST_OVERRIDE=true environment variable when launching:

PT_ANALYTICS_TEST_OVERRIDE=true npm start

The value must be exactly true; any other value (including other truthy-looking strings) is ignored and normal resolution applies.

GitHub Pages

Platform.Bible API Documentation

  • Explore the declarations of types available on the PAPI.

Platform.Bible React Components and Hooks Documentation

  • Check out the React components and hooks available to use.

Platform.Bible Utilities Documentation

  • Check out the utility functions, types, and classes available to use.

Platform.Bible and Paratext 10 Studio Wiki

  • Explore links to other resources relevant to Platform.Bible and Paratext 10 Studio.

Packaging for Production

To package apps for the local platform:

npm run package

Publishing

These steps will walk you through releasing a version on GitHub and bumping the version to a new version so future changes apply to the new in-progress version.

  1. Prepare each repository in your dev-packages.json depending on what kind of release you are publishing:

    • Release candidate, alpha, etc.: rebase release-prep on main if it has not been rebased already for this release cycle.
    • Full release: create a release of that repository, then set its revision in dev-packages.json to that release's tag. These packages are not published to npm — this repo stages them from a checkout of that revision — so a release is pinned by naming its tag here.
  2. Make sure the versions in this repo are on the version number you want to release. If they are not, manually dispatch the Bump Versions workflow or run the bump-versions npm script to set the versions to what you want to release on the branch you want to release from.

  3. Manually dispatch the Publish workflow in GitHub Actions targeting the branch you want to release from. This workflow creates a new pre-release for the version you intend to release and creates a new bump-versions-<next_version> branch to bump the version after the release so future changes apply to a new in-progress version instead of to the already released version. This workflow has the following inputs:

    • version: enter the version you intend to publish (e.g. 0.2.0). This is simply for verification to make sure you release the code that you intend to release. It is compared to the version in the code, and the workflow will fail if they do not match.
    • newVersionAfterPublishing: enter the version you want to bump to after releasing (e.g. 0.3.0-alpha.0). Future changes will apply to this new version instead of to the version that was already released. Leave blank if you don't want to bump
    • bumpRef: enter the Git ref you want to create the bump versions branch from, e.g. main. Leave blank if you want to use the branch selected for the workflow run. For example, if you release from a stable branch named release-prep, you may want to bump the version on main so future development work happens on the new version, then you can rebase release-prep onto main when you are ready to start preparing the next stable release.
    • uploadReleaseAssets: whether to upload the Windows and macOS installers to Amazon S3 for internal sharing. This is the only place the built installers go; the GitHub release itself carries no assets.
  4. In GitHub, adjust the new draft release's body and other metadata as desired, then publish the release. The release carries no installers - it is the version tag and the generated notes, which is what a downstream build constructs its own release from.

  5. Open a PR and merge the newly created bump-versions-<next_version> branch.

  6. Update the Software Version Info page with information about this release.

Configure uploading release assets to Amazon S3

Automatically uploading release assets to Amazon S3 when running the Publish workflow requires that some GitHub repository secrets and repository variables are configured properly. If uploading to S3 isn't working, make sure the GitHub secrets and variables are set up properly by checking the following:

  1. You need an AWS access key for a user with the following permissions:

    • s3:* on the S3 bucket where the release assets should be uploaded and the files in the directory in which to put the release assets (see below for more information). There are likely narrower permissions you can set and still successfully upload the release assets.
  2. The following repository secrets and variables need to be correct

    • Repository secrets:
      • AWS_S3_RELEASE_ACCESS_KEY_ID: The access key ID for authenticating with AWS
      • AWS_S3_RELEASE_SECRET_ACCESS_KEY: The secret access key for authenticating with AWS
    • Repository variables:
      • AWS_S3_RELEASE_BUCKET_NAME: The name of the S3 bucket where the release assets should be uploaded
      • AWS_S3_RELEASE_DIRECTORY: The directory in which to put the release build directories. The builds themselves will be in $AWS_S3_RELEASE_DIRECTORY/$RELEASE_VERSION/$RUNNER_OS/

Bumping version without publishing a release

Sometimes, it may be useful to change the version without publishing a release.

To bump versions without publishing a release, manually dispatch the Bump Versions workflow in GitHub Actions targeting the branch on which you want to change versions. Alternatively, you can run the bump-versions npm script. This workflow will create a branch named bump-versions-<version> from the target branch (or, if running the script, your current head) with the needed changes. Open a PR and merge that new branch into the branch on which you want to change versions.

This workflow has the following inputs:

  • newVersion: enter the version you want to bump to (e.g. 0.3.0-alpha.0). Future changes will apply to this new version instead of to the version.
  • newMarketingVersion: a human-readable "marketing-level" version to call this version. It is best to set this only on the specific commit you intend to release so there is no confusion over which version is running. E.g. β1
  • newMarketingVersionMoniker: a human-readable "marketing-level" version moniker to call this version. It is best to set this only on the specific commit you intend to release so there is no confusion over which version is running. E.g. Developer Preview

For example, to bump branch my-branch to version 0.2.0 with optional marketing version β1 and optional marketing version moniker Developer Preview, run the following:

git checkout my-branch
npm run bump-versions -- 0.2.0 --marketing-version β1 --marketing-version-moniker "Developer Preview"

Then create a PR and merge the bump-versions-0.2.0 branch into my-branch. my-branch is now ready for release.

Publishing problems

Following are some problems you may encounter while publishing and steps to solve them.

@swc/core Failed to load native binding

If you see the following error in the GitHub Actions workflow logs while packaging:

Module build failed (from ./node_modules/swc-loader/src/index.js):
Error: Failed to load native binding

Please see "Failed to load native binding" in the Troubleshooting guide for how to solve this problem.

Testing

The following tests run automatically on each GitHub PR (see test.yml).

To run C# unit tests:

cd c-sharp-tests
dotnet test

To run C# unit tests watching for file changes:

cd c-sharp-tests
dotnet watch test

To run all TS unit tests:

npm test

To run an individual TS unit test watching for file changes:

npm test -- <path/to/test-file.test.ts> --watch

You can also use the recommended VS Code extensions to run tests there.

To run playwright-based UI tests that also run on GitHub PRs, you can run:

npm run test:e2e:smoke

All test:e2e:* scripts are there for running variations of the playwright end-to-end tests.

  • test:e2e:smoke runs a single instance of the application, and all tests share that instance
  • test:e2e:smoke-wsl runs the same smoke tests using a "hidden UI" on Linux instead of a visible UI. Its script also takes --wrap <command> to run any other e2e command the same way, e.g. e2e-tests/run-e2e-wsl.sh --wrap npm run test:e2e:isolated multi-window
  • test:e2e:isolated runs tests that need their own application instance rather than sharing one. These tests are organized into feature subsets under e2e-tests/tests/isolated/; run npm run test:e2e:isolated <subset> for one feature, or pass no arguments to list the available subsets (no tests are run in that case). npm run test:e2e:isolated all runs every subset: specs that attach to an app you started live in e2e-tests/tests/attached/ instead, so nothing under tests/isolated/ needs a running app
  • test:e2e-cdp runs tests that require the application UI to be open already
  • test:e2e:all runs every project in e2e-tests/playwright.config.ts. It cannot currently pass: enhanced-resources attaches to a running app, which that config's global setup refuses to start alongside

Storybook

To run Storybook locally:

npm run storybook

To build Storybook:

npm run storybook:build

To run Storybook as a web app, after it was built successfully:

npx http-server ./storybook-static

Windows Development with WSL2

On Windows, you can install WSL (Windows Subsystem for Linux) so you can test cross-platform compatibility on Linux (as well as Windows). You'll need to use a Linux distribution with WSL2 (rather than WSL1) so the X-Server windows can be opened for Electron.

  1. Here is how to install Linux on Windows with WSL.
  2. You'll want to follow that by setting up to use VS Code, Git and NodeJS with WSL. See the various tutorials.
  3. In the WSL distribution, add system libraries needed for Electron, see Linux Development Pre-requisites above.
  4. In the WSL distribution, clone the repo as described above under Developer Install.

You'll be running a copy of the repo in both Windows and WSL so make sure they are both up-to-date.

You can use VS Code from your host to access code in your WSL repo clone using the Microsoft Remote Development VS Code extension.

VS Code Extension Options

Extensions highly recommended for this repo are already displayed in VS Code through the Extensions Recommendations settings. These are optional extensions that our developers enjoy using:

Formatting and Linting

Formatting happens automatically when you commit. If you use VS Code with this repo's recommended extensions it will format when you save.

To check TypeScript for readability, maintainability, and functionality errors, and to check a few other files for proper formatting, run the following from the repo root (or just use VS Code with this repo's recommended extensions)

npm run format
npm run lint

To check C# for readability, maintainability, and functionality errors, run the following from the repo root (or just use VS Code with this repo's recommended extensions)

cd c-sharp
dotnet tool restore
dotnet csharpier .

Documentation in papi.d.ts

VSCode renders JSDoc comments in the UI to make it easier for developers to use functions and properties as intended. However, those comments do not always propagate from modules to the d.ts type definition file when those modules are re-exported. To help with this problem in papi.d.ts that we export for extensions to reference, we added some custom functionality.

If you want comments to be copied from one location in papi.d.ts to another, do the following:

  • In the JSDoc comments that you want copied elsewhere, add "JSDOC SOURCE myServiceName" (must have a blank line after) in the JSDoc comments like this:
/**
 * JSDOC SOURCE myService
 *
 * myService is amazing. Here are more details about it.
 * ...
 */
const myService = {
  ...
}
  • In the location where you want the docs copied, add "JSDOC DESTINATION myServiceName" like this:
const papi = {
  ...
  /** JSDOC DESTINATION myService */
  myService,

Third-party notices

THIRD-PARTY-NOTICES.md and its sidecar THIRD-PARTY-NOTICES.lock.json are generated, never edited by hand. They describe what the packaged application redistributes: the npm packages webpack compiled into dist/, the NuGet closure of the .NET data provider, and the components that belong to neither graph. The document ships inside every installer.

This section is the procedure. For what the document covers and deliberately does not, which policy entry answers a blocked build, and how to change the generator, see .erb/scripts/third-party-notices/README.md.

You do not need to regenerate them as part of ordinary work. Building never touches them and never complains, whatever your webpack cache holds. Regenerate deliberately, when a production dependency changes - an npm package that reaches the bundle, or a NuGet version in c-sharp/ParanextDataProvider.csproj. If you forget, CI's Linux leg fails: it verifies, never regenerates, so the committed copy has to be brought up to date here and committed.

Regenerating (Linux)

The generator reads what webpack actually compiled, and a warm webpack filesystem cache can under-report modules that were served from cache instead of rebuilt - so it refuses to write from one. Start from a cold cache:

rm -rf node_modules/.cache/webpack-* .notices
npm run build
npm run build:extensions:production
dotnet restore c-sharp/ParanextDataProvider.csproj
npm run build:third-party-notices

npm run build leaves its extensions leg in development mode, and every job that packages an installer follows it with build:extensions:production — so the second command is what makes the document describe the graph the installers actually carry. Each manifest records the webpack mode it came from and the generator refuses a mixed set, so skipping it fails rather than producing the wrong document.

Then read the diff and commit both files together; they are written as a pair.

If a package cannot be cleared, the run stops and prints the package, both signals it read, and the exact policy entry to add to .erb/scripts/third-party-notices/notices-policy.json. Nothing is written from an incomplete set.

If dependencies were genuinely removed and the npm set drops by more than 10%, the run refuses that too - the drop is otherwise indistinguishable from a broken tree. Acknowledge a real one explicitly:

NOTICES_ACCEPT_SHRINK=1 npm run build:third-party-notices

Checking without regenerating

Command Answers Needs
npm run verify:third-party-notices Does the committed pair match what this tree derives, verdicts and license texts included? Linux, Ruby, dotnet restore
npm run verify:third-party-notices:shipping-set Is the committed document the one its lock was written beside, and does this platform ship the same npm packages the lock records? Nothing beyond a build
npm run verify:third-party-notices:document Is the committed document the one its lock was written beside? That half on its own. Nothing at all

The shipping-set check runs on every platform, and the release workflows (publish.yml, package-main.yml) run it immediately after their production extension build. Its two halves answer under different conditions. The document check compares two committed files — the lock records a sha256 of the document it was written beside — so nothing can stop it running; it is what keeps a hand-edited THIRD-PARTY-NOTICES.md out of an installer on the paths that cannot afford the full check. The npm shipping-set check reads what webpack compiled, so it can only answer straight after a build whose cache was cold: on a warm cache it says so and skips that half rather than reporting a difference it cannot trust. In CI it only ever runs in that cold position, so a warm stamp there is a real anomaly and fails the build.

npm run package runs the document check rather than the shipping-set one, and has to: it rebuilds from a tree that has already been built, so its webpack caches are warm by construction and the shipping-set half would refuse to answer on every platform. The document is what electron-builder packs into each installer, so package verifies it before packaging it.

Thanks

Some important decisions in this project were inspired by the work done in Visual Studio Code. Thanks VS Code developers for some great ideas!

License

This repository contains code under two licenses:

  • The core Platform.Bible application — the Electron client, extension host, .NET data provider, the bundled extensions, and the build- and lint-time packages lib/papi-dts, lib/eslint-plugin-paranext, and lib/browserslist-config-detect-electron — is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).
  • The two developer libraries an extension links against at runtime — platform-bible-react and platform-bible-utils, both under lib/ — remain under the MIT License, so an extension takes those two under MIT rather than the AGPL. Each carries its own LICENSE file. The boundary keys on runtime linkage, not on the lib/ directory: three of the five packages there are AGPL. Extensions are covered by the Platform.Bible Extension License Exception, an additional permission under AGPL section 7: an extension that talks to Platform.Bible only through the published Extension Interface may be conveyed under terms of its author's choosing. It frees an extension author to choose, and takes no position on what they should choose — see LICENSING.md, "What a third-party extension links against".

See LICENSING.md for the authoritative path-by-path map and copyright attributions.

Copyright © 2017-2026 SIL Global and United Bible Societies

About

Electron client, extension host, and C# library for Paranext

Resources

Stars

27 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages