Skip to content

Commit

Permalink
fix(misc): avoid writing unformatted json if prettier is available
Browse files Browse the repository at this point in the history
  • Loading branch information
AgentEnder committed May 5, 2023
1 parent b59a0a1 commit 1a1d215
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 76 deletions.
96 changes: 54 additions & 42 deletions packages/devkit/src/generators/format-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,65 @@ const { updateJson, readJson } = requireNx();
* @param tree - the file system tree
*/
export async function formatFiles(tree: Tree): Promise<void> {
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(
Expand Down Expand Up @@ -61,43 +113,3 @@ export async function formatFiles(tree: Tree): Promise<void> {
})
);
}

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;
}
}
1 change: 1 addition & 0 deletions packages/nx/src/devkit-internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -9,47 +11,39 @@ import type * as Prettier from 'prettier';
export async function formatChangedFilesWithPrettierIfAvailable(
tree: Tree
): Promise<void> {
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}"`);
}
})
);
}

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;
}
}
11 changes: 8 additions & 3 deletions packages/nx/src/utils/fileutils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -67,9 +71,10 @@ export function writeJsonFile<T extends object = object>(
): 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' });
}

Expand Down
67 changes: 67 additions & 0 deletions packages/nx/src/utils/prettier.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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}`, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ async function createNewPlugin(tree: Tree) {
e2eTestRunner: 'none',
publishable: false,
});
getCreateGeneratorsJson()(
await getCreateGeneratorsJson()(
tree,
readProjectConfiguration(tree, PROJECT_NAME).root,
PROJECT_NAME
Expand All @@ -209,6 +209,7 @@ function moveGeneratedPlugin(
updateImportPath: true,
destinationRelativeToRoot: true,
importPath: importPath,
skipFormat: true,
});
}
}
Expand Down

0 comments on commit 1a1d215

Please sign in to comment.