Jack Hsu cc441a6dc7
fix(js): sort package.json fields by idiomatic order (#29635)
This PR updates our app/lib generators such that `package.json` files
generated have fields in idiomatic order.

e.g.

```json
{
  "name": "...",
  "version": "...",
  "private": true,
  "type": "module",
  "main": "...",
  ...
  "dependencies": { ... }
}
```

The import fields such as name, version, private, and type are at the
top. Dep fields that could be noisy are at the bottom.

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2025-01-15 15:52:23 -05:00

175 lines
5.2 KiB
TypeScript

import {
addProjectConfiguration,
formatFiles,
GeneratorCallback,
installPackagesTask,
joinPathFragments,
runTasksInSerial,
toJS,
Tree,
updateJson,
writeJson,
} from '@nx/devkit';
import { addTsConfigPath, initGenerator as jsInitGenerator } from '@nx/js';
import { vueInitGenerator } from '../init/init';
import { Schema } from './schema';
import { normalizeOptions } from './lib/normalize-options';
import { addLinting } from '../../utils/add-linting';
import { createLibraryFiles } from './lib/create-library-files';
import { extractTsConfigBase } from '../../utils/create-ts-config';
import componentGenerator from '../component/component';
import { addVite } from './lib/add-vite';
import { ensureDependencies } from '../../utils/ensure-dependencies';
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
import { getRelativeCwd } from '@nx/devkit/src/generators/artifact-name-and-directory-utils';
import { relative } from 'path';
import { getImportPath } from '@nx/js/src/utils/get-import-path';
import {
addProjectToTsSolutionWorkspace,
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 });
}
export async function libraryGeneratorInternal(tree: Tree, schema: Schema) {
const tasks: GeneratorCallback[] = [];
tasks.push(await jsInitGenerator(tree, { ...schema, skipFormat: true }));
const options = await normalizeOptions(tree, schema);
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)`
);
}
if (options.isUsingTsSolutionConfig) {
writeJson(tree, joinPathFragments(options.projectRoot, 'package.json'), {
name: getImportPath(tree, options.name),
version: '0.0.1',
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,
},
});
} else {
addProjectConfiguration(tree, options.name, {
root: options.projectRoot,
sourceRoot: joinPathFragments(options.projectRoot, 'src'),
projectType: 'library',
tags: options.parsedTags,
targets: {},
});
}
tasks.push(
await vueInitGenerator(tree, {
...options,
skipFormat: true,
})
);
if (!options.skipPackageJson) {
tasks.push(ensureDependencies(tree, options));
}
extractTsConfigBase(tree);
tasks.push(await addLinting(tree, options, 'lib'));
createLibraryFiles(tree, options);
tasks.push(await addVite(tree, options));
if (options.component) {
const relativeCwd = getRelativeCwd();
const path = joinPathFragments(
options.projectRoot,
'src/lib',
options.fileName
);
await componentGenerator(tree, {
path: relativeCwd ? relative(relativeCwd, path) : path,
skipTests:
options.unitTestRunner === 'none' ||
(options.unitTestRunner === 'vitest' && options.inSourceTests == true),
export: true,
routing: options.routing,
js: options.js,
inSourceTests: options.inSourceTests,
skipFormat: true,
});
}
if (
!options.isUsingTsSolutionConfig &&
(options.publishable || options.bundler !== 'none')
) {
updateJson(tree, `${options.projectRoot}/package.json`, (json) => {
json.name = options.importPath;
return json;
});
}
if (!options.skipTsConfig && !options.isUsingTsSolutionConfig) {
addTsConfigPath(tree, options.importPath, [
joinPathFragments(
options.projectRoot,
'./src',
'index.' + (options.js ? 'js' : 'ts')
),
]);
}
if (options.js) toJS(tree);
if (options.isUsingTsSolutionConfig) {
updateTsconfigFiles(
tree,
options.projectRoot,
'tsconfig.lib.json',
{
jsx: 'preserve',
jsxImportSource: 'vue',
module: 'esnext',
moduleResolution: 'bundler',
resolveJsonModule: true,
},
options.linter === 'eslint'
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
: 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.projectRoot);
}
sortPackageJsonFields(tree, options.projectRoot);
if (!options.skipFormat) await formatFiles(tree);
// Always run install to link packages.
if (options.isUsingTsSolutionConfig) {
tasks.push(() => installPackagesTask(tree, true));
}
tasks.push(() => {
logShowProjectCommand(options.name);
});
return runTasksInSerial(...tasks);
}
export default libraryGenerator;