feat(vite): add a preview-server executor for Vite (#14326)
This commit is contained in:
parent
00caf6ae5e
commit
c94ac41f56
@ -6000,6 +6000,14 @@
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "preview-server",
|
||||
"path": "/packages/vite/executors/preview-server",
|
||||
"name": "preview-server",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
|
||||
@ -2655,6 +2655,15 @@
|
||||
"originalFilePath": "/packages/vite/src/executors/test/schema.json",
|
||||
"path": "/packages/vite/executors/test",
|
||||
"type": "executor"
|
||||
},
|
||||
"/packages/vite/executors/preview-server": {
|
||||
"description": "Vite preview server",
|
||||
"file": "generated/packages/vite/executors/preview-server.json",
|
||||
"hidden": false,
|
||||
"name": "preview-server",
|
||||
"originalFilePath": "/packages/vite/src/executors/preview-server/schema.json",
|
||||
"path": "/packages/vite/executors/preview-server",
|
||||
"type": "executor"
|
||||
}
|
||||
},
|
||||
"generators": {
|
||||
|
||||
@ -2624,6 +2624,15 @@
|
||||
"originalFilePath": "/packages/vite/src/executors/test/schema.json",
|
||||
"path": "vite/executors/test",
|
||||
"type": "executor"
|
||||
},
|
||||
{
|
||||
"description": "Vite preview server",
|
||||
"file": "generated/packages/vite/executors/preview-server.json",
|
||||
"hidden": false,
|
||||
"name": "preview-server",
|
||||
"originalFilePath": "/packages/vite/src/executors/preview-server/schema.json",
|
||||
"path": "vite/executors/preview-server",
|
||||
"type": "executor"
|
||||
}
|
||||
],
|
||||
"generators": [
|
||||
|
||||
55
docs/generated/packages/vite/executors/preview-server.json
Normal file
55
docs/generated/packages/vite/executors/preview-server.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "preview-server",
|
||||
"implementation": "/packages/vite/src/executors/preview-server/preview-server.impl.ts",
|
||||
"schema": {
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"version": 2,
|
||||
"cli": "nx",
|
||||
"title": "Vite Preview Server",
|
||||
"description": "Preview Server for Vite.",
|
||||
"type": "object",
|
||||
"presets": [
|
||||
{ "name": "Default minimum setup", "keys": ["buildTarget"] },
|
||||
{ "name": "Using a Different Port", "keys": ["buildTarget", "port"] }
|
||||
],
|
||||
"properties": {
|
||||
"buildTarget": {
|
||||
"type": "string",
|
||||
"description": "Target which builds the application."
|
||||
},
|
||||
"proxyConfig": {
|
||||
"type": "string",
|
||||
"description": "Path to the proxy configuration file.",
|
||||
"x-completion-type": "file"
|
||||
},
|
||||
"port": { "type": "number", "description": "Port to listen on." },
|
||||
"host": {
|
||||
"description": "Specify which IP addresses the server should listen on.",
|
||||
"oneOf": [{ "type": "boolean" }, { "type": "string" }]
|
||||
},
|
||||
"https": { "type": "boolean", "description": "Serve using HTTPS." },
|
||||
"open": {
|
||||
"description": "Automatically open the app in the browser on server start. When the value is a string, it will be used as the URL's pathname.",
|
||||
"oneOf": [{ "type": "boolean" }, { "type": "string" }]
|
||||
},
|
||||
"logLevel": {
|
||||
"type": "string",
|
||||
"description": "Adjust console output verbosity.",
|
||||
"enum": ["info", "warn", "error", "silent"]
|
||||
},
|
||||
"mode": { "type": "string", "description": "Mode to run the server in." },
|
||||
"clearScreen": {
|
||||
"description": "Set to false to prevent Vite from clearing the terminal screen when logging certain messages.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"definitions": {},
|
||||
"required": ["buildTarget"],
|
||||
"examplesFile": "`project.json`:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"preview\": {\n \"executor\": \"@nrwl/vite:preview-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n```bash\nnx preview my-app\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Set up a custom port\" %}\n\nYou can always set the port in your `vite.config.ts` file. However, you can also set it directly in your `project.json` file, in the `preview` target options:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"preview\": {\n \"executor\": \"@nrwl/vite:preview-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"port\": 4200,\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% /tab %}\n{% tab label=\"Specify a proxyConfig\" %}\n\nYou can specify a proxy config by pointing to the path of your proxy configuration file:\n\n```json\n//...\n\"my-app\": {\n \"targets\": {\n //...\n \"preview\": {\n \"executor\": \"@nrwl/vite:preview-server\",\n \"defaultConfiguration\": \"development\",\n \"options\": {\n \"buildTarget\": \"my-app:build\",\n \"proxyConfig\": \"apps/my-app/proxy.conf.json\"\n },\n \"configurations\": {\n ...\n }\n },\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n"
|
||||
},
|
||||
"description": "Vite preview server",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/vite/src/executors/preview-server/schema.json",
|
||||
"type": "executor"
|
||||
}
|
||||
80
packages/vite/docs/preview-server-examples.md
Normal file
80
packages/vite/docs/preview-server-examples.md
Normal file
@ -0,0 +1,80 @@
|
||||
`project.json`:
|
||||
|
||||
```json
|
||||
//...
|
||||
"my-app": {
|
||||
"targets": {
|
||||
//...
|
||||
"preview": {
|
||||
"executor": "@nrwl/vite:preview-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "my-app:build",
|
||||
},
|
||||
"configurations": {
|
||||
...
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
nx preview my-app
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Set up a custom port" %}
|
||||
|
||||
You can always set the port in your `vite.config.ts` file. However, you can also set it directly in your `project.json` file, in the `preview` target options:
|
||||
|
||||
```json
|
||||
//...
|
||||
"my-app": {
|
||||
"targets": {
|
||||
//...
|
||||
"preview": {
|
||||
"executor": "@nrwl/vite:preview-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "my-app:build",
|
||||
"port": 4200,
|
||||
},
|
||||
"configurations": {
|
||||
...
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Specify a proxyConfig" %}
|
||||
|
||||
You can specify a proxy config by pointing to the path of your proxy configuration file:
|
||||
|
||||
```json
|
||||
//...
|
||||
"my-app": {
|
||||
"targets": {
|
||||
//...
|
||||
"preview": {
|
||||
"executor": "@nrwl/vite:preview-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "my-app:build",
|
||||
"proxyConfig": "apps/my-app/proxy.conf.json"
|
||||
},
|
||||
"configurations": {
|
||||
...
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% /tabs %}
|
||||
@ -14,6 +14,11 @@
|
||||
"implementation": "./src/executors/test/compat",
|
||||
"schema": "./src/executors/test/schema.json",
|
||||
"description": "Test with Vitest"
|
||||
},
|
||||
"preview-server": {
|
||||
"implementation": "./src/executors/preview-server/compat",
|
||||
"schema": "./src/executors/preview-server/schema.json",
|
||||
"description": "Vite preview server"
|
||||
}
|
||||
},
|
||||
"executors": {
|
||||
@ -31,6 +36,11 @@
|
||||
"implementation": "./src/executors/test/vitest.impl",
|
||||
"schema": "./src/executors/test/schema.json",
|
||||
"description": "Test with Vitest"
|
||||
},
|
||||
"preview-server": {
|
||||
"implementation": "./src/executors/preview-server/preview-server.impl",
|
||||
"schema": "./src/executors/preview-server/schema.json",
|
||||
"description": "Vite preview server"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ export default async function* viteDevServerExecutor(
|
||||
}
|
||||
|
||||
// This Promise intentionally never resolves, leaving the process running
|
||||
await new Promise<{ success: boolean }>(() => {});
|
||||
await new Promise(() => {});
|
||||
}
|
||||
|
||||
async function runViteDevServer(server: ViteDevServer): Promise<void> {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { FileReplacement } from '../../plugins/rollup-replace-files.plugin';
|
||||
export interface ViteDevServerExecutorOptions {
|
||||
buildTarget: string;
|
||||
proxyConfig?: string;
|
||||
@ -8,7 +7,7 @@ export interface ViteDevServerExecutorOptions {
|
||||
hmr?: boolean;
|
||||
open?: string | boolean;
|
||||
cors?: boolean;
|
||||
logLevel?: info | warn | error | silent;
|
||||
logLevel?: 'info' | 'warn' | 'error' | 'silent';
|
||||
mode?: string;
|
||||
clearScreen?: boolean;
|
||||
force?: boolean;
|
||||
|
||||
4
packages/vite/src/executors/preview-server/compat.ts
Normal file
4
packages/vite/src/executors/preview-server/compat.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { convertNxExecutor } from '@nrwl/devkit';
|
||||
import vitePreviewServerExecutor from './preview-server.impl';
|
||||
|
||||
export default convertNxExecutor(vitePreviewServerExecutor);
|
||||
@ -0,0 +1,88 @@
|
||||
import { ExecutorContext, parseTargetString, runExecutor } from '@nrwl/devkit';
|
||||
import { InlineConfig, mergeConfig, preview } from 'vite';
|
||||
import {
|
||||
getNxTargetOptions,
|
||||
getViteSharedConfig,
|
||||
getViteBuildOptions,
|
||||
getVitePreviewOptions,
|
||||
} from '../../utils/options-utils';
|
||||
import { ViteBuildExecutorOptions } from '../build/schema';
|
||||
import { VitePreviewServerExecutorOptions } from './schema';
|
||||
|
||||
export default async function* vitePreviewServerExecutor(
|
||||
options: VitePreviewServerExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
// Retrieve the option for the configured buildTarget.
|
||||
const buildTargetOptions: ViteBuildExecutorOptions = getNxTargetOptions(
|
||||
options.buildTarget,
|
||||
context
|
||||
);
|
||||
|
||||
// Merge the options from the build and preview-serve targets.
|
||||
// The latter takes precedence.
|
||||
const mergedOptions = {
|
||||
...buildTargetOptions,
|
||||
...options,
|
||||
};
|
||||
|
||||
// Launch the build target.
|
||||
const target = parseTargetString(options.buildTarget, context.projectGraph);
|
||||
const build = await runExecutor(target, mergedOptions, context);
|
||||
for await (const result of build) {
|
||||
if (!result.success) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Launch the server.
|
||||
const serverConfig: InlineConfig = mergeConfig(
|
||||
getViteSharedConfig(mergedOptions, options.clearScreen, context),
|
||||
{
|
||||
build: getViteBuildOptions(mergedOptions, context),
|
||||
preview: getVitePreviewOptions(mergedOptions, context),
|
||||
}
|
||||
);
|
||||
|
||||
if (serverConfig.mode === 'production') {
|
||||
console.warn('WARNING: preview is not meant to be run in production!');
|
||||
}
|
||||
|
||||
try {
|
||||
const server = await preview(serverConfig);
|
||||
server.printUrls();
|
||||
|
||||
const processOnExit = async () => {
|
||||
const { httpServer } = server;
|
||||
// closeAllConnections was added in Node v18.2.0
|
||||
httpServer.closeAllConnections && httpServer.closeAllConnections();
|
||||
httpServer.close(() => {
|
||||
process.off('SIGINT', processOnExit);
|
||||
process.off('SIGTERM', processOnExit);
|
||||
process.off('exit', processOnExit);
|
||||
});
|
||||
};
|
||||
|
||||
process.on('SIGINT', processOnExit);
|
||||
process.on('SIGTERM', processOnExit);
|
||||
process.on('exit', processOnExit);
|
||||
|
||||
const resolvedUrls = [
|
||||
...server.resolvedUrls.local,
|
||||
...server.resolvedUrls.network,
|
||||
];
|
||||
|
||||
yield {
|
||||
success: true,
|
||||
baseUrl: resolvedUrls[0] ?? '',
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
yield {
|
||||
success: false,
|
||||
baseUrl: '',
|
||||
};
|
||||
}
|
||||
|
||||
await new Promise(() => {});
|
||||
}
|
||||
11
packages/vite/src/executors/preview-server/schema.d.ts
vendored
Normal file
11
packages/vite/src/executors/preview-server/schema.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
export interface VitePreviewServerExecutorOptions {
|
||||
buildTarget: string;
|
||||
proxyConfig?: string;
|
||||
port?: number;
|
||||
host?: string | boolean;
|
||||
https?: boolean;
|
||||
open?: string | boolean;
|
||||
logLevel?: 'info' | 'warn' | 'error' | 'silent';
|
||||
mode?: string;
|
||||
clearScreen?: boolean;
|
||||
}
|
||||
75
packages/vite/src/executors/preview-server/schema.json
Normal file
75
packages/vite/src/executors/preview-server/schema.json
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema",
|
||||
"version": 2,
|
||||
"cli": "nx",
|
||||
"title": "Vite Preview Server",
|
||||
"description": "Preview Server for Vite.",
|
||||
"type": "object",
|
||||
"presets": [
|
||||
{
|
||||
"name": "Default minimum setup",
|
||||
"keys": ["buildTarget"]
|
||||
},
|
||||
{
|
||||
"name": "Using a Different Port",
|
||||
"keys": ["buildTarget", "port"]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"buildTarget": {
|
||||
"type": "string",
|
||||
"description": "Target which builds the application."
|
||||
},
|
||||
"proxyConfig": {
|
||||
"type": "string",
|
||||
"description": "Path to the proxy configuration file.",
|
||||
"x-completion-type": "file"
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to listen on."
|
||||
},
|
||||
"host": {
|
||||
"description": "Specify which IP addresses the server should listen on.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"https": {
|
||||
"type": "boolean",
|
||||
"description": "Serve using HTTPS."
|
||||
},
|
||||
"open": {
|
||||
"description": "Automatically open the app in the browser on server start. When the value is a string, it will be used as the URL's pathname.",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"logLevel": {
|
||||
"type": "string",
|
||||
"description": "Adjust console output verbosity.",
|
||||
"enum": ["info", "warn", "error", "silent"]
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"description": "Mode to run the server in."
|
||||
},
|
||||
"clearScreen": {
|
||||
"description": "Set to false to prevent Vite from clearing the terminal screen when logging certain messages.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"definitions": {},
|
||||
"required": ["buildTarget"],
|
||||
"examplesFile": "../../../docs/preview-server-examples.md"
|
||||
}
|
||||
@ -11,13 +11,57 @@ import {
|
||||
BuildOptions,
|
||||
InlineConfig,
|
||||
PluginOption,
|
||||
PreviewOptions,
|
||||
searchForWorkspaceRoot,
|
||||
ServerOptions,
|
||||
} from 'vite';
|
||||
import { ViteDevServerExecutorOptions } from '../executors/dev-server/schema';
|
||||
import { VitePreviewServerExecutorOptions } from '../executors/preview-server/schema';
|
||||
import replaceFiles from '../../plugins/rollup-replace-files.plugin';
|
||||
import { ViteBuildExecutorOptions } from '../executors/build/schema';
|
||||
|
||||
/**
|
||||
* Returns the path to the vite config file or undefined when not found.
|
||||
*/
|
||||
export function normalizeViteConfigFilePath(
|
||||
projectRoot: string,
|
||||
configFile?: string
|
||||
): string | undefined {
|
||||
return configFile && existsSync(joinPathFragments(configFile))
|
||||
? configFile
|
||||
: existsSync(joinPathFragments(`${projectRoot}/vite.config.ts`))
|
||||
? joinPathFragments(`${projectRoot}/vite.config.ts`)
|
||||
: existsSync(joinPathFragments(`${projectRoot}/vite.config.js`))
|
||||
? joinPathFragments(`${projectRoot}/vite.config.js`)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the proxy configuration file or undefined when not found.
|
||||
*/
|
||||
export function getViteServerProxyConfigPath(
|
||||
nxProxyConfig: string | undefined,
|
||||
context: ExecutorContext
|
||||
): string | undefined {
|
||||
if (nxProxyConfig) {
|
||||
const projectRoot =
|
||||
context.projectsConfigurations.projects[context.projectName].root;
|
||||
|
||||
const proxyConfigPath = nxProxyConfig
|
||||
? join(context.root, nxProxyConfig)
|
||||
: join(projectRoot, 'proxy.conf.json');
|
||||
|
||||
if (existsSync(proxyConfigPath)) {
|
||||
return proxyConfigPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the shared options for vite.
|
||||
*
|
||||
* Most shared options are derived from the build target.
|
||||
*/
|
||||
export function getViteSharedConfig(
|
||||
options: ViteBuildExecutorOptions,
|
||||
clearScreen: boolean | undefined,
|
||||
@ -38,19 +82,9 @@ export function getViteSharedConfig(
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeViteConfigFilePath(
|
||||
projectRoot: string,
|
||||
configFile?: string
|
||||
): string {
|
||||
return configFile && existsSync(joinPathFragments(configFile))
|
||||
? configFile
|
||||
: existsSync(joinPathFragments(`${projectRoot}/vite.config.ts`))
|
||||
? joinPathFragments(`${projectRoot}/vite.config.ts`)
|
||||
: existsSync(joinPathFragments(`${projectRoot}/vite.config.js`))
|
||||
? joinPathFragments(`${projectRoot}/vite.config.js`)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the options for the vite dev server.
|
||||
*/
|
||||
export function getViteServerOptions(
|
||||
options: ViteDevServerExecutorOptions,
|
||||
context: ExecutorContext
|
||||
@ -64,33 +98,29 @@ export function getViteServerOptions(
|
||||
hmr: options.hmr,
|
||||
open: options.open,
|
||||
cors: options.cors,
|
||||
};
|
||||
|
||||
if (options.proxyConfig) {
|
||||
const proxyConfigPath = options.proxyConfig
|
||||
? join(context.root, options.proxyConfig)
|
||||
: join(projectRoot, 'proxy.conf.json');
|
||||
|
||||
if (existsSync(proxyConfigPath)) {
|
||||
logger.info(`Loading proxy configuration from: ${proxyConfigPath}`);
|
||||
serverOptions.proxy = require(proxyConfigPath);
|
||||
serverOptions.fs = {
|
||||
fs: {
|
||||
allow: [
|
||||
searchForWorkspaceRoot(joinPathFragments(projectRoot)),
|
||||
joinPathFragments(context.root, 'node_modules/vite'),
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const proxyConfigPath = getViteServerProxyConfigPath(
|
||||
options.proxyConfig,
|
||||
context
|
||||
);
|
||||
if (proxyConfigPath) {
|
||||
logger.info(`Loading proxy configuration from: ${proxyConfigPath}`);
|
||||
serverOptions.proxy = require(proxyConfigPath);
|
||||
}
|
||||
|
||||
return serverOptions;
|
||||
}
|
||||
|
||||
export function getNxTargetOptions(target: string, context: ExecutorContext) {
|
||||
const targetObj = parseTargetString(target, context.projectGraph);
|
||||
return readTargetOptions(targetObj, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the build options for the vite.
|
||||
*/
|
||||
export function getViteBuildOptions(
|
||||
options: ViteBuildExecutorOptions,
|
||||
context: ExecutorContext
|
||||
@ -114,3 +144,36 @@ export function getViteBuildOptions(
|
||||
ssr: options.ssr,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the options for the vite preview server.
|
||||
*/
|
||||
export function getVitePreviewOptions(
|
||||
options: VitePreviewServerExecutorOptions,
|
||||
context: ExecutorContext
|
||||
): PreviewOptions {
|
||||
const projectRoot =
|
||||
context.projectsConfigurations.projects[context.projectName].root;
|
||||
const serverOptions: ServerOptions = {
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
https: options.https,
|
||||
open: options.open,
|
||||
};
|
||||
|
||||
const proxyConfigPath = getViteServerProxyConfigPath(
|
||||
options.proxyConfig,
|
||||
context
|
||||
);
|
||||
if (proxyConfigPath) {
|
||||
logger.info(`Loading proxy configuration from: ${proxyConfigPath}`);
|
||||
serverOptions.proxy = require(proxyConfigPath);
|
||||
}
|
||||
|
||||
return serverOptions;
|
||||
}
|
||||
|
||||
export function getNxTargetOptions(target: string, context: ExecutorContext) {
|
||||
const targetObj = parseTargetString(target, context.projectGraph);
|
||||
return readTargetOptions(targetObj, context);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user