Skip to content

Commit

Permalink
fix: Support legacy schema properties (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdjermanovic authored Nov 4, 2024
1 parent c24083b commit 3a87bbb
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 0 deletions.
15 changes: 15 additions & 0 deletions packages/compat/src/fixup-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ export function fixupRule(ruleDefinition) {
create: ruleCreate,
};

// copy `schema` property of function-style rule or top-level `schema` property of object-style rule into `meta` object
// @ts-ignore -- top-level `schema` property was not offically supported for object-style rules so it doesn't exist in types
const { schema } = ruleDefinition;
if (schema) {
if (!newRuleDefinition.meta) {
newRuleDefinition.meta = { schema };
} else {
newRuleDefinition.meta = {
...newRuleDefinition.meta,
// top-level `schema` had precedence over `meta.schema` so it's okay to overwrite `meta.schema` if it exists
schema,
};
}
}

// cache the fixed up rule
fixedUpRuleReplacements.set(ruleDefinition, newRuleDefinition);
fixedUpRules.add(newRuleDefinition);
Expand Down
178 changes: 178 additions & 0 deletions packages/compat/tests/fixup-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,184 @@ describe("@eslint/backcompat", () => {
assert.strictEqual(fixedUpRule, fixedUpRule2);
});

it("should return a new rule object with `meta.schema` when a rule with top-level `schema` and without `meta` is passed to fixupRule", () => {
const schema = [{ type: "string" }];
const rule = {
schema,
create(context) {
return {
Identifier(node) {
context.report(node, context.options[0]);
},
};
},
};
const fixedUpRule = fixupRule(rule);

assert.notStrictEqual(rule, fixedUpRule);
assert.deepStrictEqual(Object.keys(fixedUpRule), [
...Object.keys(rule),
"meta",
]);
assert.strictEqual(typeof fixedUpRule.meta, "object");
assert.deepStrictEqual(fixedUpRule.meta.schema, schema);

const config = {
plugins: {
test: {
rules: {
"test-rule": fixedUpRule,
},
},
},
rules: {
"test/test-rule": ["error", "my-option"],
},
};

const linter = new Linter();
const code = "var foo;";
const messages = linter.verify(code, config, {
filename: "test.js",
});

assert.strictEqual(messages.length, 1);
assert.strictEqual(messages[0].message, "my-option");
});

it("should return a new rule object with `meta.schema` when a rule with top-level `schema` and with `meta` is passed to fixupRule", () => {
const schema = [{ type: "string" }];
const rule = {
schema,
meta: {
docs: {},
},
create(context) {
return {
Identifier(node) {
context.report(node, context.options[0]);
},
};
},
};
const fixedUpRule = fixupRule(rule);

assert.notStrictEqual(rule, fixedUpRule);
assert.deepStrictEqual(Object.keys(rule), Object.keys(fixedUpRule));
assert.deepStrictEqual(Object.keys(fixedUpRule.meta), [
...Object.keys(rule.meta),
"schema",
]);
assert.deepStrictEqual(fixedUpRule.meta.schema, schema);

const config = {
plugins: {
test: {
rules: {
"test-rule": fixedUpRule,
},
},
},
rules: {
"test/test-rule": ["error", "my-option"],
},
};

const linter = new Linter();
const code = "var foo;";
const messages = linter.verify(code, config, {
filename: "test.js",
});

assert.strictEqual(messages.length, 1);
assert.strictEqual(messages[0].message, "my-option");
});

it("should return a rule object when a function-style rule is passed to fixupRule", () => {
function rule(context) {
return {
Identifier(node) {
context.report(node, "My message.");
},
};
}
const fixedUpRule = fixupRule(rule);

assert.strictEqual(typeof fixedUpRule, "object");
assert.deepStrictEqual(Object.keys(fixedUpRule), ["create"]);
assert.strictEqual(typeof fixedUpRule.create, "function");
assert.notStrictEqual(rule, fixedUpRule.create); // the original rule should be wrapped in `create`

const config = {
plugins: {
test: {
rules: {
"test-rule": fixedUpRule,
},
},
},
rules: {
"test/test-rule": "error",
},
};

const linter = new Linter();
const code = "var foo;";
const messages = linter.verify(code, config, {
filename: "test.js",
});

assert.strictEqual(messages.length, 1);
assert.strictEqual(messages[0].message, "My message.");
});

it("should return a rule object with `meta.schema` when a function-style rule with schema is passed to fixupRule", () => {
function rule(context) {
return {
Identifier(node) {
context.report(node, context.options[0]);
},
};
}

const schema = [{ type: "string" }];
rule.schema = schema;

const fixedUpRule = fixupRule(rule);

assert.strictEqual(typeof fixedUpRule, "object");
assert.deepStrictEqual(Object.keys(fixedUpRule), [
"create",
"meta",
]);
assert.strictEqual(typeof fixedUpRule.create, "function");
assert.notStrictEqual(rule, fixedUpRule.create); // the original rule should be wrapped in `create`
assert.strictEqual(typeof fixedUpRule.meta, "object");
assert.deepStrictEqual(fixedUpRule.meta.schema, schema);

const config = {
plugins: {
test: {
rules: {
"test-rule": fixedUpRule,
},
},
},
rules: {
"test/test-rule": ["error", "my-option"],
},
};

const linter = new Linter();
const code = "var foo;";
const messages = linter.verify(code, config, {
filename: "test.js",
});

assert.strictEqual(messages.length, 1);
assert.strictEqual(messages[0].message, "my-option");
});

it("should create a rule where getDeclaredVariables() returns the same value as sourceCode.getDeclaredVariables(node)", () => {
const rule = {
create(context) {
Expand Down

0 comments on commit 3a87bbb

Please sign in to comment.