Skip to content

Commit e7a15b1

Browse files
author
Paul Marbach
committed
feat(web-scripts): add format script; use implicit prettier config
1 parent a6284a6 commit e7a15b1

File tree

4 files changed

+123
-36
lines changed

4 files changed

+123
-36
lines changed

packages/web-scripts/config/lint-staged.config.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
const { PRETTIER_CONFIG } = require('../cjs/Paths');
21
const { getEslintConfig } = require('../cjs/Tasks/LintTask');
2+
const { getPrettierConfig } = require('../cjs/Tasks/FormatTask');
33
const { getJestConfig } = require('../cjs/Tasks/TestTask');
44

55
const fix = process.env.WEB_SCRIPTS_SHOULD_FIX === 'true';
66
const tests = process.env.WEB_SCRIPTS_RUN_TESTS === 'true';
77
const jestConfig = process.env.WEB_SCRIPTS_JEST_CONFIG || getJestConfig();
88
const prettierConfig =
9-
process.env.WEB_SCRIPTS_PRETTIER_CONFIG || PRETTIER_CONFIG;
9+
process.env.WEB_SCRIPTS_PRETTIER_CONFIG || getPrettierConfig();
1010
const eslintConfig = process.env.WEB_SCRIPTS_ESLINT_CONFIG || getEslintConfig();
1111

1212
const testRelatedChanges = `jest ${
@@ -22,9 +22,9 @@ const lintRelatedChanges = `eslint ${fix ? '--fix' : ''} ${
2222
// https://github.com/okonet/lint-staged/issues/174#issuecomment-461423707
2323
const typecheckRelatedChanges = `bash -c \"tsc --noEmit\"`;
2424

25-
const formatRelatedChanges = `prettier ${
26-
fix ? '--write' : '--check'
27-
} --config ${prettierConfig}`;
25+
const formatRelatedChanges = `prettier ${fix ? '--write' : '--check'} ${
26+
prettierConfig ? `--config ${prettierConfig}` : ''
27+
}`.trim();
2828

2929
const gitAdd = 'git add';
3030

packages/web-scripts/src/SharedTypes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export type TaskName =
33
| 'build'
44
| 'test'
55
| 'lint'
6+
| 'format'
67
| 'commit'
78
| 'commitmsg'
89
| 'precommit'
@@ -31,6 +32,11 @@ export type LintTaskDesc = {
3132
typecheck: boolean;
3233
} & TaskDesc;
3334

35+
export type FormatTaskDesc = {
36+
name: 'format';
37+
config?: string;
38+
} & TaskDesc;
39+
3440
export type CommitTaskDesc = {
3541
name: 'commit';
3642
path: string;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { default as Debug } from 'debug';
2+
import { default as spawn } from 'cross-spawn';
3+
import { SpawnSyncReturns } from 'child_process';
4+
5+
import { FormatTaskDesc } from '../SharedTypes';
6+
import { PRETTIER_CONFIG } from '../Paths';
7+
import { hasConfig } from '../hasConfig';
8+
9+
const dbg = Debug('web-scripts:format'); // eslint-disable-line new-cap
10+
11+
export function getPrettierConfig(): string | null {
12+
if (
13+
!hasConfig([
14+
{ type: 'file', pattern: '.prettierrc' },
15+
{ type: 'file', pattern: 'prettier.config.js' },
16+
{ type: 'package.json', property: 'prettierrc' },
17+
])
18+
) {
19+
return PRETTIER_CONFIG;
20+
}
21+
22+
return null;
23+
}
24+
25+
export function formatTask(task: FormatTaskDesc): SpawnSyncReturns<Buffer> {
26+
const cmd = 'npx';
27+
const config = task.config || getPrettierConfig();
28+
29+
const args = [
30+
'--no-install',
31+
'prettier',
32+
...(config ? ['--config', config] : []),
33+
...task.restOptions,
34+
];
35+
dbg('npx args %o', args);
36+
return spawn.sync(cmd, args, { stdio: 'inherit' });
37+
}

packages/web-scripts/src/index.ts

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ import {
55
TestTaskDesc,
66
BuildTaskDesc,
77
LintTaskDesc,
8+
FormatTaskDesc,
89
CommitTaskDesc,
910
CommitMsgTaskDesc,
1011
ReleaseTaskDesc,
1112
PrecommitTaskDesc,
1213
} from './SharedTypes';
13-
import { COMMITLINT_CONIFG, PRETTIER_CONFIG } from './Paths';
14+
import { COMMITLINT_CONIFG } from './Paths';
1415
import { testTask } from './Tasks/TestTask';
1516
import { buildTask } from './Tasks/BuildTask';
1617
import { lintTask } from './Tasks/LintTask';
18+
import { formatTask } from './Tasks/FormatTask';
1719
import {
1820
commitTask,
1921
commitMsgTask,
@@ -35,14 +37,16 @@ program
3537
.option('--no-esm', 'do not build esm target')
3638
.option('--no-cjs', 'do not build cjs target')
3739
.option('--no-types', 'do not build types target')
38-
.action((cmd: Command) => {
39-
const { esm, types, cjs } = cmd.opts();
40+
.action((...args) => {
41+
const cmd = getCommand(args);
42+
const rest = getPositionalArgs(args);
43+
const { esm, types, cjs } = getOpts(cmd);
4044
const t: BuildTaskDesc = {
4145
name: 'build',
4246
esm,
4347
types,
4448
cjs,
45-
restOptions: parseRestOptions(cmd),
49+
restOptions: [...parseRestOptions(cmd), ...rest],
4650
};
4751

4852
handlePromiseResult(buildTask(t));
@@ -53,12 +57,14 @@ program
5357
.allowUnknownOption()
5458
.description('Run tests via jest')
5559
.option('--config [path]', 'path to jest config')
56-
.action((cmd: Command) => {
57-
const { config } = cmd.opts();
60+
.action((...args) => {
61+
const cmd = getCommand(args);
62+
const rest = getPositionalArgs(args);
63+
const { config } = getOpts(cmd);
5864
const t: TestTaskDesc = {
5965
name: 'test',
6066
config,
61-
restOptions: parseRestOptions(cmd),
67+
restOptions: [...parseRestOptions(cmd), ...rest],
6268
};
6369

6470
const result = testTask(t);
@@ -71,47 +77,65 @@ program
7177
.description('Run ESLint and TypeScript to statically analyze your code')
7278
.option('--config [path]', 'path to ESLint config')
7379
.option('--typecheck', 'run a TypeScript type check')
74-
.action((cmd: Command) => {
75-
const { typecheck, config } = cmd.opts();
80+
.action((...args) => {
81+
const cmd = getCommand(args);
82+
const rest = getPositionalArgs(args);
83+
const { typecheck, config } = getOpts(cmd);
7684
const t: LintTaskDesc = {
7785
name: 'lint',
7886
config,
7987
typecheck,
80-
restOptions: parseRestOptions(cmd),
88+
restOptions: [...parseRestOptions(cmd), ...rest],
8189
};
8290

8391
handlePromiseResult(lintTask(t));
8492
});
8593

94+
program
95+
.command('format')
96+
.allowUnknownOption()
97+
.description('Run Prettier to format your code')
98+
.option('--config [path]', 'path to Prettier config')
99+
.action((...args) => {
100+
const cmd = getCommand(args);
101+
const rest = getPositionalArgs(args);
102+
const { config } = getOpts(cmd);
103+
const t: FormatTaskDesc = {
104+
name: 'format',
105+
config,
106+
restOptions: [...parseRestOptions(cmd), ...rest],
107+
};
108+
109+
handleSpawnResult(formatTask(t));
110+
});
111+
86112
program
87113
.command('precommit')
88114
.allowUnknownOption()
89115
.description('Locally validate the repo before committing')
90116
.option('--jest-config [path]', 'path to jest config')
91-
.option(
92-
'--prettier-config [path]',
93-
'path to prettier config',
94-
PRETTIER_CONFIG,
95-
)
117+
.option('--prettier-config [path]', 'path to prettier config')
96118
.option('--eslint-config [path]', 'path to eslint config')
97119
.option('--no-fix', 'Do not auto-fix any static analysis errors')
98120
.option('--no-tests', 'Do not run Jest tests')
99-
.action((cmd: Command) => {
121+
.action((...args) => {
122+
const cmd = getCommand(args);
123+
const rest = getPositionalArgs(args);
100124
const {
101125
fix,
102126
tests,
103127
'jest-config': jestConfig,
104128
'eslint-config': eslintConfig,
105129
'prettier-config': prettierConfig,
106-
} = cmd.opts();
130+
} = getOpts(cmd);
107131
const t: PrecommitTaskDesc = {
108132
name: 'precommit',
109133
fix,
110134
tests,
111135
jestConfig,
112136
eslintConfig,
113137
prettierConfig,
114-
restOptions: parseRestOptions(cmd),
138+
restOptions: [...parseRestOptions(cmd), ...rest],
115139
};
116140

117141
handleSpawnResult(precommitTask(t));
@@ -126,12 +150,14 @@ program
126150
'path for commitizen adapter to use',
127151
'cz-conventional-changelog',
128152
)
129-
.action((cmd: Command) => {
130-
const { path } = cmd.opts();
153+
.action((...args) => {
154+
const cmd = getCommand(args);
155+
const rest = getPositionalArgs(args);
156+
const { path } = getOpts(cmd);
131157
const t: CommitTaskDesc = {
132158
name: 'commit',
133159
path,
134-
restOptions: parseRestOptions(cmd),
160+
restOptions: [...parseRestOptions(cmd), ...rest],
135161
};
136162

137163
try {
@@ -150,12 +176,14 @@ program
150176
'path to the commitlint config.',
151177
COMMITLINT_CONIFG,
152178
)
153-
.action((cmd: Command) => {
154-
const { config } = cmd.opts();
179+
.action((...args) => {
180+
const cmd = getCommand(args);
181+
const rest = getPositionalArgs(args);
182+
const { config } = getOpts(cmd);
155183
const t: CommitMsgTaskDesc = {
156184
name: 'commitmsg',
157185
config,
158-
restOptions: parseRestOptions(cmd),
186+
restOptions: [...parseRestOptions(cmd), ...rest],
159187
};
160188

161189
handleSpawnResult(commitMsgTask(t));
@@ -165,10 +193,12 @@ program
165193
.command('release')
166194
.allowUnknownOption()
167195
.description('Run semantic-release')
168-
.action((cmd: Command) => {
196+
.action((...args) => {
197+
const cmd = getCommand(args);
198+
const rest = getPositionalArgs(args);
169199
const t: ReleaseTaskDesc = {
170200
name: 'release',
171-
restOptions: parseRestOptions(cmd),
201+
restOptions: [...parseRestOptions(cmd), ...rest],
172202
};
173203

174204
handleSpawnResult(releaseTask(t));
@@ -178,11 +208,14 @@ function handlePromiseResult(result: Promise<any>) {
178208
result.catch(handleError);
179209
}
180210

181-
function handleError(error: Error) {
211+
function handleError(error: Error & { exitStatus?: number }) {
182212
/* eslint-disable no-console */
183-
console.error(error);
213+
// only log if the error is useful (e.g. not the default message from non-zero status is in cross-spawn)
214+
if (error.message && error.message.indexOf('Error: Exited with status') < 0) {
215+
console.error(error);
216+
}
184217
/* eslint-enable no-console */
185-
process.exit(1);
218+
process.exit(error.exitStatus || 1);
186219
}
187220

188221
function handleSpawnResult(result: SpawnSyncReturns<Buffer>) {
@@ -195,8 +228,19 @@ function handleSpawnResult(result: SpawnSyncReturns<Buffer>) {
195228
}
196229
}
197230

198-
function parseRestOptions(cmd: Command) {
199-
// parse the rest of the options
231+
function getCommand(args: any[]): Command {
232+
return args[args.length - 1] as Command;
233+
}
234+
235+
function getPositionalArgs(args: any[]): string[] {
236+
return args.slice(0, args.length - 1) as string[];
237+
}
238+
239+
function getOpts(cmd: Command): { [key: string]: any } {
240+
return cmd.opts();
241+
}
242+
243+
function parseRestOptions(cmd: Command): string[] {
200244
return cmd.parseOptions(process.argv).unknown;
201245
}
202246

0 commit comments

Comments
 (0)