Mark
Posted on May 28, 2024
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";
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
import { resolve } from "path";
export const componentPath = resolve(__dirname, "../../");
export const pkgPath = resolve(__dirname, "../../../");
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;
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
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());
Add scripts to easyest/package.json in the root directory.
"scripts": {
"build:stellarnovaui": "gulp -f packages/components/script/build/index.ts"
},
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
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`));
};
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);
});
};
Then export the 'run' function.
export const buildComponent = async () => {
run("pnpm run build", componentPath);
};
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()
)
);
Finally, ignore the less files when packaging with Vite, components/vite.config.ts
In order to see the result after packaging, we can write a simple Icon component, the directory is as follows.
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.
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
Posted on May 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.