This repository is an example of how to setup a development environment for building online psych experiments. Key aspects are:
- TypeScript centric
- Future-looking linting and formatting configuration using ESLint and Prettier
- Vite bundler for robust cross-platform support
- jsPsych for UX flow
- serverless data handling with Firestore
- Firebase hosting
- Prolific integration
- Fork this repo:
Use this template
>Create a new repository
- Git clone the forked repo (replace with your repo info): e.g.
git clone --branch main https://github.com/daeh/jspsych-template.git jspsych-template
- Enter the repo folder (replace with your repo info): e.g.
cd jspsych-template
Install the dependencies using Yarn
###
### From within the cloned repo folder
###
### Install the Firebase dependencies
yarn install
### Start the Vite server
yarn dev
If you're new to Yarn
Yarn is a package manager, like npm
, for Node.
https://yarnpkg.com/getting-started/install
npm install -g corepack
corepack enable
yarn set version stable
yarn install
You can format, lint and build the project from the command line by calling the commands in package.json
:
package.json scripts
{
"scripts": {
"dev": "vite --config vite.config.mts hosting",
"build": "vite build --config vite.config.mts hosting",
"lint": "ESLINT_USE_FLAT_CONFIG=true && prettier --config prettier.config.mjs --write . && eslint --config eslint.config.mjs --fix . && tsc --project tsconfig.json --noEmit"
}
}
To develop the website, run yarn dev
, which will open a localhost Vite server that will update as you make modifications.
You don't need to set up Firebase, Firestore or Prolific to develop the experiment. This app comes built to start development immediately. There are primarily two toggles to help with sandboxed development, which are found in appConfig.ts
.
-
Setting
const debuggingMode = true
will increase the verbosity of the console output.- Alternatively, you can force debugging mode by including
debug
as a URL Parameter, e.g.https://mysite.web.app/?debug=true
- Alternatively, you can force debugging mode by including
-
Setting
const simulateMockDatabase = true
will make the app use a serverless emulator so that you don't have to set up Firestore before beginning development.- Similarly, you can modulate the backend with a URL Parameter, e.g.
https://mysite.web.app/?debug=true&mock=false
- Similarly, you can modulate the backend with a URL Parameter, e.g.
For developing the website, this project uses
- ESLint (configured in
eslint.config.mjs
) - TypeScript (configured in
tsconfig.*.json
) - Prettier (configured in
prettier.config.mjs
)
The ESLint config integrates these configurations.
For bundling the website, this project uses
- Vite (configured in
vite.config.mts
) - Tailwind CSS (configured in
tailwind.config.ts
) - PostCSS (configured in
vite.config.mts
; uses Autoprefixer) - Browserslist (via the browserslist-to-esbuild plugin; configured in
package.json
) - jsPsych - UX (experiment flow, data capture)
For serving the website, this project uses
- Firebase - hosting (configured by files in the project root)
- Firestore - serverless data storage/access (configured by files in the project root)
- Prolific - recruitment
This project uses a future-looking configuration that implements the major developments from ESLint. The main config file in this repo is the flat ESLint config, eslint.config.mjs
.
ES Module parsing
This project is configured as an ES Module
, so this config file could be named eslint.config.js
, but I have given it the mjs
extension to make this config work for Common.js Module
development with minimal reconfiguration.
Beginning in ESLint v9.0.0
, the default will be the new flat config system. This will deprecate the Common.js Module
config system (which uses .eslintrc.js
), replacing it with the ES Module
config system (which uses eslint.config.js
).
ESLint is deprecating formatting rules, passing over maintenance and development to the community-run plugin ESLint Stylistic.
For VS Code to respect the configuration, you need to specify the formatter for the relevant files. This is done for you in VSCodeProject.code-workspace
and in .vscode/settings.json
(these are redundant, you only need one). This configures the ESLint extension to use the flat config system, makes VS Code use the Prettier - Code Formatter extensions for formatting filetypes not covered by ESLint, and enables Tailwind CSS IntelliSense. This obviously requires that you have these extensions enabled for the workspace. Activate the VSCodeProject.code-workspace
via File > Open Workspace from File...
(or by double-clicking it), or activate .vscode
via File > Open Folder...
in VS Code.
The relevant settings are:
VSCode Settings
{
"editor.formatOnSave": true,
"eslint.useFlatConfig": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"files.associations": {
"*.css": "tailwindcss"
},
"[javascript][javascriptreact][typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json][jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css][scss][less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
For IntelliJ IDEA / WebStorm to respect the configuration, you need to enable ESLint and Prettier for the relevant filetypes. There is an example config in .idea
. To enable ESLint and Prettier manually:
IntelliJ Setup
Settings... > Languages & Frameworks > JavaScript > Code Quality Tools > ESLint
- Enable
Automatic ESLint configuration
- Enable
Run eslint --fix on save
- Add the additional filetypes to the
Run for files
field:{**/*,*}.{ts,mts,cts,tsx,mtsx,js,mjs,cjs,jsx,mjsx,html,vue}
- Enable
Settings... > Languages & Frameworks > JavaScript > Prettier
- Enable
Automatic Prettier configuration
- Enable
Run on save
- Add the additional filetypes to the
Run for files
field:{**/*,*}.{ts,mts,cts,tsx,mtsx,js,mjs,cjs,jsx,mjsx,json,html,css,scss,vue,astro}
- Enable
If you change the project from an ES Module
to a Common.js Module
, or if ESLint isn't working, try this fix from Ditlef Diseth:
Settings... > Languages & Frameworks > JavaScript > Code Quality Tools > ESLint
- Switch to
Manual ESLint configuration
- Add this string to the
Extra ESLint options
field:ESLINT_USE_FLAT_CONFIG=true yarn eslint --config eslint.config.mjs
- Switch to
Firebase Setup
TODO: describe how to setup hosting and database
Here's a useful guide
After you setup your Firebase and Firestore accounts and services, add your configurations to
Push the Firestore rules (defined in firestore.rules)
yarn deploy-rules
Once you have the rules deployed, you can switch from using the mock database (a simple database emulator) to Firestore.
Set const simulateMockDatabase = false
in appConfig.ts
. This will cause the application to use creds.ts
and store the input in Firestore. In production, the app writes to exptData/:uid
, but will instead write to exptData-dbug/:uid
when it is in debugging mode or running locally. This is so that there's no confusion about what data was generated locally during development.
When you're ready to deploy the website, push it to Firebase
yarn deploy
Prolific URL Search Params
The project is looks for Prolific URL parameters and stores them. Make sure that you've set up Prolific to use URL Search Params.
In order to register that Prolific users have completed the experiment, add the study's Completion Code to const prolificCompletionCode = ...
in creds.ts
.
The script scripts/releaseScript.mjs
automates deployment of the experiments. You can run it from the root directory with:
yarn release
The script will walk you through committing your changes to the git repo that you forked.
A key idea here is that there should never be ambiguity about what code was served to a participant.
The releaseScript.mjs
prompts you to increment the version number, generates a new git commit, injects the version number and the git commit's SHA hash variables into the static website, and then deploys the website to Firebase. The version and hash variables are stored alongside a user's responses.
Deployment
TODO: elaborate
Retrieve the data from Firestore using the Firebase Admin SDK, which you must authorize with credentials from your Firebase project.
- Go to the Firebase Console.
- Select your project.
- Navigate to "Project settings" > "Service accounts".
- Click "Generate new private key" and save the JSON file.
Encryption Setup
TODO: describe sparse image strategy
pip install firebase-admin
Python Setup
TODO: give environment requirements for python script
Run the script in scripts/retrieve_data.py
python retrieve_data.py \
--cred "~/Desktop/myproject-firebase-adminsdk.json" \
--out "~/Desktop/dataout" \
--collection 'exptData' 'sharedData'
--cred
the path to the private key you downloaded from Firebase.--out
the path to a directory where the files will be saved (the directory will be created; the path cannot exist yet)--collection
the collections to download (during development, these areexptData-dbug
andsharedData-dbug
)