How to Build a Bit rudimental Compilers for Svelte

 How to Build a Bit rudimental Compilers for Svelte

In this article, I am going to show you how to build a rudimental compilers for Svelte and how you can apply the same approach to any type of tooling.

Bit is a tool that helps teams build components, test and render them in isolation, search and visualize them in a neat UI, and share them with other teams with ease.

This is image title

While Bit provides official tooling for the most important frameworks out there, you also have the possibility to extend and use it for a new shiny framework or for your own tools, or if you have a configuration that requires it.

While going through the documentation I noticed that Bit has an extensive configurability that allows you to customize their CLI platform with new functionalities: one of them, writing a custom compiler, immediately caught my attention, so I set out to build an extension for one of the hottest libraries to build components: Svelte.

In this article, I am going to show you how to build a rudimental compiler for Svelte and how you can apply the same approach to any type of tooling.

Notice: the following is a simple, native, quick solution and does not aim to match the quality of Bit’s official compilers. But it does aim to show you how quick and easy it is to build your own compilers.

A look at Svelte’s API

The first thing I did to make sure I could build a compiler for Svelte components was to analyze the API provided: as expected, it was way easier than with other major frameworks. Svelte’s API is small, comprehensive and works amazingly.

The following snippet is taken directly from svelte.dev:

const svelte = require('svelte/compiler');

const result = svelte.compile(source, {
    // options
});

That’s pretty much it! Now we need to find out how to wire this up with Bit’s API.

Wiring up the Svelte compiler with Bit’s API

To understand well how to build a Bit compiler, I went through their list of compilers officially available.

The first thing to do is to start a new Typescript project called svelte-compiler and initiate a new Bit project with the following command:

▶ bit init

If you’re not logged in, also run:

▶ bit login

As the documentation states, the interface our compile function needs to implement is pretty simple:

function compile(files: Vinyl[], distPath: string): Promise<Vinyl[]> {      
    // code here...      
    return compiledFilesArray;
}

Vinyl is a wrapper class to handle files

Let’s see how we can implement this function step by step. Initially, we are concerned about two things:

  • handle gracefully when the list of files is empty
  • handle gracefully when the dist path provided by the user doesn’t exist. Of course, the following implementation is naive, as it will simply check if the path exists or not, and will create the path if it doesn’t
export function compile(
  files: Vinyl[],
  distPath: string
): Promise<Vinyl[]> {
  if (files.length === 0) {
    return Promise.resolve([]);
  }
  if (!fs.existsSync(distPath)) {
     console.warn('Path does not exist. Automatically generating path');
    fs.mkdirSync(distPath);
  }

  // more code
}

The next step is to read the Svelte files, compile them, and then write them to the dist folder provided by the configuration. Let’s create a standalone function for that:

  • we loop through the files and compile them one by one using svelte.compile
  • we retrieve the component’s filename and we write the resulting code to the dist path received from the configuration
function compileSvelteFiles(
  files: Vinyl[], 
  distPath: string
) {
  files.forEach(file => {
    const source = fs.readFileSync(file.path, 'utf-8');
    const result = svelte.compile(source, {});
    const fileNamePathParts = file.path.split('/');
    const fileName = fileNamePathParts[
       fileNamePathParts.length - 1
    ];
    const fileDistPath = path.resolve(distPath + '/' + fileName);
    fs.writeFileSync(fileDistPath, result.js.code);
  });
}

As we’ve seen initially, the function requires us to return a list of Vinyl files. What we will do next is to loop through the files created in the previous step and push them to an array as Vinyl files:

function getVinylFiles(distPath: string): Vinyl[] {
  const outputFiles = [];
  fs.readdirSync(distPath).forEach(name => {
    const fileDistPath = distPath + '/' + name;
    const fileContent = fs.readFileSync(fileDistPath);

    outputFiles.push(
      new Vinyl({
        contents: fileContent,
        base: distPath,
        path: path.join(distPath, name),
        basename: replaceSvelteExtension(name)
     })
   );
  });

  return outputFiles;
}

Finally, we wrap the result of the previous function in a Promise:

export function compile(
  files: Vinyl[],
  distPath: string
): Promise<Vinyl[]> {
  if (files.length === 0) {
    return Promise.resolve([]);
  }
  if (!fs.existsSync(distPath)) {
    console.warn('Path does not exist. Automatically generating path');
    fs.mkdirSync(distPath);
}
  return new Promise(resolve => {
    compileSvelteFiles(files, distPath);
    resolve(getVinylFiles(distPath));
  });
}

Implementing Bit’s Compiler API

First, we want to install some dependencies needed to use Bit’s compiler

▶ npm i @bit/bit.envs.common.compiler-types

Now, we can run our compile function and wire it up with Bit’s compiler API. Bit provides an interface Compiler we can implement:

import {
  Compiler,
  InitAPI,
  CompilerContext,
  ActionReturnType
} from '@bit/bit.envs.common.compiler-types';
import { compile } from './compile';
export class SvelteCompiler implements Compiler {
  init(ctx: { api: InitAPI }) {
    return {
      write: true
    };
  }

  getDynamicPackageDependencies(
    ctx: CompilerContext, name?: string) 
   { 
     return {};
   }
  async action(ctx: CompilerContext): Promise<ActionReturnType> {
    const dists = await compile(ctx.files, ctx.context.rootDistDir);
    return { dists };
  }
}

Finally, we export our compiler with a barrel file index.ts:

import { SvelteCompiler } from './svelte-compiler';
export { SvelteCompiler };
export default new SvelteCompiler();

Using Our Compiler with Bit

Now that our compiler is finished, we may want to export it to Bit or we can run it locally by pointing our configuration to the compiled file.

In order to export it to Bit, you can run the following commands:

▶ bit add .
▶ bit tag --all 1.0.0
▶ bit export <collection_name>

Assuming, you already have 2 repositories:

  • one with a collection of components you want to export
  • one with an application that needs to use the exported components

Let’s configure our project so that our configuration will point to the compiler we created.

Importing the Compiler

Run the following command in your Bit project so that we can use the compiler for the components project:

▶ bit import <your-bit-compiler> --compiler

Configuring the Project with the custom compiler

Open your package.json and set the following values:

"bit": {
 "env": {
   "compiler": "<your-bit-compiler>@<version>"
 },
 "componentsDefaultDirectory": "components/{name}",
 "packageManager": "npm"
}

Of course, make sure you set the actual name and versions of your compiler.

Exporting Components

Now that the configuration is set, it’s time to export our components. Let’s assume our components live in the folder src/components and that we have 1 component called Alert.svelte.

We start by tracking the components, e.g. we tell Bit where our components live:

▶ bit add src/components/* 
tracking component alert:
added src/components/Alert.svelte

We can then proceed and build the components with the following command:

▶ bit build

We tag the components with a version:

▶ bit tag --all 1.0.0

Finally, we export and sync them with Bit:

▶ bit export <your-collection-name>
exported 1 components to scope <your-collection-name>

Using exported components in your Svelte app

Once we exported our components from our component library, it’s time to use them in our application project.

In order to retrieve them, we use Bit’s import command. We assume we only want to import the Alert.svelte component. Assuming we have already initialized Bit in the project, we run the following command:

▶ bit import <your-collection-name>/alert
successfully ran npm install at <path-to-project>/components/alert
successfully imported one component
- up to date <your-collection-name>/alert

Finally, let’s update your application to use our newly imported component:

<script>
  import Alert from "../components/alert";
</script>
<Alert type="danger">My Alert</Alert>

And that’s all!

We can now build components with Svelte, export them to our Bit collection and use them within our project.Of course, this example is naive and far from what an officially supported compiler would look like, but it can help you think what compilers can be implemented and integrated with Bit in only a few lines of code.

If you need any clarifications, or if you think something is unclear or wrong, do please leave a comment! Thank you for reading !

javascript typescript web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Hire Web Developer

Looking for an attractive & user-friendly web developer? HourlyDeveloper.io, a leading web, and mobile app development company, offers web developers for hire through flexible engagement models. You can **[Hire Web...

Why Web Development is Important for your Business

With the rapid development in technology, the old ways to do business have changed completely. A lot more advanced and developed ways are ...

Important Reasons to Hire a Professional Web Development Company

    You name the business and I will tell you how web development can help you promote your business. If it is a startup or you seeking some...

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

How long does it take to develop/build an app?

This article covers A-Z about the mobile and web app development process and answers your question on how long does it take to develop/build an app.