fix(bundling): set project type correct for buildable vite projects (#26420)
<!-- 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` --> This PR: 1. fixes `@nx/vite/plugin` so that it infers the correct `projectType` 2. changes the default `package-json-workspaces` plugin such that `projectType` is not inferred is `appsDir` and `libsDir` are not set 3. Update PDV to look for the normalized `type` property on projects if `projectType` is missing (due to no longer being inferred by any plugin). ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
73c8c3343d
commit
fbd7f80bc9
@ -835,3 +835,64 @@ export const Cart = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// We're normalizing `type` from `projectType`, so if projectType is missing we'll fallback to `type`.
|
||||
// See: packages/nx/src/project-graph/utils/normalize-project-nodes.ts
|
||||
export const FallbackType = {
|
||||
args: {
|
||||
project: {
|
||||
name: 'mypkg',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: '.',
|
||||
name: 'mypkg',
|
||||
targets: {
|
||||
echo: {
|
||||
executor: 'nx:run-script',
|
||||
metadata: {
|
||||
scriptContent: 'echo 1',
|
||||
runCommand: 'npm run echo',
|
||||
},
|
||||
options: {
|
||||
script: 'echo',
|
||||
},
|
||||
configurations: {},
|
||||
},
|
||||
},
|
||||
sourceRoot: '.',
|
||||
implicitDependencies: [],
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
sourceMap: {
|
||||
root: ['nx/core/project-json', 'project.json'],
|
||||
name: ['nx/core/project-json', 'project.json'],
|
||||
targets: ['nx/core/package-json', 'project.json'],
|
||||
'targets.echo': ['nx/core/package-json-workspaces', 'package.json'],
|
||||
'targets.echo.executor': [
|
||||
'nx/core/package-json-workspaces',
|
||||
'package.json',
|
||||
],
|
||||
'targets.echo.options': [
|
||||
'nx/core/package-json-workspaces',
|
||||
'package.json',
|
||||
],
|
||||
'targets.echo.metadata': [
|
||||
'nx/core/package-json-workspaces',
|
||||
'package.json',
|
||||
],
|
||||
'targets.echo.options.script': [
|
||||
'nx/core/package-json-workspaces',
|
||||
'package.json',
|
||||
],
|
||||
'targets.echo.metadata.scriptContent': [
|
||||
'nx/core/package-json-workspaces',
|
||||
'package.json',
|
||||
],
|
||||
'targets.echo.metadata.runCommand': [
|
||||
'nx/core/package-json-workspaces',
|
||||
'package.json',
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -6,7 +6,6 @@ import type { ProjectGraphProjectNode } from '@nx/devkit';
|
||||
// nx-ignore-next-line
|
||||
import { GraphError } from 'nx/src/command-line/graph/graph';
|
||||
/* eslint-enable @nx/enforce-module-boundaries */
|
||||
|
||||
import { EyeIcon } from '@heroicons/react/24/outline';
|
||||
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||
import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text';
|
||||
@ -29,6 +28,24 @@ export interface ProjectDetailsProps {
|
||||
viewInProjectGraphPosition?: 'top' | 'bottom';
|
||||
}
|
||||
|
||||
const typeToProjectType = {
|
||||
app: 'Application',
|
||||
lib: 'Library',
|
||||
e2e: 'E2E',
|
||||
};
|
||||
|
||||
function getDisplayType(project: ProjectGraphProjectNode) {
|
||||
if (project.data.projectType) {
|
||||
return (
|
||||
project.data.projectType &&
|
||||
project.data.projectType?.charAt(0)?.toUpperCase() +
|
||||
project.data.projectType?.slice(1)
|
||||
);
|
||||
} else {
|
||||
return typeToProjectType[project.type] ?? 'Library';
|
||||
}
|
||||
}
|
||||
|
||||
export const ProjectDetails = ({
|
||||
project,
|
||||
sourceMap,
|
||||
@ -41,10 +58,7 @@ export const ProjectDetails = ({
|
||||
const projectData = project.data;
|
||||
const isCompact = variant === 'compact';
|
||||
|
||||
const displayType =
|
||||
projectData.projectType &&
|
||||
projectData.projectType?.charAt(0)?.toUpperCase() +
|
||||
projectData.projectType?.slice(1);
|
||||
const displayType = getDisplayType(project);
|
||||
|
||||
const technologies = [
|
||||
...new Set(
|
||||
|
||||
@ -59,7 +59,6 @@ describe('Workspaces', () => {
|
||||
},
|
||||
},
|
||||
"name": "my-package",
|
||||
"projectType": "library",
|
||||
"root": "packages/my-package",
|
||||
"sourceRoot": "packages/my-package",
|
||||
"targets": {
|
||||
|
||||
@ -59,7 +59,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "root",
|
||||
"projectType": "library",
|
||||
"root": ".",
|
||||
"sourceRoot": ".",
|
||||
"targets": {
|
||||
@ -98,7 +97,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "lib-a",
|
||||
"projectType": "library",
|
||||
"root": "packages/lib-a",
|
||||
"sourceRoot": "packages/lib-a",
|
||||
"targets": {
|
||||
@ -145,7 +143,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "lib-b",
|
||||
"projectType": "library",
|
||||
"root": "packages/lib-b",
|
||||
"sourceRoot": "packages/lib-b",
|
||||
"targets": {
|
||||
@ -235,7 +232,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "vite",
|
||||
"projectType": "library",
|
||||
"root": "packages/vite",
|
||||
"sourceRoot": "packages/vite",
|
||||
"targets": {
|
||||
@ -322,7 +318,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "vite",
|
||||
"projectType": "library",
|
||||
"root": "packages/vite",
|
||||
"sourceRoot": "packages/vite",
|
||||
"targets": {
|
||||
@ -405,7 +400,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "vite",
|
||||
"projectType": "library",
|
||||
"root": "packages/vite",
|
||||
"sourceRoot": "packages/vite",
|
||||
"targets": {
|
||||
@ -478,7 +472,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "root",
|
||||
"projectType": "library",
|
||||
"root": "packages/a",
|
||||
"sourceRoot": "packages/a",
|
||||
"targets": {
|
||||
@ -543,7 +536,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "root",
|
||||
"projectType": "library",
|
||||
"root": "packages/a",
|
||||
"sourceRoot": "packages/a",
|
||||
"targets": {
|
||||
@ -606,7 +598,6 @@ describe('nx package.json workspaces plugin', () => {
|
||||
},
|
||||
},
|
||||
"name": "root",
|
||||
"projectType": "library",
|
||||
"root": "packages/a",
|
||||
"sourceRoot": "packages/a",
|
||||
"targets": {
|
||||
@ -624,4 +615,91 @@ describe('nx package.json workspaces plugin', () => {
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should infer library and application project types from appsDir and libsDir', () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'nx.json': JSON.stringify({
|
||||
workspaceLayout: {
|
||||
appsDir: 'apps',
|
||||
libsDir: 'packages',
|
||||
},
|
||||
}),
|
||||
'apps/myapp/package.json': JSON.stringify({
|
||||
name: 'myapp',
|
||||
scripts: { test: 'jest' },
|
||||
}),
|
||||
'packages/mylib/package.json': JSON.stringify({
|
||||
name: 'mylib',
|
||||
scripts: { test: 'jest' },
|
||||
}),
|
||||
},
|
||||
'/root'
|
||||
);
|
||||
|
||||
expect(
|
||||
createNodeFromPackageJson('apps/myapp/package.json', '/root').projects[
|
||||
'apps/myapp'
|
||||
].projectType
|
||||
).toEqual('application');
|
||||
|
||||
expect(
|
||||
createNodeFromPackageJson('packages/mylib/package.json', '/root')
|
||||
.projects['packages/mylib'].projectType
|
||||
).toEqual('library');
|
||||
});
|
||||
|
||||
it('should infer library types for root library project if both appsDir and libsDir are set to empty string', () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'nx.json': JSON.stringify({
|
||||
workspaceLayout: {
|
||||
appsDir: '',
|
||||
libsDir: '',
|
||||
},
|
||||
}),
|
||||
'package.json': JSON.stringify({
|
||||
name: 'mylib',
|
||||
scripts: { test: 'jest' },
|
||||
}),
|
||||
},
|
||||
'/root'
|
||||
);
|
||||
|
||||
expect(
|
||||
createNodeFromPackageJson('package.json', '/root').projects['.']
|
||||
.projectType
|
||||
).toEqual('library');
|
||||
});
|
||||
|
||||
it('should infer library project type if only libsDir is set', () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'nx.json': JSON.stringify({
|
||||
workspaceLayout: {
|
||||
libsDir: 'packages',
|
||||
},
|
||||
}),
|
||||
'example/package.json': JSON.stringify({
|
||||
name: 'example',
|
||||
scripts: { test: 'jest' },
|
||||
}),
|
||||
'packages/mylib/package.json': JSON.stringify({
|
||||
name: 'mylib',
|
||||
scripts: { test: 'jest' },
|
||||
}),
|
||||
},
|
||||
'/root'
|
||||
);
|
||||
|
||||
expect(
|
||||
createNodeFromPackageJson('packages/mylib/package.json', '/root')
|
||||
.projects['packages/mylib'].projectType
|
||||
).toEqual('library');
|
||||
expect(
|
||||
createNodeFromPackageJson('example/package.json', '/root').projects[
|
||||
'example'
|
||||
].projectType
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,8 +10,8 @@ import { combineGlobPatterns } from '../../utils/globs';
|
||||
import { NX_PREFIX } from '../../utils/logger';
|
||||
import { output } from '../../utils/output';
|
||||
import {
|
||||
PackageJson,
|
||||
getMetadataFromPackageJson,
|
||||
PackageJson,
|
||||
readTargetsFromPackageJson,
|
||||
} from '../../utils/package-json';
|
||||
import { joinPathFragments } from '../../utils/path';
|
||||
@ -112,22 +112,30 @@ export function buildProjectConfigurationFromPackageJson(
|
||||
}
|
||||
|
||||
let name = packageJson.name ?? toProjectName(normalizedPath);
|
||||
const projectType =
|
||||
nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir &&
|
||||
nxJson?.workspaceLayout?.appsDir &&
|
||||
projectRoot.startsWith(nxJson.workspaceLayout.appsDir)
|
||||
? 'application'
|
||||
: 'library';
|
||||
|
||||
return {
|
||||
const projectConfiguration: ProjectConfiguration & { name: string } = {
|
||||
root: projectRoot,
|
||||
sourceRoot: projectRoot,
|
||||
name,
|
||||
projectType,
|
||||
...packageJson.nx,
|
||||
targets: readTargetsFromPackageJson(packageJson),
|
||||
metadata: getMetadataFromPackageJson(packageJson),
|
||||
};
|
||||
|
||||
if (
|
||||
nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir &&
|
||||
nxJson?.workspaceLayout?.appsDir &&
|
||||
projectRoot.startsWith(nxJson.workspaceLayout.appsDir)
|
||||
) {
|
||||
projectConfiguration.projectType = 'application';
|
||||
} else if (
|
||||
typeof nxJson?.workspaceLayout?.libsDir !== 'undefined' &&
|
||||
projectRoot.startsWith(nxJson.workspaceLayout.libsDir)
|
||||
) {
|
||||
projectConfiguration.projectType = 'library';
|
||||
}
|
||||
|
||||
return projectConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,46 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`@nx/vite/plugin Library mode should exclude serve and preview targets when vite.config.ts is in library mode 1`] = `
|
||||
[
|
||||
[
|
||||
"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",
|
||||
],
|
||||
},
|
||||
],
|
||||
"options": {
|
||||
"cwd": "my-lib",
|
||||
},
|
||||
"outputs": [
|
||||
"{workspaceRoot}/dist/{projectRoot}",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@nx/vite/plugin not root project should create nodes 1`] = `
|
||||
[
|
||||
[
|
||||
@ -8,6 +49,7 @@ exports[`@nx/vite/plugin not root project should create nodes 1`] = `
|
||||
"projects": {
|
||||
"my-app": {
|
||||
"metadata": {},
|
||||
"projectType": "application",
|
||||
"root": "my-app",
|
||||
"targets": {
|
||||
"build-something": {
|
||||
@ -67,6 +109,7 @@ exports[`@nx/vite/plugin root project should create nodes 1`] = `
|
||||
"projects": {
|
||||
".": {
|
||||
"metadata": {},
|
||||
"projectType": "application",
|
||||
"root": ".",
|
||||
"targets": {
|
||||
"build": {
|
||||
|
||||
@ -27,6 +27,7 @@ describe('@nx/vite/plugin', () => {
|
||||
describe('root project', () => {
|
||||
beforeEach(async () => {
|
||||
context = {
|
||||
configFiles: [],
|
||||
nxJsonConfiguration: {
|
||||
targetDefaults: {},
|
||||
namedInputs: {
|
||||
@ -35,7 +36,6 @@ describe('@nx/vite/plugin', () => {
|
||||
},
|
||||
},
|
||||
workspaceRoot: '',
|
||||
configFiles: [],
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -3,26 +3,12 @@ import { createNodes, createNodesV2 } from './plugin';
|
||||
|
||||
// This will only create test targets since no build targets are defined in vite.config.ts
|
||||
|
||||
jest.mock('vite', () => ({
|
||||
resolveConfig: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
path: 'vite.config.ts',
|
||||
test: {
|
||||
some: 'option',
|
||||
},
|
||||
dependencies: [],
|
||||
});
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../utils/executor-utils', () => ({
|
||||
loadViteDynamicImport: jest.fn().mockResolvedValue({
|
||||
resolveConfig: jest.fn().mockResolvedValue({
|
||||
path: 'vite.config.ts',
|
||||
test: {
|
||||
some: 'option',
|
||||
},
|
||||
dependencies: [],
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
@ -33,6 +19,7 @@ describe('@nx/vite/plugin with test node', () => {
|
||||
describe('root project', () => {
|
||||
beforeEach(async () => {
|
||||
context = {
|
||||
configFiles: [],
|
||||
nxJsonConfiguration: {
|
||||
// These defaults should be overridden by plugin
|
||||
targetDefaults: {
|
||||
@ -47,7 +34,6 @@ describe('@nx/vite/plugin with test node', () => {
|
||||
},
|
||||
},
|
||||
workspaceRoot: '',
|
||||
configFiles: [],
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -1,36 +1,25 @@
|
||||
import { CreateNodesContext } from '@nx/devkit';
|
||||
import { createNodes, createNodesV2 } from './plugin';
|
||||
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
||||
|
||||
jest.mock('vite', () => ({
|
||||
resolveConfig: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
path: 'vite.config.ts',
|
||||
config: {},
|
||||
build: {},
|
||||
dependencies: [],
|
||||
});
|
||||
}),
|
||||
}));
|
||||
import { loadViteDynamicImport } from '../utils/executor-utils';
|
||||
|
||||
jest.mock('../utils/executor-utils', () => ({
|
||||
loadViteDynamicImport: jest.fn().mockResolvedValue({
|
||||
resolveConfig: jest.fn().mockResolvedValue({
|
||||
path: 'vite.config.ts',
|
||||
config: {},
|
||||
dependencies: [],
|
||||
}),
|
||||
resolveConfig: jest.fn().mockResolvedValue({}),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('@nx/vite/plugin', () => {
|
||||
let createNodesFunction = createNodesV2[1];
|
||||
let context: CreateNodesContext;
|
||||
|
||||
describe('root project', () => {
|
||||
let tempFs;
|
||||
|
||||
beforeEach(async () => {
|
||||
tempFs = new TempFs('vite-plugin-tests');
|
||||
context = {
|
||||
configFiles: [],
|
||||
nxJsonConfiguration: {
|
||||
// These defaults should be overridden by plugin
|
||||
targetDefaults: {
|
||||
@ -45,7 +34,6 @@ describe('@nx/vite/plugin', () => {
|
||||
},
|
||||
},
|
||||
workspaceRoot: tempFs.tempDir,
|
||||
configFiles: [],
|
||||
};
|
||||
tempFs.createFileSync('vite.config.ts', '');
|
||||
tempFs.createFileSync('index.html', '');
|
||||
@ -75,9 +63,11 @@ describe('@nx/vite/plugin', () => {
|
||||
|
||||
describe('not root project', () => {
|
||||
let tempFs;
|
||||
|
||||
beforeEach(() => {
|
||||
tempFs = new TempFs('test');
|
||||
context = {
|
||||
configFiles: [],
|
||||
nxJsonConfiguration: {
|
||||
namedInputs: {
|
||||
default: ['{projectRoot}/**/*'],
|
||||
@ -85,7 +75,6 @@ describe('@nx/vite/plugin', () => {
|
||||
},
|
||||
},
|
||||
workspaceRoot: tempFs.tempDir,
|
||||
configFiles: [],
|
||||
};
|
||||
|
||||
tempFs.createFileSync(
|
||||
@ -116,4 +105,48 @@ describe('@nx/vite/plugin', () => {
|
||||
expect(nodes).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Library mode', () => {
|
||||
it('should exclude serve and preview targets when vite.config.ts is in library mode', async () => {
|
||||
const tempFs = new TempFs('test');
|
||||
(loadViteDynamicImport as jest.Mock).mockResolvedValue({
|
||||
resolveConfig: jest.fn().mockResolvedValue({
|
||||
build: {
|
||||
lib: {
|
||||
entry: 'index.ts',
|
||||
name: 'my-lib',
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
(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', '');
|
||||
|
||||
const nodes = await createNodesFunction(
|
||||
['my-lib/vite.config.ts'],
|
||||
{
|
||||
buildTargetName: 'build',
|
||||
serveTargetName: 'serve',
|
||||
},
|
||||
context
|
||||
);
|
||||
|
||||
expect(nodes).toMatchSnapshot();
|
||||
|
||||
jest.resetModules();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -113,14 +113,23 @@ async function createNodesInternal(
|
||||
);
|
||||
|
||||
const { targets, metadata } = targetsCache[hash];
|
||||
|
||||
return {
|
||||
projects: {
|
||||
[projectRoot]: {
|
||||
const project: ProjectConfiguration = {
|
||||
root: projectRoot,
|
||||
targets,
|
||||
metadata,
|
||||
},
|
||||
};
|
||||
|
||||
// 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';
|
||||
}
|
||||
|
||||
return {
|
||||
projects: {
|
||||
[projectRoot]: project,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -135,7 +144,6 @@ async function buildViteTargets(
|
||||
context.workspaceRoot,
|
||||
configFilePath
|
||||
);
|
||||
|
||||
// Workaround for the `build$3 is not a function` error that we sometimes see in agents.
|
||||
// This should be removed later once we address the issue properly
|
||||
try {
|
||||
@ -178,12 +186,13 @@ async function buildViteTargets(
|
||||
projectRoot
|
||||
);
|
||||
|
||||
// If running in library mode, then there is nothing to serve.
|
||||
if (!viteConfig.build?.lib) {
|
||||
targets[options.serveTargetName] = serveTarget(projectRoot);
|
||||
|
||||
targets[options.previewTargetName] = previewTarget(projectRoot);
|
||||
|
||||
targets[options.serveStaticTargetName] = serveStaticTarget(options) as {};
|
||||
}
|
||||
}
|
||||
|
||||
// if file is vitest.config or vite.config has definition for test, create target for test
|
||||
if (configFilePath.includes('vitest.config') || hasTest) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user