Preview Overview

The Preview Aspect enables component visualizations to be displayed by the browser, in an isolated Preview environment that is agnostic to the way visualizations are implemented (their framework, etc).

Component visualizations include the component compositions and documentation, but may also include other custom component previews. Preview provides other Aspects with an API to register their own component visualization. The content of these visualizations is retrieved from the component directories using a set of glob patterns defined by the Aspect (for example, the Docs Aspect uses the Preview Aspect to preview all *.docs.* files).

The preview environment or runtime, is integrated into Bit's UI using an iframe.

For example, the following screenshot shows a preview (a component's 'Overview') rendered in a preview environment and displayed in an iframe, inside the workspace UI:

preview screenshot

Preview runtime

The preview runtime is an isolated execution environment that is integrated into Bit's UI using an iframe. It is essentially, an application that renders component previews, using the technologies and configurations that are right for them (configured by the components' Envs).

Having an isolated preview environment or multiple isolated preview environments, means that your component previews can be implemented in any way, and will not be affected by anything other than themselves.

For example, you can implement and render component previews as Angular components, even though Bit's UI is implemented using React.

See here to learn more about the previewing process.

Previewing components in development

When running bit start the Preview aspect runs the dev servers that are needed to bundle and serve component visualizations.

The preview's dev servers are configured by default (via the DevServer Aspect), to use Hot Module Replacement (HMR), for quick page updates and persisted states.

Since component previews are bundled using the DevServer, the number of running dev servers is determined by the number of different DevServer configurations, set by the various Envs. For performance optimization, Envs that use identical DevServer configs will share a single dev server.

The example below shows the Preview's dev server running (along with the main UI server):

ENVIRONMENT NAME                    URL                                  STATUS
teambit.harmony/aspect on behalf    http://localhost:3300                RUNNING
of teambit.harmony/aspect,
company.scope/envs/my-react


You can now view 'my-workspace' components in the browser.
Bit server is running on http://localhost:3000
CopiedCopy

Building the component previews

The Preview Aspect registers its own Build Task to generate preview artifacts during build time. This build task uses the Bundler (and not the DevServer) to generate minimized assets, for optimized page loads.

$ bit build ui/text

✔ env "company.scope/envs/my-react", task "teambit.preview/preview:GeneratePreview" has completed successfully in 39s
✔ env "teambit.harmony/aspect", task "teambit.preview/preview:GeneratePreview" has completed successfully in 25s
CopiedCopy

Artifacts that are generated as part of the snap or tag pipelines, will be persisted in the component objects.

For example:

# run tag
$ bit tag ui/text

# list the component artifacts
$ bit artifacts ui/text
# ...
  teambit.preview/preview
    GeneratePreview
      public/asset-manifest.json
      public/index.html
      public/service-worker.js
      public/workbox-4979716c.js
      public/static/css/173.00004871.css
      public/static/css/main.e10f3a52.css
      public/static/js/173.67a47fd6.js
      public/static/js/173.67a47fd6.js.LICENSE.txt
      public/static/js/main.8f2d7ca0.js
      public/static/js/runtime-main.f8aedb2c.js
CopiedCopy

Previews for component versions are loaded to the preview runtime from the component objects.

The previewing process

Main runtime

  1. Collects all relevant files for a preview and registers them in a link file. The collected files include component files (using a specific glob pattern), templates, setup code, etc. To learn how to extend this step, see 'Register files for bundling'.

  2. Bundles the collected files to make them browser-ready. This process is done via the Bundler Aspect (for distributions) or the DevServer Aspect (in development). The specific implementations and configurations for the Bundler and DevServer services are set by the components' Envs.

  3. Registers its plugins to the express server (provided by the Express Aspect) to provide routing to the generated bundles.

Preview runtime

  1. Loads all link files (from step 1).

  2. Executes all *.preview.runtime.ts files, provided by the various Aspects in use (with the most prominent ones being the Envs).

  3. Retrieves and renders the relevant preview in the browser, according to the URL sent by the browser. To learn how to extend this step, see 'Register files for routing and rendering'.

diagram

A link file is a pseudo entry file that requires all modules, component files and a templates, of a specific preview. It is generated by the Preview Aspect and served to the bundler. The bundler, then, crawls over the link file's dependencies (the requireed modules) and processes them, as it would in a standard application.

Link files generated in development are placed in the node_modules/.cache/bit/teambit.preview/preview/{env-id}/ directory (inside the workspace).

Link files generated for distribution, as part of the build process, are placed in a capsule, under the {env-id}_{env-name}-preview directory (for example, ~/Library/Caches/Bit/capsules/06b995f03584ade4d8468fcf2d1e422dff4bccbe/ teambit.react__react-preview/__overview-1608752075118.js)

import { linkModules } from '/Users/user/.config/yarn/global/node_modules/@teambit/preview/dist/preview.preview.runtime.js';
import harmony from '/Users/user/.config/yarn/global/node_modules/@teambit/harmony/dist/index.js';
const defaultModule = require('/Users/user/.config/yarn/global/node_modules/@teambit/react/dist/docs/index.js');
linkModules('overview', defaultModule, {
  'ui/card': [
    require('/Users/user/Library/Caches/Bit/capsules/e5e3c066720abd3391b917ed141cc7c7163b17ce/company.demo_ui_card_@1.0.1/dist/card.docs.mdx'),
  ],
  'ui/heading': [
    require('/Users/user/Library/Caches/Bit/capsules/e5e3c066720abd3391b917ed141cc7c7163b17ce/company.demo_ui_heading@0.0.1/dist/heading.docs.mdx'),
  ],
  'ui/text': [
    require('/Users/user/Library/Caches/Bit/capsules/e5e3c066720abd3391b917ed141cc7c7163b17ce/company.demo_ui_text@1.0.1/dist/text.docs.mdx'),
  ],
  'pages/welcome': [
    require('/Users/user/Library/Caches/Bit/capsules/e5e3c066720abd3391b917ed141cc7c7163b17ce/company.demo_pages_welcome@1.0.0/dist/welcome.docs.mdx'),
  ],
});
CopiedCopy

The linkModules function registers the preview name, the main module, and the mapping of component names to their dist paths. This enables Preview to retrieve specific modules from the generated bundles, and use them to render specific previews for specific components.

Bundlers process and concatenate modules of an application by crawling over a network of dependencies that starts from the app's entry file. Components, however, are not all dependencies (direct or indirect) of a single entry file. They do not provide the bundler with a single network of modules to process.

diagram

To solve that, the Preview aspect imports all the modules of a single preview into a single link file, named using a timestamp and prefixed by the preview name. For example: __overview-1633481243984.js.

diagram