diff --git a/packages/devkit/src/generators/format-files.ts b/packages/devkit/src/generators/format-files.ts index de359ee0af8de..81ec86da6cec8 100644 --- a/packages/devkit/src/generators/format-files.ts +++ b/packages/devkit/src/generators/format-files.ts @@ -12,13 +12,65 @@ const { updateJson, readJson } = requireNx(); * @param tree - the file system tree */ export async function formatFiles(tree: Tree): Promise { + sortTsConfig(tree); + + return ( + requireNx().formatChangedFilesWithPrettierIfAvailable?.(tree) ?? + formatChangedFilesLegacy(tree) + ); +} + +function sortTsConfig(tree: Tree) { + try { + const tsConfigPath = getRootTsConfigPath(tree); + if (!tsConfigPath) { + return; + } + updateJson(tree, tsConfigPath, (tsconfig) => ({ + ...tsconfig, + compilerOptions: { + ...tsconfig.compilerOptions, + paths: sortObjectByKeys(tsconfig.compilerOptions.paths), + }, + })); + } catch (e) { + // catch noop + } +} + +function getRootTsConfigPath(tree: Tree): string | null { + for (const path of ['tsconfig.base.json', 'tsconfig.json']) { + if (tree.exists(path)) { + return path; + } + } + + return null; +} + +function getChangedPrettierConfigInTree(tree: Tree): Prettier.Options | null { + if (tree.listChanges().find((file) => file.path === '.prettierrc')) { + try { + return readJson(tree, '.prettierrc'); + } catch { + return null; + } + } else { + return null; + } +} + +/** + * @deprecated Use formatChangedFilesWithPrettierIfAvailable from nx/devkit-internals instead. + * @todo Remove this implementation in Nx v18, since the method in nx/devkit-internals will have + * been available for more than 1 major version. + */ +async function formatChangedFilesLegacy(tree: Tree) { let prettier: typeof Prettier; try { prettier = await import('prettier'); } catch {} - sortTsConfig(tree); - if (!prettier) return; const files = new Set( @@ -61,43 +113,3 @@ export async function formatFiles(tree: Tree): Promise { }) ); } - -function sortTsConfig(tree: Tree) { - try { - const tsConfigPath = getRootTsConfigPath(tree); - if (!tsConfigPath) { - return; - } - updateJson(tree, tsConfigPath, (tsconfig) => ({ - ...tsconfig, - compilerOptions: { - ...tsconfig.compilerOptions, - paths: sortObjectByKeys(tsconfig.compilerOptions.paths), - }, - })); - } catch (e) { - // catch noop - } -} - -function getRootTsConfigPath(tree: Tree): string | null { - for (const path of ['tsconfig.base.json', 'tsconfig.json']) { - if (tree.exists(path)) { - return path; - } - } - - return null; -} - -function getChangedPrettierConfigInTree(tree: Tree): Prettier.Options | null { - if (tree.listChanges().find((file) => file.path === '.prettierrc')) { - try { - return readJson(tree, '.prettierrc'); - } catch { - return null; - } - } else { - return null; - } -} diff --git a/packages/nx/src/devkit-internals.ts b/packages/nx/src/devkit-internals.ts index fca393f59c319..cd4f9ab69dc1a 100644 --- a/packages/nx/src/devkit-internals.ts +++ b/packages/nx/src/devkit-internals.ts @@ -4,3 +4,4 @@ * These may not be available in certain version of Nx, so be sure to check them first. */ export { createTempNpmDirectory } from './utils/package-manager'; +export { formatChangedFilesWithPrettierIfAvailable } from './generators/internal-utils/format-changed-files-with-prettier-if-available'; diff --git a/packages/nx/src/generators/internal-utils/format-changed-files-with-prettier-if-available.ts b/packages/nx/src/generators/internal-utils/format-changed-files-with-prettier-if-available.ts index 965d3a59ff60b..1316771f0b984 100644 --- a/packages/nx/src/generators/internal-utils/format-changed-files-with-prettier-if-available.ts +++ b/packages/nx/src/generators/internal-utils/format-changed-files-with-prettier-if-available.ts @@ -1,6 +1,8 @@ import type { Tree } from '../tree'; import * as path from 'path'; import type * as Prettier from 'prettier'; +import { formatFileContentsWithPrettierIfAvailable } from '../../utils/prettier'; +import { readJson } from '../utils/json'; /** * Formats all the created or updated files using Prettier @@ -9,43 +11,23 @@ import type * as Prettier from 'prettier'; export async function formatChangedFilesWithPrettierIfAvailable( tree: Tree ): Promise { - let prettier: typeof Prettier; - try { - prettier = await import('prettier'); - } catch {} - - if (!prettier) return; - const files = new Set( tree.listChanges().filter((file) => file.type !== 'DELETE') ); + + const changedPrettierInTree = getChangedPrettierConfigInTree(tree); + await Promise.all( Array.from(files).map(async (file) => { const systemPath = path.join(tree.root, file.path); - let options: any = { - filepath: systemPath, - }; - - const resolvedOptions = await prettier.resolveConfig(systemPath, { - editorconfig: true, - }); - if (!resolvedOptions) { - return; - } - options = { - ...options, - ...resolvedOptions, - }; - - const support = await prettier.getFileInfo(systemPath, options); - if (support.ignored || !support.inferredParser) { - return; - } - try { tree.write( file.path, - prettier.format(file.content.toString('utf-8'), options) + await formatFileContentsWithPrettierIfAvailable( + systemPath, + file.content.toString('utf-8'), + changedPrettierInTree + ) ); } catch (e) { console.warn(`Could not format ${file.path}. Error: "${e.message}"`); @@ -53,3 +35,15 @@ export async function formatChangedFilesWithPrettierIfAvailable( }) ); } + +function getChangedPrettierConfigInTree(tree: Tree): Prettier.Options | null { + if (tree.listChanges().find((file) => file.path === '.prettierrc')) { + try { + return readJson(tree, '.prettierrc'); + } catch { + return null; + } + } else { + return null; + } +} diff --git a/packages/nx/src/utils/fileutils.ts b/packages/nx/src/utils/fileutils.ts index 3e840744de30c..cad3777e0c377 100644 --- a/packages/nx/src/utils/fileutils.ts +++ b/packages/nx/src/utils/fileutils.ts @@ -13,6 +13,10 @@ import { import { dirname } from 'path'; import * as tar from 'tar-stream'; import { createGunzip } from 'zlib'; +import { + formatFileContentsWithPrettierIfAvailable, + formatFileContentsWithPrettierIfAvailableSync, +} from './prettier'; export interface JsonReadOptions extends JsonParseOptions { /** @@ -67,9 +71,10 @@ export function writeJsonFile( ): void { mkdirSync(dirname(path), { recursive: true }); const serializedJson = serializeJson(data, options); - const content = options?.appendNewLine - ? `${serializedJson}\n` - : serializedJson; + const content = formatFileContentsWithPrettierIfAvailableSync( + path, + options?.appendNewLine ? `${serializedJson}\n` : serializedJson + ); writeFileSync(path, content, { encoding: 'utf-8' }); } diff --git a/packages/nx/src/utils/prettier.ts b/packages/nx/src/utils/prettier.ts new file mode 100644 index 0000000000000..e08ce074a1275 --- /dev/null +++ b/packages/nx/src/utils/prettier.ts @@ -0,0 +1,67 @@ +import type * as Prettier from 'prettier'; + +let prettier: typeof Prettier | null; +export function getPrettierOrNull() { + if (prettier === undefined) { + try { + prettier = require('prettier'); + } catch (e) { + prettier = null; + } + } + return prettier; +} + +export function formatFileContentsWithPrettierIfAvailableSync< + T extends string | Buffer +>(path: string, contents: T, extraOptions?: Prettier.Options): string { + const prettier = getPrettierOrNull(); + const contentsAsString = + typeof contents === 'string' ? contents : contents.toString('utf-8'); + if (!prettier) { + return contentsAsString; + } + + const resolvedOptions = prettier.resolveConfig.sync(path, { + editorconfig: true, + }); + const options: Prettier.Options = { + ...resolvedOptions, + ...extraOptions, + filepath: path, + }; + + const support = prettier.getFileInfo.sync(path, options as any); + if (support.ignored || !support.inferredParser) { + return contentsAsString; + } + + return prettier.format(contentsAsString, options); +} + +export async function formatFileContentsWithPrettierIfAvailable< + T extends string | Buffer +>(path: string, contents: T, extraOptions?: Prettier.Options): Promise { + const prettier = getPrettierOrNull(); + const contentsAsString = + typeof contents === 'string' ? contents : contents.toString('utf-8'); + if (!prettier) { + return contentsAsString; + } + + const resolvedOptions = await prettier.resolveConfig(path, { + editorconfig: true, + }); + const options: Prettier.Options = { + ...resolvedOptions, + ...extraOptions, + filepath: path, + }; + + const support = await prettier.getFileInfo(path, options as any); + if (support.ignored || !support.inferredParser) { + return contentsAsString; + } + + return prettier.format(contentsAsString, options); +} diff --git a/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ b/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ index 6df9c79f95352..ff2d772f30572 100644 --- a/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ +++ b/packages/plugin/src/generators/create-package/files/create-framework-package/bin/index.ts__tmpl__ @@ -12,7 +12,7 @@ async function main() { // This assumes "<%= preset %>" and "<%= projectName %>" are at the same version // eslint-disable-next-line @typescript-eslint/no-var-requires - const presetVersion = require('../package.json').version, + const presetVersion = require('../package.json').version // TODO: update below to customize the workspace const { directory } = await createWorkspace(`<%= preset %>@${presetVersion}`, { diff --git a/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts b/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts index f9bb7c51b91ff..26949c7e469ff 100644 --- a/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts +++ b/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.spec.ts @@ -24,7 +24,6 @@ describe('move-workspace-generators-to-local-plugin', () => { name: 'my-generator', }); await generator(tree); - console.log(getProjects(tree).keys()); const config = readProjectConfiguration(tree, 'workspace-plugin'); expect(config.root).toEqual('tools/workspace-plugin'); diff --git a/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.ts b/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.ts index b27ce66ba1a71..0c004394141fa 100644 --- a/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.ts +++ b/packages/workspace/src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin.ts @@ -187,7 +187,7 @@ async function createNewPlugin(tree: Tree) { e2eTestRunner: 'none', publishable: false, }); - getCreateGeneratorsJson()( + await getCreateGeneratorsJson()( tree, readProjectConfiguration(tree, PROJECT_NAME).root, PROJECT_NAME @@ -209,6 +209,7 @@ function moveGeneratedPlugin( updateImportPath: true, destinationRelativeToRoot: true, importPath: importPath, + skipFormat: true, }); } }