From f1465d84852b7930efe7dc9cefc4722742d54eec Mon Sep 17 00:00:00 2001 From: Antoine Rey Date: Tue, 31 May 2022 18:10:43 +0200 Subject: [PATCH 1/2] fix(vite-node): Prevent crash when passing single module as options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Right now, when passing a single module with the new `--options` CLI flag, `cac` returns a string instead of an array. This causes a crash, trying to call `map` on it. ``` ❯ ./packages/vite-node/vite-node.mjs --options.deps.inline="module" hello.js file:///Users/antoine/Code/vitest/packages/vite-node/dist/cli.js:697 inline: (_b = (_a = serverOptions.deps) == null ? void 0 : _a.inline) == null ? void 0 : _b.map((dep) => { ^ TypeError: _b.map is not a function at parseServerOptions (file:///Users/antoine/Code/vitest/packages/vite-node/dist/cli.js:697:99) at CAC.run (file:///Users/antoine/Code/vitest/packages/vite-node/dist/cli.js:660:49) at CAC.runMatchedCommand (file:///Users/antoine/Code/vitest/packages/vite-node/dist/cli.js:622:34) at CAC.parse (file:///Users/antoine/Code/vitest/packages/vite-node/dist/cli.js:549:12) at file:///Users/antoine/Code/vitest/packages/vite-node/dist/cli.js:652:5 at ModuleJob.run (node:internal/modules/esm/module_job:185:25) at async Promise.all (index 0) at async ESMLoader.import (node:internal/modules/esm/loader:281:24) ``` This PR updates the typings to reflect that, and makes sure that both `string` and `string[]` are properly handled. --- packages/vite-node/src/cli.ts | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/vite-node/src/cli.ts b/packages/vite-node/src/cli.ts index 05da28f7673a..d80891bbed9d 100644 --- a/packages/vite-node/src/cli.ts +++ b/packages/vite-node/src/cli.ts @@ -89,16 +89,32 @@ async function run(files: string[], options: CliOptions = {}) { } function parseServerOptions(serverOptions: ViteNodeServerOptionsCLI): ViteNodeServerOptions { + const serverOptionsDepsInline = typeof serverOptions.deps?.inline === 'string' + ? [serverOptions.deps?.inline] + : serverOptions.deps?.inline + + const serverOptionsDepsExternal = typeof serverOptions.deps?.external === 'string' + ? [serverOptions.deps?.external] + : serverOptions.deps?.external + + const serverOptionsTransformModeSsr = typeof serverOptions.transformMode?.ssr === 'string' + ? [serverOptions.transformMode?.ssr] + : serverOptions.transformMode?.ssr + + const serverOptionsTransformModeWeb = typeof serverOptions.transformMode?.web === 'string' + ? [serverOptions.transformMode?.web] + : serverOptions.transformMode?.web + return { ...serverOptions, deps: { ...serverOptions.deps, - inline: serverOptions.deps?.inline?.map((dep) => { + inline: serverOptionsDepsInline?.map((dep) => { return dep.startsWith('/') && dep.endsWith('/') ? new RegExp(dep) : dep }), - external: serverOptions.deps?.external?.map((dep) => { + external: serverOptionsDepsExternal?.map((dep) => { return dep.startsWith('/') && dep.endsWith('/') ? new RegExp(dep) : dep @@ -108,8 +124,8 @@ function parseServerOptions(serverOptions: ViteNodeServerOptionsCLI): ViteNodeSe transformMode: { ...serverOptions.transformMode, - ssr: serverOptions.transformMode?.ssr?.map(dep => new RegExp(dep)), - web: serverOptions.transformMode?.ssr?.map(dep => new RegExp(dep)), + ssr: serverOptionsTransformModeSsr?.map(dep => new RegExp(dep)), + web: serverOptionsTransformModeWeb?.map(dep => new RegExp(dep)), }, } } @@ -117,9 +133,9 @@ function parseServerOptions(serverOptions: ViteNodeServerOptionsCLI): ViteNodeSe type Optional = T | undefined type ComputeViteNodeServerOptionsCLI> = { [K in keyof T]: T[K] extends Optional - ? string[] + ? string | string[] : T[K] extends Optional<(string | RegExp)[]> - ? string[] + ? string | string[] : T[K] extends Optional> ? ComputeViteNodeServerOptionsCLI : T[K] From d218ee271fea25ca0f74201c750600468cd51d12 Mon Sep 17 00:00:00 2001 From: Antoine Rey Date: Wed, 1 Jun 2022 09:44:33 +0200 Subject: [PATCH 2/2] refactor: switch to toArray instead --- packages/vite-node/src/cli.ts | 25 +++++-------------------- packages/vite-node/src/types.ts | 3 +++ packages/vite-node/src/utils.ts | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/vite-node/src/cli.ts b/packages/vite-node/src/cli.ts index d80891bbed9d..47c47bddf7ff 100644 --- a/packages/vite-node/src/cli.ts +++ b/packages/vite-node/src/cli.ts @@ -5,6 +5,7 @@ import { version } from '../package.json' import { ViteNodeServer } from './server' import { ViteNodeRunner } from './client' import type { ViteNodeServerOptions } from './types' +import { toArray } from './utils' const cli = cac('vite-node') @@ -89,32 +90,16 @@ async function run(files: string[], options: CliOptions = {}) { } function parseServerOptions(serverOptions: ViteNodeServerOptionsCLI): ViteNodeServerOptions { - const serverOptionsDepsInline = typeof serverOptions.deps?.inline === 'string' - ? [serverOptions.deps?.inline] - : serverOptions.deps?.inline - - const serverOptionsDepsExternal = typeof serverOptions.deps?.external === 'string' - ? [serverOptions.deps?.external] - : serverOptions.deps?.external - - const serverOptionsTransformModeSsr = typeof serverOptions.transformMode?.ssr === 'string' - ? [serverOptions.transformMode?.ssr] - : serverOptions.transformMode?.ssr - - const serverOptionsTransformModeWeb = typeof serverOptions.transformMode?.web === 'string' - ? [serverOptions.transformMode?.web] - : serverOptions.transformMode?.web - return { ...serverOptions, deps: { ...serverOptions.deps, - inline: serverOptionsDepsInline?.map((dep) => { + inline: toArray(serverOptions.deps?.inline).map((dep) => { return dep.startsWith('/') && dep.endsWith('/') ? new RegExp(dep) : dep }), - external: serverOptionsDepsExternal?.map((dep) => { + external: toArray(serverOptions.deps?.external).map((dep) => { return dep.startsWith('/') && dep.endsWith('/') ? new RegExp(dep) : dep @@ -124,8 +109,8 @@ function parseServerOptions(serverOptions: ViteNodeServerOptionsCLI): ViteNodeSe transformMode: { ...serverOptions.transformMode, - ssr: serverOptionsTransformModeSsr?.map(dep => new RegExp(dep)), - web: serverOptionsTransformModeWeb?.map(dep => new RegExp(dep)), + ssr: toArray(serverOptions.transformMode?.ssr).map(dep => new RegExp(dep)), + web: toArray(serverOptions.transformMode?.web).map(dep => new RegExp(dep)), }, } } diff --git a/packages/vite-node/src/types.ts b/packages/vite-node/src/types.ts index 06666b4c2cb6..0a4defce4fe5 100644 --- a/packages/vite-node/src/types.ts +++ b/packages/vite-node/src/types.ts @@ -1,5 +1,8 @@ import type { ModuleCacheMap } from './client' +export type Nullable = T | null | undefined +export type Arrayable = T | Array + export interface DepsHandlingOptions { external?: (string | RegExp)[] inline?: (string | RegExp)[] diff --git a/packages/vite-node/src/utils.ts b/packages/vite-node/src/utils.ts index 54fbcc696121..2157a594e3d9 100644 --- a/packages/vite-node/src/utils.ts +++ b/packages/vite-node/src/utils.ts @@ -1,6 +1,7 @@ import { fileURLToPath, pathToFileURL } from 'url' import { dirname, resolve } from 'pathe' import type { TransformResult } from 'vite' +import type { Arrayable, Nullable } from './types' export const isWindows = process.platform === 'win32' @@ -74,3 +75,18 @@ export async function withInlineSourcemap(result: TransformResult) { return result } + +/** + * Convert `Arrayable` to `Array` + * + * @category Array + */ +export function toArray(array?: Nullable>): Array { + if (array === null || array === undefined) + array = [] + + if (Array.isArray(array)) + return array + + return [array] +}