Improve max file size and missing file handler errors
MODIFY
internal/core/common/file-handlers/index.ts
internal/core/common/file-handlers/index.ts
@@ -17,7 +17,11 @@ import {
tsxHandler,
} from "./javascript";
import {htmlHandler} from "./html";
-import {DiagnosticLanguage} from "@internal/diagnostics";
+import {
+ DiagnosticLanguage,
+ createSingleDiagnosticError,
+ descriptions,
+} from "@internal/diagnostics";
import {markdownHandler} from "@internal/core/common/file-handlers/markdown";
import {
assetHandler,
@@ -96,7 +100,12 @@ export function getFileHandlerFromPathAssert(
const {handler, ext} = getFileHandlerFromPath(path, projectConfig);
if (handler === undefined) {
- throw new Error(`No file handler found for '${path.join()}'`);
+ throw createSingleDiagnosticError({
+ description: descriptions.FILES.NO_FILE_HANDLER(path),
+ location: {
+ filename: path.join(),
+ },
+ });
} else {
return {handler, ext};
}
@@ -163,8 +172,8 @@ setHandler("html", htmlHandler);
setHandler("htm", htmlHandler);
setHandler("md", markdownHandler);
setHandler("css", cssHandler);
-// Config
+// Config
for (const handler of CONFIG_HANDLERS) {
for (const ext of handler.extensions) {
if (ext === "yaml" || ext === "yml" || ext === "toml" || ext === "ini") {

MODIFY
internal/core/server/ServerRequest.ts
internal/core/server/ServerRequest.ts
@@ -143,6 +143,7 @@ async function globUnmatched(
if (configCategory !== undefined && !ignoreProjectIgnore) {
const globber = await req.glob({
...opts,
+ configCategory: undefined,
ignoreProjectIgnore: true,
});
const withoutIgnore = await globber.get(false);

MODIFY
internal/core/server/fs/FileAllocator.ts
internal/core/server/fs/FileAllocator.ts
@@ -12,6 +12,8 @@ import {FilePathLocker} from "../../../async/lockers";
import {AbsoluteFilePath, AbsoluteFilePathMap} from "@internal/path";
import {AnyMarkup, concatMarkup, markup} from "@internal/markup";
import {ReporterNamespace} from "@internal/cli-reporter";
+import {createSingleDiagnosticError, descriptions} from "@internal/diagnostics";
+import {matchPathPatterns} from "@internal/path-match";
export default class FileAllocator {
constructor(server: Server) {
@@ -55,10 +57,25 @@ export default class FileAllocator {
}
const maxSize = project.config.files.maxSize;
- if (stats.size > maxSize) {
- throw new Error(
- `The file ${path.join()} exceeds the project config max size of ${maxSize} bytes`,
- );
+ if (
+ stats.size > maxSize &&
+ matchPathPatterns(
+ path,
+ project.config.files.maxSizeIgnore,
+ project.directory,
+ ) === "NO_MATCH"
+ ) {
+ throw createSingleDiagnosticError({
+ description: descriptions.FILES.TOO_BIG(
+ path,
+ project.directory,
+ stats.size,
+ maxSize,
+ ),
+ location: {
+ filename: path.join(),
+ },
+ });
}
}

MODIFY
internal/core/server/fs/MemoryFileSystem.ts
internal/core/server/fs/MemoryFileSystem.ts
@@ -39,7 +39,6 @@ import {
readFileText,
watch,
} from "@internal/fs";
-import {getFileHandlerFromPath} from "@internal/core";
import crypto = require("crypto");
import {FileNotFound} from "@internal/fs/FileNotFound";
import {markup} from "@internal/markup";
@@ -646,19 +645,13 @@ export default class MemoryFileSystem {
}
private isIgnored(path: AbsoluteFilePath, type: "directory" | "file"): boolean {
+ type;
+
const project = this.server.projectManager.findLoadedProject(path);
if (project === undefined) {
return false;
}
- // If we're a file and don't have an extension handler so there's no reason for us to care about it
- if (
- type === "file" &&
- getFileHandlerFromPath(path, project.config) === undefined
- ) {
- return true;
- }
-
// Ensure we aren't in any of the default denylists
const basename = path.getBasename();
if (DEFAULT_DENYLIST.includes(basename)) {

MODIFY
internal/core/server/fs/glob.ts
internal/core/server/fs/glob.ts
@@ -107,8 +107,10 @@ export class Globber {
continue;
}
- // Check extensions
- if (extensions !== undefined) {
+ // Check extensions against input list only when it is a child of the input search path
+ // Explicitly specifying the exact filename is enough signal that they really wanted to
+ // target this file
+ if (!this.args.has(path) && extensions !== undefined) {
let matchedExt = false;
for (const ext of extensions) {
matchedExt = path.hasEndExtension(ext);

MODIFY
internal/core/server/fs/resolverSuggest.ts
internal/core/server/fs/resolverSuggest.ts
@@ -168,7 +168,7 @@ export default function resolverSuggest(
let relativePath = originDirectory.relative(absolute);
// If the user didn't use extensions, then neither should we
- if (!query.source.hasExtensions()) {
+ if (!query.source.hasAnyExtensions()) {
// TODO only do this if it's an implicit extension
relativePath = relativePath.changeBasename(
relativePath.getExtensionlessBasename(),

MODIFY
internal/diagnostics/categories.ts
internal/diagnostics/categories.ts
@@ -22,6 +22,8 @@ export type DiagnosticCategory =
| "compile/const-enums"
| "compile/jsx"
| "compile/nonnumeric-enum-values"
+ | "files/missingHandler"
+ | "files/tooBig"
| "flags/invalid"
| "format/disabled"
| "internalError/fatal"
@@ -346,6 +348,8 @@ const categoryNameMap: {[name in DiagnosticCategory]: true} = {
"compile/const-enums": true,
"compile/jsx": true,
"compile/nonnumeric-enum-values": true,
+ "files/missingHandler": true,
+ "files/tooBig": true,
"flags/invalid": true,
"format/disabled": true,
"internalError/fatal": true,

ADD
internal/diagnostics/descriptions/files.ts
+import {createDiagnosticsCategory} from "./index";
+import {markup} from "@internal/markup";
+import {AbsoluteFilePath, AnyFilePath} from "@internal/path";
+import {DiagnosticAdvice} from "../types";
+
+export const files = createDiagnosticsCategory({
+ NO_FILE_HANDLER: (path: AnyFilePath) => {
+ let advice: DiagnosticAdvice = [];
+
+ if (path.hasAnyExtensions()) {
+ advice.push({
+ type: "action",
+ instruction: markup`You can treat this file extension as a binary asset by running`,
+ noun: markup`Treat this file extension as a binary asset`,
+ command: "config",
+ args: ["push", "files.assetExtensions", path.getDotlessExtensions()],
+ });
+ }
+
+ return {
+ category: "files/missingHandler",
+ message: markup`No file handler found for <emphasis>${path}</emphasis>`,
+ advice,
+ };
+ },
+ TOO_BIG: (
+ path: AbsoluteFilePath,
+ projectPath: AbsoluteFilePath,
+ size: bigint,
+ maxSize: number,
+ ) => {
+ const relative = projectPath.relative(path).join();
+
+ return {
+ category: "files/tooBig",
+ message: markup`Size of <emphasis>${path}</emphasis> is <filesize>${String(
+ size,
+ )}</filesize> which exceeds the project maximum of <filesize>${String(
+ maxSize,
+ )}</filesize>`,
+ advice: [
+ {
+ type: "log",
+ category: "none",
+ text: markup`The file size limit exists to prevent us inadvertently slowing down and loading large files that we shouldn't.`,
+ },
+ {
+ type: "action",
+ instruction: markup`You can ignore this file from linting by running`,
+ noun: markup`Ignore this file from linting`,
+ command: "config",
+ args: ["push", "lint.ignore", relative],
+ },
+ {
+ type: "action",
+ instruction: markup`Or can allow this specific file to exceed the size limit with`,
+ noun: markup`Allow only this specific file to exceed the limit`,
+ command: "config",
+ args: ["push", "files.maxSizeIgnore", relative],
+ },
+ {
+ type: "action",
+ instruction: markup`Or just increase the size limit for all files to <filesize>${String(
+ size,
+ )}</filesize>`,
+ noun: markup`Increase project max file size limit`,
+ command: "config",
+ args: ["set", "files.maxSize", String(size)],
+ },
+ ],
+ };
+ },
+});

MODIFY
internal/diagnostics/descriptions/flags.ts
internal/diagnostics/descriptions/flags.ts
@@ -38,8 +38,8 @@ export const flags = createDiagnosticsCategory({
}),
NO_FILES_FOUND: (noun: undefined | string) => ({
message: noun === undefined
- ? markup`No files found`
- : markup`No files to ${noun} found`,
+ ? markup`No matching files found`
+ : markup`No matching ${noun} files found`,
}),
UNKNOWN_COMMAND: (
{

MODIFY
internal/diagnostics/descriptions/index.ts
internal/diagnostics/descriptions/index.ts
@@ -14,6 +14,7 @@ import {semver} from "./semver";
import {v8} from "./v8";
import {lintCommand} from "./commands/lintCommand";
import {projectManager} from "./projectManager";
+import {files} from "./files";
import {compiler} from "./compiler";
import {stringEscape} from "./stringEscape";
import {analyzeDependencies} from "./analyzeDependencies";
@@ -170,6 +171,7 @@ export const descriptions = {
CONSUME: consume,
MANIFEST: manifest,
PROJECT_CONFIG: projectConfig,
+ FILES: files,
USER_CONFIG: userConfig,
HTML_PARSER: htmlParser,
MARKDOWN_PARSER: markdownParser,

MODIFY
internal/path/index.ts
internal/path/index.ts
@@ -314,7 +314,11 @@ export abstract class BasePath<Super extends AnyFilePath = AnyFilePath> {
}
}
- public hasExtensions() {
+ public getDotlessExtensions(): string {
+ return this.getExtensions().slice(1);
+ }
+
+ public hasAnyExtensions() {
return this.getExtensions() !== "";
}

MODIFY
internal/project/load.ts
internal/project/load.ts
@@ -349,6 +349,10 @@ export async function normalizeProjectConfig(
config.files.maxSize = files.get("maxSize").asNumber();
}
+ if (files.has("maxSizeIgnore")) {
+ config.files.maxSizeIgnore = arrayOfPatterns(files.get("maxSizeIgnore"));
+ }
+
if (files.has("assetExtensions")) {
config.files.assetExtensions = files.get("assetExtensions").asMappedArray((
item,
@@ -507,6 +511,14 @@ async function extendProjectConfig(
merged.bundler.externals = bundlerExternals;
}
+ const filesMaxSizeIgnore = mergeArrays(
+ extendsObj.files.maxSizeIgnore,
+ config.files.maxSizeIgnore,
+ );
+ if (filesMaxSizeIgnore !== undefined) {
+ merged.files.maxSizeIgnore = filesMaxSizeIgnore;
+ }
+
return {
partial: merged,
meta: {

MODIFY
internal/project/types.ts
internal/project/types.ts
@@ -80,6 +80,7 @@ export type ProjectConfigObjects = {
};
files: {
assetExtensions: string[];
+ maxSizeIgnore: PathPatterns;
maxSize: number;
vendorPath: AbsoluteFilePath;
};
@@ -191,6 +192,7 @@ export function createDefaultProjectConfig(): ProjectConfig {
files: {
vendorPath: TEMP_PATH.append("rome-remote"),
assetExtensions: [],
+ maxSizeIgnore: [],
maxSize: 40_000_000, // 40 megabytes
},
targets: new Map(),

Improve max file size and missing file handler errors (#1334)