Skip to content

Commit

Permalink
refactor: seperate vite runner to vite-node package (#495)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu authored Jan 10, 2022
1 parent 25eb95d commit 4498ae9
Show file tree
Hide file tree
Showing 25 changed files with 696 additions and 318 deletions.
82 changes: 82 additions & 0 deletions packages/vite-node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# vite-node

[![NPM version](https://img.shields.io/npm/v/vite-node?color=a1b858&label=)](https://www.npmjs.com/package/vite-node)

Vite as Node runtime. The engine powers [Vitest](https://github.com/vitest-dev/vitest).

## Features

- Out-of-box ESM & TypeScript support (possible for more with plugins)
- Top-level await
- Vite plugins, resolve, aliasing
- Respect `vite.config.ts`
- Shims for `__dirname` and `__filename` in ESM
- Access to native node modules like `fs`, `path`, etc.

## CLI Usage

Run JS/TS file on Node.js using Vite's resolvers and transformers.

```bash
npx vite-node index.ts
```

Options:

```bash
npx vite-node -h
```

## Programmatic Usage

In Vite Node, the server and runner (client) are separated, so you can integrate them in different contexts (workers, cross-process, or remote) if needed. The demo below shows a simple example of having the server and running in the same context

```ts
import { createServer } from 'vite'
import { ViteNodeServer } from 'vite-node/server'
import { ViteNodeRunner } from 'vite-node/client'

// create vite server
const server = await createServer()
// this is need to initialize the plugins
await server.pluginContainer.buildStart({})

// create vite-node server
const node = new ViteNodeServer(server)

// create vite-node runner
const runner = new ViteNodeRunner({
root: server.config.root,
base: server.config.base,
// when having the server and runner in a different context,
// you will need to handle the communication between them
// and pass to this function
fetchModule(id) {
return node.fetchModule(id)
},
})

// execute the file
await runner.run('./example.ts')

// close the vite server
await server.close()
```

## Credits

Based on [@pi0](https://github.com/pi0)'s brilliant idea of having a Vite server as the on-demand transforming service for [Nuxt's Vite SSR](https://github.com/nuxt/vite/pull/201).

Thanks [@brillout](https://github.com/brillout) for kindly sharing this package name.

## Sponsors

<p align="center">
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
</a>
</p>

## License

[MIT](./LICENSE) License © 2021 [Anthony Fu](https://github.com/antfu)
67 changes: 67 additions & 0 deletions packages/vite-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "vite-node",
"version": "0.0.139",
"description": "Vite as Node.js runtime",
"homepage": "https://github.com/vitest-dev/vitest#readme",
"bugs": {
"url": "https://github.com/vitest-dev/vitest/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vitest-dev/vitest.git",
"directory": "packages/vite-node"
},
"funding": "https://github.com/sponsors/antfu",
"license": "MIT",
"author": "Anthony Fu <[email protected]>",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./client": {
"import": "./dist/client.js",
"types": "./dist/client.d.ts"
},
"./server": {
"import": "./dist/server.js",
"types": "./dist/server.d.ts"
},
"./utils": {
"import": "./dist/utils.js",
"types": "./dist/utils.d.ts"
}
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
"vite-node": "./vite-node.js"
},
"files": [
"dist",
"*.d.ts",
"*.mjs"
],
"scripts": {
"build": "rimraf dist && rollup -c",
"dev": "rollup -c --watch --watch.include=src/**",
"prepublishOnly": "nr build",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"kolorist": "^1.5.1",
"minimist": "^1.2.5",
"mlly": "^0.3.17",
"pathe": "^0.2.0",
"vite": "^2.7.10"
},
"devDependencies": {
"@types/minimist": "^1.2.2",
"rollup": "^2.63.0"
},
"engines": {
"node": ">=14.14.0"
}
}
59 changes: 59 additions & 0 deletions packages/vite-node/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import esbuild from 'rollup-plugin-esbuild'
import dts from 'rollup-plugin-dts'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import alias from '@rollup/plugin-alias'
import pkg from './package.json'

const entry = [
'src/index.ts',
'src/server.ts',
'src/client.ts',
'src/utils.ts',
'src/cli.ts',
]

const external = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
'birpc',
'vite',
]

export default () => [
{
input: entry,
output: {
dir: 'dist',
format: 'esm',
},
external,
plugins: [
alias({
entries: [
{ find: /^node:(.+)$/, replacement: '$1' },
],
}),
resolve({
preferBuiltins: true,
}),
json(),
commonjs(),
esbuild({
target: 'node14',
}),
],
},
...entry.map(input => ({
input,
output: {
file: input.replace('src/', 'dist/').replace('.ts', '.d.ts'),
format: 'esm',
},
external,
plugins: [
dts({ respectExternal: true }),
],
})),
]
91 changes: 91 additions & 0 deletions packages/vite-node/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import minimist from 'minimist'
import { dim, red } from 'kolorist'
import { createServer } from 'vite'
import { ViteNodeServer } from './server'
import { ViteNodeRunner } from './client'

const argv = minimist(process.argv.slice(2), {
'alias': {
r: 'root',
c: 'config',
h: 'help',
w: 'watch',
s: 'silent',
},
'--': true,
'string': ['root', 'config'],
'boolean': ['help', 'watch', 'silent'],
unknown(name: string) {
if (name[0] === '-') {
console.error(red(`Unknown argument: ${name}`))
help()
process.exit(1)
}
return true
},
})

if (argv.help) {
help()
process.exit(0)
}

if (!argv._.length) {
console.error(red('No files specified.'))
help()
process.exit(1)
}

// forward argv
process.argv = [...process.argv.slice(0, 2), ...(argv['--'] || [])]

run(argv)

function help() {
// eslint-disable-next-line no-console
console.log(`
Usage:
$ vite-node [options] [files]
Options:
-r, --root <path> ${dim('[string]')} use specified root directory
-c, --config <file> ${dim('[string]')} use specified config file
-w, --watch ${dim('[boolean]')} restart on file changes, similar to "nodemon"
-s, --silent ${dim('[boolean]')} do not emit errors and logs
--vue ${dim('[boolean]')} support for importing Vue component
`)
}

export interface CliOptions {
files?: string[]
_?: string[]
root?: string
config?: string
}

async function run(options: CliOptions = {}) {
const files = options.files || options._ || []

const server = await createServer({
logLevel: 'error',
clearScreen: false,
configFile: options.config,
root: options.root,
})
await server.pluginContainer.buildStart({})

const node = new ViteNodeServer(server)

const runner = new ViteNodeRunner({
root: server.config.root,
base: server.config.base,
fetchModule(id) {
return node.fetchModule(id)
},
})

for (const file of files)
await runner.run(file)

await server.close()
}
Loading

0 comments on commit 4498ae9

Please sign in to comment.