fix(vite): plugin should infer serve target if server config defined #27370 (#27507)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
Vite Plugin was originally only checking if the `lib` option was defined
in a vite config, and using that to avoid inferring serve targets.


## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
Some libraries may still need a serve target, and if they have a server
config defined, the serve target should be inferred

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #27370
This commit is contained in:
Colum Ferry 2024-08-19 17:02:05 +01:00 committed by GitHub
parent 813770862e
commit 08fc13de5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 164 additions and 17 deletions

View File

@ -41,6 +41,7 @@ describe('@nx/vite/plugin', () => {
afterEach(() => { afterEach(() => {
jest.resetModules(); jest.resetModules();
tempFs.cleanup();
}); });
it('should create nodes', async () => { it('should create nodes', async () => {
@ -113,6 +114,7 @@ describe('@nx/vite/plugin', () => {
afterEach(() => { afterEach(() => {
jest.resetModules(); jest.resetModules();
tempFs.cleanup();
}); });
it('should create nodes', async () => { it('should create nodes', async () => {
@ -171,8 +173,152 @@ describe('@nx/vite/plugin', () => {
); );
expect(nodes).toMatchSnapshot(); expect(nodes).toMatchSnapshot();
tempFs.cleanup();
});
it('should not exclude serve and preview targets when vite.config.ts is in library mode when user has defined a server config', async () => {
const tempFs = new TempFs('test-exclude');
(loadViteDynamicImport as jest.Mock).mockResolvedValue({
resolveConfig: jest.fn().mockResolvedValue({
build: {
lib: {
entry: 'index.ts',
name: 'my-lib',
},
},
server: {},
}),
}),
(context = {
configFiles: [],
nxJsonConfiguration: {
namedInputs: {
default: ['{projectRoot}/**/*'],
production: ['!{projectRoot}/**/*.spec.ts'],
},
},
workspaceRoot: tempFs.tempDir,
});
tempFs.createFileSync(
'my-lib/project.json',
JSON.stringify({ name: 'my-lib' })
);
tempFs.createFileSync('my-lib/vite.config.ts', '');
jest.resetModules(); const nodes = await createNodesFunction(
['my-lib/vite.config.ts'],
{
buildTargetName: 'build',
serveTargetName: 'serve',
previewTargetName: 'preview',
},
context
);
expect(nodes).toMatchInlineSnapshot(`
[
[
"my-lib/vite.config.ts",
{
"projects": {
"my-lib": {
"metadata": {},
"projectType": "library",
"root": "my-lib",
"targets": {
"build": {
"cache": true,
"command": "vite build",
"dependsOn": [
"^build",
],
"inputs": [
"production",
"^production",
{
"externalDependencies": [
"vite",
],
},
],
"metadata": {
"description": "Run Vite build",
"help": {
"command": "npx vite build --help",
"example": {
"options": {
"manifest": "manifest.json",
"sourcemap": true,
},
},
},
"technologies": [
"vite",
],
},
"options": {
"cwd": "my-lib",
},
"outputs": [
"{workspaceRoot}/dist/{projectRoot}",
],
},
"preview": {
"command": "vite preview",
"dependsOn": [
"build",
],
"metadata": {
"description": "Locally preview Vite production build",
"help": {
"command": "npx vite preview --help",
"example": {
"options": {
"port": 3000,
},
},
},
"technologies": [
"vite",
],
},
"options": {
"cwd": "my-lib",
},
},
"serve": {
"command": "vite serve",
"metadata": {
"description": "Starts Vite dev server",
"help": {
"command": "npx vite --help",
"example": {
"options": {
"port": 3000,
},
},
},
"technologies": [
"vite",
],
},
"options": {
"cwd": "my-lib",
},
},
"serve-static": {
"executor": "@nx/web:file-server",
"options": {
"buildTarget": "build",
"spa": true,
},
},
},
},
},
},
],
]
`);
}); });
}); });
}); });

View File

@ -109,12 +109,13 @@ async function createNodesInternal(
[getLockFileName(detectPackageManager(context.workspaceRoot))] [getLockFileName(detectPackageManager(context.workspaceRoot))]
)) + configFilePath; )) + configFilePath;
targetsCache[hash] ??= await buildViteTargets( const { isLibrary, ...viteTargets } = await buildViteTargets(
configFilePath, configFilePath,
projectRoot, projectRoot,
normalizedOptions, normalizedOptions,
context context
); );
targetsCache[hash] ??= viteTargets;
const { targets, metadata } = targetsCache[hash]; const { targets, metadata } = targetsCache[hash];
const project: ProjectConfiguration = { const project: ProjectConfiguration = {
@ -126,9 +127,7 @@ async function createNodesInternal(
// If project is buildable, then the project type. // If project is buildable, then the project type.
// If it is not buildable, then leave it to other plugins/project.json to set the project type. // If it is not buildable, then leave it to other plugins/project.json to set the project type.
if (project.targets[options.buildTargetName]) { if (project.targets[options.buildTargetName]) {
project.projectType = project.targets[options.serveTargetName] project.projectType = isLibrary ? 'library' : 'application';
? 'application'
: 'library';
} }
return { return {
@ -143,7 +142,7 @@ async function buildViteTargets(
projectRoot: string, projectRoot: string,
options: VitePluginOptions, options: VitePluginOptions,
context: CreateNodesContext context: CreateNodesContext
): Promise<ViteTargets> { ): Promise<ViteTargets & { isLibrary: boolean }> {
const absoluteConfigFilePath = joinPathFragments( const absoluteConfigFilePath = joinPathFragments(
context.workspaceRoot, context.workspaceRoot,
configFilePath configFilePath
@ -157,7 +156,7 @@ async function buildViteTargets(
// do nothing // do nothing
} }
const { resolveConfig } = await loadViteDynamicImport(); const { resolveConfig } = await loadViteDynamicImport();
const viteConfig = await resolveConfig( const viteBuildConfig = await resolveConfig(
{ {
configFile: absoluteConfigFilePath, configFile: absoluteConfigFilePath,
mode: 'development', mode: 'development',
@ -165,11 +164,8 @@ async function buildViteTargets(
'build' 'build'
); );
const { buildOutputs, testOutputs, hasTest, isBuildable } = getOutputs( const { buildOutputs, testOutputs, hasTest, isBuildable, hasServeConfig } =
viteConfig, getOutputs(viteBuildConfig, projectRoot, context.workspaceRoot);
projectRoot,
context.workspaceRoot
);
const namedInputs = getNamedInputs(projectRoot, context); const namedInputs = getNamedInputs(projectRoot, context);
@ -177,7 +173,8 @@ async function buildViteTargets(
// If file is not vitest.config and buildable, create targets for build, serve, preview and serve-static // If file is not vitest.config and buildable, create targets for build, serve, preview and serve-static
const hasRemixPlugin = const hasRemixPlugin =
viteConfig.plugins && viteConfig.plugins.some((p) => p.name === 'remix'); viteBuildConfig.plugins &&
viteBuildConfig.plugins.some((p) => p.name === 'remix');
if ( if (
!configFilePath.includes('vitest.config') && !configFilePath.includes('vitest.config') &&
!hasRemixPlugin && !hasRemixPlugin &&
@ -191,7 +188,7 @@ async function buildViteTargets(
); );
// If running in library mode, then there is nothing to serve. // If running in library mode, then there is nothing to serve.
if (!viteConfig.build?.lib) { if (!viteBuildConfig.build?.lib || hasServeConfig) {
targets[options.serveTargetName] = serveTarget(projectRoot); targets[options.serveTargetName] = serveTarget(projectRoot);
targets[options.previewTargetName] = previewTarget( targets[options.previewTargetName] = previewTarget(
projectRoot, projectRoot,
@ -211,7 +208,7 @@ async function buildViteTargets(
} }
const metadata = {}; const metadata = {};
return { targets, metadata }; return { targets, metadata, isLibrary: Boolean(viteBuildConfig.build?.lib) };
} }
async function buildTarget( async function buildTarget(
@ -349,7 +346,7 @@ function serveStaticTarget(options: VitePluginOptions) {
} }
function getOutputs( function getOutputs(
viteConfig: Record<string, any> | undefined, viteBuildConfig: Record<string, any> | undefined,
projectRoot: string, projectRoot: string,
workspaceRoot: string workspaceRoot: string
): { ): {
@ -357,8 +354,9 @@ function getOutputs(
testOutputs: string[]; testOutputs: string[];
hasTest: boolean; hasTest: boolean;
isBuildable: boolean; isBuildable: boolean;
hasServeConfig: boolean;
} { } {
const { build, test } = viteConfig; const { build, test, server } = viteBuildConfig;
const buildOutputPath = normalizeOutputPath( const buildOutputPath = normalizeOutputPath(
build?.outDir, build?.outDir,
@ -372,6 +370,8 @@ function getOutputs(
build?.rollupOptions?.input || build?.rollupOptions?.input ||
existsSync(join(workspaceRoot, projectRoot, 'index.html')); existsSync(join(workspaceRoot, projectRoot, 'index.html'));
const hasServeConfig = Boolean(server);
const reportsDirectoryPath = normalizeOutputPath( const reportsDirectoryPath = normalizeOutputPath(
test?.coverage?.reportsDirectory, test?.coverage?.reportsDirectory,
projectRoot, projectRoot,
@ -384,6 +384,7 @@ function getOutputs(
testOutputs: [reportsDirectoryPath], testOutputs: [reportsDirectoryPath],
hasTest: !!test, hasTest: !!test,
isBuildable, isBuildable,
hasServeConfig,
}; };
} }