diff --git a/e2e/js/src/js-ts-solution.test.ts b/e2e/js/src/js-ts-solution.test.ts index 156a402072afe..6e6b1716a0068 100644 --- a/e2e/js/src/js-ts-solution.test.ts +++ b/e2e/js/src/js-ts-solution.test.ts @@ -114,70 +114,70 @@ ${content}` // check build expect(runCLI(`build ${esbuildParentLib}`)).toContain( - `Successfully ran target build for project ${esbuildParentLib} and 5 tasks it depends on` + `Successfully ran target build for project @proj/${esbuildParentLib} and 5 tasks it depends on` ); expect(runCLI(`build ${rollupParentLib}`)).toContain( - `Successfully ran target build for project ${rollupParentLib} and 5 tasks it depends on` + `Successfully ran target build for project @proj/${rollupParentLib} and 5 tasks it depends on` ); expect(runCLI(`build ${swcParentLib}`)).toContain( - `Successfully ran target build for project ${swcParentLib} and 5 tasks it depends on` + `Successfully ran target build for project @proj/${swcParentLib} and 5 tasks it depends on` ); expect(runCLI(`build ${tscParentLib}`)).toContain( - `Successfully ran target build for project ${tscParentLib} and 5 tasks it depends on` + `Successfully ran target build for project @proj/${tscParentLib} and 5 tasks it depends on` ); expect(runCLI(`build ${viteParentLib}`)).toContain( - `Successfully ran target build for project ${viteParentLib} and 5 tasks it depends on` + `Successfully ran target build for project @proj/${viteParentLib} and 5 tasks it depends on` ); // check typecheck expect(runCLI(`typecheck ${esbuildParentLib}`)).toContain( - `Successfully ran target typecheck for project ${esbuildParentLib} and 5 tasks it depends on` + `Successfully ran target typecheck for project @proj/${esbuildParentLib} and 5 tasks it depends on` ); expect(runCLI(`typecheck ${rollupParentLib}`)).toContain( - `Successfully ran target typecheck for project ${rollupParentLib} and 5 tasks it depends on` + `Successfully ran target typecheck for project @proj/${rollupParentLib} and 5 tasks it depends on` ); expect(runCLI(`typecheck ${swcParentLib}`)).toContain( - `Successfully ran target typecheck for project ${swcParentLib} and 5 tasks it depends on` + `Successfully ran target typecheck for project @proj/${swcParentLib} and 5 tasks it depends on` ); expect(runCLI(`typecheck ${tscParentLib}`)).toContain( - `Successfully ran target typecheck for project ${tscParentLib} and 5 tasks it depends on` + `Successfully ran target typecheck for project @proj/${tscParentLib} and 5 tasks it depends on` ); expect(runCLI(`typecheck ${viteParentLib}`)).toContain( - `Successfully ran target typecheck for project ${viteParentLib} and 5 tasks it depends on` + `Successfully ran target typecheck for project @proj/${viteParentLib} and 5 tasks it depends on` ); // check lint expect(runCLI(`lint ${esbuildParentLib}`)).toContain( - `Successfully ran target lint for project ${esbuildParentLib}` + `Successfully ran target lint for project @proj/${esbuildParentLib}` ); expect(runCLI(`lint ${rollupParentLib}`)).toContain( - `Successfully ran target lint for project ${rollupParentLib}` + `Successfully ran target lint for project @proj/${rollupParentLib}` ); expect(runCLI(`lint ${swcParentLib}`)).toContain( - `Successfully ran target lint for project ${swcParentLib}` + `Successfully ran target lint for project @proj/${swcParentLib}` ); expect(runCLI(`lint ${tscParentLib}`)).toContain( - `Successfully ran target lint for project ${tscParentLib}` + `Successfully ran target lint for project @proj/${tscParentLib}` ); expect(runCLI(`lint ${viteParentLib}`)).toContain( - `Successfully ran target lint for project ${viteParentLib}` + `Successfully ran target lint for project @proj/${viteParentLib}` ); // check test expect(runCLI(`test ${esbuildParentLib}`)).toContain( - `Successfully ran target test for project ${esbuildParentLib}` + `Successfully ran target test for project @proj/${esbuildParentLib}` ); expect(runCLI(`test ${rollupParentLib}`)).toContain( - `Successfully ran target test for project ${rollupParentLib}` + `Successfully ran target test for project @proj/${rollupParentLib}` ); expect(runCLI(`test ${swcParentLib}`)).toContain( - `Successfully ran target test for project ${swcParentLib}` + `Successfully ran target test for project @proj/${swcParentLib}` ); expect(runCLI(`test ${tscParentLib}`)).toContain( - `Successfully ran target test for project ${tscParentLib}` + `Successfully ran target test for project @proj/${tscParentLib}` ); expect(runCLI(`test ${viteParentLib}`)).toContain( - `Successfully ran target test for project ${viteParentLib}` + `Successfully ran target test for project @proj/${viteParentLib}` ); }, 300_000); }); diff --git a/e2e/vite/src/vite-ts-solution.test.ts b/e2e/vite/src/vite-ts-solution.test.ts index 9ed56e3aede6d..ae74cbde8d43b 100644 --- a/e2e/vite/src/vite-ts-solution.test.ts +++ b/e2e/vite/src/vite-ts-solution.test.ts @@ -32,13 +32,27 @@ describe('Vite - TS solution setup', () => { const viteLib = uniq('vite-lib'); const noBundlerLib = uniq('no-bundler-lib'); - runCLI(`generate @nx/react:app apps/${reactApp} --bundler=vite`); - runCLI(`generate @nx/js:lib packages/${esbuildLib} --bundler=esbuild`); - runCLI(`generate @nx/js:lib packages/${rollupLib} --bundler=rollup`); - runCLI(`generate @nx/js:lib packages/${swcLib} --bundler=swc`); - runCLI(`generate @nx/js:lib packages/${tscLib} --bundler=tsc`); - runCLI(`generate @nx/js:lib packages/${viteLib} --bundler=vite`); - runCLI(`generate @nx/js:lib packages/${noBundlerLib} --bundler=none`); + runCLI( + `generate @nx/react:app apps/${reactApp} --bundler=vite --e2eTestRunner=none` + ); + runCLI( + `generate @nx/js:lib packages/${esbuildLib} --bundler=esbuild --e2eTestRunner=none` + ); + runCLI( + `generate @nx/js:lib packages/${rollupLib} --bundler=rollup --e2eTestRunner=none` + ); + runCLI( + `generate @nx/js:lib packages/${swcLib} --bundler=swc --e2eTestRunner=none` + ); + runCLI( + `generate @nx/js:lib packages/${tscLib} --bundler=tsc --e2eTestRunner=none` + ); + runCLI( + `generate @nx/js:lib packages/${viteLib} --bundler=vite --e2eTestRunner=none` + ); + runCLI( + `generate @nx/js:lib packages/${noBundlerLib} --bundler=none --e2eTestRunner=none` + ); // import all libs from the app updateFile( @@ -87,12 +101,12 @@ ${content}` // check build expect(runCLI(`build ${reactApp}`)).toContain( - `Successfully ran target build for project ${reactApp} and 5 tasks it depends on` + `Successfully ran target build for project @proj/${reactApp} and 5 tasks it depends on` ); // check typecheck expect(runCLI(`typecheck ${reactApp}`)).toContain( - `Successfully ran target typecheck for project ${reactApp} and 6 tasks it depends on` + `Successfully ran target typecheck for project @proj/${reactApp} and 6 tasks it depends on` ); }, 300_000); }); diff --git a/e2e/vue/src/vue-ts-solution.test.ts b/e2e/vue/src/vue-ts-solution.test.ts index c4e144f02e1e4..15e133d6a163a 100644 --- a/e2e/vue/src/vue-ts-solution.test.ts +++ b/e2e/vue/src/vue-ts-solution.test.ts @@ -50,11 +50,11 @@ describe('Vue Plugin', () => { ` ); - expect(() => runCLI(`lint ${app}`)).not.toThrow(); - expect(() => runCLI(`test ${app}`)).not.toThrow(); - expect(() => runCLI(`build ${app}`)).not.toThrow(); - expect(() => runCLI(`lint ${lib}`)).not.toThrow(); - expect(() => runCLI(`test ${lib}`)).not.toThrow(); - expect(() => runCLI(`build ${lib}`)).not.toThrow(); + expect(() => runCLI(`lint @proj/${app}`)).not.toThrow(); + expect(() => runCLI(`test @proj/${app}`)).not.toThrow(); + expect(() => runCLI(`build @proj/${app}`)).not.toThrow(); + expect(() => runCLI(`lint @proj/${lib}`)).not.toThrow(); + expect(() => runCLI(`test @proj/${lib}`)).not.toThrow(); + expect(() => runCLI(`build @proj/${lib}`)).not.toThrow(); }, 300_000); }); diff --git a/packages/cypress/src/generators/component-configuration/component-configuration.ts b/packages/cypress/src/generators/component-configuration/component-configuration.ts index 0b36d8f929da2..423dce2b3d9d3 100644 --- a/packages/cypress/src/generators/component-configuration/component-configuration.ts +++ b/packages/cypress/src/generators/component-configuration/component-configuration.ts @@ -202,14 +202,13 @@ export function updateTsConfigForComponentTesting( tree: Tree, projectConfig: ProjectConfiguration ) { - const tsConfigPath = joinPathFragments( - projectConfig.root, - projectConfig.projectType === 'library' - ? 'tsconfig.lib.json' - : 'tsconfig.app.json' - ); + let tsConfigPath: string | null = null; + for (const candidate of ['tsconfig.lib.json', 'tsconfig.app.json']) { + const p = joinPathFragments(projectConfig.root, candidate); + if (tree.exists(p)) tsConfigPath = p; + } - if (tree.exists(tsConfigPath)) { + if (tsConfigPath !== null) { updateJson(tree, tsConfigPath, (json) => { const excluded = new Set([ ...(json.exclude || []), diff --git a/packages/devkit/src/generators/update-ts-configs-to-js.ts b/packages/devkit/src/generators/update-ts-configs-to-js.ts index 98b56d70fb0c3..310f592f78288 100644 --- a/packages/devkit/src/generators/update-ts-configs-to-js.ts +++ b/packages/devkit/src/generators/update-ts-configs-to-js.ts @@ -4,7 +4,7 @@ export function updateTsConfigsToJs( tree: Tree, options: { projectRoot: string } ): void { - let updateConfigPath: string; + let updateConfigPath: string | null = null; const paths = { tsConfig: `${options.projectRoot}/tsconfig.json`, @@ -12,19 +12,6 @@ export function updateTsConfigsToJs( tsConfigApp: `${options.projectRoot}/tsconfig.app.json`, }; - const getProjectType = (tree: Tree) => { - if (tree.exists(paths.tsConfigApp)) { - return 'application'; - } - if (tree.exists(paths.tsConfigLib)) { - return 'library'; - } - - throw new Error( - `project is missing tsconfig.lib.json or tsconfig.app.json` - ); - }; - updateJson(tree, paths.tsConfig, (json) => { if (json.compilerOptions) { json.compilerOptions.allowJs = true; @@ -34,25 +21,30 @@ export function updateTsConfigsToJs( return json; }); - const projectType = getProjectType(tree); - - if (projectType === 'library') { + if (tree.exists(paths.tsConfigLib)) { updateConfigPath = paths.tsConfigLib; } - if (projectType === 'application') { + + if (tree.exists(paths.tsConfigApp)) { updateConfigPath = paths.tsConfigApp; } - updateJson(tree, updateConfigPath, (json) => { - json.include = uniq([...json.include, 'src/**/*.js']); - json.exclude = uniq([ - ...json.exclude, - 'src/**/*.spec.js', - 'src/**/*.test.js', - ]); - - return json; - }); + if (updateConfigPath) { + updateJson(tree, updateConfigPath, (json) => { + json.include = uniq([...json.include, 'src/**/*.js']); + json.exclude = uniq([ + ...json.exclude, + 'src/**/*.spec.js', + 'src/**/*.test.js', + ]); + + return json; + }); + } else { + throw new Error( + `project is missing tsconfig.lib.json or tsconfig.app.json` + ); + } } const uniq = (value: T) => [...new Set(value)] as T; diff --git a/packages/eslint/src/generators/lint-project/lint-project.ts b/packages/eslint/src/generators/lint-project/lint-project.ts index 1128837a6c44c..b19cd8c7365d8 100644 --- a/packages/eslint/src/generators/lint-project/lint-project.ts +++ b/packages/eslint/src/generators/lint-project/lint-project.ts @@ -39,6 +39,7 @@ import { } from '../../utils/config-file'; import { hasEslintPlugin } from '../utils/plugin'; import { setupRootEsLint } from './setup-root-eslint'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; interface LintProjectOptions { project: string; @@ -99,7 +100,7 @@ export async function lintProjectGeneratorInternal( lintFilePatterns && lintFilePatterns.length && !lintFilePatterns.includes('{projectRoot}') && - isBuildableLibraryProject(projectConfig) + isBuildableLibraryProject(tree, projectConfig) ) { lintFilePatterns.push(`{projectRoot}/package.json`); } @@ -175,7 +176,7 @@ export async function lintProjectGeneratorInternal( // Buildable libs need source analysis enabled for linting `package.json`. if ( - isBuildableLibraryProject(projectConfig) && + isBuildableLibraryProject(tree, projectConfig) && !isJsAnalyzeSourceFilesEnabled(tree) ) { updateJson(tree, 'nx.json', (json) => { @@ -225,7 +226,7 @@ function createEsLintConfiguration( const addDependencyChecks = options.addPackageJsonDependencyChecks || - isBuildableLibraryProject(projectConfig); + isBuildableLibraryProject(tree, projectConfig); const overrides: Linter.ConfigOverride[] = useFlatConfig( tree @@ -328,10 +329,12 @@ function isJsAnalyzeSourceFilesEnabled(tree: Tree): boolean { } function isBuildableLibraryProject( + tree: Tree, projectConfig: ProjectConfiguration ): boolean { return ( - projectConfig.projectType === 'library' && + getProjectType(tree, projectConfig.root, projectConfig.projectType) === + 'library' && projectConfig.targets?.build && !!projectConfig.targets.build ); diff --git a/packages/expo/src/generators/component/component.ts b/packages/expo/src/generators/component/component.ts index c0e3f391b8250..b2a0d0fe5f91b 100644 --- a/packages/expo/src/generators/component/component.ts +++ b/packages/expo/src/generators/component/component.ts @@ -11,6 +11,7 @@ import { import { NormalizedSchema, normalizeOptions } from './lib/normalize-options'; import { addImport } from './lib/add-import'; import { dirname, join, parse, relative } from 'path'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export async function expoComponentGenerator(host: Tree, schema: Schema) { const options = await normalizeOptions(host, schema); @@ -46,8 +47,9 @@ function createComponentFiles(host: Tree, options: NormalizedSchema) { function addExportsToBarrel(host: Tree, options: NormalizedSchema) { const workspace = getProjects(host); + const proj = workspace.get(options.projectName); const isApp = - workspace.get(options.projectName).projectType === 'application'; + getProjectType(host, proj.root, proj.projectType) === 'application'; if (options.export && !isApp) { const indexFilePath = joinPathFragments( diff --git a/packages/expo/src/generators/component/lib/normalize-options.ts b/packages/expo/src/generators/component/lib/normalize-options.ts index c7268b731b05d..082d5fc37ffc7 100644 --- a/packages/expo/src/generators/component/lib/normalize-options.ts +++ b/packages/expo/src/generators/component/lib/normalize-options.ts @@ -4,6 +4,7 @@ import { type FileExtensionType, } from '@nx/devkit/src/generators/artifact-name-and-directory-utils'; import { Schema } from '../schema'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export interface NormalizedSchema extends Omit { directory: string; @@ -38,9 +39,12 @@ export async function normalizeOptions( const { className } = names(name); const project = getProjects(host).get(projectName); - const { sourceRoot: projectSourceRoot, projectType } = project; + const { root, sourceRoot: projectSourceRoot, projectType } = project; - if (options.export && projectType === 'application') { + if ( + options.export && + getProjectType(host, root, projectType) === 'application' + ) { logger.warn( `The "--export" option should not be used with applications and will do nothing.` ); diff --git a/packages/jest/src/generators/configuration/lib/create-jest-config.ts b/packages/jest/src/generators/configuration/lib/create-jest-config.ts index 344199b6c75d0..91fac54c7c613 100644 --- a/packages/jest/src/generators/configuration/lib/create-jest-config.ts +++ b/packages/jest/src/generators/configuration/lib/create-jest-config.ts @@ -13,6 +13,7 @@ import { type JestPresetExtension, } from '../../../utils/config/config-file'; import type { NormalizedJestProjectSchema } from '../schema'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export async function createJestConfig( tree: Tree, @@ -91,7 +92,13 @@ module.exports = { ...nxPreset };` } const jestProjectConfig = `jest.config.${ - rootProjectConfig.projectType === 'application' ? 'app' : 'lib' + getProjectType( + tree, + rootProjectConfig.root, + rootProjectConfig.projectType + ) === 'application' + ? 'app' + : 'lib' }.${options.js ? 'js' : 'ts'}`; tree.rename(rootJestPath, jestProjectConfig); diff --git a/packages/jest/src/generators/configuration/lib/update-tsconfig.ts b/packages/jest/src/generators/configuration/lib/update-tsconfig.ts index 42ea392a5bf67..1a32ed9758998 100644 --- a/packages/jest/src/generators/configuration/lib/update-tsconfig.ts +++ b/packages/jest/src/generators/configuration/lib/update-tsconfig.ts @@ -6,12 +6,16 @@ import { type Tree, } from '@nx/devkit'; import type { NormalizedJestProjectSchema } from '../schema'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export function updateTsConfig( host: Tree, options: NormalizedJestProjectSchema ) { - const { root, projectType } = readProjectConfiguration(host, options.project); + const { root, projectType: _projectType } = readProjectConfiguration( + host, + options.project + ); if (!host.exists(joinPathFragments(root, 'tsconfig.json'))) { throw new Error( `Expected ${joinPathFragments( @@ -31,6 +35,7 @@ export function updateTsConfig( } return json; }); + const projectType = getProjectType(host, root, _projectType); // fall-back runtime tsconfig file path in case the user didn't provide one let runtimeTsconfigPath = joinPathFragments( diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 7b33890e64a35..bc1c2ee7f25be 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -68,6 +68,7 @@ import type { NormalizedLibraryGeneratorOptions, } from './schema'; import { sortPackageJsonFields } from '../../utils/package-json/sort-fields'; +import { getImportPath } from '../../utils/get-import-path'; const defaultOutputDirectory = 'dist'; @@ -100,6 +101,12 @@ export async function libraryGeneratorInternal( ); const options = await normalizeOptions(tree, schema); + // If we are using the new TS solution + // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(tree, options.projectRoot); + } + createFiles(tree, options); await configureProject(tree, options); @@ -234,12 +241,6 @@ export async function libraryGeneratorInternal( ); } - // If we are using the new TS solution - // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(tree, options.projectRoot); - } - sortPackageJsonFields(tree, options.projectRoot); if (!options.skipFormat) { @@ -369,8 +370,24 @@ async function configureProject( delete projectConfiguration.tags; } + // We want a minimal setup, so unless targets and tags are set, just skip the `nx` property in `package.json`. + if (options.isUsingTsSolutionConfig) { + delete projectConfiguration.projectType; + // SWC executor has logic around sourceRoot and `--strip-leading-paths`. If it is not set then dist will contain the `src` folder rather than being flat. + // TODO(leo): Look at how we can remove the dependency on sourceRoot for SWC. + if (options.bundler !== 'swc') { + delete projectConfiguration.sourceRoot; + } + } + // empty targets are cleaned up automatically by `updateProjectConfiguration` - updateProjectConfiguration(tree, options.name, projectConfiguration); + updateProjectConfiguration( + tree, + options.isUsingTsSolutionConfig + ? options.importPath ?? options.name + : options.name, + projectConfiguration + ); } else if (options.config === 'workspace' || options.config === 'project') { addProjectConfiguration(tree, options.name, projectConfiguration); } else { @@ -897,7 +914,9 @@ async function normalizeOptions( return { ...options, fileName, - name: projectName, + name: isUsingTsSolutionConfig + ? getImportPath(tree, projectName) + : projectName, projectNames, projectRoot, parsedTags, diff --git a/packages/js/src/utils/typescript/ts-solution-setup.ts b/packages/js/src/utils/typescript/ts-solution-setup.ts index 2e34616d42003..ee481dd7ab396 100644 --- a/packages/js/src/utils/typescript/ts-solution-setup.ts +++ b/packages/js/src/utils/typescript/ts-solution-setup.ts @@ -244,3 +244,22 @@ export function addProjectToTsSolutionWorkspace( } } } + +export function getProjectType( + tree: Tree, + projectRoot: string, + projectType?: 'library' | 'application' +): 'library' | 'application' { + if (projectType) return projectType; + if (tree.exists(joinPathFragments(projectRoot, 'tsconfig.lib.json'))) + return 'library'; + if (tree.exists(joinPathFragments(projectRoot, 'tsconfig.app.json'))) + return 'application'; + // If there are no exports, assume it is an application since both buildable and non-buildable libraries have exports. + const packageJsonPath = joinPathFragments(projectRoot, 'package.json'); + const packageJson = tree.exists(packageJsonPath) + ? readJson(tree, joinPathFragments(projectRoot, 'package.json')) + : null; + if (!packageJson?.exports) return 'application'; + return 'library'; +} diff --git a/packages/nest/src/generators/library/lib/normalize-options.ts b/packages/nest/src/generators/library/lib/normalize-options.ts index 8b1f1673c3902..df1ad4a9a8627 100644 --- a/packages/nest/src/generators/library/lib/normalize-options.ts +++ b/packages/nest/src/generators/library/lib/normalize-options.ts @@ -7,6 +7,8 @@ import { getNpmScope } from '@nx/js/src/utils/package-json/get-npm-scope'; import type { LibraryGeneratorSchema as JsLibraryGeneratorSchema } from '@nx/js/src/generators/library/schema'; import { Linter } from '@nx/eslint'; import type { LibraryGeneratorOptions, NormalizedOptions } from '../schema'; +import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export async function normalizeOptions( tree: Tree, @@ -47,7 +49,9 @@ export async function normalizeOptions( linter: options.linter ?? Linter.EsLint, parsedTags, prefix: getNpmScope(tree), // we could also allow customizing this - projectName, + projectName: isUsingTsSolutionSetup(tree) + ? getImportPath(tree, projectName) + : projectName, projectRoot, importPath, service: options.service ?? false, diff --git a/packages/node/src/generators/library/library.spec.ts b/packages/node/src/generators/library/library.spec.ts index bfb45305807e5..3d488bc497d30 100644 --- a/packages/node/src/generators/library/library.spec.ts +++ b/packages/node/src/generators/library/library.spec.ts @@ -563,7 +563,6 @@ describe('lib', () => { "main", "types", "exports", - "nx", "dependencies", ] `); @@ -580,11 +579,6 @@ describe('lib', () => { }, "main": "./src/index.ts", "name": "@proj/mylib", - "nx": { - "name": "mylib", - "projectType": "library", - "sourceRoot": "mylib/src", - }, "private": true, "types": "./src/index.ts", "version": "0.0.1", @@ -684,9 +678,6 @@ describe('lib', () => { "module": "./dist/index.js", "name": "@proj/mylib", "nx": { - "name": "mylib", - "projectType": "library", - "sourceRoot": "mylib/src", "targets": { "build": { "executor": "@nx/js:swc", diff --git a/packages/node/src/generators/library/library.ts b/packages/node/src/generators/library/library.ts index a504368cdb7ac..e67276235cea2 100644 --- a/packages/node/src/generators/library/library.ts +++ b/packages/node/src/generators/library/library.ts @@ -54,6 +54,13 @@ export async function libraryGenerator(tree: Tree, schema: Schema) { export async function libraryGeneratorInternal(tree: Tree, schema: Schema) { const options = await normalizeOptions(tree, schema); + + // If we are using the new TS solution + // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(tree, options.projectRoot); + } + const tasks: GeneratorCallback[] = []; if (options.publishable === true && !schema.importPath) { @@ -112,12 +119,6 @@ export async function libraryGeneratorInternal(tree: Tree, schema: Schema) { tasks.push(() => installPackagesTask(tree, true)); } - // If we are using the new TS solution - // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(tree, options.projectRoot); - } - sortPackageJsonFields(tree, options.projectRoot); if (!schema.skipFormat) { @@ -163,14 +164,17 @@ async function normalizeOptions( ? options.tags.split(',').map((s) => s.trim()) : []; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(tree); return { ...options, fileName, - projectName, + projectName: isUsingTsSolutionConfig + ? getImportPath(tree, projectName) + : projectName, projectRoot, parsedTags, importPath, - isUsingTsSolutionConfig: isUsingTsSolutionSetup(tree), + isUsingTsSolutionConfig, }; } diff --git a/packages/plugin/src/generators/plugin/plugin.spec.ts b/packages/plugin/src/generators/plugin/plugin.spec.ts index 89972b46c6e71..fc0d6436336c3 100644 --- a/packages/plugin/src/generators/plugin/plugin.spec.ts +++ b/packages/plugin/src/generators/plugin/plugin.spec.ts @@ -330,6 +330,7 @@ describe('NxPlugin Plugin Generator', () => { describe('TS solution setup', () => { beforeEach(() => { tree = createTreeWithEmptyWorkspace(); + tree.write('.gitignore', ''); updateJson(tree, 'package.json', (json) => { json.workspaces = ['packages/*']; return json; @@ -371,7 +372,7 @@ describe('NxPlugin Plugin Generator', () => { swcJestConfig.swcrc = false; export default { - displayName: 'my-plugin', + displayName: '@proj/my-plugin', preset: '../jest.preset.js', testEnvironment: 'node', transform: { @@ -412,11 +413,119 @@ describe('NxPlugin Plugin Generator', () => { const projectTargets = readProjectConfiguration( tree, - 'my-plugin' + '@proj/my-plugin' ).targets; expect(projectTargets.test).toBeDefined(); expect(projectTargets.test?.executor).toEqual('@nx/jest:jest'); }); + + it('should add project references when using TS solution', async () => { + await pluginGenerator( + tree, + getSchema({ + e2eTestRunner: 'jest', + }) + ); + + expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(` + [ + { + "path": "./my-plugin", + }, + { + "path": "./my-plugin-e2e", + }, + ] + `); + expect(readJson(tree, 'my-plugin/package.json')).toMatchInlineSnapshot(` + { + "dependencies": { + "tslib": "^2.3.0", + }, + "exports": { + ".": { + "default": "./dist/index.js", + "import": "./dist/index.js", + "types": "./dist/index.d.ts", + }, + "./package.json": "./package.json", + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "name": "@proj/my-plugin", + "types": "./dist/index.d.ts", + "version": "0.0.1", + } + `); + expect(readJson(tree, 'my-plugin/tsconfig.json')).toMatchInlineSnapshot(` + { + "extends": "../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + { + "path": "./tsconfig.spec.json", + }, + ], + } + `); + expect(readJson(tree, 'my-plugin/tsconfig.lib.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "baseUrl": ".", + "emitDeclarationOnly": false, + "module": "nodenext", + "moduleResolution": "nodenext", + "outDir": "dist", + "rootDir": "src", + "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", + "types": [ + "node", + ], + }, + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + ], + "extends": "../tsconfig.base.json", + "include": [ + "src/**/*.ts", + ], + "references": [], + } + `); + expect(readJson(tree, 'my-plugin/tsconfig.spec.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext", + "outDir": "./out-tsc/jest", + "types": [ + "jest", + "node", + ], + }, + "extends": "../tsconfig.base.json", + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts", + ], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + ], + } + `); + }); }); }); diff --git a/packages/plugin/src/generators/plugin/plugin.ts b/packages/plugin/src/generators/plugin/plugin.ts index 978f53cae37e5..aaa819f4e44c7 100644 --- a/packages/plugin/src/generators/plugin/plugin.ts +++ b/packages/plugin/src/generators/plugin/plugin.ts @@ -41,7 +41,7 @@ async function addFiles(host: Tree, options: NormalizedSchema) { } function updatePluginConfig(host: Tree, options: NormalizedSchema) { - const project = readProjectConfiguration(host, options.name); + const project = readProjectConfiguration(host, options.projectName); if (project.targets.build) { if (options.isTsSolutionSetup && options.bundler === 'tsc') { @@ -69,7 +69,7 @@ function updatePluginConfig(host: Tree, options: NormalizedSchema) { ); } - updateProjectConfiguration(host, options.name, project); + updateProjectConfiguration(host, options.projectName, project); } } @@ -144,7 +144,7 @@ export async function pluginGeneratorInternal(host: Tree, schema: Schema) { if (options.e2eTestRunner !== 'none') { tasks.push( await e2eProjectGenerator(host, { - pluginName: options.name, + pluginName: options.projectName, projectDirectory: options.projectDirectory, pluginOutputPath: joinPathFragments( 'dist', @@ -161,7 +161,7 @@ export async function pluginGeneratorInternal(host: Tree, schema: Schema) { } if (options.linter === Linter.EsLint && !options.skipLintChecks) { - await pluginLintCheckGenerator(host, { projectName: options.name }); + await pluginLintCheckGenerator(host, { projectName: options.projectName }); } if (!options.skipFormat) { diff --git a/packages/plugin/src/generators/plugin/utils/normalize-schema.ts b/packages/plugin/src/generators/plugin/utils/normalize-schema.ts index 8cc585b253056..b27d4519b6a8b 100644 --- a/packages/plugin/src/generators/plugin/utils/normalize-schema.ts +++ b/packages/plugin/src/generators/plugin/utils/normalize-schema.ts @@ -10,9 +10,11 @@ import { } from '@nx/js/src/utils/generator-prompts'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; import type { Schema } from '../schema'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export interface NormalizedSchema extends Schema { name: string; + projectName: string; fileName: string; projectRoot: string; projectDirectory: string; @@ -71,6 +73,9 @@ export async function normalizeOptions( bundler: options.compiler ?? 'tsc', fileName: projectName, name: projectName, + projectName: isTsSolutionSetup + ? getImportPath(host, projectName) + : projectName, projectRoot, projectDirectory, parsedTags, diff --git a/packages/react-native/src/generators/application/application.ts b/packages/react-native/src/generators/application/application.ts index 127f079f18903..41e02447094cd 100644 --- a/packages/react-native/src/generators/application/application.ts +++ b/packages/react-native/src/generators/application/application.ts @@ -82,13 +82,14 @@ export async function reactNativeApplicationGeneratorInternal( options.appProjectRoot, options.js, options.skipPackageJson, - options.addPlugin + options.addPlugin, + 'tsconfig.app.json' ); tasks.push(jestTask); const webTask = await webConfigurationGenerator(host, { ...options, - project: options.name, + project: options.projectName, skipFormat: true, }); tasks.push(webTask); diff --git a/packages/react-native/src/generators/application/lib/add-project.ts b/packages/react-native/src/generators/application/lib/add-project.ts index b61a98b34c980..3ffb114836e2f 100644 --- a/packages/react-native/src/generators/application/lib/add-project.ts +++ b/packages/react-native/src/generators/application/lib/add-project.ts @@ -29,13 +29,10 @@ export function addProject(host: Tree, options: NormalizedSchema) { if (isUsingTsSolutionSetup(host)) { writeJson(host, joinPathFragments(options.appProjectRoot, 'package.json'), { - name: getImportPath(host, options.name), + name: options.projectName, version: '0.0.1', private: true, nx: { - name: options.name, - projectType: 'application', - sourceRoot: `${options.appProjectRoot}/src`, targets: hasPlugin ? {} : getTargets(options), tags: options.parsedTags?.length ? options.parsedTags : undefined, }, diff --git a/packages/react-native/src/generators/application/lib/normalize-options.ts b/packages/react-native/src/generators/application/lib/normalize-options.ts index dfcde96a76a3b..d032572866162 100644 --- a/packages/react-native/src/generators/application/lib/normalize-options.ts +++ b/packages/react-native/src/generators/application/lib/normalize-options.ts @@ -5,6 +5,7 @@ import { } from '@nx/devkit/src/generators/project-name-and-root-utils'; import { Schema } from '../schema'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export interface NormalizedSchema extends Schema { className: string; // app name in class case @@ -56,6 +57,8 @@ export async function normalizeOptions( const entryFile = options.js ? 'src/main.js' : 'src/main.tsx'; + const isTsSolutionSetup = isUsingTsSolutionSetup(host); + return { ...options, name: projectNames.projectSimpleName, @@ -63,7 +66,9 @@ export async function normalizeOptions( fileName, lowerCaseName: className.toLowerCase(), displayName: options.displayName || className, - projectName: appProjectName, + projectName: isTsSolutionSetup + ? getImportPath(host, appProjectName) + : appProjectName, appProjectRoot, iosProjectRoot, androidProjectRoot, @@ -72,6 +77,6 @@ export async function normalizeOptions( rootProject, e2eProjectName, e2eProjectRoot, - isTsSolutionSetup: isUsingTsSolutionSetup(host), + isTsSolutionSetup, }; } diff --git a/packages/react-native/src/generators/component/component.ts b/packages/react-native/src/generators/component/component.ts index 8cae0a4e7942b..966729626937d 100644 --- a/packages/react-native/src/generators/component/component.ts +++ b/packages/react-native/src/generators/component/component.ts @@ -11,6 +11,7 @@ import { NormalizedSchema, normalizeOptions } from './lib/normalize-options'; import { addImport } from './lib/add-import'; import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript'; import { dirname, join, parse, relative } from 'path'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export async function reactNativeComponentGenerator( host: Tree, @@ -52,8 +53,9 @@ function addExportsToBarrel(host: Tree, options: NormalizedSchema) { tsModule = ensureTypescript(); } const workspace = getProjects(host); + const proj = workspace.get(options.projectName); const isApp = - workspace.get(options.projectName).projectType === 'application'; + getProjectType(host, proj.root, proj.projectType) === 'application'; if (options.export && !isApp) { const indexFilePath = joinPathFragments( diff --git a/packages/react-native/src/generators/component/lib/normalize-options.ts b/packages/react-native/src/generators/component/lib/normalize-options.ts index 9aa857e88033e..0185c2d95a0bc 100644 --- a/packages/react-native/src/generators/component/lib/normalize-options.ts +++ b/packages/react-native/src/generators/component/lib/normalize-options.ts @@ -4,6 +4,7 @@ import { type FileExtensionType, } from '@nx/devkit/src/generators/artifact-name-and-directory-utils'; import { Schema } from '../schema'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export interface NormalizedSchema extends Omit { directory: string; @@ -40,9 +41,12 @@ export async function normalizeOptions( const { className } = names(name); - const { sourceRoot: projectSourceRoot, projectType } = project; + const { root, sourceRoot: projectSourceRoot, projectType } = project; - if (options.export && projectType === 'application') { + if ( + options.export && + getProjectType(host, root, projectType) === 'application' + ) { logger.warn( `The "--export" option should not be used with applications and will do nothing.` ); diff --git a/packages/react-native/src/generators/library/lib/normalize-options.ts b/packages/react-native/src/generators/library/lib/normalize-options.ts index b98a0c2a932ac..3bc82180a9ce9 100644 --- a/packages/react-native/src/generators/library/lib/normalize-options.ts +++ b/packages/react-native/src/generators/library/lib/normalize-options.ts @@ -5,6 +5,7 @@ import { } from '@nx/devkit/src/generators/project-name-and-root-utils'; import { Schema } from '../schema'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export interface NormalizedSchema extends Schema { name: string; @@ -44,15 +45,18 @@ export async function normalizeOptions( ? options.tags.split(',').map((s) => s.trim()) : []; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(host); const normalized: NormalizedSchema = { ...options, fileName: projectName, routePath: `/${projectNames.projectSimpleName}`, - name: projectName, + name: isUsingTsSolutionConfig + ? getImportPath(host, projectName) + : projectName, projectRoot, parsedTags, importPath, - isUsingTsSolutionConfig: isUsingTsSolutionSetup(host), + isUsingTsSolutionConfig, }; return normalized; diff --git a/packages/react-native/src/generators/library/library.ts b/packages/react-native/src/generators/library/library.ts index 62d50dee2d24f..d6244850604f0 100644 --- a/packages/react-native/src/generators/library/library.ts +++ b/packages/react-native/src/generators/library/library.ts @@ -101,7 +101,8 @@ export async function reactNativeLibraryGeneratorInternal( options.projectRoot, options.js, options.skipPackageJson, - options.addPlugin + options.addPlugin, + 'tsconfig.lib.json' ); tasks.push(jestTask); @@ -171,14 +172,11 @@ async function addProject( 'package.json' ); if (options.isUsingTsSolutionConfig) { - writeJson(host, packageJsonPath, { - name: getImportPath(host, options.name), + writeJson(host, joinPathFragments(options.projectRoot, 'package.json'), { + name: options.name, version: '0.0.1', ...determineEntryFields(options), nx: { - name: options.name, - sourceRoot: joinPathFragments(options.projectRoot, 'src'), - projectType: 'library', tags: options.parsedTags?.length ? options.parsedTags : undefined, }, }); diff --git a/packages/react-native/src/utils/add-jest.ts b/packages/react-native/src/utils/add-jest.ts index 23a5437662257..e2b72663037bf 100644 --- a/packages/react-native/src/utils/add-jest.ts +++ b/packages/react-native/src/utils/add-jest.ts @@ -8,7 +8,8 @@ export async function addJest( appProjectRoot: string, js: boolean, skipPackageJson: boolean, - addPlugin: boolean + addPlugin: boolean, + runtimeTsconfigFileName: string ) { if (unitTestRunner !== 'jest') { return () => {}; @@ -24,6 +25,7 @@ export async function addJest( skipPackageJson, skipFormat: true, addPlugin, + runtimeTsconfigFileName, }); // overwrite the jest.config.ts file because react native needs to have special transform property diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 33d60bc8388bc..01177ceaffc36 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -1331,7 +1331,6 @@ describe('app', () => { "name", "version", "private", - "nx", ] `); expect(readJson(appTree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(` diff --git a/packages/react/src/generators/application/application.ts b/packages/react/src/generators/application/application.ts index 694aeb9b732eb..bc882e80d76be 100644 --- a/packages/react/src/generators/application/application.ts +++ b/packages/react/src/generators/application/application.ts @@ -72,6 +72,13 @@ export async function applicationGeneratorInternal( tasks.push(jsInitTask); const options = await normalizeOptions(tree, schema); + + // If we are using the new TS solution + // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); + } + showPossibleWarnings(tree, options); const initTask = await reactInitGenerator(tree, { @@ -179,12 +186,6 @@ export async function applicationGeneratorInternal( : undefined ); - // If we are using the new TS solution - // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); - } - sortPackageJsonFields(tree, options.appProjectRoot); if (!options.skipFormat) { diff --git a/packages/react/src/generators/application/lib/add-project.ts b/packages/react/src/generators/application/lib/add-project.ts index a4cee10a39ac8..8dfa4e2ceff4e 100644 --- a/packages/react/src/generators/application/lib/add-project.ts +++ b/packages/react/src/generators/application/lib/add-project.ts @@ -40,15 +40,14 @@ export function addProject(host: Tree, options: NormalizedSchema) { if (options.isUsingTsSolutionConfig) { writeJson(host, joinPathFragments(options.appProjectRoot, 'package.json'), { - name: getImportPath(host, options.name), + name: options.projectName, version: '0.0.1', private: true, - nx: { - name: options.name, - projectType: 'application', - sourceRoot: `${options.appProjectRoot}/src`, - tags: options.parsedTags?.length ? options.parsedTags : undefined, - }, + nx: options.parsedTags?.length + ? { + tags: options.parsedTags, + } + : undefined, }); } diff --git a/packages/react/src/generators/application/lib/normalize-options.ts b/packages/react/src/generators/application/lib/normalize-options.ts index 37768a6a0d36e..f9cc72b8fe3e9 100644 --- a/packages/react/src/generators/application/lib/normalize-options.ts +++ b/packages/react/src/generators/application/lib/normalize-options.ts @@ -7,6 +7,7 @@ import { assertValidStyle } from '../../../utils/assertion'; import { NormalizedSchema, Schema } from '../schema'; import { findFreePort } from './find-free-port'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export function normalizeDirectory(options: Schema) { options.directory = options.directory?.replace(/\\{1,2}/g, '/'); @@ -57,10 +58,13 @@ export async function normalizeOptions( assertValidStyle(options.style); + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(host); const normalized = { ...options, - name: names(options.name).fileName, - projectName: appProjectName, + name: appProjectName, + projectName: isUsingTsSolutionConfig + ? getImportPath(host, appProjectName) + : appProjectName, appProjectRoot, e2eProjectName, e2eProjectRoot, @@ -68,7 +72,7 @@ export async function normalizeOptions( fileName, styledModule, hasStyles: options.style !== 'none', - isUsingTsSolutionConfig: isUsingTsSolutionSetup(host), + isUsingTsSolutionConfig, } as NormalizedSchema; normalized.routing = normalized.routing ?? false; diff --git a/packages/react/src/generators/component/lib/normalize-options.ts b/packages/react/src/generators/component/lib/normalize-options.ts index 49b36dfc9f1f2..8ff259747f953 100644 --- a/packages/react/src/generators/component/lib/normalize-options.ts +++ b/packages/react/src/generators/component/lib/normalize-options.ts @@ -4,6 +4,7 @@ import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generat import { assertValidStyle } from '../../../utils/assertion'; import { NormalizedSchema, Schema } from '../schema'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export async function normalizeOptions( tree: Tree, @@ -41,7 +42,10 @@ export async function normalizeOptions( ? null : options.style; - if (options.export && projectType === 'application') { + if ( + options.export && + getProjectType(tree, projectRoot, projectType) === 'application' + ) { logger.warn( `The "--export" option should not be used with applications and will do nothing.` ); diff --git a/packages/react/src/generators/hook/hook.ts b/packages/react/src/generators/hook/hook.ts index d796474c7e870..487b11089d238 100644 --- a/packages/react/src/generators/hook/hook.ts +++ b/packages/react/src/generators/hook/hook.ts @@ -17,6 +17,7 @@ import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript' import { join } from 'path'; import { addImport } from '../../utils/ast-utils'; import { Schema } from './schema'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; interface NormalizedSchema extends Omit { projectSourceRoot: string; @@ -127,9 +128,12 @@ async function normalizeOptions( const hookTypeName = names(hookName).className; const project = getProjects(host).get(projectName); - const { sourceRoot: projectSourceRoot, projectType } = project; + const { root, sourceRoot: projectSourceRoot, projectType } = project; - if (options.export && projectType === 'application') { + if ( + options.export && + getProjectType(host, root, projectType) === 'application' + ) { logger.warn( `The "--export" option should not be used with applications and will do nothing.` ); diff --git a/packages/react/src/generators/host/host.rspack.spec.ts b/packages/react/src/generators/host/host.rspack.spec.ts index 2d3cb39bd4773..fad69b5ffc31c 100644 --- a/packages/react/src/generators/host/host.rspack.spec.ts +++ b/packages/react/src/generators/host/host.rspack.spec.ts @@ -1,4 +1,3 @@ -import * as devkit from '@nx/devkit'; import { Tree, updateJson, writeJson } from '@nx/devkit'; import { ProjectGraph, readJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; diff --git a/packages/react/src/generators/host/host.ts b/packages/react/src/generators/host/host.ts index d08550b0b403e..d1d6ab0b9f2d7 100644 --- a/packages/react/src/generators/host/host.ts +++ b/packages/react/src/generators/host/host.ts @@ -64,7 +64,7 @@ export async function hostGenerator( const initTask = await applicationGenerator(host, { ...options, directory: options.appProjectRoot, - name: options.projectName, + name: options.name, // The target use-case is loading remotes as child routes, thus always enable routing. routing: true, skipFormat: true, diff --git a/packages/react/src/generators/library/lib/normalize-options.ts b/packages/react/src/generators/library/lib/normalize-options.ts index 9110d3f1a4d7f..11743e5989056 100644 --- a/packages/react/src/generators/library/lib/normalize-options.ts +++ b/packages/react/src/generators/library/lib/normalize-options.ts @@ -70,7 +70,7 @@ export async function normalizeOptions( bundler, fileName, routePath: `/${projectNames.projectSimpleName}`, - name: projectName, + name: isUsingTsSolutionConfig ? importPath : projectName, projectRoot, parsedTags, importPath, diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index 91f2b67d58f4c..3362b353d3106 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -932,7 +932,7 @@ module.exports = withNx( beforeEach(() => { tree = createTreeWithEmptyWorkspace(); updateJson(tree, 'package.json', (json) => { - json.workspaces = ['packages/*', 'apps/*']; + json.workspaces = ['packages/*', 'apps/*', 'libs/*']; return json; }); writeJson(tree, 'tsconfig.base.json', { @@ -953,11 +953,11 @@ module.exports = withNx( ...defaultSchema, bundler: 'vite', unitTestRunner: 'vitest', - directory: 'mylib', - name: 'mylib', + directory: 'libs/mylib', }); - expect(tree.read('mylib/vite.config.ts', 'utf-8')).toMatchInlineSnapshot(` + expect(tree.read('libs/mylib/vite.config.ts', 'utf-8')) + .toMatchInlineSnapshot(` "/// import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; @@ -966,7 +966,7 @@ module.exports = withNx( export default defineConfig({ root: __dirname, - cacheDir: '../node_modules/.vite/mylib', + cacheDir: '../../node_modules/.vite/libs/mylib', plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })], // Uncomment this if you are using workers. // worker: { @@ -984,7 +984,7 @@ module.exports = withNx( lib: { // Could also be a dictionary or array of multiple entry points. entry: 'src/index.ts', - name: 'mylib', + name: '@proj/mylib', fileName: 'index', // Change this to the formats you want to support. // Don't forget to update your package.json as well. @@ -1013,12 +1013,12 @@ module.exports = withNx( expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(` [ { - "path": "./mylib", + "path": "./libs/mylib", }, ] `); // Make sure keys are in idiomatic order - expect(Object.keys(readJson(tree, 'mylib/package.json'))) + expect(Object.keys(readJson(tree, 'libs/mylib/package.json'))) .toMatchInlineSnapshot(` [ "name", @@ -1028,12 +1028,11 @@ module.exports = withNx( "module", "types", "exports", - "nx", ] `); - expect(readJson(tree, 'mylib/tsconfig.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'libs/mylib/tsconfig.json')).toMatchInlineSnapshot(` { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [ @@ -1046,7 +1045,8 @@ module.exports = withNx( ], } `); - expect(readJson(tree, 'mylib/tsconfig.lib.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'libs/mylib/tsconfig.lib.json')) + .toMatchInlineSnapshot(` { "compilerOptions": { "jsx": "react-jsx", @@ -1089,7 +1089,7 @@ module.exports = withNx( "eslint.config.cjs", "eslint.config.mjs", ], - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "include": [ "src/**/*.js", "src/**/*.jsx", @@ -1098,7 +1098,8 @@ module.exports = withNx( ], } `); - expect(readJson(tree, 'mylib/tsconfig.spec.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'libs/mylib/tsconfig.spec.json')) + .toMatchInlineSnapshot(` { "compilerOptions": { "jsx": "react-jsx", @@ -1113,7 +1114,7 @@ module.exports = withNx( "vitest", ], }, - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "include": [ "vite.config.ts", "vite.config.mts", @@ -1143,20 +1144,18 @@ module.exports = withNx( ...defaultSchema, bundler: 'none', unitTestRunner: 'none', - directory: 'mylib', - name: 'mylib', + directory: 'libs/mylib', }); await libraryGenerator(tree, { ...defaultSchema, bundler: 'none', unitTestRunner: 'none', - directory: 'myjslib', - name: 'myjslib', + directory: 'libs/myjslib', js: true, }); - expect(readJson(tree, 'mylib/package.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'libs/mylib/package.json')).toMatchInlineSnapshot(` { "exports": { ".": { @@ -1168,16 +1167,12 @@ module.exports = withNx( }, "main": "./src/index.ts", "name": "@proj/mylib", - "nx": { - "name": "mylib", - "projectType": "library", - "sourceRoot": "mylib/src", - }, "types": "./src/index.ts", "version": "0.0.1", } `); - expect(readJson(tree, 'myjslib/package.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'libs/myjslib/package.json')) + .toMatchInlineSnapshot(` { "exports": { ".": "./src/index.js", @@ -1185,11 +1180,6 @@ module.exports = withNx( }, "main": "./src/index.js", "name": "@proj/myjslib", - "nx": { - "name": "myjslib", - "projectType": "library", - "sourceRoot": "myjslib/src", - }, "types": "./src/index.js", "version": "0.0.1", } @@ -1201,11 +1191,10 @@ module.exports = withNx( ...defaultSchema, bundler: 'rollup', unitTestRunner: 'none', - directory: 'mylib', - name: 'mylib', + directory: 'libs/mylib', }); - expect(tree.read('mylib/rollup.config.cjs', 'utf-8')) + expect(tree.read('libs/mylib/rollup.config.cjs', 'utf-8')) .toMatchInlineSnapshot(` "const { withNx } = require('@nx/rollup/with-nx'); const url = require('@rollup/plugin-url'); @@ -1242,13 +1231,12 @@ module.exports = withNx( await libraryGenerator(tree, { ...defaultSchema, bundler: 'rollup', - directory: 'mylib', - name: 'mylib', + directory: 'libs/mylib', publishable: true, importPath: '@acme/mylib', }); - expect(readJson(tree, 'mylib/package.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'libs/mylib/package.json')).toMatchInlineSnapshot(` { "exports": { ".": { @@ -1265,11 +1253,6 @@ module.exports = withNx( "main": "./dist/index.esm.js", "module": "./dist/index.esm.js", "name": "@acme/mylib", - "nx": { - "name": "mylib", - "projectType": "library", - "sourceRoot": "mylib/src", - }, "type": "module", "types": "./dist/index.esm.d.ts", "version": "0.0.1", diff --git a/packages/react/src/generators/library/library.ts b/packages/react/src/generators/library/library.ts index 9ddef16ad30b3..083de14d069bb 100644 --- a/packages/react/src/generators/library/library.ts +++ b/packages/react/src/generators/library/library.ts @@ -55,6 +55,11 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) { tasks.push(jsInitTask); const options = await normalizeOptions(host, schema); + + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(host, options.projectRoot); + } + if (options.publishable === true && !schema.importPath) { throw new Error( `For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)` @@ -75,12 +80,11 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) { name: options.importPath, version: '0.0.1', ...determineEntryFields(options), - nx: { - name: options.importPath === options.name ? undefined : options.name, - projectType: 'library', - sourceRoot: `${options.projectRoot}/src`, - tags: options.parsedTags?.length ? options.parsedTags : undefined, - }, + nx: options.parsedTags?.length + ? { + tags: options.parsedTags, + } + : undefined, files: options.publishable ? ['dist', '!**/*.tsbuildinfo'] : undefined, }); } else { @@ -273,10 +277,6 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) { : undefined ); - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(host, options.projectRoot); - } - sortPackageJsonFields(host, options.projectRoot); if (!options.skipFormat) { diff --git a/packages/react/src/generators/redux/redux.ts b/packages/react/src/generators/redux/redux.ts index ee0cf7e44e15e..d721bdbe68840 100644 --- a/packages/react/src/generators/redux/redux.ts +++ b/packages/react/src/generators/redux/redux.ts @@ -20,6 +20,7 @@ import { import { getRootTsConfigPathInTree } from '@nx/js'; import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript'; import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generators/artifact-name-and-directory-utils'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; let tsModule: typeof import('typescript'); @@ -165,14 +166,14 @@ async function normalizeOptions( const projects = getProjects(host); const project = projects.get(projectName); - const { sourceRoot, projectType } = project; + const { root, sourceRoot, projectType } = project; const tsConfigJson = readJson(host, getRootTsConfigPathInTree(host)); const tsPaths: { [module: string]: string[] } = tsConfigJson.compilerOptions ? tsConfigJson.compilerOptions.paths || {} : {}; const modulePath = - projectType === 'application' + getProjectType(host, root, projectType) === 'application' ? options.path ? `./app/${options.path}/${extraNames.fileName}.slice` : `./app/${extraNames.fileName}.slice` diff --git a/packages/react/src/generators/remote/remote.ts b/packages/react/src/generators/remote/remote.ts index face5a7eed5c1..9e1e923b12e32 100644 --- a/packages/react/src/generators/remote/remote.ts +++ b/packages/react/src/generators/remote/remote.ts @@ -111,10 +111,10 @@ export async function remoteGenerator(host: Tree, schema: Schema) { if (options.dynamic) { // Dynamic remotes generate with library { type: 'var' } by default. // We need to ensure that the remote name is a valid variable name. - const isValidRemote = isValidVariable(options.projectName); + const isValidRemote = isValidVariable(options.name); if (!isValidRemote.isValid) { throw new Error( - `Invalid remote name provided: ${options.projectName}. ${isValidRemote.message}` + `Invalid remote name provided: ${options.name}. ${isValidRemote.message}` ); } } @@ -122,9 +122,9 @@ export async function remoteGenerator(host: Tree, schema: Schema) { await ensureProjectName(host, options, 'application'); const REMOTE_NAME_REGEX = '^[a-zA-Z_$][a-zA-Z_$0-9]*$'; const remoteNameRegex = new RegExp(REMOTE_NAME_REGEX); - if (!remoteNameRegex.test(options.projectName)) { + if (!remoteNameRegex.test(options.name)) { throw new Error( - stripIndents`Invalid remote name: ${options.projectName}. Remote project names must: + stripIndents`Invalid remote name: ${options.name}. Remote project names must: - Start with a letter, dollar sign ($) or underscore (_) - Followed by any valid character (letters, digits, underscores, or dollar signs) The regular expression used is ${REMOTE_NAME_REGEX}.` @@ -132,14 +132,14 @@ export async function remoteGenerator(host: Tree, schema: Schema) { } const initAppTask = await applicationGenerator(host, { ...options, - name: options.projectName, + name: options.name, skipFormat: true, alwaysGenerateProjectJson: true, }); tasks.push(initAppTask); - if (schema.host) { - updateHostWithRemote(host, schema.host, options.projectName); + if (options.host) { + updateHostWithRemote(host, options.host, options.name); } // Module federation requires bootstrap code to be dynamically imported. diff --git a/packages/react/src/generators/stories/stories.ts b/packages/react/src/generators/stories/stories.ts index f5e23e6f90f05..4666e7042acce 100644 --- a/packages/react/src/generators/stories/stories.ts +++ b/packages/react/src/generators/stories/stories.ts @@ -19,6 +19,7 @@ import { basename, join } from 'path'; import { minimatch } from 'minimatch'; import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript'; import { nxVersion } from '../../utils/versions'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; let tsModule: typeof import('typescript'); @@ -35,7 +36,7 @@ export async function projectRootPath( config: ProjectConfiguration ): Promise { let projectDir: string; - if (config.projectType === 'application') { + if (getProjectType(tree, config.root, config.projectType) === 'application') { const isNextJs = await isNextJsProject(tree, config); if (isNextJs) { // Next.js apps diff --git a/packages/remix/src/generators/application/application.impl.spec.ts b/packages/remix/src/generators/application/application.impl.spec.ts index 4316a21f4abc3..1b19774098b7a 100644 --- a/packages/remix/src/generators/application/application.impl.spec.ts +++ b/packages/remix/src/generators/application/application.impl.spec.ts @@ -379,10 +379,8 @@ describe('Remix Application', () => { "engines": { "node": ">=20", }, - "name": "myapp", + "name": "@proj/myapp", "nx": { - "projectType": "application", - "sourceRoot": "myapp", "tags": [ "foo", ], @@ -541,6 +539,41 @@ describe('Remix Application', () => { `); }); + it('should skip nx property in package.json when no tags are provided', async () => { + await applicationGenerator(tree, { + directory: 'apps/myapp', + e2eTestRunner: 'playwright', + unitTestRunner: 'jest', + addPlugin: true, + }); + + expect(readJson(tree, 'apps/myapp/package.json')).toMatchInlineSnapshot(` + { + "dependencies": { + "@remix-run/node": "^2.14.0", + "@remix-run/react": "^2.14.0", + "@remix-run/serve": "^2.14.0", + "isbot": "^4.4.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + }, + "devDependencies": { + "@remix-run/dev": "^2.14.0", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + }, + "engines": { + "node": ">=20", + }, + "name": "@proj/myapp", + "private": true, + "scripts": {}, + "sideEffects": false, + "type": "module", + } + `); + }); + it('should generate valid package.json without formatting', async () => { await applicationGenerator(tree, { directory: 'myapp', diff --git a/packages/remix/src/generators/application/application.impl.ts b/packages/remix/src/generators/application/application.impl.ts index 01ab072077521..3150f1d28e134 100644 --- a/packages/remix/src/generators/application/application.impl.ts +++ b/packages/remix/src/generators/application/application.impl.ts @@ -12,12 +12,10 @@ import { Tree, updateJson, updateProjectConfiguration, - visitNotIgnoredFiles, } from '@nx/devkit'; import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command'; import { initGenerator as jsInitGenerator } from '@nx/js'; import { extractTsConfigBase } from '@nx/js/src/utils/typescript/create-ts-config'; -import { dirname } from 'node:path'; import { createNxCloudOnboardingURLForWelcomeApp, getNxCloudAppOnBoardingUrl, @@ -39,14 +37,13 @@ import initGenerator from '../init/init'; import { updateDependencies } from '../utils/update-dependencies'; import { addE2E, + addViteTempFilesToGitIgnore, normalizeOptions, updateUnitTestConfig, - addViteTempFilesToGitIgnore, } from './lib'; import { NxRemixGeneratorSchema } from './schema'; import { addProjectToTsSolutionWorkspace, - isUsingTsSolutionSetup, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; @@ -85,9 +82,13 @@ export async function remixApplicationGeneratorInternal( ); } - const isUsingTsSolution = isUsingTsSolutionSetup(tree); + // If we are using the new TS solution + // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(tree, options.projectRoot); + } - if (!isUsingTsSolution) { + if (!options.isUsingTsSolutionConfig) { addProjectConfiguration(tree, options.projectName, { root: options.projectRoot, sourceRoot: `${options.projectRoot}`, @@ -122,7 +123,6 @@ export async function remixApplicationGeneratorInternal( eslintVersion, typescriptVersion, viteVersion, - isUsingTsSolution, }; generateFiles( @@ -154,7 +154,7 @@ export async function remixApplicationGeneratorInternal( ); } - if (isUsingTsSolution) { + if (options.isUsingTsSolutionConfig) { generateFiles( tree, joinPathFragments(__dirname, 'files/ts-solution'), @@ -328,12 +328,6 @@ export default {...nxPreset}; '.' ); - // If we are using the new TS solution - // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project - if (options.useTsSolution) { - addProjectToTsSolutionWorkspace(tree, options.projectRoot); - } - sortPackageJsonFields(tree, options.projectRoot); if (!options.skipFormat) { diff --git a/packages/remix/src/generators/application/files/ts-solution/package.json__tmpl__ b/packages/remix/src/generators/application/files/ts-solution/package.json__tmpl__ index 594f4bd31ab48..961e49c286733 100644 --- a/packages/remix/src/generators/application/files/ts-solution/package.json__tmpl__ +++ b/packages/remix/src/generators/application/files/ts-solution/package.json__tmpl__ @@ -19,13 +19,8 @@ "engines": { "node": ">=20" }, - "sideEffects": false<% if (isUsingTsSolution) { %>, + "sideEffects": false<% if (isUsingTsSolutionConfig && parsedTags?.length) { %>, "nx": { - <%_ if (name !== projectName) { _%> - "name": "<%= name %>",<%_ } _%> - "projectType": "application", - "sourceRoot": "<%- projectRoot %>"<%_ if (parsedTags?.length) { _%>, "tags": <%- JSON.stringify(parsedTags) %> - <%_ } _%> }<% } %> } diff --git a/packages/remix/src/generators/application/lib/normalize-options.ts b/packages/remix/src/generators/application/lib/normalize-options.ts index d1ae13cf4e0ab..0140e7bfae4a2 100644 --- a/packages/remix/src/generators/application/lib/normalize-options.ts +++ b/packages/remix/src/generators/application/lib/normalize-options.ts @@ -6,6 +6,7 @@ import { import { Linter } from '@nx/eslint'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { type NxRemixGeneratorSchema } from '../schema'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export interface NormalizedSchema extends NxRemixGeneratorSchema { projectName: string; @@ -13,6 +14,7 @@ export interface NormalizedSchema extends NxRemixGeneratorSchema { e2eProjectName: string; e2eProjectRoot: string; parsedTags: string[]; + isUsingTsSolutionConfig: boolean; } export async function normalizeOptions( @@ -43,14 +45,18 @@ export async function normalizeOptions( ? options.tags.split(',').map((s) => s.trim()) : []; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(tree); return { ...options, linter: options.linter ?? Linter.EsLint, - projectName, + projectName: isUsingTsSolutionConfig + ? getImportPath(tree, projectName) + : projectName, projectRoot, e2eProjectName, e2eProjectRoot, parsedTags, - useTsSolution: options.useTsSolution ?? isUsingTsSolutionSetup(tree), + useTsSolution: options.useTsSolution ?? isUsingTsSolutionConfig, + isUsingTsSolutionConfig, }; } diff --git a/packages/remix/src/generators/library/lib/add-tsconfig-entry-points.ts b/packages/remix/src/generators/library/lib/add-tsconfig-entry-points.ts index 891bf3a1c78b8..64e87d0d0b7cf 100644 --- a/packages/remix/src/generators/library/lib/add-tsconfig-entry-points.ts +++ b/packages/remix/src/generators/library/lib/add-tsconfig-entry-points.ts @@ -16,7 +16,7 @@ export function addTsconfigEntryPoints( tree, options.projectName ); - const serverFilePath = joinPathFragments(sourceRoot, 'server.ts'); + const serverFilePath = joinPathFragments(sourceRoot ?? 'src', 'server.ts'); tree.write( serverFilePath, diff --git a/packages/remix/src/generators/library/lib/normalize-options.ts b/packages/remix/src/generators/library/lib/normalize-options.ts index 1dac5a6758083..a94199dec3208 100644 --- a/packages/remix/src/generators/library/lib/normalize-options.ts +++ b/packages/remix/src/generators/library/lib/normalize-options.ts @@ -5,6 +5,7 @@ import { } from '@nx/devkit/src/generators/project-name-and-root-utils'; import type { NxRemixGeneratorSchema } from '../schema'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export interface RemixLibraryOptions extends NxRemixGeneratorSchema { projectName: string; @@ -32,11 +33,14 @@ export async function normalizeOptions( nxJson.useInferencePlugins !== false; options.addPlugin ??= addPluginDefault; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(tree); return { ...options, unitTestRunner: options.unitTestRunner ?? 'vitest', - projectName, + projectName: isUsingTsSolutionConfig + ? getImportPath(tree, projectName) + : projectName, projectRoot, - isUsingTsSolutionConfig: isUsingTsSolutionSetup(tree), + isUsingTsSolutionConfig, }; } diff --git a/packages/remix/src/generators/library/library.impl.spec.ts b/packages/remix/src/generators/library/library.impl.spec.ts index c514be28a27d7..1ced53dba5680 100644 --- a/packages/remix/src/generators/library/library.impl.spec.ts +++ b/packages/remix/src/generators/library/library.impl.spec.ts @@ -173,7 +173,6 @@ describe('Remix Library Generator', () => { "main", "types", "exports", - "nx", ] `); expect(readJson(tree, 'packages/foo/package.json')) @@ -189,15 +188,27 @@ describe('Remix Library Generator', () => { }, "main": "./src/index.ts", "name": "@proj/foo", - "nx": { - "name": "foo", - "projectType": "library", - "sourceRoot": "packages/foo/src", - }, "types": "./src/index.ts", "version": "0.0.1", } `); }); + + it('should generate server entrypoint', async () => { + await libraryGenerator(tree, { + directory: 'test', + style: 'css', + addPlugin: true, + }); + + expect(tree.exists(`test/src/server.ts`)); + expect(tree.children(`test/src/lib`).sort()).toMatchInlineSnapshot(` + [ + "test.module.css", + "test.spec.tsx", + "test.tsx", + ] + `); + }, 25_000); }); }); diff --git a/packages/remix/src/generators/library/library.impl.ts b/packages/remix/src/generators/library/library.impl.ts index f2187973199df..4eac8a79af530 100644 --- a/packages/remix/src/generators/library/library.impl.ts +++ b/packages/remix/src/generators/library/library.impl.ts @@ -29,6 +29,10 @@ export async function remixLibraryGeneratorInternal( const tasks: GeneratorCallback[] = []; const options = await normalizeOptions(tree, schema); + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(tree, options.projectRoot); + } + const jsInitTask = await jsInitGenerator(tree, { js: options.js, skipFormat: true, @@ -77,10 +81,6 @@ export async function remixLibraryGeneratorInternal( : undefined ); - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(tree, options.projectRoot); - } - sortPackageJsonFields(tree, options.projectRoot); if (!options.skipFormat) { diff --git a/packages/rspack/src/generators/configuration/configuration.ts b/packages/rspack/src/generators/configuration/configuration.ts index 4a977e450f320..22d938eaae0a7 100644 --- a/packages/rspack/src/generators/configuration/configuration.ts +++ b/packages/rspack/src/generators/configuration/configuration.ts @@ -20,6 +20,7 @@ import { import { editTsConfig } from '../application/lib/create-ts-config'; import rspackInitGenerator from '../init/init'; import { ConfigurationSchema } from './schema'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export async function configurationGenerator( tree: Tree, @@ -73,7 +74,7 @@ export async function configurationGenerator( if ( alreadyHasNxRspackTargets.build && (alreadyHasNxRspackTargets.serve || - projectType === 'library' || + getProjectType(tree, options.project, projectType) === 'library' || options.framework === 'nest') ) { throw new Error( diff --git a/packages/storybook/src/generators/configuration/configuration.spec.ts b/packages/storybook/src/generators/configuration/configuration.spec.ts index 63ebd8247ba95..dc139f0ae0673 100644 --- a/packages/storybook/src/generators/configuration/configuration.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration.spec.ts @@ -825,7 +825,7 @@ describe('@nx/storybook:configuration for Storybook v7', () => { }); await configurationGenerator(tree, { - project: 'mylib', + project: '@proj/mylib', standaloneConfig: false, uiFramework: '@storybook/react-vite', addPlugin: true, diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index c7f40e83427ad..7acd7c53dd096 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -47,6 +47,7 @@ import { import { interactionTestsDependencies } from './lib/interaction-testing.utils'; import { ensureDependencies } from './lib/ensure-dependencies'; import { editRootTsConfig } from './lib/edit-root-tsconfig'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export function configurationGenerator( tree: Tree, @@ -126,7 +127,8 @@ export async function configurationGeneratorInternal( ); const mainDir = - !!nextConfigFilePath && projectType === 'application' + !!nextConfigFilePath && + getProjectType(tree, root, projectType) === 'application' ? 'components' : 'src'; diff --git a/packages/storybook/src/generators/configuration/lib/util-functions.ts b/packages/storybook/src/generators/configuration/lib/util-functions.ts index fcee845126085..ee8a71da3119d 100644 --- a/packages/storybook/src/generators/configuration/lib/util-functions.ts +++ b/packages/storybook/src/generators/configuration/lib/util-functions.ts @@ -30,6 +30,7 @@ import { findEslintFile } from '@nx/eslint/src/generators/utils/eslint-file'; import { useFlatConfig } from '@nx/eslint/src/utils/flat-config'; import { findRuntimeTsConfigName, + getProjectType, isUsingTsSolutionSetup, } from '@nx/js/src/utils/typescript/ts-solution-setup'; @@ -581,7 +582,8 @@ export function createProjectStorybookDir( usesReactNative?: boolean ) { let projectDirectory = - projectType === 'application' + getProjectType(tree, root, projectType as 'application' | 'library') === + 'application' ? isNextJs ? 'components' : 'src/app' @@ -616,7 +618,10 @@ export function createProjectStorybookDir( projectType, interactionTests, mainDir, - isNextJs: isNextJs && projectType === 'application', + isNextJs: + isNextJs && + getProjectType(tree, root, projectType as 'application' | 'library') === + 'application', usesSwc, usesVite, isRootProject: projectIsRootProjectInStandaloneWorkspace, @@ -651,7 +656,7 @@ export function getTsConfigPath( root, path?.length > 0 ? path - : projectType === 'application' + : getProjectType(tree, root, projectType) === 'application' ? 'tsconfig.app.json' : 'tsconfig.lib.json' ); diff --git a/packages/vite/src/generators/configuration/configuration.ts b/packages/vite/src/generators/configuration/configuration.ts index 1d020b00a1602..01e558c35b16f 100644 --- a/packages/vite/src/generators/configuration/configuration.ts +++ b/packages/vite/src/generators/configuration/configuration.ts @@ -15,7 +15,10 @@ import { initGenerator as jsInitGenerator, } from '@nx/js'; import { getImportPath } from '@nx/js/src/utils/get-import-path'; -import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { + getProjectType, + isUsingTsSolutionSetup, +} from '@nx/js/src/utils/typescript/ts-solution-setup'; import { join } from 'node:path/posix'; import type { PackageJson } from 'nx/src/utils/package-json'; import { ensureDependencies } from '../../utils/ensure-dependencies'; @@ -50,8 +53,11 @@ export async function viteConfigurationGeneratorInternal( const projectConfig = readProjectConfiguration(tree, schema.project); const { targets, root: projectRoot } = projectConfig; - const projectType = - schema.projectType ?? projectConfig.projectType ?? 'library'; + const projectType = getProjectType( + tree, + projectConfig.root, + schema.projectType ?? projectConfig.projectType + ); schema.includeLib ??= projectType === 'library'; @@ -196,7 +202,7 @@ function updatePackageJson( name: getImportPath(tree, options.project), version: '0.0.1', }; - if (projectType === 'application') { + if (getProjectType(tree, project.root, projectType) === 'application') { packageJson.private = true; } } diff --git a/packages/vite/src/generators/configuration/lib/convert-non-vite.ts b/packages/vite/src/generators/configuration/lib/convert-non-vite.ts index 6a6153f7e4ace..8a225bd352985 100644 --- a/packages/vite/src/generators/configuration/lib/convert-non-vite.ts +++ b/packages/vite/src/generators/configuration/lib/convert-non-vite.ts @@ -16,12 +16,13 @@ import { handleUnknownConfiguration, moveAndEditIndexHtml, } from '../../../utils/generator-utils'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; export async function convertNonVite( tree: Tree, schema: ViteConfigurationGeneratorSchema, projectRoot: string, - projectType: string, + _projectType: string, targets: { [targetName: string]: TargetConfiguration; } @@ -34,6 +35,11 @@ export async function convertNonVite( // Check if it has webpack const hasWebpackConfig = findWebpackConfig(tree, projectRoot); + const projectType = getProjectType( + tree, + projectRoot, + _projectType as 'application' | 'library' + ); if (hasWebpackConfig) { if (projectType === 'application') { moveAndEditIndexHtml(tree, schema); diff --git a/packages/vite/src/generators/vitest/vitest-generator.ts b/packages/vite/src/generators/vitest/vitest-generator.ts index 11909e964d7e6..8193edef45043 100644 --- a/packages/vite/src/generators/vitest/vitest-generator.ts +++ b/packages/vite/src/generators/vitest/vitest-generator.ts @@ -15,7 +15,10 @@ import { updateNxJson, } from '@nx/devkit'; import { initGenerator as jsInitGenerator } from '@nx/js'; -import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { + getProjectType, + isUsingTsSolutionSetup, +} from '@nx/js/src/utils/typescript/ts-solution-setup'; import { join } from 'path'; import { ensureDependencies } from '../../utils/ensure-dependencies'; import { @@ -118,7 +121,7 @@ getTestBed().initTestEnvironment( tree, { project: schema.project, - includeLib: projectType === 'library', + includeLib: getProjectType(tree, root, projectType) === 'library', includeVitest: true, inSourceTests: schema.inSourceTests, rollupOptionsExternal: [ @@ -142,7 +145,7 @@ getTestBed().initTestEnvironment( { ...schema, includeVitest: true, - includeLib: projectType === 'library', + includeLib: getProjectType(tree, root, projectType) === 'library', }, true ); @@ -265,7 +268,9 @@ function updateTsConfig( let runtimeTsconfigPath = joinPathFragments( projectRoot, - projectType === 'application' ? 'tsconfig.app.json' : 'tsconfig.lib.json' + getProjectType(tree, projectRoot, projectType) === 'application' + ? 'tsconfig.app.json' + : 'tsconfig.lib.json' ); if (options.runtimeTsconfigFileName) { runtimeTsconfigPath = joinPathFragments( diff --git a/packages/vue/src/generators/application/application.ts b/packages/vue/src/generators/application/application.ts index 52a78720c8157..9869402fb1a3f 100644 --- a/packages/vue/src/generators/application/application.ts +++ b/packages/vue/src/generators/application/application.ts @@ -52,6 +52,13 @@ export async function applicationGeneratorInternal( ); const options = await normalizeOptions(tree, _options); + + // If we are using the new TS solution + // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); + } + const nxJson = readNxJson(tree); options.addPlugin ??= @@ -60,15 +67,14 @@ export async function applicationGeneratorInternal( if (options.isUsingTsSolutionConfig) { writeJson(tree, joinPathFragments(options.appProjectRoot, 'package.json'), { - name: getImportPath(tree, options.name), + name: options.projectName, version: '0.0.1', private: true, - nx: { - name: options.name, - projectType: 'application', - sourceRoot: `${options.appProjectRoot}/src`, - tags: options.parsedTags?.length ? options.parsedTags : undefined, - }, + nx: options.parsedTags?.length + ? { + tags: options.parsedTags, + } + : undefined, }); } else { addProjectConfiguration(tree, options.projectName, { @@ -108,6 +114,7 @@ export async function applicationGeneratorInternal( setParserOptionsProject: options.setParserOptionsProject, rootProject: options.rootProject, addPlugin: options.addPlugin, + projectName: options.projectName, }, 'app' ) @@ -146,12 +153,6 @@ export async function applicationGeneratorInternal( ); } - // If we are using the new TS solution - // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); - } - sortPackageJsonFields(tree, options.appProjectRoot); if (!options.skipFormat) await formatFiles(tree); diff --git a/packages/vue/src/generators/application/lib/normalize-options.ts b/packages/vue/src/generators/application/lib/normalize-options.ts index 544d0685f97aa..d5cac49596f45 100644 --- a/packages/vue/src/generators/application/lib/normalize-options.ts +++ b/packages/vue/src/generators/application/lib/normalize-options.ts @@ -5,6 +5,7 @@ import { } from '@nx/devkit/src/generators/project-name-and-root-utils'; import { NormalizedSchema, Schema } from '../schema'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export async function normalizeOptions( host: Tree, @@ -27,20 +28,24 @@ export async function normalizeOptions( ? options.tags.split(',').map((s) => s.trim()) : []; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(host); + const normalized = { ...options, - projectName: appProjectName, + projectName: isUsingTsSolutionConfig + ? getImportPath(host, appProjectName) + : appProjectName, appProjectRoot, e2eProjectName, e2eProjectRoot, parsedTags, + isUsingTsSolutionConfig, } as NormalizedSchema; normalized.style = options.style ?? 'css'; normalized.routing = normalized.routing ?? false; normalized.unitTestRunner ??= 'vitest'; normalized.e2eTestRunner = normalized.e2eTestRunner ?? 'playwright'; - normalized.isUsingTsSolutionConfig = isUsingTsSolutionSetup(host); normalized.bundler = normalized.bundler ?? 'vite'; return normalized; diff --git a/packages/vue/src/generators/component/lib/utils.ts b/packages/vue/src/generators/component/lib/utils.ts index 8abbef6f39943..5b8ec749209a9 100644 --- a/packages/vue/src/generators/component/lib/utils.ts +++ b/packages/vue/src/generators/component/lib/utils.ts @@ -12,6 +12,7 @@ import { determineArtifactNameAndDirectoryOptions } from '@nx/devkit/src/generat import { NormalizedSchema, ComponentGeneratorSchema } from '../schema'; import { addImport } from '../../../utils/ast-utils'; +import { getProjectType } from '@nx/js/src/utils/typescript/ts-solution-setup'; let tsModule: typeof import('typescript'); @@ -33,9 +34,12 @@ export async function normalizeOptions( let { className } = names(fileName); const componentFileName = fileName; const project = getProjects(host).get(projectName); - const { sourceRoot: projectSourceRoot, projectType } = project; + const { root, sourceRoot: projectSourceRoot, projectType } = project; - if (options.export && projectType === 'application') { + if ( + options.export && + getProjectType(host, root, projectType) === 'application' + ) { logger.warn( `The "--export" option should not be used with applications and will do nothing.` ); @@ -60,12 +64,13 @@ export function addExportsToBarrel(host: Tree, options: NormalizedSchema) { tsModule = ensureTypescript(); } const workspace = getProjects(host); + const proj = workspace.get(options.projectName); const isApp = - workspace.get(options.projectName).projectType === 'application'; + getProjectType(host, proj.root, proj.projectType) === 'application'; if (options.export && !isApp) { const indexFilePath = joinPathFragments( - options.projectSourceRoot, + options.projectSourceRoot ?? 'src', options.js ? 'index.js' : 'index.ts' ); const indexSource = host.read(indexFilePath, 'utf-8'); diff --git a/packages/vue/src/generators/library/lib/add-vite.ts b/packages/vue/src/generators/library/lib/add-vite.ts index d8c1fca689f5d..357c6318067d1 100644 --- a/packages/vue/src/generators/library/lib/add-vite.ts +++ b/packages/vue/src/generators/library/lib/add-vite.ts @@ -18,7 +18,7 @@ export async function addVite( ensurePackage('@nx/vite', nxVersion); const viteTask = await viteConfigurationGenerator(tree, { uiFramework: 'none', - project: options.name, + project: options.projectName, newProject: true, includeLib: true, inSourceTests: options.inSourceTests, @@ -32,7 +32,7 @@ export async function addVite( createOrEditViteConfig( tree, { - project: options.name, + project: options.projectName, includeLib: true, includeVitest: options.unitTestRunner === 'vitest', inSourceTests: options.inSourceTests, @@ -53,7 +53,7 @@ export async function addVite( >('@nx/vite', nxVersion); const vitestTask = await vitestGenerator(tree, { uiFramework: 'none', - project: options.name, + project: options.projectName, coverageProvider: 'v8', inSourceTests: options.inSourceTests, skipFormat: true, @@ -66,7 +66,7 @@ export async function addVite( createOrEditViteConfig( tree, { - project: options.name, + project: options.projectName, includeLib: true, includeVitest: true, inSourceTests: options.inSourceTests, diff --git a/packages/vue/src/generators/library/lib/normalize-options.ts b/packages/vue/src/generators/library/lib/normalize-options.ts index eabe320f65238..20c77064f8734 100644 --- a/packages/vue/src/generators/library/lib/normalize-options.ts +++ b/packages/vue/src/generators/library/lib/normalize-options.ts @@ -11,6 +11,7 @@ import { } from '@nx/devkit/src/generators/project-name-and-root-utils'; import { NormalizedSchema, Schema } from '../schema'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { getImportPath } from '@nx/js/src/utils/get-import-path'; export async function normalizeOptions( host: Tree, @@ -51,9 +52,14 @@ export async function normalizeOptions( process.env.NX_ADD_PLUGINS !== 'false' && nxJson.useInferencePlugins !== false; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(host); + const normalized = { addPlugin, ...options, + projectName: isUsingTsSolutionConfig + ? getImportPath(host, projectName) + : projectName, bundler, fileName, routePath: `/${projectNames.projectFileName}`, @@ -61,7 +67,7 @@ export async function normalizeOptions( projectRoot, parsedTags, importPath, - isUsingTsSolutionConfig: isUsingTsSolutionSetup(host), + isUsingTsSolutionConfig, } as NormalizedSchema; // Libraries with a bundler or is publishable must also be buildable. diff --git a/packages/vue/src/generators/library/library.ts b/packages/vue/src/generators/library/library.ts index 036027e10c2ad..2a0f8d4701da4 100644 --- a/packages/vue/src/generators/library/library.ts +++ b/packages/vue/src/generators/library/library.ts @@ -47,6 +47,12 @@ export async function libraryGeneratorInternal(tree: Tree, schema: Schema) { ); } + // If we are using the new TS solution + // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(tree, options.projectRoot); + } + if (options.isUsingTsSolutionConfig) { writeJson(tree, joinPathFragments(options.projectRoot, 'package.json'), { name: getImportPath(tree, options.name), @@ -54,12 +60,11 @@ export async function libraryGeneratorInternal(tree: Tree, schema: Schema) { private: true, ...determineEntryFields(options), files: options.publishable ? ['dist', '!**/*.tsbuildinfo'] : undefined, - nx: { - name: options.name, - projectType: 'library', - sourceRoot: `${options.projectRoot}/src`, - tags: options.parsedTags?.length ? options.parsedTags : undefined, - }, + nx: options.parsedTags?.length + ? { + tags: options.parsedTags, + } + : undefined, }); } else { addProjectConfiguration(tree, options.name, { @@ -149,12 +154,6 @@ export async function libraryGeneratorInternal(tree: Tree, schema: Schema) { ); } - // If we are using the new TS solution - // We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(tree, options.projectRoot); - } - sortPackageJsonFields(tree, options.projectRoot); if (!options.skipFormat) await formatFiles(tree); diff --git a/packages/vue/src/generators/library/schema.d.ts b/packages/vue/src/generators/library/schema.d.ts index ed8dc8249f096..7f9ac552797a2 100644 --- a/packages/vue/src/generators/library/schema.d.ts +++ b/packages/vue/src/generators/library/schema.d.ts @@ -27,6 +27,7 @@ export interface Schema { export interface NormalizedSchema extends Schema { js: boolean; name: string; + projectName: string; linter: Linter | LinterType; fileName: string; projectRoot: string; diff --git a/packages/vue/src/utils/add-linting.ts b/packages/vue/src/utils/add-linting.ts index 7b3eb3a9edcc3..466e3115357b3 100644 --- a/packages/vue/src/utils/add-linting.ts +++ b/packages/vue/src/utils/add-linting.ts @@ -30,6 +30,7 @@ export async function addLinting( skipPackageJson?: boolean; rootProject?: boolean; addPlugin?: boolean; + projectName: string; }, projectType: 'lib' | 'app' ) { @@ -37,7 +38,7 @@ export async function addLinting( const tasks: GeneratorCallback[] = []; const lintTask = await lintProjectGenerator(host, { linter: options.linter, - project: options.name, + project: options.projectName, tsConfigPaths: [ joinPathFragments(options.projectRoot, `tsconfig.${projectType}.json`), ], diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts index 862a8868fb04d..8a0eaaa9cdeb1 100644 --- a/packages/web/src/generators/application/application.ts +++ b/packages/web/src/generators/application/application.ts @@ -247,12 +247,11 @@ async function addProject(tree: Tree, options: NormalizedSchema) { 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, - }, + nx: options.parsedTags?.length + ? { + tags: options.parsedTags, + } + : undefined, }); } else { addProjectConfiguration(tree, options.projectName, { @@ -288,6 +287,10 @@ export async function applicationGenerator(host: Tree, schema: Schema) { export async function applicationGeneratorInternal(host: Tree, schema: Schema) { const options = await normalizeOptions(host, schema); + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(host, options.appProjectRoot); + } + const tasks: GeneratorCallback[] = []; const jsInitTask = await jsInitGenerator(host, { @@ -666,10 +669,6 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) { : undefined ); - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(host, options.appProjectRoot); - } - if (!options.skipFormat) { await formatFiles(host); } @@ -719,7 +718,9 @@ async function normalizeOptions( name: names(options.name).fileName, compiler: options.compiler ?? 'babel', bundler: options.bundler ?? 'webpack', - projectName: appProjectName, + projectName: isUsingTsSolutionConfig + ? getImportPath(host, appProjectName) + : appProjectName, strict: options.strict ?? true, appProjectRoot, e2eProjectRoot, diff --git a/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.ts b/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.ts index cb2b579db6d02..20f20040e86d6 100644 --- a/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.ts +++ b/packages/workspace/src/generators/convert-to-monorepo/convert-to-monorepo.ts @@ -9,6 +9,7 @@ import { writeJson, } from '@nx/devkit'; import { moveGenerator } from '../move/move'; +import { getProjectType } from '../../utils/ts-solution-setup'; export async function monorepoGenerator(tree: Tree, options: {}) { const projects = getProjects(tree); @@ -32,7 +33,9 @@ export async function monorepoGenerator(tree: Tree, options: {}) { // Currently, Nx only handles apps+libs or packages. You cannot mix and match them. // If the standalone project is an app (React, Angular, etc), then use apps+libs. // Otherwise, for TS standalone (lib), use packages. - const isRootProjectApp = rootProject.projectType === 'application'; + const isRootProjectApp = + getProjectType(tree, rootProject.root, rootProject.projectType) === + 'application'; const appsDir = isRootProjectApp ? 'apps' : 'packages'; const libsDir = isRootProjectApp ? 'libs' : 'packages'; @@ -50,11 +53,12 @@ export async function monorepoGenerator(tree: Tree, options: {}) { } for (const project of projectsToMove) { + const projectType = getProjectType(tree, project.root, project.projectType); await moveGenerator(tree, { projectName: project.name, newProjectName: project.name, destination: - project.projectType === 'application' + projectType === 'application' ? joinPathFragments( appsDir, project.root === '.' ? project.name : project.root @@ -63,7 +67,7 @@ export async function monorepoGenerator(tree: Tree, options: {}) { libsDir, project.root === '.' ? project.name : project.root ), - updateImportPath: project.projectType === 'library', + updateImportPath: projectType === 'library', }); } } diff --git a/packages/workspace/src/generators/move/lib/normalize-schema.ts b/packages/workspace/src/generators/move/lib/normalize-schema.ts index 24a9ebc74d505..d4433fc8fa5bf 100644 --- a/packages/workspace/src/generators/move/lib/normalize-schema.ts +++ b/packages/workspace/src/generators/move/lib/normalize-schema.ts @@ -1,7 +1,12 @@ -import { type ProjectConfiguration, type Tree } from '@nx/devkit'; +import { + joinPathFragments, + type ProjectConfiguration, + type Tree, +} from '@nx/devkit'; import { getNpmScope } from '../../../utilities/get-import-path'; import type { NormalizedSchema, Schema } from '../schema'; import { normalizePathSlashes } from './utils'; +import { getProjectType } from '../../../utils/ts-solution-setup'; export async function normalizeSchema( tree: Tree, @@ -35,7 +40,7 @@ async function determineProjectNameAndRootOptions( options: Schema, projectConfiguration: ProjectConfiguration ): Promise { - validateName(options.newProjectName, projectConfiguration); + validateName(tree, options.newProjectName, projectConfiguration); const projectNameAndRootOptions = getProjectNameAndRootOptions( tree, options, @@ -46,6 +51,7 @@ async function determineProjectNameAndRootOptions( } function validateName( + tree: Tree, name: string | undefined, projectConfiguration: ProjectConfiguration ): void { @@ -66,15 +72,26 @@ function validateName( const libraryPattern = '(?:^@[a-zA-Z0-9-*~][a-zA-Z0-9-*._~]*\\/[a-zA-Z0-9-~][a-zA-Z0-9-._~]*|^[a-zA-Z][^:]*)$'; const appPattern = '^[a-zA-Z][^:]*$'; + const projectType = getProjectType( + tree, + projectConfiguration.root, + projectConfiguration.projectType + ); - if (projectConfiguration.projectType === 'application') { + if (projectType === 'application') { const validationRegex = new RegExp(appPattern); if (!validationRegex.test(name)) { throw new Error( `The new project name should match the pattern "${appPattern}". The provided value "${name}" does not match.` ); } - } else if (projectConfiguration.projectType === 'library') { + } else if ( + getProjectType( + tree, + projectConfiguration.root, + projectConfiguration.projectType + ) === 'library' + ) { const validationRegex = new RegExp(libraryPattern); if (!validationRegex.test(name)) { throw new Error( diff --git a/packages/workspace/src/generators/move/lib/utils.ts b/packages/workspace/src/generators/move/lib/utils.ts index 2a54d2cfd1f0b..18b59024e4a60 100644 --- a/packages/workspace/src/generators/move/lib/utils.ts +++ b/packages/workspace/src/generators/move/lib/utils.ts @@ -1,48 +1,4 @@ -import { - getWorkspaceLayout, - joinPathFragments, - normalizePath, - ProjectConfiguration, - Tree, -} from '@nx/devkit'; -import { Schema } from '../schema'; - -/** - * This helper function ensures that we don't move libs or apps - * outside of the folders they should be in. - * - * This will break if someone isn't using the default libs/apps - * folders. In that case, they're on their own :/ - */ -export function getDestination( - host: Tree, - schema: Schema, - project: ProjectConfiguration -): string { - const projectType = project.projectType; - - const workspaceLayout = getWorkspaceLayout(host); - - let rootFolder = workspaceLayout.libsDir; - if (projectType === 'application') { - rootFolder = workspaceLayout.appsDir; - } - return joinPathFragments(rootFolder, schema.destination); -} - -/** - * Joins path segments replacing slashes with dashes - * - * @param path - */ -export function getNewProjectName(path: string): string { - // strip leading '/' or './' or '../' and trailing '/' and replaces '/' with '-' - return normalizePath(path) - .replace(/(^\.{0,2}\/|\.{1,2}\/|\/$)/g, '') - .split('/') - .filter((x) => !!x) - .join('-'); -} +import { normalizePath } from '@nx/devkit'; /** * Normalizes slashes (removes duplicates) diff --git a/packages/workspace/src/utils/ts-solution-setup.ts b/packages/workspace/src/utils/ts-solution-setup.ts new file mode 100644 index 0000000000000..8163e4f0b450e --- /dev/null +++ b/packages/workspace/src/utils/ts-solution-setup.ts @@ -0,0 +1,19 @@ +import { joinPathFragments, readJson, type Tree } from '@nx/devkit'; + +// This is copied from `@nx/js` to avoid circular dependencies. +export function getProjectType( + tree: Tree, + projectRoot: string, + projectType?: 'library' | 'application' +): 'library' | 'application' { + if (projectType) return projectType; + if (joinPathFragments(projectRoot, 'tsconfig.lib.json')) return 'library'; + if (joinPathFragments(projectRoot, 'tsconfig.app.json')) return 'application'; + // If there are no exports, assume it is an application since both buildable and non-buildable libraries have exports. + const packageJsonPath = joinPathFragments(projectRoot, 'package.json'); + const packageJson = tree.exists(packageJsonPath) + ? readJson(tree, joinPathFragments(projectRoot, 'package.json')) + : null; + if (!packageJson?.exports) return 'application'; + return 'library'; +}