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(() => {
jest.resetModules();
tempFs.cleanup();
});
it('should create nodes', async () => {
@ -113,6 +114,7 @@ describe('@nx/vite/plugin', () => {
afterEach(() => {
jest.resetModules();
tempFs.cleanup();
});
it('should create nodes', async () => {
@ -171,8 +173,152 @@ describe('@nx/vite/plugin', () => {
);
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))]
)) + configFilePath;
targetsCache[hash] ??= await buildViteTargets(
const { isLibrary, ...viteTargets } = await buildViteTargets(
configFilePath,
projectRoot,
normalizedOptions,
context
);
targetsCache[hash] ??= viteTargets;
const { targets, metadata } = targetsCache[hash];
const project: ProjectConfiguration = {
@ -126,9 +127,7 @@ async function createNodesInternal(
// 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 (project.targets[options.buildTargetName]) {
project.projectType = project.targets[options.serveTargetName]
? 'application'
: 'library';
project.projectType = isLibrary ? 'library' : 'application';
}
return {
@ -143,7 +142,7 @@ async function buildViteTargets(
projectRoot: string,
options: VitePluginOptions,
context: CreateNodesContext
): Promise<ViteTargets> {
): Promise<ViteTargets & { isLibrary: boolean }> {
const absoluteConfigFilePath = joinPathFragments(
context.workspaceRoot,
configFilePath
@ -157,7 +156,7 @@ async function buildViteTargets(
// do nothing
}
const { resolveConfig } = await loadViteDynamicImport();
const viteConfig = await resolveConfig(
const viteBuildConfig = await resolveConfig(
{
configFile: absoluteConfigFilePath,
mode: 'development',
@ -165,11 +164,8 @@ async function buildViteTargets(
'build'
);
const { buildOutputs, testOutputs, hasTest, isBuildable } = getOutputs(
viteConfig,
projectRoot,
context.workspaceRoot
);
const { buildOutputs, testOutputs, hasTest, isBuildable, hasServeConfig } =
getOutputs(viteBuildConfig, projectRoot, context.workspaceRoot);
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
const hasRemixPlugin =
viteConfig.plugins && viteConfig.plugins.some((p) => p.name === 'remix');
viteBuildConfig.plugins &&
viteBuildConfig.plugins.some((p) => p.name === 'remix');
if (
!configFilePath.includes('vitest.config') &&
!hasRemixPlugin &&
@ -191,7 +188,7 @@ async function buildViteTargets(
);
// 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.previewTargetName] = previewTarget(
projectRoot,
@ -211,7 +208,7 @@ async function buildViteTargets(
}
const metadata = {};
return { targets, metadata };
return { targets, metadata, isLibrary: Boolean(viteBuildConfig.build?.lib) };
}
async function buildTarget(
@ -349,7 +346,7 @@ function serveStaticTarget(options: VitePluginOptions) {
}
function getOutputs(
viteConfig: Record<string, any> | undefined,
viteBuildConfig: Record<string, any> | undefined,
projectRoot: string,
workspaceRoot: string
): {
@ -357,8 +354,9 @@ function getOutputs(
testOutputs: string[];
hasTest: boolean;
isBuildable: boolean;
hasServeConfig: boolean;
} {
const { build, test } = viteConfig;
const { build, test, server } = viteBuildConfig;
const buildOutputPath = normalizeOutputPath(
build?.outDir,
@ -372,6 +370,8 @@ function getOutputs(
build?.rollupOptions?.input ||
existsSync(join(workspaceRoot, projectRoot, 'index.html'));
const hasServeConfig = Boolean(server);
const reportsDirectoryPath = normalizeOutputPath(
test?.coverage?.reportsDirectory,
projectRoot,
@ -384,6 +384,7 @@ function getOutputs(
testOutputs: [reportsDirectoryPath],
hasTest: !!test,
isBuildable,
hasServeConfig,
};
}