From 42d9e8bcb3f078608694c3ea6992964dcbaa55db Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Fri, 10 Jan 2025 13:52:36 -0500 Subject: [PATCH] feat(web): add support for TS solution setup for @nx/web (#29583) This PR adds the new TS setup support to `@nx/web:app` generator. Previously it errored out since it was not handled. ## Current Behavior Cannot generate webapp in new setup/ ## Expected Behavior Can generate webapp in new setup ## Related Issue(s) Fixes # --- .../packages/web/generators/application.json | 18 +- e2e/web/src/file-server.test.ts | 8 +- e2e/web/src/web-vite.test.ts | 6 +- e2e/web/src/web.test.ts | 14 +- .../application/application.spec.ts | 159 +++++++++++++++ .../src/generators/application/application.ts | 183 +++++++++++++----- .../src/generators/application/schema.d.ts | 1 - .../src/generators/application/schema.json | 18 +- packages/web/src/generators/init/init.ts | 3 - 9 files changed, 320 insertions(+), 90 deletions(-) diff --git a/docs/generated/packages/web/generators/application.json b/docs/generated/packages/web/generators/application.json index 6dfd5c1c50..a21a2118ed 100644 --- a/docs/generated/packages/web/generators/application.json +++ b/docs/generated/packages/web/generators/application.json @@ -51,8 +51,8 @@ "bundler": { "type": "string", "description": "The bundler to use.", - "enum": ["webpack", "none", "vite"], - "default": "webpack", + "enum": ["vite", "webpack", "none"], + "default": "vite", "x-prompt": "Which bundler do you want to use?", "x-priority": "important" }, @@ -60,7 +60,8 @@ "description": "The tool to use for running lint checks.", "type": "string", "enum": ["eslint", "none"], - "default": "eslint" + "default": "none", + "x-prompt": "Which linter would you like to use?" }, "skipFormat": { "description": "Skip formatting files", @@ -71,8 +72,9 @@ "unitTestRunner": { "type": "string", "enum": ["vitest", "jest", "none"], - "default": "vitest", - "description": "Test runner to use for unit tests. Default value is 'jest' when using 'webpack' or 'none' as the bundler and 'vitest' when using 'vite' as the bundler" + "default": "none", + "description": "Test runner to use for unit tests. Default value is 'jest' when using 'webpack' or 'none' as the bundler and 'vitest' when using 'vite' as the bundler", + "x-prompt": "What unit test runner should be used?" }, "inSourceTests": { "type": "boolean", @@ -99,12 +101,6 @@ "type": "boolean", "description": "Creates an application with strict mode and strict type checking.", "default": true - }, - "standaloneConfig": { - "description": "Split the project configuration into `/project.json` rather than including it inside workspace.json", - "type": "boolean", - "default": true, - "x-deprecated": "Nx only supports standaloneConfig" } }, "required": ["directory"], diff --git a/e2e/web/src/file-server.test.ts b/e2e/web/src/file-server.test.ts index 5bb1a4da8d..a95c6baec6 100644 --- a/e2e/web/src/file-server.test.ts +++ b/e2e/web/src/file-server.test.ts @@ -22,7 +22,9 @@ describe('file-server', () => { const appName = uniq('app'); const port = 4301; - runCLI(`generate @nx/web:app apps/${appName} --no-interactive`); + runCLI( + `generate @nx/web:app apps/${appName} --no-interactive --bundler=webpack` + ); updateJson(join('apps', appName, 'project.json'), (config) => { config.targets['serve'] = { executor: '@nx/web:file-server', @@ -52,7 +54,9 @@ describe('file-server', () => { const appName = uniq('app'); const port = 4301; - runCLI(`generate @nx/web:app apps/${appName} --no-interactive`); + runCLI( + `generate @nx/web:app apps/${appName} --no-interactive --bundler=webpack` + ); // Used to copy index.html rather than the normal webpack build. updateFile( `apps/${appName}/copy-index.js`, diff --git a/e2e/web/src/web-vite.test.ts b/e2e/web/src/web-vite.test.ts index ebe91c0e35..8294756bd1 100644 --- a/e2e/web/src/web-vite.test.ts +++ b/e2e/web/src/web-vite.test.ts @@ -19,7 +19,7 @@ describe('Web Components Applications with bundler set as vite', () => { it('should be able to generate a web app', async () => { const appName = uniq('app'); runCLI( - `generate @nx/web:app apps/${appName} --bundler=vite --no-interactive` + `generate @nx/web:app apps/${appName} --bundler=vite --no-interactive --linter=eslint --unitTestRunner=vitest` ); const lintResults = runCLI(`lint ${appName}`); @@ -50,10 +50,10 @@ describe('Web Components Applications with bundler set as vite', () => { const libName = uniq('lib'); runCLI( - `generate @nx/web:app apps/${appName} --bundler=vite --no-interactive` + `generate @nx/web:app apps/${appName} --bundler=vite --no-interactive --linter=eslint --unitTestRunner=vitest` ); runCLI( - `generate @nx/react:lib libs/${libName} --bundler=vite --no-interactive --unitTestRunner=vitest` + `generate @nx/react:lib libs/${libName} --bundler=vite --no-interactive --unitTestRunner=vitest --linter=eslint` ); createFile(`dist/apps/${appName}/_should_remove.txt`); diff --git a/e2e/web/src/web.test.ts b/e2e/web/src/web.test.ts index f71e44e417..d7d436ee0e 100644 --- a/e2e/web/src/web.test.ts +++ b/e2e/web/src/web.test.ts @@ -26,7 +26,7 @@ describe('Web Components Applications', () => { it('should be able to generate a web app', async () => { const appName = uniq('app'); runCLI( - `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive` + `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive --unitTestRunner=vitest --linter=eslint` ); const lintResults = runCLI(`lint ${appName}`); @@ -110,7 +110,7 @@ describe('Web Components Applications', () => { it('should emit decorator metadata when --compiler=babel and it is enabled in tsconfig', async () => { const appName = uniq('app'); runCLI( - `generate @nx/web:app apps/${appName} --bundler=webpack --compiler=babel --no-interactive` + `generate @nx/web:app apps/${appName} --bundler=webpack --compiler=babel --no-interactive --unitTestRunner=vitest --linter=eslint` ); updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => { @@ -175,7 +175,7 @@ describe('Web Components Applications', () => { it('should emit decorator metadata when using --compiler=swc', async () => { const appName = uniq('app'); runCLI( - `generate @nx/web:app apps/${appName} --bundler=webpack --compiler=swc --no-interactive` + `generate @nx/web:app apps/${appName} --bundler=webpack --compiler=swc --no-interactive --unitTestRunner=vitest --linter=eslint` ); updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => { @@ -223,7 +223,7 @@ describe('Web Components Applications', () => { const appName = uniq('app1'); runCLI( - `generate @nx/web:app ${appName} --bundler=webpack --no-interactive` + `generate @nx/web:app ${appName} --bundler=webpack --no-interactive --unitTestRunner=vitest --linter=eslint` ); // check files are generated without the layout directory ("apps/") and @@ -285,7 +285,7 @@ describe('CLI - Environment Variables', () => { `; runCLI( - `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive --compiler=babel` + `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive --compiler=babel --unitTestRunner=vitest --linter=eslint` ); const content = readFile(main); @@ -310,7 +310,7 @@ describe('CLI - Environment Variables', () => { const newCode2 = `const envVars = [process.env.NODE_ENV, process.env.NX_PUBLIC_WS_BASE, process.env.NX_PUBLIC_WS_ENV_LOCAL, process.env.NX_PUBLIC_WS_LOCAL_ENV, process.env.NX_PUBLIC_APP_BASE, process.env.NX_PUBLIC_APP_ENV_LOCAL, process.env.NX_PUBLIC_APP_LOCAL_ENV, process.env.NX_PUBLIC_SHARED_ENV];`; runCLI( - `generate @nx/web:app apps/${appName2} --bundler=webpack --no-interactive --compiler=babel` + `generate @nx/web:app apps/${appName2} --bundler=webpack --no-interactive --compiler=babel --unitTestRunner=vitest --linter=eslint` ); const content2 = readFile(main2); @@ -351,7 +351,7 @@ describe('index.html interpolation', () => { const appName = uniq('app'); runCLI( - `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive` + `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive --unitTestRunner=vitest --linter=eslint` ); const srcPath = `apps/${appName}/src`; diff --git a/packages/web/src/generators/application/application.spec.ts b/packages/web/src/generators/application/application.spec.ts index 4f56ce5633..35e4a81756 100644 --- a/packages/web/src/generators/application/application.spec.ts +++ b/packages/web/src/generators/application/application.spec.ts @@ -5,7 +5,9 @@ import { readNxJson, readProjectConfiguration, Tree, + updateJson, updateNxJson, + writeJson, } from '@nx/devkit'; import { getProjects, readJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; @@ -694,4 +696,161 @@ describe('app', () => { ).toBe(false); }); }); + + describe('TS solution setup', () => { + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (json) => { + json.workspaces = ['packages/*', 'apps/*']; + return json; + }); + writeJson(tree, 'tsconfig.base.json', { + compilerOptions: { + composite: true, + declaration: true, + }, + }); + writeJson(tree, 'tsconfig.json', { + extends: './tsconfig.base.json', + files: [], + references: [], + }); + }); + + it('should add project references when using TS solution', async () => { + await applicationGenerator(tree, { + directory: 'apps/myapp', + addPlugin: true, + linter: 'none', + style: 'none', + bundler: 'vite', + unitTestRunner: 'vitest', + e2eTestRunner: 'playwright', + }); + + expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(` + [ + { + "path": "./apps/myapp-e2e", + }, + { + "path": "./apps/myapp", + }, + ] + `); + expect(readJson(tree, 'apps/myapp/tsconfig.json')).toMatchInlineSnapshot(` + { + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json", + }, + { + "path": "./tsconfig.spec.json", + }, + ], + } + `); + expect(readJson(tree, 'apps/myapp/tsconfig.app.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "out-tsc/myapp", + "rootDir": "src", + "tsBuildInfoFile": "out-tsc/myapp/tsconfig.app.tsbuildinfo", + "types": [ + "node", + ], + }, + "exclude": [ + "out-tsc", + "dist", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + ], + "extends": "../../tsconfig.base.json", + "include": [ + "src/**/*.ts", + ], + } + `); + expect(readJson(tree, 'apps/myapp/tsconfig.spec.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "./out-tsc/vitest", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest", + ], + }, + "extends": "../../tsconfig.base.json", + "include": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + ], + "references": [ + { + "path": "./tsconfig.app.json", + }, + ], + } + `); + expect(readJson(tree, 'apps/myapp-e2e/tsconfig.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "allowJs": true, + "outDir": "out-tsc/playwright", + "sourceMap": false, + }, + "exclude": [ + "out-tsc", + "test-output", + ], + "extends": "../../tsconfig.base.json", + "include": [ + "**/*.ts", + "**/*.js", + "playwright.config.ts", + "src/**/*.spec.ts", + "src/**/*.spec.js", + "src/**/*.test.ts", + "src/**/*.test.js", + "src/**/*.d.ts", + ], + } + `); + }); + }); }); diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts index 4c3a0a8bc6..6782e1a00b 100644 --- a/packages/web/src/generators/application/application.ts +++ b/packages/web/src/generators/application/application.ts @@ -13,7 +13,6 @@ import { readNxJson, readProjectConfiguration, runTasksInSerial, - TargetConfiguration, Tree, updateJson, updateNxJson, @@ -49,7 +48,12 @@ import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-com import staticServeConfiguration from '../static-serve/static-serve-configuration'; import { findPluginForConfigFile } from '@nx/devkit/src/utils/find-plugin-for-config-file'; import { E2EWebServerDetails } from '@nx/devkit/src/generators/e2e-web-server-info-utils'; -import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { + addProjectToTsSolutionWorkspace, + isUsingTsSolutionSetup, + updateTsconfigFiles, +} from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; interface NormalizedSchema extends Schema { projectName: string; @@ -57,9 +61,14 @@ interface NormalizedSchema extends Schema { e2eProjectName: string; e2eProjectRoot: string; parsedTags: string[]; + isUsingTsSolutionConfig: boolean; } function createApplicationFiles(tree: Tree, options: NormalizedSchema) { + const rootTsConfigPath = getRelativePathToRootTsConfig( + tree, + options.appProjectRoot + ); if (options.bundler === 'vite') { generateFiles( tree, @@ -70,10 +79,7 @@ function createApplicationFiles(tree: Tree, options: NormalizedSchema) { ...names(options.name), tmpl: '', offsetFromRoot: offsetFromRoot(options.appProjectRoot), - rootTsConfigPath: getRelativePathToRootTsConfig( - tree, - options.appProjectRoot - ), + rootTsConfigPath, } ); } else { @@ -86,10 +92,7 @@ function createApplicationFiles(tree: Tree, options: NormalizedSchema) { ...names(options.name), tmpl: '', offsetFromRoot: offsetFromRoot(options.appProjectRoot), - rootTsConfigPath: getRelativePathToRootTsConfig( - tree, - options.appProjectRoot - ), + rootTsConfigPath, webpackPluginOptions: hasWebpackPlugin(tree) ? { compiler: options.compiler, @@ -116,19 +119,36 @@ function createApplicationFiles(tree: Tree, options: NormalizedSchema) { ); } } - updateJson( - tree, - joinPathFragments(options.appProjectRoot, 'tsconfig.json'), - (json) => { - return { - ...json, - compilerOptions: { - ...(json.compilerOptions || {}), - strict: options.strict, - }, - }; - } - ); + if (options.isUsingTsSolutionConfig) { + updateJson( + tree, + joinPathFragments(options.appProjectRoot, 'tsconfig.json'), + () => ({ + extends: rootTsConfigPath, + files: [], + include: [], + references: [ + { + path: './tsconfig.app.json', + }, + ], + }) + ); + } else { + updateJson( + tree, + joinPathFragments(options.appProjectRoot, 'tsconfig.json'), + (json) => { + return { + ...json, + compilerOptions: { + ...(json.compilerOptions || {}), + strict: options.strict, + }, + }; + } + ); + } } async function setupBundler(tree: Tree, options: NormalizedSchema) { @@ -205,6 +225,7 @@ async function setupBundler(tree: Tree, options: NormalizedSchema) { } else if (options.bundler === 'none') { const project = readProjectConfiguration(tree, options.projectName); addBuildTargetDefaults(tree, `@nx/js:${options.compiler}`); + project.targets ??= {}; project.targets.build = { executor: `@nx/js:${options.compiler}`, outputs: ['{options.outputPath}'], @@ -221,20 +242,27 @@ async function setupBundler(tree: Tree, options: NormalizedSchema) { } async function addProject(tree: Tree, options: NormalizedSchema) { - const targets: Record = {}; - - addProjectConfiguration( - tree, - options.projectName, - { + if (options.isUsingTsSolutionConfig) { + writeJson(tree, joinPathFragments(options.appProjectRoot, 'package.json'), { + name: getImportPath(tree, options.name), + version: '0.0.1', + private: true, + nx: { + name: options.name, + projectType: 'application', + sourceRoot: `${options.appProjectRoot}/src`, + tags: options.parsedTags?.length ? options.parsedTags : undefined, + }, + }); + } else { + addProjectConfiguration(tree, options.projectName, { projectType: 'application', root: options.appProjectRoot, sourceRoot: joinPathFragments(options.appProjectRoot, 'src'), tags: options.parsedTags, - targets, - }, - options.standaloneConfig - ); + targets: {}, + }); + } } function setDefaults(tree: Tree, options: NormalizedSchema) { @@ -258,8 +286,6 @@ export async function applicationGenerator(host: Tree, schema: Schema) { } export async function applicationGeneratorInternal(host: Tree, schema: Schema) { - assertNotUsingTsSolutionSetup(host, 'web', 'application'); - const options = await normalizeOptions(host, schema); const tasks: GeneratorCallback[] = []; @@ -432,14 +458,31 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) { const { configurationGenerator } = ensurePackage< typeof import('@nx/cypress') >('@nx/cypress', nxVersion); - addProjectConfiguration(host, options.e2eProjectName, { - root: options.e2eProjectRoot, - sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'), - projectType: 'application', - targets: {}, - tags: [], - implicitDependencies: [options.projectName], - }); + if (options.isUsingTsSolutionConfig) { + writeJson( + host, + joinPathFragments(options.e2eProjectRoot, 'package.json'), + { + name: options.e2eProjectName, + version: '0.0.1', + private: true, + nx: { + projectType: 'application', + sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'), + implicitDependencies: [options.projectName], + }, + } + ); + } else { + addProjectConfiguration(host, options.e2eProjectName, { + root: options.e2eProjectRoot, + sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'), + projectType: 'application', + targets: {}, + tags: [], + implicitDependencies: [options.projectName], + }); + } const cypressTask = await configurationGenerator(host, { ...options, project: options.e2eProjectName, @@ -493,14 +536,31 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) { const { configurationGenerator: playwrightConfigGenerator } = ensurePackage< typeof import('@nx/playwright') >('@nx/playwright', nxVersion); - addProjectConfiguration(host, options.e2eProjectName, { - root: options.e2eProjectRoot, - sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'), - projectType: 'application', - targets: {}, - tags: [], - implicitDependencies: [options.projectName], - }); + if (options.isUsingTsSolutionConfig) { + writeJson( + host, + joinPathFragments(options.e2eProjectRoot, 'package.json'), + { + name: options.e2eProjectName, + version: '0.0.1', + private: true, + nx: { + projectType: 'application', + sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'), + implicitDependencies: [options.projectName], + }, + } + ); + } else { + addProjectConfiguration(host, options.e2eProjectName, { + root: options.e2eProjectRoot, + sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'), + projectType: 'application', + targets: {}, + tags: [], + implicitDependencies: [options.projectName], + }); + } const playwrightTask = await playwrightConfigGenerator(host, { project: options.e2eProjectName, skipFormat: true, @@ -592,7 +652,24 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) { ) ); - if (!schema.skipFormat) { + updateTsconfigFiles( + host, + options.appProjectRoot, + 'tsconfig.app.json', + { + module: 'esnext', + moduleResolution: 'bundler', + }, + options.linter === 'eslint' + ? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs'] + : undefined + ); + + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(host, options.appProjectRoot); + } + + if (!options.skipFormat) { await formatFiles(host); } @@ -622,6 +699,7 @@ async function normalizeOptions( const e2eProjectName = `${appProjectName}-e2e`; const e2eProjectRoot = `${appProjectRoot}-e2e`; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(host); const npmScope = getNpmScope(host); @@ -646,6 +724,7 @@ async function normalizeOptions( e2eProjectRoot, e2eProjectName, parsedTags, + isUsingTsSolutionConfig, }; } diff --git a/packages/web/src/generators/application/schema.d.ts b/packages/web/src/generators/application/schema.d.ts index 1a21df09e7..2cedac1120 100644 --- a/packages/web/src/generators/application/schema.d.ts +++ b/packages/web/src/generators/application/schema.d.ts @@ -13,7 +13,6 @@ export interface Schema { inSourceTests?: boolean; e2eTestRunner?: 'cypress' | 'playwright' | 'none'; linter?: Linter | LinterType; - standaloneConfig?: boolean; setParserOptionsProject?: boolean; strict?: boolean; addPlugin?: boolean; diff --git a/packages/web/src/generators/application/schema.json b/packages/web/src/generators/application/schema.json index 288feee9a8..327edc0633 100644 --- a/packages/web/src/generators/application/schema.json +++ b/packages/web/src/generators/application/schema.json @@ -54,8 +54,8 @@ "bundler": { "type": "string", "description": "The bundler to use.", - "enum": ["webpack", "none", "vite"], - "default": "webpack", + "enum": ["vite", "webpack", "none"], + "default": "vite", "x-prompt": "Which bundler do you want to use?", "x-priority": "important" }, @@ -63,7 +63,8 @@ "description": "The tool to use for running lint checks.", "type": "string", "enum": ["eslint", "none"], - "default": "eslint" + "default": "none", + "x-prompt": "Which linter would you like to use?" }, "skipFormat": { "description": "Skip formatting files", @@ -74,8 +75,9 @@ "unitTestRunner": { "type": "string", "enum": ["vitest", "jest", "none"], - "default": "vitest", - "description": "Test runner to use for unit tests. Default value is 'jest' when using 'webpack' or 'none' as the bundler and 'vitest' when using 'vite' as the bundler" + "default": "none", + "description": "Test runner to use for unit tests. Default value is 'jest' when using 'webpack' or 'none' as the bundler and 'vitest' when using 'vite' as the bundler", + "x-prompt": "What unit test runner should be used?" }, "inSourceTests": { "type": "boolean", @@ -102,12 +104,6 @@ "type": "boolean", "description": "Creates an application with strict mode and strict type checking.", "default": true - }, - "standaloneConfig": { - "description": "Split the project configuration into `/project.json` rather than including it inside workspace.json", - "type": "boolean", - "default": true, - "x-deprecated": "Nx only supports standaloneConfig" } }, "required": ["directory"], diff --git a/packages/web/src/generators/init/init.ts b/packages/web/src/generators/init/init.ts index bd11047504..a95eb936db 100644 --- a/packages/web/src/generators/init/init.ts +++ b/packages/web/src/generators/init/init.ts @@ -6,7 +6,6 @@ import { runTasksInSerial, Tree, } from '@nx/devkit'; -import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { nxVersion } from '../../utils/versions'; import { Schema } from './schema'; @@ -27,8 +26,6 @@ function updateDependencies(tree: Tree, schema: Schema) { } export async function webInitGenerator(tree: Tree, schema: Schema) { - assertNotUsingTsSolutionSetup(tree, 'web', 'init'); - let installTask: GeneratorCallback = () => {}; if (!schema.skipPackageJson) { installTask = updateDependencies(tree, schema);