diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 406314d179c5f..64c123e10ec0e 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'; @@ -365,8 +366,20 @@ 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; + 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 { @@ -889,7 +902,9 @@ async function normalizeOptions( return { ...options, fileName, - name: projectName, + name: isUsingTsSolutionConfig + ? getImportPath(tree, projectName) + : projectName, projectNames, projectRoot, parsedTags, 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 f000e9cac18d0..cc8c3a8038324 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/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/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/lib/add-project.ts b/packages/react/src/generators/application/lib/add-project.ts index a4cee10a39ac8..a7dad3bcb916d 100644 --- a/packages/react/src/generators/application/lib/add-project.ts +++ b/packages/react/src/generators/application/lib/add-project.ts @@ -40,13 +40,10 @@ 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, }, }); 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/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/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/remix/src/generators/application/application.impl.spec.ts b/packages/remix/src/generators/application/application.impl.spec.ts index 4316a21f4abc3..936171f418cc6 100644 --- a/packages/remix/src/generators/application/application.impl.spec.ts +++ b/packages/remix/src/generators/application/application.impl.spec.ts @@ -339,7 +339,7 @@ describe('Remix Application', () => { it('should add project references when using TS solution', async () => { await applicationGenerator(tree, { - directory: 'myapp', + directory: 'apps/myapp', e2eTestRunner: 'playwright', unitTestRunner: 'jest', addPlugin: true, @@ -347,7 +347,7 @@ describe('Remix Application', () => { }); // Make sure keys are in idiomatic order - expect(Object.keys(readJson(tree, 'myapp/package.json'))) + expect(Object.keys(readJson(tree, 'apps/myapp/package.json'))) .toMatchInlineSnapshot(` [ "name", @@ -361,7 +361,7 @@ describe('Remix Application', () => { "devDependencies", ] `); - expect(readJson(tree, 'myapp/package.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'apps/myapp/package.json')).toMatchInlineSnapshot(` { "dependencies": { "@remix-run/node": "^2.14.0", @@ -379,10 +379,8 @@ describe('Remix Application', () => { "engines": { "node": ">=20", }, - "name": "myapp", + "name": "@proj/myapp", "nx": { - "projectType": "application", - "sourceRoot": "myapp", "tags": [ "foo", ], @@ -397,16 +395,16 @@ describe('Remix Application', () => { expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(` [ { - "path": "./myapp-e2e", + "path": "./apps/myapp-e2e", }, { - "path": "./myapp", + "path": "./apps/myapp", }, ] `); - expect(readJson(tree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'apps/myapp/tsconfig.json')).toMatchInlineSnapshot(` { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [ @@ -419,7 +417,8 @@ describe('Remix Application', () => { ], } `); - expect(readJson(tree, 'myapp/tsconfig.app.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'apps/myapp/tsconfig.app.json')) + .toMatchInlineSnapshot(` { "compilerOptions": { "allowJs": true, @@ -456,14 +455,11 @@ describe('Remix Application', () => { "tests/**/*.test.js", "tests/**/*.spec.jsx", "tests/**/*.test.jsx", - "jest.config.ts", - "src/**/*.spec.ts", - "src/**/*.test.ts", "eslint.config.js", "eslint.config.cjs", "eslint.config.mjs", ], - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "include": [ "app/**/*.ts", "app/**/*.tsx", @@ -476,7 +472,8 @@ describe('Remix Application', () => { ], } `); - expect(readJson(tree, 'myapp/tsconfig.spec.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'apps/myapp/tsconfig.spec.json')) + .toMatchInlineSnapshot(` { "compilerOptions": { "jsx": "react-jsx", @@ -488,7 +485,7 @@ describe('Remix Application', () => { "node", ], }, - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "include": [ "vite.config.ts", "vitest.config.ts", @@ -512,7 +509,8 @@ describe('Remix Application', () => { ], } `); - expect(readJson(tree, 'myapp-e2e/tsconfig.json')).toMatchInlineSnapshot(` + expect(readJson(tree, 'apps/myapp-e2e/tsconfig.json')) + .toMatchInlineSnapshot(` { "compilerOptions": { "allowJs": true, @@ -526,7 +524,7 @@ describe('Remix Application', () => { "eslint.config.mjs", "eslint.config.cjs", ], - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.base.json", "include": [ "**/*.ts", "**/*.js", @@ -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 afc8f4cead7cb..f31b7887a2402 100644 --- a/packages/remix/src/generators/application/lib/normalize-options.ts +++ b/packages/remix/src/generators/application/lib/normalize-options.ts @@ -5,6 +5,8 @@ import { } from '@nx/devkit/src/generators/project-name-and-root-utils'; import { type NxRemixGeneratorSchema } from '../schema'; import { Linter } from '@nx/eslint'; +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 NxRemixGeneratorSchema { projectName: string; @@ -12,6 +14,7 @@ export interface NormalizedSchema extends NxRemixGeneratorSchema { e2eProjectName: string; e2eProjectRoot: string; parsedTags: string[]; + isUsingTsSolutionConfig: boolean; } export async function normalizeOptions( @@ -42,13 +45,17 @@ 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, + 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/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,