feat(vite): add angular option to vitest generator (#29055)

## Current Behavior

`@nx/vite:vitest` generator does not provide Angular support in the
`uiFramework` options.

## Expected Behavior

`angular` option should generate the vitest configuration just like
`@nx/angular:application` and `@nx/angular/library` do.
This commit is contained in:
Younes Jaaidi 2024-12-11 14:42:12 +01:00 committed by GitHub
parent 5068a26ed6
commit 77dc090a75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 552 additions and 172 deletions

View File

@ -16,8 +16,7 @@
},
"uiFramework": {
"type": "string",
"enum": ["react", "none"],
"default": "none",
"enum": ["angular", "react", "none"],
"description": "UI framework to use with vitest."
},
"inSourceTests": {

View File

@ -1,10 +1,5 @@
import {
addDependenciesToPackageJson,
ensurePackage,
joinPathFragments,
type Tree,
} from '@nx/devkit';
import { analogVitestAngular, nxVersion } from '../../utils/versions';
import { ensurePackage, type Tree } from '@nx/devkit';
import { nxVersion } from '../../utils/versions';
export type AddVitestOptions = {
name: string;
@ -17,69 +12,16 @@ export async function addVitest(
tree: Tree,
options: AddVitestOptions
): Promise<void> {
if (!options.skipPackageJson) {
addDependenciesToPackageJson(
tree,
{},
{
'@analogjs/vitest-angular': analogVitestAngular,
'@analogjs/vite-plugin-angular': analogVitestAngular,
},
undefined,
true
);
}
const { createOrEditViteConfig, vitestGenerator } = ensurePackage<
typeof import('@nx/vite')
>('@nx/vite', nxVersion);
const relativeTestSetupPath = joinPathFragments('src', 'test-setup.ts');
const setupFile = joinPathFragments(
options.projectRoot,
relativeTestSetupPath
const { vitestGenerator } = ensurePackage<typeof import('@nx/vite')>(
'@nx/vite',
nxVersion
);
if (!tree.exists(setupFile)) {
tree.write(
setupFile,
`import '@analogjs/vitest-angular/setup-zone';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
`
);
await vitestGenerator(tree, {
project: options.name,
uiFramework: 'none',
skipViteConfig: true,
testEnvironment: 'jsdom',
coverageProvider: 'v8',
addPlugin: false,
});
createOrEditViteConfig(
tree,
{
project: options.name,
includeLib: false,
includeVitest: true,
inSourceTests: false,
imports: [`import angular from '@analogjs/vite-plugin-angular'`],
plugins: ['angular()'],
setupFile: relativeTestSetupPath,
useEsmExtension: true,
},
true
);
}
await vitestGenerator(tree, {
project: options.name,
uiFramework: 'angular',
testEnvironment: 'jsdom',
coverageProvider: 'v8',
addPlugin: false,
});
}

View File

@ -14,10 +14,7 @@ export type PackageVersionNames =
export type VersionMap = {
angularV17: Record<
Exclude<
CompatPackageVersionNames,
'analogVitestAngular' | 'typescriptEslintVersion'
>,
Exclude<CompatPackageVersionNames, 'typescriptEslintVersion'>,
string
>;
angularV18: Record<CompatPackageVersionNames, string>;
@ -80,7 +77,6 @@ export const backwardCompatibleVersions: VersionMap = {
jestPresetAngularVersion: '~14.1.0',
typesNodeVersion: '18.16.9',
jasmineMarblesVersion: '^0.9.2',
analogVitestAngular: '~1.9.1',
jsoncEslintParserVersion: '^2.1.0',
},
};

View File

@ -28,6 +28,5 @@ export const tsNodeVersion = '10.9.1';
export const jestPresetAngularVersion = '~14.4.0';
export const typesNodeVersion = '18.16.9';
export const jasmineMarblesVersion = '^0.9.2';
export const analogVitestAngular = '~1.10.0';
export const jsoncEslintParserVersion = '^2.1.0';

View File

@ -1,5 +1,52 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`vitest generator angular should generate src/test-setup.ts 1`] = `
"import '@analogjs/vitest-angular/setup-zone';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
"
`;
exports[`vitest generator angular should generate vite.config.mts 1`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';
import angular from '@analogjs/vite-plugin-angular';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/my-test-angular-app',
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
setupFiles: ['src/test-setup.ts'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/apps/my-test-angular-app',
provider: 'v8',
},
},
});
"
`;
exports[`vitest generator insourceTests should add the insourceSource option in the vite config 1`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';
@ -39,6 +86,36 @@ exports[`vitest generator tsconfig should add vitest.workspace.ts at the root 1`
"
`;
exports[`vitest generator vite.config for libs should create correct vite.config.ts file for non buildable libs 1`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/libs/react-lib-nonb-jest',
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/libs/react-lib-nonb-jest',
provider: 'v8',
},
},
});
"
`;
exports[`vitest generator vite.config should create correct vite.config.ts file for apps 1`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';
@ -68,33 +145,3 @@ export default defineConfig({
});
"
`;
exports[`vitest generator vite.config should create correct vite.config.ts file for non buildable libs 1`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/libs/react-lib-nonb-jest',
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/libs/react-lib-nonb-jest',
provider: 'v8',
},
},
});
"
`;

View File

@ -1,6 +1,6 @@
export interface VitestGeneratorSchema {
project: string;
uiFramework: 'react' | 'none';
uiFramework?: 'angular' | 'react' | 'none';
coverageProvider: 'v8' | 'istanbul' | 'custom';
inSourceTests?: boolean;
skipViteConfig?: boolean;

View File

@ -15,8 +15,7 @@
},
"uiFramework": {
"type": "string",
"enum": ["react", "none"],
"default": "none",
"enum": ["angular", "react", "none"],
"description": "UI framework to use with vitest."
},
"inSourceTests": {

View File

@ -27,7 +27,11 @@ import {
} from '../../utils/versions';
import initGenerator from '../init/init';
import { VitestGeneratorSchema } from './schema';
import { detectUiFramework } from '../../utils/detect-ui-framework';
/**
* @param hasPlugin some frameworks (e.g. Nuxt) provide their own plugin. Their generators handle the plugin detection.
*/
export function vitestGenerator(
tree: Tree,
schema: VitestGeneratorSchema,
@ -56,6 +60,8 @@ export async function vitestGeneratorInternal(
schema.project
);
const projectType = schema.projectType ?? _projectType;
const uiFramework =
schema.uiFramework ?? (await detectUiFramework(schema.project));
const isRootProject = root === '.';
tasks.push(await jsInitGenerator(tree, { ...schema, skipFormat: true }));
@ -64,22 +70,49 @@ export async function vitestGeneratorInternal(
addPlugin: schema.addPlugin,
});
tasks.push(initTask);
tasks.push(ensureDependencies(tree, schema));
tasks.push(ensureDependencies(tree, { ...schema, uiFramework }));
const nxJson = readNxJson(tree);
const hasPluginCheck = nxJson.plugins?.some(
(p) =>
(typeof p === 'string'
? p === '@nx/vite/plugin'
: p.plugin === '@nx/vite/plugin') || hasPlugin
);
if (!hasPluginCheck) {
const testTarget = schema.testTarget ?? 'test';
addOrChangeTestTarget(tree, schema, testTarget);
}
addOrChangeTestTarget(tree, schema, hasPlugin);
if (!schema.skipViteConfig) {
if (schema.uiFramework === 'react') {
if (uiFramework === 'angular') {
const relativeTestSetupPath = joinPathFragments('src', 'test-setup.ts');
const setupFile = joinPathFragments(root, relativeTestSetupPath);
if (!tree.exists(setupFile)) {
tree.write(
setupFile,
`import '@analogjs/vitest-angular/setup-zone';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
`
);
}
createOrEditViteConfig(
tree,
{
project: schema.project,
includeLib: false,
includeVitest: true,
inSourceTests: false,
imports: [`import angular from '@analogjs/vite-plugin-angular'`],
plugins: ['angular()'],
setupFile: relativeTestSetupPath,
useEsmExtension: true,
},
true
);
} else if (uiFramework === 'react') {
createOrEditViteConfig(
tree,
{

View File

@ -1,29 +1,79 @@
import 'nx/src/internal-testing-utils/mock-project-graph';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Tree } from '@nx/devkit';
import generator from './vitest-generator';
import { VitestGeneratorSchema } from './schema';
import {
createProjectGraphAsync,
readJson,
Tree,
updateJson,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import {
mockAngularAppGenerator,
mockReactAppGenerator,
mockReactLibNonBuildableJestTestRunnerGenerator,
} from '../../utils/test-utils';
import { VitestGeneratorSchema } from './schema';
import generator from './vitest-generator';
describe('vitest generator', () => {
let appTree: Tree;
const options: VitestGeneratorSchema = {
project: 'my-test-react-app',
uiFramework: 'react',
coverageProvider: 'v8',
addPlugin: true,
};
describe('test target', () => {
it('should fail if test target is already defined', async () => {
const { runGenerator } = setUpAngularWorkspace();
await expect(
runGenerator({
addPlugin: false,
})
).rejects.toThrow('Target "test" already exists in the project.');
});
it('should not add test target to the project', async () => {
const { runGenerator, tree } = setUpAngularWorkspace();
updateJson(tree, 'apps/my-test-angular-app/project.json', (json) => {
delete json.targets.test;
return json;
});
await runGenerator();
expect(
readJson(tree, 'apps/my-test-angular-app/project.json').targets.test
).toBeUndefined();
});
it('should add test target to the project if plugin is not used', async () => {
const { runGenerator, tree } = setUpAngularWorkspace();
updateJson(tree, 'apps/my-test-angular-app/project.json', (json) => {
delete json.targets.test;
return json;
});
await runGenerator({
addPlugin: false,
});
expect(
readJson(tree, 'apps/my-test-angular-app/project.json').targets.test
.executor
).toBe('@nx/vite:test');
});
});
describe('tsconfig', () => {
beforeAll(async () => {
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
mockReactAppGenerator(appTree);
await generator(appTree, options);
const { runGenerator, tree } = setUpReactWorkspace();
appTree = tree;
await runGenerator();
});
it('should add vitest.workspace.ts at the root', async () => {
@ -80,18 +130,61 @@ describe('vitest generator', () => {
}
`);
});
});
describe('vite.config', () => {
beforeAll(async () => {
const { runGenerator, tree } = setUpReactWorkspace();
appTree = tree;
await runGenerator();
});
it('should add @nx/vite dependency', async () => {
const { devDependencies } = readJson(appTree, 'package.json');
expect(devDependencies['@nx/vite']).toBeDefined();
});
it('should create correct vite.config.ts file for apps', async () => {
expect(
appTree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
).toMatchSnapshot();
});
});
describe('vite.config for libs', () => {
it('should create correct vite.config.ts file for non buildable libs', async () => {
const { runGenerator, tree } = setUpReactWorkspace();
mockReactLibNonBuildableJestTestRunnerGenerator(tree);
setProjectGraphDependencies('react-lib-nonb-jest', ['npm:react']);
await runGenerator({ project: 'react-lib-nonb-jest' });
expect(
tree.read('libs/react-lib-nonb-jest/vite.config.ts', 'utf-8')
).toMatchSnapshot();
});
});
describe('insourceTests', () => {
it('should add the insourceSource option in the vite config', async () => {
const { runGenerator, tree } = setUpReactWorkspace();
await runGenerator({ inSourceTests: true });
expect(
tree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
).toMatchSnapshot();
});
it('should add vitest/importMeta when inSourceTests is true', async () => {
mockReactAppGenerator(appTree, 'my-test-react-app-2');
await generator(appTree, {
...options,
inSourceTests: true,
project: 'my-test-react-app-2',
});
const { tree, runGenerator } = setUpReactWorkspace();
await runGenerator({ inSourceTests: true });
const tsconfig = JSON.parse(
appTree
.read('apps/my-test-react-app-2/tsconfig.app.json')
?.toString() ?? '{}'
tree.read('apps/my-test-react-app/tsconfig.app.json')?.toString() ??
'{}'
);
expect(tsconfig.compilerOptions.types).toMatchInlineSnapshot(`
[
@ -101,37 +194,131 @@ describe('vitest generator', () => {
});
});
describe('vite.config', () => {
describe('angular', () => {
beforeAll(async () => {
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
mockReactAppGenerator(appTree);
await generator(appTree, options);
const { tree, runGenerator } = setUpAngularWorkspace();
appTree = tree;
await runGenerator();
});
it('should create correct vite.config.ts file for apps', async () => {
it('should generate vite.config.mts', async () => {
expect(
appTree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
appTree.read('apps/my-test-angular-app/vite.config.mts', 'utf-8')
).toMatchSnapshot();
});
it('should create correct vite.config.ts file for non buildable libs', async () => {
mockReactLibNonBuildableJestTestRunnerGenerator(appTree);
await generator(appTree, { ...options, project: 'react-lib-nonb-jest' });
expect(
appTree.read('libs/react-lib-nonb-jest/vite.config.ts', 'utf-8')
).toMatchSnapshot();
it('should not generate vite.config.ts', async () => {
expect(appTree.exists('apps/my-test-angular-app/vite.config.ts')).toBe(
false
);
});
});
describe('insourceTests', () => {
beforeAll(async () => {
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
mockReactAppGenerator(appTree);
await generator(appTree, { ...options, inSourceTests: true });
});
it('should add the insourceSource option in the vite config', async () => {
it('should generate src/test-setup.ts', async () => {
expect(
appTree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
appTree.read('apps/my-test-angular-app/src/test-setup.ts', 'utf-8')
).toMatchSnapshot();
});
it('should exclude src/test-setup.ts in tsconfig.app.json', async () => {
const tsConfig = readJson(
appTree,
'apps/my-test-angular-app/tsconfig.app.json'
);
expect(tsConfig.exclude).toContain('src/test-setup.ts');
});
it('should include src/test-setup.ts in tsconfig.spec.json', async () => {
const tsConfig = readJson(
appTree,
'apps/my-test-angular-app/tsconfig.spec.json'
);
expect(tsConfig.files).toContain('src/test-setup.ts');
});
it('should add vitest-angular', async () => {
const { devDependencies } = readJson(appTree, 'package.json');
expect(devDependencies['@analogjs/vite-plugin-angular']).toBeDefined();
expect(devDependencies['@analogjs/vitest-angular']).toBeDefined();
});
});
});
function setUpAngularWorkspace() {
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
const project = 'my-test-angular-app';
(
createProjectGraphAsync as jest.MockedFn<typeof createProjectGraphAsync>
).mockResolvedValue({
dependencies: {
[project]: [
{
type: 'static',
source: project,
target: 'npm:@angular/core',
},
],
},
nodes: {},
});
mockAngularAppGenerator(tree);
return {
async runGenerator({ addPlugin = true }: { addPlugin?: boolean } = {}) {
await generator(tree, {
project,
coverageProvider: 'v8',
addPlugin,
});
return tree;
},
tree,
};
}
function setUpReactWorkspace() {
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
const appName = 'my-test-react-app';
mockReactAppGenerator(tree);
setProjectGraphDependencies(appName, ['npm:react']);
return {
project: appName,
async runGenerator({
addPlugin = true,
inSourceTests,
project,
}: {
addPlugin?: boolean;
inSourceTests?: boolean;
project?: string;
} = {}) {
await generator(tree, {
project: project ?? appName,
coverageProvider: 'v8',
addPlugin,
inSourceTests,
});
return tree;
},
tree,
};
}
function setProjectGraphDependencies(project: string, dependencies: string[]) {
(
createProjectGraphAsync as jest.MockedFn<typeof createProjectGraphAsync>
).mockResolvedValue({
dependencies: {
[project]: dependencies.map((target) => ({
type: 'static',
source: project,
target,
})),
},
nodes: {},
});
}

View File

@ -0,0 +1,66 @@
import { createProjectGraphAsync, ProjectGraph } from '@nx/devkit';
import { detectUiFramework } from './detect-ui-framework';
jest.mock('@nx/devkit', () => ({
...jest.requireActual('@nx/devkit'),
createProjectGraphAsync: jest.fn().mockImplementation(() => {
throw new Error('createProjectGraphAsync stub is not configured');
}),
}));
describe(detectUiFramework.name, () => {
it.each([
{ framework: 'angular', dependency: '@angular/core' },
{ framework: 'angular', dependency: '@nx/angular' },
{ framework: 'react', dependency: 'react' },
{ framework: 'react', dependency: '@nx/react' },
{ framework: 'none', dependency: null },
])(
`should detect $framework when dependency "$dependency" is present`,
async ({ framework, dependency }) => {
const { projectGraphFake } = setUp();
if (dependency) {
await projectGraphFake.addNpmDependency('my-project', dependency);
}
expect(await detectUiFramework('my-project')).toBe(framework);
}
);
});
function setUp() {
const projectGraphFake = new ProjectGraphFake();
(
createProjectGraphAsync as jest.MockedFn<typeof createProjectGraphAsync>
).mockImplementation(async (options) =>
projectGraphFake.createProjectGraphAsync(options)
);
return { projectGraphFake };
}
class ProjectGraphFake {
private _graph: ProjectGraph = {
dependencies: {},
nodes: {},
};
async createProjectGraphAsync(
opts: { exitOnError: boolean; resetDaemonClient?: boolean } = {
exitOnError: false,
resetDaemonClient: false,
}
): Promise<ProjectGraph> {
return this._graph;
}
async addNpmDependency(project: string, dependency: string) {
this._graph.dependencies[project] ??= [];
this._graph.dependencies[project].push({
source: project,
type: 'static',
target: `npm:${dependency}`,
});
}
}

View File

@ -0,0 +1,32 @@
import { createProjectGraphAsync } from '@nx/devkit';
const ANGULAR_NPM_SCOPE = 'angular';
const ANGULAR_DEPS = ['@nx/angular'];
const REACT_DEPS = ['react', '@nx/react'];
export async function detectUiFramework(
project: string
): Promise<'angular' | 'react' | 'none'> {
const graph = await createProjectGraphAsync();
for (const dep of graph.dependencies[project] ?? []) {
if (dep.source !== project || !dep.target.startsWith('npm:')) {
continue;
}
const npmDependency = dep.target.replace('npm:', '');
if (
dep.target.startsWith(`npm:@${ANGULAR_NPM_SCOPE}/`) ||
ANGULAR_DEPS.includes(npmDependency)
) {
return 'angular';
}
if (REACT_DEPS.includes(npmDependency)) {
return 'react';
}
}
return 'none';
}

View File

@ -5,6 +5,7 @@ import {
type Tree,
} from '@nx/devkit';
import {
analogVitestAngular,
edgeRuntimeVmVersion,
happyDomVersion,
jsdomVersion,
@ -14,7 +15,7 @@ import {
} from './versions';
export type EnsureDependenciesOptions = {
uiFramework: 'react' | 'none';
uiFramework: 'angular' | 'react' | 'none';
compiler?: 'babel' | 'swc';
includeLib?: boolean;
testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string;
@ -38,6 +39,11 @@ export function ensureDependencies(
);
}
if (schema.uiFramework === 'angular') {
devDependencies['@analogjs/vitest-angular'] = analogVitestAngular;
devDependencies['@analogjs/vite-plugin-angular'] = analogVitestAngular;
}
if (schema.uiFramework === 'react') {
if (schema.compiler === 'swc') {
devDependencies['@vitejs/plugin-react-swc'] = vitePluginReactSwcVersion;

View File

@ -3,6 +3,7 @@ import {
logger,
offsetFromRoot,
readJson,
readNxJson,
readProjectConfiguration,
TargetConfiguration,
Tree,
@ -16,6 +17,7 @@ import { VitePreviewServerExecutorOptions } from '../executors/preview-server/sc
import { VitestExecutorOptions } from '../executors/test/schema';
import { ViteConfigurationGeneratorSchema } from '../generators/configuration/schema';
import { ensureViteConfigIsCorrect } from './vite-config-edit-utils';
import { VitestGeneratorSchema } from '../generators/vitest/schema';
export type Target = 'build' | 'serve' | 'test' | 'preview';
export type TargetFlags = Partial<Record<Target, boolean>>;
@ -81,10 +83,23 @@ export function findExistingJsBuildTargetInProject(targets: {
export function addOrChangeTestTarget(
tree: Tree,
options: ViteConfigurationGeneratorSchema,
target: string
options: VitestGeneratorSchema,
hasPlugin: boolean
) {
const nxJson = readNxJson(tree);
hasPlugin = nxJson.plugins?.some((p) =>
typeof p === 'string'
? p === '@nx/vite/plugin'
: p.plugin === '@nx/vite/plugin' || hasPlugin
);
if (hasPlugin) {
return;
}
const project = readProjectConfiguration(tree, options.project);
const target = options.testTarget ?? 'test';
const reportsDirectory = joinPathFragments(
offsetFromRoot(project.root),
@ -98,8 +113,7 @@ export function addOrChangeTestTarget(
project.targets ??= {};
if (project.targets[target]) {
project.targets[target].executor = '@nx/vite:test';
delete project.targets[target].options?.jestConfig;
throw new Error(`Target "${target}" already exists in the project.`);
} else {
project.targets[target] = {
executor: '@nx/vite:test',
@ -373,10 +387,7 @@ export function createOrEditViteConfig(
projectAlreadyHasViteTargets?: TargetFlags,
vitestFileName?: boolean
) {
const { root: projectRoot, projectType } = readProjectConfiguration(
tree,
options.project
);
const { root: projectRoot } = readProjectConfiguration(tree, options.project);
const extension = options.useEsmExtension ? 'mts' : 'ts';
const viteConfigPath = vitestFileName

View File

@ -407,6 +407,67 @@ export function mockAngularAppGenerator(tree: Tree): Tree {
projectType: 'application',
});
writeJson(tree, `apps/${appName}/tsconfig.json`, {
compilerOptions: {
target: 'es2022',
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
strict: true,
noImplicitOverride: true,
noPropertyAccessFromIndexSignature: true,
noImplicitReturns: true,
noFallthroughCasesInSwitch: true,
},
files: [],
include: [],
references: [
{
path: './tsconfig.editor.json',
},
{
path: './tsconfig.app.json',
},
{
path: './tsconfig.spec.json',
},
],
extends: '../../tsconfig.base.json',
angularCompilerOptions: {
enableI18nLegacyMessageIdFormat: false,
strictInjectionParameters: true,
strictInputAccessModifiers: true,
strictTemplates: true,
},
});
writeJson(tree, `apps/${appName}/tsconfig.app.json`, {
extends: './tsconfig.json',
compilerOptions: {
outDir: '../../dist/out-tsc',
types: [],
},
files: ['src/main.ts'],
include: ['src/**/*.d.ts'],
exclude: ['jest.config.ts', 'src/**/*.test.ts', 'src/**/*.spec.ts'],
});
writeJson(tree, `apps/${appName}/tsconfig.spec.json`, {
extends: './tsconfig.json',
compilerOptions: {
outDir: '../../dist/out-tsc',
module: 'commonjs',
target: 'es2016',
types: ['jest', 'node'],
},
files: ['src/test-setup.ts'],
include: [
'jest.config.ts',
'src/**/*.test.ts',
'src/**/*.spec.ts',
'src/**/*.d.ts',
],
});
return tree;
}

View File

@ -9,6 +9,8 @@ export const vitePluginDtsVersion = '~3.8.1';
export const happyDomVersion = '~9.20.3';
export const edgeRuntimeVmVersion = '~3.0.2';
export const analogVitestAngular = '~1.10.0';
// Coverage providers
export const vitestCoverageV8Version = '^1.0.4';
export const vitestCoverageIstanbulVersion = '^1.0.4';