diff --git a/packages/expo/src/generators/application/application.spec.ts b/packages/expo/src/generators/application/application.spec.ts index 1e7a2f67b4..b4f0298264 100644 --- a/packages/expo/src/generators/application/application.spec.ts +++ b/packages/expo/src/generators/application/application.spec.ts @@ -368,6 +368,16 @@ describe('app', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'my-app/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + ] + `); expect(readJson(tree, 'my-app/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/expo/src/generators/application/application.ts b/packages/expo/src/generators/application/application.ts index a854ca39f2..1bbb0485da 100644 --- a/packages/expo/src/generators/application/application.ts +++ b/packages/expo/src/generators/application/application.ts @@ -24,6 +24,7 @@ import { Schema } from './schema'; import { ensureDependencies } from '../../utils/ensure-dependencies'; import { initRootBabelConfig } from '../../utils/init-root-babel-config'; import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function expoApplicationGenerator( host: Tree, @@ -104,6 +105,8 @@ export async function expoApplicationGeneratorInternal( addProjectToTsSolutionWorkspace(host, options.appProjectRoot); } + sortPackageJsonFields(host, options.appProjectRoot); + if (!options.skipFormat) { await formatFiles(host); } diff --git a/packages/expo/src/generators/library/library.spec.ts b/packages/expo/src/generators/library/library.spec.ts index 3e08a40af6..27401b95d4 100644 --- a/packages/expo/src/generators/library/library.spec.ts +++ b/packages/expo/src/generators/library/library.spec.ts @@ -6,6 +6,7 @@ import { readProjectConfiguration, Tree, updateJson, + writeJson, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; @@ -468,4 +469,144 @@ describe('lib', () => { ).not.toBeDefined(); }); }); + + describe('TS solution setup', () => { + beforeEach(() => { + updateJson(appTree, 'package.json', (json) => { + json.workspaces = ['packages/*', 'apps/*']; + return json; + }); + writeJson(appTree, 'tsconfig.base.json', { + compilerOptions: { + composite: true, + declaration: true, + }, + }); + writeJson(appTree, 'tsconfig.json', { + extends: './tsconfig.base.json', + files: [], + references: [], + }); + }); + + it('should add project references when using TS solution', async () => { + await expoLibraryGenerator(appTree, { + ...defaultSchema, + strict: false, + }); + + expect(readJson(appTree, 'tsconfig.json').references) + .toMatchInlineSnapshot(` + [ + { + "path": "./my-lib", + }, + ] + `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(appTree, 'my-lib/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "main", + "types", + "nx", + ] + `); + expect(readJson(appTree, 'my-lib/tsconfig.json')).toMatchInlineSnapshot(` + { + "extends": "../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + { + "path": "./tsconfig.spec.json", + }, + ], + } + `); + expect(readJson(appTree, 'my-lib/tsconfig.lib.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "out-tsc/my-lib", + "rootDir": "src", + "tsBuildInfoFile": "out-tsc/my-lib/tsconfig.lib.tsbuildinfo", + "types": [ + "node", + ], + }, + "exclude": [ + "out-tsc", + "dist", + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "src/test-setup.ts", + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "eslint.config.js", + "eslint.config.cjs", + "eslint.config.mjs", + ], + "extends": "../tsconfig.base.json", + "include": [ + "**/*.js", + "**/*.jsx", + "**/*.ts", + "**/*.tsx", + ], + } + `); + expect(readJson(appTree, 'my-lib/tsconfig.spec.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "./out-tsc/jest", + "types": [ + "jest", + "node", + ], + }, + "extends": "../tsconfig.base.json", + "files": [ + "src/test-setup.ts", + ], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + ], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + ], + } + `); + }); + }); }); diff --git a/packages/expo/src/generators/library/library.ts b/packages/expo/src/generators/library/library.ts index 79d5313299..5873decb5a 100644 --- a/packages/expo/src/generators/library/library.ts +++ b/packages/expo/src/generators/library/library.ts @@ -41,6 +41,7 @@ import { 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( host: Tree, @@ -137,6 +138,8 @@ export async function expoLibraryGeneratorInternal( addProjectToTsSolutionWorkspace(host, options.projectRoot); } + sortPackageJsonFields(host, options.projectRoot); + if (!options.skipFormat) { await formatFiles(host); } diff --git a/packages/express/src/generators/application/application.spec.ts b/packages/express/src/generators/application/application.spec.ts index 05eb3e85a9..a9fd9acb39 100644 --- a/packages/express/src/generators/application/application.spec.ts +++ b/packages/express/src/generators/application/application.spec.ts @@ -171,6 +171,16 @@ describe('app', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(appTree, 'myapp/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + ] + `); expect(readJson(appTree, 'myapp/package.json')).toMatchInlineSnapshot(` { "name": "@proj/myapp", diff --git a/packages/js/src/generators/library/library.spec.ts b/packages/js/src/generators/library/library.spec.ts index f1ba983c7c..9c671a5e93 100644 --- a/packages/js/src/generators/library/library.spec.ts +++ b/packages/js/src/generators/library/library.spec.ts @@ -1694,6 +1694,20 @@ describe('lib', () => { linter: 'none', }); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'my-ts-lib/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "type", + "main", + "types", + "exports", + "dependencies", + ] + `); expect(readJson(tree, 'my-ts-lib/package.json')).toMatchInlineSnapshot(` { "dependencies": {}, diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 94714ae4c3..283c8ef816 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -67,6 +67,7 @@ import type { LibraryGeneratorSchema, NormalizedLibraryGeneratorOptions, } from './schema'; +import { sortPackageJsonFields } from '../../utils/package-json/sort-fields'; const defaultOutputDirectory = 'dist'; @@ -222,6 +223,8 @@ export async function libraryGeneratorInternal( addProjectToTsSolutionWorkspace(tree, options.projectRoot); } + sortPackageJsonFields(tree, options.projectRoot); + if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/js/src/utils/package-json/sort-fields.ts b/packages/js/src/utils/package-json/sort-fields.ts new file mode 100644 index 0000000000..c268d07e8f --- /dev/null +++ b/packages/js/src/utils/package-json/sort-fields.ts @@ -0,0 +1,41 @@ +import { joinPathFragments, updateJson, type Tree } from '@nx/devkit'; + +export function sortPackageJsonFields(tree: Tree, projectRoot: string) { + const packageJsonPath = joinPathFragments(projectRoot, 'package.json'); + if (!tree.exists(packageJsonPath)) return; + updateJson(tree, packageJsonPath, (json) => { + // Note that these are fields that our generators may use, so it's not exhaustive. + const orderedTopFields = new Set([ + 'name', + 'version', + 'private', + 'description', + 'type', + 'main', + 'module', + 'types', + 'exported', + ]); + const orderedBottomFields = new Set([ + 'dependencies', + 'devDependencies', + 'peerDependencies', + 'optionalDependencies', + ]); + const otherFields = new Set( + Object.keys(json).filter( + (k) => !orderedTopFields.has(k) && !orderedBottomFields.has(k) + ) + ); + const allFields = [ + ...orderedTopFields, + ...otherFields, + ...orderedBottomFields, + ]; + const sortedJson = {}; + for (const k of allFields) { + sortedJson[k] = json[k]; + } + return sortedJson; + }); +} diff --git a/packages/next/src/generators/application/application.spec.ts b/packages/next/src/generators/application/application.spec.ts index 2ea0cbbdcd..6eb9567290 100644 --- a/packages/next/src/generators/application/application.spec.ts +++ b/packages/next/src/generators/application/application.spec.ts @@ -917,6 +917,17 @@ describe('app (legacy)', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'myapp/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + "dependencies", + ] + `); expect(readJson(tree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(` { "compilerOptions": { diff --git a/packages/next/src/generators/application/application.ts b/packages/next/src/generators/application/application.ts index e9b3adb33f..9f72e7a003 100644 --- a/packages/next/src/generators/application/application.ts +++ b/packages/next/src/generators/application/application.ts @@ -34,6 +34,7 @@ import { addProjectToTsSolutionWorkspace, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function applicationGenerator(host: Tree, schema: Schema) { return await applicationGeneratorInternal(host, { @@ -141,6 +142,8 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) { addProjectToTsSolutionWorkspace(host, options.appProjectRoot); } + sortPackageJsonFields(host, options.appProjectRoot); + if (!options.skipFormat) { await formatFiles(host); } diff --git a/packages/next/src/generators/library/library.spec.ts b/packages/next/src/generators/library/library.spec.ts index f1f42cc9a1..3a74083c7d 100644 --- a/packages/next/src/generators/library/library.spec.ts +++ b/packages/next/src/generators/library/library.spec.ts @@ -1,5 +1,5 @@ import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version'; -import { readJson } from '@nx/devkit'; +import { readJson, Tree, updateJson, writeJson } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import { Linter } from '@nx/eslint'; import libraryGenerator from './library'; @@ -113,4 +113,148 @@ describe('next library', () => { readJson(appTree, 'package.json').devDependencies['cypress'] ).toBeUndefined(); }); + + describe('TS solution setup', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + updateJson(tree, 'package.json', (json) => { + json.workspaces = ['packages/*', 'apps/*']; + return json; + }); + writeJson(tree, 'tsconfig.base.json', { + compilerOptions: { + composite: true, + declaration: true, + }, + }); + writeJson(tree, 'tsconfig.json', { + extends: './tsconfig.base.json', + files: [], + references: [], + }); + }); + + it('should add project references when using TS solution', async () => { + await libraryGenerator(tree, { + directory: 'mylib', + linter: Linter.EsLint, + skipFormat: false, + skipTsConfig: false, + unitTestRunner: 'jest', + style: 'css', + component: false, + }); + + expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(` + [ + { + "path": "./mylib", + }, + ] + `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'mylib/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "main", + "types", + "exports", + "nx", + ] + `); + expect(readJson(tree, 'mylib/tsconfig.json')).toMatchInlineSnapshot(` + { + "extends": "../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + { + "path": "./tsconfig.spec.json", + }, + ], + } + `); + expect(readJson(tree, 'mylib/tsconfig.lib.json')).toMatchInlineSnapshot(` + { + "compilerOptions": { + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "out-tsc/mylib", + "rootDir": "src", + "tsBuildInfoFile": "out-tsc/mylib/tsconfig.lib.tsbuildinfo", + "types": [ + "node", + "@nx/react/typings/cssmodule.d.ts", + "@nx/react/typings/image.d.ts", + "next", + "@nx/next/typings/image.d.ts", + ], + }, + "exclude": [ + "out-tsc", + "dist", + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.spec.tsx", + "src/**/*.test.tsx", + "src/**/*.spec.js", + "src/**/*.test.js", + "src/**/*.spec.jsx", + "src/**/*.test.jsx", + "eslint.config.js", + "eslint.config.cjs", + "eslint.config.mjs", + ], + "extends": "../tsconfig.base.json", + "include": [ + "src/**/*.js", + "src/**/*.jsx", + "src/**/*.ts", + "src/**/*.tsx", + ], + } + `); + expect(readJson(tree, 'mylib/tsconfig.spec.json')).toMatchInlineSnapshot(` + { + "compilerOptions": { + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "./out-tsc/jest", + "types": [ + "jest", + "node", + ], + }, + "extends": "../tsconfig.base.json", + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + ], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + ], + } + `); + }); + }); }); diff --git a/packages/next/src/generators/library/library.ts b/packages/next/src/generators/library/library.ts index 0fa2954600..9686e2b6db 100644 --- a/packages/next/src/generators/library/library.ts +++ b/packages/next/src/generators/library/library.ts @@ -20,6 +20,7 @@ import { addProjectToTsSolutionWorkspace, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function libraryGenerator(host: Tree, rawOptions: Schema) { return await libraryGeneratorInternal(host, { @@ -166,6 +167,8 @@ export async function libraryGeneratorInternal(host: Tree, rawOptions: Schema) { addProjectToTsSolutionWorkspace(host, options.projectRoot); } + sortPackageJsonFields(host, options.projectRoot); + if (!options.skipFormat) { await formatFiles(host); } diff --git a/packages/node/src/generators/application/application.spec.ts b/packages/node/src/generators/application/application.spec.ts index 129b25882c..d564c3859f 100644 --- a/packages/node/src/generators/application/application.spec.ts +++ b/packages/node/src/generators/application/application.spec.ts @@ -599,6 +599,16 @@ describe('app', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'myapp/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + ] + `); expect(readJson(tree, 'myapp/package.json')).toMatchInlineSnapshot(` { "name": "@proj/myapp", diff --git a/packages/node/src/generators/application/application.ts b/packages/node/src/generators/application/application.ts index dabfa8bc4e..5a25fd3528 100644 --- a/packages/node/src/generators/application/application.ts +++ b/packages/node/src/generators/application/application.ts @@ -60,6 +60,7 @@ import { isUsingTsSolutionSetup, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export interface NormalizedSchema extends Schema { appProjectRoot: string; @@ -594,6 +595,8 @@ export async function applicationGeneratorInternal(tree: Tree, schema: Schema) { addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); } + sortPackageJsonFields(tree, options.appProjectRoot); + if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/node/src/generators/library/library.spec.ts b/packages/node/src/generators/library/library.spec.ts index 0482b474d6..f000e9cac1 100644 --- a/packages/node/src/generators/library/library.spec.ts +++ b/packages/node/src/generators/library/library.spec.ts @@ -553,6 +553,20 @@ describe('lib', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'mylib/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "main", + "types", + "exports", + "nx", + "dependencies", + ] + `); expect(readJson(tree, 'mylib/package.json')).toMatchInlineSnapshot(` { "dependencies": {}, diff --git a/packages/node/src/generators/library/library.ts b/packages/node/src/generators/library/library.ts index 1f8bb82480..0d7530098c 100644 --- a/packages/node/src/generators/library/library.ts +++ b/packages/node/src/generators/library/library.ts @@ -34,6 +34,7 @@ import { isUsingTsSolutionSetup, } 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 interface NormalizedSchema extends Schema { fileName: string; @@ -117,6 +118,8 @@ export async function libraryGeneratorInternal(tree: Tree, schema: Schema) { addProjectToTsSolutionWorkspace(tree, options.projectRoot); } + sortPackageJsonFields(tree, options.projectRoot); + if (!schema.skipFormat) { await formatFiles(tree); } diff --git a/packages/nuxt/src/generators/application/application.spec.ts b/packages/nuxt/src/generators/application/application.spec.ts index aece521c02..f8ffb47a4b 100644 --- a/packages/nuxt/src/generators/application/application.spec.ts +++ b/packages/nuxt/src/generators/application/application.spec.ts @@ -242,6 +242,16 @@ describe('app', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'myapp/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + ] + `); expect(readJson(tree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/nuxt/src/generators/application/application.ts b/packages/nuxt/src/generators/application/application.ts index 95b7de45ec..f196a17339 100644 --- a/packages/nuxt/src/generators/application/application.ts +++ b/packages/nuxt/src/generators/application/application.ts @@ -37,6 +37,7 @@ import { addProjectToTsSolutionWorkspace, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function applicationGenerator(tree: Tree, schema: Schema) { const tasks: GeneratorCallback[] = []; @@ -197,6 +198,8 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); } + sortPackageJsonFields(tree, options.appProjectRoot); + if (!options.skipFormat) await formatFiles(tree); tasks.push(() => { diff --git a/packages/react-native/src/generators/application/application.spec.ts b/packages/react-native/src/generators/application/application.spec.ts index 98eefd74ca..678fa25c18 100644 --- a/packages/react-native/src/generators/application/application.spec.ts +++ b/packages/react-native/src/generators/application/application.spec.ts @@ -291,6 +291,16 @@ describe('app', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'my-app/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + ] + `); expect(readJson(tree, 'my-app/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/react-native/src/generators/application/application.ts b/packages/react-native/src/generators/application/application.ts index bec9a0421c..f4a719ec29 100644 --- a/packages/react-native/src/generators/application/application.ts +++ b/packages/react-native/src/generators/application/application.ts @@ -29,6 +29,7 @@ import { addProjectToTsSolutionWorkspace, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function reactNativeApplicationGenerator( host: Tree, @@ -152,6 +153,8 @@ export async function reactNativeApplicationGeneratorInternal( addProjectToTsSolutionWorkspace(host, options.appProjectRoot); } + sortPackageJsonFields(host, options.appProjectRoot); + if (!options.skipFormat) { await formatFiles(host); } diff --git a/packages/react-native/src/generators/library/library.spec.ts b/packages/react-native/src/generators/library/library.spec.ts index 1cb5a433aa..f7faceabf0 100644 --- a/packages/react-native/src/generators/library/library.spec.ts +++ b/packages/react-native/src/generators/library/library.spec.ts @@ -5,6 +5,7 @@ import { readProjectConfiguration, Tree, updateJson, + writeJson, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import libraryGenerator from './library'; @@ -459,4 +460,140 @@ describe('lib', () => { expect(readJson(appTree, 'package.json')).toEqual(packageJsonBefore); }); }); + + describe('TS solution setup', () => { + it('should add project references when using TS solution', async () => { + updateJson(appTree, 'package.json', (json) => { + json.workspaces = ['packages/*', 'apps/*']; + return json; + }); + writeJson(appTree, 'tsconfig.base.json', { + compilerOptions: { + composite: true, + declaration: true, + }, + }); + writeJson(appTree, 'tsconfig.json', { + extends: './tsconfig.base.json', + files: [], + references: [], + }); + + await libraryGenerator(appTree, { + ...defaultSchema, + }); + + expect(readJson(appTree, 'tsconfig.json').references) + .toMatchInlineSnapshot(` + [ + { + "path": "./my-lib", + }, + ] + `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(appTree, 'my-lib/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "main", + "types", + "exports", + "nx", + ] + `); + expect(readJson(appTree, 'my-lib/tsconfig.json')).toMatchInlineSnapshot(` + { + "extends": "../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + { + "path": "./tsconfig.spec.json", + }, + ], + } + `); + expect(readJson(appTree, 'my-lib/tsconfig.lib.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "out-tsc/my-lib", + "rootDir": "src", + "tsBuildInfoFile": "out-tsc/my-lib/tsconfig.lib.tsbuildinfo", + "types": [ + "node", + ], + }, + "exclude": [ + "out-tsc", + "dist", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/test-setup.ts", + "jest.config.ts", + "eslint.config.js", + "eslint.config.cjs", + "eslint.config.mjs", + ], + "extends": "../tsconfig.base.json", + "include": [ + "src/**/*.js", + "src/**/*.jsx", + "src/**/*.ts", + "src/**/*.tsx", + ], + } + `); + expect(readJson(appTree, 'my-lib/tsconfig.spec.json')) + .toMatchInlineSnapshot(` + { + "compilerOptions": { + "jsx": "react-jsx", + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "./out-tsc/jest", + "types": [ + "jest", + "node", + ], + }, + "extends": "../tsconfig.base.json", + "files": [ + "src/test-setup.ts", + ], + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts", + ], + "references": [ + { + "path": "./tsconfig.lib.json", + }, + ], + } + `); + }); + }); }); diff --git a/packages/react-native/src/generators/library/library.ts b/packages/react-native/src/generators/library/library.ts index a92e719ec3..62d50dee2d 100644 --- a/packages/react-native/src/generators/library/library.ts +++ b/packages/react-native/src/generators/library/library.ts @@ -40,6 +40,7 @@ import { } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { getImportPath } from '@nx/js/src/utils/get-import-path'; import type { PackageJson } from 'nx/src/utils/package-json'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function reactNativeLibraryGenerator( host: Tree, @@ -135,6 +136,8 @@ export async function reactNativeLibraryGeneratorInternal( addProjectToTsSolutionWorkspace(host, options.projectRoot); } + sortPackageJsonFields(host, options.projectRoot); + if (!options.skipFormat) { await formatFiles(host); } diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 2afd9bda82..41939096b5 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -1314,6 +1314,16 @@ describe('app', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(appTree, 'myapp/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + ] + `); expect(readJson(appTree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/react/src/generators/application/application.ts b/packages/react/src/generators/application/application.ts index 01ba1dcdd1..088e61630e 100644 --- a/packages/react/src/generators/application/application.ts +++ b/packages/react/src/generators/application/application.ts @@ -43,6 +43,7 @@ import { setupVitestConfiguration, } from './lib/bundlers/add-vite'; import { Schema } from './schema'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function applicationGenerator( tree: Tree, @@ -183,6 +184,8 @@ export async function applicationGeneratorInternal( addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); } + sortPackageJsonFields(tree, options.appProjectRoot); + if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index ab4ad38f58..5125f61fe1 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -1017,6 +1017,20 @@ module.exports = withNx( }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'mylib/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "type", + "main", + "module", + "types", + "nx", + "exports", + ] + `); expect(readJson(tree, 'mylib/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/react/src/generators/library/library.ts b/packages/react/src/generators/library/library.ts index 5ee6d3c2aa..9ddef16ad3 100644 --- a/packages/react/src/generators/library/library.ts +++ b/packages/react/src/generators/library/library.ts @@ -36,6 +36,7 @@ import { updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { determineEntryFields } from './lib/determine-entry-fields'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function libraryGenerator(host: Tree, schema: Schema) { return await libraryGeneratorInternal(host, { @@ -275,6 +276,9 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) { if (options.isUsingTsSolutionConfig) { addProjectToTsSolutionWorkspace(host, options.projectRoot); } + + sortPackageJsonFields(host, options.projectRoot); + if (!options.skipFormat) { await formatFiles(host); } diff --git a/packages/remix/src/generators/application/application.impl.spec.ts b/packages/remix/src/generators/application/application.impl.spec.ts index 4d3ca33870..4316a21f4a 100644 --- a/packages/remix/src/generators/application/application.impl.spec.ts +++ b/packages/remix/src/generators/application/application.impl.spec.ts @@ -346,6 +346,21 @@ describe('Remix Application', () => { tags: 'foo', }); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'myapp/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "private", + "type", + "scripts", + "engines", + "sideEffects", + "nx", + "dependencies", + "devDependencies", + ] + `); expect(readJson(tree, 'myapp/package.json')).toMatchInlineSnapshot(` { "dependencies": { diff --git a/packages/remix/src/generators/application/application.impl.ts b/packages/remix/src/generators/application/application.impl.ts index 040b4bf7bf..aa6c302d84 100644 --- a/packages/remix/src/generators/application/application.impl.ts +++ b/packages/remix/src/generators/application/application.impl.ts @@ -49,6 +49,7 @@ import { isUsingTsSolutionSetup, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export function remixApplicationGenerator( tree: Tree, @@ -332,6 +333,8 @@ export default {...nxPreset}; addProjectToTsSolutionWorkspace(tree, options.projectRoot); } + sortPackageJsonFields(tree, options.projectRoot); + if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/remix/src/generators/library/library.impl.spec.ts b/packages/remix/src/generators/library/library.impl.spec.ts index bebcebbea7..c514be28a2 100644 --- a/packages/remix/src/generators/library/library.impl.spec.ts +++ b/packages/remix/src/generators/library/library.impl.spec.ts @@ -164,6 +164,18 @@ describe('Remix Library Generator', () => { addPlugin: true, }); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'packages/foo/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "main", + "types", + "exports", + "nx", + ] + `); expect(readJson(tree, 'packages/foo/package.json')) .toMatchInlineSnapshot(` { diff --git a/packages/remix/src/generators/library/library.impl.ts b/packages/remix/src/generators/library/library.impl.ts index e60b55c836..f218797319 100644 --- a/packages/remix/src/generators/library/library.impl.ts +++ b/packages/remix/src/generators/library/library.impl.ts @@ -13,6 +13,7 @@ import { addProjectToTsSolutionWorkspace, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export async function remixLibraryGenerator( tree: Tree, @@ -80,6 +81,8 @@ export async function remixLibraryGeneratorInternal( addProjectToTsSolutionWorkspace(tree, options.projectRoot); } + sortPackageJsonFields(tree, options.projectRoot); + if (!options.skipFormat) { await formatFiles(tree); } diff --git a/packages/vue/src/generators/application/application.spec.ts b/packages/vue/src/generators/application/application.spec.ts index 5633c6d115..efcd684f89 100644 --- a/packages/vue/src/generators/application/application.spec.ts +++ b/packages/vue/src/generators/application/application.spec.ts @@ -244,6 +244,16 @@ describe('application generator', () => { }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'test/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "nx", + ] + `); expect(readJson(tree, 'test/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/vue/src/generators/application/application.ts b/packages/vue/src/generators/application/application.ts index 64b6a09dac..0ee86fcbb0 100644 --- a/packages/vue/src/generators/application/application.ts +++ b/packages/vue/src/generators/application/application.ts @@ -27,6 +27,7 @@ import { addProjectToTsSolutionWorkspace, updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export function applicationGenerator(tree: Tree, options: Schema) { return applicationGeneratorInternal(tree, { addPlugin: false, ...options }); @@ -150,6 +151,8 @@ export async function applicationGeneratorInternal( addProjectToTsSolutionWorkspace(tree, options.appProjectRoot); } + sortPackageJsonFields(tree, options.appProjectRoot); + if (!options.skipFormat) await formatFiles(tree); tasks.push(() => { diff --git a/packages/vue/src/generators/library/library.spec.ts b/packages/vue/src/generators/library/library.spec.ts index 73ee4dea02..d324c68271 100644 --- a/packages/vue/src/generators/library/library.spec.ts +++ b/packages/vue/src/generators/library/library.spec.ts @@ -501,6 +501,19 @@ module.exports = [ }, ] `); + // Make sure keys are in idiomatic order + expect(Object.keys(readJson(tree, 'my-lib/package.json'))) + .toMatchInlineSnapshot(` + [ + "name", + "version", + "private", + "module", + "types", + "exports", + "nx", + ] + `); expect(readJson(tree, 'my-lib/tsconfig.json')).toMatchInlineSnapshot(` { "extends": "../tsconfig.base.json", diff --git a/packages/vue/src/generators/library/library.ts b/packages/vue/src/generators/library/library.ts index de3386677e..036027e10c 100644 --- a/packages/vue/src/generators/library/library.ts +++ b/packages/vue/src/generators/library/library.ts @@ -29,6 +29,7 @@ import { updateTsconfigFiles, } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { determineEntryFields } from './lib/determine-entry-fields'; +import { sortPackageJsonFields } from '@nx/js/src/utils/package-json/sort-fields'; export function libraryGenerator(tree: Tree, schema: Schema) { return libraryGeneratorInternal(tree, { addPlugin: false, ...schema }); @@ -154,6 +155,8 @@ export async function libraryGeneratorInternal(tree: Tree, schema: Schema) { addProjectToTsSolutionWorkspace(tree, options.projectRoot); } + sortPackageJsonFields(tree, options.projectRoot); + if (!options.skipFormat) await formatFiles(tree); // Always run install to link packages.