vite-node 0.0.3 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +16 -7
  2. package/index.mjs +128 -40
  3. package/package.json +10 -10
package/README.md CHANGED
@@ -6,9 +6,6 @@ Vite as Node runtime.
6
6
 
7
7
  > **EXPERIMENTAL**
8
8
 
9
- ## Why?
10
-
11
- It runs Vite's id resolving, module transforming, and most importantly, the powerful plugins system!
12
9
 
13
10
  ## Usage
14
11
 
@@ -16,19 +13,31 @@ It runs Vite's id resolving, module transforming, and most importantly, the powe
16
13
  npx vite-node index.ts
17
14
  ```
18
15
 
16
+ Options:
17
+
18
+ ```bash
19
+ npx vite-node -h
20
+ ```
21
+
19
22
  ## Features
20
23
 
21
24
  - Out-of-box ESM & TypeScript support (possible for more with plugins)
22
25
  - Top-level await
23
- - Shims for `__dirname` and `__filename`
26
+ - Vite plugins, resolve, aliasing
24
27
  - Respect `vite.config.ts`
25
- - Access to node modules like `fs`, `path` etc.
28
+ - Shims for `__dirname` and `__filename` in ESM
29
+ - Access to native node modules like `fs`, `path`, etc.
30
+ - Watch mode (like `nodemon`)
26
31
 
27
32
  ## When NOT to Use
28
33
 
29
34
  - Production, yet - in very early stage, check it later
30
- - Most of the time when other tools can do that job
31
- - We will need to start a Vite server upon each execution, which will have certain overhead. Only use it when you want the exactly same behavior as Vite or the powerful plugins system (for example, testing components that have Vite-specific setup).
35
+ - Most of the time, when other tools can do that job
36
+ - We need to start a Vite server upon each execution, which inevitably introduces some overhead. Only use it when you want the same behavior as Vite or the powerful plugins system (for example, testing components with a Vite-specific setup).
37
+
38
+ ## Why?
39
+
40
+ It runs Vite's id resolving, module transforming, and most importantly, the powerful plugins system!
32
41
 
33
42
  ## How?
34
43
 
package/index.mjs CHANGED
@@ -1,19 +1,23 @@
1
+ /* eslint-disable no-console */
1
2
  import { builtinModules, createRequire } from 'module'
2
3
  import { pathToFileURL } from 'url'
3
- import { join, dirname, resolve } from 'path'
4
+ import { dirname, resolve, relative } from 'path'
5
+ import vm from 'vm'
4
6
  import { createServer } from 'vite'
5
7
  import createDebug from 'debug'
6
8
  import minimist from 'minimist'
7
- import { red, dim } from 'kolorist'
9
+ import { red, dim, yellow, green, inverse, cyan } from 'kolorist'
8
10
 
9
11
  const argv = minimist(process.argv.slice(2), {
10
12
  alias: {
11
13
  r: 'root',
12
14
  c: 'config',
13
15
  h: 'help',
16
+ w: 'watch',
17
+ s: 'silent',
14
18
  },
15
19
  string: ['root', 'config'],
16
- boolean: ['help', 'vue'],
20
+ boolean: ['help', 'vue', 'watch', 'silent'],
17
21
  unknown(name) {
18
22
  if (name[0] === '-') {
19
23
  console.error(red(`Unknown argument: ${name}`))
@@ -37,12 +41,13 @@ if (!argv._.length) {
37
41
 
38
42
  const debugRequest = createDebug('vite-node:request')
39
43
  const debugTransform = createDebug('vite-node:transform')
40
- const AsyncFunction = Object.getPrototypeOf(async() => {}).constructor
41
44
 
42
45
  const root = argv.root || process.cwd()
43
46
  process.chdir(root)
44
47
 
45
48
  const server = await createServer({
49
+ logLevel: 'error',
50
+ clearScreen: false,
46
51
  configFile: argv.config,
47
52
  root,
48
53
  resolve: argv.vue
@@ -60,27 +65,95 @@ const server = await createServer({
60
65
  : {},
61
66
  })
62
67
  await server.pluginContainer.buildStart({})
63
- await execute(files, server, argv)
64
- await server.close()
68
+ let executing = false
69
+
70
+ async function run() {
71
+ process.exitCode = 0
72
+ executing = true
73
+ let err
74
+ try {
75
+ await execute(files, server, argv)
76
+ }
77
+ catch (e) {
78
+ console.error(e)
79
+ err = e
80
+ if (!argv.watch)
81
+ process.exit(1)
82
+ }
83
+ finally {
84
+ executing = false
85
+ }
86
+
87
+ if (argv.watch) {
88
+ setTimeout(() => {
89
+ if (err || process.exitCode)
90
+ log(inverse(red(' vite node ')), red('program exited with error, waiting for file changes...'))
91
+ else
92
+ log(inverse(green(' vite node ')), green('program exited, waiting for file changes...'))
93
+ }, 10)
94
+ }
95
+ else {
96
+ await server.close()
97
+ }
98
+ }
99
+
100
+ if (argv.watch) {
101
+ log(inverse(cyan(' vite node ')), cyan('watch mode enabled\n'))
102
+
103
+ server.watcher.on('change', (file) => {
104
+ if (!executing) {
105
+ log(inverse(yellow(' vite node ')), yellow(`${file} changed, restarting...\n`))
106
+ run()
107
+ }
108
+ })
109
+ }
110
+
111
+ await run(files, server, argv)
65
112
 
66
113
  // --- CLI END ---
67
114
 
115
+ function normalizeId(id) {
116
+ // Virtual modules start with `\0`
117
+ if (id && id.startsWith('/@id/__x00__'))
118
+ id = `\0${id.slice('/@id/__x00__'.length)}`
119
+ if (id && id.startsWith('/@id/'))
120
+ id = id.slice('/@id/'.length)
121
+ return id
122
+ }
123
+
124
+ function toFilePath(id) {
125
+ const absolute = id.startsWith('/@fs/')
126
+ ? id.slice(4)
127
+ : slash(resolve(server.config.root, id.slice(1)))
128
+
129
+ return absolute
130
+ }
131
+
68
132
  async function execute(files, server) {
69
- const cache = {}
133
+ const __pendingModules__ = new Map()
70
134
 
71
- async function request(id) {
72
- // Virtual modules start with `\0`
73
- if (id && id.startsWith('/@id/__x00__'))
74
- id = `\0${id.slice('/@id/__x00__'.length)}`
75
- if (id && id.startsWith('/@id/'))
76
- id = id.slice('/@id/'.length)
135
+ const result = []
136
+ for (const file of files)
137
+ result.push(await cachedRequest(`/@fs/${slash(resolve(file))}`, []))
138
+ return result
77
139
 
78
- if (builtinModules.includes(id))
79
- return import(id)
140
+ async function directRequest(rawId, callstack) {
141
+ if (builtinModules.includes(rawId))
142
+ return import(rawId)
143
+
144
+ callstack = [...callstack, rawId]
145
+ const request = async(dep) => {
146
+ if (callstack.includes(dep)) {
147
+ throw new Error(`${red('Circular dependency detected')}\nStack:\n${[...callstack, dep].reverse().map((i) => {
148
+ const path = relative(server.config.root, toFilePath(normalizeId(i)))
149
+ return dim(' -> ') + (i === dep ? yellow(path) : path)
150
+ }).join('\n')}\n`)
151
+ }
152
+ return cachedRequest(dep, callstack)
153
+ }
80
154
 
81
- const absolute = id.startsWith('/@fs/')
82
- ? id.slice(3)
83
- : slash(join(server.config.root, id.slice(1)))
155
+ const id = normalizeId(rawId)
156
+ const absolute = toFilePath(id)
84
157
 
85
158
  debugRequest(absolute)
86
159
 
@@ -89,7 +162,7 @@ async function execute(files, server) {
89
162
  ? `/${absolute}`
90
163
  : absolute
91
164
 
92
- if (id.includes('/node_modules/'))
165
+ if (absolute.includes('/node_modules/'))
93
166
  return import(unifiedPath)
94
167
 
95
168
  const result = await server.transformRequest(id, { ssr: true })
@@ -99,42 +172,50 @@ async function execute(files, server) {
99
172
  debugTransform(id, result.code)
100
173
 
101
174
  const url = pathToFileURL(unifiedPath)
102
-
103
175
  const exports = {}
104
176
 
105
177
  const context = {
106
178
  require: createRequire(url),
107
179
  __filename: absolute,
108
180
  __dirname: dirname(absolute),
109
- __vite_ssr_import__: cachedRequest,
110
- __vite_ssr_dynamic_import__: cachedRequest,
181
+ __vite_ssr_import__: request,
182
+ __vite_ssr_dynamic_import__: request,
111
183
  __vite_ssr_exports__: exports,
112
- __vite_ssr_exportAll__: obj => Object.assign(exports, obj),
184
+ __vite_ssr_exportAll__: obj => exportAll(exports, obj),
113
185
  __vite_ssr_import_meta__: { url },
114
186
  }
115
187
 
116
- const fn = new AsyncFunction(
117
- ...Object.keys(context),
118
- result.code,
119
- )
120
-
121
- // prefetch deps
122
- result.deps.forEach(dep => cachedRequest(dep))
123
-
188
+ const fn = vm.runInThisContext(`async (${Object.keys(context).join(',')}) => { ${result.code} }`, {
189
+ filename: absolute,
190
+ lineOffset: 0,
191
+ })
124
192
  await fn(...Object.values(context))
193
+
125
194
  return exports
126
195
  }
127
196
 
128
- function cachedRequest(path) {
129
- if (!cache[path])
130
- cache[path] = request(path)
131
- return cache[path]
197
+ async function cachedRequest(id, callstack) {
198
+ if (__pendingModules__[id])
199
+ return __pendingModules__[id]
200
+ __pendingModules__[id] = directRequest(id, callstack)
201
+ return await __pendingModules__[id]
132
202
  }
133
203
 
134
- const result = []
135
- for (const file of files)
136
- result.push(await request(`/@fs/${slash(resolve(file))}`))
137
- return result
204
+ function exportAll(exports, sourceModule) {
205
+ // eslint-disable-next-line no-restricted-syntax
206
+ for (const key in sourceModule) {
207
+ if (key !== 'default') {
208
+ try {
209
+ Object.defineProperty(exports, key, {
210
+ enumerable: true,
211
+ configurable: true,
212
+ get() { return sourceModule[key] },
213
+ })
214
+ }
215
+ catch (_err) { }
216
+ }
217
+ }
218
+ }
138
219
  }
139
220
 
140
221
  function slash(path) {
@@ -142,7 +223,6 @@ function slash(path) {
142
223
  }
143
224
 
144
225
  function help() {
145
- // eslint-disable-next-line no-console
146
226
  console.log(`
147
227
  Usage:
148
228
  $ vite-node [options] [files]
@@ -150,6 +230,14 @@ Usage:
150
230
  Options:
151
231
  -r, --root <path> ${dim('[string]')} use specified root directory
152
232
  -c, --config <file> ${dim('[string]')} use specified config file
233
+ -w, --watch ${dim('[boolean]')} restart on file changes, similar to "nodemon"
234
+ -s, --silent ${dim('[boolean]')} do not emit errors and logs
153
235
  --vue ${dim('[boolean]')} support for importing Vue component
154
236
  `)
155
237
  }
238
+
239
+ function log(...args) {
240
+ if (argv.silent)
241
+ return
242
+ console.log(...args)
243
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-node",
3
- "version": "0.0.3",
3
+ "version": "0.1.1",
4
4
  "description": "Vite as Node runtime",
5
5
  "keywords": [
6
6
  "vite"
@@ -33,23 +33,23 @@
33
33
  "bin"
34
34
  ],
35
35
  "dependencies": {
36
- "debug": "^4.3.2",
36
+ "debug": "^4.3.3",
37
37
  "kolorist": "^1.5.0",
38
38
  "minimist": "^1.2.5",
39
39
  "vite": "^2.6.5"
40
40
  },
41
41
  "devDependencies": {
42
- "@antfu/eslint-config": "^0.9.0",
43
- "@antfu/ni": "^0.10.0",
42
+ "@antfu/eslint-config": "^0.11.1",
43
+ "@antfu/ni": "^0.11.0",
44
44
  "@types/debug": "^4.1.7",
45
- "@types/node": "^16.10.3",
46
- "@vitejs/plugin-vue": "^1.9.3",
45
+ "@types/node": "^16.11.11",
46
+ "@vitejs/plugin-vue": "^1.10.1",
47
47
  "bumpp": "^7.1.1",
48
- "eslint": "^7.32.0",
49
- "esno": "^0.10.1",
50
- "typescript": "^4.4.3",
48
+ "eslint": "^8.3.0",
49
+ "esno": "^0.12.1",
50
+ "typescript": "^4.5.2",
51
51
  "uvu": "^0.5.2",
52
- "vue": "^3.2.20"
52
+ "vue": "^3.2.23"
53
53
  },
54
54
  "engines": {
55
55
  "node": ">=14.0.0"