Skip to content

Commit

Permalink
feat(vite): future-proof Vite plugin to support ESM-only version of V…
Browse files Browse the repository at this point in the history
…ite (#19533)
  • Loading branch information
jaysoo authored Oct 11, 2023
1 parent 47e9fc8 commit 1899a9e
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 19 deletions.
37 changes: 37 additions & 0 deletions e2e/vite/src/vite-esm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
checkFilesExist,
newProject,
renameFile,
runCLI,
uniq,
updateJson,
} from '@nx/e2e/utils';

// TODO(jack): This test file can be removed when Vite goes ESM-only.
// This test ensures that when CJS is gone from the published `vite` package, Nx will continue to work.

describe('Vite ESM tests', () => {
beforeAll(() => newProject({ unsetProjectNameAndRootFormat: false }));

it('should build with Vite when it is ESM-only', async () => {
const appName = uniq('viteapp');
runCLI(`generate @nx/react:app ${appName} --bundler=vite`);

// .mts file is needed because Nx will transpile .ts files as CJS
renameFile(`${appName}/vite.config.ts`, `${appName}/vite.config.mts`);

// Remove CJS entry point for Vite
updateJson('node_modules/vite/package.json', (json) => {
for (const [key, value] of Object.entries(json.exports['.'])) {
if (typeof value === 'string' && value.endsWith('.cjs')) {
delete json.exports['.'][key];
}
}
return json;
});

runCLI(`build ${appName}`);

checkFilesExist(`dist/${appName}/index.html`);
});
});
Empty file.
14 changes: 6 additions & 8 deletions packages/vite/src/executors/build/build.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
stripIndents,
writeJsonFile,
} from '@nx/devkit';
import { build, InlineConfig, mergeConfig } from 'vite';
import {
getProjectTsConfigPath,
getViteBuildOptions,
Expand All @@ -30,6 +29,11 @@ export async function* viteBuildExecutor(
options: ViteBuildExecutorOptions,
context: ExecutorContext
) {
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
const { mergeConfig, build } = await (Function(
'return import("vite")'
)() as Promise<typeof import('vite')>);

const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;

Expand All @@ -52,7 +56,7 @@ export async function* viteBuildExecutor(
});
}

const watcherOrOutput = await runInstance(buildConfig);
const watcherOrOutput = await build(buildConfig);

const libraryPackageJson = resolve(projectRoot, 'package.json');
const rootPackageJson = resolve(context.root, 'package.json');
Expand Down Expand Up @@ -143,12 +147,6 @@ export async function* viteBuildExecutor(
}
}

function runInstance(options: InlineConfig) {
return build({
...options,
});
}

function normalizeOptions(options: ViteBuildExecutorOptions) {
const normalizedOptions = { ...options };

Expand Down
13 changes: 9 additions & 4 deletions packages/vite/src/executors/dev-server/dev-server.impl.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ExecutorContext } from '@nx/devkit';
import { createServer, InlineConfig, mergeConfig, ViteDevServer } from 'vite';
import type { InlineConfig, ViteDevServer } from 'vite';

import {
getViteSharedConfig,
getNxTargetOptions,
getViteServerOptions,
getViteBuildOptions,
getViteServerOptions,
getViteSharedConfig,
} from '../../utils/options-utils';

import { ViteDevServerExecutorOptions } from './schema';
Expand All @@ -16,6 +16,11 @@ export async function* viteDevServerExecutor(
options: ViteDevServerExecutorOptions,
context: ExecutorContext
): AsyncGenerator<{ success: boolean; baseUrl: string }> {
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
const { mergeConfig, createServer } = await (Function(
'return import("vite")'
)() as Promise<typeof import('vite')>);

const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;

Expand All @@ -39,7 +44,7 @@ export async function* viteDevServerExecutor(
getViteSharedConfig(mergedOptions, options.clearScreen, context),
{
build: getViteBuildOptions(mergedOptions, context),
server: getViteServerOptions(mergedOptions, context),
server: await getViteServerOptions(mergedOptions, context),
}
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ExecutorContext, parseTargetString, runExecutor } from '@nx/devkit';
import { InlineConfig, mergeConfig, preview, PreviewServer } from 'vite';
import type { InlineConfig, PreviewServer } from 'vite';
import {
getNxTargetOptions,
getViteSharedConfig,
getViteBuildOptions,
getVitePreviewOptions,
getViteSharedConfig,
} from '../../utils/options-utils';
import { ViteBuildExecutorOptions } from '../build/schema';
import { VitePreviewServerExecutorOptions } from './schema';
Expand All @@ -17,6 +17,11 @@ export async function* vitePreviewServerExecutor(
options: VitePreviewServerExecutorOptions,
context: ExecutorContext
) {
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
const { mergeConfig, preview } = await (Function(
'return import("vite")'
)() as Promise<typeof import('vite')>);

const target = parseTargetString(options.buildTarget, context);
const targetConfiguration =
context.projectsConfigurations.projects[target.project]?.targets[
Expand Down
8 changes: 6 additions & 2 deletions packages/vite/src/executors/test/vitest.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
stripIndents,
workspaceRoot,
} from '@nx/devkit';
import { CoverageOptions, File, Reporter } from 'vitest';
import { loadConfigFromFile } from 'vite';
import type { CoverageOptions, File, Reporter } from 'vitest';
import { VitestExecutorOptions } from './schema';
import { join, relative, resolve } from 'path';
import { existsSync } from 'fs';
Expand Down Expand Up @@ -100,6 +99,11 @@ async function getSettings(
context: ExecutorContext,
projectRoot: string
) {
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
const { loadConfigFromFile } = await (Function(
'return import("vite")'
)() as Promise<typeof import('vite')>);

const packageJsonPath = join(workspaceRoot, 'package.json');
const packageJson = existsSync(packageJsonPath)
? readJsonFile(packageJsonPath)
Expand Down
Empty file.
9 changes: 6 additions & 3 deletions packages/vite/src/utils/options-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
InlineConfig,
PluginOption,
PreviewOptions,
searchForWorkspaceRoot,
ServerOptions,
} from 'vite';
import { ViteDevServerExecutorOptions } from '../executors/dev-server/schema';
Expand Down Expand Up @@ -109,10 +108,14 @@ export function getViteSharedConfig(
/**
* Builds the options for the vite dev server.
*/
export function getViteServerOptions(
export async function getViteServerOptions(
options: ViteDevServerExecutorOptions,
context: ExecutorContext
): ServerOptions {
): Promise<ServerOptions> {
// Allows ESM to be required in CJS modules. Vite will be published as ESM in the future.
const { searchForWorkspaceRoot } = await (Function(
'return import("vite")'
)() as Promise<typeof import('vite')>);
const projectRoot =
context.projectsConfigurations.projects[context.projectName].root;
const serverOptions: ServerOptions = {
Expand Down

1 comment on commit 1899a9e

@vercel
Copy link

@vercel vercel bot commented on 1899a9e Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-dev-nrwl.vercel.app
nx.dev
nx-five.vercel.app

Please sign in to comment.