This repo contains the code for the MeowWolf MWAPP.
- Included tools
- Requirements
- Getting started
- Contributing guide
- Pre-defined scripts
- Wormhole API Controller
- CI/CD
This repo contains some preconfigured development tools:
- Typescript: A superset of Javascript that adds an additional layer of Typings, bringing more security and efficiency to the written code.
- Prettier: Code formatting that assures consistency across all Finsweet's projects.
- ESLint: Code linting that enforces industries' best practices. It uses our own custom configuration to maintain consistency across all Finsweet's projects.
- Playwright: Fast and reliable end-to-end testing.
- esbuild: Javascript bundler that compiles, bundles and minifies the original Typescript files.
- Changesets: A way to manage your versioning and changelogs.
- Finsweet's TypeScript Utils: Some utilities to help you in your Webflow development.
This repo requires the use of pnpm. You can install pnpm with:
npm i -g pnpm
To enable automatic deployments to npm, please read the Continuous Deployment section.
In your terminal and install the packages by running:
pnpm install
It is also recommended that you install the following extensions in your VSCode editor:
To build the files, you have two defined scripts:
-
pnpm dev
: Builds and creates a local server that serves all files (check Serving files on development mode for more info). -
pnpm build
: Builds to the production directory (dist
).
When you run pnpm dev
, two things happen:
- esbuild is set to
watch
mode. Every time that you save your files, the project will be rebuilt. - A local server is created under
http://localhost:3000
that serves all your project files. You can import them in your Webflow projects like:
<script defer src="http://localhost:3000/{FILE_PATH}.js"></script>
- Live Reloading is enabled by default, meaning that every time you save a change in your files, the website you're working on will reload automatically. You can disable it in
/bin/build.js
.
You can review all the build files by updating the build settings.
In bin/build.js
, update the ENTRY_POINTS
array with any files you'd like to build:
const ENTRY_POINTS = [
'src/home/index.ts',
'src/contact/whatever.ts',
'src/hooyah.ts',
'src/home/other.ts',
];
This will tell esbuild
to build all those files and output them in the dist
folder for production and in http://localhost:3000
for development.
CSS files are also supported by the bundler. When including a CSS file as an entry point, the compiler will generate a minified version in your output folder.
You can define a CSS entry point by either:
- Manually defining it in the
bin/build.js
config. See previous section for reference. - Or importing the file inside any of your JavaScript / TypeScript files:
// src/index.ts
import './index.css';
CSS outputs are also available in localhost
during development mode.
Path aliases are very helpful to avoid code like:
import example from '../../../../utils/example';
Instead, we can create path aliases that map to a specific folder, so the code becomes cleaner like:
import example from '$utils/example';
You can set up path aliases using the paths
setting in tsconfig.json
. This template has an already predefined path as an example:
{
"paths": {
"$utils/*": ["src/utils/*"]
}
}
To avoid any surprises, take some time to familiarize yourself with the tsconfig enabled flags.
In general, your development workflow should look like this:
- Create a new branch where to develop a new feature or bug fix.
- Once you've finished the implementation, create a Changeset (or multiple) explaining the changes that you've made in the codebase.
- Open a Pull Request and wait until the CI workflows finish. If something fails, please try to fix it before merging the PR. If you don't want to wait for the CI workflows to run on GitHub to know if something fails, it will be always faster to run them in your machine before opening a PR.
- Merge the Pull Request. The Changesets bot will automatically open a new PR with updates to the
CHANGELOG.md
, you should also merge that one. If you have automatic npm deployments enabled, Changesets will also publish this new version on npm.
If you need to work on several features before publishing a new version on npm, it is a good practise to create a development
branch where to merge all the PR's before pushing your code to master.
This template contains a set of predefined scripts in the package.json
file:
-
pnpm dev
: Builds and creates a local server that serves all files (check Serving files on development mode for more info). -
pnpm build
: Builds to the production directory (dist
). -
pnpm lint
: Scans the codebase with ESLint and Prettier to see if there are any errors. -
pnpm lint:fix
: Fixes all auto-fixable issues in ESLint. -
pnpm check
: Checks for TypeScript errors in the codebase. -
pnpm format
: Formats all the files in the codebase using Prettier. You probably won't need this script if you have automatic formatting on save active in your editor. -
pnpm test
: Will run all the tests that are located in the/tests
folder. -
pnpm test:headed
: Will run all the tests that are located in the/tests
folder visually in headed browsers. -
pnpm release
: This command is defined for Changesets. You don't have to interact with it. -
pnpm run update
: Scans the dependencies of the project and provides an interactive UI to select the ones that you want to update.
The authentication process is handled by the MWAuth
class. This class is responsible for handling the authentication process and storing the user's session in the browser's local storage. Its a convenient way to handle the Auth0 process, this class actually wraps the auth0-spa-js
library and provide some helpers on top of it.
An instance can also be retrieved from the 'window' global object, allowing you to access it from anywhere in the Webflow side. For example, you could check if the user is authenticated by doing:
const { mwAuth } = window;
if (mwAuth.isAuthenticated) {
// Do something
}
Below you'll find instructions on how to interact with the Wormhole API's using the APIController
with some examples and utilities.
The APIController
is a class used to interact with a GraphQL API. It uses an asynchronous approach and is designed to handle the complexity of authorization, query initialization, and error management.
import APIController from 'path/to/APIController';
let apiController = new APIController('https://your-api-url.com');
The APIController takes a single argument during instantiation, which is the URL of your GraphQL API.
const someData = await apiController.execute(queryName, variables);
The execute method is used to run a predefined GraphQL query. The method expects two parameters:
-
queryName
: A string representing the name of the query to execute. The query must have been predefined and stored in the APIController instance. -
variables
: An optional object containing variables to be passed into the GraphQL query. The execute method will return a Promise resolving to the data from the executed query.
const customQuery = gql`
query Person {
Person {
id
givenName
affiliationId
affiliation {
id
}
}
}
`;
const someData = await apiController.query(customQuery);
The query
method is used to run a GraphQL query that is not predefined. The method expects two parameters:
-
queryDocument
: A DocumentNode representing the GraphQL query to be executed. This is typically a parsed GraphQL string. For convenience, you can use thegql
function fromgraphql-tag
to parse a GraphQL string. -
variables
: An optional object containing variables to be passed into the GraphQL query. The query method will return a Promise that resolves to the data returned from the GraphQL query.
This section demonstrates how to use the utility functions to build a complex GraphQL query variable.
First, import the necessary utility functions:
// Step 1: Import the utility functions
// import { buildWhereClause, buildOrderByClause, buildLimitClause } from './utils';
// Step 2: Define the inputs
const nestStructure = ['id'];
const operation = 'equals';
const value = 1;
const field = 'name';
const direction = 'asc';
const limit = 10;
// Step 3: Use the utility functions to build the required variable object
const whereClause = buildWhereClause(nestStructure, operation, value);
const orderByClause = buildOrderByClause(field, direction);
const limitClause = buildLimitClause(limit);
/* The above would result in:
{
where: { id: { _eq: 1 } },
order_by: { name: 'asc' },
limit: 10
}
*/
// Step 4: Combine the clauses
const queryArguments = {
...whereClause,
...orderByClause,
...limitClause,
};
The above variable object can then be passed into the execute
or query
methods as the variables
parameter.
In case of GraphQL errors, both execute and query methods will throw an Error. Make sure to handle this properly. An example would be:
try {
const data = await apiController.execute('getUser', { userId: 1 });
console.log(data);
} catch (error) {
console.error('An error occurred', error);
}
This class uses an access token stored in session storage for authorizing requests. It is assumed that the session storage contains an item with a key as defined in ACCESS_TOKEN from '$auth/utils/constants'
. The token is then added to each request as a Bearer token in the Authorization header.
On instantiation, the APIController class initializes its predefined queries. The method used for this is inizializeQueries
, which loads queries using loadQueries function. Any error during this process would prevent the instance from being created.
New queries need to be added to the queries
object in the initializeQueries
method. The queries
object is a key-value pair where the key is the name of the query and the value is the query itself. The query must be a DocumentNode, which can be created using the gql
function from graphql-tag
.
This repo contains a set of helpers with proper CI/CD workflows. TODO: Add deployment instructions for vendor of choise: Cloudflare Workers?
When you open a Pull Request, a Continuous Integration workflow will run to:
- Lint & check your code. It uses the
pnpm lint
andpnpm check
commands under the hood. - Run the automated tests. It uses the
pnpm test
command under the hood.
If any of these jobs fail, you will get a warning in your Pull Request and should try to fix your code accordingly.
Changesets allows us to generate automatic changelog updates when merging a Pull Request to the master
branch.
Before starting, make sure to enable full compatibility with Changesets in the repository.
To generate a new changelog, run:
pnpm changeset
You'll be prompted with a few questions to complete the changelog.
Once the Pull Request is merged into master
, a new Pull Request will automatically be opened by a changesets bot that bumps the package version and updates the CHANGELOG.md
file.
You'll have to manually merge this new PR to complete the workflow.
If an NPM_TOKEN
secret is included in the repository secrets, Changesets will automatically deploy the new package version to npm.
See how to automatically deploy updates to npm for more info.
Some repositories may not have the required permissions to let Changesets interact with the repository.
To enable full compatibility with Changesets, go to the repository settings (Settings > Actions > General > Workflow Permissions
) and define:
- ✅ Read and write permissions.
- ✅ Allow GitHub Actions to create and approve pull requests.
Enabling this setting for your organization account (Account Settings > Actions > General
) could help streamline the process. By doing so, any new repos created under the org will automatically inherit the setting, which can save your teammates time and effort. This can only be applied to organization accounts at the time.
As mentioned before, Changesets will automatically deploy the new package version to npm if an NPM_TOKEN
secret is provided.
This npm token should be:
- From Finsweet's npm organization if this repository is meant for internal/product development.
- From a client's npm organization if this repository is meant for client development. In this case, you should ask the client to create an npm account and provide you the credentials (or the npm token, if they know how to get it).
Once you're logged into the npm account, you can get an access token by following this guide.
The access token must be then placed in a repository secret named NPM_TOKEN
.
- On the
master
branch - Run
pnpm changeset
to create a new changeset. - Push the changeset to the repository.
- GitHub actions will create a new PR with the changeset.
- Approve and merge the PR.
- Make sure you have the
NPM_TOKEN
secret set up in your repository. - Run
pnpm build
to build the project. - Run
pnpm changeset pre enter next
to create a new changeset. - Run
pnpm changeset version
to bump the package version. - Run
git add .
- Run
git commit -m "enter pre-release and bumped version"
- Run
pnpm changeset publish
- Make sure you have the
NPM_TOKEN
secret set up in your repository. - Run
pnpm build
to build the project. - Run
pnpm changeset
to create a new changeset. - Run
pnpm changeset version --snapshot
to bump the package version. - OR Run
pnpm changeset version --snapshot <custom_name>
to add a custom snapshot name. - Run
git add .
- Run
git commit -m "created snapshot version"
- Run
pnpm changeset publish --no-git-tag --snapshot
to publish the new version to npm.
- Make sure you have the
NPM_TOKEN
secret set up in your repository. - Run
pnpm build
to build the project. - Run
pnpm changeset
to create a new changeset. - Run
pnpm changeset version
to bump the package version. - Run
git add .
- Run
git commit -m "bumped version"
- Run
pnpm changeset publish
to publish the new version to npm.
- [x] how to deploy on cloudflare workers
- [ ] remove finsweet dependencies
- [x] workflow actions
- eslint
- [ ] membership info (Later)
- infer membership type
- show membership banner on home
- show membership page on settings
- build membership page in settings
- [x] calendar title and links / using new npm tool
- [ ] consolidate events and visits (Later)
- [ ] home-ui_grid (membership)
- [ ] hide view all links until page is loaded
- [x] on home only show view all if more than 3 upcoming events
- [x] verify calendar fn
- [x] faster page loading with component loading state and show content when available
- [x] filter missing location and order data
- [x] header account loading (name and avatar)
- [x] settings account avatar loading
- [x] verify stuff going into sentry
- [x] ui.ts add sentry release version
- [x] remove /account directory
Calendar date formatting is cleaned up:
- both apple and google open a google calendar
- event title sucks