Skip to content

Commit

Permalink
Merge pull request #3598 from wanlwanl/wanl/adapt-new-rules-to-old-rules
Browse files Browse the repository at this point in the history
[TSV] adapt new rules to old rules
  • Loading branch information
wanlwanl authored Jan 24, 2025
2 parents a327b82 + 9a5ce80 commit e1764dc
Show file tree
Hide file tree
Showing 32 changed files with 2,725 additions and 278 deletions.
32 changes: 32 additions & 0 deletions eng/tools/eslint-plugin-tsv/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@azure-tools/eslint-plugin-tsv",
"private": true,
"type": "module",
"main": "src/index.js",
"dependencies": {
"ajv": "^8.17.1",
"yaml-eslint-parser": "^1.2.3"
},
"peerDependencies": {
"eslint": ">=9.0.0"
},
"devDependencies": {
"@types/node": "^18.19.31",
"@vitest/coverage-v8": "^2.0.4",
"eslint": "^9.17.0",
"memfs": "^4.15.0",
"rimraf": "^5.0.10",
"typescript": "~5.6.2",
"vitest": "^2.0.4"
},
"scripts": {
"build": "tsc --build",
"cbt": "npm run clean && npm run build && npm run test:ci",
"clean": "rimraf ./dist ./temp",
"test": "vitest",
"test:ci": "vitest run --coverage --reporter=verbose"
},
"engines": {
"node": ">= 18.0.0"
}
}
117 changes: 117 additions & 0 deletions eng/tools/eslint-plugin-tsv/src/config/config-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copied from https://github.com/microsoft/typespec/blob/main/packages/compiler/src/config/config-schema.ts

import type { JSONSchemaType } from "ajv";
import { EmitterOptions, TypeSpecRawConfig } from "./types.js";

export const emitterOptionsSchema: JSONSchemaType<EmitterOptions> = {
type: "object",
additionalProperties: true,
required: [],
properties: {
"emitter-output-dir": { type: "string", nullable: true } as any,
},
};

export const TypeSpecConfigJsonSchema: JSONSchemaType<TypeSpecRawConfig> = {
type: "object",
additionalProperties: false,
properties: {
extends: {
type: "string",
nullable: true,
},
"environment-variables": {
type: "object",
nullable: true,
required: [],
additionalProperties: {
type: "object",
properties: {
default: { type: "string" },
},
required: ["default"],
},
},
parameters: {
type: "object",
nullable: true,
required: [],
additionalProperties: {
type: "object",
properties: {
default: { type: "string" },
},
required: ["default"],
},
},

"output-dir": {
type: "string",
nullable: true,
},
"warn-as-error": {
type: "boolean",
nullable: true,
},
trace: {
oneOf: [
{ type: "string" },
{
type: "array",
items: { type: "string" },
},
],
} as any, // Issue with AJV optional property typing https://github.com/ajv-validator/ajv/issues/1664
imports: {
type: "array",
nullable: true,
items: { type: "string" },
},
emit: {
type: "array",
nullable: true,
items: { type: "string" },
},
options: {
type: "object",
nullable: true,
required: [],
additionalProperties: emitterOptionsSchema,
},
emitters: {
type: "object",
nullable: true,
deprecated: true,
required: [],
additionalProperties: {
oneOf: [{ type: "boolean" }, emitterOptionsSchema],
},
},

linter: {
type: "object",
nullable: true,
required: [],
additionalProperties: false,
properties: {
extends: {
type: "array",
nullable: true,
items: { type: "string" },
},
enable: {
type: "object",
required: [],
nullable: true,
additionalProperties: { type: "boolean" },
},
disable: {
type: "object",
required: [],
nullable: true,
additionalProperties: { type: "string" },
},
},
} as any, // ajv type system doesn't like the string templates
},
};
112 changes: 112 additions & 0 deletions eng/tools/eslint-plugin-tsv/src/config/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copied from https://github.com/microsoft/typespec/blob/main/packages/compiler/src/config/types.ts

import type { Diagnostic, RuleRef } from "@typespec/compiler";
import type { YamlScript } from "../yaml/types.js";

/**
* Represent the normalized user configuration.
*/
export interface TypeSpecConfig {
/**
* Project root.
*/
projectRoot: string;

/** Yaml file used in this configuration. */
file?: YamlScript;

/**
* Path to the config file used to create this configuration.
*/
filename?: string;

/**
* Diagnostics reported while loading the configuration
*/
diagnostics: Diagnostic[];

/**
* Path to another TypeSpec config to extend.
*/
extends?: string;

/**
* Environment variables configuration
*/
environmentVariables?: Record<string, ConfigEnvironmentVariable>;

/**
* Parameters that can be used
*/
parameters?: Record<string, ConfigParameter>;

/**
* Treat warning as error.
*/
warnAsError?: boolean;

/**
* Output directory
*/
outputDir: string;

/**
* Trace options.
*/
trace?: string[];

/**
* Additional imports.
*/
imports?: string[];

/**
* Name of emitters or path to emitters that should be used.
*/
emit?: string[];

/**
* Name of emitters or path to emitters that should be used.
*/
options?: Record<string, EmitterOptions>;

linter?: LinterConfig;
}

/**
* Represent the configuration that can be provided in a config file.
*/
export interface TypeSpecRawConfig {
extends?: string;
"environment-variables"?: Record<string, ConfigEnvironmentVariable>;
parameters?: Record<string, ConfigParameter>;

"warn-as-error"?: boolean;
"output-dir"?: string;
trace?: string | string[];
imports?: string[];

emit?: string[];
options?: Record<string, EmitterOptions>;
emitters?: Record<string, boolean | EmitterOptions>;

linter?: LinterConfig;
}

export interface ConfigEnvironmentVariable {
default: string;
}

export interface ConfigParameter {
default: string;
}

export type EmitterOptions = Record<string, unknown> & {
"emitter-output-dir"?: string;
};

export interface LinterConfig {
extends?: RuleRef[];
enable?: Record<RuleRef, boolean>;
disable?: Record<RuleRef, string>;
}
35 changes: 35 additions & 0 deletions eng/tools/eslint-plugin-tsv/src/eslint-plugin-tsv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import parser from "yaml-eslint-parser";
import { NamedESLint } from "./interfaces/named-eslint.js";
import emitAutorest from "./rules/emit-autorest.js";
import kebabCaseOrg from "./rules/kebab-case-org.js";
import tspconfigValidationRules from "./rules/tspconfig-validation-rules.js";

const plugin: NamedESLint.Plugin = {
configs: { recommended: {} },
name: "tsv",
rules: {
[kebabCaseOrg.name]: kebabCaseOrg,
[emitAutorest.name]: emitAutorest,
},
};

plugin.configs.recommended = {
plugins: {
[plugin.name]: plugin,
},
files: ["*.yaml", "**/*.yaml"],
rules: {
[`${plugin.name}/${kebabCaseOrg.name}`]: "error",
[`${plugin.name}/${emitAutorest.name}`]: "error",
},
languageOptions: {
parser: parser,
},
};

tspconfigValidationRules.forEach((rule) => {
plugin.rules![rule.name] = rule;
plugin.configs.recommended.rules![`${plugin.name}/${rule.name}`] = "error";
});

export default plugin;
5 changes: 5 additions & 0 deletions eng/tools/eslint-plugin-tsv/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ESLint } from "eslint";
import tsvPlugin from "./eslint-plugin-tsv.js";

export { ESLint };
export default tsvPlugin;
17 changes: 17 additions & 0 deletions eng/tools/eslint-plugin-tsv/src/interfaces/named-eslint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ESLint, Linter, Rule } from "eslint";

// ESLint with names for convenience

export namespace NamedRule {
export interface RuleModule extends Rule.RuleModule {
name: string;
}
}

export namespace NamedESLint {
export interface Plugin extends ESLint.Plugin {
configs: { recommended: Linter.Config };
name: string;
rules?: Record<string, NamedRule.RuleModule>;
}
}
34 changes: 34 additions & 0 deletions eng/tools/eslint-plugin-tsv/src/interfaces/rule-interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Rule } from "eslint";
import { TypeSpecConfig } from "../config/types.js";

export enum KeyType {
EmitterOption,
Parameter,
}

export interface RuleDocument {
description: string;
error: string;
action: string;
example: string;
}

export interface RuleInfo {
name: string;
documentation: RuleDocument;
functions: {
messages: () => { [messageId: string]: string } | undefined;
condition: (tspconfig: TypeSpecConfig, context: Rule.RuleContext) => boolean;
validation: (tspconfig: TypeSpecConfig, context: Rule.RuleContext, node: Rule.Node) => void;
};
}

export interface CreateCodeGenSDKRuleArgs {
rule: string;
type: KeyType;
key: string;
expectedValue: string | boolean | RegExp;
exampleValue: string | boolean;
extraExplanation?: string;
condition?: (tspconfig: TypeSpecConfig, context: Rule.RuleContext) => boolean;
}
Loading

0 comments on commit e1764dc

Please sign in to comment.