npm i lerna -g
lerna init
命令 | 功能 |
---|---|
lerna bootstrap | 安装依赖 |
lerna clean | 删除各个包下的node_modules |
lerna init | 创建新的lerna库 |
lerna list | 查看本地包列表 |
lerna changed | 显示自上次release tag以来有修改的包, 选项通 list |
lerna diff | 显示自上次release tag以来有修改的包的差异, 执行 git diff |
lerna exec | 在每个包目录下执行任意命令 |
lerna run | 执行每个包package.json中的脚本命令 |
lerna add | 添加一个包的版本为各个包的依赖 |
lerna import | 引入package |
lerna link | 链接互相引用的库 |
lerna create | 新建package |
lerna publish | 发布 |
{
"name": "root",
"private": true,
"devDependencies": {
"lerna": "^4.0.0"
}
}
{
"packages": [
"packages/*"
],
"version": "0.0.0"
}
node_modules
.DS_Store
design
*.log
packages/test
dist
temp
.vuerc
.version
.versions
.changelog
package.json
{
"name": "root",
"private": true,
+ "workspaces": [
+ "packages/*"
+ ],
"devDependencies": {
"lerna": "^4.0.0"
}
}
lerna.json
{
"packages": [
"packages/*"
],
"version": "1.0.0",
+ "useWorkspaces": true,
+ "npmClient": "yarn"
}
设置加速镜像
yarn config set registry http://registry.npm.taobao.org
npm config set registry https://registry.npm.taobao.org
作用 | 命令 |
---|---|
查看工作空间信息 | yarn workspaces info |
给根空间添加依赖 | yarn add chalk cross-spawn fs-extra --ignore-workspace-root-check |
给某个项目添加依赖 | yarn workspace create-react-app3 add commander |
删除所有的 node_modules | lerna clean 等于 yarn workspaces run clean |
安装和link | yarn install 等于 lerna bootstrap --npm-client yarn --use-workspaces |
重新获取所有的 node_modules | yarn install --force |
查看缓存目录 | yarn cache dir |
清除本地缓存 | yarn cache clean |
lerna create vite-cli
lerna create vite-project
{
"name": "vite-cli",
"version": "0.0.0",
"bin":{
"vite-cli":"./bin/vite.js"
},
"scripts": {}
}
packages\vite-cli\bin\vite.js
#!/usr/bin/env node
function start() {
require('../lib/cli')
}
start()
packages\vite-cli\lib\cli.js
console.log('vite');
{
"name": "vite-project",
"version": "0.0.0",
"scripts": {}
}
yarn
cd packages/vite-cli
npm link
npm root -g
vite-cli
cd packages/vite-project
yarn workspace vite-project add vite
cd packages/vite-cli
yarn workspace vite-cli add es-module-lexer koa koa-static magic-string
packages\vite-project\package.json
{
"name": "vite-project",
"version": "0.0.0",
+ "scripts": {
+ "dev":"vite"
+ },
"dependencies": {
"vite": "^2.4.1"
}
}
packages\vite-project\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
packages\vite-project\src\main.js
console.log('main.js');
.vscode\launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "vue-cli",
"cwd":"${workspaceFolder}/packages/vite-project",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"dev"
],
"port":9229,
"autoAttachChildProcesses": true,
"stopOnEntry": true,
"skipFiles": [
"<node_internals>/**"
]
}
]
}
packages\vite-cli\lib\serverPluginServeStatic.js
const path = require('path');
function serveStaticPlugin({ app, root }) {
// 以当前根目录作为静态目录
app.use(require('koa-static')(root));
// 以public目录作为根目录
app.use(require('koa-static')(path.join(root, 'public')))
}
exports.serveStaticPlugin = serveStaticPlugin;
packages\vite-cli\lib\cli.js
const Koa = require('koa');
const {serveStaticPlugin} = require('./serverPluginServeStatic');
function createServer() {
const app = new Koa();
const root = process.cwd();
// 构建上下文对象
const context = {
app,
root
}
app.use((ctx, next) => {
// 扩展ctx属性
Object.assign(ctx, context);
return next();
});
const resolvedPlugins = [
serveStaticPlugin
];
// 依次注册所有插件
resolvedPlugins.forEach(plugin => plugin(context));
return app;
}
createServer().listen(4000);
yarn workspace vite-project add vue@3 @vitejs/plugin-vue @vue/compiler-sfc
node ./node_modules/esbuild/install.js
packages\vite-project\nodemon.json
{
"watch":["../vite-cli"]
}
nodemon ../vite-cli/bin/vite.js
packages\vite-project\vite.config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue({})],
});
packages\vite-project\src\main.js
+import {createApp} from 'vue';
+console.log(createApp);
packages\vite-cli\lib\cli.js
const Koa = require('koa');
const {serveStaticPlugin} = require('./serverPluginServeStatic');
+const {moduleRewritePlugin} = require('./serverPluginModuleRewrite');
function createServer() {
const app = new Koa();
const root = process.cwd();
// 构建上下文对象
const context = {
app,
root
}
app.use((ctx, next) => {
// 扩展ctx属性
Object.assign(ctx, context);
return next();
});
const resolvedPlugins = [
+ moduleRewritePlugin,
serveStaticPlugin
];
// 依次注册所有插件
resolvedPlugins.forEach(plugin => plugin(context));
return app;
}
createServer().listen(4000,()=>{
+ console.log(`dev server running at: http://localhost:4000/`);
});
packages\vite-cli\lib\serverPluginModuleRewrite.js
magic-string
是一个操作字符串和生成source-map的工具
var MagicString = require('magic-string');
var magicString = new MagicString('export var name = "zhufeng"');
//返回magicString的克隆,删除原始字符串开头和结尾字符之前的所有内容
console.log(magicString.snip(0, 6).toString());
//从开始到结束删除字符(原始字符串,而不是生成的字符串)
console.log(magicString.remove(0,7).toString());
//使用MagicString.Bundle可以联合多个源代码
let bundleString = new MagicString.Bundle();
bundleString.addSource({
content: 'var a = 1;',
separator: '\n'
})
bundleString.addSource({
content: 'var b = 2;',
separator: '\n'
})
console.log(bundleString.toString());
const { readBody } = require("./utils");
const { parse } = require('es-module-lexer');
const MagicString = require('magic-string');
function rewriteImports(source) {
let imports = parse(source)[0];
const magicString = new MagicString(source);
if (imports.length) {
for (let i = 0; i < imports.length; i++) {
const { s, e } = imports[i];
let id = source.substring(s, e);//vue
if (/^[^\/\.]/.test(id)) {
id = `/@modules/${id}`;// /node_modules/.vite/vue.js?v=14157793
magicString.overwrite(s, e, id);
}
}
}
return magicString.toString();
}
function moduleRewritePlugin({ app, root }) {
app.use(async (ctx, next) => {
await next();
console.log(ctx.body);
// 对类型是js的文件进行拦截
if (ctx.body && ctx.response.is('js')) {
// 读取文件中的内容
const content = await readBody(ctx.body);
// 重写import中无法识别的路径
const result = rewriteImports(content);
ctx.body = result;
}
});
}
exports.moduleRewritePlugin = moduleRewritePlugin;
packages\vite-cli\lib\utils.js
const { Readable } = require('stream')
async function readBody(stream) {
if (stream instanceof Readable) {
return new Promise((resolve) => {
let buffers = [];
stream
.on('data', (chunk) => buffers.push(chunk))
.on('end', () => resolve(Buffer.concat(buffers).toString()));
})
}else{
return stream.toString()
}
}
exports.readBody = readBody
packages\vite-cli\lib\serverPluginModuleResolve.js
const fs = require('fs').promises;
const moduleRE = /^\/@modules\//;
const { resolveVue } = require('./utils')
function moduleResolvePlugin({ app, root }) {
const vueResolved = resolveVue(root)
app.use(async (ctx, next) => {
// 对 /@modules 开头的路径进行映射
if (!moduleRE.test(ctx.path)) {
return next();
}
// 去掉 /@modules/路径
const id = ctx.path.replace(moduleRE, '');
ctx.type = 'js';
const content = await fs.readFile(vueResolved[id], 'utf8');
ctx.body = content
});
}
exports.moduleResolvePlugin = moduleResolvePlugin;
packages\vite-cli\lib\serverPluginHtml.js
const { readBody } = require("./utils");
function htmlRewritePlugin({root,app}){
const devInjection = `
<script>
window.process = {env:{NODE_ENV:'development'}}
</script>
`
app.use(async(ctx,next)=>{
await next();
if(ctx.response.is('html')){
const html = await readBody(ctx.body);
ctx.body = html.replace(/<head>/,`$&${devInjection}`)
}
})
}
exports.htmlRewritePlugin = htmlRewritePlugin
packages\vite-cli\lib\utils.js
const { Readable } = require('stream')
const path = require('path');
async function readBody(stream) {
if (stream instanceof Readable) {
return new Promise((resolve) => {
let buffers = [];
stream
.on('data', (chunk) => buffers.push(chunk))
.on('end', () => resolve(Buffer.concat(buffers).toString()));
})
} else {
return stream.toString()
}
}
exports.readBody = readBody
function resolveVue(root) {
const compilerPkgPath = path.resolve(root, '../../node_modules', '@vue/compiler-sfc/package.json');
const compilerPkg = require(compilerPkgPath);
// 编译模块的路径 node中编译
const compilerPath = path.join(path.dirname(compilerPkgPath), compilerPkg.main);
const resolvePath = (name) => path.resolve(root, '../../node_modules', `@vue/${name}/dist/${name}.esm-bundler.js`);
// dom运行
const runtimeDomPath = resolvePath('runtime-dom')
// 核心运行
const runtimeCorePath = resolvePath('runtime-core')
// 响应式模块
const reactivityPath = resolvePath('reactivity')
// 共享模块
const sharedPath = resolvePath('shared')
return {
vue: runtimeDomPath,
'@vue/runtime-dom': runtimeDomPath,
'@vue/runtime-core': runtimeCorePath,
'@vue/reactivity': reactivityPath,
'@vue/shared': sharedPath,
compiler: compilerPath,
}
}
exports.resolveVue=resolveVue;
packages\vite-cli\lib\cli.js
const Koa = require('koa');
const {serveStaticPlugin} = require('./serverPluginServeStatic');
const {moduleRewritePlugin} = require('./serverPluginModuleRewrite');
const {moduleResolvePlugin} = require('./serverPluginModuleResolve');
const {htmlRewritePlugin} = require('./serverPluginHtml');
function createServer() {
const app = new Koa();
const root = process.cwd();
// 构建上下文对象
const context = {
app,
root
}
app.use((ctx, next) => {
// 扩展ctx属性
Object.assign(ctx, context);
return next();
});
const resolvedPlugins = [
htmlRewritePlugin,
moduleRewritePlugin,
moduleResolvePlugin,
serveStaticPlugin
];
// 依次注册所有插件
resolvedPlugins.forEach(plugin => plugin(context));
return app;
}
createServer().listen(4000,()=>{
console.log(`dev server running at: http://localhost:4000/`);
});
packages\vite-project\src\main.js
import {createApp} from 'vue';
+import App from './App.vue';
+createApp(App).mount("#app");
packages\vite-project\src\App.vue
<template>
<h1>App</h1>
</template>
<script>
export default {
name:'App'
}
</script>
packages\vite-cli\lib\serverPluginVue.js
const path = require('path');
const fs = require('fs').promises;
const { resolveVue } = require('./utils');
const defaultExportRE = /((?:^|\n|;)\s*)export default/
function serverPluginVue({ app, root }) {
app.use(async (ctx, next) => {
if (!ctx.path.endsWith('.vue')) {
return next();
}
// vue文件处理
const filePath = path.join(root, ctx.path);
const content = await fs.readFile(filePath, 'utf8');
// 获取文件内容
let { parse, compileTemplate } = require(resolveVue(root).compiler);
let { descriptor } = parse(content); // 解析文件内容
if (!ctx.query.type) {
let code = ``;
if (descriptor.script) {
let content = descriptor.script.content;
let replaced = content.replace(defaultExportRE, '$1const __script =');
code += replaced;
}
if (descriptor.template) {
const templateRequest = ctx.path + `?type=template`
code += `\nimport { render as __render } from ${JSON.stringify(
templateRequest
)}`;
code += `\n__script.render = __render`
}
ctx.type = 'js'
if (descriptor.script)
code += `\nexport default __script`;
ctx.body = code;
}
if (ctx.query.type == 'template') {
ctx.type = 'js';
let content = descriptor.template.content;
const { code } = compileTemplate({ source: content });
ctx.body = code;
}
})
}
exports.serverPluginVue = serverPluginVue;
packages\vite-cli\lib\cli.js
const Koa = require('koa');
const {serveStaticPlugin} = require('./serverPluginServeStatic');
const {moduleRewritePlugin} = require('./serverPluginModuleRewrite');
const {moduleResolvePlugin} = require('./serverPluginModuleResolve');
const {htmlRewritePlugin} = require('./serverPluginHtml');
+const {serverPluginVue} = require('./serverPluginVue')
function createServer() {
const app = new Koa();
const root = process.cwd();
// 构建上下文对象
const context = {
app,
root
}
app.use((ctx, next) => {
// 扩展ctx属性
Object.assign(ctx, context);
return next();
});
const resolvedPlugins = [
htmlRewritePlugin,
moduleRewritePlugin,
moduleResolvePlugin,
+ serverPluginVue,
serveStaticPlugin
];
// 依次注册所有插件
resolvedPlugins.forEach(plugin => plugin(context));
return app;
}
createServer().listen(4000,()=>{
console.log(`dev server running at: http://localhost:4000/`);
});