Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

请求支持monorepo #16

Open
zhen-one opened this issue Mar 31, 2022 · 9 comments
Open

请求支持monorepo #16

zhen-one opened this issue Mar 31, 2022 · 9 comments

Comments

@zhen-one
Copy link

monorepo下vite+react 构建报错

image

monorepo会将公共依赖提至根目录

看了一下源码,希望能支持下monorepo

image

感谢!

@cixing
Copy link

cixing commented May 25, 2022

monorepo下vite+react 构建报错

image

monorepo会将公共依赖提至根目录

看了一下源码,希望能支持下monorepo

image

感谢!

我也是这样,老哥解决了吗

@zhen-one
Copy link
Author

@cixing

我参考源码,自己简单封了一个vite插件

@cixing
Copy link

cixing commented May 25, 2022

@cixing

我参考源码,自己简单封了一个vite插件

可以给我用用吗大佬,加你QQ了,能通过交流一下不~

@zhen-one
Copy link
Author

import externalGlobals from "rollup-plugin-external-globals";
import fs from "fs";
import path from "path";
import { Plugin, UserConfig } from "vite";

/**
 * get npm module version
 * @param name
 * @returns
 */
function getModuleVersion(name: string): string {
	let pwd = process.cwd();
	let pkgFile = path.join(pwd, "node_modules", name, "package.json");
	console.log("pkgFile-first", pkgFile);
	let times = 3;
	while (!fs.existsSync(pkgFile) && times > 0) {
		pwd = path.resolve(pwd, "..");
		console.log("pwd", pwd);
		pkgFile = path.join(pwd, "node_modules", name, "package.json");
		console.log("pkgFile", pkgFile);
		times--;
	}
	if (!fs.existsSync(pkgFile)) {
		return "";
	}
	const pkgJson = JSON.parse(fs.readFileSync(pkgFile, "utf8"));
	return pkgJson.version;
}

/**
 * 是否完整的 url
 * @param path
 * @returns
 */
function isFullPath(path: string) {
	return path.startsWith("http:") || path.startsWith("https:") || path.startsWith("//")
		? true
		: false;
}

function renderUrl(
	url: string,
	data: {
		name: string;
		version: string;
		path: string;
	},
) {
	const { path } = data;
	if (isFullPath(path)) {
		url = path;
	}
	return url
		.replace(/\{name\}/g, data.name)
		.replace(/\{version\}/g, data.version)
		.replace(/\{path\}/g, path);
}

export interface Module {
	name: string;
	var: string;
	path: string | string[];
	css?: string | string[];
}

export interface Options {
	modules: Array<Module | ((prodUrl: string) => Module)>;
	prodUrl?: string;
}

/**
 * module 配置自动完成
 */
const modulesConfig = {
	react: {
		var: "React",
		jsdeliver: {
			path: "umd/react.production.min.js",
		},
	},
	"react-dom": {
		var: "ReactDOM",
		jsdeliver: {
			path: "umd/react-dom.production.min.js",
		},
	},
	"react-router-dom": {
		var: "ReactRouterDOM",
		jsdeliver: {
			path: "umd/react-router-dom.min.js",
		},
	},
	antd: {
		var: "antd",
		jsdeliver: {
			path: "dist/antd.min.js",
			css: "dist/antd.min.css",
		},
	},
	ahooks: {
		var: "ahooks",
		jsdeliver: {
			path: "dist/ahooks.js",
		},
	},
	"@ant-design/charts": {
		var: "charts",
		jsdeliver: {
			path: "dist/charts.min.js",
		},
	},
	vue: {
		var: "Vue",
		jsdeliver: {
			path: "dist/vue.global.prod.js",
		},
	},
	vue2: {
		var: "Vue",
		jsdeliver: {
			name: "vue",
			path: "dist/vue.runtime.min.js",
		},
	},
	"@vueuse/shared": {
		var: "VueUse",
		jsdeliver: {
			path: "index.iife.min.js",
		},
	},
	"@vueuse/core": {
		var: "VueUse",
		jsdeliver: {
			path: "index.iife.min.js",
		},
	},
	moment: {
		var: "moment",
		jsdeliver: {
			path: "moment.min.js",
		},
	},
	eventemitter3: {
		var: "EventEmitter3",
		jsdeliver: {
			path: "umd/eventemitter3.min.js",
		},
	},
	"file-saver": {
		var: "window",
		jsdeliver: {
			path: "dist/FileSaver.min.js",
		},
	},
	"browser-md5-file": {
		var: "browserMD5File",
		jsdeliver: {
			path: "dist/index.umd.min.js",
		},
	},
	xlsx: {
		var: "XLSX",
		jsdeliver: {
			path: "dist/xlsx.full.min.js",
		},
	},
	axios: {
		var: "axios",
		jsdeliver: {
			path: "dist/axios.min.js",
		},
	},
	lodash: {
		var: "_",
		jsdeliver: {
			path: "lodash.min.js",
		},
	},
	"crypto-js": {
		var: "crypto-js",
		jsdeliver: {
			path: "crypto-js.min.js",
		},
	},
	localforage: {
		var: "localforage",
		jsdeliver: {
			path: "dist/localforage.min.js",
		},
	},
};

function isJsdeliver(prodUrl: string) {
	return prodUrl.includes("//cdn.jsdelivr.net");
}

function isUnpkg(prodUrl: string) {
	return prodUrl.includes("//unpkg.com");
}

function isCdnjs(prodUrl: string) {
	return prodUrl.includes("//cdnjs.cloudflare.com");
}
export type ModuleName = keyof typeof modulesConfig;

function autoComplete(name: ModuleName) {
	const config = modulesConfig[name];
	if (!config) {
		throw new Error(`The configuration of module ${name} does not exist `);
	}
	return (prodUrl: string) => {
		if (isCdnjs(prodUrl)) {
			throw new Error(`The configuration of module ${name} in ${prodUrl} does not exist `);
		} else {
			if (!(isJsdeliver(prodUrl) || isUnpkg(prodUrl))) {
				console.warn("Unknown prodUrl, using the jsdeliver rule");
			}
			return {
				name,
				var: config.var,
				...config.jsdeliver,
			} as Module;
		}
	};
}

function PluginImportToCDN(options: Options): Plugin[] {
	const { modules = [], prodUrl = "https://cdn.jsdelivr.net/npm/{name}@{version}/{path}" } =
		options;

	let isBuild = false;

	const data = modules.map(m => {
		let v: Module;
		if (typeof m === "function") {
			v = m(prodUrl);
		} else {
			v = m;
		}
		const version = getModuleVersion(v.name);
		let pathList: string[] = [];
		if (!Array.isArray(v.path)) {
			pathList.push(v.path);
		} else {
			pathList = v.path;
		}

		const data = {
			...v,
			version,
		};

		pathList = pathList.map(p => {
			if (!version && !isFullPath(p)) {
				throw new Error(`modules: ${data.name} package.json file does not exist`);
			}
			return renderUrl(prodUrl, {
				...data,
				path: p,
			});
		});

		let css = v.css || [];
		if (!Array.isArray(css) && css) {
			css = [css];
		}

		const cssList = !Array.isArray(css)
			? []
			: css.map(c =>
					renderUrl(prodUrl, {
						...data,
						path: c,
					}),
			  );

		return {
			...v,
			version,
			pathList,
			cssList,
		};
	});

	const externalMap: {
		[name: string]: string;
	} = {};

	data.forEach(v => {
		externalMap[v.name] = v.var;
	});

	// const externalLibs = Object.keys(externalMap);

	const plugins: Plugin[] = [
		{
			name: "vite-plugin-cdn-import",
			config(_, { command }) {
				const userConfig: UserConfig = {
					build: {
						rollupOptions: {},
					},
				};

				if (command === "build") {
					isBuild = true;

					userConfig.build!.rollupOptions = {
						// external: [...externalLibs],
						plugins: [externalGlobals(externalMap)],
					};
				} else {
					isBuild = false;
				}

				return userConfig;
			},
			transformIndexHtml(html) {
				const cssCode = data
					.map(v => v.cssList.map(css => `<link href="${css}" rel="stylesheet">`).join("\n"))
					.filter(v => v)
					.join("\n");

				const jsCode = !isBuild
					? ""
					: data
							.map(p => p.pathList.map(url => `<script src="${url}"></script>`).join("\n"))
							.join("\n");

				return html.replace(/<\/title>/i, `</title>${cssCode}\n${jsCode}`);
			},
		},
	];

	return plugins;
}

export { PluginImportToCDN as Plugin, autoComplete };

export default PluginImportToCDN;

@cixing 使用方式没变

@cixing
Copy link

cixing commented May 25, 2022

感谢大佬,我去试一下,感谢~

@cixing
Copy link

cixing commented May 25, 2022

哥,你这个确实没报错了,但是还是把cdn的库打包进去了,相当于没有使用插件呀

@zhen-one
Copy link
Author

@cixing

等我找下代码试一下 隔离久了 代码都混了

@cixing
Copy link

cixing commented May 25, 2022 via email

@aceHubert
Copy link

image
require.resolve 就可以找到package.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants