@aller/svelte-components
TypeScript icon, indicating that this package has built-in type declarations

1.5.16 • Public • Published

Svelte components

This repository contains svelte components that can be used in other projects. You should consider creating a svelte components if you need a component that requires a lot of clientside logic. If you want to create a component that can be ESI'd in without a lot of clientside logic, take a look at the esi-components

Run

  1. yarn

  2. yarn dev:myComponentName to run a single component (faster) or yarn dev to run all components

  3. localhost:3000/render/example

Making changes

This project serves content both through a NPM package and an express server. For your changes to be available everywhere, follow these steps

  1. Make your changes or create your component and create a pull request. (make sure to follow the required structure, run yarn new-component -- yourcomponentname to setup a new component with the correct structure)
  2. Bump the version in package.json (optional, only needed if you are going to release the npm package)
  3. If you want to use the component through the NPM package, add your component to the buildPackage component variable in the webpack.config.js file.
  4. Merge to master.
  5. Clear cache for your component, the cache channel will be svelte-components-yourcomponentname or just svelte-components for all of them. You can do this through the admin panel in janitr (https://github.com/dbmedialab/janitr)

If your component is not used through the NPM package, you do not need to do anything else. If it's used through the package and you need to release a new version, follow these additional steps to release it:

  1. git pull from master locally and make sure you do not have any local changes.
  2. Run yarn build-package
  3. Run npm publish --access public to publish your changes to NPM. (Make sure you are logged in with npm login and that your NPM user is a part of the Aller org)
  4. Update the @aller/svelte-components version in the repositories that uses it (wolverine-frontend-xavier).

Structure

There is a specific structure and naming convention that needs to be followed for this to work properly right now. Run yarn new-component -- yourcomponentname to automatically setup the correct structure for your new component.

Each svelte component needs to be placed within it's own folder in /src. The component's folder should contain a entry.svelte and entry.js. The entry.js file needs to import your svelte component, and export the return value from generateComponentEntry. The entry.svelte file should be the root of your svelte component. You can also add the optional getInitialProps.js file to run code on the server before the component is rendered. All other files should be within subfolders, and there is no specific structure that is required here.

Example structure:

src
|
└───mynewcomponent
│   │   entry.js
│   │   entry.svelte
│   │   getInitialProps.js
│   │
│   └───components
│       │   Button.svelte
│       │   List.svelte
│       │   ...
│   └───stores
│       │   store.js
│       │   ...

Styles

This project uses svelte-preprocess-cssmodules. Make sure you add module="mixed" to the style tags in all of your svelte files, like so: <style module="mixed">. This prevents conflicts with the styles on the page your app is included on, and also allows for the uniqueID/uniqueMode functionality where you can have multiple instances of your app running on the same page.

If you define any global css variables, consider their names as they might conflict with variables set on the page your component is used on.

Server side code / getInitialProps

In an attempt to give the components some similar functionality to what getInitialProps in Next.js offers, I've added the ability to create a getInitialProps.js file in the component root folder. The contents of this file will be transpiled and run on the server, and whatever is returned by the default export (has to be an object) will be inserted as props in the component both on the server and client. The main usecase of this is the ability to make requests on the server, and have the data returned be available as props when the component is rendered.

Importing NPM packages inside of getInitialProps can cause issues, avoid it if possible and make sure everything still works both locally and on stage if you really need to use any. Currenly getInitialProps recieves both fetch and req as arguments from the server. If you need something more, consider if you can add it in the server folder and send it as arguments to all getInitialProps functions.

How to use

The simplest way of using these components is to ESI them in. <esi:include src="/app/svelte-components/render/example"></esi:include>. This will return server side rendered markup with script tags that fetches the clientside bundle. There's two optional query parameteres you can use

Available query params
parameter description
&ssrModernClientBundle=true Will insert the modern client bundle's code on the server instead of downloading it clientside. For modern browsers the component will be interactive almost immediately, but legacy browsers will now downlaod both the modern and legacy bundles. Consider the size of the client bundle before using this.
&uniqueID=my-unique-id If you need to ESI in multiple instances of the same component on a page, you can send in a unique ID for each of them. This is needed to avoid targeting issues for the clientside scripts. This works by server side rendering all scripts and inserting the unique ID before classnames. Note that this forces all browsers to download the legacy clientside bundle, which is a fairly large increase in bundle size. Each component with a unique ID will be cached separately, so avoid too many different ones.
&props=myPropName=myValue You can send values into your component through the ESI request. Any key-value pair inside of the props param will be sent into the component both on the sever and client. You can comma separate multiple pairs if you need to send in multiple different props. The param &props=myPropName=myValue can be accessed in your components entry.svelte file with export let myPropName. The variable myPropName will be the string myValue in this case.
&cachetime=600 Sets the max-age for your component. cachetime=600 will give it a max-age of 10 minutes. The component will default to a max-age of 1 year if you do not provide a custom max-age. This is useful if you are fetching data in getInitialProps and you need the component to be up to date.

Labrador contentbox

The svelteComponent contentbox in labrador can be used both on frontpages and articles. It will insert an ESI on the page. The contentbox will automatically put a svelte component into uniqueMode if it detects multiple of the same component on the page.

The content box is populated by the componentlist_v2.json config in the root of this project. If you want your component to be available in the content box for a specific site, add it within that site's config object. You can also add it to shared, which will make it available for all sites. The key will be the name displayed in the dropdown in the contentbox, the value is the ESI path used. This also means that you can add predefined parameters to the paths.

Tracing through jaeger

For tracing we use the boiler-plate npm-package @aller/express-opentracing. Inside each component, to use tracing through fetches, and trace separate operations to see whether an operation takes long, this should be done on the following form;

async function getInitialProps({ useMock, span, fetcher, tracer }: IGetInitialProps) {
  [...]

  const componentTracing = createComponentSpan(span, COMPONENT_NAME, tracer);

  [...]

  const { body } = await fetcher(fetchURL, componentTracing);

  log({
    event: 'get_initial_props_COMPONENT_NAME_HERE',
    span: componentTracing.span,
    phase: componentTracing.phase,
  });
  componentTracing.span.finish();
  return DATAYOURETURN
}

If you need a lot more tracing, you can add more log({...}) events throughout your getInitialProps.

NPM package

Some projects, like wolverine-frontend-xavier, can not use ESIs. In those cases, we can add the @aller/svelte-components package which exposes two functions for server side rendering the svelte components. The package includes both the renderComponent and renderComponentAsync functions. Note that renderComponentAsync will not work inside of a React component that is rendered on the server because it returns a promise.

Here is an example of how this can be used server side with express
import { renderComponentAsync } from '@aller/svelte-components';

const componentMarkup = await renderComponentAsync({
  name: 'example',
  ssrModernClientBundle: true,
});

res.send(componentMarkup);
Server side with React
import renderComponent from '@aller/svelte-components';

const componentMarkup = renderComponent({ name: 'example' });

return <div dangerouslySetInnerHTML={{ __html: componentMarkup }} />;
Options for package
parameter type description sync async
name String Name of the component you want to render ✔️ ✔️
bundlePath String Path to the bundle, leave as default if import from package. Useful for local devlopment, as you can target localhost this way ✔️ ✔️
ssrModernClientBundle Boolean Will insert the modern client bundle's code on the server instead of downloading it clientside. For modern browsers the component will be interactive almost immediately, but legacy browsers will now downlaod both the modern and legacy bundles. Consider the size of the client bundle before using this. Only works in node, will not work in Next.JS. ✔️
uniqueMode Boolean Allows for multiple instances of the same svelte component on the same page. This works by server side rendering all scripts and inserting a unique hash before classnames. Note that this forces all browsers to download the legacy clientside bundle ✔️

How it works

This works by having webpack create three different bundles. One for the server, one for modern browsers and one for legacy browsers.

  • The server bundle is used to server side render the svelte component. The bundle is created by targeting the entry.svelte file directly (src/yourcomponent/entry.svelte)

  • The modern and legacy bundle are used clientside. These bundles are created by targeting each components entry.js file (src/yourcomponent/entry.js). The legacy bundle is transpiled and polyfilled to support IE11

The server will respond with styled markup and a script tag that will download the bundle. Once the bundle is downloaded, the component will hydrate and become interactive

IE11 Support

We create two differende bundles for the client because we would like to avoid serving a heavily transpiled and polyfilled bundle to browsers that does not need it. We include the modern bundle with a script type="module" tag and the legacy bundle with a script nomodule tag. Modern browsers that support ES2015+ will download the modern bundle and ignore the legacy bundle. Legacy browsers that does not support type="module" will download and execute the legacy bundle. Some old browsers might download the modern bundle as well, but not execute it. More information about this approach

Todo list for the project

  • [ ] Move client side chunk generated for each version into a bucket, having them in git is kinda messy. Similar solution to view-res should work.
  • [ ] Proper way of polyfilling. Fetch needs to be polyfilled for each component right now
  • [ ] Proper typescript support for the entire project, not just the server
  • [ ] Set up tracing and metrics for the server
  • [ ] Theming

Readme

Keywords

none

Package Sidebar

Install

npm i @aller/svelte-components

Weekly Downloads

42

Version

1.5.16

License

none

Unpacked Size

998 kB

Total Files

37

Last publish

Collaborators

  • aslak2800
  • roninjc
  • idanko
  • mariusjn
  • lobunto
  • konrad-j
  • oyvigri
  • goodleby
  • jimoe
  • vnorvik
  • kevinmidboe