@varlinor/node-tools
Version:
This package provides a collection of utility functions for Node.js environments, focusing on file operations, JSON parsing, dependency management, and Vue component scanning. It includes functions for recursively scanning directories, loading and parsing
174 lines (169 loc) • 6.15 kB
JavaScript
import fs from 'fs-extra';
import { cloneDeep } from 'lodash-es';
import { n as normalizePath } from './shared/node-tools.7c2c5cad.mjs';
import { createFilter } from '@rollup/pluginutils';
const svgTitle = /<svg([^>+].*?)>/;
const clearHeightWidth = /(width|height)="([^>+].*?)"/g;
const hasViewBox = /(viewBox="[^>+].*?")/g;
const clearReturn = /(\r)|(\n)/g;
const clearFill = /(fill="[^>+].*?")/g;
function findSvgFile(dir, idPrefix, iconNames) {
const svgRes = [];
const dirents = fs.readdirSync(dir, {
withFileTypes: true
});
for (const dirent of dirents) {
iconNames.push(`${idPrefix}-${dirent.name.replace(".svg", "")}`);
if (dirent.isDirectory()) {
svgRes.push(...findSvgFile(`${dir}${dirent.name}/`, idPrefix, iconNames));
} else {
const svg = fs.readFileSync(dir + dirent.name).toString().replace(clearReturn, "").replace(clearFill, 'fill=""').replace(svgTitle, ($1, $2) => {
let width = 0;
let height = 0;
let content = $2.replace(clearHeightWidth, (s1, s2, s3) => {
if (s2 === "width") {
width = s3;
} else if (s2 === "height") {
height = s3;
}
return "";
});
if (!hasViewBox.test($2)) {
content += `viewBox="0 0 ${width} ${height}"`;
}
return `<symbol id="${idPrefix}-${dirent.name.replace(".svg", "")}" ${content}>`;
}).replace("</svg>", "</symbol>");
svgRes.push(svg);
}
}
return svgRes;
}
const svgBuilder = (path, prefix = "local") => {
if (path === "")
return;
const iconNames = [];
const res = findSvgFile(path, prefix, iconNames);
return {
name: "rts-plugin:svg-transform",
transformIndexHtml(html) {
return html.replace(
"<body>",
`<body>
<svg id="local-icon" data-icon-name="${iconNames.join(
","
)}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
${res.join("")}
</svg>`
);
}
};
};
const extensions = [".ts", ".js", ".jsx", ".tsx", ".css", ".scss", ".vue"];
const tryResolveFile = async (basePath, extensions2, context, importer, options) => {
for (const ext of extensions2) {
const resolved = await context.resolve(`${basePath}${ext}`, importer, options);
if (resolved)
return resolved.id;
}
return null;
};
const tryDistResolve = async (distPath, context, importer, options) => {
return tryResolveFile(distPath, [".js", "/index.js"], context, importer, options);
};
const trySrcResolve = async (srcPath, context, importer, options) => {
if (!srcPath.includes("src/"))
return null;
return tryResolveFile(
srcPath,
[".vue", "/index.vue", "/index.js", "/index.ts"],
context,
importer,
options
);
};
function vue3SfcAdapter(scopes) {
let curAlias = [];
return {
name: "rts-plugin:vue3-sfc-adapter",
configResolved(config) {
const {
resolve: { alias }
} = config;
curAlias = cloneDeep(alias);
},
/*
可能遇到的情况:
1、加载已经安装的指定scopes里的第三方包做处理,此时source为@scope开头
2、加载本地packages里某个包的组件,因为在vite.config中配置了alias,所以会将@scope/packageName转换成本地路径,
此时source变成了/yourPackagePath/packages/components/这种格式
PS:经过验证,本地包不添加alias时,无法通过@开头的处理逻辑进行解析,即使添加src也不行
*/
async resolveId(source, importer, options) {
const hasSuffix = extensions.some((e) => source.toLowerCase().endsWith(e));
if (!hasSuffix) {
const matched = curAlias.find((a) => source.indexOf(a.replacement) > -1);
if (matched || scopes.some((sc) => source.includes(sc))) {
try {
const p = normalizePath(source);
const pArr = p.split("/");
let resolvedId = null;
if (matched) {
resolvedId = await trySrcResolve(p, this, importer, options);
} else if (p.startsWith("@") && pArr.length > 2) {
resolvedId = await tryDistResolve(p, this, importer, options);
} else if (pArr.length > 1) {
resolvedId = await tryDistResolve(p, this, importer, options);
}
if (resolvedId)
return resolvedId;
const resolved = await this.resolve(source, importer, { skipSelf: true });
if (resolved)
return resolved.id;
} catch (error) {
console.error("Error resolving module:", source, error);
}
}
}
return null;
}
};
}
const isTargetImporter = (p) => {
const buildCachePath = "@varlinor/builder/utils/dynamic-import".replace(/\//g, "_");
return p.includes("varlinor/builder") && p.includes("/utils/dynamic-import") || p.includes(buildCachePath);
};
function dynamicImport({ include, exclude, componentsMap, presetModules } = {}) {
createFilter(include, exclude);
return {
name: "qkt-plugin:dynamic-import",
enforce: "pre",
transform(code, id) {
const p = normalizePath(id);
if (isTargetImporter(p) && componentsMap && Object.keys(componentsMap).length) {
try {
const maps = [];
for (const key in componentsMap) {
if (Object.prototype.hasOwnProperty.call(componentsMap, key)) {
maps.push(`case '${key}' : return () => import('${key}');`);
}
}
const modifierCode = `export default function(id){
//console.warn('Dynamic load:',id)
switch(id){
${maps.join("\n")}
default:
return Promise.reject(new Error('Unknow variable dynamic import: '+id));
}
}`;
return modifierCode;
} catch (error) {
console.error(error);
this.error(error);
return code;
}
}
return null;
}
};
}
export { dynamicImport, svgBuilder, vue3SfcAdapter };