From 8bd0bcdd97d59c3a56595ed7115dda8eb5043387 Mon Sep 17 00:00:00 2001 From: Jack Hsu Date: Wed, 5 Feb 2025 16:27:56 -0500 Subject: [PATCH] fix(js): generate pacakge.json for non-buildable nest and expo libs (#29891) This PR fixes a couple of issues for TS solution setup: 1. Expo library should generate with correct `package.json` file (e.g. `exports` maps either to source or dist). See [spec file](https://github.com/nrwl/nx/pull/29891/files#diff-ae2eb3d10d58786c17aa21f5603043b68043faaebafaec77912f3d69ac0c5295). 2. Nest library should generate `package.json` when non-buildable. See [spec file](https://github.com/nrwl/nx/pull/29891/files#diff-368467bcd2215def98ef14aaff9dcb056a915b0a724d0eb857f3a0badef8b40a). **Notes:** - Also removed an unsupported `standaloneConfig` option from `@nx/nest:lib` generator. This was removed a long time ago in other generators. - Expo lib generator isn't crystalized when using Rollup for build. This is a separate issue and we'll handle it in another task. ## Current Behavior - Non-buildable Expo libs generate without `exports` - Buildable Expo libs fail to generate due to error - Non-buildable Nest libs do not generate `package.json` ## Expected Behavior Expo and Nest libs generate correct `package.json` files depending on whether they are build or non-buildable. ## Related Issue(s) Fixes # --- .../packages/nest/generators/library.json | 6 -- .../library/files/lib/package.json.template | 2 +- .../library/lib/normalize-options.ts | 8 +- .../src/generators/library/library.spec.ts | 82 ++++++++++++++++++- .../expo/src/generators/library/library.ts | 51 ++++++++---- .../generators/library/lib/delete-files.ts | 6 +- .../library/lib/normalize-options.ts | 8 +- .../src/generators/library/library.spec.ts | 35 ++++++++ .../nest/src/generators/library/schema.d.ts | 2 +- .../nest/src/generators/library/schema.json | 6 -- 10 files changed, 168 insertions(+), 38 deletions(-) diff --git a/docs/generated/packages/nest/generators/library.json b/docs/generated/packages/nest/generators/library.json index 9a98b70016afd..fd58e65257ad3 100644 --- a/docs/generated/packages/nest/generators/library.json +++ b/docs/generated/packages/nest/generators/library.json @@ -118,12 +118,6 @@ "type": "boolean", "default": true }, - "standaloneConfig": { - "description": "Split the project configuration into /project.json rather than including it inside workspace.json", - "type": "boolean", - "default": true, - "x-deprecated": "Nx only supports standaloneConfig" - }, "setParserOptionsProject": { "type": "boolean", "description": "Whether or not to configure the ESLint \"parserOptions.project\" option. We do not do this by default for lint performance reasons.", diff --git a/packages/expo/src/generators/library/files/lib/package.json.template b/packages/expo/src/generators/library/files/lib/package.json.template index b2da0955b9d0c..659561c71b4ac 100644 --- a/packages/expo/src/generators/library/files/lib/package.json.template +++ b/packages/expo/src/generators/library/files/lib/package.json.template @@ -1,5 +1,5 @@ { - "name": "<%= name %>", + "name": "<%= projectName %>", "version": "0.0.1", "main": "<%= appMain %>" } diff --git a/packages/expo/src/generators/library/lib/normalize-options.ts b/packages/expo/src/generators/library/lib/normalize-options.ts index e0c37f2e84515..fbdfd1892b5f4 100644 --- a/packages/expo/src/generators/library/lib/normalize-options.ts +++ b/packages/expo/src/generators/library/lib/normalize-options.ts @@ -5,10 +5,12 @@ 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; fileName: string; + projectName: string; projectRoot: string; routePath: string; parsedTags: string[]; @@ -43,16 +45,20 @@ export async function normalizeOptions( : []; const appMain = options.js ? 'src/index.js' : 'src/index.ts'; + const isUsingTsSolutionConfig = isUsingTsSolutionSetup(host); const normalized: NormalizedSchema = { ...options, fileName: projectName, routePath: `/${projectNames.projectSimpleName}`, name: projectName, + projectName: isUsingTsSolutionConfig + ? getImportPath(host, projectName) + : projectName, projectRoot, parsedTags, importPath, appMain, - isUsingTsSolutionConfig: isUsingTsSolutionSetup(host), + isUsingTsSolutionConfig, }; return normalized; diff --git a/packages/expo/src/generators/library/library.spec.ts b/packages/expo/src/generators/library/library.spec.ts index 27401b95d4d8c..cef22923eac39 100644 --- a/packages/expo/src/generators/library/library.spec.ts +++ b/packages/expo/src/generators/library/library.spec.ts @@ -504,6 +504,22 @@ describe('lib', () => { ] `); // Make sure keys are in idiomatic order + expect(readJson(appTree, 'my-lib/package.json')).toMatchInlineSnapshot(` + { + "exports": { + ".": { + "default": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts", + }, + "./package.json": "./package.json", + }, + "main": "./src/index.ts", + "name": "@proj/my-lib", + "types": "./src/index.ts", + "version": "0.0.1", + } + `); expect(Object.keys(readJson(appTree, 'my-lib/package.json'))) .toMatchInlineSnapshot(` [ @@ -511,7 +527,7 @@ describe('lib', () => { "version", "main", "types", - "nx", + "exports", ] `); expect(readJson(appTree, 'my-lib/tsconfig.json')).toMatchInlineSnapshot(` @@ -608,5 +624,69 @@ describe('lib', () => { } `); }); + + it('should generate buildable library', async () => { + await expoLibraryGenerator(appTree, { + ...defaultSchema, + buildable: true, + strict: false, + }); + + expect(readJson(appTree, 'my-lib/package.json')).toMatchInlineSnapshot(` + { + "exports": { + ".": { + "default": "./dist/index.esm.js", + "import": "./dist/index.esm.js", + "types": "./dist/index.esm.d.ts", + }, + "./package.json": "./package.json", + }, + "main": "./dist/index.esm.js", + "module": "./dist/index.esm.js", + "name": "@proj/my-lib", + "nx": { + "projectType": "library", + "sourceRoot": "my-lib/src", + "tags": [], + "targets": { + "build": { + "executor": "@nx/rollup:rollup", + "options": { + "assets": [ + { + "glob": "my-lib/README.md", + "input": ".", + "output": ".", + }, + ], + "entryFile": "my-lib/src/index.ts", + "external": [ + "react/jsx-runtime", + "react-native", + "react", + "react-dom", + ], + "outputPath": "dist/my-lib", + "project": "my-lib/package.json", + "rollupConfig": "@nx/react/plugins/bundle-rollup", + "tsConfig": "my-lib/tsconfig.lib.json", + }, + "outputs": [ + "{options.outputPath}", + ], + }, + }, + }, + "peerDependencies": { + "react": "~18.3.1", + "react-native": "0.76.3", + }, + "type": "module", + "types": "./dist/index.esm.d.ts", + "version": "0.0.1", + } + `); + }); }); }); diff --git a/packages/expo/src/generators/library/library.ts b/packages/expo/src/generators/library/library.ts index 5873decb5a750..1b49232acd12d 100644 --- a/packages/expo/src/generators/library/library.ts +++ b/packages/expo/src/generators/library/library.ts @@ -40,7 +40,6 @@ import { addProjectToTsSolutionWorkspace, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; -import { getImportPath } from '@nx/js/src/utils/get-import-path'; import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function expoLibraryGenerator( @@ -72,6 +71,10 @@ export async function expoLibraryGeneratorInternal( ); } + if (options.isUsingTsSolutionConfig) { + addProjectToTsSolutionWorkspace(host, options.projectRoot); + } + const initTask = await init(host, { ...options, skipFormat: true }); tasks.push(initTask); if (!options.skipPackageJson) { @@ -88,7 +91,7 @@ export async function expoLibraryGeneratorInternal( const lintTask = await addLinting(host, { ...options, - projectName: options.name, + projectName: options.projectName, tsConfigPaths: [ joinPathFragments(options.projectRoot, 'tsconfig.lib.json'), ], @@ -98,7 +101,7 @@ export async function expoLibraryGeneratorInternal( const jestTask = await addJest( host, options.unitTestRunner, - options.name, + options.projectName, options.projectRoot, options.js, options.skipPackageJson, @@ -134,10 +137,6 @@ export async function expoLibraryGeneratorInternal( : undefined ); - if (options.isUsingTsSolutionConfig) { - addProjectToTsSolutionWorkspace(host, options.projectRoot); - } - sortPackageJsonFields(host, options.projectRoot); if (!options.skipFormat) { @@ -169,23 +168,35 @@ async function addProject( }; if (options.isUsingTsSolutionConfig) { - const packageName = getImportPath(host, options.name); const sourceEntry = !options.buildable ? options.js ? './src/index.js' : './src/index.ts' : undefined; writeJson(host, joinPathFragments(options.projectRoot, 'package.json'), { - name: packageName, + name: options.projectName, version: '0.0.1', main: sourceEntry, types: sourceEntry, - nx: { - name: packageName === options.name ? undefined : options.name, - projectType: 'library', - sourceRoot: joinPathFragments(options.projectRoot, 'src'), - tags: options.parsedTags?.length ? options.parsedTags : undefined, - }, + // For buildable libraries, the entries are configured by the bundler (i.e. Rollup). + exports: options.buildable + ? undefined + : { + './package.json': './package.json', + '.': options.js + ? './src/index.js' + : { + types: './src/index.ts', + import: './src/index.ts', + default: './src/index.ts', + }, + }, + + nx: options.parsedTags?.length + ? { + tags: options.parsedTags, + } + : undefined, }); } else { addProjectConfiguration(host, options.name, project); @@ -201,7 +212,7 @@ async function addProject( ); const rollupConfigTask = await configurationGenerator(host, { ...options, - project: options.name, + project: options.projectName, skipFormat: true, }); @@ -229,7 +240,7 @@ async function addProject( }, }; - updateProjectConfiguration(host, options.name, project); + updateProjectConfiguration(host, options.projectName, project); return rollupConfigTask; } @@ -271,7 +282,11 @@ function createFiles(host: Tree, options: NormalizedSchema) { } ); - if (!options.publishable && !options.buildable) { + if ( + !options.publishable && + !options.buildable && + !options.isUsingTsSolutionConfig + ) { host.delete(`${options.projectRoot}/package.json`); } diff --git a/packages/nest/src/generators/library/lib/delete-files.ts b/packages/nest/src/generators/library/lib/delete-files.ts index 6565d7256f1ed..245ea04b125d0 100644 --- a/packages/nest/src/generators/library/lib/delete-files.ts +++ b/packages/nest/src/generators/library/lib/delete-files.ts @@ -23,7 +23,11 @@ export function deleteFiles(tree: Tree, options: NormalizedOptions): void { ); } - if (!options.buildable && !options.publishable) { + if ( + !options.buildable && + !options.publishable && + !options.isUsingTsSolutionsConfig + ) { tree.delete(joinPathFragments(options.projectRoot, 'package.json')); } } diff --git a/packages/nest/src/generators/library/lib/normalize-options.ts b/packages/nest/src/generators/library/lib/normalize-options.ts index df1ad4a9a8627..cc89b7bee56b8 100644 --- a/packages/nest/src/generators/library/lib/normalize-options.ts +++ b/packages/nest/src/generators/library/lib/normalize-options.ts @@ -40,6 +40,7 @@ export async function normalizeOptions( ? options.tags.split(',').map((s) => s.trim()) : []; + const isUsingTsSolutionsConfig = isUsingTsSolutionSetup(tree); const normalized: NormalizedOptions = { ...options, strict: options.strict ?? true, @@ -49,7 +50,7 @@ export async function normalizeOptions( linter: options.linter ?? Linter.EsLint, parsedTags, prefix: getNpmScope(tree), // we could also allow customizing this - projectName: isUsingTsSolutionSetup(tree) + projectName: isUsingTsSolutionsConfig ? getImportPath(tree, projectName) : projectName, projectRoot, @@ -58,13 +59,14 @@ export async function normalizeOptions( target: options.target ?? 'es6', testEnvironment: options.testEnvironment ?? 'node', unitTestRunner: options.unitTestRunner ?? 'jest', + isUsingTsSolutionsConfig, }; return normalized; } export function toJsLibraryGeneratorOptions( - options: LibraryGeneratorOptions + options: NormalizedOptions ): JsLibraryGeneratorSchema { return { name: options.name, @@ -80,8 +82,8 @@ export function toJsLibraryGeneratorOptions( tags: options.tags, testEnvironment: options.testEnvironment, unitTestRunner: options.unitTestRunner, - config: options.standaloneConfig ? 'project' : 'workspace', setParserOptionsProject: options.setParserOptionsProject, addPlugin: options.addPlugin, + useProjectJson: !options.isUsingTsSolutionsConfig, }; } diff --git a/packages/nest/src/generators/library/library.spec.ts b/packages/nest/src/generators/library/library.spec.ts index a67dc159f8ebb..795fa67cfa808 100644 --- a/packages/nest/src/generators/library/library.spec.ts +++ b/packages/nest/src/generators/library/library.spec.ts @@ -379,6 +379,41 @@ describe('lib', () => { }, ] `); + expect(readJson(tree, 'mylib/package.json')).toMatchInlineSnapshot(` + { + "dependencies": {}, + "exports": { + ".": { + "default": "./src/index.ts", + "import": "./src/index.ts", + "types": "./src/index.ts", + }, + "./package.json": "./package.json", + }, + "main": "./src/index.ts", + "name": "@proj/mylib", + "nx": { + "targets": { + "lint": { + "executor": "@nx/eslint:lint", + }, + "test": { + "executor": "@nx/jest:jest", + "options": { + "jestConfig": "mylib/jest.config.ts", + }, + "outputs": [ + "{projectRoot}/test-output/jest/coverage", + ], + }, + }, + }, + "private": true, + "type": "module", + "types": "./src/index.ts", + "version": "0.0.1", + } + `); expect(readJson(tree, 'mylib/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/nest/src/generators/library/schema.d.ts b/packages/nest/src/generators/library/schema.d.ts index 1339b791f8cb6..63e6978b0b6fa 100644 --- a/packages/nest/src/generators/library/schema.d.ts +++ b/packages/nest/src/generators/library/schema.d.ts @@ -28,11 +28,11 @@ export interface LibraryGeneratorOptions { | 'es2021'; testEnvironment?: 'jsdom' | 'node'; unitTestRunner?: UnitTestRunner; - standaloneConfig?: boolean; setParserOptionsProject?: boolean; skipPackageJson?: boolean; simpleName?: boolean; addPlugin?: boolean; + isUsingTsSolutionsConfig?: boolean; } export interface NormalizedOptions extends LibraryGeneratorOptions { diff --git a/packages/nest/src/generators/library/schema.json b/packages/nest/src/generators/library/schema.json index 5ad79a7373f6f..4d1852297b5f3 100644 --- a/packages/nest/src/generators/library/schema.json +++ b/packages/nest/src/generators/library/schema.json @@ -118,12 +118,6 @@ "type": "boolean", "default": true }, - "standaloneConfig": { - "description": "Split the project configuration into /project.json rather than including it inside workspace.json", - "type": "boolean", - "default": true, - "x-deprecated": "Nx only supports standaloneConfig" - }, "setParserOptionsProject": { "type": "boolean", "description": "Whether or not to configure the ESLint \"parserOptions.project\" option. We do not do this by default for lint performance reasons.",