Generate .editorconfig and populate config with globals
MODIFY
packages/@romefrontend/core/client/commands/init.ts
packages/@romefrontend/core/client/commands/init.ts
@@ -18,14 +18,15 @@ export default createLocalCommand({
description: markup`create a project config`,
usage: "",
examples: [],
- hidden: true,
defineFlags() {
return {};
},
async callback(req: ClientRequest) {
const {reporter} = req.client;
- const configPath = req.client.flags.cwd.append("rome.rjson");
+ const projectPath = req.client.flags.cwd;
+ const configPath = projectPath.append("rome.rjson");
+
if (await exists(configPath)) {
reporter.error(
markup`<filelink target="${configPath.join()}" emphasis>rome.rjson</filelink> file already exists`,
@@ -37,16 +38,42 @@ export default createLocalCommand({
}
const config: Dict<unknown> = {};
- await writeConfig();
async function writeConfig() {
await writeFile(configPath, stringifyRJSON(config));
+ reporter.success(markup`Created config ${configPath}`);
}
-
// Run lint, capture diagnostics
+ const response = await req.client.query(
+ {
+ commandName: "check",
+ silent: true,
+ },
+ "server",
+ );
+ let globals: Array<string> = [];
+ if (response.type === "DIAGNOSTICS") {
+ if (response.hasDiagnostics) {
+ response.diagnostics.forEach((d) => {
+ if (d.description.category === "lint/js/undeclaredVariables") {
+ if (d.meta && d.meta.identifierName) {
+ globals.push(d.meta.identifierName);
+ }
+ }
+ });
+ }
+ }
+ if (globals.length > 0) {
+ config["globals"] = globals;
+ }
- reporter.success(markup`Created config ${configPath}`);
-
+ await writeConfig();
+ await req.client.query(
+ {
+ commandName: "init",
+ },
+ "server",
+ );
return true;
},
});

MODIFY
packages/@romefrontend/core/common/file-handlers/html.ts
packages/@romefrontend/core/common/file-handlers/html.ts
@@ -24,6 +24,7 @@ export const htmlHandler: ExtensionHandler = {
canLint: true,
canFormat: true,
language: "html",
+ hasTabs: true,
async parse({mtime, path, file, worker}) {
const sourceText = await worker.readFile(file.real);

MODIFY
packages/@romefrontend/core/common/file-handlers/index.ts
packages/@romefrontend/core/common/file-handlers/index.ts
@@ -38,7 +38,10 @@ export function inferDiagnosticLanguageFromFilename(
return existing;
}
if (filename !== undefined) {
- const {handler} = getFileHandler(createUnknownFilePath(filename), undefined);
+ const {handler} = getFileHandlerFromPath(
+ createUnknownFilePath(filename),
+ undefined,
+ );
if (handler !== undefined) {
return handler.language;
}
@@ -57,7 +60,7 @@ export function getFileHandlerExtensions(
}
}
-export function getFileHandler(
+export function getFileHandlerFromPath(
path: UnknownFilePath,
projectConfig: undefined | ProjectConfig,
): GetFileHandlerResult {
@@ -83,11 +86,28 @@ export function getFileHandler(
return {ext, handler};
}
+export function getFileHandlerFromExtension(
+ ext: string,
+ projectConfig: undefined | ProjectConfig,
+) {
+ let handler = DEFAULT_HANDLERS.get(ext.slice(1));
+
+ if (
+ handler === undefined &&
+ projectConfig !== undefined &&
+ projectConfig.files.assetExtensions.includes(ext)
+ ) {
+ handler = assetHandler;
+ }
+
+ return handler;
+}
+
export function getFileHandlerAssert(
path: UnknownFilePath,
projectConfig: undefined | ProjectConfig,
): Required<GetFileHandlerResult> {
- const {handler, ext} = getFileHandler(path, projectConfig);
+ const {handler, ext} = getFileHandlerFromPath(path, projectConfig);
if (handler === undefined) {
throw new Error(`No file handler found for '${path.join()}'`);
@@ -163,6 +183,9 @@ const DEFAULT_ASSET_EXTENSIONS = [
"eot",
"ttf",
"otf",
+ // YAML
+ "yml",
+ "yaml",
];
for (const ext of DEFAULT_ASSET_EXTENSIONS) {

MODIFY
packages/@romefrontend/core/common/file-handlers/javascript.ts
packages/@romefrontend/core/common/file-handlers/javascript.ts
@@ -33,6 +33,7 @@ function buildJSHandler(
canLint: true,
canFormat: true,
language: "js",
+ hasTabs: true,
async parse({mtime, sourceTypeJS, manifestPath, path, file, worker}) {
const sourceText = await worker.readFile(file.real);

MODIFY
packages/@romefrontend/core/common/file-handlers/json.ts
packages/@romefrontend/core/common/file-handlers/json.ts
@@ -27,6 +27,7 @@ export const jsonHandler: ExtensionHandler = {
canLint: false,
canFormat: true,
language: "json",
+ hasTabs: true,
async customFormat(
info: ExtensionHandlerMethodInfo,

MODIFY
packages/@romefrontend/core/common/file-handlers/markdown.ts
packages/@romefrontend/core/common/file-handlers/markdown.ts
@@ -6,6 +6,7 @@ export const markdownHandler: ExtensionHandler = {
canLint: true,
canFormat: true,
language: "md",
+ hasTabs: true,
async parse({mtime, path, file, worker}) {
const sourceText = await worker.readFile(file.real);

MODIFY
packages/@romefrontend/core/common/file-handlers/text.ts
packages/@romefrontend/core/common/file-handlers/text.ts
@@ -13,6 +13,7 @@ export const textHandler: PartialExtensionHandler = {
canLint: false,
canFormat: false,
language: "unknown",
+ hasTabs: false,
async parse({path, mtime, file, worker}) {
const src = await worker.readFile(file.real);

MODIFY
packages/@romefrontend/core/common/file-handlers/types.ts
packages/@romefrontend/core/common/file-handlers/types.ts
@@ -48,7 +48,7 @@ export type PartialExtensionHandler = {
isAsset?: boolean;
canHaveScale?: boolean;
language: DiagnosticLanguage;
-
+ hasTabs: boolean;
canLint: boolean;
canFormat: boolean;

MODIFY
packages/@romefrontend/core/index.ts
packages/@romefrontend/core/index.ts
export * from "./common/constants";
-export {getFileHandler} from "./common/file-handlers/index";
+export {getFileHandlerFromPath} from "./common/file-handlers/index";
export {default as Client} from "./client/Client";
export {localCommands} from "./client/commands";

MODIFY
packages/@romefrontend/core/server/commands.ts
packages/@romefrontend/core/server/commands.ts
@@ -37,6 +37,7 @@ import {JSONPropertyValue} from "@romefrontend/codec-json";
import {SharedCommand} from "../common/commands";
import {DiagnosticsPrinter} from "@romefrontend/cli-diagnostics";
import {Markup} from "@romefrontend/cli-layout";
+import init from "@romefrontend/core/server/commands/init";
export type ServerCommand<Flags extends Dict<unknown>> = SharedCommand<Flags> & {
callback: (
@@ -112,4 +113,5 @@ serverCommands.set("_moduleSignature", _moduleSignature);
serverCommands.set("noop", noop);
serverCommands.set("_projectDump", _projectDump);
serverCommands.set("json", json);
+serverCommands.set("init", init);
serverCommands.set("recover", recover);

ADD
packages/@romefrontend/core/server/commands/init.test.ts
+import {test} from "rome";
+import {createIntegrationTest} from "@romefrontend/test-helpers";
+import {dedent} from "@romefrontend/string-utils";
+
+test(
+ "should create the .editorconfig file with correct extensions if it doesn't exist and add 'unknownVariable' to globals",
+ createIntegrationTest(
+ {
+ files: {
+ "index.js": "unknownVariable",
+ "config.yml": dedent`
+ something: foo
+ - else; bar
+ `,
+ },
+ },
+ async (t, {client}) => {
+ await client.query({commandName: "init"});
+ },
+ ),
+);
+
+test(
+ "should not create the .editorconfig file if it exists",
+ createIntegrationTest(
+ {
+ files: {
+ ".editorconfig": "",
+ },
+ },
+ async (t, {client}) => {
+ await client.query({commandName: "init"});
+ },
+ ),
+);

ADD
packages/@romefrontend/core/server/commands/init.ts
+import {createServerCommand} from "@romefrontend/core/server/commands";
+import {commandCategories} from "@romefrontend/core/common/commands";
+import {markup} from "@romefrontend/cli-layout";
+import {ServerRequest} from "@romefrontend/core";
+import {getFileHandlerFromExtension} from "@romefrontend/core/common/file-handlers";
+import {ExtensionHandler} from "@romefrontend/core/common/file-handlers/types";
+import {dedent} from "@romefrontend/string-utils";
+import {exists, writeFile} from "@romefrontend/fs";
+
+export default createServerCommand({
+ category: commandCategories.PROCESS_MANAGEMENT,
+ description: markup`initialise the project`,
+ usage: "",
+ examples: [],
+ defineFlags() {
+ return {};
+ },
+ async callback({server, client, reporter}: ServerRequest) {
+ const projectPath = client.flags.cwd;
+ const editorConfigPath = projectPath.append(".editorconfig");
+
+ if (await exists(editorConfigPath)) {
+ reporter.info(markup`.editorconfig file already exists`);
+ } else {
+ await server.projectManager.assertProject(projectPath);
+
+ const paths = server.memoryFs.glob(projectPath);
+
+ // tracking unique extensions
+ const uniqueExtensions = new Set(
+ Array.from(paths, (path) => path.getExtensions()),
+ );
+ // track ext => handler
+ const uniqueHandlers = new Map<string, ExtensionHandler>();
+ for (const ext of uniqueExtensions) {
+ // there might be multiple projects, but we want to keep everything unique
+ for (const [, project] of server.projectManager.projects) {
+ const handler = getFileHandlerFromExtension(ext, project.config);
+ if (handler && !uniqueHandlers.has(ext)) {
+ uniqueHandlers.set(ext, handler);
+ }
+ }
+ }
+
+ let editorConfigTabExtensions: Array<string> = [];
+ let editorConfigSpaceExtensions: Array<string> = [];
+ for (const [ext, handler] of uniqueHandlers) {
+ if (handler.hasTabs) {
+ editorConfigTabExtensions.push(`*${ext}`);
+ } else {
+ editorConfigSpaceExtensions.push(`*${ext}`);
+ }
+ }
+
+ let editorConfigTemplate = dedent`
+ [*]
+ end_of_line = lf
+ trim_trailing_whitespace = true
+ insert_final_newline = true
+ charset = utf-8
+ indent_style = space
+ indent_size = 2
+ `;
+
+ if (editorConfigTabExtensions.length > 0) {
+ editorConfigTemplate += "\n\n";
+
+ editorConfigTemplate += dedent`
+ [{${editorConfigTabExtensions.join(", ")}}]
+ end_of_line = lf
+ trim_trailing_whitespace = true
+ insert_final_newline = true
+ charset = utf-8
+ indent_style = tab
+ indent_size = 2
+ `;
+ }
+
+ if (editorConfigSpaceExtensions.length > 0) {
+ editorConfigTemplate += "\n\n";
+
+ editorConfigTemplate += dedent`
+ [{${editorConfigSpaceExtensions.join(", ")}}]
+ end_of_line = lf
+ trim_trailing_whitespace = true
+ insert_final_newline = true
+ charset = utf-8
+ indent_style = space
+ indent_size = 2
+ `;
+ }
+
+ await writeFile(editorConfigPath, editorConfigTemplate);
+
+ reporter.success(markup`.editorconfig successfully created`);
+ }
+
+ return true;
+ },
+});

MODIFY
packages/@romefrontend/core/server/dependencies/DependencyNode.ts
packages/@romefrontend/core/server/dependencies/DependencyNode.ts
@@ -19,7 +19,7 @@ import {ProjectDefinition} from "@romefrontend/project";
import DependencyOrderer, {DependencyOrder} from "./DependencyOrderer";
import {WorkerAnalyzeDependencyResult} from "../../common/bridges/WorkerBridge";
import {AbsoluteFilePath, AbsoluteFilePathMap} from "@romefrontend/path";
-import {getFileHandler} from "../../common/file-handlers/index";
+import {getFileHandlerFromPath} from "../../common/file-handlers/index";
import {ExtensionHandler} from "../../common/file-handlers/types";
import {
AnalyzeDependency,
@@ -101,7 +101,7 @@ export default class DependencyNode {
this.analyze = res;
- const {handler} = getFileHandler(ref.real, this.project.config);
+ const {handler} = getFileHandlerFromPath(ref.real, this.project.config);
this.handler = handler;
}

MODIFY
packages/@romefrontend/core/server/fs/MemoryFileSystem.ts
packages/@romefrontend/core/server/fs/MemoryFileSystem.ts
@@ -41,7 +41,7 @@ import {
readFileText,
watch,
} from "@romefrontend/fs";
-import {getFileHandler} from "../../common/file-handlers/index";
+import {getFileHandlerFromPath} from "../../common/file-handlers/index";
import crypto = require("crypto");
import fs = require("fs");
import {FileNotFound} from "@romefrontend/core/common/FileNotFound";
@@ -609,7 +609,10 @@ export default class MemoryFileSystem {
}
// 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" && getFileHandler(path, project.config) === undefined) {
+ if (
+ type === "file" &&
+ getFileHandlerFromPath(path, project.config) === undefined
+ ) {
return true;
}

MODIFY
packages/@romefrontend/core/server/project/ProjectManager.ts
packages/@romefrontend/core/server/project/ProjectManager.ts
@@ -46,7 +46,7 @@ import {
import {FileReference, JSONFileReference} from "../../common/types/files";
import {
GetFileHandlerResult,
- getFileHandler,
+ getFileHandlerFromPath,
} from "../../common/file-handlers/index";
import {IMPLICIT_JS_EXTENSIONS} from "../../common/file-handlers/javascript";
import {createDirectory, readFileText} from "@romefrontend/fs";
@@ -733,7 +733,7 @@ export default class ProjectManager {
if (project === undefined) {
return {ext: "", handler: undefined};
} else {
- return getFileHandler(path, project.config);
+ return getFileHandlerFromPath(path, project.config);
}
}

Generate .editorconfig and populate config with globals (#875)

Co-authored-by: Sebastian <853712+sebmck@users.noreply.github.com>