@sharechest/connector

(Formerly known as the Launcher)

Usage no npm install needed!

<script type="module">
  import sharechestConnector from 'https://cdn.skypack.dev/@sharechest/connector';
</script>

README

Sharechest Connector.

(Formerly known as the Launcher)

The connector is used to walk a prospective investor through the process of providing contact information to the issuer. It communicates with the core backend to pull details for a company and its promoted activity, and to submit the contact information for the prospective investor ("prospect", in backend model terminology)

Links

[npm package] [unpkg url] [connector tickets]

Technologies

Get started

Install the dependencies...

cd svelte-app
npm install

...then start Rollup:

npm run dev

Navigate to localhost:5000. You should see the app running. Edit a component file in src, save it, and reload the page to see your changes.

By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the sirv commands in package.json to include the option --host 0.0.0.0.

Building and running in production mode

To create an optimised version of the app:

npm run build

You can run the newly built app with npm run start. This uses sirv, which is included in your package.json's dependencies so that the app will work when you deploy to platforms like Heroku.

Single-page app mode

By default, sirv will only respond to requests that match files in public. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.

If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for any path. You can make it so by editing the "start" command in package.json:

"start": "sirv public --single"

Directory Structure

  • src/
    • api/ Modules for communicating with backend and external APIs.
    • components/ Re-usable UI Components.
    • errors/ Enums and modules for error messages, error handling, etc.
    • globalStores/ Stores that are global to the entire connector flow.
      • company/ Holds details for the company that the connector is referencing. This ID comes from the embed code.
    • node_modules/ Strange to see here, but this is a local node*modules that is excluded from the gitignore. It contains our forked version of svelte-simple-modal. We needed to make some styling and behaviour changes to the pacakge, so we dropped it straight into the project. VSCode complains, but we can and do import our modal from this local node_modules folder like normal, using an absolute path.
    • util/ Utilities. The index file in here acts as the "public" API for our global utils, re-exporting the things that should be accessible.
    • views/
      • Connector views. Basically the equivalent of "pages" for a regular website. This also contains the main router component used for moving between views.
    • App.js Main Svelte file entry point, which renders the router. When this mounts, it requests the company information from the backend.
    • index.svelte Renders the button to show/hide the connector on the client website. When it's clicked, it'll open the modal.
    • main.js Exposes the Svelte app constructor to the window object so our script tags can use it (see Client Integration section below).
    • polyfill.js Imports all the polyfills we need for IE support.

Legacy Package

We need at least basic support for IE11, but Svelte doesn't support IE11 out of the box. To get around this, we build a version of the app that has all the required features polyfilled. It only gets built in production mode, so IE will not work when running dev mode (npm run dev). Testing IE can be done by running npm run ie which will build and locally serve a production instance.


Note: Additional step for "npm run ie"

When building for prod but intending to run locally, we have to make a small change to our rollup config so the connector still hits a localhost backend endpoint.

  • Open rollup.config.js
  • Find the API_URL definition
  • Make the following change:
-  const API_URL = production
+  const API_URL = !production
    ? "https://sharechest.gigalixirapp.com/api"
    : "http://localhost:4000/api";

Remember to discard this change before pushing to a branch!


During a production mode build, rollup will compile both the polyfill file (output -> public/bundles/polyfill.js) and the rest of the app (output -> public/bundles/bundle.js). After rollup is finished, the next part of the npm build command will run Babel on all JS files in the public/bundles directory, and output them as [filename].legacy.js. Doing so will ensure we generate a public/bundles/bundle.legacy.js and public/bundles/polyfill.legacy.js file for IE support. Babel does the final transformation of all app code and polyfill packages to ES2015.

When we load these scripts on the client website, we use the type="module" and nomodule HTML attributes to ensure that the legacy script (nomodule) doesn't get downloaded on modern browsers, and the modern script (type="module") never gets executed on legacy browsers.

Testing

No testing yet, since the scope is so tiny. The plan is to use Jest and @testing-library/svelte for "unit testing" (fast tests), and Cypress for "e2e testing" (slow tests).

Deployment

Github Actions CI in the Connector repo will run semantic-release after pull requests to the main branch. This will:

  1. Build the latest code
  2. Create a git tag for the new version number
  3. Zip the latest code, and publish to GitHub as a release package, tagged with the new version number
  4. Publish the latest code as an npm package under the name @sharechest/connector, tagged with the new version number

Once the npm package is published, we rely on unpkg (a mirror for everything on npm) as our CDN. It's run through Cloudflare's edge network so has great performance from pretty much anywhere in the world, and it requires no maintenance or work from our end. Plus it's free! Our new code is usually available in the unpkg cache within 5-10 minutes following a deployment.

As shown in the client integration section, we point directly to our CSS and script files on unpkg.

Notes

CSS/Styling

Since we're placing the connector on client websites which all have unique CSS rules, there can be styling conflicts which make our elements look or behave wrong. Below are some steps we've taken to avoid this, though new problems occasionally show up, as every website is different.

Style Resets

Our global css file gets included on a client's webpage. We use it to target our mount point div (widget-stake-trunk as of writing) and apply some overrides to it, in hopes that they'll be accepted by the webpage as more specific to our elements than page's global styles. Notable inclusions are resets of margins & padding, font sizes, and line heights.

Client Integration

When we need to add the connector to a client's website, we use HTML code containing some scripts and style tags referencing our CDN, and including the client's company ID from our backend. Here's an example of a production snippet for a client:

<div id="widget-stake-trunk" class="widget-stake-trunk" />
<link rel="stylesheet" href="https://www.unpkg.com/@sharechest/connector@1/public/bundles/bundle.css" />
<link rel="stylesheet" href="https://www.unpkg.com/@sharechest/connector@1/public/global.css" />
<script type="module" src="https://www.unpkg.com/@sharechest/connector@1/public/bundles/bundle.js" ></script>
<script nomodule src="https://www.unpkg.com/@sharechest/connector@1/public/bundles/polyfill.legacy.js" ></script>
<script nomodule src="https://www.unpkg.com/@sharechest/connector@1/public/bundles/bundle.legacy.js" ></script>
<script type="module">
  window.__sharechest_app(
    "widget-stake-trunk",
    "a5079158-7050-4513-91b7-94b90288006a"
  );
</script>
<script nomodule>
  window.__sharechest_app(
    "widget-stake-trunk",
    "a5079158-7050-4513-91b7-94b90288006a"
  );
</script>

The div is the mount point for the connector. We apply style overrides to this element in an attempt to avoid style overrides from the websites we're integrating into.

Following the div, we have some script tags referencing our npm package through unpkg. We reference the bundle JS file with the type="module" attribute for modern browsers, and reference the polyfill and legacy bundle JS files with the nomodule attribute for IE.

After that are the mounting script blocks, which call the window.__sharechest_app() function, passing the mount-point div ID and the company ID from our backend. The mount function here is exposed through the window object from the main.js file in the connector root.

FAQ

What is ENV.API_URL?

Anywhere we write the string ENV.API_URL, we're referencing the API URL for the current runtime environment (localhost in development, our production URL (https://api.sharechest.io) in production).

We use @rollup/plugin-replace to overwrite these strings at compile-time. Configuration for this can be found in rollup.config.js.