Full Code of Kingbultsea/vite-analysis for AI

master c51b9282f05d cached
64 files
395.0 KB
148.9k tokens
2 symbols
1 requests
Download .txt
Showing preview only (522K chars total). Download the full file or copy to clipboard to get everything.
Repository: Kingbultsea/vite-analysis
Branch: master
Commit: c51b9282f05d
Files: 64
Total size: 395.0 KB

Directory structure:
gitextract_xl9jd137/

├── 101-110/
│   └── commit-101-110.md
├── 11-20/
│   ├── commit-11-20.md
│   └── main.rs
├── 111-120/
│   └── commit-111-120.md
├── 121-130/
│   └── commit-121-130.md
├── 131-140/
│   └── commit-131-140.md
├── 141-150/
│   └── commit-141-150.md
├── 151-160/
│   └── 151-160.md
├── 161-170/
│   └── 161-170.md
├── 171-180/
│   └── 171-180.md
├── 181-190/
│   └── 181-190.md
├── 191-200/
│   └── 191-200.md
├── 201-210/
│   └── 201-210.md
├── 21-30/
│   └── commit-21-30.md
├── 211-220/
│   └── 211-220.md
├── 221-230/
│   └── 221-230.md
├── 231-240/
│   └── 231-240.md
├── 241-250/
│   └── 241-250.md
├── 251-260/
│   └── 251-260.md
├── 261-270/
│   └── 261-270.md
├── 271-280/
│   └── 271-280.md
├── 281-290/
│   └── 281-290.md
├── 291-300/
│   └── 291-300.md
├── 301-310/
│   └── 301-310.md
├── 31-40/
│   └── commit-31-40.md
├── 311-320/
│   └── 311-320.md
├── 321-330/
│   └── 321-330.md
├── 331-340/
│   └── 331-340.md
├── 341-350/
│   └── 341-350.md
├── 351-360/
│   └── 351-360.md
├── 361-370/
│   └── 361-370.md
├── 371-380/
│   └── 371-380.md
├── 381-390/
│   └── 381-390.md
├── 391-400/
│   └── 391-400.md
├── 401-410/
│   └── 401-410.md
├── 41-50/
│   └── commit-41-50.md
├── 411-420/
│   └── 411-420.md
├── 421-430/
│   └── 421-430.md
├── 431-440/
│   └── 431-440.md
├── 441-450/
│   └── 441-450.md
├── 451-460/
│   └── 451-460.md
├── 461-470/
│   └── 461-470.md
├── 471-480/
│   └── 471-480.md
├── 481-490/
│   └── 481-490.md
├── 491-500/
│   └── 491-500.md
├── 501-510/
│   └── 501-510.md
├── 51-60/
│   └── commit-51-60.md
├── 511-520/
│   └── 511-520.md
├── 521-530/
│   └── 521-530.md
├── 531-540/
│   └── 531-540.md
├── 541-550/
│   └── 541-550.md
├── 551-560/
│   └── 551-560.md
├── 561-570/
│   └── 561-570.md
├── 571-580/
│   └── 571-580.md
├── 581-590/
│   └── 581-590.md
├── 591-600/
│   └── 591-600.md
├── 601-610/
│   └── 601-610.md
├── 61-70/
│   └── commit-61-70.md
├── 611-620/
│   └── 611-620.md
├── 71-80/
│   └── commit-71-80.md
├── 81-90/
│   └── commit-81-90.md
├── 91-100/
│   └── commit-91-100.md
├── readme.md
└── temp/
    └── temp.md

================================================
FILE CONTENTS
================================================

================================================
FILE: 101-110/commit-101-110.md
================================================
# 101 - d1fe471 新增id映射到请求路径

允许`resolver`拥有`idToRequest`方法。

```typescript
idToRequest: (id: string) => {
      for (const r of resolvers) {
        const request = r.idToRequest && r.idToRequest(id)
        if (request) return request
      }
}
```

## 影响范围?

在`serverPluginModules.ts`中的`rewriteImports`,会利用`idToRequest`作为**模块改写的路径**。

```typescript
if (/^[^\/\.]/.test(id)) { // import lodash from 'lodash'
            const rewritten = resolver.idToRequest(id) || `/@modules/${id}`
            s.overwrite(start, end, rewritten)
            hasReplaced = true
            debugImportRewrite(`    "${id}" --> "${rewritten}"`)
}
```

## 可能引起的BUG?

```typescript
# serverPluginModules
// handle /@modules/:id requests
const moduleRE = /^\/@modules\//
```

无法正确解析模块了。



# 102 - fe1ef5a 修复传入的路径类型

修复`handleJSReload`:
  原本传入的是`filePath`,真正需要的是`publicPath`。



# 103 - ab2610f 使`js`与`vue`的`reload`方法传入的`timeStamp`参数为可选选项

在重载方法中使时间戳成为可选的。

```typescript
export type ViteWatcher = FSWatcher & {
  handleVueReload: (file: string, timestamp?: number, content?: string) => void
  handleJSReload: (file: string, timestamp?: number) => void
}
```



# 104 - 3fce891 @xxx自动转变为/@xxx

顺带整理一下改写`import`语句的代码。

把`resolver.idToRequest.defaultIdToRequest`的@id字符转为'/@id'。

把`vue组件`的`import`语句`/@hmr`改写为`@hmr`。

1. 一个`vue组件`,经过`serverPluginVue.ts`处理,变成`import { updateStyle } from '@hmr'`。
2. 经过`serverPluginServe.ts`中的`resolver.idToRequest.defaultToRequest`处理,变成`import { updateStyle } from '/@hmr'`



# 105 - c4a2d40 因104修改reademe文档

### HMR所做的替换

- `*.vue` files come with HMR out of the box.

- For `*.js` files, a simple HMR API is provided:

  ```js
  import { foo } from './foo.js'
  import { hot } from '@hmr' // 以前是/@hmr
  
  foo()
  
  hot.accept('./foo.js', ({ foo }) => {
    // the callback receives the updated './foo.js' module
    foo()
  })
  ```

## `hmr`对`js`文件无效

原因很简单,`importerMap`的键为`filePath`,而`handleJSReload`使用了`publicPath`来获取`importer`。

> 再次提醒importer为引入文件,importee为被引入文件



# 106 - 4c354ca hot路径修改

```typescript
// 以前:
hot.accept("/fc.js", "\\foo.js", ({ foo }) => {
  // the callback receives the updated './foo.js' module
  foo()
})

// 现在
hot.accept("/fc.js", "E:/foo.js", ({ foo }) => {
  // the callback receives the updated './foo.js' module
  foo()
})
```

`parseAcceptedDeps`:

![](./1.png)

这里我有些小失误,没有去讲解hot的使用问题,我在51-60commit重新补充了`handleJSReload`相关的。`parseAcceptedDeps`可以把他当作收集依赖与`hot.accept`表达式的改写,例如上面的例子`foo.js`依赖`fc.js`文件。



# 107 - e42d74d `serverPluginsVue`错误提示

添加错误提示(`template script style`)



# 108 - 4ac0801 index.html重写缓存

对于`index.html`文件,`rewriteCache`的`key`变成`ctx.body`。

我个人觉得改不改一样,对于功能上,也许更符合语义吧... 但`rewriteCache`的体积会变得更大,因为没有地方删除以前的内容(除非是超出LRU max)。



# 109 - ea97e3b 修复`build.ts`路径处理bug

由于之前把@xxx弄成自动的,在`build`环境下,就不是自动的了。

所以`vitePlugin`(rollup插件)的`resolveId`需要改写。



# 110 - a22472d `hmr`添加新事件`custom`

整理代码命名。

```typescript
# client.ts
const customUpdateMap = new Map<string, ((customData: any) => void)[]>()

case 'custom':
      const cbs = customUpdateMap.get(id)
      if (cbs) {
        cbs.forEach((cb) => cb(customData))
      }
      break


export const hot = {
  on(event: string, cb: () => void) {
    const exisitng = customUpdateMap.get(event) || []
    exisitng.push(cb)
    customUpdateMap.set(event, exisitng)
  }
}
```

未能分析其作用,将在后续commit解说其作用。

> 看出个大概就是服务端与client端能通过一个事件交流数据`customData`。


================================================
FILE: 11-20/commit-11-20.md
================================================
# 11 - 7f207eb annotation

添加测试注释(准备测试style HMR)。



# 12 - 5c0f552 fix propublishOnly

注释。

```json
{
-   prepublishOnly: "tsc"
+   propublishOnly: "yarn build"    
}
```



# 13 - be00e79 v0.1.0发布

修改旧名称 ```vds```为```vite```,包括注释,控制台输出,只是更改名称。



# 14 - a47c406 设置npm上传包含的文件

`files` 字段用于描述我们使用 `npm publish` 命令后推送到 `npm` 服务器的文件列表,如果指定文件夹,则文件夹内的所有内容都会包含进来。我们可以查看下载的 `antd` 的 `package.json` 的`files` 字段,内容如下:

```json
"files": [
  "dist",
+ "bin"
],
```

因为```dist```是```build```的文件,```/bin/vite.js```是启动文件,所以这一部分发布到```npm```,提供用户使用即可。



# 15 - a4f093a v0.1.1发布

```json
{
-   version: "0.1.1"
+   version: "0.1.1" 
}
```



# 16 - d58893b  chore readme

```chore: readme```,修改```readme```。



# 17 - c76ca14 添加ci

```yml
version: 2

defaults: &defaults
  docker:
    - image: vuejs/ci # https://hub.docker.com/r/vuejs/ci

step_restore_cache: &restore_cache
  restore_cache:
    keys:
    - v1-dependencies-{{ checksum "yarn.lock" }}-1
    - v1-dependencies-

step_install_deps: &install_deps
  run:
    name: Install Dependencies
    command: yarn --frozen-lockfile

step_save_cache: &save_cache
  save_cache:
    paths:
      - node_modules
      - ~/.cache/yarn
    key: v1-dependencies-{{ checksum "yarn.lock" }}-1

jobs:
  test:
    <<: *defaults
    steps:
      - checkout
      - *restore_cache
      - *install_deps
      - *save_cache
      - run: yarn test

workflows:
  version: 2
  ci:
    jobs:
      - test

```

setp1: 在自身```Github```上,创建一个```public```仓库,命名为```sbuild```,并把上传这一份代码。

step2: 使用该```github```账号登录```circleci```网站,在目录列表点击```Set Up Project ```。

![](./circle-view.png)

step3: 点击``Start Building``

![](./circle-test.png)

(失败可以不用管,这是运行test命令失败,在不同平台有些不一样)



# 18 - 97de06e test: fix pupeteer on ci

修复在```ci```环境下的,```puppeteer```报错问题(如上报错)。

![](circle-sussess.png)



# 19 - bb9baa2 使用本包,如果用户没有vue

### moduleResolver.ts

整理代码,如果用户本地路径没有寻找到```vue.runtime.esm-browser.js```,则从本包中读取```vue.runtime.esm-browser.js```。



# 20 - 0d5a2a4 fix: vue路径 & compile-sfc路径

### moduleResolver.ts

```vue.runtime.esm-browser.js```的寻找方式:从cwd层级查找。

```cwd```目录下的```vue```版本需要和本包中的```vue```版本一致,不然报错,且```compiler-sfc```使用本包中的```vue/compiler-sfc```。

如果用户不存在```vue```包,则```compiler-sfc```与```vue.runtime.esm-browser.js```均使用本包的。

这里主要是在客户端```import vue```的时候,顺带设置好```vueCompiler.ts```中需要的```compiler-sfc```。

### vueCompiler.ts

```typescript
import {
  SFCDescriptor,
  SFCStyleBlock,
  SFCTemplateBlock
} from '@vue/compiler-sfc'
```

SFC三个类型从本包取,编译功能从```moduleResolver.ts```中取```resolveCompiler```。

================================================
FILE: 11-20/main.rs
================================================
enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
}

fn main() {
    let row = vec![SpreadsheetCell::Int(3), SpreadsheetCell::Float(10.12), SpreadsheetCell::Text(String::from("String text"))];
    for i in &row {
        println!("{:?}", i);
        match i {
            SpreadsheetCell::Int(i2) => {
                println!("{}", i2);
            },
            _ => {
                println!("..");
            }
        };

        if let SpreadsheetCell::Float(value) = i {
            println!("{}", value);
        }
    }
}

================================================
FILE: 111-120/commit-111-120.md
================================================
# 111 - 14346ee 修复`__DEV__`条件下`hot.accept`没有被正确渲染的问题

```typescript
// 错误
__DEV__ && hot.accept('./foo.js', ({ foo }) => {
  // the callback receives the updated './foo.js' module
  foo()
})

// 正确
__DEV__ && hot.accept("/fuck.js", "E:/foo.js", ({ foo }) => {
  // the callback receives the updated './foo.js' module
  foo()
})
```

原因,AST语法树中没有检测不同的类型

```typescript
const checkStatements = (node: Statement) => {
    if (node.type === 'ExpressionStatement') {
      // top level hot.accept() call
      checkAcceptCall(node.expression)
      // __DEV__ && hot.accept()
      if (
        node.expression.type === 'LogicalExpression' &&
        node.expression.operator === '&&' &&
        node.expression.left.type === 'Identifier' &&
        node.expression.left.name === '__DEV__'
      ) {
        checkAcceptCall(node.expression.right)
      }
    }
    // if (__DEV__) ...
    if (
      node.type === 'IfStatement' &&
      node.test.type === 'Identifier' &&
      node.test.name === '__DEV__'
    ) {
      if (node.consequent.type === 'BlockStatement') {
        node.consequent.body.forEach(checkStatements)
      }
      if (node.consequent.type === 'ExpressionStatement') {
        checkAcceptCall(node.consequent.expression)
      }
    }
  }
```



# 112 - 1b0b4ba 配置化构建

```typescript
interface BuildOptions {
  root?: string
  cdn?: boolean
  resolvers?: Resolver[] // 路径转换
  srcRoots?: string[] // 资源白名单
  rollupInputOptions?: InputOptions // rollup 配置(plugins配置顺序 高于vite所用的plugins)
  rollupOutputOptions?: OutputOptions // rollup 输出配置(dir)
  write?: boolean // 是否写输出文件到磁盘中,默认true
  debug?: boolean // 开启debug后css与JS都不会被压缩,方便我们查看输出的代码,默认false
  indexPath?: string // 入口文件,默认index.html
}
```

补充几个知识:

1. `transform`的类型是`sequential | async`,如果多个插件实现了相同的钩子函数,那么会串式执行,按照使用插件的顺序从头到尾执行,如果是异步的,会等待之前处理完毕,在执行下一个插件。
2. `resolveId`和`load`的类型是`async, first`,如果多个插件实现了相同的钩子函数,那么会串式执行,从头到尾,但是,如果其中某个的返回值不是`null`也不是`undefined`的话,会直接终止掉后续插件。

> 中文翻译转载了这篇[文章](https://www.cnblogs.com/yangzhuxian/p/13371637.html)

## 我所认为的BUG

发现尤大没有把`input`配置为`indexPath`,难道是故意的(一定是漏了,如果我配置了`indexPath`,那`js`与其它`dom`不同步了)?

```typescript
// indexPath被拿去分析<script></script>的内容了

const bundle = await rollup({
    input: path.resolve(root, 'index.html')
})
```



# 113 - 56815ea 整理build的代码

整理代码。

> 现在write仅决定是否写入磁盘的操作,之前还决定是否分析rollup的输出。
>



# 114 - fa4c91b readme

现在可以配置化了。需要支持的功能依旧是`source map`。顺带还解析了一下`Vite`为什么叫`Vite`。

## Trivia

[vite](https://en.wiktionary.org/wiki/vite) 是法语快速的首发音 `/vit/`.



# 115 - 48f2459 修复深层次的路径BUG

```typescript
// recursive: true,创建文件夹,无论是否存在/a或/a/b

// filepath = '/a/b/c'
await fs.mkdir(path.dirname(filepath), { recursive: true })
```



# 116 - d9a0798 调整`css`文件名称 - build

去除构建的配置`indexPath`,自动寻找`root`下的`index.html`。

整理代码,当没有`generatedIndex`就不再触发`inject`的相关操作。

## `cssExtractPlugin`(`rollupPlugin`)新增`generateBundle`

利用`generateBundle`来生成`css`文件,同时可以使`css`文件名称可配置化。

```typescript
const cssExtractPlugin: Plugin = {
    name: 'vite-css',
    transform(code: string, id: string) {
      if (id.endsWith('.css')) {
        styles.set(id, code)
        return '/* css extracted by vite */'
      }
    },

    async generateBundle(_options, bundle) {
      // finalize extracted css
      styles.forEach((s) => {
        css += s
      })
      // minify with cssnano
      if (!debug) {
        css = (
          await require('postcss')([require('cssnano')]).process(css, {
            from: undefined
          })
        ).css
      }

      bundle[cssFileName] = {
        isAsset: true,
        type: 'asset',
        fileName: cssFileName,
        source: css
      }
    }
  }
```

`generateBundle`:
  类型:`(options: OutputOptions, bundle: { [fileName: string]: AssetInfo | ChunkInfo },isWrite: boolean) => void`
  钩子类型:`async`, `parallel`
  在`bundle.generate()` 后触发,`bundle.write()`前触发。

 ```typescript
 // AssetInfo
 {
   fileName: string,
   name?: string,
   source: string | Uint8Array,
   type: 'asset',
 }
 ```



# 117 - 38fd349 去除rollup的`preserveEntrySignatures`

去除`preserveEntrySignatures`,重新使用`export`的代码输出。

[字段解释](https://github.com/Kingbultsea/vite-analysis/blob/d71db28f2e4bdcecdaa4fc6ad311820e6dc81427/commit-61-70/commit-61-70.md#rollup%E9%85%8D%E7%BD%AEpreserveentrysignatures%E4%B8%BAfalse)



# 118 - e290349 build打包多出口配置

封装打包操作,遍历`rollupOutputOptions`,调用`generator`。

```typescript
await bundle.generate({
      dir: outDir,
      format: 'es',
      ...options // OutputOptions[]
})
```

> 注意哦,这里和rollup配置无关。



# 119 - 4bc3035 log静音

添加`slient`配置字段,决定是否输出`write`写入的操作。

同时还删除了一些Log。



# 120 - ddf2d26 添加`window.__DEV__`

开发环境才会有。

```html
<div id="app"></div>
233

<script>window.__DEV__ = true</script>
<script type="module" src="/main.js">
  import vue from '/@modules/vue'
  import a from './haha/h.js'
  console.log(a);
    console.log('123')
</script>
```


================================================
FILE: 121-130/commit-121-130.md
================================================
# 121 - 1a26b7a 推断正确的构建结果

判断传入build的类型,输出对应的类型。

```typescript
interface SingleBuildOptions extends BuildOptionsBase {
  rollupOutputOptions?: OutputOptions
}

interface MultiBuildOptions extends BuildOptionsBase {
  rollupOutputOptions?: OutputOptions[]
}

export async function build(options: SingleBuildOptions): Promise<BuildResult>
export async function build(options: MultiBuildOptions): Promise<BuildResult[]>
```

> 想返回特定情况下的类型,可以多次写一个方法。



# 122 - f5c6699 v0.7.0

release v0.7.0



# 123 - de67bc6 `changelog`

# [0.7.0](https://github.com/vuejs/vite/compare/v0.6.1...v0.7.0) (2020-04-29)

### Bug Fixes

- 修复写入的情况下,深路径文件的BUG ([48f2459](https://github.com/vuejs/vite/commit/48f2459444fd2affa053ad5857cb8bd325ea2af6))

### Features

- 支持`__DEV__`
- 在构建的情况下,支持修改 `cssFileName`  ([d9a0798](https://github.com/vuejs/vite/commit/d9a0798b0d8746a816ac516bd4267a409fb82c16))
- 允许通过选项自定义构建 ([1b0b4ba](https://github.com/vuejs/vite/commit/1b0b4ba340b5d552abd7fa0457f9b2de55fc1647))
- 允许插件发送自定义`hmr`事件 ([a22472d](https://github.com/vuejs/vite/commit/a22472d35718d08b4a947d064c82d645cfd49349))
- 支持省略`.js`扩展名 ([d00523f](https://github.com/vuejs/vite/commit/d00523f0efbc4453e31b138ca508d7d5d2479e34))



# 124 - a0053a0 允许配置`rollup-plugin-vue`

新增`rollupPluginVueOptions`选项。

[rollup-plugin-vue](https://rollup-plugin-vue.vuejs.org/options.html)



# 125 - 302980c debug -> minify

把`debug`字段改成`minify`。原因是`debug`所做的事情是压缩代码,所以改成`minify`更贴切。



# 126 - 5524e44 拆分`serverPluginModule`

`serverPluginModule.ts`拆分为:`serverPluginModuleRewrite.ts`、`serverPluginModuleResolve.ts`。

`serverPluginModuleRewrite.ts`: 改写`import`、`index.html`。

`serverPluginModuleResolve.ts`: 发送模块资源、`vue包`。

`serverPluginServe.ts`更名为`serverPluginServerStatic.ts`



# 127 - d4ccd15 readme

新增Build的文档示例

#### Build

```js
const { build } = require('vite')

;(async () => {
  // All options are optional.
  // check out `src/node/build.ts` for full options interface.
  const result = await build({
    rollupInputOptions: {
      // https://rollupjs.org/guide/en/#big-list-of-options
    },
    rollupOutputOptions: {
      // https://rollupjs.org/guide/en/#big-list-of-options
    },
    rollupPluginVueOptions: {
      // https://github.com/vuejs/rollup-plugin-vue/tree/next#options
    },
    root: process.cwd(),
    cdn: false,
    write: true,
    minify: true,
    silent: false
  })
})()
```



# 128 - a084cf2 重构迁移代码

把`serverPluginModuleRewrite.ts`中改写引入了`@hmr` `import`的功能,抽离给`serverPluginHMR.ts`。

```typescript
export const hmrBoundariesMap: HMRStateMap = new Map()
export const importerMap: HMRStateMap = new Map()
export const importeeMap: HMRStateMap = new Map()

// 构建路径 也被迁移到serverPluginHMR.ts
```

迁移的目的是因为,`hmrBoundariesMap`是给`reloadJS`使用的。



# 129 - a084cf2 chore合并`Import`语句

等同优化代码。



# 130 - b0122b8 readme和`es-dev-server`的区别

## How is This Different from [es-dev-server](https://open-wc.org/developing/es-dev-server.html)?

`es-dev-server` 是一个伟大的项目,在早期重构`vite`时,我们确实从中获得了一些灵感。也就是说,这就是为什么`vite`与`es-dev-server`不同,以及为什么我们不只是将`vite`作为`es-dev-server`的中间件来实现:

- `vite` 支持热模块更换, 在不重新加载页面的情况下通过更新模块。 这在开发模式中有着本质性的区别。 `es-dev-server` 内部结构有点不透明,无法通过中间件很好地工作。
- `vite`皆在成为一个拥有开发和构建功能的单一程序。 你可以在不配置任何东西的情况下,使用`vite`来打包代码。
- `vite` 需要原生 ES 模块导入。它不打算增加对旧版浏览器的支持。



================================================
FILE: 131-140/commit-131-140.md
================================================
# 131 - 30ab444 `hmr.accept`支持调用本身

输入:

```typescript
// # foo.js

import { hot } from '@hmr'

export const count = 1

hot.accept(newModule => {
  console.log('updated: count is now ', newModule.count)
})
```

输出:

```typescript
// foo.js

export const count = 1

hot.accept("/foo.js", "/foo.js", newModule => {
  console.log('updated: count is now ', newModule.count)
})
```

新增代码:

* `accept`的`callback`默认值`() =>  {}`,以防止`hot.accept('')`
* AST分析树,如果第一个参数类型是`FunctionExpression`,添加上`/foo.js`作为第二个参数。

> 对AST树有兴趣的,可以仔细研究一下。`mozila`有AST类型文档。使用的包`@babel/parser`,类型包`'@babel/types'`,最后使用`magic-string`替换。



# 132 - 4ce94b6 v0.8.0

release v0.8.0



# 133 - d609620 changelog

# [0.8.0](https://github.com/vuejs/vite/compare/v0.7.0...v0.8.0) (2020-04-30)

### Features

- 构建时,允许配置`rollupPluginVueOptions` ([a0053a0](https://github.com/vuejs/vite/commit/a0053a0eccd2659da685427ac3057cf5b436df80))
- `process.env.NODE_ENV` ([d4ccd15](https://github.com/vuejs/vite/commit/d4ccd154f54f71fb02e746924f9811d3a0e61a8f))
- `hmr.accept`支持调用本身 ([30ab444](https://github.com/vuejs/vite/commit/30ab444bd28b47eec1cf070a3c41116e8e9c64be))



# 134 - b70cd66 [#24](https://github.com/vitejs/vite/pull/24) 语法调整

语法调整



# 135 - 7d5c099 [#26](https://github.com/vitejs/vite/pull/26) 修复错字

修复了在源代码中发现的一些错字



# 136 - 770e558 更新`rollup-plugin-vue` and `vue`包



![](./pkg.png)



# 137 - 7b126af fix `resolver ensurejs` check

`ensureJS`当有请求参数的时候尝试补充`.js`拓展名称,不存在则不做任何处理地返回。

```typescript
import { statSync } from 'fs'

requestToFile: (publicPath) => {
      let resolved: string | undefined
      for (const r of resolvers) {
        const filepath = r.requestToFile(publicPath, root)
        if (filepath) {
          resolved = filepath
          break
        }
      }
      if (!resolved) {
        resolved = defaultRequestToFile(publicPath, root)
      }
      resolved = ensureJs(resolved)
      return resolved
}

// 
const ensureJs = (id: string) => {
  // 去除所有参数  
  const cleanId = id.replace(queryRE, '')
  
  // 有参数的情况
  if (!/\.\w+$/.test(cleanId)) {
    // try to see if there is actually a corresponding .js file on disk.
    // if not, return the id as-is
    try {
      statSync(cleanId + '.js')
    } catch (e) {
      return id
    }
     
    // 添加js参数  
    const queryMatch = id.match(queryRE)
    const query = queryMatch ? queryMatch[0] : ''
    return cleanId + '.js' + query
  }
    
    
  return id
}
```

## statSync

同步版本的`fs.stat`,在此作用为查看`.js`文件是否存在,不存在则返回本身`id`。



# 138 - 704fb84 v0.8.1

release v0.8.1



# 139 - 5ec60b1 changelog

## [0.8.1](https://github.com/vuejs/vite/compare/v0.8.0...v0.8.1) (2020-04-30)

### Bug Fixes

- 解决`resolver` 里的 `ensurejs` 检测问题 ([3a3442f](https://github.com/vuejs/vite/commit/3a3442f0b95873dd2a6869b00d8ac19b74d650a3))



# 140 - 54366c6 readme 接下来要做的事情

## TODOs

- 支持`import` `.css` 和 `.json`
- 公共路径处理(话说这个我不知道是什么... `Public path`我认为是请求路径,需要处理不同的类型,现在仅支持`js`)
- 支持`config`配置文件
- 自动引入`postcss config`文件
- `Vue`文件的`source map`



================================================
FILE: 141-150/commit-141-150.md
================================================
# 141 - 6125ee9 拼写错误[#27](https://github.com/vitejs/vite/pull/27)

[#27](https://github.com/vitejs/vite/pull/27)



# 142 - d27944c 拆分测试[#28](https://github.com/vitejs/vite/pull/28)

拆分测试功能,方便后续使用`test.skip`来进行TDD。

```typescript
describe('my suite', () => {
  test('my only true test', () => {
    expect(1 + 1).toEqual(2);
  });
  // Should fail, but isn't even run
  test.skip('my only true test', () => {
    expect(1 + 1).toEqual(1);
  });
});
```

> TDD是**测试驱动开发**(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于[XP](https://baike.baidu.com/item/XP/776028)(Extreme Programming),同样可以适用于其他开发方法和过程。



# 143 - f164e46 update rollup-plugin-vue

![](./pkg.png)



# 144 - 3ff579c vite命令支持布尔值

支持使用`vite`配置` --flag=false`。

```typescript
// bin/vite.js

Object.keys(argv).forEach((key) => {
  if (argv[key] === 'false') {
    argv[key] = false
  }
})
```



# 145 - 2677c93 关闭src转换

编译vue文件中的`<template>`,关闭` trasnformAssetUrls`选项。

https://vue-loader.vuejs.org/zh/options.html#transformasseturls

```typescript
// Transform asset urls found in the template into `require()` calls
  // This is off by default. If set to true, the default value is
  // {
  //   audio: 'src',
  //   video: ['src', 'poster'],
  //   source: 'src',
  //   img: 'src',
  //   image: ['xlink:href', 'href'],
  //   use: ['xlink:href', 'href']
  // }
transformAssetUrls?: AssetURLOptions | boolean
```

## 深入了解一下

我们查看关闭前的template转换成了什么?

```typescript
import { createVNode as _createVNode, toDisplayString as _toDisplayString, resolveComponent as _resolveComponent, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from "/@modules/vue"
import _imports_0 from './assest/pkg.png'


const _hoisted_1 = _createVNode("img", { src: _imports_0 }, null, -1 /* HOISTED */)

export function render(_ctx, _cache) {
  const _component_Child = _resolveComponent("Child")

  return (_openBlock(), _createBlock(_Fragment, null, [
    _hoisted_1,
    _createVNode("button", {
      class: "foo",
      onClick: _cache[1] || (_cache[1] = $event => (_ctx.count++))
    }, _toDisplayString(_ctx.count) + "123", 1 /* TEXT */),
    _createVNode(_component_Child)
  ], 64 /* STABLE_FRAGMENT */))
}
```

报错:

> pkg.png:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "image/png". Strict MIME type checking is enforced for module scripts per HTML spec.

意味着我们不能使用`import`一个`image/png`类型的文件。



我们查看关闭后:

```typescript
import { createVNode as _createVNode, toDisplayString as _toDisplayString, resolveComponent as _resolveComponent, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from "/@modules/vue"

const _hoisted_1 = _createVNode("img", { src: "./assest/pkg.png" }, null, -1 /* HOISTED */)

export function render(_ctx, _cache) {
  const _component_Child = _resolveComponent("Child")

  return (_openBlock(), _createBlock(_Fragment, null, [
    _hoisted_1,
    _createVNode("button", {
      class: "foo",
      onClick: _cache[1] || (_cache[1] = $event => (_ctx.count++))
    }, _toDisplayString(_ctx.count) + "123", 1 /* TEXT */),
    _createVNode(_component_Child)
  ], 64 /* STABLE_FRAGMENT */))
}
```

一切恢复正常,图片可以正常显示了。

为什么会有transformAssetUrls这个选项?是webpack需要使用到的,转换后webpack会再经过自身插件的转换。

> 啊哈,所以webpack就是没vite快!



# 146 - 93167d6 调整todo 新增相对路径处理

## TODOs

- 相对路径和基本公共路径处理
- 自动加载`post css`配置
- 支持 `.css` 和 `.json`
- 配置文件支持 (custom import maps)
- Vue file source maps

是不是尤大看到资源处理还没处理,需要解决相对路径的处理?



# 147 - 9af9ec1 客户端支持wss [#31](https://github.com/vitejs/vite/pull/31)

检测`location.protocol`是不是`https`,是则自动开启`wss`

> WS(WebSocket )是不安全的 ,容易被窃听,因为任何人只要知道你的ip和端口,任何人都可以去连接通讯。
> WSS(Web Socket Secure)是WebSocket的加密版本。

[443端口和80端口](https://zhuanlan.zhihu.com/p/99950177)

> 网络端口:计算机与外界通讯交流的出口

# 为什么`node`服务不用任何设置就可以使用wss?

![](./yyx.png)

尤大说,目前还不支持`wss`。

[【NODE】用WS模块创建加密的WS服务(WSS)](https://luojia.me/2015/07/21/%E3%80%90node%E3%80%91%E7%94%A8ws%E6%A8%A1%E5%9D%97%E5%88%9B%E5%BB%BA%E5%8A%A0%E5%AF%86%E7%9A%84ws%E6%9C%8D%E5%8A%A1wss/)

> 和https设置是一样的

## 最后被回滚了

![yyx2](./yyx2.png)

原因使用https也不会使用wss服务(??htttps必须使用wss的)。

关于这个我十分懵逼... 等`--https`支持了,后续再看看吧。



# 148 - 5d7ac46 处理相对资源路径+base64支持

## package.json

```json
{
+    "koa-send": "^5.0.0"
}
```

新增静态文件服务中间件。

[koa-send是什么](https://www.cnblogs.com/jiasm/p/9527536.html)

## node/utils.ts

新增`isStaticAsset`检测是否属于(常见)静态文件的方法。

```typescript
export const scriptRE = /<script\b[^>]*>([\s\S]*?)<\/script>/gm

const imageRE = /\.(png|jpe?g|gif|svg)(\?.*)?$/
const mediaRE = /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/
const fontsRE = /\.(woff2?|eot|ttf|otf)(\?.*)?$/i

export const isStaticAsset = (file: string) => {
  return imageRE.test(file) || mediaRE.test(file) || fontsRE.test(file)
}
```

## node/resolveVue.ts

区分浏览器版本(browser)和构建版本(bundler)的`vue`。

> browser是一个完整性的vue包(单文件),bundler是一个带有import的包(就是不会把所有东西都打包成一个文件,但是会从import引入)。
>
> 两者都包含编译器。

```typescript
interface ResolvedVuePaths {
  browser: string // esm.browser路径
  bundler: string // esm.bundler路径
  version: string // require('vue/package.json').version
  hasLocalVue: boolean // 是否有本地vue,没有则表示其他的路径均从vite的依赖获取
  compiler: string
  cdnLink: string
}
```

## node/buildPluginAsset.ts 新增base64

对于`css`里面的`url("")`,打包的静态资源除了`svg`以外,都会被转换成`base64`。

```typescript
export const getAssetPublicPath = async (id: string, assetsDir: string) => {
  const ext = path.extname(id)
  const baseName = path.basename(id, ext)
  const resolvedFileName = `${baseName}.${hash_sum(id)}${ext}`

  let url = slash(path.join('/', assetsDir, resolvedFileName))
  const content = await fs.readFile(id)
  if (!id.endsWith(`.svg`)) {
    if (content.length < inlineThreshold) {
      url = `data:${mime.lookup(id)};base64,${content.toString('base64')}`
    }
  }

  return {
    content,
    fileName: resolvedFileName,
    url
  }
}
```

## node/serverPluginServeStatic.ts

引入`resolver`,用于处理用户自定义路径(`resolver.requestToFile(ctx.path)`)。

```typescript
// 新增
app.use((ctx, next) => {
    const redirect = resolver.requestToFile(ctx.path)
    if (!redirect.startsWith(root)) {
      // resolver解析项目根目录之外的文件,
      // 手动发送到此处
      return send(ctx, redirect, { root: '/' })
    }
    return next()
})
```

### 为什么有了koa-static还要用send?

啊哈,主要是因为`send`好用。

在这里的作用是处理`koa-static`没有的服务,比如项目外的路径。

## node/serverPluginVue.ts

# [3.0.0-beta.9](https://github.com/vuejs/vue-next/compare/v3.0.0-beta.8...v3.0.0-beta.9) (2020-05-04)

把`transformAssetUrl: false`更改为`transformAssetUrlsBase: path.posix.dirname(publicPath)`。

然后我寻找`transformAssetUrlsBase`,`vue-next beta.9`的时候被合并到`transformAssetUrl`,也意味着后续`vite`会修改过来(雀氏没有了)。

**这块不是为了build的时候可以转换为Import资源,而是交给rollup-plugin-vue处理的,留坑MARK**。

### Bug Fixes

- **compiler:** bail strigification on runtime constant expressions ([f9a3766](https://github.com/vuejs/vue-next/commit/f9a3766fd68dc6996cdbda6475287c4005f55243))
- **transitionGroup:** fix transition children resolving condition ([f05aeea](https://github.com/vuejs/vue-next/commit/f05aeea7aec2e6cd859f40edc6236afd0ce2ea7d))

### Features

- **compiler-sfc:** support transforming absolute asset urls ([6a0be88](https://github.com/vuejs/vue-next/commit/6a0be882d4ce95eb8d8093f273ea0e868acfcd24))

### BREAKING CHANGES

- **compiler-sfc:** `@vue/compiler-sfc`'s `transformAssetUrlsBase` option has been removed. It is merged into `trasnformAssetUrls` which now also accepts the format of

  ```typescript
  {
    base?: string
    includeAbsolute?: string
    tags?: { [name: string]: string[] }
  }
  ```

## 关于构建的改动(新增资源处理、整理plugins)

#### 新增4个文件(`rollup-plugin`):

* `node/buildPluginAsset.ts`: 修改符合`isStaticAsset`的文件为`hash`路径,并存入一个Map,后续使用`generateBundle`改写对应`hash`路径的`source`。

  ```typescript
  import a from 'a.png'
  
  // a.png 被转换为
  export default 'a.bf716c.png'
  
  // 后续a.bf716c.png,bundle修改为资源类型
  bundle['a.bf716c.png'] = {
      isAsset: true,
      type: 'asset',
      fileName,
      source: await fs.readFile('a.png')
  }
  ```

* `node/buildPluginCss.ts`:  对应`build`旧的`cssExtractPlugin`,`postcss`处理后输出`bundle`资源。

* `node/buildPluginHtml.ts`: 把旧的`vitePlugin`抽离处理`html`功能独立出来,分析`<script>`,返回标签内容,入口的处理。

* `buildPluginResolve.ts`: 旧的`vitePlugin`,处理vue包,`resolver`路径转换(在这里处理了,后续的`plugins`不用单独处理)。

#### 单一出口。

去除`MultiBuildOptions`,意味着之前封装成的`generator`方法又被回滚了,出口只能有一个。

```typescript
// 已被去除
interface MultiBuildOptions extends BuildOptionsBase {
  rollupOutputOptions?: OutputOptions[]
}
```



# 149 - 756da62 bump vue version

![](./bump.png)



# 150 - f29037d 处理使用css,import css文件

注意哈,处理`css`是交给`rollup-plugin-vue`处理的,[使用`transform`处理`css`](https://github.com/vuejs/rollup-plugin-vue/blob/next/src/index.ts),这个和我们`dev`环境下是一样的。

这里做的事情是**根据内容与文件名称生成一个`bundle`**,其次把公用的方法都给抽离出来了,属于整理代码。

```typescript
 // vite:css
 createBuildCssPlugin(assetsDir, cssFileName, minify)
```

> 所有vue组件里面的style,都会被丢进style.css这个bundle。

## rollup其实充当了什么?

**rollup分析路径**,通过**钩子**可以转换路径、更改路径下的内容,最后自己定义产出`bundle`。

`vite`再把`bundle`转换成文件。



================================================
FILE: 151-160/151-160.md
================================================
# 151 - 63b4de6 处理`<script src`

1. 构建模式下,使用正则匹配出`src`里面的内容`<script src="main.js">`

```typescript
const srcRE = /\bsrc=(?:"([^"]+)"|'([^']+)'|([^'"\s]+)\b)/

// `src="main.js"`.match(srcRE) -> main.js
// 'src=main.js'  .match(srcRE) -> main.js
// `src='main.js'`.match(srcRE) -> main.js
```

2. 添加import "main.js"
3. 与`<script>`里的内容一起合并后返回



# 152 - 8ef6d4d 支持能分析按需加载类型`import` id

## node/serverPluginModuleRewrite.ts

```typescript
imports.forEach(({ s: start, e: end, d: dynamicIndex }) => {
    let id = source.substring(start, end)
    if (dynamicIndex >= 0) {
          console.log(id)
          
        
          const literalIdMatch = id.match(/^(?:'([^']+)'|"([^"]+)")$/)
          console.log(literalIdMatch)
          /*
          [
            "'./foo.js'",
            './foo.js',
            undefined,
            index: 0,
            input: "'./foo.js'",
            groups: undefined
          ]
           */
        
        
          if (literalIdMatch) {
            hasLiteralDynamicId = true
            id = literalIdMatch[1] || literalIdMatch[2]
          }
    }
}
```

## 什么是按需加载?

```typescript
import('./foo.js').then(mod => {
  console.log(mod.default)
})
```

## 和直接import有什么不一样?

AST中返回的id会多了引号。

## bug

`transformAssetUrlsBase: path.posix.dirname(publicPath)`导致了错误,这部分代码已回滚到
` transformAssetUrls: false`



# 153 - 97dc7ba 支持`json`格式的引入

## 构建方面

引入新的rollup插件:`@rollup/plugin-json`

## dev方面

实际上`json`文件默认静态资源用`serverPluginServeStatic.ts`来处理,新增`serverPluginJson.ts`,目的是改写成对象。

```typescript
import { Plugin } from './server'
import { readBody } from './utils'

export const jsonPlugin: Plugin = ({ app }) => {
  app.use(async (ctx, next) => {
    await next()
      
    // handle .json imports
    if (ctx.path.endsWith('.json')) {
      const referer = ctx.get('referer')
      
      console.log(referer)
      // http://localhost:3000/Comp.vue 
      
      // 仅在请求页面不为.html才改写
      if (/\.\w+$/.test(referer) && !referer.endsWith('.html')) {
        ctx.type = 'js'
        ctx.body = `export default ${await readBody(ctx.body)}`
      }
    }
  })
}
```

## `json`文件有`hmr`吗?

不支持,得手动刷新页面,想支持还是使用`js`对象吧。



# 154 - 67b82dc 调整`node/utils.ts`

新增`isImportRequest`,该方法是在`serverPluginServeStatic.ts`中抽离出来的。

```typescript
export const isImportRequest = (ctx: Context) => {
  const referer = ctx.get('referer')
  return /\.\w+$/.test(referer) && !referer.endsWith('.html')
}
```

暴露所有`utils.ts`的方法。

```typescript
# node/index.ts
export * from './server'
export * from './build'

// before export { cachedRead, isStaticAsset } from './utils'
export * from './utils'
```



# 155 - a3bb973 支持在`js`中使用`import css`文件

新增`serverPluginCss.ts`。

1. 处理`.css`后缀文件,且不带参数`raw`

2. 转换成`js`

3. 被转换的`js`文件调用`updateStyle`

4. 更换`link`(`updateStyle`做的事情,之前的文章有讲解)

   ```typescript
   function updateStyle(id: string, url: string) {
     const linkId = `vite-css-${id}`
     let link = document.getElementById(linkId)
     if (!link) {
       link = document.createElement('link')
       link.id = linkId
       link.setAttribute('rel', 'stylesheet')
       link.setAttribute('type', 'text/css')
       document.head.appendChild(link)
     }
     link.setAttribute('href', url)
   }
   ```

   

```typescript
import { Plugin } from './server'
import { isImportRequest } from './utils'
import { hmrClientId } from './serverPluginHmr'
import hash_sum from 'hash-sum'

export const cssPlugin: Plugin = ({ app }) => {
  app.use(async (ctx, next) => {
    await next()
    // handle .css imports
    // we rewrite it to JS that injects a <style> tag pointing to the same url
    // but with a `?raw` query which returns the actual css
    if (
      ctx.path.endsWith('.css') &&
      isImportRequest(ctx) &&
      // note ctx.body could be null if upstream set status to 304
      ctx.body &&
      // skip raw requests
      !ctx.query.raw
    ) {
      ctx.type = 'js'
      const id = JSON.stringify(hash_sum(ctx.path))
      const rawPath = JSON.stringify(ctx.path + '?raw')
      ctx.body = `
import { updateStyle } from "${hmrClientId}"\n
updateStyle(${id}, ${rawPath})
`.trim()
    }
  })
}
```



# 156 - 538198c 支持`import css hmr`功能

## `node/serverPluginCss.ts`

```typescript
// handle hmr
  watcher.on('change', (file) => {
    if (file.endsWith('.css')) {
      const publicPath = resolver.fileToRequest(file)
      const id = hash_sum(publicPath)
      watcher.send({
        type: 'style-update',
        id,
        path: publicPath,
        timestamp: Date.now()
      })
    }
  })
```

## `hmr`事件类型

更名:
`vue-style-remove` -> `style-remove`

``vue-style-update` -> `style-update`

```typescript
interface HMRPayload {
  type:
    | 'vue-rerender'
    | 'vue-reload'
    | 'vue-style-update'
    | 'js-update'
    | 'style-update'
    | 'style-remove'
    | 'full-reload'
    | 'custom' // 用户自定义事件 这个我们还没有深入应用
  timestamp: number
  path?: string
  id?: string
  index?: number
  customData?: any
}
```



# 157 - 80c5e00 v0.9.0

release v0.9.0



# 158 - 4b64c06 changelog

# [0.9.0](https://github.com/vuejs/vite/compare/v0.8.0...v0.9.0) (2020-05-03)

### Bug Fixes

- 去除trasnformAssetUrls ([2677c93](https://github.com/vuejs/vite/commit/2677c934fdeccf8d4a2b0a6f174ee55ab001b25a))(去除了出BUG)
- 修复resolver ensurejs 检测([7b126af](https://github.com/vuejs/vite/commit/7b126af193459da777fa0ca581e8f31d163541fa))

### Features

- 处理index.html的 `<script src>`  ([63b4de6](https://github.com/vuejs/vite/commit/63b4de6405e5a2e1375f8360420c7cd11fdcd665))
- 处理 js css import hmr ([538198c](https://github.com/vuejs/vite/commit/538198c8ec795d0030a0a11c076d717a26f389a9))
- 处理相对路径的资源 ([5d7ac46](https://github.com/vuejs/vite/commit/5d7ac468091adf2d6809e6a735990bf20b28de87))
- 处理css内的相对urls + base64支持([f29037d](https://github.com/vuejs/vite/commit/f29037d536de415ee115d5a48ec7a7e2b785656e))
- 支持从js里面引入css([a3bb973](https://github.com/vuejs/vite/commit/a3bb973a3c593d25ebcf74eee7b1345c4a844e9f))
- 支持引入json文件,作为对象输出([97dc7ba](https://github.com/vuejs/vite/commit/97dc7ba8e1d77f63dd1cecfc08f2bb513b3a708f))
- 支持输入 --flag=false via cli ([3ff579c](https://github.com/vuejs/vite/commit/3ff579c7de84787d2533ae0f1e2695900949e7d9))
- 支持dynamic imports ([8ef6d4d](https://github.com/vuejs/vite/commit/8ef6d4d12b5fc75b137fed7258114a2c5a17101c))
- ws protocol based on location protocol ([#31](https://github.com/vuejs/vite/issues/31)) ([9af9ec1](https://github.com/vuejs/vite/commit/9af9ec1694f1c5c09c5ce46f81b62af175997b25))(回滚)



# 159 - 5efb82d readme更新todo

## TODOs

- 自动加载`post css`配置
- 配置文件支持 (custom import maps)
- Vue file source maps

## 去除

* 相对路径和基本公共路径处理
* 支持 `.css` 和 `.json`



# 160 - a83637e 解决html响应体为null的问题

## `node/utils.ts` 没有流的情况下返回`null`

```typescript
export async function readBody(
  stream: Readable | Buffer | string | null
): Promise<string | null> {
  if (stream instanceof Readable) {
    return new Promise((resolve, reject) => {
      let res = ''
      stream
        .on('data', (chunk) => (res += chunk))
        .on('error', reject)
        .on('end', () => {
          resolve(res)
        })
    })
  } else {
    return !stream || typeof stream === 'string' ? stream : stream.toString()
  }
}
```

================================================
FILE: 161-170/161-170.md
================================================
# 161 - e947303 v0.9.1

release v0.9.1



# 162 - bf1abbf changelog

## [0.9.1](https://github.com/vuejs/vite/compare/v0.9.0...v0.9.1) (2020-05-03)

### Bug Fixes

- 现在`readBody`方法可以返回为空的内容了 ([a83637e](https://github.com/vuejs/vite/commit/a83637e82c86df43edaf28e469bec6cbf6ad8b33))



# 163 - d0b896f fix `hmr`名称

`vue`的`hmr`: `style-update` -> `vue-style-update`

## `vue-style-update`和`style-update`有什么不一样?

```typescript
case 'vue-style-update':
      updateStyle(id, `${path}?type=style&index=${index}&t=${timestamp}`)
      console.log(
        `[vite] ${path} style${index > 0 ? `#${index}` : ``} updated.`
      )
      break
case 'style-update':
      updateStyle(id, `${path}?raw&t=${timestamp}`)
      console.log(`[vite] ${path} updated.`)

export function updateStyle(id: string, url: string) {
  const linkId = `vite-css-${id}`
  let link = document.getElementById(linkId)
  if (!link) {
    link = document.createElement('link')
    link.id = linkId
    link.setAttribute('rel', 'stylesheet')
    link.setAttribute('type', 'text/css')
    document.head.appendChild(link)
  }
  link.setAttribute('href', url)
}
```

可以看到并没什么不一样,区别的是名称与参数。

> 但是这里会造成vue hmr识别不了style,因为没有type=style。



# 164 - 348a7e8 fix `isImportRequest` 取`pathname`

优化,去除请求域名。

```typescript
export const isImportRequest = (ctx: Context) => {
  const referer = new URL(ctx.get('referer')).pathname
  return /\.\w+$/.test(referer) && !referer.endsWith('.html')
}
```

## 关于`referer`的

不知道有没有小伙伴和我一样误解`referer`是浏览器当前页面的路径。

如果页面A,有脚本B,脚本B请求脚本C,那么C脚本的`referer`是脚本B,利用这个特性,我们服务器就可以知道这个是脚本发出的请求还是页面发出的请求了。



# 165 - 634a432 `json`引入添加`hmr`

啊哈,之前就觉得`json`需要加入`hmr`,现在加入了。

## `node/serverPluginJson.ts`

```typescript
// 看,被转换成js文件的好处
watcher.on('change', (file) => {
    if (file.endsWith('.json')) {
      watcher.handleJSReload(file)
    }
})

// 然鹅 并不能用,看hmrPlugin设置的watcher
  watcher.on('change', async (file) => {
    const timestamp = Date.now()
    if (file.endsWith('.vue')) {
      handleVueReload(file, timestamp)
    } else if (file.endsWith('.js')) { // js文件
      handleJSReload(file, timestamp)
    }
  })
```

## `node/utils.ts` 添加`cleanUrl`方法

```typescript
export const cleanUrl = (url: string) =>
  url.replace(hashRE, '').replace(queryRE, '')
```

## `serverPluginModuleRewrite.ts` 对是否取缓存加入参数t的判断

作用为修复`js` `hmr`的时候,因请求`url`没有改变,浏览器将使用缓存,导致`js`的内容为旧的问题。

```typescript
import foo from 'foo.js?t=123'

// 将不会对foo.js里面的import进行缓存
```

### 再补充一下`serverPluginModuleRewrite.ts`的功能

`serverPluginModuleRewrite`在洋葱模型的最外层执行,就是执行完所有`plugin`后再执行。

改写对象:1. `vueSFC组件`的`<script>`内容    2. 普通`js`的内容。

当被改写对象含有参数`t`,则其脚本内的所有import都会带有参数`t`。

对于其中的`import`语句,如果符合`/^[^\/\.]/.test(id)`,即`node_modules`的模块引入,则在路径调整为 `/@modules/${id}`(如果设置了`resolver.idToRequest`,那么以配置为准)。

## BUG

无法使用`js`的`hmr`功能,和路径的设置有关,比如`key`是`file path`(且是一个错误的`file path`),却用了`public path`作为键,去取谁引入了该`js`模块。

出问题的地方为:

```
// save the import chain for hmr analysis
const importee = cleanUrl(
  slash(path.resolve(path.dirname(importer), resolved))
)

// importee 错误
// 如E:/foo/a.js importee却为E:/a.js
```

我为了验证出这里的加入t参数的目的,暂时手动添加了字符串修复了(**ε=( o`ω′)ノ!!!**)。

## BUG-2

应该加一个白名单,不单止`js`可以`hmr`。

```typescript
  watcher.on('change', async (file) => {
    const timestamp = Date.now()
    if (file.endsWith('.vue')) {
      handleVueReload(file, timestamp)
    } else if (file.endsWith('.js')) { // js文件
      handleJSReload(file, timestamp)
    }
  })
```

## 可能引起的问题

像BUG-2说的,添加了白名单,但需要解决名称相同的问题(`foo.json` `foo.js`,会引起其中一个无法`hmr`)。



# 166 - 96f0ee02 [#38](https://github.com/vitejs/vite/pull/38) 注释

修改`node/serverPluginHmr`注释。

Change the docs which `module` has been split to `serverPluginModuleRewrite` & `serverPluginModuleResolve`

`modulePlugin`被分为两个`plugins`了,所以要修改注释。



# 167 - f69e8f4 [#36](https://github.com/vitejs/vite/pull/36)  注释

修改英文单词。



# 168 - b5421e7 [#40](https://github.com/vitejs/vite/pull/40) 添加`json`和`css`的测试

```typescript
test('json data import', async () => {
    const jsonComp = await page.$('.json')
    expect(await jsonComp.evaluate((e) => e.textContent)).toBe('hello world')
})

test('import plain css', async () => {
    const child = await page.$('.child')
    const color = await child.evaluate((e) => {
      return window.getComputedStyle(e).color
    })
    expect(color).toBe('rgb(79, 192, 141)')
})

test('style hmr', async () => {
    const stylePath = path.join(tempDir, 'main.css')
    const content = await fs.readFile(stylePath, 'utf-8')
    await fs.writeFile(
      stylePath,
      content.replace('color: #4fc08d', 'color: red')
    )

    const child = await page.$('.child')
    testByPolling('rgb(255, 0, 0)', () => {
      return child.evaluate((e) => getComputedStyle(e).color)
    })
})

// 轮询直到更新
async function testByPolling(expect, poll) {
  const maxTries = 10
  for (let tries = 0; tries < maxTries; tries++) {
    const actual = await poll()
    if (actual === expect || tries === maxTries - 1) {
      expect(actual).toBe(expect)
    } else {
      await timeout(200)
    }
  }
}
```



# 169 - d271e59 [#41](https://github.com/vitejs/vite/pull/41)加载`postcss config`

传递选项给`compileStyleAsync`处理。

https://www.npmjs.com/package/postcss-load-config

```typescript
{
+    "postcss-load-config": "^2.1.0"
}
```

## `node/serverPluginVue.ts`

```typescript
import postcssrc from 'postcss-load-config'

const result = await resolveCompiler(root).compileStyleAsync({
    source: style.content,
    filename: filePath,
    id: `data-v-${id}`,
    scoped: style.scoped != null,
    modules: style.module != null,
    preprocessLang: style.lang as any,
    preprocessCustomRequire: (id: string) => require(resolve(root, id)),
    ...loadPostCssConfig(root)
})

function loadPostCssConfig(root: string) {
  const config = postcssrc.sync({}, root)
  return {
    postcssOptions: config.options,
    postcssPlugins: config.plugins
  }
}
```



# 170 - 679e414 更新`postcss`

同时更新`lockfile`。什么是`lockfile`呀?[`package.json`版本号](https://zhuanlan.zhihu.com/p/384484213)。

^表示允许不修改`[major, minor, patch]`元组中最左边的非零元素的更改 。

所以^7.0.27 := >=7.0.27 < 8.0.0。

换句话说,有些人`yarn install`会有不同的版本出现。

> ## `Lockfile`的作用
>
> 1、确保每次install时生成稳定的依赖树,锁定依赖和依赖的依赖的版本。
>
> 2、提升install的速度。`yarn`和`npm`都有一些诸如适配和提取公共依赖版本、**扁平化依赖的优化策略**,`lockfile`的存在可节省计算时间。
>
> ### package-lock.json
>
> npm从5.0版本之后默认增加lockfile,但是早期不同版本对lockfile的实现有过变更:
>
> 1、[5.0.x版本](https://link.zhihu.com/?target=https%3A//github.com/npm/npm/releases/tag/v5.0.0),不管package.json怎么变,install时都会根据lock文件下载。
>
> 2、[5.1.0版本](https://link.zhihu.com/?target=https%3A//github.com/npm/npm/releases/tag/v5.1.0)后,npm install会无视lock文件,去下载最新的npm包。
>
> 3、[5.4.2版本](https://link.zhihu.com/?target=https%3A//github.com/npm/npm/releases/tag/v5.4.2)后,表现和yarn.lock一致。
>
> 转载自[知乎](https://zhuanlan.zhihu.com/p/260094037)

```json
{
-    "postcss": "^7.0.27",
+    "postcss": "^7.0.28",
}
```

## 有没有支持`config hmr`的可能呢?

已经支持啦,代码是动态的。

```typescript
postcssrc.sync({}, root)
```



================================================
FILE: 171-180/171-180.md
================================================
# 171 - 0187d3f 对于`js`引入的`css`也要支持`postcss config` 与 `postcss`

## `node/config.ts`

抽离`serverPluginVue.ts`的加载`postcss config`功能。

```typescript
import postcssrc from 'postcss-load-config'

// postcss-load-config doesn't expose Result type
type Result = ReturnType<typeof postcssrc> extends Promise<infer T> ? T : never

let cachedPostcssConfig: Result | null | undefined

export async function loadPostcssConfig(root: string): Promise<Result | null> {
  try {
    return (
      cachedPostcssConfig || (cachedPostcssConfig = await postcssrc({}, root))
    )
  } catch (e) {
    return (cachedPostcssConfig = null)
  }
}

```

[用null还是用undefinded](https://www.zhihu.com/question/479435433/answer/2057762335)

# `node/serverPluginCss.ts`

因为之前是通过`style-update`事件来支持`js`里引入`css`的,使用的是静态文件服务。现在我们要加载`postcss config`与`postcss`,就需要拦截该请求做处理了。

```typescript
// plain css request, apply postcss transform
        const postcssConfig = await loadPostcssConfig(root)
        if (postcssConfig) {
          const css = await readBody(ctx.body)
          try {
            const result = await require('postcss')(
              postcssConfig.plugins
            ).process(css, {
              from: resolver.requestToFile(ctx.path),
              ...postcssConfig.options
            })
            ctx.body = result.css
          } catch (e) {
            console.error(`[vite] error applying postcss transforms: `, e)
          }
        }
```

> `koa-static`已经做了处理,所以才会有`ctx.body`

> 目前对于构建来说,是不支持`postcss config`的。

## BUG 

处理`css`请求,只拦截了`.css`文件,但没有拦截`.sass`文件。



# 172 - c9c9c87c 添加`vue source map`功能 + todo

### 新增功能点

1. 提前添加`@hmr`,在client请求`.html`文件的时候,被合并到`<script>`中。
2. 去除处理`.vue`文件`plugin`的`@hmr`。
3. 对`SFCMain`和`SFCTemplate`添加`sourcemap`。

## 关于`soucemap`

[什么是sourcemap?](https://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html)

交给`vue.compiler.parse`处理,并开启`souceMap`选项,得到`descriptor.script.map`和`descriptor.template.map`。

`descriptor.template.map`需要再经过`vue.compileTemplate`处理,最后才可以获得`map`。

最后两者的map经过`genSourceMapString`转换为字符串数据,并插入到代码中。

> `soucemap`简单地说,就是收集了原本代码的所有变量名和属性名所在的位置。所以我们可以用`soucemap`还原成文件本身。

### 怎么使用`soucemap`呢?

报错的时候,或者debugger的时候可以查看到`soucemap`。

```typescript
function genSourceMapString(map: object | undefined) {
  if (!map) {
    return ''
  }
  return `\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(
    JSON.stringify(map)
  ).toString('base64')}`
}
```

> 对于构建还不支持`soucemap`功能。



# 173 - 8f6aa19 修复`hmr id`

修复对于添加global变量的代码,应用了错误的变量的问题。

```typescript
const devInjectionCode =
    `\n<script type="module">` +
    `import "${hmrClientPublicPath}"\n` + // 之前不小心用到了hmrClientId
    `window.__DEV__ = true\n` +
    `window.process = { env: { NODE_ENV: 'development' }}\n` +
    `</script>\n`

```

```html
<!-- 前 -->
<script type="module">import "@hmr"
window.__DEV__ = true
window.process = { env: { NODE_ENV: 'development' }}
</script>

<!-- 后 -->
<script type="module">import "/@hmr"
window.__DEV__ = true
window.process = { env: { NODE_ENV: 'development' }}
</script>
```

提前引入客户端的`hmr`,为了提高一点点的速度(之前是放在`vue SFC`里的)。



# 174 - ea5eb19 添加服务启动时间

我发现`vite`很多延迟加载模块的,有利性能。

```typescript
# bin/vite.js
const s = Date.now()

require('debug')('vite:server')(`server ready in ${Date.now() - s}ms.`)
```



# 175 - 1e8b584 延迟加载`postcss-load-config`

正如172所说的延迟加载。

```typescript
export async function loadPostcssConfig(root: string): Promise<Result | null> {
  if (cachedPostcssConfig !== undefined) {
    return cachedPostcssConfig
  }
  try {
    const load = require('postcss-load-config') as typeof postcssrc
    return (cachedPostcssConfig = await load({}, root))
  } catch (e) {
    return (cachedPostcssConfig = null)
  }
}
```



# 176 - c9ffb45 build支持`postcss config`

## `node/buildPluginCss`

与`serverPluginCss`转换`raw`参数的`css`文件的方式一致。

```typescript
 // postcss
        const postcssConfig = await loadPostcssConfig(root)
        if (postcssConfig) {
          try {
            const result = await require('postcss')(
              postcssConfig.plugins
            ).process(code, {
              ...postcssConfig.options,
              from: id
            })
            code = result.css
          } catch (e) {
            console.error(`[vite] error applying postcss transforms: `, e)
          }
        }
```



# 177 - b612a34 v0.10.0

release v0.10.0



# 178 - changelog

# [0.10.0](https://github.com/vuejs/vite/compare/v0.9.1...v0.10.0) (2020-05-04)

### Bug Fixes

- 修复isImportRequest获取referer的问题([348a7e8](https://github.com/vuejs/vite/commit/348a7e88e4cd104b110eb6296f5a18fdff351d32))
- 修复 vue style hmr ([d0b896f](https://github.com/vuejs/vite/commit/d0b896fde6502298cf8ef6c1a8bb79c8d9b1963d)), 关闭 [#37](https://github.com/vuejs/vite/issues/37)

### Features

- 加载自定义 postcss 配置 ([#41](https://github.com/vuejs/vite/issues/41)) ([d271e59](https://github.com/vuejs/vite/commit/d271e594a14d5c941d96d1189ffb3b7aee994f2e))
- 支持 json hmr ([634a432](https://github.com/vuejs/vite/commit/634a4328041434434260844cf8fa95d0c3340f85))
- 对于`js`引入的`css`支持`postcss config` 与 `postcss` ([0187d3f](https://github.com/vuejs/vite/commit/0187d3f525fd76fa9855284b23836f4c3b68952a))
- 在构建中支持 postcss ([c9ffb45](https://github.com/vuejs/vite/commit/c9ffb452133abc65067167e0895627703dcaeb5b))
- vue source map ([c9c9c87](https://github.com/vuejs/vite/commit/c9c9c87c855994e2f307475353c1cbb7bf9cc46a))

### Performance Improvements

- 延迟加载 postcss-load-config ([1e8b584](https://github.com/vuejs/vite/commit/1e8b58403e83b0835ee136de7e5c9f7f0adf03f0))



# 179 - 6b36f91 bump `create-vite-app` dep versions

更新`create-vite-app`所依赖的`vite`版本。

> bump: 将版本号增加到新的唯一值。



# 180 - 2600104 `create-vite-app v1.0.0`

release `create-vite-app` v1.0.0



================================================
FILE: 181-190/181-190.md
================================================
# 181 -  adea1a3 bump `create-vite-app` vue

bump vue



# 182 - 4b5d180 更新文档 - feature

### CSS / JSON Importing

你可以直接从 JavaScript 导入 `.css` 和 `.json` 文件 (当然,包括 `*.vue` 文件的 `<script>` 标签).

- `.json` 文件将作为对象导出。
- `.css`文件不会导出任何东西。导入后,在开发模式中将会被注入页面

 对于CSS 和 JSON 的导入,都支持HMR。

### 相对资源的URL处理

你可以在你的 `*.vue` 模板中引用静态资源。样式和存`.css` 文件,使用基于资源在文件系统上的位置的相对 URL。这类似于你使用 `vue-cli` 或 webpack 的 `file-loader`。

在生产版本中,引用的资源将被复制到带有散列文件名的 dist 文件夹中。

### PostCSS

`vite` 会在所有`*.vue`文件和纯`*.css`文件中应用Post CSS配置。你只需安装必要的插件,并在你的项目根目录中添加一个 `postcss.config.js`。

请注意,如果你想在 `*vue` 文件中使用 `<style module>`,你**不需要**配置 PostCSS,因为它是开箱即用的。

### CSS Pre-Processors

因为 `vite` 仅针对现代浏览器, 建议在 PostCSS 插件中使用原生 CSS ,它引入了CSSWG草案功能 (e.g. [postcss-nesting](https://github.com/jonathantneal/postcss-nesting))和编写简单的、符合未来标准的 CSS。 也就是说,如果你坚持使用 CSS 预处理器,你可以安装并使用:

```bash
yarn add -D sass
<style lang="scss">
/* use scss */
</style>
```

注意,目前不支持通过`.js`文件来引入CSS/预处理器, 但将来可能会得到支持。



# 183 - 879c829 readme 更新todo

## TODOs

- 公共基础路径支持。(这是啥...)
- 支持配置文件 (custom import maps and plugins)
- 支持TypeScript / Flow /(P)React JSX via [Sucrase](https://github.com/alangpierce/sucrase)



# 184 - c2f01f6 补充浏览器支持情况

## Browser Support

`vite` 在开发模式中使用 [native ES module imports](https://caniuse.com/#feat=es6-module) . 生产构建还依赖于动态导入进行代码拆分 (你可以使用 [polyfilled](https://github.com/GoogleChromeLabs/dynamic-import-polyfill))。

`vite` 假设你的目标是现代浏览器,因此默认情况下不会执行任何面向兼容性的代码转换。从技术角度来说,你可以通过`PostCss`配置文件添加`autoprefixer`, 或者添加必要的 `polyfills` 和`post-processing`以使你的代码能在旧版浏览器中工作,但这不是 `vite` 所关心的。

[autoprefixer配置详解](https://segmentfault.com/a/1190000023960072)



# 185 - c57c3ff chore 单词错误

文档单词修改(没影响,我在184纠正了过来了)。



# 186 - f80ad1c bump node.js

要求最低的支持版本为`node >= 12.0.0`。

[node 12特性](https://nodejs.medium.com/introducing-node-js-12-76c41a1b3f3f)

> 后续我也会关注一下 为什么要用node 12



# 187 - 586626f [#46](https://github.com/vitejs/vite/pull/46) 解决没有`<script>`崩溃的问题

添加`\n`就好了。

```typescript
const __script = {}import { updateStyle } from "/@hmr"

// 实际上需要的是
const __script = {}
import { updateStyle } from "/@hmr"
```



# 188 - e67e0e6 [#47](https://github.com/vitejs/vite/pull/47) 修复test testByPolling 还有`expect`命名冲突

调用`testByPolling`需要添加`await`。`testByPolling`的参数`expect`需要更名,因为这是`js`的关键词。



# 189 - 4807205 [#48](https://github.com/vitejs/vite/pull/48)chore 修复单词

 `recomend` -> `recomended`



# 190 - ef28ee4 [#49](https://github.com/vitejs/vite/pull/49)构建的时候,关闭`write`后,不应该写入静态资源

原因是缺少`wirte`判断,写漏了。

```typescript
else if (emitAssets && write) {
      // write asset
      const filepath = path.join(resolvedAssetsPath, chunk.fileName)
      !silent &&
        console.log(
          `write ${chalk.magenta(path.relative(process.cwd(), filepath))}`
        )
      await fs.mkdir(path.dirname(filepath), { recursive: true })
      await fs.writeFile(filepath, chunk.source)
}
```



================================================
FILE: 191-200/191-200.md
================================================
# 191 - 72d73b1 [#50](https://github.com/vitejs/vite/pull/46) 修正readme链接

已在[182(4b5d180)](https://github.com/Kingbultsea/vite-analysis/blob/master/181-190/181-190.md#css-pre-processors)修复好。



# 192 - 7548509 bump vue dep

更新vue(^3.0.0-beta.8)和@vue/compiler-sfc(^3.0.0-beta.8)版本。



# 193 - b2d4307 v0.10.1

release v0.10.1



# 194 - 354cfad changelog

## [0.10.1](https://github.com/vuejs/vite/compare/v1.0.1...v0.10.1) (2020-05-04)

### Bug Fixes

- 没有script标签会导致程序崩溃 ([#46](https://github.com/vuejs/vite/issues/46)) ([586626f](https://github.com/vuejs/vite/commit/586626fb4099042abe1964700387ee6d0946d43b))
- `buildOptions.write·设置`false`后不应该再写入assets资源到磁盘中 ([#49](https://github.com/vuejs/vite/issues/49)) ([ef28ee4](https://github.com/vuejs/vite/commit/ef28ee44d690713666d2f9b656276324a0abcd42))

**logmsg**: chore: changelog [ci skip]

> 只要在commit信息中包含`[ci skip]`或 `[skip ci]` ,提交到仓库以后会跳过CI流程



# 195 - d93fea9 定义自动提交changlog的命令

**logmsg**: workflow: auto push changelog after publishing

> 类似这种流程定义的可以以workflow来作为commit msg的前缀

## package.json

```typescript
{
    scripts: {
        "postpublish": "git add CHANGELOG.md && git commit -m 'chore: changelog [ci skip]' && git push"
    }
}
```

尤大每次修改了changelog后,用的是命令行,手动输入git命令行,觉得十分麻烦,干脆简化加入package.json脚本。



# 196 - 1782f83 支持`*.module.css`

## 更新readme css modules描述

### CSS Modules

注意不需要配置PostCSS就可以使用`CSS Modules`: 这是开箱即用的。 在 `*.vue` 组件中你可以使用`<style module>`, 对于纯 `.css` 文件, 你需要命名CSS modules files为 `*.module.css` , allows you to import the naming hash from it.

## node/serverPluginCss.ts

使用`postcss-modules`包,获取到`json`文件,拼接并返回对象。

```typescript
let code =`import { updateStyle } from "${hmrClientId}"\n` +
          `updateStyle(${id}, ${rawPath})\n`
        if (ctx.path.endsWith('.module.css')) {
          code += `export default ${JSON.stringify(
            processedCSS.get(ctx.path)!.modules
          )}`
        }
        ctx.body = code.trim()
```

```typescript
async function processCss(ctx: Context) {
    let css = (await readBody(ctx.body))!
    let modules
    const postcssConfig = await loadPostcssConfig(root)
    const expectsModule = ctx.path.endsWith('.module.css')

    // postcss processing
    if (postcssConfig || expectsModule) {
      try {
        css = (
          await require('postcss')([
            ...((postcssConfig && postcssConfig.plugins) || []),
            ...(expectsModule
              ? [
                  require('postcss-modules')({
                    getJSON(_: string, json: Record<string, string>) {
                      modules = json
                    }
                  })
                ]
              : [])
          ]).process(css, {
            ...(postcssConfig && postcssConfig.options),
            from: resolver.requestToFile(ctx.path)
          })
        ).css
      } catch (e) {
        console.error(`[vite] error applying postcss transforms: `, e)
      }
    }

    processedCSS.set(ctx.path, {
      css,
      modules
    })
  }
```

### 删除缓存

检测到`*.module.css`的存在,就可以用`processedCSS.delete(publicPath)`删除了。

```typescript
// handle hmr
  watcher.on('change', (file) => {
    if (file.endsWith('.css')) {
      const publicPath = resolver.fileToRequest(file)
      const id = hash_sum(publicPath)

      // bust process cache
      processedCSS.delete(publicPath)

      if (file.endsWith('.module.css')) {
        watcher.handleJSReload(file)
      } else {
        watcher.send({
          type: 'style-update',
          id,
          path: publicPath,
          timestamp: Date.now()
        })
      }
    }
  })
```

## node/builPluginCss.ts

如果遇到`*.module.css`,像server端口那样,返回`export default ${JSON.stringify(modules)}`,改动一致。



# 197 - dd7af0a fix(moduleResolve)不改写外链import

`dynamic import`是支持外链的。

## serverPluginModuleRewrite.ts

```typescript
// do not rewrite external imports
if (/https?:\/\//.test(id)) {
  return
}
```



# 198 - ccce482 修复打包后引用的路径不正确的问题

使用`InjectXXX`方法的时候,需要在路径上添加`assetsDir`。

```typescript
<link rel="stylesheet" href="style.css">
<div id="app"></div>
<script type="module" src="index.js"></script>

<!-- 修复后 -->
<link rel="stylesheet" href="/assets/style.css">
<div id="app"></div>
233
<script type="module" src="/assets/index.js"></script>
```



# 199 - 59c1ab1 文件改名

`node/resolveVue.ts` -> `node/vueResolver.ts`

中文理解,处理vue -> vue处理器。



# 200 - 5ca0ec4 修复资源路径

## 知识点

```typescript
"peerDependencies": {
    "@vue/compiler-sfc": "*"
 }
```

采用`*`号就不用管编译器的版本更新了(当然开发者得向下兼容得很好)。

## 恢复[transformAssetUrls](https://github.com/Kingbultsea/vite-analysis/blob/4ab007d1bad3afc617dd5bae5edacd815283b3e9/151-160/151-160.md#bug)(vue部分)

```typescript
transformAssetUrls: {
  // @ts-ignore
  base: path.posix.dirname(publicPath)
}
```

设置`base`,相当于修改路径为`path.join(base, '你在template的image资源')`。

想了解的可以看下下面的测试示例哈。

```typescript
# @vue/compiler-sfc

test('with explicit base', () => {
    const { code } = compileWithAssetUrls(
      `<img src="./bar.png"></img>` + // -> /foo/bar.png
      `<img src="~bar.png"></img>` + // -> /foo/bar.png
      `<img src="bar.png"></img>` + // -> bar.png (untouched)
        `<img src="@theme/bar.png"></img>`, // -> @theme/bar.png (untouched)
      {
        base: '/foo'
      }
    )
    expect(code).toMatchSnapshot()
  })
// transformAssetUrl
if (options.base) {
        // explicit base - directly rewrite the url into absolute url
        // does not apply to absolute urls or urls that start with `@`
        // since they are aliases
        if (
          attr.value.content[0] !== '@' &&
          isRelativeUrl(attr.value.content)
        ) {
          // when packaged in the browser, path will be using the posix-
          // only version provided by rollup-plugin-node-builtins.
          attr.value.content = (path.posix || path).join(
            options.base,
            url.path + (url.hash || '')
          )
        }
        return
      }



compileWithSrcset(src, {
        base: '/foo'
      })
<img src='./a.png' setset='/foo/a.png' />
// transformSrcset
if (options.base) {
            const base = options.base
            const set: string[] = []
            imageCandidates.forEach(({ url, descriptor }) => {
              descriptor = descriptor ? ` ${descriptor}` : ``
              if (isRelativeUrl(url)) {
                set.push((path.posix || path).join(base, url) + descriptor)
              } else {
                set.push(url + descriptor)
              }
            })
            attr.value.content = set.join(', ')
            return
          }
```

## transformAssetUrls(build部分)含BUG

代码修改为:

```typescript
transformAssetUrls: {
          includeAbsolute: true // 是否包括绝对路径
}
```

但是实际调试中,如果设置为`true`则是不合理的(可以自己设置一个绝对路径,会出现`Import bug`)。

`options.includeAbsolute`必须为`false`,不然会被处理为`import`语句(或者添加`base`)。

![1](./1.png)

## node/buildPluginCss.ts

如果`url("http://123")`符合`/^https?:\/\//`,则不对其做打包处理。



================================================
FILE: 201-210/201-210.md
================================================
# 201 - b48ae5b [#32](https://github.com/vitejs/vite/issues/32) 兼容node 10

![1](./1.png)

尝试修改`"node": ">=15.0.0"`,然后`yarn`:

![2](./2.png)

> 版本号不对,yarn无法安装依赖



# 202 - 4fa01ca changelog

## [0.10.2](https://github.com/vuejs/vite/compare/v0.10.1...v0.10.2) (2020-05-04)

### Bug Fixes

- 修复构建index资源注入 ([ccce482](https://github.com/vuejs/vite/commit/ccce48228d8220de4312585c716c1c27ea9ef1c2))
- 正确处理绝对url资源 ([5ca0ec4](https://github.com/vuejs/vite/commit/5ca0ec4abc183a3942ef169b39034ff403dd9eae)), closes [#45](https://github.com/vuejs/vite/issues/45)
- **moduleResolve:** 不要重写外部导入 ([dd7af0a](https://github.com/vuejs/vite/commit/dd7af0a9b3e77fcbdec6fe7fcda26443f1e2c8fa)), closes [#42](https://github.com/vuejs/vite/issues/42)

### Features

- 支持 *.module.css 的 CSS 模块 ([1782f83](https://github.com/vuejs/vite/commit/1782f831c62e73d961fcf71de4d1024a1f8acaf7))



# 203 - 95f6ff9 v0.10.2

release v0.10.2



# 204 - b6cafee 支持模板预处理器

```typescript
// 可以自定义一个预处理器,就算不做任何处理返回原本souce也可以
doCompileTemplate({
        ...options,
        source: preprocess(options, preprocessor)
})

function preprocess(
  { source, filename, preprocessOptions }: SFCTemplateCompileOptions,
  preprocessor: PreProcessor
): string {
  // Consolidate exposes a callback based API, but the callback is in fact
  // called synchronously for most templating engines. In our case, we have to
  // expose a synchronous API so that it is usable in Jest transforms (which
  // have to be sync because they are applied via Node.js require hooks)
  let res: string = ''
  let err: Error | null = null

  preprocessor.render(
    source,
    { filename, ...preprocessOptions },
    (_err, _res) => {
      if (_err) err = _err
      res = _res
    }
  )

  if (err) throw err
  return res
}


# compiler-sfc
export type PreprocessLang = string

# node/serverPluginVue.ts
const { code, map, errors } = resolveCompiler(root).compileTemplate({
    // ...
    preprocessLang: template.lang, // 传入id
    preprocessCustomRequire: (id: string) => require(resolve(root, id))
})
```



# 205 - a69159a 调整行内式资源最大值为4096

```typescript
if (!id.endsWith(`.svg`)) {
    if (content.length < 4096) {
      url = `data:${mime.lookup(id)};base64,${content.toString('base64')}`
    }
}
```



# 206 - a182ac4 readme

## Status

仍处于试验阶段,但我们打算使其适合生产。

## Features

...

`vite` 尝试尽可能多地镜像 [vue-cli](http://cli.vuejs.org/) 中的默认配置。如果你之前使用过 `vue-cli` 或其他基于 webpack 的脚手架, 你应该有宾至如归的感觉。

### 与 `vue-cli` 或者其他打包器有什么不同?

主要区别是`vite`在开发模式没有任何代码捆绑。源代码中的 ES 导入语法将直接提供给浏览器, 浏览器通过原生 `<script module>` 支持解析它们, 为每次导入发出 HTTP 请求。开发服务器拦截请求并在必要时执行代码转换。例如, 对 `*.vue` 文件的导入在发送回浏览器之前就会被编译。

这种方法有几个优点:

- 由于没有捆绑工作要做,服务器冷启动速度极快。
- 代码将会被按需编译, 也就是说只编译当前屏幕上实际导入的代码。您不必等到整个应用程序被捆绑后才能开始开发。这对于具有数十个展示页的应用程序来说可能是一个巨大的差异。
- 热模块更换 (HMR) 性能与模块总数分离。无论您的应用程序有多大,这都使 HMR 始终保持快速。

整页重新加载可能比基于捆绑程序稍慢, 因为原生 ES 导入会导致具有深度导入链的网络瀑布。但是由于这是本地开发, 与实际编译时间相比,差异应该是微不足道的。 (页面重新加载没有编译成本,因为已经编译的文件会被缓存在内存中。)

最后,因为编译还是在Node中完成,它可以在技术上支持捆绑程序进行任何的代码转换, 并且没有什么能阻碍你最终将代码捆绑到生产环境中。实际上, `vite` 提供了一个 `vite build` 命令来做到这一点,因此应用程序在生产中不会受到网络瀑布的影响。



# 207 - 4808f41 build支持`ssr`

## `node/build.ts`

### 添加`buildOptions`注释

```typescript
export interface BuildOptions {
  /**
   * 项目根路径
   */
  root?: string
  /**
   * 如果为 true,将从 CDN 导入 Vue。
   * 当存在本地 vue 安装时自动禁用。
   */
  cdn?: boolean
  /**
   * 映射请求路径/文件路径,
   * 可选择将模块 ID 映射到公共路径请求。
   */
  resolvers?: Resolver[]
  /**
   * 默认`dist`
   */
  outDir?: string
  /**
   * 在`outDir`下的目录下嵌套js / css / static assets
   * 默认`assets`
   */
  assetsDir?: string
  /**
   * 构建不在项目根目录内的文件,
   * 例如,如果你在 vite 之上构建一个更高级别的工具并且包括
   * 一些将被捆绑到最终构建中的代码。
   */
  srcRoots?: string[]
  /**
   * 将传递给rollup.rollup()
   */
  rollupInputOptions?: InputOptions
  /**
   * 将传递给bundle.generate()
   */
  rollupOutputOptions?: OutputOptions
  rollupPluginVueOptions?: Partial<Options>
  /**
   * 是否将assets写入磁盘
   */
  emitAssets?: boolean
  /**
   * 是否将bundle写入磁盘
   */
  write?: boolean
  /**
   * 是否压缩输出的代码
   */
  minify?: boolean
  /**
   * 是否将资源信息记录到控制台
   */
  silent?: boolean
}
```

> 为什么要配置是否写入到磁盘?因为rollup是可以提供用户配置的(而且`vite`构建会返回`output`和`html`),也就是可以配置`output`来做文件输出,所以不需要`vite`的写入,这块主要是考虑给基于`Vite`之上的工具使用,自定义构建。

## `node/build.ts`

新增`ssrbuild`,基于原有方法build传入配置。

#### external:

不进行打包处理的文件,携带`['vue', /^@vue\//]`。

```typescript
var vue = require('vue');
var serverRenderer = require('@vue/server-renderer');
```

### format:

打包后的代码格式,默认`cjs`。

```typescript
export type ModuleFormat = InternalModuleFormat | 'commonjs' | 'esm' | 'module' | 'systemjs';

// esm
import { createApp } from 'vue';
import { foo } from './foo.js';
import { ssrRenderAttr } from '@vue/server-renderer';

// cjs
var vue = require('vue');
var foo_js = require('./foo.js');
var serverRenderer = require('@vue/server-renderer');
```

### exports

导出方式,设置打包后默认`named`。

[详细](https://rollupjs.org/guide/en/#outputexports)

```typescript
exports?: 'default' | 'named' | 'none' | 'auto';
```



# 208 - 288e68e  改进构建输出并包含文件大小信息提示

```typescript
const enum WriteType {
  JS,
  CSS,
  ASSET,
  HTML
}

const writeColors = {
  [WriteType.JS]: chalk.cyan,
  [WriteType.CSS]: chalk.magenta,
  [WriteType.ASSET]: chalk.green,
  [WriteType.HTML]: chalk.blue
}
```



# 209 - a5c608d [#53](https://github.com/vitejs/vite/pull/53)

构建模式下,可以让用户配置内联的阈值,默认4096字符长度。

```typescript
export interface AssetsOptions {
  inlineThreshold?: number
}
```



# 210 - cfdbf4e bump `rollup-plugin-vue`

```json
{
    "rollup-plugin-vue": "^6.0.0-alpha.7"
}
```



================================================
FILE: 21-30/commit-21-30.md
================================================
# 21 - 3e0ff79 v0.1.2发布

```json
{
-   version: "0.1.1"
+   version: "0.1.2"    
}
```



# 22 - c74b24e 重构使用koa,废弃server-handler

均使用koa中间件的形式改写```vueMiddleware.ts moduleMiddleware.ts hmrWatcher.ts```

### server/middlewares/serve.ts

使用```koa2-history-api-fallback```,与```koa-static```中间件。

#### ```koa2-history-api-fallback```:

使用```koa2-connect-history-api-fallback```之后,```koa```就会把所有的get方式的请求都发给```/index.html```,然后由vue-router来接管页面路由。

#### ```koa-static```:

```typescript
app.use(require('koa-static')(cwd))
```

获取当前```cwd```路径的静态资源。

### 关于中间件执行的路径处理

1. 检测路径是否为```__hmrClient```,通过他来建立客户端与服务端的```ws```链接。
2. 处理包含```.js```的路径,发送模块。
3. 处理包含```.vue```的路径,与前端组件相关。
4. ```koa2-history-api-fallback```。
5. ```koa-static```。



# 23 - a307eeb index.html的指向

去除```koa```的中间件```koa2-history-api-fallback```,采用手写的方法```src/server/middlewares/historyFallback.ts```。

```typescript
import { Middleware } from '../index'

export const historyFallbackMiddleware: Middleware = ({ cwd, app }) => {
  app.use((ctx, next) => {
    const cleanUrl = ctx.url.split('?')[0].split('#')[0]
    if (ctx.method !== 'GET' || cleanUrl.includes('.')) { // 文件 get 不处理
      return next()
    }

    if (!ctx.headers || typeof ctx.headers.accept !== 'string') { // 没有header  || 不知道
      return next()
    }

    if (ctx.headers.accept.includes('application/json')) { // 不处理 期望json的数据
      return next()
    }

    if (
      !(
        ctx.headers.accept.includes('text/html') ||
        ctx.headers.accept.includes('*/*')
      ) // 边缘处理
    ) {
      return next()
    }

    ctx.url = '/index.html' // 改写路径,交给第5步,处理内容。
    return next()
  })
}

```

因为使用```koa2-history-api-fallback```,会把所有get请求都指向一个文件,如果请求一个```.vue```组件,在进行流程的第```4```步,内容必然会被改写成```index.html```文件。



# 24 - 4ed433a 改写```index.html```的```<script>```

曾经```index.html```请求页面,页面中的标签,引入```main.js```,经过```moduleRewriter.ts```处理,```import a from 'a'```改写成```import a from '__module/a'```。

现在把这个功能也用在浏览器请求```index.html```中的```<script>```。

优化目的提前一步改写处理。



# 25 - 36f1a18

### 更改名称:

```vueResole.ts``` -> ```resolveVue.ts``

### 优化改写```import```路径的代码结构

去除```moduleRewriter.ts```,并且把该```rewrite```功能合并到```middlewares/modules.ts```,由于这个用来改写```import```的,所以名称更改为```rewriteImports```。

好处是:原本在vue中间件,模块中间件,都需要指向```__modules```的功能,移交给```modules.ts```统一处理,

通过中间件,好管理代码。



# 26 - 1c95964 v0.2.0发布

这个版本的```<style>```新增标签后,触发了```rerender```,会导致新增```<style>```无效

```json
{
-   version: "0.1.2"
+   version: "0.2.0"    
}
```



# 27 - df93fda 准备```vite```配置

### ```build```前需要删除dist: 

```json
{
    script: {
        // 不支持windows
        build: "rm -rf dist && tsc -p src/client && tsc -p src/server"
    }
}
```

### ```server/index.ts```

暴露可配置的server,交付给```./bin/vite.js```去建立服务,同时添加```https```选项。

启动```vite```,以命令行的形式去输入配置(这块有BUG,还没完善)



# 28 - 87324af ```cwd```名称优化

```cwd```参数名称改为```root```,尤大觉得因为寻找模式,直接改为```root```会更加贴切(我觉得尤大觉得)。



# 29 - 5780a2a 重构```rewriteImports```

### 引入```es-module-lexer```

处理AST语法树,使用在修改```import```所引入的文件。

```typescript
// 使用新的包,重写rewriteImports
function rewriteImports(source: string) {
  const [imports] = parse(source)

  if (imports.length) {
    const s = new MagicString(source)
    let hasReplaced = false
    imports.forEach(({ s: start, e: end, d: dynamicIndex }) => {
      const id = source.substring(start, end)
      if (dynamicIndex < 0) {
        if (/^[^\/\.]/.test(id)) { // 匹配 / 和 .
          s.overwrite(start, end, `/__modules/${id}`)
          hasReplaced = true
        }
      } else {
        // 异步引入import的路径还没有改写
        // TODO dynamic import
      }
    })
    return hasReplaced ? s.toString() : source
  }

  return source
}
```



# 30 - 9efbb0e 名称优化

更改参数名称,更改原有的```src/server/middleware``` 为```src/server/plugins```

================================================
FILE: 211-220/211-220.md
================================================
# 211 - f1c03ff 设置preserveEntrySignatures

## node/build.ts

`rollup.preserveEntrySignatures = false`

- 类型:`"strict" | "allow-extension" | false`
  命令行参数:`--preserveEntrySignatures <strict|allow-extension>`/`--no-preserveEntrySignatures`
  默认值:`"strict"`

  该选项用于控制 Rollup 尝试确保入口块与基础入口模块具有相同的导出。

  - 如果它的值设置为 `"strict"`,Rollup 将在入口 chunk 中创建与相应入口模块中完全相同的导出。如果因为需要向 chunk 中添加额外的内部导出而无法这样做,那么 Rollup 将创建一个 `facade` 入口 chunk,它将仅从前其他 chunk 中导出必要的绑定,但不包含任何其他代码。对于库,推荐使用此设置。
  - 值为 `"allow-extension"`,则 Rollup 会将在入口 chunk 中创建入口模块的所有导出,但是如果有必要,还可以添加其他导出,从而避免出现 “facade” 入口 chunk。对于不需要严格签名的库,此设置很有意义。
  - 值为 `false`,则 Rollup 不会将入口模块中的任何导出内容添加到相应的 chunk 中,甚至不包含相应的代码,除非这些导出内容在 bundle 的其他位置使用。但是,可以将内部导出添加到入口 chunks 中。对于将入口 chunks 放置在脚本标记中的 Web 应用,推荐使用该设置,因为它可能同时减少 bundle 的尺寸大小 和 chunks 的数量。

**Example**
Input:

```typescript
// main.js
import { shared } from './lib.js';
export const value = `value: ${shared}`;
import('./dynamic.js');

// lib.js
export const shared = 'shared';

// dynamic.js
import { shared } from './lib.js';
console.log(shared);
```

Output for `preserveEntrySignatures: false`:

```typescript
// main.js
import('./dynamic-39821cef.js');

// dynamic-39821cef.js
const shared = 'shared';

console.log(shared);
```



# 212 - c82a597 打包后的引入路径可配置化

`BuildOptions.base`:
  类型::`stirng` | `/`
  作用:对注入的`css` `js`和静态资源,添加base值路径。
  **Example**:

```typescript
base = 'basePath'

// 转换后的 index.html    
<link rel="stylesheet" href="/base/assets/style.css">
<div id="app"></div>
<script type="module" src="/base/assets/index.js"></script>

// comp.vue
<template>
  <img src="./assest/pkg.png"/>
</template>

// 转换后的 index.js 
var _imports_0 = "/base/assets/pkg.61b85fb5.png";
```



# 213 - 59b8638 create-vite-app包 正确设置错误提示的触发

`create-vite-app`包:当文件存在/文件拒绝delete才报错误`console.error(`Error: target directory already exists.`)`



# 214 - 01135fa [#55](https://github.com/vitejs/vite/issues/55) 修正windows下无法引入模块的问题

vite寻找完模块入口后,会做一次304跳转到模块具体的脚本中。

```typescript
// 入口跳转
ctx.redirect(path.join(ctx.path, entryPoint))
```

由于`path.join(ctx.path, entryPoint)`会根据系统不同而返回不同的路径分隔符,在windows下是`\`,所以使用slash包来做兼容,统一输出为`/`。

windows: `%5C@modules%5Clodash%5Clodash.js`
slash后:`/@modules/lodash/lodash.js`



# 215 - 重构代码,迁移注入html的功能至`buildPluginHtml.ts`

尤大为了一些不知道怎么配置vite的小伙伴(其实就是rollup的配置),加上了配置文档的注释。

```typescript
  /**
   * Will be passed to rollup.rollup()
   * https://rollupjs.org/guide/en/#big-list-of-options
   */
  rollupInputOptions?: InputOptions
  /**
   * Will be passed to bundle.generate()
   * https://rollupjs.org/guide/en/#big-list-of-options
   */
  rollupOutputOptions?: OutputOptions
  /**
   * Will be passed to rollup-plugin-vue
   * https://github.com/vuejs/rollup-plugin-vue/blob/next/src/index.ts
   */
  rollupPluginVueOptions?: Partial<Options>
```

> Partial<T> 可以快速把某个接口类型中定义的属性变成可选的(Optional)



# 216 - 8c6cf4a 重构`buildPluginHtml.ts`,处理模板资源路径

改动部分:

- 修改变量名称`BuildOptions.assetsOptions` -> `BuildOptions.assetsInlineLimit`
- 静态资源添加上`ico`文件类型
- 新增`buildOptions.emitIndex`,类型可选:`boolean`,是否把`index.html`写入磁盘
- **重构`buildPluginHtml.ts`**

### 重构`buildPluginHtml.ts`

- 若不存在.html文件,则返回:

```typescript
{
      renderIndex: (...args: any[]) => '', // 
      htmlPlugin: null  // rollupPlugin
}

const assetAttrsConfig: Record<string, string[]> = {
  link: ['href'],
  video: ['src', 'poster'],
  source: ['src'],
  img: ['src'],
  image: ['xlink:href', 'href'],
  use: ['xlink:href', 'href']
}
```

- 编译传入的`.html`
  1. 收集`<script>`代码,转换`src`属性为`import`语句。
  2. 符合以`assetAttrsConfig`的`DOM`元素,都会被转换为`import`语句收集起来。
  3. 返回`html`与收集的`js`。

html文件,引入的资源将会使用`buildPuginAsset.resolveAsset`方法替换内部的路径,已确保资源能准确获取。

> 尤大用编译模板`<template/>`的方法`compiler-core.compiler`去处理`.html`文件。
>
> ```typescript
> transform(ast, {
>   nodeTransforms: [viteHtmlTrasnfrom]
> })
> ```



# 217 - ef2e6ee changelog

## [0.10.2](https://github.com/vuejs/vite/compare/v0.10.1...v0.10.2) (2020-05-04)

### Bug Fixes

- 修复构建时注入`index.html`路径错误问题([ccce482](https://github.com/vuejs/vite/commit/ccce48228d8220de4312585c716c1c27ea9ef1c2))
- 修复资源路径编译的问题 ([5ca0ec4](https://github.com/vuejs/vite/commit/5ca0ec4abc183a3942ef169b39034ff403dd9eae)), closes [#45](https://github.com/vuejs/vite/issues/45)
- **moduleResolve:** 不要重写外部导入 ([dd7af0a](https://github.com/vuejs/vite/commit/dd7af0a9b3e77fcbdec6fe7fcda26443f1e2c8fa)), closes [#42](https://github.com/vuejs/vite/issues/42)

### Features

- 通过使用 *.module.css,支持CSS模块功能 ([1782f83](https://github.com/vuejs/vite/commit/1782f831c62e73d961fcf71de4d1024a1f8acaf7))



# 218 - 09451c6 v0.10.3

release v0.10.3



# 219 - e14644f create-vite-app v1.0.2

release create-vite-app v1.0.2



# 220 - b87ba7e 利用esbuild压缩代码

改动部分:

- package.json新增`esbuild ^0.2.0`包
- **新增`node/esbuildService.ts`文件**
- **可选方式压缩代码**`BuildOptions.minify?: boolean | 'terser' | 'esbuild' `

## node/esbuildService.ts

#### `renderChunk`

类型: `(code: string, chunk: ChunkInfo, options: OutputOptions) => string | { code: string, map: SourceMap } | null`
种类: `async, sequential`
Previous Hook: [`resolveFileUrl`](https://rollup.docschina.org/guide/en/#resolvefileurl) 
Next Hook: [`generateBundle`](https://rollup.docschina.org/guide/en/#generatebundle).

可用于转换独立的代码块。被每个 Rollup 所输出的块所调用。返回 `null` 将不应用任何转换。

```typescript
import { startService, Service, TransformOptions } from 'esbuild'
import { Plugin } from 'rollup'

const transform = async (
  service: Service,
  code: string,
  options: TransformOptions,
  operation: string
) => {
  console.log(operation)
  try {
    const result = await service.transform(code, options)
    if (result.warnings.length) {
      console.error(`[vite] warnings while ${operation} with esbuild:`)
      // TODO pretty print this
      result.warnings.forEach((w) => console.error(w))
    }
    return {
      code: result.js || '',
      map: result.jsSourceMap || ''
    }
  } catch (e) {
    console.error(`[vite] error while ${operation} with esbuild:`)
    console.error(e)
    return {
      code: '',
      map: ''
    }
  }
}

export const createMinifyPlugin = async (): Promise<Plugin> => {
  const service = await startService()
  return {
    name: 'vite:minify',
    async renderChunk(code, chunk) { // 如果多个插件实现了相同的钩子函数,那么会串式执行
      return transform(
        service,
        code,
        { minify: true },
        `minifying ${chunk.fileName}`
      )
    },
    generateBundle() {
      service.stop()
    }
  }
}

```

## 可选方式压缩代码

- 旧方式使用`rollup-plugin-terser`
- 第二种方式使用`esbuild`



================================================
FILE: 221-230/221-230.md
================================================
# 221 - 4fd2cdf 构建模式默认使用esbuild

```typescript
// terser is used by default for better compression, but the user can also
// opt-in to use esbuild which is orders of magnitude faster.
const minifyPlugin = minify
  ? minify === 'esbuild'
    ? await createMinifyPlugin()
    : require('rollup-plugin-terser').terser()
  : null
```

> 现阶段遇到的问题:windows下无法使用esbuild



# 222 - 7cbaf5d 开发环境编译`jsx`、`tsx`和`ts`

改动部分:

- `node/esbuildService.ts`增加`esbuild`服务是否启动的检测(详)
- 新增`node/serverPluginEsbuild.ts`(详)
- `node/serverPluginVue.ts`的`<script lang="ts">`支持转换`ts`(详)

## `esbuildService.ts`增加`esbuild`服务是否启动的检测

新增`ensureService`方法,每次`transform`代码转换前都会检测一次,确保`esbuild`服务运行。

这是由于esbuild服务,有时候会自动退出(主要因为这次改动,`dev`也使用`esbuild`了)。

```typescript
import { startService, Service, TransformOptions } from 'esbuild'

const ensureService = async () => {
  if (!_service) {
    _service = await startService()
  }
  return _service
}

export const transform = async (
  code: string,
  options: TransformOptions,
  operation: string
) => {
  return _transform(await ensureService(), code, options, operation)
}
```

## 新增`node/serverPluginEsbuild.ts`

使用`esbuild`转换`ts`、`tsx`和`jsx`文件。

```typescript
import { Plugin } from './server'
import { readBody, isImportRequest, genSourceMapString } from './utils'
import { TransformOptions } from 'esbuild'
import { transform } from './esbuildService'

const testRE = /\.(tsx?|jsx)$/

export const esbuildPlugin: Plugin = ({ app, watcher, jsxConfig }) => {
  app.use(async (ctx, next) => {
    await next()
    if (isImportRequest(ctx) && ctx.body && testRE.test(ctx.path)) {
      ctx.type = 'js'
      let options: TransformOptions = {}
      if (ctx.path.endsWith('.ts')) {
        options = { loader: 'ts' }
      } else if (ctx.path.endsWith('tsx')) {
        options = { loader: 'tsx', ...jsxConfig }
      } else if (ctx.path.endsWith('jsx')) {
        options = { loader: 'jsx', ...jsxConfig }
      }
      const src = await readBody(ctx.body)
      const { code, map } = await transform(
        src!,
        options,
        `transpiling ${ctx.path}`
      )
      ctx.body = code
      if (map) {
        ctx.body += genSourceMapString(map)
      }
    }
  })

  watcher.on('change', (file) => {
    if (testRE.test(file)) {
      watcher.handleJSReload(file)
    }
  })
}
```

## `node/serverPluginVue.ts`支持转换`ts`

调用`esbuild`服务。

```typescript
async function compileSFCMain(
  descriptor: SFCDescriptor,
  filePath: string,
  publicPath: string
): Promise<string> {
  // ...
      
  let code = ''
  if (descriptor.script) {
    let content = descriptor.script.content
    if (descriptor.script.lang === 'ts') {
      content = (
        await transform(content, { loader: 'ts' }, `transpiling ${publicPath}`)
      ).code
    }

    code += content.replace(`export default`, 'const __script =')
  } else {
    code += `const __script = {}`
  }

  // ...    
  return code
}
```

## 洋葱模型执行顺序

```typescript
# node/server.ts
const internalPlugins: Plugin[] = [
  moduleRewritePlugin, // 洋葱模型的第一层(外层) --
  moduleResolvePlugin, // 洋葱模型的第二层(里层)
  vuePlugin,           // 洋葱模型的第三层(内层)
  esbuildPlugin,       // 洋葱模型的第四层(外层) --
  jsonPlugin,          // 洋葱模型的第五层(外层) --
  cssPlugin,           // 洋葱模型的第六层(外层) --
  hmrPlugin,           // 洋葱模型的第七层(里层)
  serveStaticPlugin    // 洋葱模型的第八层(里层)
]
```



# 223 - 81ffbc5 构建模式支持`ts`

改动部分:

- `node/esbuildService.ts`抽离rollup服务到**新文件**`node/buildPluginEsbuild.ts`
- `node/serverPluginEsbuild.ts`抽离`ts` `tsx`、`jsx`代码到`node/esbuildService.ts`的`options`(详 **改动二**)
- `node/esbuildService.ts`判断文件类型使用`loader`(详 **改动三**)
- 插件上下文和`BuildOptions`新增`jsxConfig`(详 **改动四**)

## 改动二

去除了`ts`的检测,交给`node/esbuildService.ts`判断文件后缀功能处理。

## 改动三

如果没有配置`options.loader`,则判断文件的类型,作为`loader`。

构建的时候利用,自动识别到`ts`而进行转换。

## `node/buildPluginEsbuild.ts`

```typescript
import { Plugin } from 'rollup'
import { startService, Service } from 'esbuild'
import { tjsxRE, transformWithService } from './esbuildService'

export const createEsbuildPlugin = async (
  minify: boolean,
  jsx: {
    factory?: string
    fragment?: string
  }
): Promise<Plugin> => {
  let service: Service | undefined

  const jsxConfig = {
    jsxFactory: jsx.factory,
    jsxFragment: jsx.fragment
  }

  return {
    name: 'vite:esbuild',

    async transform(code, file) {
      if (tjsxRE.test(file)) {
        return transformWithService(
          service || (service = await startService()),
          code,
          file,
          { ...jsxConfig }
        )
      }
    },

    async renderChunk(code, chunk) {
      if (minify) {
        return transformWithService(
          service || (service = await startService()),
          code,
          chunk.fileName,
          {
            minify: true
          }
        )
      } else {
        return null
      }
    },

    generateBundle() {
      service && service.stop()
    }
  }
}
```

## 改动四

[jsxFactory](https://www.typescriptlang.org/tsconfig#jsxFactory):设置转换的名称

[jsxFragment](https://www.typescriptlang.org/tsconfig#jsxFragmentFactory):设置Fragment的名称

```typescript
jsxConfig: {
    jsxFactory: string | undefined
    jsxFragment: string | undefined
}
```

## BUG点

并没有人去处理`<script lang="ts">`。在开发模式中,是交给`esbuild`去处理的。



# 224 - 8262108 构建模式中对vue文件支持ts

改动部分:

- 默认添加的文件类型:`supportedExts = ['.js', '.jsx', '.ts', '.tsx', '.json']`
- `util.ts`新增`asyncReplace`,属于代码整理(`serverPluginModuleRewrite.ts` `buildPluginCss.ts`)
- `node/buildPluginEsbuild.ts`,新增判断`.vue`文件是否含有`lang="ts"`(详 改动三)

## 改动三

**Q**:为什么`rollup-plugin-vue`的`transform`是`async`,`buildPluginEsbuild`是`async`,两者没有任何关联的情况下,能做到转换?

**A**:假如现在有组件`Comp.vue`,执行`rollup-plugin-vue`的`transform`钩子,转换三大块标签为`import`资源,经手`load`钩子返回`script`原内容,然后`buildPluginEsbuild`的`transform`钩子识别到`lang=ts`,进行转换。

```typescript
// rollup-plugin-vue的transform钩子处理后
import script from "E:\\vite\\test\\temp\\Comp.vue?vue&type=script&lang=ts"
export * from "E:\\vite\\test\\temp\\Comp.vue?vue&type=script&lang=ts"
import { render } from "E:\\vite\\test\\temp\\Comp.vue?vue&type=template&id=35b58321"
const cssModules = script.__cssModules = {}
import "E:\\vite\\test\\temp\\Comp.vue?vue&type=style&index=0&lang=scss.css"
import style0 from "E:\\vite\\test\\temp\\Comp.vue?vue&type=style&index=0&lang=scss&module=true.css.js"
cssModules["$style"] = style0
script.render = render
export default script
```

# BUG点

`esbuild`处理后返回了空代码,`ts`是无法使用的

```typescript
{
  code: "",
  map: undefinded
}
```



# 225 - 5b5bced 

改动部分:

- 脚本配置对象传递修正(详 **改动一**)
- `esbuild`开发模式中,不检测是否来自`import`请求,也就是`index.html`可以标签引入`ts`了
- `node/serverPluginModuleRewrite.ts`修改正则的回调参数(详 **修正三**)

## 改动一

通过`--jsx-factory`和`--jsx-fragment`就可以配置对象了。

```typescript
Object.keys(argv).forEach((key) => {
  if (argv[key] === 'false') {
    argv[key] = false
  }
  if (key === 'jsx-factory') {
    ;(argv.jsx || (argv.jsx = {})).factory = argv[key]
  }
  if (key === 'jsx-fragment') {
    ;(argv.jsx || (argv.jsx = {})).fragment = argv[key]
  }
})
```

## 修正三

这是由于正则的表达式获取参数获取错了,导致无法植入`index.html`内的`js`。

```typescript
ctx.body = html!.replace(scriptRE, (_, openTag, script) => { // 之前[_, openTag, script]
          // also inject __DEV__ flag
          const devFlag = hasInjectedDevFlag ? `` : devInjectionCode
          hasInjectedDevFlag = true
          const ret = `${devFlag}${openTag}${rewriteImports(
            script,
            '/index.html',
            resolver
          )}</script>`
          return ret
})
```



# 226 -  73d94b9 修正脚本直接被`index.html`引入后无法触发`hmr`的问题

`hasDeadEnd`,触发`full-reload`。

```typescript
const importer = '/index.html'

if (srcAttr) {
  // register script as a import dep for hmr
  const importee = cleanUrl(slash(path.resolve('/', srcAttr[1])))
  debugHmr(`        ${importer} imports ${importee}`)
  ensureMapEntry(importerMap, importee).add(importer)
}
```

> 尤大啥时候修复一下js无法Hmr呢?在windows下



# 227 - cb205c4 changelog

# [0.11.0](https://github.com/vuejs/vite/compare/v0.10.3...v0.11.0) (2020-05-06)

### Bug Fixes

- 修复index.html直接引入资源无法hmr的问题([73d94b9](https://github.com/vuejs/vite/commit/73d94b9ba75836b995ed276747a32ce94344c1eb))

### Features

- 开发模式支持ts ([7cbaf5d](https://github.com/vuejs/vite/commit/7cbaf5d8e5b70db2ec642bd1d34f1e0322927ccf))
- 支持使用esbuild压缩代码 ([b87ba7e](https://github.com/vuejs/vite/commit/b87ba7e321b9dd319009a55154540805969f0039))
- vue组件支持ts ([8262108](https://github.com/vuejs/vite/commit/8262108db14b35126bcaae3253bf3f6391c9d283))
- 构建模式支持tsx ([81ffbc5](https://github.com/vuejs/vite/commit/81ffbc548c3d5f9db1f040c360167f95963674d6))



# 228 - 5ba9454 v0.11.0

release v0.11.0



# 229 - ceb7d0a create-vite-app v1.0.3

release create-vite-app v1.0.3



# 230 - 16fa669 readme

### JSX

`.jsx` 和 `.tsx`也同样开箱即用, JSX 也是通过 `esbuild`来编译的, 你可以通过 `--jsx-factory` 和 `--jsx-fragment` 来配置,API可以使用 `jsx: { factory, fragment }` 。例如,在`vite`使用[Preact](https://preactjs.com/) :

```typescript
{
  "scripts": {
    "dev": "vite --jsx-factory=h"
  }
}
import { h, render } from "preact"
render(<h1>Hello, what!</h1>, document.getElementById("app"))
```

#### Notes on JSX Support

- Vue 3的 JSX代码转换尚未完成,所以 `vite`的JSX目前仅支持React/Preact。
- React不提供ES模块构建,因此必须与Snowpack预先捆绑才能工作。
- 在使用非Vue框架时,没有现成的HMR,但从技术上讲,HMR是可以通过服务器API实现的。

> 我理解的可以使用custom事件来定义一下(或者直接改源码吧)
>
> 2021年12月27日:可以通过hmr api来实现,后续vue hmr将重构为使用hmr api



================================================
FILE: 231-240/231-240.md
================================================
# 231 - e78c9f7 修复外链import路径改写错误

不应该中断改写`import`流程,不然遇到外链后,不返回任何的`code`。

```typescript
if (isExternalUrl(id)) {
  break
}
```



# 232 - redame

### JSX

`.jsx` 和 `.tsx`也同样开箱即用, JSX 也是通过 `esbuild`来编译的。

因为 React 不提供 ES 模块构建,你可以使用 [es-react](https://github.com/lukejacksonn/es-react), 或者使用 Snowpack 将 React 预捆绑到 ES 模块中。 让它运行的最简单方法是:

```js
import { React, ReactDOM } from 'https://unpkg.com/es-react'

ReactDOM.render(<h1>Hello, what!</h1>, document.getElementById("app"));
```



# 233 - fcf709e changelog 

## [0.11.1](https://github.com/vuejs/vite/compare/v0.11.0...v0.11.1) (2020-05-06)

### Bug Fixes

- 修复外链import路径改写错误 ([e78c9f7](https://github.com/vuejs/vite/commit/e78c9f7680c2652b13f4270182c860417e388b2e))



# 234 - a135bfd v0.11.1

release v0.11.1



# 235 - 2048b65 [#59](https://github.com/vitejs/vite/pull/59) readme修改

**Message**:我注意到HMR的代码在构建后没有被删除,这将会导致错误,因为`hot`被编译成一个空对象了。
我不知道你是否可以删除这部分的代码, 虽然使用 `__DEV__`也可以正常运行。

即使用HMR API都需要使用`__DEV__`判断:

```typescript
if (__DEV__) {
  hot.accept(newModule => {
    console.log('updated: count is now ', newModule.count)
  })
}

if (__DEV__) {
  hot.accept('./foo.js', (newFoo) => {
    // the callback receives the updated './foo.js' module
    newFoo.foo()
  })

  // Can also accept an array of dep modules:
  hot.accept(['./foo.js', './bar.js'], ([newFooModule, newBarModule]) => {
    // the callback receives the updated mdoules in an Array
  })
}
```



# 236 - 5ca417e 定义如何提交BUG/功能报告的模板

配置好即可在`issues` -> `new issues`  查看到。

![docs](./docs.png)

![docs2](./docs2.png)

## .github/ISSUE_TEMPLATE/bug_report.md

name: 标题

about: 描述

labels: 标签

title: 内容标题

```markdown
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

## Describe the bug

A clear and concise description of what the bug is.

## System Info

- `vite` version
- OS (required):
- Node version (required):
- Optional:
  - Installed `vue` version (from `yarn.lock` or `package-lock.json`)
  - Installed `@vue/compiler-sfc` version

## Logs

1.  **run `vite` or `vite build` with the `DEBUG` environment variable set to `vite:*`** - e.g. modify the `dev` script in your `package.json` to:

    ``` bash
    DEBUG=vite:* vite
    ```

    On windows, you will need [cross-env](https://www.npmjs.com/package/cross-env):

    ``` bash
    cross-env DEBUG=vite:* vite
    ```

2. Provide the error log here.

## Reproduction

Provide a link to a reproduction repo if applicable.

```

## .github/ISSUE_TEMPLATE/feature_request.md

```markdown
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.

```

> 我之前以为debug包失效了... 后来去查看了一下文档[debug](https://www.npmjs.com/package/debug)使用方式,发现需要一个`process.env.DEBUG`来开启。



# 237 - 更新bug报告模板

加粗提示`vite`版本 操作系统 node版本的需求。



# 238 - 353bd81 文档英文单词语法修改 [#64](https://github.com/vitejs/vite/pull/64)

文档英文单词语法修改。



# 239 - 906b14c readme todo

## TODOs

- 支持构建配置 (custom import maps and plugins)
- 通过[Sucrase](https://github.com/alangpierce/sucrase) or [esbuild](https://github.com/evanw/esbuild)支持 TypeScript / Flow /(P)React JSX

> 话说不是已经支持了?除了Flow



# 240 - bd58858 避免改变 esbuild 选项

相当于代码整理,让`options.sourcemap`可配置,再也不是`true`来固定。



================================================
FILE: 241-250/241-250.md
================================================
# 241 - 5f05f1e 简化`web_modules`路径处理器

改动:

- `node/serverPluginModuleResolve.ts`,`resolveWebModule`被重构(详 **改动一**)

## 改动一

```typescript
async function resolveWebModule(
  root: string,
  id: string
): Promise<string | undefined> {
  let webModulePath = webModulesMap.get(id)
  if (webModulePath) {
    return webModulePath
  }
  webModulePath = path.join(root, 'web_modules', id + '.js')
  if (await fs.pathExists(webModulePath)) {
    webModulesMap.set(id, webModulePath)
    return webModulePath
  }
}
```

## 什么是`webModules`?

使用方式:

创建文件夹`web_modules`,在文件夹下创建`webmodules.js`,使用`import 'webmodules'`引入。

```typescript
// # Comp.vue
import 'webmodules'
```

所以从功能和代码上看,作用仅仅是路径转换。



# 242 - e01e26d `url('data:')`应跳过处理

改动:

`node/buildPluginCss.ts`,转换`url(./foo.png)`为资源的处理器,应跳过对`url(data:)` `inlineData`的处理。



# 243 - fc75323 修复`web_modules`在构建中无法使用的问题

改动部分:

- `node/buildPluginResolve.ts`,引入`node/serverPluginModuleResolve.ts`中的`resolveWebModule`方法。(详 **改动一**)

## 改动一

修改`resolveId`为异步方式,因不依赖其他插件`resolveId`的处理,所以可以异步。

如果检测到路径非`@`且非`/`,则执行`resolveWebModule`,没有寻找到`web_module`即空,不做任何处理。

也就是说检测`import a from 'myLodash'`,尝试寻找`web_module/myLodash.js`,有则转换为`import a from 'web_module/myLodash.js'`。

> 顺带一提,rollup已经帮忙处理好node_modules了,我们不用管这一层东西

```typescript
async resolveId(id: string) {
      if (id === hmrClientId) {
        return hmrClientId
      } else if (id.startsWith('/')) {
        // if id starts with any of the src root directories, it's a file request
        if (srcRoots.some((root) => id.startsWith(root))) {
          return
        }
        const resolved = resolver.requestToFile(id)
        debug(id, `-->`, resolved)
        return resolved
      } else if (id === 'vue') {
        if (!cdn) {
          return resolveVue(root).bundler
        } else {
          return {
            id: resolveVue(root).cdnLink,
            external: true
          }
        }
      } else if (!id.startsWith('.')) {
        const request = resolver.idToRequest(id)
        if (request) {
          const resolved = resolver.requestToFile(request)
          debug(id, `-->`, request, `--> `, resolved)
          return resolved
        } else {
          const webModulePath = await resolveWebModule(root, id)
          if (webModulePath) {
            return webModulePath
          }
        }
      }
    }
```



# 244 - 7aaf458 兼容[#59](https://github.com/vitejs/vite/pull/59),定义构建字符串的范围

改动部分:

- `node/build.ts`对`@rollup/plugin-replace`(替换字符串)插件配置范围为`['**/*.js', '**/*.ts', '**/*.jsx', '**/*.tsx']`。
- `node/buildPluginResolve.ts`对返回的`hot`,增加`accept` `on`,主要是为了调用的时候为空的问题([#59](https://github.com/vitejs/vite/pull/59))。



# 245 - 6490a8d 添加`playground`测试项目

用于检测`vite`的BUG。

包含:

- `web_modules`

- `@hmr`使用

- 模块处理

- `hmr`

- `.css`引入

- `scope css`

- `.module.css`

- `template`预处理器`pug`

- 行内式资源,`url()`外链资源

- `JSON`引入

- `JSX` `TSX`应用

- `async`组件

  

# 246 - d02d694 完善功能测试

改动部分:

- 去除test/fixtures/的模板文件,test大改(详 **改动一**)
- 新增`node/buildPluginReplace.ts`,替换`@rollup/plugin-replace`(详 **改动二**)

## 改动一

检测的事情:

- 通过检测DOM字符串,判断是否正确渲染
- 通过检测`consolelog`输出是否包含404,判断资源是否正常生成
- 检测`__DEV__`,`false`为构建环境,`true`为开发环境
- `process.env.NODE_ENV`替换
- 检测`vue-router` `vuex`,判断`node_modules`模块是否正常
- 检测`web-modules-dep`,判断`web_modules`是否正常
- 替换文件`<template>`内容,检测`vue-render`
- 替换文件`<script>`,检测`vue-reload`
- 检测`js`是否被正常引入
- 检测`js`更新,冒泡触发`vue-reload`
- 检测`js`更新,`hot api`
- `posocss`与`hmr`
- `scope`与`hmr`
- `css module`与`hmr`
- `css` `import`与`hmr`
- `<template>`预处理器`pug`
- `json`与`hmr`
- `jsx`与`hmr`

### dev如何配置测试

`execa`执行命令,页面跳转到相应`url`。

```typescript
devServer = execa(binPath, ['--jsx-factory=h'], {
  cwd: tempDir
})

page = await browser.newPage()
page.on('console', (msg) => logs.push(msg.text()))
await page.goto('http://localhost:3000')
```

### build如何配置测试

`execa`执行命令,使用`koa` `http` `koa-static`运行服务。

```typescript
const buildOutput = await execa(binPath, ['build', '--jsx-factory=h'], {
        cwd: tempDir
})

const app = new (require('koa'))()
app.use(require('koa-static')(path.join(tempDir, 'dist')))
staticServer = require('http').createServer(app.callback())
await new Promise((r) => staticServer.listen(3001, r))

page = await browser.newPage()
await page.goto('http://localhost:3001')
```

### 其他点

`jest --runInBand`: 在当前进程中连续运行所有测试, 而不是创建运行测试的子进程 作为工作池,这样可以大幅度提高运行速度。

[`getComputedStyle`](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/getComputedStyle):返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有CSS属性的值。 私有的CSS属性值可以通过对象提供的API或通过简单地使用CSS属性名称进行索引来访问。

## 改动二

`rollup/plugin-replace`会执行两次替换,第一次在`transform`,第二次在`renderChunk`(转换`chunk`)。

而且`vue组件`会被转换为`js`文件,我们无法把`vue`文件给排除在外(`js`等于`vue`组件)。

```typescript
# rollup/plugin-replace
return {
    name: 'replace',

    renderChunk: function renderChunk(code, chunk) {
      var id = chunk.fileName;
      if (!keys.length) { return null; }
      if (!filter(id)) { return null; }
      return executeReplacement(code, id);
    },

    transform: function transform(code, id) {
      if (!keys.length) { return null; }
      if (!filter(id)) { return null; }
      return executeReplacement(code, id);
    }
  };

# rollup/plugin-replace.ts
import { Plugin, TransformResult } from 'rollup'
import MagicString from 'magic-string'

const filter = /\.(j|t)sx?$/

export const createReplacePlugin = (
  replacements: Record<string, string>
): Plugin => {
  const pattern = new RegExp(
    '\\b(' +
      Object.keys(replacements)
        .map((str) => {
          return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')
        })
        .join('|') +
      ')\\b',
    'g'
  )

  return {
    name: 'vite:replace',
    transform(code, id) {
      if (filter.test(id)) {
        const s = new MagicString(code)
        let hasReplaced = false
        let match

        while ((match = pattern.exec(code))) {
          hasReplaced = true
          const start = match.index
          const end = start + match[0].length
          const replacement = replacements[match[1]]
          s.overwrite(start, end, replacement)
        }

        if (!hasReplaced) {
          return null
        }

        const result: TransformResult = { code: s.toString() }
        // TODO source map
        // result.map = s.generateMap({ hires: true })
        return result
      }
    }
  }
}
```



# 247 - `vite.js`改写为`cli.ts`

原文件使用`require`引入,不用担心`bin`位置的问题。

## `node/cli.ts`

```typescript
import chalk from 'chalk'
import { RollupError } from 'rollup'
import { networkInterfaces } from 'os'

console.log(chalk.cyan(`vite v${require('../package.json').version}`))
const s = Date.now()
const argv = require('minimist')(process.argv.slice(2))

if (argv.help) {
  // TODO print supported args on --help
}

Object.keys(argv).forEach((key) => {
  // cast xxx=false into actual `false`
  if (argv[key] === 'false') {
    argv[key] = false
  }
  // map jsx args
  if (key === 'jsx-factory') {
    ;(argv.jsx || (argv.jsx = {})).factory = argv[key]
  }
  if (key === 'jsx-fragment') {
    ;(argv.jsx || (argv.jsx = {})).fragment = argv[key]
  }
})

if (argv._[0] === 'build') {
  console.log('Building for production...')
  require('../dist')
    .build({
      ...argv,
      cdn: argv.cdn === 'false' ? false : argv.cdn
    })
    .catch((err: RollupError) => {
      console.error(chalk.red(`[vite] Build errored out.`))
      // TODO pretty print this
      // rollup errors contain helpful information
      console.error(err)
      process.exit(1)
    })
} else {
  const server = require('../dist').createServer(argv)

  let port = argv.port || 3000

  server.on('error', (e: Error & { code?: string }) => {
    if (e.code === 'EADDRINUSE') {
      console.log(`Port ${port} is in use, trying another one...`)
      setTimeout(() => {
        server.close()
        server.listen(++port)
      }, 100)
    } else {
      console.error(chalk.red(`[vite] server error:`))
      console.error(e)
    }
  })

  server.on('listening', () => {
    console.log(`Dev server running at:`)
    getIPv4AddressList().forEach((ip) => {
      console.log(`  > http://${ip}:${port}`)
    })
    console.log()
    require('debug')('vite:server')(`server ready in ${Date.now() - s}ms.`)
  })

  server.listen(port)
}

function getIPv4AddressList() {
  const interfaces = networkInterfaces()
  let result: string[] = []

  Object.keys(interfaces).forEach((key) => {
    const ips = (interfaces[key] || [])
      .filter((details) => details.family === 'IPv4')
      .map((detail) => detail.address.replace('127.0.0.1', 'localhost'))

    result = result.concat(ips)
  })

  return result
}
```



# 248 - 72e021b 重构整理文件位置

改动部分:

- 新增`open: ^7.0.3` 未使用(详 **`open`**)
- 调整文件位置,根据功能划分于不同的文件夹

## open

[`open-npm`](https://www.npmjs.com/package/open) 打开诸如 URL、文件、可执行文件之类的东西。跨平台。在`vite`用于快速打开浏览器。



# 249 - 957945f 支持配置`--open`打开浏览器

改动部分:

- `node/cli.ts`,新增判断open参数,true则利用`require('./utils/openBrowser').OpenBrowser(http://${addresses[0]}:${port})`打开浏览器
- 新增`bin/openChrome.applescript`、`src/node/utils/openBrowser.ts` (搬运 [` create-react-app`](https://github.com/facebook/create-react-app/blob/bb64e31a81eb12d688c14713dce812143688750a/packages/react-dev-utils/openBrowser.js))



# 250 - 29d0bcd changelog

## [0.11.2](https://github.com/vuejs/vite/compare/v0.11.1...v0.11.2) (2020-05-07)

### Bug Fixes

- 防止`esbuild options`冲突 ([bd58858](https://github.com/vuejs/vite/commit/bd588584231cd41fb016811cf22f76d0ffa13c72))
- 修复构建时对`web_modules`的处理 ([fc75323](https://github.com/vuejs/vite/commit/fc75323ff5861318a77c0680eb94a094ceee0b27))
- 构建模式下,对`css`的`data uri`不应该做资源转换处理 ([e01e26d](https://github.com/vuejs/vite/commit/e01e26dc93070b995d75784bb48e97a024148338)), closes [#66](https://github.com/vuejs/vite/issues/66)
- `hot.accept`没有裹上`if (__DEV__)`将会被警告 ([7aaf458](https://github.com/vuejs/vite/commit/7aaf45816fe5ceadb163b5faa294eebf26044c62))

### Features

- 支持`--open`标志 ([957945f](https://github.com/vuejs/vite/commit/957945faada703513174151a4fff4cf2f97f6efc))



================================================
FILE: 251-260/251-260.md
================================================
# 251 - db52dd3 v0.11.2

release v0.11.2

> 我在某版本号规范上看到,如果改动了代码功能,都应该第二位增加,第三位是文档类的修改。但是现在似乎让我感觉不对,因为我们每次修改发布`npm`如果是文档,不需要修改小版本号提交上去。
> 功能多了或者修复的多了就第二位增加比较对。



# 252 - c51f335 readme

由于之前整理了文件的位置,所以文档内引用了文件的`url`需要修改。



# 253 - baa020e bump `esbuild` version

`esbuild: "^0.2.0"` -> `esbuild: "^0.2.4"`

> lock也更新了,所以是有必要的



# 254 - ca421cd fix [#61](https://github.com/vuejs/vite/issues/61) 无法拓展类型

他遇到的问题是引入不了`ts`类型的文件,大概是因为路径出错找不到文件类型(`resolver.requestToFile`),windows下并没有遇到这个问题。

> windows下`esbuild`也无法使用,需要升级`esbuild v0.2.6`。
>
> 修改方法使用`path.posix`,`js`的`hmr`问题会得到解决。



# 255 - c7a5a69 bump `esbuild`

`esbuild`升级,`windows`下可以转换`ts`了



# 256 - b1d6be7 完善错误提示

改动部分:

- 新增`ora ^4.4.4`,优雅的“转圈圈”
- 增加`esbuild`代码位置的错误提示

![1](./1.png)



# 257 - 38b8ada changelog

## [0.11.3](https://github.com/vuejs/vite/compare/v0.11.2...v0.11.3) (2020-05-07)

### Bug Fixes

- 修复模块重写结果 (fix [#61](https://github.com/vuejs/vite/issues/61)) ([ca421cd](https://github.com/vuejs/vite/commit/ca421cdf9348076a53ad1ff1a9e6ee4095776eae))

### Features

- 改进构建错误输出 ([b1d6be7](https://github.com/vuejs/vite/commit/b1d6be7cf3e436fce7b187d2139ee43349ca5f40))



# 258 - 3a81be4 release v0.11.3

release v0.11.3



# 259 - 6b63b34 replace构建支持输出`soucemap`

### 开发环境下一个`Vue组件`被分成3块,那`sourcemap`如何粘合起来的?

第一块为`js`输出组件,第二块`render`,第三块`css`。

```typescript
updateStyle("92a6df80-0", "/Comp.vue?type=style&index=0") // css
__script.__scopeId = "data-v-92a6df80"
import { render as __render } from "/Comp.vue?type=template" // render
__script.render = __render
__script.__hmrId = "/Comp.vue"
__script.__file = "E:\\vite\\test\\temp\\Comp.vue"
export default __script // 自身
```

我们可以拦截`genSourceMapString`方法,输出`sourcemap`,分别创建两个文件作为比较,`my`为**第一块**`js`,`template`为第二块`render`:

![2](./2.png)

使用`reverse-sourcemap`转换:

```shell
reverse-sourcemap -o my -v my.js.map
```

```sh
reverse-sourcemap -o template -v template.js.map
```

查看两者路径比对:

![my](./3.png)

![template](./4.png)

发现文件转换后的内容一致,并没有任何不同(`...`为省略,`sourcemap`是不一样的,不转换查看`sourcesContent`也会发现一样,不一样的是对应位置):

```typescript
<template>
...
</template>

<script lang="ts">
...
</script>

<style scoped lang="scss">
...
</style>

```

通过打印发现,`template`和`script`的`map`转换内容是一样的,把目标缩短到`const descriptor = await parseSFC(root, filePath, ctx.body)` -> `parseSFC` ->`resolveCompiler`  -> `'@vue/compiler-sfc'包` -> `parse`。

`block.content`为`<script>` `<template>`内容。

```typescript
#compiler-sfc/parse.ts 关键代码

if (sourceMap) {
    const genMap = (block: SFCBlock | null) => {
      if (block && !block.src) {
        block.map = generateSourceMap(
          filename,
          source,
          block.content,
          sourceRoot,
          !pad || block.type === 'template' ? block.loc.start.line - 1 : 0
        )
      }
    }
    genMap(descriptor.template)
    genMap(descriptor.script)
    descriptor.styles.forEach(genMap)
  }

function generateSourceMap(
  filename: string,
  source: string,
  generated: string,
  sourceRoot: string,
  lineOffset: number
): RawSourceMap {
  const map = new SourceMapGenerator({
    file: filename.replace(/\\/g, '/'),
    sourceRoot: sourceRoot.replace(/\\/g, '/')
  })
  map.setSourceContent(filename, source)
  generated.split(splitRE).forEach((line, index) => {
    if (!emptyRE.test(line)) {
      const originalLine = index + 1 + lineOffset
      const generatedLine = index + 1
      for (let i = 0; i < line.length; i++) {
        if (!/\s/.test(line[i])) {
          map.addMapping({
            source: filename,
            original: {
              line: originalLine,
              column: i
            },
            generated: {
              line: generatedLine,
              column: i
            }
          })
        }
      }
    }
  })
  return JSON.parse(map.toString())
}
```

#### `generated.split`做了什么?

组件的起始行偏移值,我们可以把组件第一行空1行,发现`lineOffset`为`1`。

把`js`转换成`ast`,`ast`内记录了行数,利用`addMapping`方法对应行的映射。

> `render`需要调整映射一次,比`js`的要复杂,而`js`仅仅需要偏移`lineOffset`,想了解可以看`compiler-sfc`的`mapLines`

我们还试试`template`报错,会出现什么情况吧,因为会转换为render,我们触发`undefinded`即可。

```html
<div class="a" v-if="foo.foo.foo.foo.foo" />
```

![5](./5.png)

点击`E:\vite\test\temp\Comp.vue:3`:

![6](./6.png)

结论:**`sourcesContent`是一样的,但是映射是不一样的,所以不是所谓的粘合**。

[source-map](https://www.npmjs.com/package/source-map#sourcemapgenerator)

### 那构建的`sourcemap`为什么只有一个文件,不需要合并吗?

我们再使用`reverse-sourcemap`转换我们的文件。

![7](./7.png)

发现连文件夹对应的位置都还原了,100%还原效果(没用的代码`treeshaking`掉了)。

查阅`rollup-plugin-vue`,能发现`<template> <style> <script>`都有自己的`sourcemap`。

`sourcemap`的合并也是很简单的,两个合并起来也相当于代码转换,把偏移的位置给加上即可。

所以说使用rollup转换代码,只要确保返回自己的`sourcemap`即可,rollup自动会帮你合并的,这也是为什么`vue`默认生成`sourcemap`,控制rollup是否开启`sourcemap`操作即可。

 [source-map](https://stackoverflow.com/questions/19330344/how-to-read-base64-vlq-code)

> 注意,mappings的VLQ的值都是**相对值**,不管多少个分号,值都要从第一个分号累加起来。
>
> 我们使用`magic-string`的`generateMap({ hires: true })`生成各自的map就可以了,rollup帮我们自动合并处理(没有则返回null)。



# 260 - 19f8358 在test中删除`spinner`

因为test环境下`process.env.NODE_ENV === ‘test’`,所以可以根据这删除转圈圈。



================================================
FILE: 261-270/261-270.md
================================================
# 261 - 44a8250 增强错误输出

改动部分:

- `server/serverPluginVue.ts`,错误详细位置输出。

![1](1.png)

```typescript
if (errors.length) {
    console.error(chalk.red(`\n[vite] SFC template compilation error: `))
    errors.forEach((e) => {
      if (typeof e === 'string') {
        console.error(e)
      } else {
        console.error(
          chalk.underline(
            `${filename}:${e.loc!.start.line}:${e.loc!.start.column}` // 文件位置 包括列行
          )
        )
        console.error(chalk.yellow(e.message)) // 报错提示信息
        const original = template.map!.sourcesContent![0]
        console.error(
          generateCodeFrame(original, e.loc!.start.offset, e.loc!.end.offset) // 输出视图
        )
      }
    })
  }
```

## 能够准确找到错误位置,原理是什么?

往上一步步寻找`require.resolve('@vue/compiler-sfc').compileTemplate`编译会返回这个`errors`。

![2](2.png)

我们逆向寻找,在触发的地方打上断点:

![3](3.png)

寻找调用栈相关代码:

![4](4.png)

`traverseNode`转换AST树为代码,遇到props需要调用`buildProps`进行参数分析,发现转换的是指令相关代码,获取相对应的转换器`transformModel`。

![6](6.png)

**触发原因为当前AST树节点非`input` `textarea` `select`标签**。

![7](7.png)

传递当前`loc`:

![8](8.png)

返回`error`:

![9](9.png)

`vite`检测到`errors`:

![10](10.png)

调用本次commit改动的方法,输出到控制台中。

#### `generateCodeFrame`做的事情

通过当前`template`的`sourcemap`获取到源码,与`error`的`loc`偏移量一同传递给`generateCodeFrame`:

![11](11.png)

换行符分割计算出行数,遍历行,通过字符累加count,如果  count >=start,即可以判断目标为当前行,`range`被固定为`2`,即输出目标行上下各三行。

`' '.repeat(3 - String(line).length)`: 为了保持排版,行数为双数需要去除掉一个空格,也就是说保持百行的代码排版。

![12](12.png)

```typescript
function generateCodeFrame(source, start = 0, end = source.length) {
    const lines = source.split(/\r?\n/); // 行
    let count = 0;
    const res = [];
    for (let i = 0; i < lines.length; i++) {
        count += lines[i].length + 1;
        if (count >= start) {
            for (let j = i - range; j <= i + range || end > count; j++) {
                if (j < 0 || j >= lines.length)
                    continue;
                const line = j + 1;
                res.push(`${line}${' '.repeat(3 - String(line).length)}|  ${lines[j]}`);
                const lineLength = lines[j].length;
                if (j === i) { // 如果等于当前行,需要在loc长度的代码加上'^'标记
                    // push underline
                    const pad = start - (count - lineLength) + 1;
                    const length = Math.max(1, end > count ? lineLength - pad : end - start);
                    res.push(`   |  ` + ' '.repeat(pad) + '^'.repeat(length));
                }
                else if (j > i) { // 如果长度覆盖了不止一行
                    if (end > count) {
                        const length = Math.max(Math.min(end - count, lineLength), 1);
                        res.push(`   |  ` + '^'.repeat(length));
                    }
                    count += lineLength + 1;
                }
            }
            break;
        }
    }
    return res.join('\n');
}
```

> 分析template的AST,转换AST为代码过程中检测是否错误,错误返回AST的位置,通过`sourcemap`获取到源码,通过AST错误位置切割源码,打印源码错误位置与信息到控制台



# 262 - 7bfba6c ci 删除`esbuild .install文件`兼容`esbuild`的bug 

![13](13.png)

![14](14.png)

[`esbuild`为什么快? ](https://juejin.cn/post/6992448851990806558)

`esbuild`如何运行的:`child_process`运行`esbuild.exe`,通过`promise`,`esbuild.exe`结束后,`child_process`触发`close`,`close`回调执行`resolve`返回数据。

[`postinstall`是什么](https://docs.npmjs.com/cli/v7/using-npm/scripts#examples): 在执行`npm install`也会自行`postinstall`脚本。

阅读`esbuild` `install.js`,作用为:

- 根据`process.platform`判断平台,当前为`windows`,执行`installOnWindows`,下载`esbuild-windows-64@0.2.6`,改名为`esbuild.exe`
- 把启动`esbuild.exe`的`js`代码写进`bin/esbuild`(为什么要这样做... 直接bin不行吗?除非是写`esbuild`的时候多个平台用到代码,为了聚合起来,连`install`文件都是自动生成的)

![15](15.png)

**手动删除原因:**

```typescript
// esbuild 0.2.12 修复了该BUG
// 事实证明,一些包管理器 (例如 yarn) 
// 在我们已经安装之后,有时重新运行这个包的 postinstall 脚本。
// 这意味着这个脚本必须是幂等(指的是同样的方法被执行一次与连续执行多次的效果是一样的)的。 
// 如果发生了这种情况,我们将跳过这次包的安装
if (fs.existsSync(installDir)) {
    return false;
}
```

> 查看了`vite`仓库目前最新的代码,已经删除了该改动



# 263 - df77197 重构简化`esbuild`服务使用

改动部分:

- `node/cli.ts`: 构建完成后调用`process.exit(0)`(目前没有遇到影响,不知道如何触发)
- `node/esbuildService`: 简化代码,新增`  stopService()`(详 **改动二**)
- `node/build/buildPluginEsbuild.ts`: 简化代码,去除`generateBundle`,不使用`stop()`
- `node/build/index.ts`: rollup构建完成后,调用`stopService()`

## 改动二

stop方法为`esbuild`包内方法,即`child_process`的`stop`停止子进程:

```typescript
 // This stops the service, which kills the long-lived child process. Any
  // pending requests will be aborted.
  stop(): void;
  
  stop() {
        child.kill();
      }
```



# 264 - 4df840d bump deps

![16](16.png)



# 265 - ffd1fee 组件支持`src` 引入文件

改动部分:

`node/server/serverPluginCss.ts`:  不处理遇到来自组件`src`的请求,去除`.modules.css`的`hmr`(迁移)

`node/server/serverPluginEsbuild.ts`: 去除`jsx tsx`的`hmr`(迁移)

`node/server/serverPluginHmr.ts`:  非`.css`文件,但是`.module.css`的文件可以触发`hmr`

`node/server/serverPluginJson.ts`: 去除`json` `hmr`(迁移)

`node/server/serverPluginVue.ts`: 新增`resolveSrcImport`方法,新增资源引入的`hmr`,如`style`的`src`触发`vue-style-update`事件(详 改动五)

> 现在有测试用例,比较好看出尤大想解决什么问题

### 改动五

除了设置`koa-ctx、block.content`文件内容和返回文件路径外,其调用`ensureMapEntry`和`serverPluginModulesRewrite.ts`的收集关系链调用的方法是一样的,建立文件关系,为了`hmr`能够准确寻找该触发的更新事件和目标。

```typescript
async function resolveSrcImport(
  block: SFCBlock,
  ctx: Context,
  resolver: InternalResolver
) {
  const importer = ctx.path
  const importee = slash(path.resolve(path.dirname(importer), block.src!))
  const filename = resolver.requestToFile(importee)
  await cachedRead(ctx, filename)
  block.content = ctx.body

  // register HMR import relationship
  debugHmr(`        ${importer} imports ${importee}`)
  ensureMapEntry(importerMap, importee).add(ctx.path)
  return filename
}
```

#### 为什么普通文件都直接触发`handleJSReload`?

可以去查看[洋葱模型](https://github.com/Kingbultsea/vite-analysis/blob/master/221-230/221-230.md#%E6%B4%8B%E8%91%B1%E6%A8%A1%E5%9E%8B%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F),最后执行的是`node/serverPluginModuleRewrite.ts`,注册import importee关系map,只要注册了,就可以根据普通文件路径触发`hmr`。

#### `vue`组件到底怎么处理`src`的呢?

注册import importee关系,`AST`描述树通过读取文件资源内容,手动设置`content`字段。

所以可以和标签里的内容保持一致性的处理。



# 266 - f3265c1 `create-vite-app` 使用规范的`html`作为模板 [#76](https://github.com/vitejs/vite/pull/76)

使用 `create-vite-app` 创建应用程序时,生成的 index.html 并不真正有效。我在现有内容周围添加了基本的 html5 内容。



# 267 - 9f6f0a6 修复windows下,路径改写不正确的问题 [#73](https://github.com/vitejs/vite/pull/76)

使用了`path.posix`的方法兼容好了。

> 但是有没有注意到265的改动五中的importee,一样没修复,导致`src`的`hmr`失效,后续尤大应该会整理代码,合并两者的。



# 268 - 82414b8 整理266的代码,合并两者功能

改动部分:

- `node/utils/pathUtils.ts`: 新增`resolveRelativeRequest`方法(详 **改动一**)
- `node/server/serverPluginModuleRewrite.ts`:去除路径处理,使用`resolveRelativeRequest`,代码迁移
- `node/server/serverPluginVue.ts`:去除路径处理,使用`resolveRelativeRequest`,也是代码迁移

### 改动一

把两个文件的功能整合了在一起。

```typescript
import path from 'path'
import slash from 'slash'

export const queryRE = /\?.*$/
export const hashRE = /\#.*$/

export const cleanUrl = (url: string) =>
  url.replace(hashRE, '').replace(queryRE, '')

export const resolveRelativeRequest = (importer: string, id: string) => {
  const resolved = slash(path.posix.resolve(path.dirname(importer), id))
  const queryMatch = id.match(queryRE)
  return {
    url: resolved,
    pathname: cleanUrl(resolved),
    query: queryMatch ? queryMatch[0] : ''
  }
}
```

> query在`serverPluginModuleRewrite.ts`的作用为恢复原本路径的参数,另一个功能为如果改写前后路径相同就不继续调用改写了。



# 269 - 2053c8a 修复windows报错后无法退出结束测试的问题

![17](17.png)

> 需要把`package.json`的`build`,`rm -rf`给删除了,才能跑测试。
>
> 不过还是跑失败,提示文件夹资源被占用,无法`rmdir`去除,尤大肯定注意到了这个问题,我猜的是他目前先快速解决,后续用ci配置windows的测试来发现问题。



# 270 - 946b978 ci 添加`appveyor.yml`进行windows下的自动化测试

[Appveyor](https://www.appveyor.com/docs/)

```typescript
environment:
  nodejs_version: "12" // node版本

install:
  - ps: Install-Product node $env:nodejs_version // 使用PowerShell安装node版本
  - yarn // 安装yarn

test_script:
  - git --version
  - node --version
  - yarn --version
  - yarn test

cache:
  - node_modules -> yarn.lock // yarn.lock变动node_modules的缓存也变动

build: off 
// 关闭MSBuild模式,MSBuild模式会导致AppVeyor寻找Visual Studio项目,并使用它去进行构建
// MSBuild 模式的替代方案是脚本模式。
// 此模式允许你通过运行任意脚本化操作而不是构建 Visual Studio 项目来进行构建。

```

使用过程,进入[Appveyor](https://ci.appveyor.com/projects/new),点击`Install AppVeyor App`按钮:

![18](18.png)

选择项目:

![19](19.png)

点击 项目右边的 `add`:

![20](20.png)

顺带`esbuild`的`install`脚本还发现了安装`esbuild win32`有问题。



================================================
FILE: 271-280/271-280.md
================================================
# 271 - 0c3c546 ci windows使用node x64架构

因为`esbuild`不支持在node x86架构中使用,所以切换为x64架构。

[node架构](https://newsn.net/say/node-arch.html):返回为其编译 Node.js 二进制文件的操作系统 CPU 架构。可能的值为 `'arm'`, `'arm64'`, `'ia32'`, `'mips'`, `'mipsel'`, `'ppc'`, `'ppc64'`, `'s390'`, `'s390x'`, `'x32'`, 和 `'x64'`.

ia32即x86。

```typescript
// esbuild install.js
// Pick a package to install
if (process.platform === 'linux' && os.arch() === 'x64') {
  installOnUnix('esbuild-linux-64');
} else if (process.platform === 'darwin' && os.arch() === 'x64') {
  installOnUnix('esbuild-darwin-64');
} else if (process.platform === 'win32' && os.arch() === 'x64') {
  installOnWindows();
} else {
  console.error(`error: Unsupported platform: ${process.platform} ${os.arch()}`);
  process.exit(1);
}
```

![1](1.png)



![2](2.png)

[本次ci运行信息](https://ci.appveyor.com/project/Kingbultsea/sbuild/builds/40544835)

> 64位状态下兼容32位软件,但是有些驱动什么专属的是不兼容的。



# 272 - 0345c36 拆分脚本,为windows ci添加测试`--forceExit`标记

![3](3.png)

上次`windows ci`运行测试错误后,`jest`并没有停止。

`--forceExit`: 强制Jest在所有测试运行完后退出。 对于一些由测试所生成但无法充分清理的资源来说,这是很有用的。



# 273 - 4f685b3 typo 272脚本拼写错误

`buiild` -> `build`



# 274 - 5741b79 [#71](https://github.com/vitejs/vite/issues/71)

dev与`build`统一使用`vue.runtime.esm-bundler`,为了一些依赖`vue`其他包的第三方库能在同一个地方统一获取。



# 275 - 904266b [#74](https://github.com/vitejs/vite/issues/74) 支持目录index解析

有一些包,会使用`import xxx from 'xxx'`的形式引入脚本,需要支持这种方式。

![4](4.png)



改动部分:

- `node/resolver.ts`: 方法更名,`ensureExt` ->  `resolveExt` (详 **改动一**)
- `node/serverPluginModuleRewrite.ts` :  `/util/` -> `/util` (详 **改动二**)

### 改动一

支持三种寻找方式。

- `foo/` -> `foo/index.js`
- `foo` -> `foo.js`
- `foo` -> `foo/index.js`

会引起`windows``BUG`?因为不能直接使用`/index`,需要根据系统修改路径分割号。(不会直接引起BUG,因为都是当作`publicPath`使用了,只是不规范,留坑)

![5](5.png)

```typescript
export const supportedExts = ['.js', '.ts', '.jsx', '.tsx', '.json']

const resolveExt = (id: string) => {
  const cleanId = cleanUrl(id)
  if (!/\.\w+$/.test(cleanId)) {
    const expectsIndex = id[id.length - 1] === '/'
    let inferredExt = ''
    for (const ext of supportedExts) {
      if (expectsIndex) {
        try {
          // foo/ -> foo/index.js
          statSync(id + 'index' + ext)
          inferredExt = 'index' + ext
          break
        } catch (e) {}
      } else {
        try {
          // foo -> foo.js
          statSync(id + ext)
          inferredExt = ext
          break
        } catch (e) {
          try {
            // foo -> foo/index.js
            statSync(id + '/index' + ext)
            inferredExt = '/index' + ext
            break
          } catch (e) {}
        }
      }
    }
    const queryMatch = id.match(/\?.*$/)
    const query = queryMatch ? queryMatch[0] : ''
    return cleanId + inferredExt + query
  }
  return id
}
```

### 改动二

改写`import`路径,如果发现其使用了`resolveExt`自动寻找到`index`,则删除路径的尾符号`/`,方便合并。

也就是支持`import '/util/'` -> `import '/util/index.js'`



# 276 - 7f952c7 重构 简化ext解析逻辑

不需要`foo/` -> `foo/index.js`,因为在`node/serverPluginModuleRewrite.ts` 改写`import`处理了

```typescript
try {
        // foo -> foo.js
        statSync(id + ext)
        inferredExt = ext
        break
      } catch (e) {
        try {
          // foo -> foo/index.js
          statSync(path.join(id, '/index' + ext))
          inferredExt = '/index' + ext
          break
        } catch (e) {}
      }
```





# 277 - 0be5c31 changelog

## [0.11.4](https://github.com/vuejs/vite/compare/v0.11.3...v0.11.4) (2020-05-07)

### Bug Fixes

- tests 去除 spinner ([19f8358](https://github.com/vuejs/vite/commit/19f8358a47251b35557f4c2bdd8a3ac2b7ef96c0))
- 修复windows路径解析 ([#73](https://github.com/vuejs/vite/issues/73)) ([9f6f0a6](https://github.com/vuejs/vite/commit/9f6f0a619af6f7fba22033b9540680862df3dc09))
- 修复windows路径解析 ([82414b8](https://github.com/vuejs/vite/commit/82414b88bb057630f096123fb820105817c4707c)), 关闭[#69](https://github.com/vuejs/vite/issues/69) [#72](https://github.com/vuejs/vite/issues/72)
-  支持目录index解析 (关闭[#74](https://github.com/vuejs/vite/issues/74)) ([904266b](https://github.com/vuejs/vite/commit/904266bc726e672926da3b01a8990dccd16d4e8b))
- vue使用esm-bundler build([5741b79](https://github.com/vuejs/vite/commit/5741b798c1dc535d83154e5c0e9f1c3e7e5f92b7)), 关闭[#71](https://github.com/vuejs/vite/issues/71)



# 278 - bddca2e v0.11.4

release v0.11.4



# 279 - eb0a885 [#56](https://github.com/vitejs/vite/issues/56) 支持`monorepos` `hmr`且修复`menorepos`无法使用`VUE SFC`组件的问题

[`MonoRepo`是什么](https://segmentfault.com/a/1190000038683978)

`workspace`包之间共享`node_modules`:[链接](https://www.jianshu.com/p/990afa30b6fe)

用户使用`yarn link`,引入包内的`SFC`组件,此时被插件改写为`@modules/XXX/XX.vue`,发送到浏览器,请求`@modules/XXX/XX.vue`,经过`serverPluginModuleResolve.ts`处理后没有调用下一个插件,固然vue没有被编译就会被发送到浏览器。

改动部分:

- `node/resolver.ts`: `publicPath`与`fiePath`互转过程中,如果发现路径曾在`serverPluginModuleResolve.ts`中被处理过,则返回被处理过的路径。
- `serverPluginModuleResolve.ts`使用`serve`方法后,需要调用next进行下一个组件,且对文件添加`hmr`。(详 **改动二**)

### 改动二

识别到`@modules`,获取具体文件路径,302跳转,再次识别到`@modules`,调用`serve`,返回文件内容。

现在返回到文件内容后,需要经过`serverPluginVue.ts`处理,所以调用`next`。

软链的路径是这里寻找到的(实际就指向父级,`workspace-a/Comp.vue`不会报错, `packages/workspace-a/Comp.vue`会报错,`vite`并没做任何处理):

![6](6.png)

如果像`yarn link packages/workspace-a`套了多一个路径:

![7](7.png)

> 当前没有测试用例,可以自行通过yarn link来进行测试,修复点为`next()`的调用。



# 280 - 08b6259 changelog

## [0.11.5](https://github.com/vuejs/vite/compare/v0.11.4...v0.11.5) (2020-05-07)

### Bug Fixes

- 支持monorepos (close [#56](https://github.com/vuejs/vite/issues/56)) ([eb0a885](https://github.com/vuejs/vite/commit/eb0a88514df344cbe4be3165cfa1a26af4f9f6ef))



================================================
FILE: 281-290/281-290.md
================================================
# 281 - c2806d7 v0.11.5

release v0.11.5



# 282 - 29099ae 修复在不进行本地安装的情况下处理vue问题

改动部分:

- `node/build/buildPluginResolve.ts`,去除没必要的srcRoots资源目录;`@vue/`资源将统一从`@vue/runtime-dom/dist/**.esm-bundler.js`中获取,即与vue引入的包统一。
- `node/build/index.ts`,去除调用`node/build/buildPluginResolve.ts createBuildResolvePlugin`的`srcRoots`参数传入,符合改动。
- `node/server/serverPluginModuleResolve.ts`,`@vue/`资源将统一从`@vue/runtime-dom/dist/**.esm-bundler.js`中获取。

在[`commit-5741b79`](https://github.com/vitejs/vite/commit/5741b798c1dc535d83154e5c0e9f1c3e7e5f92b7)中,指向的是`vue/dist/vue.runtime.esm-bundler.js`,现在调整为`@vue/runtime-dom/dist/**.esm-bundler.js`,不需要通过`vue`作为中介去获取其包内方法达到统一。



# 283 - 4c5a31e 修复index解析双附加

改动部分:

- `node/server/serverPluginModulesRewrite.ts`,拓展文件后缀,遇到`lodash/index`应该删除`index`再进行拓展,即`lodash` + `/index.js`;对于自身`import`自身的情况,不再做`importer` `importee`映射处理。
- `node/resolver.ts resolveExt`,拓展后缀需要使用clearnId删除`url hash`(但实际上`url hash`并不会传递给服务器的,浏览器有规定,提交过PR,但后来发现rollup构建会传入`hash`,集成的测试是需要的)



# 284 - cd8794c 如果是src导入,则仅销毁非vue文件上的vue缓存

`node/server/serverPluginHmr.ts `,对于非`vue`文件的hmr,需要删除`VUE SFC`缓存。

并不清楚该改动实际作用:

`.vue`通过`src`引入的`.js`,调用的删除不正确。

![1](1.png)

> 外部引入资源,只会触发vue-reload。



# 285 - 3ba0104 changelog

# [0.12.0](https://github.com/vuejs/vite/compare/v0.11.5...v0.12.0) (2020-05-07)

### Bug Fixes

- 修复index解析双附加 ([4c5a31e](https://github.com/vuejs/vite/commit/4c5a31e7b32e63ffb219cf75d8c69ce482a5753d))
- 修复在不进行本地安装的情况下处理vue问题 ([29099ae](https://github.com/vuejs/vite/commit/29099ae214d9ad8d8bfe3b930a509087450f3e38))
- 如果是src导入,则仅销毁非`vue`文件上的`vue`缓存 ([cd8794c](https://github.com/vuejs/vite/commit/cd8794c380559aae45908a64708214b2d0778c93))



# 286 - v0.12.0 bdec134

release v0.12.0



# 287 - a4694ba create-vite-app v1.0.4 去除`yarn.lock`

去除`yarn.lock`



# 288 - c2e5044 readme

文档中所有`vite`改为Vite。



# 289 - 8e1e09c create-vite-app 添加debug命令 [#84](https://github.com/vitejs/vite/pull/84)

为了调试起来更方便。![2](2.png)



# 290 - e1dd37f readme

### 和 [Snowpack](https://www.snowpack.dev/) 有什么不一样?

// ...

这就是说,因为Vite支持解析`web_modules`,所以您可以在Vite项目中使用Snowpack预绑定依赖项(这可以减少开发期间的网络请求),以加快整个页面的重新加载。


================================================
FILE: 291-300/291-300.md
================================================
# 291 - 9061e44 支持使用`js`引入静态资源 + 打包`public`资源特殊处理

改动部分:

- `node/build/buildPluginAsset.ts resolveAsset`:  `public`开头的路径,将被打包进以`publicBase`参数的值为文件夹(详 **改动一**)
- `node/build/buildPluginCss.ts`:调用`resolveAsset`没有检测到资源,则不做任何处理,以免空资源被注入`bundle`中(`registerAssets`)
- `node/build/index.ts`:如果`/public`存在,则复制其资源(详 **改动三**)
- 新增`node/server/serverPluginAssets.ts`,静态资源转换为`export default "路径"`(详 **新增四**)

### 改动一

之前的资源统一打包的路径为:`slash(path.join(publicBase, assetsDir, resolvedFileName))`,即现在遇到`/^public(\/|\\)/`,去除`assetsDir`。

```typescript
// 获取相对路径
const pathFromRoot = path.relative(root, id)

// 检测是否public开头的文件路径
  if (/^public(\/|\\)/.test(pathFromRoot)) {
    // assets inside the public directory will be copied over verbatim
    // so all we need to do is just append the baseDir
    resolved = {
      content: null,
      fileName: null,
      // 是则资源会被放到publicBase传入的参数
      url: slash(path.join(publicBase, pathFromRoot))
    }
  }
```

### 改动三

`import icon from './public/icon.png'` -> **改动一**识别到public,则不把其路径改为`assets`,意义就是不要将`public`的资源打包进`assets`中。

```typescript
// vite 写入
if (write) {
    //skpi...
    
    // /public的资源都会被打包到/public中,全部均为复制
    const publicDir = path.resolve(root, 'public')
    if (await fs.pathExists(publicDir)) {
      await fs.copy(publicDir, path.resolve(outDir, 'public'))
    }
}
```

![1](1.png)

### 新增四

转换为`js`模块语言。

```typescript
import { Plugin } from '.'
import { isImportRequest, isStaticAsset } from '../utils'

export const assetPathPlugin: Plugin = ({ app }) => {
  app.use(async (ctx, next) => {
    if (isStaticAsset(ctx.path) && isImportRequest(ctx)) {
      ctx.type = 'js'
      ctx.body = `export default ${JSON.stringify(ctx.path)}`
      return
    }
    return next()
  })
}

```

> `import { Plugin } from '.'`,自动加载`/index.ts`



# 292 - 12a5d47 支持 --debug 标志 for windows

改动部分

`node/cli.ts`: 参数中传入`debug`,将设置`process.env.DEBUG = true`。

> 本次commit,添加了`crocess-env`,为了windows能够正确设置环境变量(程序变量?)



# 293 - 30c9bea 调整292

需要添加上`vite:`,否则不能触发(DEBUG包,已经有解析),如`require('debug')('vite:build:asset')`。

![2](2.png)



# 294 - e5cf447 `hmr`功能,支持`hot.dispose`

改动部分

- readme 因为新增了`hot.dispose`用例,需要给大家说一下dispose的使用(详 **改动一**)
- `node/server/serverPluginHmr.ts`
- `client/client.ts`,先进行`disposer()`,再发送请求获取新的js文件,获取完成后调用accept的回调方法,这样dispose触发就可以获取旧变量了,新的js文件将按照原来流程被调用callback
- `node/server/serverPluginHmr.ts`,AST语法树识别`dispose`(详 **改动四**)

### 改动一

使用`hot.dispose`,`callback`调用栈为改动前的值。

```js
function setupSideEffect() {}
function cleanupSideEffect() {}

setupSideEffect()

if (__DEV__) {
  hot.dispose(cleanupSideEffect)
}
```

测试用例(`testHmrManual.js`):

```typescript
import { hot } from '@hmr'

export const foo = 1

if (__DEV__) {
  hot.accept(({ foo }) => {
    console.log('foo is now: ', foo)
  })

  hot.dispose(() => {
    console.log('foo was: ', foo)
  })
}

// jest
      test('hmr (manual API)', async () => {
        await updateFile('testHmrManual.js', (content) =>
          content.replace('foo = 1', 'foo = 2')
        )
        await expectByPolling(() => logs[logs.length - 1], 'foo is now:  2')
        // there will be a "js module reloaded" message in between because
        // disposers are called before the new module is loaded.
        expect(logs[logs.length - 3]).toMatch('foo was:  1')
      })
```

### 改动四

根据语法树识别到`callee`名称为`dispose`,即在对应位置插入当前调用该`hot.dispose api`的`.js`文件名称。

![3](3.png)

```typescript
if (node.callee.property.name === 'dispose') {
    // inject the imports's own path to dispose calls as well
    s.appendLeft(node.arguments[0].start!, JSON.stringify(importer) + ', ')
}
```



# 295 - 5d3cc75 重构ci,封装代码

改动部分:

- `node/cli.ts`: 完全重构了,整理了一下代码结构(详 **改动一**)

### 改动一

1. 封装启动serve的服务为`runServe`,`build`构建为`runBuild`
2. 参数处理统一封装为`parseArgs`



# 296 - a882aa4 cli新增 --help 标志

```typescript
function logHelp() {
  console.log(`
Usage: vite [command] [args] [--options]

Commands:
  vite                       Start server in current directory.
  vite serve [root=cwd]      Start server in target directory.
  vite build [root=cwd]      Build target directory.

Options:
  --help, -h                 [boolean] 显示帮助
  --version, -v              [boolean] 显示版本
  --port                     [number]  服务端口
  --open                     [boolean] 自动打开浏览器
  --base                     [string]  构建public位置 (default: /)
  --outDir                   [string]  构建输出位置 (default: dist)
  --assetsDir                [string]  在dist文件下,设置输出资源位置 (默认: assets)
  --assetsInlineLimit        [number]  最大行内式资源大小bytes(默认: 4096 byte)
  --sourcemap                [boolean] 构建生成sourcemap (默认: false)
  --minify                   [boolean | 'terser' | 'esbuild'] 设定压缩服务(默认: 'terser')
  --jsx-factory              [string]  (默认: React.createElement)
  --jsx-fragment             [string]  (默认: React.Fragment)
`)
}
```

#### `vite`直接把`public`的复制到`dist/public`,是一个写死的代码,如果我设置了`--base '/asd'`是不是会报错?

雀氏。

![4](4.png)



# 297 - 5111d42 调整`BuildOptions`的参数位置

也许更好看。(但是对于我来说,改了和没改一样)

不如把参数也跟着类型的位置调整?更美观了。

![5](5.png)



# 298 - 3af44fc readme 添加关于TS模块隔离的注释

### TypeScript

从v0.11开始, `Vite`支持 在`*.vue` 中设置`<script lang="ts">` , 也可以引入 `.ts` 文件。 请注意,`Vite`**不**执行类型检查 - 类型检查由IDE和构建过程负责 (你可以在在构建脚本中执行 `tsc --noEmit`). 考虑到这一点, `vite`使用[`esbuild`](https://github.com/evanw/esbuild)将TypeScript转换为JavaScript,速度大约是原生`js` 写的`tsc`的20~30倍,HMR更新可以在不到50毫秒的时间内反映在浏览器中。

> `esbuild`帮你构建`ts`了,所以不会做类型检测(`esbuild`不做这个,对于类型会替换为空格,[劝劝`evanw`?](https://github.com/evanw/esbuild/issues/95))

#### 执行`tsc --noEmit`可以检测`.ts`,也可以检测`.vue`麽?[#749](https://github.com/vitejs/vite/issues/749)

很多人说,自己做一个类型检测并不难,提取`<script>`内容为`.ts`就可以检测了。

尤大说,有一个[工具](https://github.com/vuedx/languagetools)可以做到,然后过了三个月尤大添加上了这个功能了。

![6](6.png)



# 299 - eab49a4 hmr API更换路径为`vite/hmr` + 添加`hmr`类型

改动部分:

- `package.json`:  `files`包括`hmr.d.ts`(files即上传到`npm`的文件),jest添加 `--clearCache`(详 **改动一**)
- `hmrClientId`更改为`vite/hmr`,即`@hmr`相关的路径,都需要更改为`vite/hmr`。

### 改动一

### `--cache`

是否使用缓存。默认为true。使用 `--no-cache` 禁用缓存。注意:只有在遇到与缓存有关的问题时,才应禁用缓存。平均而言,禁用缓存会使Jest至少慢两倍。

如果要检查缓存,请使用 `--showConfig` 并查看 `cacheDirectory` 值。如果需要清除缓存,请使用 `--clearCache` 。

转换脚本已更改或Babel已更新,Jest无法识别这些更改?

尝试使用 [`--no-cache`](https://jestjs.io/zh-Hans/docs/cli#--cache) 选项。 Jest 会缓存转换的模块文件来加速测试的执行。如果你正在使用自己的自定义转换器,考虑添加`getCacheKey` 方法 [getCacheKey in Relay](https://github.com/facebook/relay/blob/58cf36c73769690f0bbf90562707eadb062b029d/scripts/jest/preprocessor.js#L56-L61)。

> 所以尤大遇到缓存问题了。(这就不再深入探究了,问他本人吧... 蛤蛤蛤 我windows系统也不好测)



# 300 - 393fc52 构建返回的hot需要带`dispose`

之前也有提到过,免得报错,所以加一个空白调用。

![7](7.png)



================================================
FILE: 301-310/301-310.md
================================================
# 301 - 253da59 changelog

### [0.13.0](https://github.com/vuejs/vite/compare/v0.12.0...v0.13.0) (2020-05-08)

#### Features

- **`hmr`:** `hmr`路径更换为 `vite/hmr` + 添加类型 ([eab49a4](https://github.com/vuejs/vite/commit/eab49a4b7dd7e3bb0ff215c7e7937814cd63bb4f)), 关闭[#92](https://github.com/vuejs/vite/issues/92)
- cli添加帮助信息 ([a882aa4](https://github.com/vuejs/vite/commit/a882aa48cb447ec3b84019a2ce838ee75d848555))
- **`hmr`:** 支持`hot.dispose` ([e5cf447](https://github.com/vuejs/vite/commit/e5cf447762c73aafd686a69a8b0d8e24c4e00048))
- cli支持 --debug 标志 ([12a5d47](https://github.com/vuejs/vite/commit/12a5d47b2bf2cb7e1badae2e2ee1129c0ae29fe5))
- 支持`js`引入资源 + 对 `/public`文件夹的特殊处理 ([9061e44](https://github.com/vuejs/vite/commit/9061e442a7de8f94ca2931299450464f78f82148))



# 302 - 04d5561 v0.13.0

release v0.13.0



# 303 - 04d5561 补充301的`changlog`

因为`vite/hmr`的更改,使用`vite` `hmr`的人都需要更改,这是一项破坏性的改动。

![1](1.png)



# 304 - d85e751 `create-vite-app` v1.0.5

release `create-vite-app` v1.0.5



# 305 - e2185b4 [#90](https://github.com/vitejs/vite/pull/90)文档语法错误

英文老师来了



# 306 - 3653793 `web_modules`后缀处理 + 处理`Import`语句的错误提示

改动部分:

- `node/server/serverPluginModuleResolve.ts`: 处理`web_modules`时,如果请求id非`.js`后缀,则自动添加`.js`后缀。
- `node/server/serverPluginModulerEWRITE.ts`:利用`es-module-lexer`包中的`parse`处理`import`语句,如果报错,则提示 (如果你在使用 ` JSX`, 请确保文件名称为 `.jsx` 后缀.)

> 也不确定是啥错,先提示一下可能是`JSX`没有提前被编译的错。



# 307 - a847621 重构简化`web_modules`处理逻辑

没有改造前,代码重复。

![2](2.png)

重构后:

![3](3.png)

> 把重复代码合并



# 308 - b7f5ad2 [#95](https://github.com/vitejs/vite/pull/95) 使用`brotli-size`输出经`brotli`压缩后的文件大小

新增部分:

- 添加[`brotli-size`](https://www.npmjs.com/package/brotli-size)包(详 **改动一**)

### 改动一

HTTP服务器程序Apache和nginx支持Brotli压缩算法,检测站点有没有使用,可以查看**content-encoding**响应头是否为**br**,**accept-encoding**是包括**br**。

`Brotil`是很常用的压缩算法,可以输出`Brotil`压缩后的大小,[Skypack](https://www.skypack.dev/)也使用该压缩算法。经过基准测试,大约增加了3%的构建时间。

[新的开源压缩算法Brotli](https://zhuanlan.zhihu.com/p/33405940)



# 309 - 7ffa9c0 [#97](https://github.com/vitejs/vite/pull/95) windows hmr

这是一个windows路径引发的问题,我之前也提到过windows下,`importer`与`importee`关系路径不正确(`serverPluginModuleRewrite.ts`之前已修复)。

![4](4.png)

`importee`的路径错误了(重写你的import语句,根据importer获取其importee的publicPath,所以现在这里是publicPath在windows下错误输出,至于为什么平台没有测试出来,是因为尤大没有做`hot.accept`的`hmr`测试,所以这其实是windows下`serverPluginHmr,ts`改写 引用`hmr`的路径的BUG)。

![6](6.png)

修复位置:

```typescript
# serverPluginHmr.ts rewriteFileWithHMR registerDep
const depPublicPath = slash(path.resolve(path.dirname(importer), e.value))
```

修复后:

```typescript
const depPublicPath = slash(
      path.isAbsolute(e.value) // 如果是绝对路径,则不做处理,因为它就是publicPath
        ? e.value
        : resolveImport({ importer, id: e.value, resolver })
)
```

![5](5.png)

`resolveImport`:从`serverPluginModuleRewrite`中抽离改写import语句的功能。

> 主要原因,尤大遗忘漏了。



# 310 - e98102a 调整`cli.ts`

调整`logHelp`的位置,放在最外层堆。

变量名称修改:`s`-> `start`


================================================
FILE: 31-40/commit-31-40.md
================================================
# 31 - c45e066 整合```css hmr```

把更新渲染```<style>```的代码,整合到```client```中。省去每次提取```<styele>```模块都需要经过服务器语法词汇分析的过程。



# 32 - ef95a00 http缓存与读取文件的缓存

### 添加的包

#### ```koa-conditional-get```:

Conditional Get 又名 条件式请求 ,常见实现有```Last-Modified``` 和 ```ETag``` 两种。

#### ```koa-etag```:

为```koa```的响应设置```etag-header```。

#### ```lru-cache```:

要搞清楚```LruCache``` 是什么之前,首先要知道 ```Android``` 的缓存策略。其实缓存策略很简单,举个例子,就是用户第一次使用网络加载一张图片后,下次加载这张图片的时候,并不会从网络加载,而是会从内存或者硬盘加载这张图片。

缓存策略分为添加、获取和删除,为什么需要删除缓存呢?因为每个设备都会有一定的容量限制,当容量满了的话就需要删除。

那什么是 ```LruCache```呢?其实```LRU(Least Recently Used)``` 的意思就是近期最少使用算法,它的核心思想就是会优先淘汰那些近期最少使用的缓存对象。

> 作者:一团捞面
> 链接:https://www.jianshu.com/p/e09870b60046

### ```cacheRead```

弃用```fs.readFile```,转为```cacheRead```。

```cacheRead```封装```fs.readFile```,读取到的文件与文件的上一次更新的时间戳,缓存在```LRUCache```中,下次读取文件的时候,首先在```LRUCache```中寻找。

```lastModified```: https://nodejs.org/api/fs.html#fs_stats_mtimems

```typescript
const moduleReadCache = new LRUCache<string, CacheEntry>({
  max: 10000
})

export async function cachedRead(path: string, encoding?: string) {
  const lastModified = (await fs.stat(path)).mtimeMs
  const cached = moduleReadCache.get(path)
  if (cached && cached.lastModified === lastModified) {
    return cached.content
  }
  console.log('reading from disk: ', path)
  const content = await fs.readFile(path, encoding)
  moduleReadCache.set(path, {
    content,
    lastModified
  })
  return content
}
```

### http缓存

使用中间件即可。

```typescript
app.use(require('koa-conditional-get')())
app.use(require('koa-etag')())
```



# 33 - f6ef1b1 进一步利用LRU

编译```.vue```文件的方法:

```parseSFC```、```compilerSFCMain```、``` compileSFCTemplate```与```compileSFCStyle```均把转换得出的结果保存在```vueCache```中。

```typescript
interface CacheEntry {
  descriptor?: SFCDescriptor // parseSFC
  template?: string // compilerSFCTemplate
  script?: string // compilerSFCMain
  styles: string[] // compileSFCStyle
}

export const vueCache = new LRUCache<string, CacheEntry>({
  max: 65535
})
```



# 34 - 052ac90 v0.3.0发布

### 我觉得这个版本能用,```<style>```的流程:

1. 获取```.vue```
2. 根据```parseSFC```,遍历```style```,每个子```style```均生成语句:```updateStyle("92a6df80-0", "/Comp.vue?type=style&index=0&t=1617780907326")```
3. ```clinet```端,调用```updateStyle```方法,创建出```<link id="vite-css-92a6df80-0" rel="stylesheet" type="text/css" href="/Comp.vue?type=style&amp;index=0&amp;t=1617780907326">```
4. ```server```端,接收到```/Comp.vue?type=style&amp;index=0&amp;t=1617780907326```
5. ```type```为```style```,```index```为```0```,```parseSFC```所编译的```AST```语法树```descriptor```,发送```descriptor.styles[index]```的内容给```client```端

### BUG:(```vue```的bug)

当新增```<style scoped>```,再去添加```class```样式不起效。
https://github.com/vuejs/vue-next/issues/3382



# 35 - 7b75253 304将不再处理内容

```modulesPlugin```中判断请求304,将不再处理内容。

```typescript
const internalPlugins: Plugin[] = [
  modulesPlugin,
  vuePlugin,
  hmrPlugin,
  servePlugin
]

# modulesPlugin的部分代码
app.use(async (ctx, next) => {
    await next()
    
    if (ctx.status === 304) {
      return
    }
})
```

根据洋葱模型,```modulesPlugin```判断```304```处于所有中间件执行的最后一个步骤。

# 36 - 0f88118 v0.3.1

v0.3.1



# 37 - 2c1b802 删除无用的包

删除```@babel/parser```



# 38 - 1e4a78c v0.3.2

v0.3.2

# 39 - 6e66766 构建```js-map```,```js```的```hmr```

目的:创建导入文件的关系图,在```hmr```的时候,可以知道应该热加载哪些文件。

### ```rewriteImports```

遇到```/^[^\/\.]/.test(id)```,改写成为```__modules/${id}```。

例如:
```import { ref } from 'vue'``` -> ```import { ref } from '__module/vue'```

如果遇到:

```import { a } from './vue'``` 不做处理,开头非```/```,非```.```的一律不做处理。

通俗来说,我们如何写node引入模块,就把我们引入的路径给改写。

### 构建```js```关系链

![cache](./A@B5FF6KI9XL_FKRY$U80CR.png)

目的是,收集所有需要```hmr```的```非模块文件```。

### ```hmr.ts```

使用```importeeMap```,获得关系链,进行```HMR```的一些东西。



# 40 - a183791 ```snowPack```

https://github.com/vitejs/vite/pull/4

详细可以去看这个```pr```了,支持```snowpack```。

### ```modules.ts```

```typescript
async function resolveWebModule(
  root: string,
  id: string
): Promise<string | undefined> {
  const webModulePath = webModulesMap.get(id)
  if (webModulePath) {
    return webModulePath
  }
  const importMapPath = path.join(root, 'web_modules', 'import-map.json')
  if (await fs.stat(importMapPath).catch((e) => false)) {
    const importMap = require(importMapPath)
    if (importMap.imports) {
      const webModulesDir = path.dirname(importMapPath)
      Object.entries(
        importMap.imports
      ).forEach(([key, val]: [string, string]) =>
        webModulesMap.set(key, path.join(webModulesDir, val))
      )
      return webModulesMap.get(id)
    }
  }
}
```



================================================
FILE: 311-320/311-320.md
================================================
# 311 - c18451d 构建:简化tsconfig

改动部分:

`tsconfig.base.json`:去除默认选项,`esModuleInterop`开启后,`allowSyntheticDefaultImports`会为`true`。

![1](1.png)

[tsconfig](https://www.tslang.cn/docs/handbook/compiler-options.html)

[esModuleInterop](https://zhuanlan.zhihu.com/p/148081795) [allowSyntheticDefaultImports](https://blog.leodots.me/post/40-think-about-allowSyntheticDefaultImports.html): 兼容esm引入cjs

`noImplicitAny`:在表达式和声明上有隐含的 `any`类型时报错,默认`alse`

`strictNullChecks`: 在严格的 `null`检查模式下, `null`和 `undefined`值不包含在任何类型里,只允许用它们自己和 `any`来赋值(有个例外, `undefined`可以赋值到 `void`),默认`false`。

`strict`: 启用所有严格类型检查选项。

`noUnusedLocals`:  若有未使用的局部变量则抛错。



# 312 - 0708279 重构[#97](https://github.com/vitejs/vite/pull/95),且全局注册importer与importee关系,即设置`importerMap`

在[309 - 7ffa9c0](https://github.com/Kingbultsea/vite-analysis/blob/master/301-310/301-310.md#309---7ffa9c0-97-windows-hmr),修复了windows路径改写。

改动部分:

- 因为`resolveImport`方法已经处理过绝对路径,所以现在去除重复的逻辑代码。

- `handleJSReload`会读取`importerMap`作为检测,才会调用`walkImportChain`检测`hmrAcceptanceMap`,才能够触发`js-update`,现在把注册`importerMap`的逻辑放进`serverPluginHmr.ts registerDep`中。

这块有很多坑,`accept`被调用一次以上,新的将会覆盖旧的,`client.ts`处理`hot-accept`用了key value对应关系; importer调用自身作为importee,触发`js-update`,如果其他文件或者组件引入了该文件,将感受不到任何变化(合理?只能在文档中约束不要import使用了HMR API的js文件了)。

> importerMap存放所有文件的importer和importee的关系。
>
> hmrAcceptanceMap存放使用了hot.accept的importer和importee的关系,用于触发`js-update`,提高hmr性能体验。

### 关于function的参数风格

之前查阅`vue-next`也发现很少使用解构(或没有),`Vite`里面是完全没有的。

解构可以使我们打乱参数顺序,但是不能变换名称,而且写类型的时候会有重复字段。

```typescript
export const resolveImport = (
  importer: string,
  id: string,
  resolver: InternalResolver,
  timestamp?: string
): string => {}

export const resolveImport = ({
  importer,
  id,
  resolver,
  timestamp
}: {
  importer: string
  id: string
  resolver: InternalResolver
  timestamp?: string
}): string => {}
```



# 313 - 5ae6278 changelog

## [0.13.1](https://github.com/vuejs/vite/compare/v0.13.0...v0.13.1) (2020-05-09)

### Bug Fixes

- **hmr:** 修复hot.accept() 路径改写(windows下) ([#97](https://github.com/vuejs/vite/issues/97)) ([7ffa9c0](https://github.com/vuejs/vite/commit/7ffa9c0b953f4a78251a8c379a2edf8e31fd368b))
- 修复web_modules后缀添加问题 + 在非`jsx`文件下使用`jsx`将会发出警告(反正`es-module-lexer`包报错了就提示是这个错) ([3653793](https://github.com/vuejs/vite/commit/3653793a2f713b126aaefb01b00878614fc4c63c)), closes [#94](https://github.com/vuejs/vite/issues/94)

### Features

- **build:** 输出`brotli-compressed`压缩下的文件大小 ([#95](https://github.com/vuejs/vite/issues/95)) ([b7f5ad2](https://github.com/vuejs/vite/commit/b7f5ad245f10efac89be0954155639e310c46e00))



# 314 - c057265 v0.13.1

release v0.13.1



# 315 - decbfc2 create-vite-app 可以构建react模板

改动部分:

- package.json: 新增`bin` `cva`,是`create-vite-app`的简写
- 封装方法,现在有两个模板文件夹`template-vue`和`template-preact`,默认`--template vue`

> [`util.promisify`]()是在`node.js 8.x`版本中新增的一个工具,用于将老式的`Error first callback`转换为`Promise`对象,让老项目改造变得更为轻松。



# 316 - 32d6341 create-vite-app v1.1.0

release create-vite-app v1.1.0

> **记住**(之前有提到)启动yarn publich会执行`prepublishOnly` 即 `node updateVersions.js`,自动同步模板里devDependencies的`vite`版本

![2](2.png)



# 317 - 122851e [#100](https://github.com/vitejs/vite/pull/100) 调整上传到npm的文件包括范围

```json
{
    "files": [
        "index.js",
        "template-*"
    ]
}
```

> [files](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#files),默认ignored,包括文件、目录或全局模式。



# 318 - e00bf3a  [#98](https://github.com/vitejs/vite/pull/98) 增强服务开启提示,保持与`vue-cli`一致

![3](3.png)



# 319 - 6d2fabe create-vite-app v1.1.1

release create-vite-app v1.1.1



# 320 - 9336dac [#106](https://github.com/vitejs/vite/pull/106) html标签`<script src=''>`支持单引号

```typescript
const srcRE = /\bsrc=(?:"([^"]+)"|'([^']+)'|([^'"\s]+)\b)/
// 获取src的正则

// 修改后:默认srcAttr[2]
const srcAttr = openTag.match(srcRE)
            if (srcAttr) {
              // register script as a import dep for hmr
              const importee = cleanUrl(
                slash(path.resolve('/', srcAttr[1] || srcAttr[2]))
              )
              debugHmr(`        ${importer} imports ${importee}`)
              ensureMapEntry(importerMap, importee).add(importer)
            }
```

> 发现了一个BUG,当index.html的`<script src="./main.js">拥有代码</script>`,将不会记录index.html与main.js的importer importee关系;我觉得没有任何关系,这里的代码完全去除不用注册也行(如果为了规范那无视),因为`hmr`识别到顶层没有parent,是会进行页面刷新的,即`walkImportChain` 设置`hasDeadEnd`为`true`。



================================================
FILE: 321-330/321-330.md
================================================
# 321 - 6ee0168 [#105](https://github.com/vitejs/vite/pull/105) 修复浏览器无法打开的问题

主要是因为`ececa`包被放进了`devDependencies`,从而没有找到该包报错。

现在把`execa`包移动进`dependencies `。



# 322 - 32cf37f [#107](https://github.com/vitejs/vite/issues/107) 处理小写的`doctype`

**issues**: `<!doctype html>`会引起`vite build`崩溃。

原因:`@vue/compiler-core`没有处理小写的`doctype`为注释(按照标准)。

![1](1.png)

改动部分:

- `build/buildPluginHtml.ts`,替换`html`内容,把`doctype`改为大写,即`html.replace(/<!doctype\s/i, '<!DOCTYPE ')`

> 写个框架要注意的事情真的好多...



# 323 - 6776e76 防止jest警告的程序包名称不明确

`template-preact`: `package.json` `name`改为`vite-preact-starter`

`template-vue`: `package.json` `name`改为`vite-starter`

> 在`vite`的测试流没有看到,应该是尤大想知道开发者用了哪个模板



# 324 - 2320d3e changelog

## [0.13.2](https://github.com/vuejs/vite/compare/v0.13.1...v0.13.2) (2020-05-09)

### Bug Fixes

- -修复-open标志引起的错误 ([#105](https://github.com/vuejs/vite/issues/105)) ([6ee0168](https://github.com/vuejs/vite/commit/6ee016892d7b375cc8dd8cbc4dc10c03325d4dc8))
- 处理小写的doctypes (close [#107](https://github.com/vuejs/vite/issues/107)) ([32cf37f](https://github.com/vuejs/vite/commit/32cf37fd5125be7dd3b65de2024e89685d7cbc8e))
- 支持`<script src>`使用单引号  ([#106](https://github.com/vuejs/vite/issues/106)) ([9336dac](https://github.com/vuejs/vite/commit/9336dacaeaae37bd2adf36ab1816c063eddbd4eb))
- cva: package.json files 包括 template-* ([#100](https://github.com/vuejs/vite/issues/100)) ([122851e](https://github.com/vuejs/vite/commit/122851ee802c8e6374be42e704883e6ed91b0b02))

### Features

- 增强cli输出的信息,保持与vue-cli一样 ([#98](https://github.com/vuejs/vite/issues/98)) ([e00bf3a](https://github.com/vuejs/vite/commit/e00bf3a7fb029416c394e2606a3ce4ed8f3079b1))
- **cva:** 支持多种类型的模板 ([decbfc2](https://github.com/vuejs/vite/commit/decbfc2ee9e0c88c9e94a8f4f39032cdf5b5d6c5))



# 325 - b5cf006 v0.13.2

release v0.13.2



# 326 - 49e79e7 支持`build --ssr`

改动部分:

- `build/index.ts`:新增`ssr`选项,调用`ssrBuild`对`options`改造,输出的文件夹名称为`dist-ssr`。

> 不做深入探究`ssr`(因为我不熟悉,也不想深入熟悉┭┮﹏┭┮!! 知道它是在服务端完成初始`vnode` -> `html`即可)[207](https://github.com/Kingbultsea/vite-analysis/blob/d0e223924b0656785fa14ac073ff78dcdfef818a/201-210/201-210.md#207---4808f41-build%E6%94%AF%E6%8C%81ssr)



# 327 - d6151bf [#104](https://github.com/vitejs/vite/pull/104) 为插件开发者暴露`rewriteImports` 方法

**pikax**: 我在编写一个基于路由的SSR插件,我需要编译SFC文件,并构建像`vite`的脚本。但是`vite`依赖中间件处理SFC文件路径。

**尤大**:为了一致性起见,我认为我们要么公开所有 `compileSFC*` 方法,要么不公开它们。但是,将它们公开意味着我们不能再将它们视为内部结构并自由调整参数。假设它们仅在开发服务器中使用,有时可能需要 `Koa context` - 这将使它们在其他地方无法使用。我认为直到`vite`变得更稳定更安全之前,你可以复制逻辑黏贴到你的插件中。

**pikax**:同意,在我的情况下,我只需要样式,其他的我基本上复制了逻辑。 `rewriteImports` 是否适合公开?

**尤大**:可以,`rewriteImports`可能是安全的,尽管我现在认为它是半内部的。你应该做好它可能会坏了的心理准备;)

![2](2.png)



# 328 - ab940fd 支持`config`文件配置`vite`

改动部分

- 新增`src/node/config.ts`: 提供`ServerConfig`, `BuildConfig`, `resolveConfig`给`vite`,即用户可以通过`config.ts`导入`vite`类型(详 **新增一**)
- `cli.ts`:**合并来自命令行和config文件的参数**,config文件的优先级高于命令行;一切类型都为可选项,所以可以作为`runServe( options: ServerConfig & {port?: numberopen?: boolean})`和`runBuild(options: BuildConfig)`的**参数**(详 **改动二**)
- `playground`,新增`tsconfig.json`和`vite.config.ts`(详 **改动三**)

> 建议补充一下tree-shaking("遇到的难题"),webpack4和rollup都在用它 [`sideEffects`](https://zhuanlan.zhihu.com/p/41795312)。

## 新增一

- 利用rollup与`esbuild`编译`ts`类型的`config`,不做`tree-shaking`。
- `UserConfig`暴露了所有`BuildConfig`字段,其中设定`plugins`字段为:`SharedConfig`的`alias`(todo)、`transform`(todo)和`resolvers`(id public file路径映射);`BuildConfig`的`rollupInputOptions`(`rollup.rollup()`参数,即构建前的配置)、`rollupOutputOptions`(`bundle.generate()`的参数,即生成配置)和`rollupPluginVueOptions`(`rollup-plugin-vue`插件参数)。

**根据定义**`Plugin`,尤大想暴露一个`vite`特性的插件给用户在`config.ts.plugins`调用,插件能帮助用户**配置`rollup`**,设置(开发&构建)**路径映射**、(开发&构建)代码转换、**`rollup-plugin-vue`配置**和(开发)**`koa`中间件**。

用户能做的事(`UserConfig`):

- 一切构建选项
- 插拔式配置`vite`插件

```typescript
# src/node/config.ts

import { ServerPlugin } from './server'
import { Resolver } from './resolver'
import {
  InputOptions as RollupInputOptions,
  OutputOptions as RollupOutputOptions
} from 'rollup'
import { Options as RollupPluginVueOptions } from 'rollup-plugin-vue'

/**
 * server与build的共享选项.
 */
export interface SharedConfig {
  /**
   * 项目根目录. 可以是绝对路径(从盘开始的路径),
   * 或者相对于config所在目录的相对路径
   * @default process.cwd()
   */
  root?: string
  /**
   * TODO
   */
  alias?: Record<string, string>
  /**
   * TODO
   */
  transforms?: Transform[]
  /**
   * 路径映射
   */
  resolvers?: Resolver[]
  /**
   * 为jsx 设置 factory 和 fragment.
   * @default
   * {
   *   factory: 'React.createElement',
   *   fragment: 'React.Fragment'
   * }
   */
  jsx?: {
    factory?: string
    fragment?: string
  }
}

// koa 中间件
export interface ServerConfig extends SharedConfig {
  plugins?: ServerPlugin[]
}

export interface BuildConfig extends SharedConfig {
  /**
   * https://github.com/Kingbultsea/vite-analysis/blob/8358749dc960a93262458eced9868d593c58af8d/211-220/211-220.md#212---c82a597-%E6%89%93%E5%8C%85%E5%90%8E%E7%9A%84%E5%BC%95%E5%85%A5%E8%B7%AF%E5%BE%84%E5%8F%AF%E9%85%8D%E7%BD%AE%E5%8C%96
   * Base public path when served in production.
   * @default '/'
   */
  base?: string
  /**
   * vite打包后的路径,如果存在该路径则会删除整个文件夹
   * @default 'dist'
   */
  outDir?: string
  /**
   * 相对于outDir的js/css/image存放路径
   * @default 'assets'
   */
  assetsDir?: string
  /**
   * 小于该设置大小的文件,将被设置为行内资源,单位byte
   * @default 4096 (4kb)
   */
  assetsInlineLimit?: number
  /**
   * 是否生成sourcemap
   * @default false
   */
  sourcemap?: boolean
  /**
   * 指定js压缩方式
   * 可选项'terser' 或 'esbuild' 'false'
   * @default 'terser'
   */
  minify?: boolean | 'terser' | 'esbuild'
  /**
   * 用于服务器端渲染的构建
   * @default false
   */
  ssr?: boolean

  // 以下仅为API,未记录在CLI中. -----------------
  /**
   * rollup.rollup()
   * https://rollupjs.org/guide/en/#big-list-of-options
   */
  rollupInputOptions?: RollupInputOptions
  /**
   * bundle.generate()
   * https://rollupjs.org/guide/en/#big-list-of-options
   */
  rollupOutputOptions?: RollupOutputOptions
  /**
   * rollup-plugin-vue
   * https://github.com/vuejs/rollup-plugin-vue/blob/next/src/index.ts
   */
  rollupPluginVueOptions?: Partial<RollupPluginVueOptions>
  /**
   * 是否输出资源生成信息
   * @default false
   */
  silent?: boolean
  /**
   * 是否将文件写入磁盘
   * @default true
   */
  write?: boolean
  /**
   * 是否写入index.html到磁盘中&注入
   * @default true
   */
  emitIndex?: boolean
  /**
   * 是否写入资源到磁盘(不包括js文件)
   * @default true
   */
  emitAssets?: boolean
}

// 暴露给用户使用的配置
export interface UserConfig extends BuildConfig {
  plugins?: Plugin[]
}

export type Condition = RegExp | RegExp[] | (() => boolean)

export interface Transform {
  include?: Condition
  exclude?: Condition
  query?: Condition
  /**
   * @default 'js'
   */
  as?: 'js' | 'css'
  transform?: (code: string) => string | Promise<string>
}

export interface Plugin
  extends Pick<SharedConfig, 'alias' | 'transforms' | 'resolvers'>,
    Pick<
      BuildConfig,
      'rollupInputOptions' | 'rollupOutputOptions' | 'rollupPluginVueOptions'
    > {
  configureServer?: ServerPlugin
}

import path from 'path'
import fs from 'fs-extra'
import chalk from 'chalk'
import type Rollup from 'rollup'
import { createEsbuildPlugin } from './build/buildPluginEsbuild'

// 处理config
export async function resolveConfig(
  configPath: string | undefined
): Promise<UserConfig | undefined> {
  const start = Date.now()
  const resolvedPath = path.resolve(
    process.cwd(),
    configPath || 'vite.config.js'
  )
  try {
    if (await fs.pathExists(resolvedPath)) {
      const isTs = path.extname(resolvedPath) === '.ts'
      // 1. 尝试加载非config.ts文件
      if (!isTs) {
        try {
          return require(resolvedPath)
        } catch (e) {
          if (
            !/Cannot use import statement|Unexpected token 'export'/.test(
              e.message
            )
          ) {
            throw e
          }
        }
      }

      // 2. ts文件有可能使用import语法
      // 使用rollup转译es import语法为require语法
      const rollup = require('rollup') as typeof Rollup
      const esbuilPlugin = await createEsbuildPlugin(false, {})
      const bundle = await rollup.rollup({
        // .json与非. 且非绝对路径 设置为外部引入
        // 意思就是不处理网络请求和json文件
        external: (id: string) =>
          (id[0] !== '.' && !path.isAbsolute(id)) ||
          id.slice(-5, id.length) === '.json',
        input: resolvedPath,
        treeshake: false,
        plugins: [esbuilPlugin]
      })

      const {
        output: [{ code }]
      } = await bundle.generate({
        exports: 'named',  // esm使用named 可以解构 https://rollupjs.org/guide/en/#outputexports
        format: 'cjs'
      })

      const config = await loadConfigFromBundledFile(resolvedPath, code)
      // config.root规范为绝对路径 https://blog.csdn.net/kikyou_csdn/article/details/83150538
      if (config.root && !path.isAbsolute(config.root)) {
        config.root = path.resolve(path.dirname(resolvedPath), config.root)
      }

      require('debug')('vite:config')(
        `config resolved in ${Date.now() - start}ms`
      )
      console.log(config)
      return config
    }
  } catch (e) {
    console.error(
      chalk.red(`[vite] failed to load config from ${resolvedPath}:`)
    )
    console.error(e)
    process.exit(1)
  }
}

interface NodeModuleWithCompile extends NodeModule {
  _compile(code: string, filename: string): any
}

async function loadConfigFromBundledFile(
  fileName: string,
  bundledCode: string
) {
  const extension = path.extname(fileName)
  const defaultLoader = require.extensions[extension]!
  require.extensions[extension] = (module: NodeModule, filename: string) => {
    if (filename === fileName) {
      ;(module as NodeModuleWithCompile)._compile(bundledCode, filename)
    } else {
      defaultLoader(module, filename)
    }
  }
  delete require.cache[fileName]
  const raw = require(fileName)
  const config = raw.__esModule ? raw.default : raw
  require.extensions[extension] = defaultLoader
  return config
}
```

> [import type](https://segmentfault.com/a/1190000039800522),仅仅导入类型,避免导入类型且导出该类型时,babel报错。以前通过开启`isolatedModules`提醒禁止这种用法,要使用显示类型导出&显示类型导入。

## 改动二

现在可以直接通过`--debug`标志来打开`debug`信息了,当然你也可以`--debug resolve `只查看路径映射信息。

```typescript
// make sure to set debug flag before requiring anything
if (argv.debug) {
  process.env.DEBUG = `vite:` + (argv.debug === true ? '*' : argv.debug)
}
```

## 改动三

```json
{
  "compilerOptions": {
    "baseUrl": "../", // 基本路径
    "paths": {
      "vite": ["dist/index.d.ts"] // vite类型路径 即 ../dist/index.d.ts
    }
  }
}
```

```typescript
import type { UserConfig } from 'vite' // baseUrl + paths.vite[0]

const config: UserConfig = {
  jsx: {
    factory: 'h',
    fragment: 'Fragment'
  },
  minify: false
}

export default config
```



# 329 - 7cdaa0b 构建时支持配置`rollup-plugin-vue`&`serverPluginVue` 的`compilerOptions`选项

改动部分:

- `serverPluginVue.ts`&`build/index.ts.rollup-plugin-vue`:  用户可以配置`compilerOptions`来影响`vue`对`<template>`标签的`vnode render`转换(**改动一**)
- 插件处理`jsx`,直接获取通过用户的`config.jsx.factory` & `config.jsx.fragment`获取
- `node/config.ts`: 用户配置新增`configureServer: ServerPlugin`,即用户可以配置`koa`中间件;Plugin插件新增`vueCompilerOptions`(即**改动一**)能力;Plugin处理器,即合并用户config(详 **改动三**);修复return BUG。

## 改动三

每个插件的功能将会被合并到config中,倒序优先(alias除外,config配置文件优先),**`Plugin`插件后者覆盖前者重复字段**。

```typescript
function resolvePlugin(config: UserConfig, plugin: Plugin): UserConfig {
  return {
    alias: {
      ...plugin.alias,
      ...config.alias
    },
    transforms: [...(config.transforms || []), ...(plugin.transforms || [])],
    resolvers: [...(config.resolvers || []), ...(plugin.resolvers || [])],
    configureServer: (ctx) => {
      if (config.configureServer) {
        config.configureServer(ctx)
      }
      if (plugin.configureServer) {
        plugin.configureServer(ctx)
      }
    },
    vueCompilerOptions: {
      ...config.vueCompilerOptions,
      ...plugin.vueCompilerOptions
    },
    rollupInputOptions: {
      ...config.rollupInputOptions,
      ...plugin.rollupInputOptions
    },
    rollupOutputOptions: {
      ...config.rollupOutputOptions,
      ...plugin.rollupOutputOptions
    }
  }
}
```

> 输出空白的console.log()可以达到换行效果。
>
> 用户可以配置config文件的额外字段,作为服务插件的参数传递(非`koa`上下文)。



# 330 - a66ac5b 更新 issue模板

**328 - ab940fd** 中的改动二,配置了`--debug`,现在需要更新issue模板,告诉反馈者使用`--debug`打开调试信息。



================================================
FILE: 331-340/331-340.md
================================================
# 331 - 6cf1e31 issue 模板

## Before you continue...

如果你升级`Vite`后,出现了问题,尝试使用浏览器删除缓存,勾选“Disable cache"



# 332 - a4524b4 修复`hmr hot.on`回调参数类型

`on`可以绑定事件名称和回调的关系,`hmr` `custom`事件可以根据server端传入的`id`事件名称,触发回调。

![1](1.png)



# 333 - 86d550a 支持`config` `alias`

改动部分:

- `node/resolver.ts`:去除`idToRequest`,新增`alias`(详 **改动一**)
- `node/server/index.ts`加入`alias`
- `node/build/index.ts`加入`alias`
- `node/config.ts`输出`export { Resolver }`类型(详 **改动四**)

## 改动一

1. **废弃**`idToRequest`,该功能可以帮助我们id转换为request路径(开发和构建都用到),即import a from 'id' -> import a from '/@modules/id',我们可以操控其转换为'/@modules/id' -> '/@modules/aid'。简单来说就是`vite`需要改写你的路径,你使用`idToRequest`拦截了该行为。

> 别问为什么要改写路径,不改写浏览器请求到时候重新传入`vite`分析不了你的模块类型,`import 'a'` 和 `import '/a'`,给到浏览器请求回来都是`localhost:8080/a`,然鹅前者是模块,后者是绝对路径的a文件。不信你自己试试🤨,反正我没试,蛤蛤蛤。

2. 新增`alias`,顶替`idToRequest`作用,传入的参数是对象,即映射id -> 用户想要的id,。

```typescript
export function createResolver(
  root: string,
  resolvers: Resolver[],
  alias: Record<string, string> // 新增参数
): InternalResolver {
  return {
    // ...  
    alias: (id: string) => {
      let aliased: string | undefined = alias[id]
      if (aliased) {
        return aliased
      }
      for (const r of resolvers) {
        aliased = r.alias && r.alias(id)
        if (aliased) {
          return aliased
        }
      }
    }
  }
}
```

#### `idToRequest`不就可以了?为什么要alias?

`idToRequest`需要映射后的路径符合`vite`的改写,开发者使用起来**不方便**(开发者不一定会看源码,需要提醒添加`@modules/`),比如我想要改写`a`为`b`模块,我需要返回`@modules/b`。(构建模式下,`@/modules`会被`requestToFile`去除)

`alias`,利用对象做映射关系,且改版后(不是alias),开发者不需要添加`@modules`了。

## 改动四

输出Resolver类型,提供用户定义Resolver。

```typescript
export interface Resolver {
  requestToFile(publicPath: string, root: string): string | undefined
  fileToRequest(filePath: string, root: string): string | undefined
  alias?(id: string): string | undefined
}
```



# 334 - b85de93 修复加载ts类型的config

由于先前判断用户没有设置`configPath`,默认为`vite.config.js`,然后利用`await fs.pathExists(resolvedPath)`寻找是否有该文件,没有则不做任何事情。

现在判断有没有`js`再判断有没有`ts`就好。

![2](2.png)



# 335 - b7b9d85 添加alias测试

注意哈,该测试写了映射为`/aliased`,所以不是模块,不要误会认为`alias`不会被当作模块。

```typescript
# vite.config.ts
alias: {
    alias: '/aliased'
}

# TestAlias.vue
import { msg } from 'alias'

# aliased
export const msg = 'alias works.'
```



# 336 - 87ee998 支持transform config / vite插件

支持通过**Vite特色的plugins**改变代码,先看实现功能目标测试例子:

```typescript
# vite.config,ts
import type { UserConfig } from 'vite'
import { sassPlugin } from './plugins/sassPlugin'
import { jsPlugin } from './plugins/jsPlugin'

const config: UserConfig = {
  alias: {
    alias: '/aliased'
  },
  jsx: {
    factory: 'h',
    fragment: 'Fragment'
  },
  minify: false,
  plugins: [sassPlugin, jsPlugin]
}

export default config

# jsPlugin.js vite特色插件
export const jsPlugin = {
  transforms: [
    {
      test(id) {
        return id.endsWith('testTransform.js')
      },
      transform(code) {
        return code.replace(/__TEST_TRANSFORM__ = (\d)/, (matched, n) => {
          return `__TEST_TRANSFORM__ = ${Number(n) + 1}`
        })
      }
    }
  ]
}

# sassPlugin.js vite特色插件
import sass from 'sass'

export const sassPlugin = {
  transforms: [
    {
      as: 'css',
      test(id) {
        return id.endsWith('.scss')
      },
      transform(code) {
        return sass
          .renderSync({
            data: code
          })
          .css.toString()
      }
    }
  ]
}

# TestTransform.vue
<template>
  <h2>Transforms</h2>
  <div class="transform-scss">This should be cyan</div>
  <div class="transform-js">{{ transformed }}</div>
</template>

<script>
import './testTransform.scss'
import { __TEST_TRANSFORM__ } from './testTransform.js'

export default {
  data() {
    return {
      transformed: __TEST_TRANSFORM__
    }
  }
}
</script>
```

> 该示例配置了两个vite的插件,想通过`sass`转换`.scss`文件代码,转换特定变量。

改动部分:

- `node/cli.ts`:  build & dev 统一使用`UserConfig`选项类型(`BuildConfig`没有`ServerConfig`的`plugins`,即没有vite特色的koa插件),合并没有任何影响,全是可选项,字段没没有冲突,不管是build还是dev,只负责自己的字段就好了。
- `node/build/buildPluginCss.ts`:  识别新参数`transforms: Transform[]`传入,筛选出符合`Transform.as === ‘css’`的`Transform`,再次符合筛选`Transform.test(路径)`;符合`as`和`test`方法的Transform,即可调用`Transform.transform`改变css文件。
- `node/build/index.ts`:新增选项`transform`(`SharedConfig`已经有了,所以现在只要取就可以了,前几个commit讲解过),该选项交给`buildPluginCss.ts`插件使用,即提供给css资源构建处理器使用,也就是rollup插件, ` transform`生命周期钩子; **除此之外还帮助用户封装**带有`transform`的`rollup。`
- `node/server/index.ts`: 同build一样,新增`transform`选项,**帮助用户封装仅带有koa洋葱模型插件功能的vite特色插件**(详 主要补充一下洋葱模型的执行顺序 **改动四**)。
- `node/server/serverPluginCss.ts`: `config.trnasforms`转换`css`,和`buildPluginCss.ts`行为一致。对`config.transforms.test`匹配到的文件进行监听。
- `node/server/serverPluginHmr.ts`:  `config.transform` `test`字段匹配到的`.XXX`文件,不会触发`handleJSReload`,仅`.module.css`变动需要触发`handleJSReload`(因为是`js`,注册关系在`moduleRewritePlugin`完成,该插件在第三层外层,目前来说是最后执行,交给`cssPlugin`设置`ctx.type = 'js'`)------> **反正意思就是`.modules.css`才触发,经过转换的`.XXX`视为普通`.css`文件。** 看新增七,有说明为什么要这么做。
- 新增`node/transform.ts`,封装用户传入的config.transforms,转换为`dev`和`build`插件。(详 **新增七**)

> rollup插件是串行执行的,即使是async,也会等待上一个transform的执行完毕再执行下一个transform,文档上有点不清晰(毕竟中华文化博大精深~~~)[transform resolveId](https://github.com/Kingbultsea/vite-analysis/blob/842e5ef132fa68d5e4fa8dcf480cd444b4b0c8f3/111-120/commit-111-120.md#112---1b0b4ba-%E9%85%8D%E7%BD%AE%E5%8C%96%E6%9E%84%E5%BB%BA)

### 改动四

#### 洋葱模型执行顺序(更新-1)

hmrPlugin被提高到内置插件的第一层,[eab49a4](https://github.com/Kingbultsea/vite-analysis/blob/ea4fb552986c95f4da44839e03a00192ce424139/291-300/291-300.md)。注意,文件名称命名与插件名称不一样。

```typescript
import { serveStaticPlugin } from './serverPluginServeStatic'
import { assetPathPlugin } from './serverPluginAssets'
import { cssPlugin } from './serverPluginCss'
import { jsonPlugin } from './serverPluginJson'
import { esbuildPlugin } from './serverPluginEsbuild'
import { vuePlugin } from './serverPluginVue'
import { moduleRewritePlugin } from './serverPluginModuleRewrite'
import { moduleResolvePlugin } from './serverPluginModuleResolve'
import { hmrPlugin, HMRWatcher } from './serverPluginHmr'

# node/server.ts
const internalPlugins: Plugin[] = [
  ...config.plugins,     // 洋葱模型的第一层  (自定)
  hmrPlugin,             // 洋葱模型的第二层  (里层)  
  moduleRewritePlugin,   // 洋葱模型的第三层  (外层) --
  moduleResolvePlugin,   // 洋葱模型的第四层  (里层)
  vuePlugin,             // 洋葱模型的第五层  (内层)
  esbuildPlugin,         // 洋葱模型的第六层  (外层) --
  jsonPlugin,            // 洋葱模型的第七层  (外层) --
  cssPlugin,             // 洋葱模型的第八层  (外层) --
  assetPathPlugin        // 洋葱模型的第九层  (里层) 
  ServerTransformPlugin, // 洋葱模型的第十层  (外层) -- 
  serveStaticPlugin      // 洋葱模型的第十一层(里层)
]
```

`hmrPlugin`: 初始化ws;**洋葱模型**中,仅发送`path.resolve(__dirname, '../client.js')`文件

`moduleRewritePlugin`:  **洋葱模型**中,改写`import`语句、发送被改造后的`index.html`

`moduleResolvePlugin`: **洋葱模型**中,处理`vue`、 `web_modules` 和 `node_modules`与跳转

`vuePlugin`: **洋葱模型**中,处理SFC组件,ts调用`esbuild`做处理(**sourcemap失效,没有合并**)

`esbuildPlugin`: **洋葱模型**中,转换`.ts`文件,且调用了`genSourceMapString`,有效的`sourcemap`

`jsonPlugin`: **洋葱模型**中转换`json`为`esm`语法的对象

`cssPlugin`:  监听css文件变动(非SFC的src,这在`vuePlugin`中处理变动,即捆绑css与SFC文件关系,css文件变动,获取SFC路径后设置id,触发`vue-style-update`,更新`<link href="XXX">`的`href`链接),监听`config.transforms`变动;**洋葱模型**中处理import类`.modules.css`esm化、css预处理器

`assetPathPlugin`: **洋葱模型**中,识别到是`import`静态资源类型的请求,将数据esm化

`ServerTransformPlugin`:根据`config.transform`动态创建,一个代码转换器,**vite特性插件**,凡是经过该插件处理的,`koa.ctx`上下文会带有`_transformed = true`属性;**洋葱模型**中担当用户自定义转换代码的职责。

> 这里指的里层是比外层先执行

#### 补充[eab49a4](https://github.com/Kingbultsea/vite-analysis/blob/ea4fb552986c95f4da44839e03a00192ce424139/291-300/291-300.md),提升`hmrPlugin`到内置插件第一层的动机

`WebSocket`初始化,`watcher`参数初始化。

为什么用户的plugins是第一层?覆盖用户对`watcher`的定义(是的,我想不出其他影响了)。

```typescript
// start a websocket server to send hmr notifications to the client
  const wss = new WebSocket.Server({ server })
  const sockets = new Set<WebSocket>()

  wss.on('connection', (socket) => {
    debugHmr('ws client connected')
    sockets.add(socket)
    socket.send(JSON.stringify({ type: 'connected' }))
    socket.on('close', () => {
      sockets.delete(socket)
    })
  })

  wss.on('error', (e: Error & { code: string }) => {
    if (e.code !== 'EADDRINUSE') {
      console.error(chalk.red(`[vite] WebSocket server error:`))
      console.error(e)
    }
  })

  watcher.handleVueReload = handleVueReload
  watcher.handleJSReload = handleJSReload
  watcher.send = send
```

> 我个人认为这个提升没什么实际作用,提前建立WebSocket罢了。

### 新增七

**规范**vite特色插件,这两个插件都会被丢进插件组中。

dev: `...(transforms.length ? [createServerTransformPlugin(transforms)] : [])`

build: `...(transforms.length ? [createBuildJsTransformPlugin(transforms)] : [])`

> as?: 'js' | 'css' // 类型,如是css 将会与sPlugin buildPluginCss一同处理,serverPluginHmr仅仅是过滤掉这些实际是css的文件,就怕其他文件也引入了触发了handleJSReload(**尤大防了一手**~)。
>
> test.query 在dev中会携带时间参数t

```typescript
import { ServerPlugin } from './server'
import { Plugin as RollupPlugin } from 'rollup'
import { parseWithQuery, readBody, isImportRequest } from './utils'

export interface Transform {
  /**
   * @default 'js'
   */
  as?: 'js' | 'css' // 类型,如是css 将会与sPlugin buildPluginCss一同处理
  test: (
    path: string,
    query: Record<string, string | string[] | undefined> // 路径参数 import a from 'a.css?a=2' 在dev中会携带时间参数t
  ) => boolean
  transform: (code: string, isImport: boolean) => string | Promise<string>
}

export function normalizeTransforms(transforms: Transform[]) {}

// vite特性的koa插件
export function createServerTransformPlugin(
  transforms: Transform[]
): ServerPlugin {
  return ({ app }) => {
    app.use(async (ctx, next) => {
      await next()
      for (const t of transforms) {
        if (t.test(ctx.path, ctx.query)) {
          ctx.type = t.as || 'js'
          if (ctx.body) {
            const code = await readBody(ctx.body)
            if (code) {
              ctx.body = await t.transform(code, isImportRequest(ctx))
              ctx._transformed = true
            }
          }
        }
      }
    })
  }
}

// rollup的插件
export function createBuildJsTransformPlugin(
  transforms: Transform[]
): RollupPlugin {
  transforms = transforms.filter((t) => t.as === 'js' || !t.as)

  return {
    name: 'vite:transforms',
    async transform(code, id) {
      const { path, query } = parseWithQuery(id)
      for (const t of transforms) {
        if (t.test(path, query)) {
          return t.transform(code, true)
        }
      }
    }
  }
}
```

![dev下的timestamp参数t](3.png)

### 总结

添加transform为了开发者更加便捷,用rollup插件和vite特色的koa插件也可以“完成”,但是难度巨大,比如as: css取消hmr是做不到的,跨插件是不可能完成的。

顺带一提,vite特色插件文件是不会被添加进hmr的,每次修改需要重新打开服务(可能有点难以小改动做到,把这些插件的路径保存起来,hmr新增一个handleVitePlugin,需要更新已经调用的`app.use`)。



# 337 - bf4d394 [#110](https://github.com/vitejs/vite/pull/110) 代码整理

![4](4.png)



# 338 - ed5b9e7 [#113](https://github.com/vitejs/vite/pull/110) fix: transform应该调用所有的插件

算是个小失误。

```typescript
# node/transform.ts  createBuildJsTransformPlugin
for (const t of transforms) {
    if (t.test(path, query)) {
      return t.transform(code, true)
    }
}

// 修改后:
let result: string | Promise<string> = code
  for (const t of transforms) {
    if (t.test(path, query)) {
      result = await t.transform(result, true)
    }
}
return result
```



# 339 - 9adbfdb 修复测试环境下的tsconfig

![5](5.png)

解决编辑器提示报错:

![6](6.png)



# 340 - d6dd2f0

改动部分:

- `node/server/serverPluginVue.ts`: `generateCodeFrame`统一在`resolveCompiler`方法中获取。
- `node/esbuildService.ts`: 懒加载`generateCodeFrame`(直接包内获取)。



================================================
FILE: 341-350/341-350.md
================================================
# 341 - efc853f 对于jsx|tsx自动引入`jsxFactory `&`Fragment`,新增VUE版JSX处理器

改动部分:

- `node/config.ts`:jsx选项新增`'vue' | 'preact' | 'react'`(详 **改动一**)
- `node/esbuildService.ts`:自动添加`h ` & `Fragment`,新增`resolveJsxOptions`方法(详 **改动二**)
- `client/vueJsxCompat.ts`(client端,首次新增新脚本):为了能vue也能享受到(j|t)sx,vue的虚拟dom转换,和preact的dom转换做一些传递的改动,即可适配(详 **新增三**)

### 改动一

```typescript
export interface SharedConfig {
  /**
   * Configure what to use for jsx factory and fragment.
   * @default
   * {
   *   factory: 'React.createElement',
   *   fragment: 'React.Fragment'
   * }
   */
  jsx?:
    | 'vue'
    | 'preact'
    | 'react'
    | {
        factory?: string
        fragment?: string
      }
}
```

### 改动二

不论有没有引入,总是添加`import`语句;`resolveJsxOptions`被用于处理用户传入的`config.jsx`字段,转换为`{ jsxFactory: 'XXXXX', jsxFragment: 'XXXXX' }`,和之前处理jsx的配置不变,**好处就是方便用户使用**`preact` | `react`可以自动配置`jsxFactory`、`jsxFragment`。

```typescript
const JsxPresets: Record<
  string,
  Pick<TransformOptions, 'jsxFactory' | 'jsxFragment'>
> = {
  vue: { jsxFactory: 'jsx', jsxFragment: 'Fragment' },
  preact: { jsxFactory: 'h', jsxFragment: 'Fragment' },
  react: {} // use esbuild default
}

export function reoslveJsxOptions(options: SharedConfig['jsx'] = 'vue') {
  if (typeof options === 'string') {
    if (!(options in JsxPresets)) {
      console.error(`[vite] unknown jsx preset: '${options}'.`)
    }
    return JsxPresets[options] || {}
  } else if (options) {
    return {
      jsxFactory: options.factory,
      jsxFragment: options.fragment
    }
  }
}


export const transform = async (
  src: string,
  file: string,
  options: TransformOptions = {},
  jsxOption?: SharedConfig['jsx']
) => {
    let code = (result.js || '').replace(sourceMapRE, '')

    // if transpiling (j|t)sx file, inject the imports for the jsx helper and
    // Fragment.
    if (file.endsWith('x')) {
      if (!jsxOption || jsxOption === 'vue') {
        code +=
          `\nimport { jsx } from '${vueJsxPublicPath}'` + // /vite/jsx createVnode处理器
          `\nimport { Fragment } from 'vue'`
      }
      if (jsxOption === 'preact') {
        code += `\nimport { h, Fragment } from 'preact'`
      }
    }

    return {
      code,
      map: result.jsSourceMap
    }
  } catch (e) {
  }
}
```

### 新增三

preact: `h("div", null, "Rendered from Preact JSX", h(Test, {
    count: 1337
}))`

jsx:`jsx("div", null, "Rendered from Preact JSX", jsx(Test, {
    count: 1337
}))`

createVNode: `createVNode("div", null, ["Rendered from Preact JSX", 
    createVNode(Test, {  count: 1337 }
)])`

> tag为字符串,转换为数组参数传入createVNode。
>
> tag为组件(就是个对象)转换为FunctionalComponent,返回数组vnode。

#### 挖一下preact的h:

3个参数外的参数,都是children,那就是说**转换为vue的只需要把包括参数3与大于参数3外的参数统一为数组**。

```typescript
export function createElement(type, props, children) {
	let normalizedProps = {},
		i;
	for (i in props) {
		if (i !== 'key' && i !== 'ref') normalizedProps[i] = props[i];
	}

    // 超出的参数 会组合成数组children中
	if (arguments.length > 3) {
		children = [children];
		// https://github.com/preactjs/preact/issues/1916
		for (i = 3; i < arguments.length; i++) {
			children.push(arguments[i]);
		}
	}
	if (children != null) {
		normalizedProps.children = children;
	}

	// If a Component VNode, check for and apply defaultProps
	// Note: type may be undefined in development, must never error here.
	if (typeof type == 'function' && type.defaultProps != null) {
		for (i in type.defaultProps) {
			if (normalizedProps[i] === undefined) {
				normalizedProps[i] = type.defaultProps[i];
			}
		}
	}

	return createVNode(
		type,
		normalizedProps,
		props && props.key,
		props && props.ref,
		null
	);
}
```

#### 什么是FunctionalComponent呀?

现在type为`function`,所以渲染标记为`0`,`FunctionalComponent`。

```typescript
# runtime-core vnode.ts
const shapeFlag = isString(type)
    ? ShapeFlags.ELEMENT
    : __FEATURE_SUSPENSE__ && isSuspense(type)
      ? ShapeFlags.SUSPENSE
      : isTeleport(type)
        ? ShapeFlags.TELEPORT
        : isObject(type) // 只要type是Obj 那么就是statefull_component
          ? ShapeFlags.STATEFUL_COMPONENT
          : isFunction(type)
            ? ShapeFlags.FUNCTIONAL_COMPONENT
            : 0
```

#### 为什么要转换呢?

因为那是插槽,截取一段`vnode.ts`的`normalizeChildren`(影响用什么方式去渲染element)代码:

```typescript
# vnode.ts -> normalizeChildren
if (isFunction(children)) {
    children = { default: children, _ctx: currentRenderingInstance }
    type = ShapeFlags.SLOTS_CHILDREN
}
```

再看一下createVNode例子:

![1](1.png)

#### 本次新增的vueJsxCompat.ts代码

```typescript
# client/vueJsxCompat.ts

import { createVNode } from 'vue'

declare const __DEV__: boolean

if (__DEV__) {
  console.log(
    `[vue tip] You are using an non-optimized version of Vue 3 JSX, ` +
      `which does not take advantage of Vue 3's runtime fast paths. An improved ` +
      `JSX transform will be provided at a later stage.`
  )
}

// 转换为插槽,(props.count) => "Rendered from Preact TSX: count is " + props.count
export function jsx(tag: any, props = null) {
  const c =
    arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null // 去除前两位
  return createVNode(tag, props, typeof tag === 'string' ? c : () => c)
}
```



# 342 - b0713fe 在服务端也输出hmr log

客户端会输出hmr信息,但是在服务端没有,现在新增一个。

```typescript
# node/server/serverPluginHmr.ts

if (needReload || needRerender || didUpdateStyle) {
      let updateType = needReload ? `reload` : needRerender ? `template` : ``
      if (didUpdateStyle) {
        updateType += ` & style`
      }
      console.log(
        chalk.green(`[vite:hmr] `) +
          `${path.relative(root, file)} updated. (${updateType})`
      )
    }
```



# 343 - 2ac7469 importChain链将准确更新文件

改动部分:

`server/serverPluginHmr.ts`: 名称更换,加入`hmrDirtyFilesMap`,**记录文件改动时间戳需要更新哪些文件**(详 **改动一**)

`server/serverPluginModulesRewrite.ts`: 从`node/utils/pathUtils.ts`迁移过来 `resolveImport`(id转换为publicPath)方法,并且通过`hmrDirtyFilesMap`传入`timestamp`获取`dirtyFiles`,被处理的`import`语句需要存在于`dirtyFiles`才可以添加`timestamp`。**不需要的再次载入的文件不会被载入,importChain得到强大的优化**。(详 **改动二**)

### 改动一

#### handleJSReload

`vueImports` -> `vueBoundaries`(需要更新的SFC路径,就是谁引入了该文件)

`jsImporters` -> `jsBoundaries`(需要更新的JS文件路径,使用了`hmr api`的文件或其`hmr.accept`的文件)

`hmrDirtyFilesMap`: key: 文件更新的时间戳 <---> value: 改动文件的`publicPath` & 引入了`hmr api`的文件publicPath & 引入了该改动文件的**SFC文件publicPath**

**`hmrDirtyFilesMap`在`server/serverPluginModulesRewrite.ts resolveImport`使用。**

#### walkImportChain

寻找import链的方法`walkImportChain`新增了`dirtyFiles`与`currentChain`参数,`currentChain`识别到`importer`类型为hot.accept(importee) | SFC,都会被添加进 `dirtyFiles.add(importer)`。

**`currentChain`只定义,但未被使用。**

> 再说亿遍,假如A.file引入了B.file,那么A.file叫importer,B.file叫importee。
>
> 引入过`hmr api`的文件(不要在SFC使用,无效果的,import被改写的时候就过滤了),他accept的文件都会被丢进一个key set对应关系。
>
> 还不懂请来微信dd我~

### 改动二

我们要知道参数`timestamp`相同的`get`请求,会自动使用缓存(浏览器)。

```typescript
# A.js
import C.js
console.log(1)

# B.vue
import A.js

// 当A.js改变
# A.js timestamp变更t=1
import C.js
console.log(2)

// dirtyFiles:['/A.js', '/B.vue']
// hmrDirtyFilesMap:{ key: '1', value: dirtyFiles:['/A.js', '/B.vue'] }
// 触发vue-reload 使得B.file被重新加载
# 触发的语句,不是文件
import B.vue?t=1

// resolveImport将要处理import语句
// hmrDirtyFilesMap.get(1)
// (dirtyFiles && dirtyFiles.has('/A.js')) || /\.vue\?type/.test('A.js')
// (true && true) || false
// 符合条件,添加timestamp
# B.vue 被改写后
import A.js?t=1

// resolveImport将要处理A.js?t=1的import语句
// hmrDirtyFilesMap.get(1)
// (dirtyFiles && dirtyFiles.has('/C.js')) || /\.vue\?type/.test('C.js')
// (true && false) || false
# A.js 被改写后
import C.js // 此时此刻C.js将会使用浏览器缓存
console.log(2)
```



# 344 - 06e51cc build不要在index.html中重写外部引入的脚本 [#116](https://github.com/vitejs/vite/issues/166)

改动部分:

- `build/buildPluginHtml`: 编译html文件时,如果识别到src为外链,将不做处理。

#### 改动前的行为

![2](2.png)

#### 改动后

![3](3.png)



# 345 - 02491a4(cva) fix [#111](https://github.com/vitejs/vite/issues/111) [#112](https://github.com/vitejs/vite/issues/122)

可以使用`cva .`创建模板到当前文件中。

如果创建文件夹失败,检测当前文件是否为空,非空则报错提示,空则在当前文件夹创建模板。



# 346 - 97ae7c3 `__BASE__`值输出publicBasePath

`publicBasePath`: 你的资源位置,如传入`/asd`,你的资源路径将全部加上`/asd`。

![4](4.png)

> 开发环境下的`__BASE__` 将被固定为`/`,构建环境下为`publicBasePath`

> 是不是可以支持一下index.html的输出位置呢?因为资源和index.html经常不会在同一个位置。



# 347 - 5346037 fix `client/vueJsxCompat.ts`

如果传入的children是一个VNode,则转换为[VNode],否则也被当slot。

![5](5.png)

> 要么是字符串,要么是数组,否则都不能走mountChildren



# 348 - 185a9cd readme

Vite 是一个固执的 web 开发构建工具,它在开发过程中通过原生 ES 模块导入为您的代码提供服务,并将其与 [Rollup](https://rollupjs.org/)捆绑在一起用于生产。

Vite支持引入 `.ts` 文件 ,可在Vue SFC(开箱即用)中使用`<script lang="ts">`。

Vite 只对`.ts` 文件执行转译,而**不** 执行类型检查。它假定类型检查由你的IDE 和构建过程负责(你可以在构建脚本中运行` tsc --noEmit`) 

Vite 使用 [esbuild](https://github.com/evanw/esbuild) 将 TypeScript 转换为 JavaScript,这比通过使用原生js的`tsc` 快约 20 ~ 30 倍,并且 HMR 更新可以在 50 毫秒内反映在浏览器中。

所有**静态**路径引用,包括绝对路径和以`/public`开头的路径,都应该基于你的工作目录结构。如果你在嵌套的公共路径下部署你的项目,只需指定`--base = /你的定义的路径/public/path/` ,所有资源路径都将会相应地重写。

对于动态路径引用,有两种方式:

- 你可以通过从 JavaScript 导入静态资源文件来获取解析的公共路径。例如,`import path from'./foo.png'` 会将其解析的公共路径作为字符串提供。
- 如果你需要动态连接路径,你可以使用全局注入的 `__BASE__` 变量作为公共基本路径。

### JSX

还支持`.jsx` 和`.tsx` 文件。JSX 转译也通过`esbuild` 处理。请注意,目前没有对任何基于JSX 的自动HMR(只会刷新页面) 支持。

默认的 JSX 配置在 Vue 3 中开箱即用:

```jsx
import { createApp } from 'vue'

function App() {
  return <Child>{() => 'bar'}</Child>
}

function Child(_, { slots }) {
  return <div onClick={console.log('hello')}>{slots.default()}</div>
}

createApp(App).mount('#app')
```

Currently this is auto-importing a `jsx` compatible function that converts esbuild-produced JSX calls into Vue 3 compatible vnode calls, which is sub-optimal. Vue 3 will eventually provide a custom JSX transform that can take advantage of Vue 3's runtime fast paths.

目前这是自动导入一个 `jsx` 兼容函数,该函数将 esbuild 生成的 JSX 调用转换为 Vue 3 兼容的 vnode,这是次优的。Vue 3 最终将提供一个自定义 JSX 转换,可以快速利用 Vue 3 的运行时路径。

#### 关于 React / Preact 的 JSX

There are two other presets provided: `react` and `preact`. You can specify the preset by running Vite with `--jsx react` or `--jsx preact`. For the Preact preset, `h` is also auto injected so you don't need to manually import it.

提供了两个预设:`react` 和`preact`。你可以通过使用`--jsx react` 或`--jsx preact` 运行Vite 来指定预设。对于Preact 预设,`h` 也是自动注入的,所以你不需要手动导入它。

由于 React 不提供esm版,因此需要使用 [es-react](https://github.com/lukejacksonn/es-react),或者使用 Snowpack 将 React 预先捆绑到 ES 模块中。最简单的方法让它运行是:

```js
import { React, ReactDOM } from 'https://unpkg.com/es-react'

ReactDOM.render(<h1>Hello, what!</h1>, document.getElementById('app'))
```

如果你需要自定义JSX,也可以通过 CLI 中的 `--jsx-factory` 和 `--jsx-fragment` 标志或通过使用 API 中的 `jsx: {factory, fragment}` 来自定义 JSX。例如,你可以运行 `vite --jsx-factory = h` 来使用 `h` 进行 JSX 元素创建调用。

## Config File

You can create a `vite.config.js` or `vite.config.ts` file in your project. Vite will automatically use it if one is found in the current working directory. You can also explicitly specify a config file via `vite --config my-config.js`.

In addition to options mapped from CLI flags, it also supports `alias`, `transforms`, and plugins (which is a subset of the config interface). For now, see [config.ts](https://github.com/vuejs/vite/blob/master/src/node/config.ts) for full details before more thorough documentation is available.

你可以在你的项目中创建一个 `vite.config.js` 或 `vite.config.ts` 文件。如果能在当前工作目录中找到,Vite 会自动使用它。你也可以显式指定一个配置文件`--config my-config.js`。

除了从 CLI 标志的选项之外,它还支持 `alias`、`transforms` 和插件(vite特色插件)。现在,请参阅 [config.ts](https://github.com/vuejs/vite/blob/master/src/node/config.ts)) 在更详尽的文档可用之前获取完整详细信息。

> 可以使用alias定义`@`这些简化路径哦



# 349 - fb95a11 changelog

# [0.14.0](https://github.com/vuejs/vite/compare/v0.13.2...v0.14.0) (2020-05-10)

### Bug Fixes

- 不要在 index.html 中重写外部脚本(fix [#116](https://github.com/vuejs/vite/issues/116)) ([06e51cc](https://github.com/vuejs/vite/commit/06e51cc3ce2fbaeec3150394dac0b630b7601b78))
- 修复加载 ts 配置 ([b85de93](https://github.com/Kingbultsea/vite-analysis/blob/352208e9ba4ea9555c19cc33f3af3921526df7e7/331-340/331-340.md#334---b85de93-%E4%BF%AE%E5%A4%8D%E5%8A%A0%E8%BD%BDts%E7%B1%BB%E5%9E%8B%E7%9A%84config))
- 应该调用所有的插件transform ([#113](https://github.com/vuejs/vite/issues/113)) ([ed5b9e7](https://github.com/vuejs/vite/commit/ed5b9e7f51e906d3a42d056571c0d5091ed5cd4c))
- **types:**修复`hmr hot.on`回调参数类型 ([a4524b4](https://github.com/vuejs/vite/commit/a4524b443ba6bfb53b78c053c27ac7ccb9f66749))



# 350 - faf7dfc v0.14.0

release v0.14.0


================================================
FILE: 351-360/351-360.md
================================================
# 351 - cva v1.2.0

relase cva v1.2.0



# 352 - cva 更新模板

`fs`包更改为`fs-extra`支持`promise`。

> 省略模板更新了什么



# 353 - 3d16951 spinner在debug | test模式下不启用

spinner会干扰控制台信息输出,在开启后会发现控制台的信息经常缺失。



# 354 - 60e94e6 readme

### 和 [Snowpack](https://www.snowpack.dev/)有什么不一样?

- 专门针对 Vue,Vite 提供了内置的 HMR,而 Snowpack 只是在任何文件编辑时重新加载页面。由于这两种解决方案都依赖于原生 ES 导入,因此整页重新加载的网络瀑布实际上可能成为编辑 -> 反馈速度的瓶颈,HMR 允许你避免在开发时间内等待过长的页面reload。



# 355 - e7b64f0 changelog 

## [0.14.1](https://github.com/vuejs/vite/compare/v0.14.0...v0.14.1) (2020-05-11)

### Bug Fixes

- 在记录写入之前停止spinner ([3d16951](https://github.com/vuejs/vite/commit/3d1695100a17502dcb49d074ed15627604cd03f0))

### Features

- **cva:**更新模板 ([8cd2354](https://github.com/vuejs/vite/commit/8cd235451f91b9a73c5419067af0c1bf7c992655))



# 356 - 2e5585a release v0.14.1

release v0.14.1



# 357 - a968795 调整 issue template

 调整 issue template



# 358 - fd00853 cva v1.3.0

更新vite依赖

release cva v1.3.0



# 359 - d58ae83 readme

## Getting Started

```bash
$ npx create-vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
```

使用 Yarn:

```bash
$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev
```

> 虽然 Vite 主要是为 Vue 3 设计的,但它实际上也可以支持其他框架。例如,尝试使用 `npx create-vite-app` 和 `--template react` 或 `--template preact`。



# 360 - 0dafd33 chore cva路径 [#125](https://github.com/vitejs/vite/pull/125)

内容路径需要更新一下。





================================================
FILE: 361-370/361-370.md
================================================
# 362 - 0f106bd cva v1.3.1

release cva v1.3.1



# 363 - 0cacb17 增强编译error输出(格式)

`server/serverPluginVue.ts` 增强编译error输出,报错位置输出换行(格式)



# 364 - 995a827 依赖 bump vue

更新vue包版本 ^3.0.0-beta11



# 365 - 3974669 改进浏览器中的 hmr 失败消息

改动部分:

`client/client.ts`: 新增warnFailedFetch方法(详 **改动一**)

### 改动一

`vue-reload`会调用`__VUE_HMR_RUNTIME__.reload`,如果失败则提示:加载XXX失败,可能是由于语法错误或者引入的文件不存在(请查看上方的错误信息)

```typescript
function warnFailedFetch(err: Error, path: string | string[]) {
  if (!err.message.match('fetch')) {
    console.error(err)
  }
  console.error(
    `[hmr] Failed to reload ${path}. ` +
      `This could be due to syntax errors or importing non-existent ` +
      `modules. (see errors above)`
  )
}
```



# 366 - 8558a6d cva 调整vue模板

模板使用vue `Fragment`



# 367 - 1cffde6 bump vue

更新vue包,^3.0.0-beta12



# 368 - de7b7f7 changelog

## [0.14.2](https://github.com/vuejs/vite/compare/v0.14.1...v0.14.2) (2020-05-11)

> 一个什么信息都没有的changelog



# 369 - 800d0b2 v0.14.2

release v0.14.2



# 370 - c46007d cva v1.3.2

release cva v1.3.2

> vite更新 cva依赖的vite版本也需要更新



================================================
FILE: 371-380/371-380.md
================================================
# 371 - 3e27277 chore git ignore explorations

`.gitignore`添加`explorations`文件夹,我们想提交PR,fork `vite`后,可以在创建`explorations`文件夹在里面乱用代码。

> 我这些调试的最方便了,不需要处理冲突,`sourcetree` merge UI线条也不会增多



# 372 - b2377bf `isImportRequest`兼容safari

我们可以在技术上使用更严格的检查,通过检查request 的`referer` 是不是compile-to-JS 源文件,但这不是在 Safari 中工作,因为 Safari 使用页面 URL 作为`referer`,即使对于 ES 模块imports。

```typescript
export const isImportRequest = (ctx: Context): boolean => {
  const dest = ctx.get('sec-fetch-dest')
  if (dest && dest !== 'script') {
    return false
  }
  return ctx.get('accept') === '*/*'
}
```

`sec-fetch-dest`:表示请求的目的地

`<link rel="stylesheet" href="/style.css">`显示`style`

`import('/style.css')`显示`script`



# 373 - 496b3fb wip(work in progress) service worker

改动部分:

- `src/node/server/serverPluginServiceWorker.ts`: (详 **新增一**)
- `src/sw/serviceWorker.ts`: (详 **新增二**)
- `src/node/server/serverPluginHmr.ts`:当触发`handleJSReload`,若检测到其处于import链,即传递`publicPath`,触发`bustSwCache`(在service-worker文件里交互的信息类型为`bust-cache`)。
- `src/client/client.ts`: path参数,用于`bustSwCache`,`vue-rerender` | `vue-style-update` | `style-update` | `style-remove`会对path进行调整

> [service-worker深度教程](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle)

### serverPluginServiceWorker.ts 新增一

#### 洋葱模型执行顺序(更新-2)

```typescript
import { serveStaticPlugin } from './serverPluginServeStatic'
import { assetPathPlugin } from './serverPluginAssets'
import { cssPlugin } from './serverPluginCss'
import { jsonPlugin } from './serverPluginJson'
import { esbuildPlugin } from './serverPluginEsbuild'
import { vuePlugin } from './serverPluginVue'
import { moduleRewritePlugin } from './serverPluginModuleRewrite'
import { moduleResolvePlugin } from './serverPluginModuleResolve'
import { hmrPlugin, HMRWatcher } from './serverPluginHmr'
import { serviceWorkerPlugin } from './serverPluginServiceWorker.ts'

# node/server.ts
const internalPlugins: Plugin[] = [
  ...config.plugins,     // 洋葱模型的第一层  (自定)
  serviceWorkerPlugin,   // 洋葱模型的第二层  (里层)
  hmrPlugin,             // 洋葱模型的第三层  (里层)  
  moduleRewritePlugin,   // 洋葱模型的第四层  (外层) --
  moduleResolvePlugin,   // 洋葱模型的第五层  (里层)
  vuePlugin,             // 洋葱模型的第六层  (内层)
  esbuildPlugin,         // 洋葱模型的第七层  (外层) --
  jsonPlugin,            // 洋葱模型的第八层  (外层) --
  cssPlugin,             // 洋葱模型的第九层  (外层) --
  assetPathPlugin        // 洋葱模型的第十层  (里层) 
  ServerTransformPlugin, // 洋葱模型的第十一层(外层) -- 
  serveStaticPlugin      // 洋葱模型的第十二层(里层)
]
```

`vite`的`koa`插件,拦截`sw.js`发送`serviceWorker.js`文件(现在不分析todo要做的事情)

```typescript
import fs from 'fs'
import path from 'path'
import { ServerPlugin } from '.'

// 读取worker文件,__SERVER_TIMESTAMP__ 设置为vite服务开启时间
let swScript = fs
  .readFileSync(path.resolve(__dirname, '../serviceWorker.js'), 'utf-8')
  // inject server start time so the sw cache is invalidated
  .replace(/__SERVER_TIMESTAMP__ =.*/, `__SERVER_TIMESTAMP__ = ${Date.now()}`)

// TODO inject lockfile hash

// TODO resolve module entry directly during rewrite so that we don't need the
// redirect in module resolve plugin
// 尤大想在这里改写@module,这样我们就不用跳转了


export const serviceWorkerPlugin: ServerPlugin = ({
  app,
  watcher,
  resolver
}) => {
  if (process.env.DEBUG) {
    // 在debug模式下允许控制台输出信息
    swScript = swScript.replace(/\/\/ console.log/g, 'console.log')
  }

  // TODO watch lockfile hash

  // const bustSwCache = (file: string) => {
  //   // vue cache busting is handled in vue-specific client listeners
  //   // so we can invalidate each blocks separately
  //   if (!file.endsWith('.vue')) {
  //     watcher.send({
  //       type: 'sw-bust-cache',
  //       timestamp: Date.now(),
  //       path: resolver.fileToRequest(file)
  //     })
  //   }
  // }

  // watcher.on('change', bustSwCache)
  // watcher.on('unlink', bustSwCache)

  app.use(async (ctx, next) => {
    if (ctx.path === '/sw.js') {
      ctx.type = 'js'
      ctx.status = 200
      ctx.body = swScript
      return
    }
    return next()
  })
}
```

### 新增二

```typescript
// 这两个是由服务器动态注入的,以便我们在服务器重新启动或用户锁定文件更改时使缓存无效。
const __SERVER_TIMESTAMP__ = 1
const __LOCKFILE_HASH__ = 'a'

const CACHE_NAME = `vite-cache-${__SERVER_TIMESTAMP__ + __LOCKFILE_HASH__}`

const sw = (self as any) as ServiceWorkerGlobalScope

sw.addEventListener('install', () => {
  // 跳过等待上一次ws 开发模式下必用 否则需要关闭所有选项卡 & 刷新页面
  sw.skipWaiting()
})

sw.addEventListener('activate', (e) => {
  // 首次加载页面可以控制作用域内的请求(对时间敏感)
  sw.clients.claim()
    
  // 删除没有匹配到CACHE_NAME的缓存
  e.waitUntil(
    (async () => {
      const keys = await caches.keys()
      for (const key of keys) {
        if (key !== CACHE_NAME) {
          await caches.delete(key)
        }
      }
    })()
  )
})

sw.addEventListener('message', async (e) => {
  // 删除当前CACHE_NAME下的某缓存
  if (e.data.type === 'bust-cache') {
    const cache = await caches.open(CACHE_NAME)
    // console.log(`busted cache for ${e.data.path}`)
    cache.delete(e.data.path)
  }
})

const cacheableRequestRE = /^\/@modules\/|\.vue($|\?)|\.(t|j)sx?$|\.css$/
const hmrRequestRE = /(&|\?)t=\d+/

// 请求
sw.addEventListener('fetch', (e) => {
  const url = new URL(e.request.url)
  // @modules .vue .jsx .tsx .css使用缓存,但是拥有t参数的不使用缓存
  if (
    cacheableRequestRE.test(url.pathname) &&
    // no need to cache hmr update requests
    !url.search.match(hmrRequestRE)
  ) {
    e.respondWith(tryCache(e.request))
  }
})

async function tryCache(req: Request) {
  const cached = await caches.match(req)
  // 拥有缓存则返回缓存res
  if (cached) {
    // console.log(`serving ${req.url} from cache`)
    return cached
  } else {  
    // console.log(`fetching`, req)
    const res = await fetch(req)
    // console.log(`got res:`, res)
    // 无效请求 https://developer.mozilla.org/zh-CN/docs/Web/API/Response/type
    if (!res || res.status !== 200 || res.type !== 'basic') {
      // console.log(`not caching ${req.url}`)
      return res
    }
      
    // 缓存  
    // console.log(`caching ${req.url}`)
    const cache = await caches.open(CACHE_NAME)
    cache.put(req, res.clone())
    return res
  }
}
```

> sw事件`bust-cache`,传入`path`,可删除CACHE_NAME的res缓存。
>
> 现在一切的缓存|import链,都是publicPath,publicPath是唯一的。



# 374 - e18f21a 调整css,不需要raw参数

改动部分:

- `serverPluginModuleRewrite.ts`: 非`.jsx` | `.tsx` | `.vue`后缀的import语句,加入参数`import`作为标识。
- `node/pathUtils.ts`: 不再检测请求头,通过参数`import`来区别这是不是一个来自脚本的请求。
- `serverPluginCss.ts`:识别到参数带有`raw`,将直接返回存文件内容(交给静态资源获取的插件处理)。



# 375 - b4b84e3 sw cache busting

改动部分:

- `src/client/client.ts`: `sw`文件改动,询问是否重新加载页面;新增changSrcPath,用于调用bustSwCache删除缓存;`vue-style-update` 去除参数`t`。(详 **改动一**)
- `src/server/serverPluginHmr.ts`: `HMRPayload`新增参数`changeSrcPath`,`vue-reload` & `js-update`均传递`changeSrcPath`(详 **改动二**)
- `src/server/serverPluginModuleResolve.ts`: 去除跳转功能。
- `src/server/serverPluginModuleRewrite.ts`: `serverPluginModuleResolve.ts`的跳转路径,被迁移到这里的`resolveImport`,即`/@modules/${resolveNodeModuleEntry(root, id) || id}`。
- `serverPluginServiceWorker.ts`: `watch.on('unlink')`触发`sw-bust-cache`事件(文件删除会触发),新增`__PROJECT_ROOT__ = root`。

> Cache Busting,是指通过一定技术手段, 强行使得浏览器端的缓存失效, 使得浏览器获取资源的最新版本。
>
> tips: 使用者只能通过`resolver.alias`来影响对模块的路径改写。

### 改动一

触发`bustSwCache`方式:

1. `sw-bust-cache`事件,传递`path`
2. 任意事件,传递`changeSrcPath`
3. `vue-rerender`,自动触发,`${path}?type=template`
4. `vue-style-update`,自动触发,``${path}?type=style&index=${index}``
5. `full-reload`事件,传递`path`

**为什么`serviceWorker`变动,需要询问是否reload页面?**

因为`serviceWorker`的更新不能确保第一时间能拦截所有请求,(sw安装成功)重新加载页面才能保证。

**为什么去除参数`t`,这样不是浏览器一直都缓存了吗?**

上了`serviceWorker`后,就不需要参数`t`了。

### 改动二

传递`changeSrcPath`的事件:

1. `vue-reload`事件,`changeSrcPath = publicPath`
2. `vue-js`事件,`changeSrcPath = publicPath`



# 376 - 3dc39fa commnents

`client.ts`添加注释,vite经常更新,sw也会更新,所以注释提醒用户是否需要重新reload页面。

`sw-bust-cache`: 这只会在文件被删除的时候触发。

`full-reload`: 传递的path也派上用场了(符合`hasDeadEnd`,`path`为文件改动的`publicPath`)



# 377 - ee6a03d 去除`sw-bust-cache`事件,根据`-sw`决定是否使用缓存

改动部分:

- `client/client.ts`: 去除`sw-bust-cache`事件,`full-reload`不会触发`bustSwCache`,现在改为`path`不等于`changeSrcPath`触发`bustSwCache(path)`。
- `node/cli.ts`: 可以通过`-sw`来开启`service-wroker`,类型`[boolean | deps-only]`,默认`true`
- 其余部分根据`-sw`配置,选择性进行缓存
- `../client.js`将不再缓存(不知道为什么,我觉得是暂时性停止缓存,后续还是会根据`-sw`加上去的)

1. 配置`-sw`为`true`: 开启完全功能的`service-worker`
2. 配置`-sw`为`false`: `service-worker`的`fetch`将不做任何操作,服务器会判断304缓存,开启`koa-conditional-get` & `koa-etag`
3. 配置`-sw`为`deps-only`: 继承点**2**,`sw`会在匹配到非`/@modules/`的`publicPath`请求,不使用`service-worker`



# 378 - 59d0103 测试sw

页面刷新后,测试hmr是否正常工作。



# 379 - 3408b97 `koa-etag`重新改为默认调用,即使开启了`-sw = true`,也要设置`etag`

改动部分:

- `server/serverPluginServiceWorker.ts`: `__SERVER_TIMESTAMP__ = ${config.serviceWorker ? Date.now() : '0'}`,如果`-sw`为`false`,则`__SERVER_TIMESTAMP__ === '0'`(详 **改动一**)
- `server/serverPluginServerStatic.ts`: `koa-etag`回到当初改动,默认调用
- `server/serverPluginVue.ts` & `node/utils/fsUtils.ts cachedRead `:  不管什么情况,正常设置`etag` `header`

> cachedRead 设置304,依旧会设置body(serverPluginStatic)
>

### 改动一

`__SERVER_TIMESTAMP__`的变动,可以让vite服务重启后使得`sw.js`内容有所改动,可以触发重新安装sw。

### BUG

index.html与src注册的importMap关系,在windows下不正确,需要使用`posix`。



# 380 - 3bb1324 lockfile hash 新增lockFile文件的hash值给`sw`

改动部分:

`server/serverPluginServiceWorker.ts`: 新增`__LOCKFILE_HASH__`,变量更名,新增方法`getLockfileHash`(详 **改动一**)

### 改动一

更名: `__SERVER_TIMESTAMP__`  -> `__SERVER_ID__`。

`__LOCKFILE_HASH__ = ${JSON.stringify(getLockfileHash(root))}`

```typescript
// lockfile可能的名称
const lockfileFormats = [
  'package-lock.json',
  'yarn.lock',
  'pnpm-lock.yaml',
  'package.json'
]

function getLockfileHash(root: string): string {
  for (const format of lockfileFormats) {
    const fullPath = path.join(root, format)
    
    // 寻找到则使用'crypto'包的createHash创建为base64数据格式的hash
    if (fs.existsSync(fullPath)) {
      const content = fs.readFileSync(fullPath, 'utf-8')
      return createHash('sha1').update(content).digest('base64')
    }
  }
  return ``
}
```



================================================
FILE: 381-390/381-390.md
================================================
# 381 - e8e4a4b remove todo

去除todo: 是否使用文件内容hash 或者 lastModified来代替timestamp?

![1](1.png)

`sw`缓存有两种类型:

1. `const USER_CACHE_NAME = vite-cache-${__PROJECT_ROOT__}-${__SERVER_ID__}`,模块的缓存,即匹配`@modules`

2. `const DEPS_CACHE_NAME = vite-cache-${__PROJECT_ROOT__}-${__LOCKFILE_HASH__}`,用户文件缓存,即匹配`/(&|\?)t=\d+/`,参数t

每次服务启动,sw更新,都会删除用户缓存。

而模块缓存,根据`lockfile`的hash变动来决定(如你的`yarn.lock` | `package-lock.json`)。



# 382 - b5871eb fix 改写import id同样也需要改写为webmodules

改动部分:

- `server/serverPluginModuleRewrite.ts`: 改写为`@modules/`的`resolveImport`,需要添加对`webmodules`的路径改写支持(详 修复一)

### 修复一

优先判断`webmodules`。

```typescript
return `/@modules/${
      resolveWebModule(root, id) || resolveNodeModuleEntry(root, id) || id
}`
```



# 383 - 0e7ea5a 选项错误提示 `deps-only`的小修改

改动部分:

- `server/serverPluginServiceWorker.ts`: 用户输入的选项`enabled`如果为字符串,但是不为`deps-only`,则输出错误警告,此时将改为`true`;`config.serviceWorker === true`才会把`__SERVER_ID__`设置为服务启动时间。

```typescript
`const __SERVER_ID__ = ${
        // only inject if caching user files. When caching deps only, only
        // the lockfile change invalidates the cache.
        config.serviceWorker === true ? Date.now() : '0'
      }`
```

> deps-only是来缓存模块的,`__SERVER_ID__`不应该去改动它(虽然没什么大碍,服务重启一个tip reload)。



# 384 - 82098a8 sw去除deps-only

改动部分:

- `client/client.ts`: `__SW_ENABLED__`为真值 或 已经存在sw服务 才注册sw服务(存在也要注册一遍,可以删除旧的cache);新增`env.d.ts`(详 **改动一**)
- `-sw`去除`deps-only`,现在从默认为`true`更改为默认`false`
- `server/serverPluginModuleRewrite.ts`: `window.__SW_ENABLED__ = ${!!config.serviceWorker}`现在被设置为全局字段
- `server/serverPluginServeStatic.ts`: `-sw`为`false`,即使用`koa-conditional-get`(实际没改动,原来为`!==true`)
- `server/serverPluginServiceWorker.ts` & `serviceWorker.ts`:去除`deps-only`逻辑

> 完全去除`deps-only`,不区分什么用户缓存|模块缓存了。
>
> `sw`缓存以`hashlocak` & `timestamp`为准。

### 改动一

命名全局变量类型。

```typescript
# env.d.ts

declare const __DEV__: boolean
declare const __BASE__: string
declare const __SW_ENABLED__: boolean
```



# 385 - 617f9f0 测试 async组件显示`performance.now()`

[performance.now()](https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/now)

返回值表示为从[time origin](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#the_time_origin)之后到当前调用时经过的时间



# 386 - fccc261 -sw测试

添加`appveyor.yml` & `circleci`的serviceWorker测试。



# 387 - 893ae7b windows ci

区分windows测试,设置`crocess-env`。



# 388 - ce41994 修复web_modules改写错误

因为`serverPluginModuleResolve.ts`会进行resolve处理,所以返回id即可

```typescript
const isWebModule = !!resolveWebModule(root, id)
return `/@modules/${
    isWebModule ? id : resolveNodeModuleEntry(root, id) || id
}`
```



# 389 - 2efe9b3 serviceWorker可用才注册sw

检测`navigator.serviceWorker`是否存在



# 390 - a44bd76 简化ci的脚本

可能尤大觉得crocess-env只能在windows用吧,又恢复回去了,现在ci测试脚本统一使用`test` `test-sw`

```json
"test": "jest --clearCache && jest --runInBand --forceExit",
"test-sw": "cross-env USE_SW=1 yarn test",
```



================================================
FILE: 391-400/391-400.md
================================================
# 391 - f9b267f cva 使用@pika/react & alias

![1](1.png)



# 392 - c8a1ffd 服务启动后的每个首次请求不返回304

改动部分:

- `node/server/serverPluginServeStatic.ts`: 去除`koa-conditional-get`,使用`ctx.fresh`(详 **改动一**)
- `node/server/serverPluginVue.ts`: `seenUrls`检测是否首个请求,是则返回200
- `node/utils/fsUtils.ts`: `seenUrls`检测是否首个请求,是则返回200

**为什么不要第一个缓存呢?**

因为`moduleRewritePlugin.ts`检测到304,就不做改写了,比如SFC文件返回304,就不能够改写`import`,也就没有importChain。

> 虽然`serverPluginVue.ts`没有调用next(),但是记住`serverPluginRewrite.ts`先被use的,是他调用了`await next()`,让SFC处理后,再回头改写`import`

### 改动一

seenUrls用户储存ctx.url,用来判断该请求是否为新的请求。

```typescript
if (!config.serviceWorker) {
  app.use(async (ctx, next) => {
    await next()
    // the first request to the server should never 304
    if (seenUrls.has(ctx.url) && ctx.fresh) {
      ctx.status = 304
    }
    seenUrls.add(ctx.url)
  })
}
app.use(require('koa-etag')())
```

### request.fresh

检查请求缓存是否“新鲜”,也就是内容没有改变。此方法用于 `If-None-Match` / `ETag`, 和 `If-Modified-Since` 和 `Last-Modified` 之间的缓存协商。 在设置一个或多个这些响应头后应该引用它。

```js
// 新鲜度检查需要状态20x或304
ctx.status = 200;
ctx.set('ETag', '123');

// 缓存是好的
if (ctx.fresh) {
  ctx.status = 304;
  return;
}

// 缓存是陈旧的
// 获取新数据
ctx.body = await db.find('something');
```



# 393 - f3a45f3 changelog

## [0.14.3](https://github.com/vuejs/vite/compare/v0.14.2...v0.14.3) (2020-05-12)

### Bug Fixes

- 服务器启动时的第一个请求不应为304 ([c8a1ffd](https://github.com/vuejs/vite/commit/c8a1ffd71db0916cd6386130e3eb170fa09c31d2))
- web modules resolve,返回id即可,因为`serverPluginModuleResolve.ts`会进行resolve处理 ([ce41994](https://github.com/vuejs/vite/commit/ce41994ee4e395bb304191b5d9a26f0f32d3b47a))
- 让`isImportRequest`也能在Safari中检测(后面改写为直接检测`ctx.query.import`参数,在`serverPluginModuleRewrite.ts`中会加入`import`参数) ([b2377bf](https://github.com/vuejs/vite/commit/b2377bf3b6b14ed972327930644fe6937fa814dd))
- sw 可用才进行注册 ([2efe9b3](https://github.com/vuejs/vite/commit/2efe9b3215f04e751d19cd50169bddf4250d114d))
- 对于改写import路径,也需要处理web_modules ([b5871eb](https://github.com/vuejs/vite/commit/b5871eba505e5a109b8b8ae07d6f8a70c6d970eb))

### Features

- **cva:** @pika/react + alias ([f9b267f](https://github.com/vuejs/vite/commit/f9b267fbe3f6e8cc11a3e6855f7775aeb863b0f8))
- **sw:** lockfile hash ([3bb1324](https://github.com/vuejs/vite/commit/3bb13240d9d6c3ef84020cd69b2e60835f206f8f))
- service worker 缓存([ee6a03d](https://github.com/vuejs/vite/commit/ee6a03d3497433150c13fc9370b17daaa43e1e1d))



# 394 - 120cd78 v0.14.3

release v0.14.3



# 395 - 8a1fbfe cva v1.4.0

release create-vite-app v1.4.0



# 396 - f558a88 [#133](https://github.com/vitejs/vite/pull/133) cleanUrl 使用ctx.path

将`cleanUrl`替换为 `ctx.path`



# 397 - 140751f wip 新增optimize命令

改动部分:

- 新增`@rollup/plugin-commonjs: ^11.1.0`,用来寻找包入口。
- 新增`src/node/optimizer.ts`(详 **新增二**)

### 新增二

`dependencies`的包,转换为es5。(目前有BUG,留~)

```typescript
import fs from 'fs-extra'
import path from 'path'
import { createHash } from 'crypto'
import { ResolvedConfig } from './config'
import type Rollup from 'rollup'
import { supportedExts } from './resolver'

export interface OptimizeOptions extends ResolvedConfig {
  force?: boolean
}

export async function optimize(config: OptimizeOptions) {
  // scan lockfile
  const root = config.root || process.cwd()
  const cacheDir = path.join(root, `node_modules`, `.vite`)
  console.log(cacheDir)
  const hashPath = path.join(cacheDir, 'hash')
  const depHash = getDepHash(root, config.__path)

  if (!config.force) {
    let prevhash
    try {
      prevhash = await fs.readFile(hashPath, 'utf-8')
    } catch (e) {}
    // hash is consistent, no need to re-bundle
    if (prevhash === depHash) {
      console.log('hash is consistent. skipping.')
      return
    }
  }

  await fs.ensureDir(cacheDir)
  await fs.writeFile(hashPath, depHash)

  const pkg = lookupFile(root, [`package.json`])
  if (!pkg) {
    console.log(`package.json not found. skipping.`)
    return
  }

  const deps = JSON.parse(pkg).dependencies || {}
  const depKeys = Object.keys(deps)
  if (!depKeys.length) {
    console.log(`no dependencies listed in package.json. skipping.`)
    return
  }

  console.log(`optimizing dependencies...`)
  const rollup = require('rollup') as typeof Rollup

  console.log(depKeys)

  await rollup.rollup({
    input: depKeys,
    plugins: [
      require('@rollup/plugin-node-resolve')({
        rootDir: root,
        extensions: supportedExts
      }),
      require('@rollup/plugin-commonjs')({
        sourceMap: false
      })
    ]
  })
}

const lockfileFormats = [
  'package-lock.json',
  'yarn.lock',
  'pnpm-lock.yaml',
  'package.json'
]

let cachedHash: string | undefined

export function getDepHash(
  root: string,
  configPath: string | undefined
): string {
  if (cachedHash) {
    return cachedHash
  }
  let content = lookupFile(root, lockfileFormats) || ''
  // also take config into account
  if (configPath) {
    content += fs.readFileSync(configPath, 'utf-8')
  }
  return createHash('sha1').update(content).digest('base64')
}

function lookupFile(dir: string, formats: string[]): string | undefined {
  for (const format of formats) {
    const fullPath = path.join(dir, format)
    if (fs.existsSync(fullPath)) {
      return fs.readFileSync(fullPath, 'utf-8')
    }
  }
  const parentDir = path.dirname(dir)
  if (parentDir !== dir) {
    return lookupFile(parentDir, formats)
  }
}
```



# 398 - fa52279 wip vite optimize

改动部分:

`server/serverPluginModuleResolve.ts`: 新增`resolveOptimizedModule` & `resolveBareModule`方法,处理`vite optimized modules`(详 **新增一**)

### 新增一

虽然397不能使用,但是可以看出: `vite optimized`转换`dependcies`的所有包为`es5`,vite检测到这类模块,则优先利用。

`resolveBareModule`提供给`serverPluginRewrite.ts`判断模块类型使用。

> 把所有文件给打进一个文件,可以减少网络请求。

```typescript
export function resolveBareModule(root: string, id: string) {
  const optimized = resolveOptimizedModule(root, id)
  if (optimized) {
    return id + '.js'
  }
  const web = resolveWebModule(root, id)
  if (web) {
    return id + '.js'
  }
  const nodeEntry = resolveNodeModuleEntry(root, id)
  if (nodeEntry) {
    return nodeEntry
  }
  return id
}


const viteOptimizedMap = new Map()

export function resolveOptimizedModule(
  root: string,
  id: string
): string | undefined {
  const cached = viteOptimizedMap.get(id)
  if (cached) {
    return cached
  }

  if (!id.endsWith('.js')) id += '.js'
  const file = path.join(root, `node_modules`, `.vite`, id)
  if (fs.existsSync(file)) {
    viteOptimizedMap.set(id, file)
    return file
  }
}
```



# 399 - 405f685 代码整理

改动部分:

- `serverPluginModuleResolve.ts`:  封装`index.html`代码改写逻辑
- `serverPluginModuleRewrite.ts`: 整理代码,名称更改,增强错误输出(详 **改动二**)

### 改动二

如果模块无法处理,则输出`referer`的路径(引入了这个模块的文件路径)。

```typescript
const importer = new URL(ctx.get('referer')).pathname
    console.error(
      chalk.red(
        `[vite] Failed to resolve module import "${id}". ` +
          `(imported by ${importer})`
      )
    )
```



# 400 - e62473a 获取文件后缀使用`path.extname`

把正则`/\.\w+$/`更改为`path.extname`



================================================
FILE: 401-410/401-410.md
================================================
# 401 - 7f5e459 代码整理

 代码整理



# 402 - 02753b7 添加`.mjs`拓展

引入模块尝试,在寻找不了`package.json`的情况下,拓展文件后缀。

如`import lodash from 'lodash-es/lodash'` -> `'@modules/lodash-es/lodash.js'`



# 403 - 4f2953e 修复windows下模块的入口路径处理

`path.join(id, '/', pkg.module || pkg.main || 'index.js')`

更改为:`id + '/' + (pkg.module || pkg.main || 'index.js')`

以前返回:`lodash-es\lodash.js`

现在返回:`lodash-es/lodash.js`

改写到浏览器,为windows路径,传递到服务器寻找资源就错误了。

```js
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
// Returns: '/foo/bar/baz/asdf'

path.join('foo', {}, 'bar');
// Throws 'TypeError: Path must be a string. Received {}'
```

> id就是import from 'id',pkg.module为package.json module pkg.main 为package.json main



# 404 - c243d09 cva & 构建下alias可以在其他plugins中使用

改动部分:

- `cva`命令行:`build: "vite build src --jsx react --outDir dist"`
- `build/buildPluginResolve.ts`: 在resolveId钩子使用resolve(详 **改动二**)

[rollup this.resolve](https://rollup.docschina.org/guide/en/#thisresolvesource-string-importer-string-options-skipself-boolean--promiseid-string-external-boolean--null): 重新运行钩子,但是跳过当前。

tips: 你可以不把skip关掉,你就知道这个有什么用。

### 改动二

`resolveId`内调用resolve,等于重新走一次流程了,加`skipSelf`跳过自身钩子。

为了让`id = resolver.alias(id) || id`起作用,所以调用`resolve`重新执行流程(`skpiSelf`跳过自身,不然无限循环),如果别的钩子进行处理(不存在,这里添加await也可以),就默返回被`alias`处理过的结果。

```typescript
async resolveId(id, importer) {
   // fallback to node-resolve
   const resolved = this.resolve(id, importer, { skipSelf: true })
   return resolved || { id }
}
```

https://github.com/rollup/rollup/pull/2844

> 对应模块设置moduleSideEffects后,其他引入者只关注你的模块变量是否被引入的部分,副作用不管。

![1](1.png)

> 之前我对tree-shaking有误解,以为require也可以做到很好的tree-shaking(反正流程是可以知道引入了什么),实际是忘了动态引入,根本分析不了。esm就可以很好知道谁引入了,谁没引入了,sideEffect false可以完全保证tree-shaking(要遵守没有副作用的代码原则)



# 405 - b05808d changlog

## [0.14.4](https://github.com/vuejs/vite/compare/v0.14.3...v0.14.4) (2020-05-13)

### Bug Fixes

- cva对react构建命令更改 + 构建下alias可以在其他plugins中使用 ([c243d09](https://github.com/vuejs/vite/commit/c243d09dbb7cbc7aaf5c79e2e2ea3be899d37933))
- 修复windows下模块改写错误 ([4f2953e](https://github.com/vuejs/vite/commit/4f2953e429718c28ec4f1a8e6559d7c75630e70b))
- 支持.mjs拓展 &模块路径寻找中尝试添加拓展名 ([02753b7](https://github.com/vuejs/vite/commit/02753b7fda300bd15b7fa61d5e9ed2cce1a6ac4f)), closes [#127](https://github.com/vuejs/vite/issues/127)
- **history-fallback:** 添加`.`正确重定向网址 ([7f5e459](https://github.com/vuejs/vite/commit/7f5e4596a4e7254cc5f173fbf5261f3f47c926a9)), closes [#130](https://github.com/vuejs/vite/issues/130)
- 使用ctx.path代替cleanUrl ([#133](https://github.com/vuejs/vite/issues/133)) ([f558a88](https://github.com/vuejs/vite/commit/f558a880a3aa04f6024ff05f25924568a94a9b54))

### Features

- 改进模块解析([405f685](https://github.com/vuejs/vite/commit/405f685f7b0772881f5bd296b136296e94e35085))



# 406 - 816f3e5 v0.14.4

release v0.14.4



# 407 - c0c1991 cva v1.4.1

release cva v1.4.1



# 408 - 976b8a2 自动卸载sw

改动部分:

- `client/client.ts`: 在sw被禁止的情况下,如果发现有sw,则把它卸载掉
- `serverPluginHmr.ts`: `__SW_ENABLED__ = !!config.serviceWorker`,提供给`client.ts`使用
- `sw/serviceWorker.ts`: 对`client.ts`不进行缓存



# 409 - e6bfd20 cva 原本在命令行的flag现移动在config中配置jsx

![2](2.png)



# 410 - 49a44b6 服务启动即调用`optimizeDeps` & 去除`web_modules` & 支持命名导入cjs & 去除`windows.__SW_ENABLED__`

改动部分:

- `node/resolver.ts`: 新增`resolveBareModule` & `resolveOptimizedModule` ;迁移 `resolveNodeModule`到这里(详 **改动一**)
- `build/buildPluginResolve.ts`: `resolveId`中去除对`webModulePath`的路径处理
- `build/index.ts`: 封装部分plugin为`createBaseRollupPlugins`,提供`optimizeDeps` rollup构建使用;使用`@rollup/plugin-commonjs`支持命名导入cjs(详 **改动三**)
- `node/depOptimizer.ts`: 把`node/optimizer.ts`重新命名为`/node/depOptimizer.ts`
- `node/server/serverPluginModuleRewrite.ts`: `__SW_ENABLED__`在`clint.ts`替换即可,不需要暴露到`windows`

### 改动一

#### resolveOptimizedModule

比如现在有A包,经过路径处理会变成`/@modules/depOptimizer/A.js`

> 我比较好奇的是为什么不直接使用resolveEx拓展后缀?而且在这里拓展成A.js是错误吧。
>
> 目前windwos有BUG,这里反向推理可以知道optimizeDeps把dependencies的包给打成一个文件入口了。

```typescript
import { OPTIMIZE_CACHE_DIR } from './depOptimizer'
// `node_modules/.vite_opt_cache`

const viteOptimizedMap = new Map()

export function resolveOptimizedModule(
  root: string,
  id: string
): string | undef
Download .txt
gitextract_xl9jd137/

├── 101-110/
│   └── commit-101-110.md
├── 11-20/
│   ├── commit-11-20.md
│   └── main.rs
├── 111-120/
│   └── commit-111-120.md
├── 121-130/
│   └── commit-121-130.md
├── 131-140/
│   └── commit-131-140.md
├── 141-150/
│   └── commit-141-150.md
├── 151-160/
│   └── 151-160.md
├── 161-170/
│   └── 161-170.md
├── 171-180/
│   └── 171-180.md
├── 181-190/
│   └── 181-190.md
├── 191-200/
│   └── 191-200.md
├── 201-210/
│   └── 201-210.md
├── 21-30/
│   └── commit-21-30.md
├── 211-220/
│   └── 211-220.md
├── 221-230/
│   └── 221-230.md
├── 231-240/
│   └── 231-240.md
├── 241-250/
│   └── 241-250.md
├── 251-260/
│   └── 251-260.md
├── 261-270/
│   └── 261-270.md
├── 271-280/
│   └── 271-280.md
├── 281-290/
│   └── 281-290.md
├── 291-300/
│   └── 291-300.md
├── 301-310/
│   └── 301-310.md
├── 31-40/
│   └── commit-31-40.md
├── 311-320/
│   └── 311-320.md
├── 321-330/
│   └── 321-330.md
├── 331-340/
│   └── 331-340.md
├── 341-350/
│   └── 341-350.md
├── 351-360/
│   └── 351-360.md
├── 361-370/
│   └── 361-370.md
├── 371-380/
│   └── 371-380.md
├── 381-390/
│   └── 381-390.md
├── 391-400/
│   └── 391-400.md
├── 401-410/
│   └── 401-410.md
├── 41-50/
│   └── commit-41-50.md
├── 411-420/
│   └── 411-420.md
├── 421-430/
│   └── 421-430.md
├── 431-440/
│   └── 431-440.md
├── 441-450/
│   └── 441-450.md
├── 451-460/
│   └── 451-460.md
├── 461-470/
│   └── 461-470.md
├── 471-480/
│   └── 471-480.md
├── 481-490/
│   └── 481-490.md
├── 491-500/
│   └── 491-500.md
├── 501-510/
│   └── 501-510.md
├── 51-60/
│   └── commit-51-60.md
├── 511-520/
│   └── 511-520.md
├── 521-530/
│   └── 521-530.md
├── 531-540/
│   └── 531-540.md
├── 541-550/
│   └── 541-550.md
├── 551-560/
│   └── 551-560.md
├── 561-570/
│   └── 561-570.md
├── 571-580/
│   └── 571-580.md
├── 581-590/
│   └── 581-590.md
├── 591-600/
│   └── 591-600.md
├── 601-610/
│   └── 601-610.md
├── 61-70/
│   └── commit-61-70.md
├── 611-620/
│   └── 611-620.md
├── 71-80/
│   └── commit-71-80.md
├── 81-90/
│   └── commit-81-90.md
├── 91-100/
│   └── commit-91-100.md
├── readme.md
└── temp/
    └── temp.md
Download .txt
SYMBOL INDEX (2 symbols across 1 files)

FILE: 11-20/main.rs
  type SpreadsheetCell (line 1) | enum SpreadsheetCell {
  function main (line 7) | fn main() {
Condensed preview — 64 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (531K chars).
[
  {
    "path": "101-110/commit-101-110.md",
    "chars": 3361,
    "preview": "# 101 - d1fe471 新增id映射到请求路径\n\n允许`resolver`拥有`idToRequest`方法。\n\n```typescript\nidToRequest: (id: string) => {\n      for (con"
  },
  {
    "path": "11-20/commit-11-20.md",
    "chars": 2515,
    "preview": "# 11 - 7f207eb annotation\n\n添加测试注释(准备测试style HMR)。\n\n\n\n# 12 - 5c0f552 fix propublishOnly\n\n注释。\n\n```json\n{\n-   prepublishOnl"
  },
  {
    "path": "11-20/main.rs",
    "chars": 566,
    "preview": "enum SpreadsheetCell {\n        Int(i32),\n        Float(f64),\n        Text(String),\n}\n\nfn main() {\n    let row = vec![Spr"
  },
  {
    "path": "111-120/commit-111-120.md",
    "chars": 4823,
    "preview": "# 111 - 14346ee 修复`__DEV__`条件下`hot.accept`没有被正确渲染的问题\n\n```typescript\n// 错误\n__DEV__ && hot.accept('./foo.js', ({ foo }) =>"
  },
  {
    "path": "121-130/commit-121-130.md",
    "chars": 3250,
    "preview": "# 121 - 1a26b7a 推断正确的构建结果\n\n判断传入build的类型,输出对应的类型。\n\n```typescript\ninterface SingleBuildOptions extends BuildOptionsBase {\n"
  },
  {
    "path": "131-140/commit-131-140.md",
    "chars": 2979,
    "preview": "# 131 - 30ab444 `hmr.accept`支持调用本身\n\n输入:\n\n```typescript\n// # foo.js\n\nimport { hot } from '@hmr'\n\nexport const count = 1\n\n"
  },
  {
    "path": "141-150/commit-141-150.md",
    "chars": 9000,
    "preview": "# 141 - 6125ee9 拼写错误[#27](https://github.com/vitejs/vite/pull/27)\n\n[#27](https://github.com/vitejs/vite/pull/27)\n\n\n\n# 14"
  },
  {
    "path": "151-160/151-160.md",
    "chars": 7161,
    "preview": "# 151 - 63b4de6 处理`<script src`\n\n1. 构建模式下,使用正则匹配出`src`里面的内容`<script src=\"main.js\">`\n\n```typescript\nconst srcRE = /\\bsrc="
  },
  {
    "path": "161-170/161-170.md",
    "chars": 6913,
    "preview": "# 161 - e947303 v0.9.1\n\nrelease v0.9.1\n\n\n\n# 162 - bf1abbf changelog\n\n## [0.9.1](https://github.com/vuejs/vite/compare/v0"
  },
  {
    "path": "171-180/171-180.md",
    "chars": 5714,
    "preview": "# 171 - 0187d3f 对于`js`引入的`css`也要支持`postcss config` 与 `postcss`\n\n## `node/config.ts`\n\n抽离`serverPluginVue.ts`的加载`postcss c"
  },
  {
    "path": "181-190/181-190.md",
    "chars": 2833,
    "preview": "# 181 -  adea1a3 bump `create-vite-app` vue\n\nbump vue\n\n\n\n# 182 - 4b5d180 更新文档 - feature\n\n### CSS / JSON Importing\n\n你可以直接"
  },
  {
    "path": "191-200/191-200.md",
    "chars": 6869,
    "preview": "# 191 - 72d73b1 [#50](https://github.com/vitejs/vite/pull/46) 修正readme链接\n\n已在[182(4b5d180)](https://github.com/Kingbultse"
  },
  {
    "path": "201-210/201-210.md",
    "chars": 5440,
    "preview": "# 201 - b48ae5b [#32](https://github.com/vitejs/vite/issues/32) 兼容node 10\n\n![1](./1.png)\n\n尝试修改`\"node\": \">=15.0.0\"`,然后`ya"
  },
  {
    "path": "21-30/commit-21-30.md",
    "chars": 3738,
    "preview": "# 21 - 3e0ff79 v0.1.2发布\n\n```json\n{\n-   version: \"0.1.1\"\n+   version: \"0.1.2\"    \n}\n```\n\n\n\n# 22 - c74b24e 重构使用koa,废弃serve"
  },
  {
    "path": "211-220/211-220.md",
    "chars": 6451,
    "preview": "# 211 - f1c03ff 设置preserveEntrySignatures\n\n## node/build.ts\n\n`rollup.preserveEntrySignatures = false`\n\n- 类型:`\"strict\" | "
  },
  {
    "path": "221-230/221-230.md",
    "chars": 9283,
    "preview": "# 221 - 4fd2cdf 构建模式默认使用esbuild\n\n```typescript\n// terser is used by default for better compression, but the user can als"
  },
  {
    "path": "231-240/231-240.md",
    "chars": 3700,
    "preview": "# 231 - e78c9f7 修复外链import路径改写错误\n\n不应该中断改写`import`流程,不然遇到外链后,不返回任何的`code`。\n\n```typescript\nif (isExternalUrl(id)) {\n  brea"
  },
  {
    "path": "241-250/241-250.md",
    "chars": 9950,
    "preview": "# 241 - 5f05f1e 简化`web_modules`路径处理器\n\n改动:\n\n- `node/serverPluginModuleResolve.ts`,`resolveWebModule`被重构(详 **改动一**)\n\n## 改动"
  },
  {
    "path": "251-260/251-260.md",
    "chars": 4992,
    "preview": "# 251 - db52dd3 v0.11.2\n\nrelease v0.11.2\n\n> 我在某版本号规范上看到,如果改动了代码功能,都应该第二位增加,第三位是文档类的修改。但是现在似乎让我感觉不对,因为我们每次修改发布`npm`如果是文档,"
  },
  {
    "path": "261-270/261-270.md",
    "chars": 8198,
    "preview": "# 261 - 44a8250 增强错误输出\n\n改动部分:\n\n- `server/serverPluginVue.ts`,错误详细位置输出。\n\n![1](1.png)\n\n```typescript\nif (errors.length) {\n"
  },
  {
    "path": "271-280/271-280.md",
    "chars": 5539,
    "preview": "# 271 - 0c3c546 ci windows使用node x64架构\n\n因为`esbuild`不支持在node x86架构中使用,所以切换为x64架构。\n\n[node架构](https://newsn.net/say/node-ar"
  },
  {
    "path": "281-290/281-290.md",
    "chars": 2103,
    "preview": "# 281 - c2806d7 v0.11.5\n\nrelease v0.11.5\n\n\n\n# 282 - 29099ae 修复在不进行本地安装的情况下处理vue问题\n\n改动部分:\n\n- `node/build/buildPluginResol"
  },
  {
    "path": "291-300/291-300.md",
    "chars": 6531,
    "preview": "# 291 - 9061e44 支持使用`js`引入静态资源 + 打包`public`资源特殊处理\n\n改动部分:\n\n- `node/build/buildPluginAsset.ts resolveAsset`:  `public`开头的路"
  },
  {
    "path": "301-310/301-310.md",
    "chars": 2873,
    "preview": "# 301 - 253da59 changelog\n\n### [0.13.0](https://github.com/vuejs/vite/compare/v0.12.0...v0.13.0) (2020-05-08)\n\n#### Feat"
  },
  {
    "path": "31-40/commit-31-40.md",
    "chars": 4529,
    "preview": "# 31 - c45e066 整合```css hmr```\n\n把更新渲染```<style>```的代码,整合到```client```中。省去每次提取```<styele>```模块都需要经过服务器语法词汇分析的过程。\n\n\n\n# 32 "
  },
  {
    "path": "311-320/311-320.md",
    "chars": 4345,
    "preview": "# 311 - c18451d 构建:简化tsconfig\n\n改动部分:\n\n`tsconfig.base.json`:去除默认选项,`esModuleInterop`开启后,`allowSyntheticDefaultImports`会为`"
  },
  {
    "path": "321-330/321-330.md",
    "chars": 12191,
    "preview": "# 321 - 6ee0168 [#105](https://github.com/vitejs/vite/pull/105) 修复浏览器无法打开的问题\n\n主要是因为`ececa`包被放进了`devDependencies`,从而没有找到该"
  },
  {
    "path": "331-340/331-340.md",
    "chars": 11330,
    "preview": "# 331 - 6cf1e31 issue 模板\n\n## Before you continue...\n\n如果你升级`Vite`后,出现了问题,尝试使用浏览器删除缓存,勾选“Disable cache\"\n\n\n\n# 332 - a4524b4"
  },
  {
    "path": "341-350/341-350.md",
    "chars": 12204,
    "preview": "# 341 - efc853f 对于jsx|tsx自动引入`jsxFactory `&`Fragment`,新增VUE版JSX处理器\n\n改动部分:\n\n- `node/config.ts`:jsx选项新增`'vue' | 'preact' |"
  },
  {
    "path": "351-360/351-360.md",
    "chars": 1389,
    "preview": "# 351 - cva v1.2.0\n\nrelase cva v1.2.0\n\n\n\n# 352 - cva 更新模板\n\n`fs`包更改为`fs-extra`支持`promise`。\n\n> 省略模板更新了什么\n\n\n\n# 353 - 3d1695"
  },
  {
    "path": "361-370/361-370.md",
    "chars": 1066,
    "preview": "# 362 - 0f106bd cva v1.3.1\n\nrelease cva v1.3.1\n\n\n\n# 363 - 0cacb17 增强编译error输出(格式)\n\n`server/serverPluginVue.ts` 增强编译error"
  },
  {
    "path": "371-380/371-380.md",
    "chars": 9826,
    "preview": "# 371 - 3e27277 chore git ignore explorations\n\n`.gitignore`添加`explorations`文件夹,我们想提交PR,fork `vite`后,可以在创建`explorations`文"
  },
  {
    "path": "381-390/381-390.md",
    "chars": 2861,
    "preview": "# 381 - e8e4a4b remove todo\n\n去除todo: 是否使用文件内容hash 或者 lastModified来代替timestamp?\n\n![1](1.png)\n\n`sw`缓存有两种类型:\n\n1. `const USE"
  },
  {
    "path": "391-400/391-400.md",
    "chars": 6936,
    "preview": "# 391 - f9b267f cva 使用@pika/react & alias\n\n![1](1.png)\n\n\n\n# 392 - c8a1ffd 服务启动后的每个首次请求不返回304\n\n改动部分:\n\n- `node/server/serv"
  },
  {
    "path": "401-410/401-410.md",
    "chars": 7101,
    "preview": "# 401 - 7f5e459 代码整理\n\n 代码整理\n\n\n\n# 402 - 02753b7 添加`.mjs`拓展\n\n引入模块尝试,在寻找不了`package.json`的情况下,拓展文件后缀。\n\n如`import lodash from "
  },
  {
    "path": "41-50/commit-41-50.md",
    "chars": 5770,
    "preview": "# 41 - ae3c83a 修改特殊路径名称\n\n处理特殊路径```__```改为```@```\n\n### hmr.ts\n\n```typescript\n- if(ctx.path !== '/__hmrClient')\n+ if(ctx.p"
  },
  {
    "path": "411-420/411-420.md",
    "chars": 9898,
    "preview": "# 411 - 47bfc41 build resolve去除`optimizedModule`\n\n插件去除对`optimizedModule`的处理,即构建模式不会使用被`optimizedModule`处理过的包。\n\n> 什么原因?该功"
  },
  {
    "path": "421-430/421-430.md",
    "chars": 5512,
    "preview": "# 421 - d7fb6a9 optimize功能中不支持的拓展不应打包 & 修复无法引入如`css`的包问题\n\n英文msg: fix support for `non-js` module imports,即修复无法引入非`js`模块包"
  },
  {
    "path": "431-440/431-440.md",
    "chars": 12217,
    "preview": "# 431 - 0bfd283 changelog\n\n [0.15.1](https://github.com/vuejs/vite/compare/v0.15.0...v0.15.1) (2020-05-14)\n\n### Bug Fixe"
  },
  {
    "path": "441-450/441-450.md",
    "chars": 8381,
    "preview": "# 441 - 5769749 v0.15.3\n\nrelease `vite` v0.15.3\n\n\n\n# 442 - d1bdf5a `vite:resolve`插件,当alias不一样时才会调用resolve\n\nmsg: 修复`vite:"
  },
  {
    "path": "451-460/451-460.md",
    "chars": 6665,
    "preview": "# 451 - d3e08d2 `bump vue` & 补上忘了添加进去的`mime-types`依赖\n\n`mime-types`被`puppeteer`作为了依赖,可以正常使用,所以尤大并没有察觉。\n\n\n\n# 452 - c40978f"
  },
  {
    "path": "461-470/461-470.md",
    "chars": 12355,
    "preview": "# 461 - 0e68cc1 在没有`esm`依赖的时候输出更多有用的信息\n\n改动部分:\n\n- `node/depOptimizer.ts`: 新增`commonJSWhitelist`,调用`resolveNodeModuleEntry"
  },
  {
    "path": "471-480/471-480.md",
    "chars": 4693,
    "preview": "# 471 - 595a093 chore 删除debugger\n\nundef的hmr accept重构PR忘记删除了`debugger`\n\n\n\n# 472 - ef4fc42 支持无参数直接调用`hot.accept()` & 支持分析`"
  },
  {
    "path": "481-490/481-490.md",
    "chars": 2420,
    "preview": "# 481 - 0498f73 cva将被分开为单独一个项目\n\nvite不再内嵌`cva`,删除`cva`。\n\n\n\n# 482 - d3a4793 chore 修正readme proxy的用法 [#192](https://github."
  },
  {
    "path": "491-500/491-500.md",
    "chars": 8381,
    "preview": "# 491 - fb86f0a 重构 [#185](https://github.com/vitejs/vite/pull/185) html逻辑代码被转移到新增的`htmlPlugin`插件当中 & `full-reload`事件准确刷新"
  },
  {
    "path": "501-510/501-510.md",
    "chars": 6292,
    "preview": "# 501 - ada8886 依赖优化的包,使用depImport且没有后缀,将输出warning\n\n改动部分:\n\n- `node/resolver.ts`:新增检测后缀 & jsSrcRE(详 **改动一**)\n\n> depImport"
  },
  {
    "path": "51-60/commit-51-60.md",
    "chars": 13533,
    "preview": "# 51 - 86147d1 重构server路径\n\n```server```端,名称统一改成```node```端,即更改路径/文件夹名称\n\n新增了两个文件:\n\n- ```src/node/build.ts```:后续分析\n\n- ```s"
  },
  {
    "path": "511-520/511-520.md",
    "chars": 3938,
    "preview": "# 511 - 8a9710b 如果`index.html`不存在,则设置`ctx.url`为`/index.html`\n\n- `node/server/serverPluginServerStatic.ts`:`history API f"
  },
  {
    "path": "521-530/521-530.md",
    "chars": 12086,
    "preview": "# 521 - 89fe0a9 支持从`.env`文件加载`env`属性 [#223](https://github.com/vitejs/vite/issues/223)\n\n改动部分:\n\n- `package.json`:新增`doten"
  },
  {
    "path": "531-540/531-540.md",
    "chars": 5618,
    "preview": "# 531 - f448ffe v0.16.7\n\nrelease vite v0.16.7\n\n\n\n# 532 - b5ddcdc [#239](https://github.com/vitejs/vite/pull/239) 为#230的d"
  },
  {
    "path": "541-550/541-550.md",
    "chars": 2980,
    "preview": "# 541 - bf2b2a9 public需要被复制到dist目录下,而不是dist/public\n\n改动部分:\n\n- `src/node/build/buildPluginAsset.ts`:`resolveAsset`生成的路径去除`"
  },
  {
    "path": "551-560/551-560.md",
    "chars": 9798,
    "preview": "# 551 - db8b6b2 #235 支持自定义`NODE_ENV`,支持`--mode`\n\n改动部分:\n\n- `src/node/cli.ts`:新增`--mode | -m`,默认serve模式下`development`,buil"
  },
  {
    "path": "561-570/561-570.md",
    "chars": 8710,
    "preview": "# 561 - 84fcfb6 import改写需要考虑非项目下的路径\n\n改动部分:\n\n- `src/node/server/serverPluginModuleRewrite.ts`:帮文件添加后缀,需要考虑到路径本身是不在`vite c"
  },
  {
    "path": "571-580/571-580.md",
    "chars": 3283,
    "preview": "# 571 - dd0205f `esbuild`服务,如果停止,则设置`_service = undefined`\n\n`src/nopde/esbuildService.ts`:`stopService`调用后,需要设置`_service"
  },
  {
    "path": "581-590/581-590.md",
    "chars": 11074,
    "preview": "# 581 - 2e81e64 新增`babelParse.ts`,用于封装`parse`选项 & fix #271 `serverPluginVue`对于`export default`的错误替换\n\n改动部分:\n\n- `src/node/"
  },
  {
    "path": "591-600/591-600.md",
    "chars": 4432,
    "preview": "# 591 - 4508f31 #273 readme 环境变量\n\n### Modes and Environment Variables\n\n> 0.16.7+\n\nThe mode option is used to specify the"
  },
  {
    "path": "601-610/601-610.md",
    "chars": 7437,
    "preview": "# 601 - 4ad4fea v0.17.2\n\nrelease v0.17.2\n\n\n\n# 602 - ed2be13 ci调试 判断import.meta.hot.data存在再输出`console.log`\n\n#### 关于`impor"
  },
  {
    "path": "61-70/commit-61-70.md",
    "chars": 2883,
    "preview": "# 61 - 7eda441  readme\n\n新增build参数配置(但是未实现)\n\n- `vite build --root dir`:  在目标目录而不是当前工作目录中生成文件\n\n- `vite build --cdn`:  从CDN"
  },
  {
    "path": "611-620/611-620.md",
    "chars": 2592,
    "preview": "# 611 - 71b1d0e changelog\n\n# [0.18.0](https://github.com/vuejs/vite/compare/v0.17.2...v0.18.0) (2020-05-28)\n\n### Bug Fix"
  },
  {
    "path": "71-80/commit-71-80.md",
    "chars": 4268,
    "preview": "# 71 - 1147a79 create-vite-app模板更新\n\n新增```create-vite-app```文件夹,可以通过命令`npx create-vite-app <project-name>`来创建。\n\n![image-2"
  },
  {
    "path": "81-90/commit-81-90.md",
    "chars": 5086,
    "preview": "# commit-81 暴露watchAPI\n\n```typescript\nexport type ViteWatcher = FSWatcher & {\n  handleVueReload: (file: string, timestam"
  },
  {
    "path": "91-100/commit-91-100.md",
    "chars": 1786,
    "preview": "# 91 - 43fe56f 修改字符\n\n`util.ts`中的`readCache`方法里面写错`etag`字母(原tag)。\n\n\n\n# 92 - 875c0c6 缓存304\n\n我在commit-84提过,为什么在`serverPlugi"
  },
  {
    "path": "readme.md",
    "chars": 23847,
    "preview": "# 1 - 820c2cf 根基\n\n### npm bin的知识点\n\n我们写包的时候,可以在package.json中添加bin字段。\n\n```json\n{\n  \"bin\": {\n    \"vds\": \"bin/vds.js\"\n  }\n}\n"
  },
  {
    "path": "temp/temp.md",
    "chars": 0,
    "preview": ""
  }
]

About this extraction

This page contains the full source code of the Kingbultsea/vite-analysis GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 64 files (395.0 KB), approximately 148.9k tokens, and a symbol index with 2 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!