mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
26425a137b
This PR extracts the core part of https://github.com/denoland/deno/pull/27203 to make it easier to review and land in parts. It contains: - The JS plugin code the deserializes and walks the buffer - The Rust portion to serialize SWC to the buffer format (a bunch of nodes are still todos, but imo these can land anytime later) - Basic lint plugin types, without the AST node types to make this PR easier to review - Added more code comments to explain the format etc. More fixes and changes will be done in follow-up PRs. --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
557 lines
11 KiB
TypeScript
557 lines
11 KiB
TypeScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
import { assertEquals } from "./test_util.ts";
|
|
|
|
// TODO(@marvinhagemeister) Remove once we land "official" types
|
|
export interface LintReportData {
|
|
// deno-lint-ignore no-explicit-any
|
|
node: any;
|
|
message: string;
|
|
}
|
|
// TODO(@marvinhagemeister) Remove once we land "official" types
|
|
interface LintContext {
|
|
id: string;
|
|
}
|
|
// TODO(@marvinhagemeister) Remove once we land "official" types
|
|
// deno-lint-ignore no-explicit-any
|
|
type LintVisitor = Record<string, (node: any) => void>;
|
|
|
|
// TODO(@marvinhagemeister) Remove once we land "official" types
|
|
interface LintRule {
|
|
create(ctx: LintContext): LintVisitor;
|
|
destroy?(): void;
|
|
}
|
|
|
|
// TODO(@marvinhagemeister) Remove once we land "official" types
|
|
interface LintPlugin {
|
|
name: string;
|
|
rules: Record<string, LintRule>;
|
|
}
|
|
|
|
function runLintPlugin(plugin: LintPlugin, fileName: string, source: string) {
|
|
// deno-lint-ignore no-explicit-any
|
|
return (Deno as any)[(Deno as any).internal].runLintPlugin(
|
|
plugin,
|
|
fileName,
|
|
source,
|
|
);
|
|
}
|
|
|
|
function testPlugin(
|
|
source: string,
|
|
rule: LintRule,
|
|
) {
|
|
const plugin = {
|
|
name: "test-plugin",
|
|
rules: {
|
|
testRule: rule,
|
|
},
|
|
};
|
|
|
|
return runLintPlugin(plugin, "source.tsx", source);
|
|
}
|
|
|
|
function testVisit(source: string, ...selectors: string[]): string[] {
|
|
const log: string[] = [];
|
|
|
|
testPlugin(source, {
|
|
create() {
|
|
const visitor: LintVisitor = {};
|
|
|
|
for (const s of selectors) {
|
|
visitor[s] = () => log.push(s);
|
|
}
|
|
|
|
return visitor;
|
|
},
|
|
});
|
|
|
|
return log;
|
|
}
|
|
|
|
function testLintNode(source: string, ...selectors: string[]) {
|
|
// deno-lint-ignore no-explicit-any
|
|
const log: any[] = [];
|
|
|
|
testPlugin(source, {
|
|
create() {
|
|
const visitor: LintVisitor = {};
|
|
|
|
for (const s of selectors) {
|
|
visitor[s] = (node) => {
|
|
log.push(node[Symbol.for("Deno.lint.toJsValue")]());
|
|
};
|
|
}
|
|
|
|
return visitor;
|
|
},
|
|
});
|
|
|
|
return log;
|
|
}
|
|
|
|
Deno.test("Plugin - visitor enter/exit", () => {
|
|
const enter = testVisit("foo", "Identifier");
|
|
assertEquals(enter, ["Identifier"]);
|
|
|
|
const exit = testVisit("foo", "Identifier:exit");
|
|
assertEquals(exit, ["Identifier:exit"]);
|
|
|
|
const both = testVisit("foo", "Identifier", "Identifier:exit");
|
|
assertEquals(both, ["Identifier", "Identifier:exit"]);
|
|
});
|
|
|
|
Deno.test("Plugin - Program", () => {
|
|
const node = testLintNode("", "Program");
|
|
assertEquals(node[0], {
|
|
type: "Program",
|
|
sourceType: "script",
|
|
range: [1, 1],
|
|
body: [],
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - BlockStatement", () => {
|
|
const node = testLintNode("{ foo; }", "BlockStatement");
|
|
assertEquals(node[0], {
|
|
type: "BlockStatement",
|
|
range: [1, 9],
|
|
body: [{
|
|
type: "ExpressionStatement",
|
|
range: [3, 7],
|
|
expression: {
|
|
type: "Identifier",
|
|
name: "foo",
|
|
range: [3, 6],
|
|
},
|
|
}],
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - BreakStatement", () => {
|
|
let node = testLintNode("break;", "BreakStatement");
|
|
assertEquals(node[0], {
|
|
type: "BreakStatement",
|
|
range: [1, 7],
|
|
label: null,
|
|
});
|
|
|
|
node = testLintNode("break foo;", "BreakStatement");
|
|
assertEquals(node[0], {
|
|
type: "BreakStatement",
|
|
range: [1, 11],
|
|
label: {
|
|
type: "Identifier",
|
|
range: [7, 10],
|
|
name: "foo",
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - ContinueStatement", () => {
|
|
let node = testLintNode("continue;", "ContinueStatement");
|
|
assertEquals(node[0], {
|
|
type: "ContinueStatement",
|
|
range: [1, 10],
|
|
label: null,
|
|
});
|
|
|
|
node = testLintNode("continue foo;", "ContinueStatement");
|
|
assertEquals(node[0], {
|
|
type: "ContinueStatement",
|
|
range: [1, 14],
|
|
label: {
|
|
type: "Identifier",
|
|
range: [10, 13],
|
|
name: "foo",
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - DebuggerStatement", () => {
|
|
const node = testLintNode("debugger;", "DebuggerStatement");
|
|
assertEquals(node[0], {
|
|
type: "DebuggerStatement",
|
|
range: [1, 10],
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - DoWhileStatement", () => {
|
|
const node = testLintNode("do {} while (foo);", "DoWhileStatement");
|
|
assertEquals(node[0], {
|
|
type: "DoWhileStatement",
|
|
range: [1, 19],
|
|
test: {
|
|
type: "Identifier",
|
|
range: [14, 17],
|
|
name: "foo",
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [4, 6],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - ExpressionStatement", () => {
|
|
const node = testLintNode("foo;", "ExpressionStatement");
|
|
assertEquals(node[0], {
|
|
type: "ExpressionStatement",
|
|
range: [1, 5],
|
|
expression: {
|
|
type: "Identifier",
|
|
range: [1, 4],
|
|
name: "foo",
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - ForInStatement", () => {
|
|
const node = testLintNode("for (a in b) {}", "ForInStatement");
|
|
assertEquals(node[0], {
|
|
type: "ForInStatement",
|
|
range: [1, 16],
|
|
left: {
|
|
type: "Identifier",
|
|
range: [6, 7],
|
|
name: "a",
|
|
},
|
|
right: {
|
|
type: "Identifier",
|
|
range: [11, 12],
|
|
name: "b",
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [14, 16],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - ForOfStatement", () => {
|
|
let node = testLintNode("for (a of b) {}", "ForOfStatement");
|
|
assertEquals(node[0], {
|
|
type: "ForOfStatement",
|
|
range: [1, 16],
|
|
await: false,
|
|
left: {
|
|
type: "Identifier",
|
|
range: [6, 7],
|
|
name: "a",
|
|
},
|
|
right: {
|
|
type: "Identifier",
|
|
range: [11, 12],
|
|
name: "b",
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [14, 16],
|
|
body: [],
|
|
},
|
|
});
|
|
|
|
node = testLintNode("for await (a of b) {}", "ForOfStatement");
|
|
assertEquals(node[0], {
|
|
type: "ForOfStatement",
|
|
range: [1, 22],
|
|
await: true,
|
|
left: {
|
|
type: "Identifier",
|
|
range: [12, 13],
|
|
name: "a",
|
|
},
|
|
right: {
|
|
type: "Identifier",
|
|
range: [17, 18],
|
|
name: "b",
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [20, 22],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - ForStatement", () => {
|
|
let node = testLintNode("for (;;) {}", "ForStatement");
|
|
assertEquals(node[0], {
|
|
type: "ForStatement",
|
|
range: [1, 12],
|
|
init: null,
|
|
test: null,
|
|
update: null,
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [10, 12],
|
|
body: [],
|
|
},
|
|
});
|
|
|
|
node = testLintNode("for (a; b; c) {}", "ForStatement");
|
|
assertEquals(node[0], {
|
|
type: "ForStatement",
|
|
range: [1, 17],
|
|
init: {
|
|
type: "Identifier",
|
|
range: [6, 7],
|
|
name: "a",
|
|
},
|
|
test: {
|
|
type: "Identifier",
|
|
range: [9, 10],
|
|
name: "b",
|
|
},
|
|
update: {
|
|
type: "Identifier",
|
|
range: [12, 13],
|
|
name: "c",
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [15, 17],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - IfStatement", () => {
|
|
let node = testLintNode("if (foo) {}", "IfStatement");
|
|
assertEquals(node[0], {
|
|
type: "IfStatement",
|
|
range: [1, 12],
|
|
test: {
|
|
type: "Identifier",
|
|
name: "foo",
|
|
range: [5, 8],
|
|
},
|
|
consequent: {
|
|
type: "BlockStatement",
|
|
range: [10, 12],
|
|
body: [],
|
|
},
|
|
alternate: null,
|
|
});
|
|
|
|
node = testLintNode("if (foo) {} else {}", "IfStatement");
|
|
assertEquals(node[0], {
|
|
type: "IfStatement",
|
|
range: [1, 20],
|
|
test: {
|
|
type: "Identifier",
|
|
name: "foo",
|
|
range: [5, 8],
|
|
},
|
|
consequent: {
|
|
type: "BlockStatement",
|
|
range: [10, 12],
|
|
body: [],
|
|
},
|
|
alternate: {
|
|
type: "BlockStatement",
|
|
range: [18, 20],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - LabeledStatement", () => {
|
|
const node = testLintNode("foo: {};", "LabeledStatement");
|
|
assertEquals(node[0], {
|
|
type: "LabeledStatement",
|
|
range: [1, 8],
|
|
label: {
|
|
type: "Identifier",
|
|
name: "foo",
|
|
range: [1, 4],
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [6, 8],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - ReturnStatement", () => {
|
|
let node = testLintNode("return", "ReturnStatement");
|
|
assertEquals(node[0], {
|
|
type: "ReturnStatement",
|
|
range: [1, 7],
|
|
argument: null,
|
|
});
|
|
|
|
node = testLintNode("return foo;", "ReturnStatement");
|
|
assertEquals(node[0], {
|
|
type: "ReturnStatement",
|
|
range: [1, 12],
|
|
argument: {
|
|
type: "Identifier",
|
|
name: "foo",
|
|
range: [8, 11],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - SwitchStatement", () => {
|
|
const node = testLintNode(
|
|
`switch (foo) {
|
|
case foo:
|
|
case bar:
|
|
break;
|
|
default:
|
|
{}
|
|
}`,
|
|
"SwitchStatement",
|
|
);
|
|
assertEquals(node[0], {
|
|
type: "SwitchStatement",
|
|
range: [1, 94],
|
|
discriminant: {
|
|
type: "Identifier",
|
|
range: [9, 12],
|
|
name: "foo",
|
|
},
|
|
cases: [
|
|
{
|
|
type: "SwitchCase",
|
|
range: [22, 31],
|
|
test: {
|
|
type: "Identifier",
|
|
range: [27, 30],
|
|
name: "foo",
|
|
},
|
|
consequent: [],
|
|
},
|
|
{
|
|
type: "SwitchCase",
|
|
range: [38, 62],
|
|
test: {
|
|
type: "Identifier",
|
|
range: [43, 46],
|
|
name: "bar",
|
|
},
|
|
consequent: [
|
|
{
|
|
type: "BreakStatement",
|
|
label: null,
|
|
range: [56, 62],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: "SwitchCase",
|
|
range: [69, 88],
|
|
test: null,
|
|
consequent: [
|
|
{
|
|
type: "BlockStatement",
|
|
range: [86, 88],
|
|
body: [],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - ThrowStatement", () => {
|
|
const node = testLintNode("throw foo;", "ThrowStatement");
|
|
assertEquals(node[0], {
|
|
type: "ThrowStatement",
|
|
range: [1, 11],
|
|
argument: {
|
|
type: "Identifier",
|
|
range: [7, 10],
|
|
name: "foo",
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - TryStatement", () => {
|
|
let node = testLintNode("try {} catch {};", "TryStatement");
|
|
assertEquals(node[0], {
|
|
type: "TryStatement",
|
|
range: [1, 16],
|
|
block: {
|
|
type: "BlockStatement",
|
|
range: [5, 7],
|
|
body: [],
|
|
},
|
|
handler: {
|
|
type: "CatchClause",
|
|
range: [8, 16],
|
|
param: null,
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [14, 16],
|
|
body: [],
|
|
},
|
|
},
|
|
finalizer: null,
|
|
});
|
|
|
|
node = testLintNode("try {} catch (e) {};", "TryStatement");
|
|
assertEquals(node[0], {
|
|
type: "TryStatement",
|
|
range: [1, 20],
|
|
block: {
|
|
type: "BlockStatement",
|
|
range: [5, 7],
|
|
body: [],
|
|
},
|
|
handler: {
|
|
type: "CatchClause",
|
|
range: [8, 20],
|
|
param: {
|
|
type: "Identifier",
|
|
range: [15, 16],
|
|
name: "e",
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [18, 20],
|
|
body: [],
|
|
},
|
|
},
|
|
finalizer: null,
|
|
});
|
|
|
|
node = testLintNode("try {} finally {};", "TryStatement");
|
|
assertEquals(node[0], {
|
|
type: "TryStatement",
|
|
range: [1, 18],
|
|
block: {
|
|
type: "BlockStatement",
|
|
range: [5, 7],
|
|
body: [],
|
|
},
|
|
handler: null,
|
|
finalizer: {
|
|
type: "BlockStatement",
|
|
range: [16, 18],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|
|
|
|
Deno.test("Plugin - WhileStatement", () => {
|
|
const node = testLintNode("while (foo) {}", "WhileStatement");
|
|
assertEquals(node[0], {
|
|
type: "WhileStatement",
|
|
range: [1, 15],
|
|
test: {
|
|
type: "Identifier",
|
|
range: [8, 11],
|
|
name: "foo",
|
|
},
|
|
body: {
|
|
type: "BlockStatement",
|
|
range: [13, 15],
|
|
body: [],
|
|
},
|
|
});
|
|
});
|