Skip to content

Commit 3cc3fdf

Browse files
committed
feat(@angular/cli): automatic formatting files modified by schematics
This change introduces automatic formatting of files generated or modified during a schematic execution. When a schematic is executed, the CLI will now attempt to find and use the project's local Prettier installation to format all created or updated files. This ensures that generated code adheres to the project's established coding style without requiring developers to manually run a formatting command. If Prettier is not installed in the user's workspace, the formatting step will be gracefully skipped. This avoids adding a direct dependency on Prettier to the CLI and respects the user's project setup. The implementation leverages the Prettier CLI directly for a more robust and reliable formatting process. Closes #10777
1 parent 98a24d0 commit 3cc3fdf

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

packages/angular/cli/src/command-builder/schematics-command-module.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
OtherOptions,
3030
} from './command-module';
3131
import { Option, parseJsonSchemaToOptions } from './utilities/json-schema';
32+
import { formatFiles } from './utilities/prettier';
3233
import { SchematicEngineHost } from './utilities/schematic-engine-host';
3334
import { subscribeToWorkflow } from './utilities/schematic-workflow';
3435

@@ -361,7 +362,16 @@ export abstract class SchematicsCommandModule
361362

362363
if (executionOptions.dryRun) {
363364
logger.warn(`\nNOTE: The "--dry-run" option means no changes were made.`);
365+
366+
return 0;
367+
}
368+
369+
if (files.size) {
370+
// Note: we could use a task executor to format the files but this is simpler.
371+
await formatFiles(this.context.root, files);
364372
}
373+
374+
return 0;
365375
} catch (err) {
366376
// In case the workflow was not successful, show an appropriate error message.
367377
if (err instanceof UnsuccessfulWorkflowExecution) {
@@ -376,8 +386,6 @@ export abstract class SchematicsCommandModule
376386
} finally {
377387
unsubscribe();
378388
}
379-
380-
return 0;
381389
}
382390

383391
private getProjectName(): string | undefined {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { execFile } from 'node:child_process';
10+
import { createRequire } from 'node:module';
11+
import { dirname, extname, join, relative } from 'node:path';
12+
import { promisify } from 'node:util';
13+
14+
const execFileAsync = promisify(execFile);
15+
let prettierCliPath: string | null | undefined;
16+
17+
/**
18+
* File types that can be formatted using Prettier.
19+
*/
20+
const fileTypes: ReadonlySet<string> = new Set([
21+
'.ts',
22+
'.html',
23+
'.js',
24+
'.mjs',
25+
'.cjs',
26+
'.json',
27+
'.css',
28+
'.less',
29+
'.scss',
30+
'.sass',
31+
]);
32+
33+
/**
34+
* Formats files using Prettier.
35+
* @param cwd The current working directory.
36+
* @param files The files to format.
37+
*/
38+
export async function formatFiles(cwd: string, files: Set<string>): Promise<void> {
39+
if (prettierCliPath === undefined) {
40+
try {
41+
const prettierPath = createRequire(cwd + '/').resolve('prettier/package.json');
42+
prettierCliPath = join(dirname(prettierPath), 'bin/prettier.cjs');
43+
} catch {
44+
// Prettier is not installed.
45+
prettierCliPath = null;
46+
}
47+
}
48+
49+
if (!prettierCliPath) {
50+
return;
51+
}
52+
53+
await execFileAsync(prettierCliPath, [
54+
'--write',
55+
...[...files].filter((f) => fileTypes.has(extname(f))).map((f) => relative(cwd, f)),
56+
]);
57+
}

0 commit comments

Comments
 (0)