{"id":17026485,"url":"https://github.com/daeh/jspsych-template","last_synced_at":"2025-04-23T14:41:42.205Z","repository":{"id":208355800,"uuid":"721432084","full_name":"daeh/jspsych-template","owner":"daeh","description":"jsPsych with Firestore (serverless) and ESLint flat config","archived":false,"fork":false,"pushed_at":"2024-11-19T20:56:48.000Z","size":5647,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-19T21:33:35.655Z","etag":null,"topics":["eslint","eslint-config","experiment","firestore","jspsych","prettier","psychology","serverless","template","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daeh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-21T03:35:00.000Z","updated_at":"2024-11-19T20:52:03.000Z","dependencies_parsed_at":"2024-03-29T04:30:51.680Z","dependency_job_id":"bac1d766-03ae-44e9-bb0f-5f160c825526","html_url":"https://github.com/daeh/jspsych-template","commit_stats":null,"previous_names":["daeh/jspsych-template"],"tags_count":6,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daeh%2Fjspsych-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daeh%2Fjspsych-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daeh%2Fjspsych-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daeh%2Fjspsych-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daeh","download_url":"https://codeload.github.com/daeh/jspsych-template/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227076358,"owners_count":17727457,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["eslint","eslint-config","experiment","firestore","jspsych","prettier","psychology","serverless","template","typescript"],"created_at":"2024-10-14T07:32:56.956Z","updated_at":"2025-04-23T14:41:42.197Z","avatar_url":"https://github.com/daeh.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# serverless jsPsych Template (jsPsych-Firebase-Firestore-Prolific-Vite)\n\nThis repository is an example of how to setup a development environment for building online psych experiments. Key aspects are:\n\n- TypeScript centric\n- Future-looking linting and formatting configuration using [ESLint](https://eslint.org) and [Prettier](https://prettier.io/)\n- [Vite](https://vitejs.dev/) bundler for robust cross-platform support\n- [jsPsych](https://www.jspsych.org/) for UX flow\n- [serverless](https://www.serverless.com/) data handling with [Firestore](https://firebase.google.com/docs/firestore)\n- [Firebase](https://firebase.google.com/) hosting\n- [Prolific](https://www.prolific.com/) integration\n\n## Installation\n\n- Fork this repo: `Use this template` \u003e `Create a new repository`\n- 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`\n- Enter the repo folder (replace with your repo info): e.g. `cd jspsych-template`\n\n### Install Node Dependencies\n\nInstall the dependencies using [Yarn](https://yarnpkg.com/)\n\n```shell\n###\n### From within the cloned repo folder\n###\n\n### Install the Firebase dependencies\nyarn install\n\n### Start the Vite server\nyarn dev\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003eIf you're new to Yarn\u003c/summary\u003e\n\nYarn is a package manager, like `npm`, for Node.\n\nhttps://yarnpkg.com/getting-started/install\n\nhttps://yarnpkg.com/corepack\n\n```shell\nnpm install -g corepack\ncorepack enable\nyarn set version stable\nyarn install\n```\n\n\u003c/details\u003e\n\n## Usage\n\nYou can format, lint and build the project from the command line by calling the commands in [`package.json`](package.json):\n\n\u003cdetails\u003e\n\n\u003csummary\u003epackage.json scripts\u003c/summary\u003e\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"vite --config vite.config.mts hosting\",\n    \"build\": \"vite build --config vite.config.mts hosting\",\n    \"lint\": \"ESLINT_USE_FLAT_CONFIG=true \u0026\u0026 prettier --config prettier.config.mjs --write . \u0026\u0026 eslint --config eslint.config.mjs --fix . \u0026\u0026 tsc --project tsconfig.json --noEmit\"\n  }\n}\n```\n\n\u003c/details\u003e\n\nTo develop the website, run `yarn dev`, which will open a localhost Vite server that will update as you make modifications.\n\n### Sandbox\n\nYou 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`](hosting/src/appConfig.ts).\n\n- Setting `const debuggingMode = true` will increase the verbosity of the console output.\n\n  - Alternatively, you can force debugging mode by including `debug` as a URL Parameter, e.g. `https://mysite.web.app/?debug=true`\n\n- 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.\n\n  - Similarly, you can modulate the backend with a URL Parameter, e.g. `https://mysite.web.app/?debug=false\u0026mock=true`\n\n# Configuration\n\n## Components\n\nFor developing the website, this project uses\n\n- [ESLint](https://eslint.org/) (configured in [`eslint.config.mjs`](eslint.config.mjs))\n- [TypeScript](https://www.typescriptlang.org/) (configured in [`tsconfig.*.json`](tsconfig.base.json))\n- [Prettier](https://prettier.io/) (configured in [`prettier.config.mjs`](prettier.config.mjs))\n\nThe ESLint config integrates these configurations.\n\nFor bundling the website, this project uses\n\n- [Vite](https://vitejs.dev/) (configured in [`vite.config.mts`](vite.config.mts))\n- [Tailwind CSS](https://tailwindcss.com/)\n- [PostCSS](https://postcss.org/) (configured in [`vite.config.mts`](vite.config.mts); uses [Autoprefixer](https://github.com/postcss/autoprefixer))\n- [Browserslist](https://github.com/browserslist/browserslist) (via the [browserslist-to-esbuild plugin](https://github.com/marcofugaro/browserslist-to-esbuild); configured in [`package.json`](package.json))\n- [jsPsych](https://www.jspsych.org/) - UX (experiment flow, data capture)\n\nFor serving the website, this project uses\n\n- [Firebase](https://firebase.google.com/) - hosting (configured by files in the project root)\n- [Firestore](https://firebase.google.com/docs/firestore) - [serverless](https://www.serverless.com/) data storage/access (configured by files in the project root)\n- [Prolific](https://www.prolific.com/) - recruitment\n\n## ESLint\n\nThis project uses a future-looking configuration that implements the major developments from [ESLint](https://eslint.org). The main config file in this repo is the flat ESLint config, [`eslint.config.mjs`](eslint.config.mjs).\n\n\u003cdetails\u003e\n\n\u003csummary\u003eES Module parsing\u003c/summary\u003e\n\nThis 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.\n\n\u003c!-- While ESLint has no issue using the `mjs` config file, at present, IDEs like VS Code and IntelliJ IDEA require the `js` extension. A simple workaround is to make an alias `eslint.config.js` that points to `eslint.config.mjs`. This is done automatically during installation by the [`package.json`](package.json) file. --\u003e\n\n\u003c/details\u003e\n\n### Flat Config System\n\nBeginning in ESLint `v9.0.0`, the default will be the new [flat config system](https://eslint.org/docs/latest/use/configure/configuration-files-new). 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`).\n\n### Stylistic Plugin\n\nESLint is [deprecating formatting rules](https://eslint.org/blog/2023/10/deprecating-formatting-rules/), passing over maintenance and development to the community-run plugin [ESLint Stylistic](https://eslint.style/).\n\n## IDE\n\n### VS Code Settings\n\nFor [VS Code](https://code.visualstudio.com/) to respect the configuration, you need to specify the formatter for the relevant files. This is done for you in [`VSCodeProject.code-workspace`](VSCodeProject.code-workspace) and in [`.vscode/settings.json`](.vscode/settings.json) (these are redundant, you only need one). This configures the [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension to use the flat config system, makes VS Code use the [Prettier - Code Formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) extensions for formatting filetypes not covered by ESLint, and enables [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss). This obviously requires that you have these extensions enabled for the workspace. Activate the `VSCodeProject.code-workspace` via `File \u003e Open Workspace from File...` (or by double-clicking it), or activate `.vscode` via `File \u003e Open Folder...` in VS Code.\n\nThe relevant settings are:\n\n\u003cdetails\u003e\n\n\u003csummary\u003eVSCode Settings\u003c/summary\u003e\n\n```json\n{\n  \"editor.formatOnSave\": true,\n  \"eslint.useFlatConfig\": true,\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll.eslint\": \"explicit\"\n  },\n  \"files.associations\": {\n    \"*.css\": \"tailwindcss\"\n  },\n  \"[javascript][javascriptreact][typescript]\": {\n    \"editor.defaultFormatter\": \"dbaeumer.vscode-eslint\"\n  },\n  \"[html]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[json][jsonc]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[css][scss][less]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  }\n}\n```\n\n\u003c/details\u003e\n\n### IntelliJ IDEA Settings\n\nFor [IntelliJ IDEA](https://www.jetbrains.com/idea/) / [WebStorm](https://www.jetbrains.com/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:\n\n\u003cdetails\u003e\n\n\u003csummary\u003eIntelliJ Setup\u003c/summary\u003e\n\n- `Settings... \u003e Languages \u0026 Frameworks \u003e JavaScript \u003e Code Quality Tools \u003e ESLint`\n  - Enable `Automatic ESLint configuration`\n  - Enable `Run eslint --fix on save`\n  - Add the additional filetypes to the `Run for files` field:\n    - `{**/*,*}.{ts,mts,cts,tsx,mtsx,js,mjs,cjs,jsx,mjsx,html,vue}`\n- `Settings... \u003e Languages \u0026 Frameworks \u003e JavaScript \u003e Prettier`\n  - Enable `Automatic Prettier configuration`\n  - Enable `Run on save`\n  - Add the additional filetypes to the `Run for files` field:\n    - `{**/*,*}.{ts,mts,cts,tsx,mtsx,js,mjs,cjs,jsx,mjsx,json,html,css,scss,vue,astro}`\n\nIf 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](https://youtrack.jetbrains.com/issue/WEB-61117/ESLint-flat-config-doesnt-work-with-non-default-custom-path-to-the-config-file#focus=Comments-27-8196242.0-0):\n\n- `Settings... \u003e Languages \u0026 Frameworks \u003e JavaScript \u003e Code Quality Tools \u003e ESLint`\n  - Switch to `Manual ESLint configuration`\n  - Add this string to the `Extra ESLint options` field:\n    ```shell\n    ESLINT_USE_FLAT_CONFIG=true yarn eslint --config eslint.config.mjs\n    ```\n\n\u003c/details\u003e\n\n# Integrations\n\n## Hosting and Database\n\n### Firebase and Firestore Configuration\n\n\u003cdetails\u003e\n\n\u003csummary\u003eFirebase Setup\u003c/summary\u003e\n\nTODO: describe how to setup hosting and database\n\nHere's a [useful guide](https://firebase.google.com/docs/hosting/quickstart)\n\nAfter you setup your Firebase and Firestore accounts and services, add your configurations to\n\n- [`.firebaserc`](.firebaserc)\n- [`creds.ts`](hosting/src/creds.ts)\n\n### Firestore\n\nPush the Firestore rules (defined in firestore.rules)\n\n```sh\nyarn deploy-rules\n```\n\nOnce you have the rules deployed, you can switch from using the mock database (a simple database emulator) to Firestore.\n\nSet `const simulateMockDatabase = false` in [`appConfig.ts`](hosting/src/appConfig.ts). This will cause the application to use [`creds.ts`](hosting/src/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.\n\n### Firebase\n\nWhen you're ready to deploy the website, push it to Firebase\n\n```sh\nyarn deploy\n```\n\n\u003c/details\u003e\n\n## Data Collection\n\n### Prolific Configuration\n\n\u003cdetails\u003e\n\u003csummary\u003eProlific URL Search Params\u003c/summary\u003e\n\n### Prolific URL Search Params\n\nThe project is looks for Prolific URL parameters and stores them. Make sure that you've set up Prolific to use URL Search Params.\n\n![Prolific_param](assets-readme/Prolific_param.png)\n\n### Prolific Completion Code\n\nIn order to register that Prolific users have completed the experiment, add the study's **Completion Code** to `const prolificCompletionCode = ...` in [`creds.ts`](hosting/src/creds.ts).\n\n\u003c/details\u003e\n\n## Deploying an experiment\n\nThe script [`scripts/releaseScript.mjs`](scripts/releaseScript.mjs) automates deployment of the experiments. You can run it from the root directory with:\n\n```shell\nyarn release\n```\n\nThe script will walk you through committing your changes to the git repo [that you forked](#installation).\n\nA key idea here is that there should never be ambiguity about what code was served to a participant.\n\nThe `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.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eDeployment\u003c/summary\u003e\n\nTODO: elaborate\n\n\u003c/details\u003e\n\n## Retrieving Data\n\nRetrieve the data from Firestore using the Firebase Admin SDK, which you must authorize with credentials from your Firebase project.\n\n### Generate credentials to access the firestore database\n\n1. Go to the Firebase Console.\n2. Select your project.\n3. Navigate to \"Project settings\" \u003e \"Service accounts\".\n4. Click \"Generate new private key\" and save the JSON file.\n\n### Save credential in an encrypted disk image (NOT IN YOUR GIT REPO)\n\n\u003cdetails\u003e\n\n\u003csummary\u003eEncryption Setup\u003c/summary\u003e\n\nTODO: describe sparse image strategy\n\n\u003c/details\u003e\n\n### Install Firebase Admin SDK\n\n```shell\npip install firebase-admin\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003ePython Setup\u003c/summary\u003e\n\nTODO: give environment requirements for python script\n\n\u003c/details\u003e\n\n### Download the data\n\nRun the script in [`scripts/retrieve_data.py`](scripts/retrieve_data.py)\n\n```shell\npython retrieve_data.py \\\n    --cred \"~/Desktop/myproject-firebase-adminsdk.json\" \\\n    --out \"~/Desktop/dataout\" \\\n    --collection 'exptData' 'sharedData'\n```\n\n- `--cred` the path to the private key you downloaded from Firebase.\n- `--out` the path to a directory where the files will be saved (the directory will be created; the path cannot exist yet)\n- `--collection` the collections to download (during development, these are `exptData-dbug` and `sharedData-dbug`)\n\n---\n\n## Author\n\n[![Personal Website](https://img.shields.io/badge/personal%20website-daeh.info-orange?style=for-the-badge)](https://daeh.info) [![BlueSky](https://img.shields.io/badge/bsky-@dae.bsky.social-skyblue?style=for-the-badge\u0026logo=bluesky)](https://bsky.app/profile/dae.bsky.social) [![Twitter](https://img.shields.io/badge/twitter-@DaeHoulihan-white?style=for-the-badge\u0026logo=x)](https://x.com/DaeHoulihan)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaeh%2Fjspsych-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaeh%2Fjspsych-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaeh%2Fjspsych-template/lists"}