Fixes #5
This commit is contained in:
Connor Peet 2021-01-21 23:04:02 -08:00
Родитель f351c7f968
Коммит c7d50c8e29
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: CF8FD2EA0DBC61BD
8 изменённых файлов: 69 добавлений и 17 удалений

22
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\src\\index.ts",
"args": ["src/**/*.ts"],
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
]
}
]
}

Просмотреть файл

@ -8,7 +8,9 @@ import * as cluster from 'cluster';
import * as commander from 'commander'; import * as commander from 'commander';
import { cpus } from 'os'; import { cpus } from 'os';
import * as prettier from 'prettier'; import * as prettier from 'prettier';
import { version } from '../package.json';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { version } = require('../package.json');
function startMaster() { function startMaster() {
const program = commander const program = commander

Просмотреть файл

@ -39,7 +39,7 @@ export function spawnWorkers(options: IOptions) {
() => { () => {
progress.complete(); progress.complete();
if (progress.reformatted && options.check) { if ((progress.reformatted && options.check) || progress.failed) {
process.exit(1); process.exit(1);
} else { } else {
process.exit(0); process.exit(0);

Просмотреть файл

@ -12,6 +12,7 @@ import { IFormatResults } from './protocol';
export class ProgressReporter { export class ProgressReporter {
public total = 0; public total = 0;
public reformatted = 0; public reformatted = 0;
public failed = 0;
private spinner?: ora.Ora; private spinner?: ora.Ora;
constructor(quiet: boolean, private readonly check: boolean) { constructor(quiet: boolean, private readonly check: boolean) {
@ -26,6 +27,7 @@ export class ProgressReporter {
public update(results: IFormatResults) { public update(results: IFormatResults) {
this.total += results.files; this.total += results.files;
this.reformatted += results.formatted.length; this.reformatted += results.formatted.length;
this.failed += results.failed.length;
if (results.formatted.length) { if (results.formatted.length) {
if (this.spinner) { if (this.spinner) {

Просмотреть файл

@ -46,6 +46,7 @@ export type MasterMessage = IInitializationMessage | IFilesMessage;
*/ */
export interface IFormatResults { export interface IFormatResults {
files: number; files: number;
failed: IDiscoveredFile[];
formatted: IDiscoveredFile[]; formatted: IDiscoveredFile[];
} }

Просмотреть файл

@ -3,8 +3,8 @@
*--------------------------------------------------------*/ *--------------------------------------------------------*/
import * as cluster from 'cluster'; import * as cluster from 'cluster';
import { fromEvent, Observable } from 'rxjs'; import { BehaviorSubject, fromEvent, Observable } from 'rxjs';
import { filter, map, take, tap } from 'rxjs/operators'; import { filter, map, switchMap, take, tap } from 'rxjs/operators';
import { import {
IFormatResults, IFormatResults,
IInitializationMessage, IInitializationMessage,
@ -14,11 +14,17 @@ import {
WorkerMode, WorkerMode,
} from './protocol'; } from './protocol';
export class WorkerExitedError extends Error {
constructor(codeOrSignal: number | string) {
super(`Worker exited with unexpected ${codeOrSignal} code`);
}
}
/** /**
* Pool of workers. * Pool of workers.
*/ */
export class WorkerPool { export class WorkerPool {
private readonly workers: Array<{ worker: cluster.Worker; active: number }> = []; private readonly workers: Array<{ worker: Observable<cluster.Worker>; active: number }> = [];
private workIdCounter = 0; private workIdCounter = 0;
/** /**
@ -41,10 +47,13 @@ export class WorkerPool {
const target = this.workers[0]; const target = this.workers[0];
const id = this.workIdCounter++; const id = this.workIdCounter++;
target.active++; target.active++;
target.worker.send({ type: MessageType.WorkerFiles, files, id });
this.sortWorkers(); this.sortWorkers();
return fromEvent<[WorkerMessage]>(target.worker, 'message').pipe( return target.worker.pipe(
switchMap((worker) => {
worker.send({ type: MessageType.WorkerFiles, files, id });
return fromEvent<[WorkerMessage]>(worker, 'message');
}),
map(([m]) => m), map(([m]) => m),
filter((m) => m.id === id), filter((m) => m.id === id),
take(1), take(1),
@ -60,9 +69,14 @@ export class WorkerPool {
} }
private spawnWorker() { private spawnWorker() {
const worker = { worker: cluster.fork(), active: 0 }; const worker = cluster.fork();
this.workers.unshift(worker); const subject = new BehaviorSubject(worker);
worker.worker.send({ this.workers.unshift({ worker: subject, active: 0 });
worker.on('exit', (code, signal) => subject.error(new WorkerExitedError(code ?? signal)));
worker.on('error', (err) => subject.error(err));
worker.send({
mode: this.options.check mode: this.options.check
? WorkerMode.Assert ? WorkerMode.Assert
: this.options.write : this.options.write

Просмотреть файл

@ -6,7 +6,7 @@ import { readFile, writeFile } from 'fs';
import * as prettier from 'prettier'; import * as prettier from 'prettier';
import { combineLatest, Observable, of, Subject } from 'rxjs'; import { combineLatest, Observable, of, Subject } from 'rxjs';
import { last, mergeMap } from 'rxjs/operators'; import { last, mergeMap } from 'rxjs/operators';
import { promisify } from 'util'; import { inspect, promisify } from 'util';
import { import {
IFilesMessage, IFilesMessage,
IFormattedMessage, IFormattedMessage,
@ -32,6 +32,7 @@ function runFormatting(
const output: IFormattedMessage = { const output: IFormattedMessage = {
files: files.files.length, files: files.files.length,
formatted: [], formatted: [],
failed: [],
id: files.id, id: files.id,
type: MessageType.Formatted, type: MessageType.Formatted,
}; };
@ -39,10 +40,17 @@ function runFormatting(
return of(...files.files).pipe( return of(...files.files).pipe(
mergeMap(async (file) => { mergeMap(async (file) => {
const contents = await readFileAsync(file.path, 'utf-8'); const contents = await readFileAsync(file.path, 'utf-8');
const formatted = prettier.format(contents, { let formatted: string;
...(await prettier.resolveConfig(file.path)), try {
filepath: file.path, formatted = prettier.format(contents, {
}); ...(await prettier.resolveConfig(file.path)),
filepath: file.path,
});
} catch (e) {
process.stderr.write('\r\n' + inspect(e) + '\r\n');
output.failed.push(file);
return output;
}
if (formatted === contents) { if (formatted === contents) {
return output; return output;
@ -76,7 +84,7 @@ export function startWorker() {
} }
}); });
combineLatest(settings, files) combineLatest([settings, files])
.pipe(mergeMap(([s, f]) => runFormatting(s, f))) .pipe(mergeMap(([s, f]) => runFormatting(s, f)))
.subscribe( .subscribe(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion

Просмотреть файл

@ -14,5 +14,8 @@
"lib": ["es6", "es7"], "lib": ["es6", "es7"],
"outDir": "dist", "outDir": "dist",
"types": ["node"] "types": ["node"]
} },
"include": [
"src"
]
} }