DEV Community

Mark
Mark

Posted on • Updated on

Building Vue3 Component Library from Scratch #7 Using Gulp to Implement On-Demand Import

When we use Vite to package the component library, Vite will package all style files into a single file. This means that we have to import all style files in bulk each time, which prevents on-demand loading. Therefore, during packaging, we can configure Vite to not package the style files, and instead use Gulp to package the style files. This article will introduce how to use Gulp to package style files and how to load style files on demand.

Automatic On-Demand Import Plugin

Nowadays, many component libraries solve on-demand imports with the help of plugins. For example, ElementPlus uses unplugin-vue-components and unplugin-auto-import. These two plugins can achieve on-demand imports.

import { Button } from "stellarnovaui";

//means to be
import "stellarnovaui/es/src/button/style/index.css";
import "stellarnovaui/es/src/button/index.mjs";
Enter fullscreen mode Exit fullscreen mode

Thus, on-demand imports can be achieved. We will not go into detail about the use of these plugins here, as this article does not focus on that topic. If you are interested, you can directly look up the usage of unplugin-vue-components.

Delete Package Files

As we all know, it's necessary to delete the previously packaged files before packaging again. So, we need to write a delete function first. Before doing this, we will create a new script folder in the components directory to store our script-related content. The contents of the build folder under the script directory will be the packaging-related content introduced in this article.

Create paths.ts file in script/utils to maintain the paths of the component library. Remember to install the necessary dependencies first.

pnpm add @types/node -D -w
Enter fullscreen mode Exit fullscreen mode
import { resolve } from "path";

export const componentPath = resolve(__dirname, "../../");

export const pkgPath = resolve(__dirname, "../../../");
Enter fullscreen mode Exit fullscreen mode

The function to delete the packaging directory can be placed in build/utils/delpath.ts. Note that since the packaged stellarnovaui folder is the one we will ultimately publish, we need to retain the package.json and README.md files.

import fs from "fs";
import { resolve } from "path";
import { pkgPath } from "./paths";
//keep files
const stayFile = ["package.json", "README.md"];

const delPath = async (path: string) => {
  let files: string[] = [];

  if (fs.existsSync(path)) {
    files = fs.readdirSync(path);

    files.forEach(async (file) => {
      let curPath = resolve(path, file);

      if (fs.statSync(curPath).isDirectory()) {
        // recurse
        if (file != "node_modules") await delPath(curPath);
      } else {
        // delete file
        if (!stayFile.includes(file)) {
          fs.unlinkSync(curPath);
        }
      }
    });

    if (path != `${pkgPath}/easyest`) fs.rmdirSync(path);
  }
};
export default delPath;
Enter fullscreen mode Exit fullscreen mode

Use Gulp

We need to use TypeScript and the new ES6 syntax, but Gulp does not support these by default. Therefore, we need to install some dependencies to enable Gulp to support them. Sucrase allows us to execute Gulp using the latest syntax and support TypeScript.

pnpm i gulp @types/gulp sucrase -D -w
Enter fullscreen mode Exit fullscreen mode

Execute the deletion process in build/index.ts.

import delPath from "../utils/delpath";
import { series, parallel } from "gulp";
import { pkgPath } from "../utils/paths";

export const removeDist = () => {
  return delPath(`${pkgPath}/stellarnovaui`);
};

export default series(async () => removeDist());
Enter fullscreen mode Exit fullscreen mode

Add scripts to easyest/package.json in the root directory.

  "scripts": {
    "build:stellarnovaui": "gulp -f packages/components/script/build/index.ts"
  },
Enter fullscreen mode Exit fullscreen mode

run pnpm run build:stellarnovaui, You can see files in stellarnovaui folder is deleted.

Now we can start to build styles.

Use Gulp to Build Styles

Since we are using less to write styles, we need to install 'gulp-less'. At the same time, we should install the 'gulp-autoprefixer' plugin for automatically adding CSS prefixes, as well as their type files.

pnpm add gulp-less @types/gulp-less gulp-autoprefixer @types/gulp-autoprefixer -D -w
Enter fullscreen mode Exit fullscreen mode

Then write a function to package the styles. Here we use the 'dest' and 'src' functions of gulp. If you don't know what these mean, you can refer to the previous article introducing gulp.

export const buildStyle = () => {
  return src(`${componentPath}/src/**/style/**.less`)
    .pipe(less())
    .pipe(autoprefixer())
    .pipe(dest(`${pkgPath}/stellarnovaui/lib/src`))
    .pipe(dest(`${pkgPath}/stellarnovaui/es/src`));
};
Enter fullscreen mode Exit fullscreen mode

Build Components

Finally, write a function to package the components. Here, we need to write a utility function to execute commands, in 'utils/run.ts'.

import { spawn } from 'child_process';

export default async (command: string, path: string) => {
  // cmd' represents the command, and 'args' represents the parameters.
  // For example, in 'rm -rf', 'rm' is the command, and '-rf' are the parameters.
  const [cmd, ...args] = command.split(' ');
  return new Promise((resolve) => {
    const app = spawn(cmd, args, {
      cwd: path, //The path where the command is executed.
      stdio: 'inherit', // Output is shared with the parent process.
      shell: true // Mac does not need to be enabled, but on Windows, Git Bash needs to be enabled for support.
    });
    // After execution is complete, close and resolve.
    app.on('close', resolve);
  });
};
Enter fullscreen mode Exit fullscreen mode

Then export the 'run' function.

export const buildComponent = async () => {
  run("pnpm run build", componentPath);
};
Enter fullscreen mode Exit fullscreen mode

Because packaging styles and packaging components can be done in parallel, the final build/index.ts is as follows.

import delPath from '../utils/delpath';
import { series, parallel, src, dest } from 'gulp';
import { pkgPath, componentPath } from '../utils/paths';
import less from 'gulp-less';
import autoprefixer from 'gulp-autoprefixer';
import run from '../utils/run';

export const removeDist = () => {
  return delPath(`${pkgPath}/stellarnovaui`);
};

export const buildStyle = () => {
  return src(`${componentPath}/src/**/style/**.less`)
    .pipe(less())
    .pipe(autoprefixer())
    .pipe(dest(`${pkgPath}/stellarnovaui/lib/src`))
    .pipe(dest(`${pkgPath}/stellarnovaui/es/src`));
};

export const buildComponent = async () => {
  run('pnpm run build', componentPath);
};
export default series(
  async () => removeDist(),
  parallel(
    async () => buildStyle(),
    async () => buildComponent()
  )
);
Enter fullscreen mode Exit fullscreen mode

Finally, ignore the less files when packaging with Vite, components/vite.config.ts

Image description

In order to see the result after packaging, we can write a simple Icon component, the directory is as follows.

Image description

Since Vite ignores the packaging of .less files during the build process, the .less files are automatically skipped in the packaged output. However, since we have already packaged the .less files into .css files, we need to replace the .less files in the code with .css."

Add a new content in the plugins of components/vite.config.ts.

Image description

Now execute 'pnpm run build:stellarnovaui', and then look at the file imports after packaging,.less has been replaced with .css, and the packaging is complete.

The next step is to publish it. The following article will introduce how to publish a component library.

The final source code: https://github.com/markliu2013/StellarNovaUI

For more information about build your component library, checkout this article, https://dev.to/markliu2013/how-to-build-component-libraries-8k2

Top comments (0)