Improve worker parser diagnostics behaviour
MODIFY
packages/@romejs/core/common/bridges/WorkerBridge.ts
packages/@romejs/core/common/bridges/WorkerBridge.ts
import {ModuleSignature} from '@romejs/js-analysis';
import {Manifest} from '@romejs/codec-js-manifest';
-import {Program, ConstSourceType} from '@romejs/js-ast';
+import {Program, ConstSourceType, ConstProgramSyntax} from '@romejs/js-ast';
import {
BundleCompileOptions,
CompileResult,
@@ -53,9 +53,20 @@ export type WorkerAnalyzeDependencyResult = AnalyzeDependencyResult & {
cached: boolean;
};
+export type WorkerFormatOptions = {
+ allowParserDiagnostics: boolean;
+};
+
+export type WorkerLintOptions = WorkerFormatOptions & {
+ prefetchedModuleSignatures: PrefetchedModuleSignatures;
+ fix: boolean;
+};
+
export type WorkerParseOptions = {
- compact: boolean;
- sourceType: undefined | ConstSourceType;
+ sourceType?: ConstSourceType;
+ syntax?: Array<ConstProgramSyntax>;
+ cache?: boolean;
+ allowDiagnostics?: boolean;
};
export type WorkerStatus = {
@@ -136,9 +147,10 @@ export default class WorkerBridge extends Bridge {
direction: 'server->client',
});
- format = this.createEvent<{file: JSONFileReference},
- | undefined
- | WorkerFormatResult>({
+ format = this.createEvent<{
+ file: JSONFileReference;
+ options: WorkerFormatOptions;
+ }, undefined | WorkerFormatResult>({
name: 'format',
direction: 'server->client',
});
@@ -161,8 +173,7 @@ export default class WorkerBridge extends Bridge {
lint = this.createEvent<{
file: JSONFileReference;
- prefetchedModuleSignatures: PrefetchedModuleSignatures;
- fix: boolean;
+ options: WorkerLintOptions;
}, WorkerLintResult>({name: 'lint', direction: 'server->client'});
compileJS = this.createEvent<{

MODIFY
packages/@romejs/core/common/fileHandlers.ts
packages/@romejs/core/common/fileHandlers.ts
import {ProjectConfig} from '@romejs/project';
import {FileReference} from '@romejs/core';
-import {PrefetchedModuleSignatures} from '../common/bridges/WorkerBridge';
+import {
+ WorkerFormatOptions,
+ WorkerLintOptions,
+} from '../common/bridges/WorkerBridge';
import Worker, {ParseResult} from '../worker/Worker';
import {Diagnostics, DiagnosticSuppressions} from '@romejs/diagnostics';
import * as compiler from '@romejs/js-compiler';
@@ -73,8 +76,12 @@ export function getFileHandlerAssert(
}
}
+export type ExtensionFormatInfo = ExtensionHandlerMethodInfo & {
+ options: WorkerFormatOptions;
+};
+
export type ExtensionLintInfo = ExtensionHandlerMethodInfo & {
- prefetchedModuleSignatures: PrefetchedModuleSignatures;
+ options: WorkerLintOptions;
format: boolean;
};
@@ -98,7 +105,7 @@ export type ExtensionHandler = {
isAsset?: boolean;
canHaveScale?: boolean;
lint?: (info: ExtensionLintInfo) => Promise<ExtensionLintResult>;
- format?: (info: ExtensionHandlerMethodInfo) => Promise<ExtensionLintResult>;
+ format?: (info: ExtensionFormatInfo) => Promise<ExtensionLintResult>;
toJavaScript?: (opts: ExtensionHandlerMethodInfo) => Promise<{
generated: boolean;
sourceText: string;
@@ -318,13 +325,7 @@ function buildJSHandler(
},
async lint(info: ExtensionLintInfo): Promise<ExtensionLintResult> {
- const {
- file: ref,
- project,
- format,
- prefetchedModuleSignatures,
- worker,
- } = info;
+ const {file: ref, project, format, options, worker} = info;
const {ast, sourceText, generated}: ParseResult = await worker.parseJS(
ref,
@@ -355,7 +356,7 @@ function buildJSHandler(
if (typeCheckingEnabled) {
const typeCheckProvider = await worker.getTypeCheckProvider(
ref.project,
- prefetchedModuleSignatures,
+ options.prefetchedModuleSignatures,
);
const typeDiagnostics = await typeCheck({
ast,

MODIFY
packages/@romejs/core/master/MasterRequest.ts
packages/@romejs/core/master/MasterRequest.ts
@@ -57,6 +57,8 @@ import WorkerBridge, {
WorkerCompilerOptions,
WorkerFormatResult,
WorkerLintResult,
+ WorkerFormatOptions,
+ WorkerLintOptions,
} from '../common/bridges/WorkerBridge';
import {ModuleSignature} from '@romejs/js-analysis';
import {
@@ -781,8 +783,10 @@ export default class MasterRequest {
async requestWorkerLint(
path: AbsoluteFilePath,
- fix: boolean,
- ): Promise<WorkerLintResult> {
+ optionsWithoutModSigs: Omit<WorkerLintOptions, 'prefetchedModuleSignatures'>,
+ ): Promise<
+ WorkerLintResult
+ > {
const {cache} = this.master;
const cacheEntry = await cache.get(path);
if (cacheEntry.lint !== undefined) {
@@ -793,10 +797,15 @@ export default class MasterRequest {
path,
);
+ const options: WorkerLintOptions = {
+ ...optionsWithoutModSigs,
+ prefetchedModuleSignatures,
+ };
+
const res = await this.wrapRequestDiagnostic(
'lint',
path,
- (bridge, file) => bridge.lint.call({file, fix, prefetchedModuleSignatures}),
+ (bridge, file) => bridge.lint.call({file, options}),
);
await cache.update(path, {
@@ -808,11 +817,12 @@ export default class MasterRequest {
async requestWorkerFormat(
path: AbsoluteFilePath,
+ options: WorkerFormatOptions,
): Promise<undefined | WorkerFormatResult> {
return await this.wrapRequestDiagnostic(
'format',
path,
- (bridge, file) => bridge.format.call({file}),
+ (bridge, file) => bridge.format.call({file, options}),
);
}

MODIFY
packages/@romejs/core/master/commands/format.ts
packages/@romejs/core/master/commands/format.ts
@@ -9,12 +9,23 @@ import {MasterRequest} from '@romejs/core';
import {createMasterCommand, commandCategories} from '../../commands';
import {createUnknownFilePath} from '@romejs/path';
+import {Consumer} from '@romejs/consume';
+
+type Flags = {
+ allowDiagnostics: boolean;
+};
export default createMasterCommand({
category: commandCategories.INTERNAL,
description: 'TODO',
- async default(req: MasterRequest): Promise<undefined | string> {
+ defineFlags(c: Consumer): Flags {
+ return {
+ allowDiagnostics: c.get('allowDiagnostics').asBoolean(false),
+ };
+ },
+
+ async default(req: MasterRequest, flags: Flags): Promise<undefined | string> {
const {reporter, master} = req;
const {args} = req.query;
req.expectArgumentLength(1);
@@ -24,7 +35,10 @@ export default createMasterCommand({
source: createUnknownFilePath(args[0]),
}, {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})});
- const res = await req.requestWorkerFormat(filename);
+ const res = await req.requestWorkerFormat(filename, {
+ allowParserDiagnostics: flags.allowDiagnostics,
+ });
+
if (res === undefined) {
reporter.error('No formatter for this file');
return undefined;

MODIFY
packages/@romejs/core/master/commands/parse.ts
packages/@romejs/core/master/commands/parse.ts
import {MasterRequest} from '@romejs/core';
import {Consumer} from '@romejs/consume';
-import {DiagnosticsError} from '@romejs/diagnostics';
import {commandCategories, createMasterCommand} from '../../commands';
import {createUnknownFilePath} from '@romejs/path';
-import {ConstSourceType} from '@romejs/js-ast';
+import {ConstSourceType, program} from '@romejs/js-ast';
+import {removeLoc} from '@romejs/js-ast-utils';
type Flags = {
- showDespiteDiagnostics: boolean;
+ allowDiagnostics: boolean;
compact: boolean;
sourceType: undefined | ConstSourceType;
};
@@ -24,13 +24,13 @@ export default createMasterCommand({
defineFlags(c: Consumer): Flags {
return {
- showDespiteDiagnostics: c.get('showDespiteDiagnostics').asBoolean(false),
+ allowDiagnostics: c.get('allowDiagnostics').asBoolean(false),
compact: c.get('compact').asBoolean(true),
sourceType: c.get('sourceType').asStringSetOrVoid(['module', 'script']),
};
},
- async default(req: MasterRequest, commandFlags: Flags): Promise<void> {
+ async default(req: MasterRequest, flags: Flags): Promise<void> {
const {master, reporter} = req;
const {args} = req.query;
req.expectArgumentLength(1);
@@ -40,18 +40,15 @@ export default createMasterCommand({
source: createUnknownFilePath(args[0]),
}, {location: req.getDiagnosticPointerFromFlags({type: 'arg', key: 0})});
- const ast = await req.requestWorkerParse(filename, {
- compact: commandFlags.compact,
- sourceType: commandFlags.sourceType,
+ let ast = await req.requestWorkerParse(filename, {
+ sourceType: flags.sourceType,
+ allowDiagnostics: flags.allowDiagnostics,
});
- const hasErrors = ast.diagnostics.length > 0;
- if (!hasErrors || commandFlags.showDespiteDiagnostics) {
- reporter.inspect(ast);
+ if (flags.compact) {
+ ast = program.assert(removeLoc(ast));
}
- if (hasErrors) {
- throw new DiagnosticsError('Parsing failed', ast.diagnostics);
- }
+ reporter.inspect(ast);
},
});

MODIFY
packages/@romejs/core/master/linter/Linter.ts
packages/@romejs/core/master/linter/Linter.ts
@@ -170,7 +170,11 @@ class LintRunner {
diagnostics,
suppressions,
fixed,
- } = await this.request.requestWorkerLint(path, this.fix);
+ } = await this.request.requestWorkerLint(path, {
+ fix: this.fix,
+ // TODO add this option?
+ allowParserDiagnostics: false,
+ });
processor.addSuppressions(suppressions);
processor.addDiagnostics(diagnostics);
this.compilerDiagnosticsCache.set(path, {suppressions, diagnostics});

MODIFY
packages/@romejs/core/worker/Worker.ts
packages/@romejs/core/worker/Worker.ts
@@ -11,6 +11,7 @@ import WorkerBridge, {
PrefetchedModuleSignatures,
WorkerPartialManifest,
WorkerPartialManifests,
+ WorkerParseOptions,
} from '../common/bridges/WorkerBridge';
import {Program, ConstSourceType, ConstProgramSyntax} from '@romejs/js-ast';
import Logger from '../common/utils/Logger';
@@ -21,7 +22,7 @@ import {Reporter} from '@romejs/cli-reporter';
import setupGlobalErrorHandlers from '../common/utils/setupGlobalErrorHandlers';
import {UserConfig, loadUserConfig} from '../common/userConfig';
import {hydrateJSONProjectConfig} from '@romejs/project';
-import {Diagnostics} from '@romejs/diagnostics';
+import {Diagnostics, DiagnosticsError} from '@romejs/diagnostics';
import {
createUnknownFilePath,
AbsoluteFilePath,
@@ -159,13 +160,15 @@ export default class Worker {
bridge.lint.subscribe((payload) => {
return this.api.lint(
convertTransportFileReference(payload.file),
- payload.prefetchedModuleSignatures,
- payload.fix,
+ payload.options,
);
});
bridge.format.subscribe((payload) => {
- return this.api.format(convertTransportFileReference(payload.file));
+ return this.api.format(
+ convertTransportFileReference(payload.file),
+ payload.options,
+ );
});
bridge.analyzeDependencies.subscribe((payload) => {
@@ -301,11 +304,10 @@ export default class Worker {
}
}
- async parseJS(ref: FileReference, opts: {
- sourceType?: ConstSourceType;
- syntax?: Array<ConstProgramSyntax>;
- cache?: boolean;
- } = {}): Promise<ParseResult> {
+ async parseJS(
+ ref: FileReference,
+ opts: WorkerParseOptions = {},
+ ): Promise<ParseResult> {
const path = createAbsoluteFilePath(ref.real);
const {project: projectId, uid} = ref;
@@ -394,6 +396,20 @@ export default class Worker {
syntax,
});
+ // If the AST is corrupt then we don't under any circumstance allow it
+ if (ast.corrupt) {
+ throw new DiagnosticsError('Corrupt AST', ast.diagnostics);
+ }
+
+ // Sometimes we may want to allow the "fixed" AST
+ const allowDiagnostics = opts.allowDiagnostics === true;
+ if (!allowDiagnostics && ast.diagnostics.length > 0) {
+ throw new DiagnosticsError(
+ "AST diagnostics aren't allowed",
+ ast.diagnostics,
+ );
+ }
+
const res: ParseResult = {
ast,
lastAccessed: Date.now(),

MODIFY
packages/@romejs/core/worker/WorkerAPI.ts
packages/@romejs/core/worker/WorkerAPI.ts
*/
import {Worker, FileReference} from '@romejs/core';
-import {Program, program} from '@romejs/js-ast';
+import {Program} from '@romejs/js-ast';
import {Diagnostics, descriptions, catchDiagnostics} from '@romejs/diagnostics';
import {
TransformStageName,
@@ -15,14 +15,14 @@ import {
compile,
} from '@romejs/js-compiler';
import {
- PrefetchedModuleSignatures,
WorkerParseOptions,
WorkerCompilerOptions,
WorkerFormatResult,
WorkerLintResult,
+ WorkerFormatOptions,
+ WorkerLintOptions,
} from '../common/bridges/WorkerBridge';
import Logger from '../common/utils/Logger';
-import {removeLoc} from '@romejs/js-ast-utils';
import * as jsAnalysis from '@romejs/js-analysis';
import {
getFileHandlerAssert,
@@ -166,17 +166,14 @@ export default class WorkerAPI {
cache: false,
});
- ast = this.interceptAndAddGeneratedToDiagnostics(ast, generated);
-
- if (opts.compact) {
- return program.assert(removeLoc(ast));
- } else {
- return ast;
- }
+ return this.interceptAndAddGeneratedToDiagnostics(ast, generated);
}
- async format(ref: FileReference): Promise<undefined | WorkerFormatResult> {
- const res = await this._format(ref);
+ async format(
+ ref: FileReference,
+ opts: WorkerFormatOptions,
+ ): Promise<undefined | WorkerFormatResult> {
+ const res = await this._format(ref, opts);
if (res === undefined) {
return undefined;
} else {
@@ -199,7 +196,10 @@ export default class WorkerAPI {
'NO_MATCH';
}
- async _format(ref: FileReference): Promise<undefined | ExtensionLintResult> {
+ async _format(
+ ref: FileReference,
+ options: WorkerFormatOptions,
+ ): Promise<undefined | ExtensionLintResult> {
const project = this.worker.getProject(ref.project);
this.logger.info(`Formatting:`, ref.real);
@@ -217,6 +217,7 @@ export default class WorkerAPI {
file: ref,
project,
worker: this.worker,
+ options,
});
return res;
@@ -224,8 +225,7 @@ export default class WorkerAPI {
async lint(
ref: FileReference,
- prefetchedModuleSignatures: PrefetchedModuleSignatures,
- fix: boolean,
+ options: WorkerLintOptions,
): Promise<WorkerLintResult> {
const project = this.worker.getProject(ref.project);
this.logger.info(`Linting:`, ref.real);
@@ -248,14 +248,14 @@ export default class WorkerAPI {
message: 'Caught by WorkerAPI.lint',
}, () => {
if (lint === undefined) {
- return this._format(ref);
+ return this._format(ref, options);
} else {
return lint({
format: this.shouldFormat(ref),
file: ref,
project,
- prefetchedModuleSignatures,
worker: this.worker,
+ options,
});
}
});
@@ -294,13 +294,13 @@ export default class WorkerAPI {
const needsFix = formatted !== sourceText;
// Autofix if necessary
- if (fix && needsFix) {
+ if (options.fix && needsFix) {
// Save the file and evict it from the cache
await this.worker.writeFile(ref.real, formatted);
// Relint this file without fixing it, we do this to prevent false positive error messages
return {
- ...(await this.lint(ref, prefetchedModuleSignatures, false)),
+ ...(await this.lint(ref, {...options, fix: false})),
fixed: true,
};
}

MODIFY
packages/@romejs/js-compiler/api/lint.ts
packages/@romejs/js-compiler/api/lint.ts
@@ -31,16 +31,6 @@ export default async function lint(req: FormatRequest): Promise<LintResult> {
return cached;
}
- if (ast.corrupt) {
- const result: LintResult = {
- suppressions: [],
- diagnostics: [...ast.diagnostics],
- src: req.sourceText,
- };
- lintCache.set(query, result);
- return result;
- }
-
let formattedCode = sourceText;
if (format) {
// Perform autofixes

Improve worker parser diagnostics behaviour