222 lines
6.0 KiB
TypeScript
222 lines
6.0 KiB
TypeScript
import { execSync } from 'child_process';
|
|
import * as path from 'path';
|
|
import {
|
|
getProjectRoots,
|
|
NxArgs,
|
|
parseFiles,
|
|
splitArgsIntoNxArgsAndOverrides,
|
|
} from '../../utils/command-line-utils';
|
|
import { getIgnoreObject } from '../../utils/ignore';
|
|
import { fileExists, readJsonFile, writeJsonFile } from '../../utils/fileutils';
|
|
import { calculateFileChanges, FileData } from '../../project-graph/file-utils';
|
|
import * as yargs from 'yargs';
|
|
|
|
import * as prettier from 'prettier';
|
|
import type { SupportInfo } from 'prettier';
|
|
import { sortObjectByKeys } from '../../utils/object-sort';
|
|
import { readModulePackageJson } from '../../utils/package-json';
|
|
import {
|
|
getRootTsConfigFileName,
|
|
getRootTsConfigPath,
|
|
} from '../../plugins/js/utils/typescript';
|
|
import { createProjectGraphAsync } from '../../project-graph/project-graph';
|
|
import { filterAffected } from '../../project-graph/affected/affected-project-graph';
|
|
import { readNxJson } from '../../config/configuration';
|
|
import { ProjectGraph } from '../../config/project-graph';
|
|
import { chunkify } from '../../utils/chunkify';
|
|
import { allFileData } from '../../utils/all-file-data';
|
|
|
|
const PRETTIER_PATH = getPrettierPath();
|
|
|
|
export async function format(
|
|
command: 'check' | 'write',
|
|
args: yargs.Arguments
|
|
): Promise<void> {
|
|
const { nxArgs } = splitArgsIntoNxArgsAndOverrides(
|
|
args,
|
|
'affected',
|
|
{ printWarnings: false },
|
|
readNxJson()
|
|
);
|
|
const patterns = (await getPatterns({ ...args, ...nxArgs } as any)).map(
|
|
// prettier removes one of the \
|
|
// prettier-ignore
|
|
(p) => `"${p.replace(/\$/g, "\\\$")}"`
|
|
);
|
|
|
|
// Chunkify the patterns array to prevent crashing the windows terminal
|
|
const chunkList: string[][] = chunkify(patterns);
|
|
|
|
switch (command) {
|
|
case 'write':
|
|
sortTsConfig();
|
|
addRootConfigFiles(chunkList, nxArgs);
|
|
chunkList.forEach((chunk) => write(chunk));
|
|
break;
|
|
case 'check':
|
|
const pass = chunkList.reduce(
|
|
(pass, chunk) => check(chunk) && pass,
|
|
true
|
|
);
|
|
if (!pass) {
|
|
process.exit(1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
async function getPatterns(
|
|
args: NxArgs & { libsAndApps: boolean; _: string[] }
|
|
): Promise<string[]> {
|
|
const graph = await createProjectGraphAsync({ exitOnError: true });
|
|
const allFilesPattern = ['.'];
|
|
|
|
if (args.all) {
|
|
return allFilesPattern;
|
|
}
|
|
|
|
try {
|
|
if (args.projects && args.projects.length > 0) {
|
|
return getPatternsFromProjects(args.projects, graph);
|
|
}
|
|
|
|
const p = parseFiles(args);
|
|
|
|
// In prettier v3 the getSupportInfo result is a promise
|
|
const supportedExtensions = (
|
|
await (prettier.getSupportInfo() as Promise<SupportInfo> | SupportInfo)
|
|
).languages
|
|
.flatMap((language) => language.extensions)
|
|
.filter((extension) => !!extension)
|
|
// Prettier supports ".swcrc" as a file instead of an extension
|
|
// So we add ".swcrc" as a supported extension manually
|
|
// which allows it to be considered for calculating "patterns"
|
|
.concat('.swcrc');
|
|
|
|
const patterns = p.files.filter(
|
|
(f) => fileExists(f) && supportedExtensions.includes(path.extname(f))
|
|
);
|
|
|
|
// exclude patterns in .nxignore or .gitignore
|
|
const nonIgnoredPatterns = getIgnoreObject().filter(patterns);
|
|
|
|
return args.libsAndApps
|
|
? await getPatternsFromApps(
|
|
nonIgnoredPatterns,
|
|
await allFileData(),
|
|
graph
|
|
)
|
|
: nonIgnoredPatterns;
|
|
} catch {
|
|
return allFilesPattern;
|
|
}
|
|
}
|
|
|
|
async function getPatternsFromApps(
|
|
affectedFiles: string[],
|
|
allWorkspaceFiles: FileData[],
|
|
projectGraph: ProjectGraph
|
|
): Promise<string[]> {
|
|
const graph = await createProjectGraphAsync({ exitOnError: true });
|
|
const affectedGraph = await filterAffected(
|
|
graph,
|
|
calculateFileChanges(affectedFiles, allWorkspaceFiles)
|
|
);
|
|
return getPatternsFromProjects(
|
|
Object.keys(affectedGraph.nodes),
|
|
projectGraph
|
|
);
|
|
}
|
|
|
|
function addRootConfigFiles(chunkList: string[][], nxArgs: NxArgs): void {
|
|
if (nxArgs.all) {
|
|
return;
|
|
}
|
|
const chunk = [];
|
|
const addToChunkIfNeeded = (file: string) => {
|
|
if (chunkList.every((c) => !c.includes(`"${file}"`))) {
|
|
chunk.push(file);
|
|
}
|
|
};
|
|
// if (workspaceJsonPath) {
|
|
// addToChunkIfNeeded(workspaceJsonPath);
|
|
// }
|
|
['nx.json', getRootTsConfigFileName()]
|
|
.filter(Boolean)
|
|
.forEach(addToChunkIfNeeded);
|
|
|
|
if (chunk.length > 0) {
|
|
chunkList.push(chunk);
|
|
}
|
|
}
|
|
|
|
function getPatternsFromProjects(
|
|
projects: string[],
|
|
projectGraph: ProjectGraph
|
|
): string[] {
|
|
return getProjectRoots(projects, projectGraph);
|
|
}
|
|
|
|
function write(patterns: string[]) {
|
|
if (patterns.length > 0) {
|
|
const [swcrcPatterns, regularPatterns] = patterns.reduce(
|
|
(result, pattern) => {
|
|
result[pattern.includes('.swcrc') ? 0 : 1].push(pattern);
|
|
return result;
|
|
},
|
|
[[], []] as [swcrcPatterns: string[], regularPatterns: string[]]
|
|
);
|
|
|
|
execSync(
|
|
`node "${PRETTIER_PATH}" --write --list-different ${regularPatterns.join(
|
|
' '
|
|
)}`,
|
|
{
|
|
stdio: [0, 1, 2],
|
|
}
|
|
);
|
|
|
|
if (swcrcPatterns.length > 0) {
|
|
execSync(
|
|
`node "${PRETTIER_PATH}" --write --list-different ${swcrcPatterns.join(
|
|
' '
|
|
)} --parser json`,
|
|
{
|
|
stdio: [0, 1, 2],
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function check(patterns: string[]): boolean {
|
|
if (patterns.length === 0) {
|
|
return true;
|
|
}
|
|
try {
|
|
execSync(`node "${PRETTIER_PATH}" --list-different ${patterns.join(' ')}`, {
|
|
stdio: [0, 1, 2],
|
|
});
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function sortTsConfig() {
|
|
try {
|
|
const tsconfigPath = getRootTsConfigPath();
|
|
const tsconfig = readJsonFile(tsconfigPath);
|
|
const sortedPaths = sortObjectByKeys(tsconfig.compilerOptions.paths);
|
|
tsconfig.compilerOptions.paths = sortedPaths;
|
|
writeJsonFile(tsconfigPath, tsconfig);
|
|
} catch (e) {
|
|
// catch noop
|
|
}
|
|
}
|
|
|
|
function getPrettierPath() {
|
|
const { bin } = readModulePackageJson('prettier').packageJson;
|
|
return require.resolve(path.join('prettier', bin as string));
|
|
}
|