Родитель
f351c7f968
Коммит
c7d50c8e29
|
@ -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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче