npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

yuan-vite

v1.0.0

Published

![](https://gitee.com/aeipyuan/picture_bed/raw/master/picture_bed/images/20201218214232.png) ## 1. 设置入口并链接全局 - 编写入口文件 ```javascript // /bin/www.js #! /usr/bin/env node

Readme

vite基本实现

1. 设置入口并链接全局

  • 编写入口文件
// /bin/www.js
#! /usr/bin/env node

console.log('入口调用成功!!!')
  • 配置package.json
{
  "name": "yuan-vite",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin": {
    "yuan-vite": "./bin/www.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  • 命令行执行npm link链接到全局,命令行输入yuan-vite测试是否输出www.js执行结果

2. 搭建本地服务

  • 安装相关插件
npm i koa koa-static
  • 入口文件启动服务
// /bin/ww.js
#! /usr/bin/env node
const createServer = require('./index.js');
const PORT = 4000;
createServer().listen(PORT, () => {
  console.log(`Dev server run at http://localhost:${PORT}`);
});
  • 编写index.js实现创建服务的函数
const Koa = require('koa');
const serverStaticPlugin = require('../plugins/serverStatic');
const pathRewritePlugin = require('../plugins/serverPathRewrite');
const resolvePlugin = require('../plugins/serverResolve');
const VuePlugin = require('../plugins/serverVue');
const htmlRewritePlugin = require('../plugins/serverHtmlRewrite');

function createServer() {
  const app = new Koa();
  // 获取执行 yuan-vite 的项目路径
  const root = process.cwd();
  // 处理文件的插件数组
  const plugins = [
    htmlRewritePlugin,
    // 路径修改
    pathRewritePlugin,
    // 解析.vue
    VuePlugin,
    // 解析vue包
    resolvePlugin,
    // 静态目录
    serverStaticPlugin
  ];
  plugins.forEach(plugin => plugin({ app, root }));
  return app;
}

module.exports = createServer;

3. 静态目录托管

  • 根据传入的项目路径和app实例,使用中间件进行托管设置
// /plugins/serverStatic.js
const koaStatic = require('koa-static');
const path = require('path');
function serverStaticPlugin({ app, root }) {
  app.use(koaStatic(root));
  app.use(koaStatic(path.join(root, 'public')));
}
module.exports = serverStaticPlugin;
  • index.js引入,注册到plugins中

4. 修改第三方包引入路径

为了防止浏览器报错对第三方包引入路径报错,对此类路径加上/@modules/前缀

  • 读取响应体内容
// /plugins/utils.js
// 读取数据流
function readBody(stream) {
  if (stream instanceof Readable) {
    return new Promise((resolve) => {
      let res = '';
      stream.on('data', data => res += data);
      stream.on('end', () => resolve(res));
    });
  };
  return stream.toString();
}
  • 进行字符串替换,加上前缀
// /plugins/serverPathRewrite.js
const { readBody } = require('./utils');
const { parse } = require('es-module-lexer');
const MagicString = require('magic-string');
function reWriteBody(source) {
  let imports = parse(source)[0];// 获取引入坐标信息
  let magicString = new MagicString(source);// 魔法字符串对象
  imports.forEach(({ s, e }) => {// 起始位和终止位
    let importPath = source.substring(s, e);
    // 第三方引入加前缀
    if (/^[^\/\.]/.test(importPath)) {
      magicString.overwrite(s, e, `/@modules/${importPath}`);
    }
  })
  return magicString.toString();
}
function pathRewritePlugin({ app, root }) {
  app.use(async (ctx, next) => {
    await next();
    // 只对js文件处理
    if (ctx.body && ctx.response.is('js')) {
      let content = await readBody(ctx.body);// 响应体内容
      ctx.body = reWriteBody(content);
    }
  })
}
module.exports = pathRewritePlugin;

5. 解析路径映射表

  • 将对vue引入的url路径映射为读取文件的路径
// /plugins/utils.js
// 构建vue文件真实路径映射表
function resolveVue(root) {
  // 获取compiler-sfc的目标文件
  const compilerPkgPath = path.join(root, 'node_modules', '@vue/compiler-sfc/package.json');
  const compilerPkg = require(compilerPkgPath);
  const compilerPath = path.join(path.dirname(compilerPkgPath), compilerPkg.main);
  // 获取 runtime-dom runtime-core reactivity shared 对应的真实路径
  const resolve = name => path.resolve(root, 'node_modules', `@vue/${name}/dist/${name}.esm-bundler.js`);
  const runtimeDomPath = resolve("runtime-dom");
  const runtimeCorePath = resolve("runtime-core");
  const reactivePath = resolve("reactivity");
  const sharedPath = resolve("shared");
  // 返回映射表
  return {
    compiler: compilerPath,
    "@vue/runtime-dom": runtimeDomPath,
    "@vue/runtime-core": runtimeCorePath,
    "@vue/reactivity": reactivePath,
    "@vue/shared": sharedPath,
    vue: runtimeDomPath
  }
}
  • 根据路径读取文件内容,将结果放到响应体中
// /plugins/serverResolve.js
const fs = require('fs').promises;
const { resolveVue } = require('./utils');

function resolvePlugin({ app, root }) {
  // 获取路径映射表
  const vueResolved = resolveVue(root);
  console.log(JSON.stringify(vueResolved, null, " "));
  // 拦截请求
  app.use(async (ctx, next) => {
    const moduleREG = /\/@modules\//;
    if (!moduleREG.test(ctx.path))
      return next();
    let id = ctx.path.replace(moduleREG, '');// 去掉前缀
    ctx.type = "js";
    // console.log(id, "----", vueResolved[id])
    let content = await fs.readFile(vueResolved[id], 'utf8');// 读取真实路径
    ctx.body = content;// 返回读取出来的数据
  })
}
module.exports = resolvePlugin;

6. 转化.vue文件

步骤

  • 获取.vue内容
  • 获取模板解析方法
  • 修改template的内容
  • 修改script的内容
  • 对于?type=template的请求返回模板编译结果
// /plugins/serverVue.js
const path = require('path');
const fs = require('fs').promises;
const { resolveVue } = require('./utils');

function VuePlugin({ app, root }) {
  app.use(async (ctx, next) => {
    if (!ctx.path.endsWith('.vue')) {
      return next();
    };
    // 1. 获取.vue文件内容
    let filePath = path.join(root, ctx.path);
    let content = await fs.readFile(filePath, 'utf8');
    // 2. 获取compiler内的方法
    const { parse, compileTemplate } = require(resolveVue(root).compiler);
    const { descriptor } = parse(content);
    // 修改.vue文件的内容
    if (!ctx.query.type) {
      let code = '';
      // 替换script
      if (descriptor.script) {
        let { content } = descriptor.script;
        const defaultExportREP = /((?:^|\n|;)\s*)export default/;
        code += content.replace(defaultExportREP, `$1const __script = `);
      }
      // 替换template
      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";
      code += `\nexport default __script`;
      ctx.body = code;
    }
    // 获取模板编译后的文件
    if (ctx.query.type == 'template') {
      ctx.type = "js";
      let { content } = descriptor.template;
      const { code } = compileTemplate({ source: content });
      // 返回模板编译后的代码
      ctx.body = code;
    }
  })
}

module.exports = VuePlugin;

7. 往html添加环境变量

// /plugins/serverHtmlRewrite.js
const { readBody } = require('./utils');
function htmlRewrite({ app, root }) {
  const inject = `
    <script>
    window.process = {
      env: { NODE_ENV: 'development' }
    }
    </script>`;
  app.use(async (ctx, next) => {
    await next();
    if (ctx.body && ctx.response.is('html')) {
      let content = await readBody(ctx.body);
      ctx.body = content.replace(/<head>/, `$&${inject}`);
    }
  })
}
module.exports = htmlRewrite;

8. 总结

总的来说,vite的原理就是起一个静态服务器,使用ES6Module进行数据请求,普通文件直接通过路径获取,特殊文件则通过处理请求路径,转化为磁盘路径,fs读取文件内容后放到响应头中,同时对于.vue文件在服务端进行template的编译,前端通过新请求获得template编译结果挂载到__script.render上;