import {
compileSelector,
parseSelector,
splitSelectors,
} from "ext:cli/40_lint_selector.js";
import { core, internals } from "ext:core/mod.js";
const {
op_lint_get_source,
op_lint_report,
op_lint_create_serialized_ast,
op_is_cancelled,
} = core.ops;
let doReport = op_lint_report;
let doGetSource = op_lint_get_source;
const AST_IDX_INVALID = 0;
const AST_GROUP_TYPE = 1;
const NODE_SIZE = 1 + 4 + 4 + 4 + 4;
const PROP_OFFSET = 1;
const CHILD_OFFSET = 1 + 4;
const NEXT_OFFSET = 1 + 4 + 4;
const PARENT_OFFSET = 1 + 4 + 4 + 4;
const SPAN_SIZE = 4 + 4;
const AST_PROP_TYPE = 1;
const AST_PROP_PARENT = 2;
const AST_PROP_RANGE = 3;
const AST_PROP_LENGTH = 4;
const PropFlags = {
Ref: 0,
RefArr: 1,
String: 2,
Number: 3,
Bool: 4,
Null: 5,
Undefined: 6,
Obj: 7,
Regex: 8,
BigInt: 9,
Array: 10,
};
const state = {
plugins: [],
installedPlugins: new Set(),
ignoredRules: new Set(),
};
function resetState() {
state.plugins = [];
state.installedPlugins.clear();
state.ignoredRules.clear();
}
class CancellationToken {
isCancellationRequested() {
return op_is_cancelled();
}
}
class Fixer {
insertTextAfter(node, text) {
return {
range: ([node.range[1], node.range[1]]),
text,
};
}
insertTextAfterRange(range, text) {
return {
range: ([range[1], range[1]]),
text,
};
}
insertTextBefore(node, text) {
return {
range: ([node.range[0], node.range[0]]),
text,
};
}
insertTextBeforeRange(range, text) {
return {
range: ([range[0], range[0]]),
text,
};
}
remove(node) {
return {
range: node.range,
text: "",
};
}
removeRange(range) {
return {
range,
text: "",
};
}
replaceText(node, text) {
return {
range: node.range,
text,
};
}
replaceTextRange(range, text) {
return {
range,
text,
};
}
}
export class SourceCode {
#source = null;
#ctx;
constructor(ctx) {
this.#ctx = ctx;
}
get text() {
return this.#getSource();
}
get ast() {
const program = (getNode(
this.#ctx,
this.#ctx.rootOffset,
));
return program;
}
getText(node) {
const source = this.#getSource();
if (node === undefined) {
return source;
}
return source.slice(node.range[0], node.range[1]);
}
getAncestors(node) {
const { buf } = this.#ctx;
const ancestors = [];
let parent = (node)[INTERNAL_IDX];
while ((parent = readParent(buf, parent)) > AST_IDX_INVALID) {
if (readType(buf, parent) === AST_GROUP_TYPE) continue;
const parentNode = (getNode(this.#ctx, parent));
if (parentNode !== null) {
ancestors.push(parentNode);
}
}
ancestors.reverse();
return ancestors;
}
getAllComments() {
materializeComments(this.#ctx);
return this.#ctx.comments;
}
getCommentsBefore(node) {
materializeComments(this.#ctx);
const before = [];
const { comments } = this.#ctx;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (comment.range[0] <= node.range[0]) {
before.push(comment);
}
}
return before;
}
getCommentsAfter(node) {
materializeComments(this.#ctx);
const after = [];
const { comments } = this.#ctx;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (comment.range[0] >= node.range[1]) {
after.push(comment);
}
}
return after;
}
getCommentsInside(node) {
materializeComments(this.#ctx);
const inside = [];
const { comments } = this.#ctx;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (
comment.range[0] >= node.range[0] && comment.range[1] <= node.range[1]
) {
inside.push(comment);
}
}
return inside;
}
#getSource() {
if (this.#source === null) {
this.#source = doGetSource();
}
return (this.#source);
}
}
export class Context {
id;
filename;
sourceCode;
constructor(ctx, id, fileName) {
this.id = id;
this.filename = fileName;
this.sourceCode = new SourceCode(ctx);
}
getFilename() {
return this.filename;
}
getSourceCode() {
return this.sourceCode;
}
report(data) {
const range = data.node ? data.node.range : data.range ? data.range : null;
if (range == null) {
throw new Error(
"Either `node` or `range` must be provided when reporting an error",
);
}
const start = range[0];
const end = range[1];
if (start > end) {
throw new RangeError(
`Invalid range. Start value is bigger than end value: [${start}, ${end}]`,
);
}
const fixes = [];
if (typeof data.fix === "function") {
const fixer = new Fixer();
const result = data.fix(fixer);
if (Symbol.iterator in result) {
for (const fix of result) {
fixes.push(fix);
}
} else {
fixes.push(result);
}
}
doReport(
this.id,
data.message,
data.hint,
start,
end,
fixes,
);
}
}
function materializeComments(ctx) {
const { buf, commentsOffset, comments, strTable } = ctx;
let offset = commentsOffset;
const count = readU32(buf, offset);
offset += 4;
if (comments.length === count) return;
while (offset < buf.length && comments.length < count) {
const kind = buf[offset];
offset++;
const spanId = readU32(buf, offset);
offset += 4;
const strId = readU32(buf, offset);
offset += 4;
comments.push({
type: kind === 0 ? "Line" : "Block",
range: readSpan(ctx, spanId),
value: getString(strTable, strId),
});
}
}
export function installPlugins(plugins, exclude) {
if (Array.isArray(exclude)) {
for (let i = 0; i < exclude.length; i++) {
state.ignoredRules.add(exclude[i]);
}
}
return plugins.map((plugin) => installPlugin(plugin));
}
function installPlugin(plugin) {
if (typeof plugin !== "object") {
throw new Error("Linter plugin must be an object");
}
if (typeof plugin.name !== "string") {
throw new Error("Linter plugin name must be a string");
}
if (!/^[a-z-]+$/.test(plugin.name)) {
throw new Error(
"Linter plugin name must only contain lowercase letters (a-z) or hyphens (-).",
);
}
if (plugin.name.startsWith("-") || plugin.name.endsWith("-")) {
throw new Error(
"Linter plugin name must start and end with a lowercase letter.",
);
}
if (plugin.name.includes("--")) {
throw new Error(
"Linter plugin name must not have consequtive hyphens.",
);
}
if (typeof plugin.rules !== "object") {
throw new Error("Linter plugin rules must be an object");
}
if (state.installedPlugins.has(plugin.name)) {
throw new Error(`Linter plugin ${plugin.name} has already been registered`);
}
state.plugins.push(plugin);
state.installedPlugins.add(plugin.name);
return {
name: plugin.name,
ruleNames: Object.keys(plugin.rules),
};
}
function getNode(ctx, idx) {
if (idx === AST_IDX_INVALID) return null;
const cached = ctx.nodes.get(idx);
if (cached !== undefined) return (cached);
const node = new FacadeNode(ctx, idx);
ctx.nodes.set(idx, (node));
return (node);
}
function findPropOffset(buf, offset, search) {
const count = buf[offset];
offset += 1;
for (let i = 0; i < count; i++) {
const maybe = offset;
const prop = buf[offset++];
const kind = buf[offset++];
if (prop === search) return maybe;
if (kind === PropFlags.Obj) {
const len = readU32(buf, offset);
offset += 4;
offset += len * (1 + 1 + 4);
} else {
offset += 4;
}
}
return -1;
}
const INTERNAL_CTX = Symbol("ctx");
const INTERNAL_IDX = Symbol("offset");
class FacadeNode {
[INTERNAL_CTX];
[INTERNAL_IDX];
constructor(ctx, idx) {
this[INTERNAL_CTX] = ctx;
this[INTERNAL_IDX] = idx;
}
[Symbol.for("Deno.customInspect")](_, options) {
const json = nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]);
return Deno.inspect(json, options);
}
[Symbol.for("Deno.lint.toJsValue")]() {
return nodeToJson(this[INTERNAL_CTX], this[INTERNAL_IDX]);
}
}
const appliedGetters = new Set();
let hasCommenstGetter = false;
function setNodeGetters(ctx) {
if (appliedGetters.size === ctx.strByProp.length) return;
for (let i = 0; i < ctx.strByProp.length; i++) {
const id = ctx.strByProp[i];
if (id === 0 || appliedGetters.has(i)) continue;
appliedGetters.add(i);
const name = getString(ctx.strTable, id);
Object.defineProperty(FacadeNode.prototype, name, {
enumerable: name !== "parent",
get() {
return readValue(
this[INTERNAL_CTX],
this[INTERNAL_IDX],
i,
getNode,
);
},
});
}
if (!hasCommenstGetter) {
hasCommenstGetter = true;
Object.defineProperty(FacadeNode.prototype, "comments", {
get() {
materializeComments(this[INTERNAL_CTX]);
return this[INTERNAL_CTX].comments;
},
});
}
}
function nodeToJson(ctx, idx) {
const node = {
type: readValue(ctx, idx, AST_PROP_TYPE, nodeToJson),
range: readValue(ctx, idx, AST_PROP_RANGE, nodeToJson),
};
const { buf } = ctx;
let offset = readPropOffset(ctx, idx);
const count = buf[offset++];
for (let i = 0; i < count; i++) {
const prop = buf[offset];
const _kind = buf[offset + 1];
const name = getString(ctx.strTable, ctx.strByProp[prop]);
node[name] = readProperty(ctx, offset, nodeToJson);
offset += 1 + 1 + 4;
}
return node;
}
function readType(buf, idx) {
return buf[idx * NODE_SIZE];
}
function readSpan(ctx, idx) {
let offset = ctx.spansOffset + (idx * SPAN_SIZE);
const start = readU32(ctx.buf, offset);
offset += 4;
const end = readU32(ctx.buf, offset);
return [start, end];
}
function readRawPropOffset(buf, idx) {
const offset = (idx * NODE_SIZE) + PROP_OFFSET;
return readU32(buf, offset);
}
function readPropOffset(ctx, idx) {
return readRawPropOffset(ctx.buf, idx) + ctx.propsOffset;
}
function readChild(buf, idx) {
const offset = (idx * NODE_SIZE) + CHILD_OFFSET;
return readU32(buf, offset);
}
function readNext(buf, idx) {
const offset = (idx * NODE_SIZE) + NEXT_OFFSET;
return readU32(buf, offset);
}
function readParent(buf, idx) {
const offset = (idx * NODE_SIZE) + PARENT_OFFSET;
return readU32(buf, offset);
}
function readRegex(strTable, strId) {
const raw = getString(strTable, strId);
const idx = raw.lastIndexOf("/");
const pattern = raw.slice(1, idx);
const flags = idx < raw.length - 1 ? raw.slice(idx + 1) : undefined;
return new RegExp(pattern, flags);
}
function readObject(ctx, offset, parseNode) {
const { buf, strTable, strByProp } = ctx;
const obj = {};
const count = readU32(buf, offset);
offset += 4;
for (let i = 0; i < count; i++) {
const prop = buf[offset];
const name = getString(strTable, strByProp[prop]);
obj[name] = readProperty(ctx, offset, parseNode);
offset += 1 + 1 + 4;
}
return obj;
}
function readProperty(ctx, offset, parseNode) {
const { buf } = ctx;
const _name = buf[offset++];
const kind = buf[offset++];
if (kind === PropFlags.Ref) {
const value = readU32(buf, offset);
return parseNode(ctx, value);
} else if (kind === PropFlags.RefArr) {
const groupId = readU32(buf, offset);
const nodes = [];
let next = readChild(buf, groupId);
while (next > AST_IDX_INVALID) {
nodes.push(parseNode(ctx, next));
next = readNext(buf, next);
}
return nodes;
} else if (kind === PropFlags.Bool) {
const v = readU32(buf, offset);
return v === 1;
} else if (kind === PropFlags.String) {
const v = readU32(buf, offset);
return getString(ctx.strTable, v);
} else if (kind === PropFlags.Number) {
const v = readU32(buf, offset);
return Number(getString(ctx.strTable, v));
} else if (kind === PropFlags.BigInt) {
const v = readU32(buf, offset);
return BigInt(getString(ctx.strTable, v));
} else if (kind === PropFlags.Regex) {
const v = readU32(buf, offset);
return readRegex(ctx.strTable, v);
} else if (kind === PropFlags.Null) {
return null;
} else if (kind === PropFlags.Undefined) {
return undefined;
} else if (kind === PropFlags.Obj) {
const objOffset = readU32(buf, offset) + ctx.propsOffset;
return readObject(ctx, objOffset, parseNode);
}
throw new Error(`Unknown prop kind: ${kind}`);
}
function readValue(ctx, idx, search, parseNode) {
const { buf } = ctx;
if (search === AST_PROP_TYPE) {
const type = readType(buf, idx);
return getString(ctx.strTable, ctx.strByType[type]);
} else if (search === AST_PROP_RANGE) {
return readSpan(ctx, idx);
} else if (search === AST_PROP_PARENT) {
let parent = readParent(buf, idx);
const parentType = readType(buf, parent);
if (parentType === AST_GROUP_TYPE) {
parent = readParent(buf, parent);
}
return getNode(ctx, parent);
}
const propOffset = readPropOffset(ctx, idx);
const offset = findPropOffset(ctx.buf, propOffset, search);
if (offset === -1) return undefined;
return readProperty(ctx, offset, parseNode);
}
const DECODER = new TextDecoder();
function readU32(buf, i) {
return (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) +
buf[i + 3];
}
function getString(strTable, id) {
const name = strTable.get(id);
if (name === undefined) {
throw new Error(`Missing string id: ${id}`);
}
return name;
}
class MatchCtx {
parentLimitIdx = 0;
constructor(ctx, cancellationToken) {
this.ctx = ctx;
this.cancellationToken = cancellationToken;
}
getParent(idx) {
if (idx === this.parentLimitIdx) return AST_IDX_INVALID;
const parent = readParent(this.ctx.buf, idx);
const parentType = readType(this.ctx.buf, parent);
if (parentType === AST_GROUP_TYPE) {
return readParent(this.ctx.buf, parent);
}
return parent;
}
getType(idx) {
return readType(this.ctx.buf, idx);
}
getField(idx, propId) {
if (idx === AST_IDX_INVALID) return -1;
switch (propId) {
case AST_PROP_TYPE:
case AST_PROP_PARENT:
case AST_PROP_RANGE:
return -1;
}
const { buf } = this.ctx;
let offset = readPropOffset(this.ctx, idx);
offset = findPropOffset(buf, offset, propId);
if (offset === -1) return -1;
const _prop = buf[offset++];
const kind = buf[offset++];
if (kind === PropFlags.Ref) {
return readU32(buf, offset);
}
return -1;
}
getAttrPathValue(idx, propIds, propIdx) {
if (idx === AST_IDX_INVALID) throw -1;
const { buf, strTable, strByType } = this.ctx;
const propId = propIds[propIdx];
switch (propId) {
case AST_PROP_TYPE: {
const type = readType(buf, idx);
return getString(strTable, strByType[type]);
}
case AST_PROP_PARENT:
case AST_PROP_RANGE:
throw -1;
}
let offset = readPropOffset(this.ctx, idx);
offset = findPropOffset(buf, offset, propId);
if (offset === -1) throw -1;
const _prop = buf[offset++];
const kind = buf[offset++];
if (kind === PropFlags.Ref) {
const value = readU32(buf, offset);
if (propIdx === propIds.length - 1) throw -1;
return this.getAttrPathValue(value, propIds, propIdx + 1);
} else if (kind === PropFlags.RefArr) {
const arrIdx = readU32(buf, offset);
offset += 4;
let count = 0;
let child = readChild(buf, arrIdx);
while (child > AST_IDX_INVALID) {
count++;
child = readNext(buf, child);
}
if (
propIdx < propIds.length - 1 && propIds[propIdx + 1] === AST_PROP_LENGTH
) {
return count;
}
throw -1;
} else if (kind === PropFlags.Obj) {
}
if (propIdx < propIds.length - 1) throw -1;
if (kind === PropFlags.String) {
const s = readU32(buf, offset);
return getString(strTable, s);
} else if (kind === PropFlags.Number) {
const s = readU32(buf, offset);
return Number(getString(strTable, s));
} else if (kind === PropFlags.Regex) {
const v = readU32(buf, offset);
return readRegex(strTable, v);
} else if (kind === PropFlags.Bool) {
return readU32(buf, offset) === 1;
} else if (kind === PropFlags.Null) {
return null;
} else if (kind === PropFlags.Undefined) {
return undefined;
}
throw -1;
}
getFirstChild(idx) {
const siblings = this.getSiblings(idx);
return siblings[0] ?? -1;
}
getLastChild(idx) {
const siblings = this.getSiblings(idx);
return siblings.at(-1) ?? -1;
}
getSiblings(idx) {
const { buf } = this.ctx;
const parent = readParent(buf, idx);
const parentType = readType(buf, parent);
if (parentType !== AST_GROUP_TYPE) {
return [];
}
const out = [];
let child = readChild(buf, parent);
while (child > AST_IDX_INVALID) {
out.push(child);
child = readNext(buf, child);
}
return out;
}
subSelect(selectors, idx) {
const prevLimit = this.parentLimitIdx;
this.parentLimitIdx = idx;
try {
return subTraverse(this.ctx, selectors, idx, idx, this.cancellationToken);
} finally {
this.parentLimitIdx = prevLimit;
}
}
}
function createAstContext(buf, token) {
const strTable = new Map();
const commentsOffset = readU32(buf, buf.length - 28);
const propsOffset = readU32(buf, buf.length - 24);
const spansOffset = readU32(buf, buf.length - 20);
const typeMapOffset = readU32(buf, buf.length - 16);
const propMapOffset = readU32(buf, buf.length - 12);
const strTableOffset = readU32(buf, buf.length - 8);
const rootOffset = readU32(buf, buf.length - 4);
let offset = strTableOffset;
const stringCount = readU32(buf, offset);
offset += 4;
let strId = 0;
for (let i = 0; i < stringCount; i++) {
const len = readU32(buf, offset);
offset += 4;
const strBytes = buf.slice(offset, offset + len);
offset += len;
const s = DECODER.decode(strBytes);
strTable.set(strId, s);
strId++;
}
if (strTable.size !== stringCount) {
throw new Error(
`Could not deserialize string table. Expected ${stringCount} items, but got ${strTable.size}`,
);
}
offset = typeMapOffset;
const typeCount = readU32(buf, offset);
offset += 4;
const typeByStr = new Map();
const strByType = new Array(typeCount).fill(0);
for (let i = 0; i < typeCount; i++) {
const v = readU32(buf, offset);
offset += 4;
strByType[i] = v;
typeByStr.set(strTable.get(v), i);
}
offset = propMapOffset;
const propCount = readU32(buf, offset);
offset += 4;
const propByStr = new Map();
const strByProp = new Array(propCount).fill(0);
for (let i = 0; i < propCount; i++) {
const v = readU32(buf, offset);
offset += 4;
strByProp[i] = v;
propByStr.set(strTable.get(v), i);
}
const ctx = {
buf,
strTable,
rootOffset,
spansOffset,
propsOffset,
commentsOffset,
nodes: new Map(),
comments: [],
strTableOffset,
strByProp,
strByType,
typeByStr,
propByStr,
matcher: (null),
};
ctx.matcher = new MatchCtx(ctx, token);
setNodeGetters(ctx);
return ctx;
}
const NOOP = (_node) => {};
export function runPluginsForFile(fileName, serializedAst) {
const token = new CancellationToken();
const ctx = createAstContext(serializedAst, token);
const bySelector = new Map();
const destroyFns = [];
for (let i = 0; i < state.plugins.length; i++) {
const plugin = state.plugins[i];
for (const name of Object.keys(plugin.rules)) {
const rule = plugin.rules[name];
const id = `${plugin.name}/${name}`;
if (state.ignoredRules.has(id)) {
continue;
}
const ruleCtx = new Context(ctx, id, fileName);
const visitor = rule.create(ruleCtx);
for (let key in visitor) {
const fn = visitor[key];
if (fn === undefined) continue;
let isExit = false;
if (key.endsWith(":exit")) {
isExit = true;
key = key.slice(0, -":exit".length);
}
const selectors = splitSelectors(key);
for (let j = 0; j < selectors.length; j++) {
const key = selectors[j];
let info = bySelector.get(key);
if (info === undefined) {
info = { enter: NOOP, exit: NOOP };
bySelector.set(key, info);
}
const prevFn = isExit ? info.exit : info.enter;
const wrapped = (node) => {
prevFn(node);
try {
fn(node);
} catch (err) {
throw new Error(`Visitor "${name}" of plugin "${id}" errored`, {
cause: err,
});
}
};
if (isExit) {
info.exit = wrapped;
} else {
info.enter = wrapped;
}
}
}
if (typeof rule.destroy === "function") {
const destroyFn = rule.destroy.bind(rule);
destroyFns.push(() => {
try {
destroyFn(ruleCtx);
} catch (err) {
throw new Error(`Destroy hook of "${id}" errored`, { cause: err });
}
});
}
}
}
const toElem = (str) => {
const id = ctx.typeByStr.get(str);
return id === undefined ? 0 : id;
};
const toAttr = (str) => {
const id = ctx.propByStr.get(str);
return id === undefined ? 0 : id;
};
const visitors = [];
for (const [sel, info] of bySelector.entries()) {
const parsed = parseSelector(sel, toElem, toAttr)[0];
const matcher = compileSelector(parsed);
visitors.push({ info, matcher });
}
try {
traverse(ctx, visitors, ctx.rootOffset, token);
} finally {
ctx.nodes.clear();
for (let i = 0; i < destroyFns.length; i++) {
destroyFns[i]();
}
}
}
function traverse(ctx, visitors, idx, cancellationToken) {
const { buf } = ctx;
while (idx !== AST_IDX_INVALID) {
if (cancellationToken.isCancellationRequested()) return;
const nodeType = readType(buf, idx);
let exits = null;
if (nodeType !== AST_GROUP_TYPE) {
for (let i = 0; i < visitors.length; i++) {
const v = visitors[i];
if (v.matcher(ctx.matcher, idx)) {
if (v.info.exit !== NOOP) {
if (exits === null) {
exits = [v.info.exit];
} else {
exits.push(v.info.exit);
}
}
if (v.info.enter !== NOOP) {
const node = (getNode(ctx, idx));
v.info.enter(node);
}
}
}
}
try {
const childIdx = readChild(buf, idx);
if (childIdx > AST_IDX_INVALID) {
traverse(ctx, visitors, childIdx, cancellationToken);
}
} finally {
if (exits !== null) {
for (let i = 0; i < exits.length; i++) {
const node = (getNode(ctx, idx));
exits[i](node);
}
}
}
idx = readNext(buf, idx);
}
}
function subTraverse(ctx, selectors, rootIdx, idx, cancellationToken) {
const { buf } = ctx;
while (idx > AST_IDX_INVALID) {
if (cancellationToken.isCancellationRequested()) return false;
const nodeType = readType(buf, idx);
if (nodeType !== AST_GROUP_TYPE) {
for (let i = 0; i < selectors.length; i++) {
const sel = selectors[i];
if (sel(ctx.matcher, idx)) {
return true;
}
}
}
const childIdx = readChild(buf, idx);
if (
childIdx > AST_IDX_INVALID &&
subTraverse(ctx, selectors, rootIdx, childIdx, cancellationToken)
) {
return true;
}
if (idx === rootIdx) {
break;
}
idx = readNext(buf, idx);
}
return false;
}
function _dump(ctx) {
const { buf, strTableOffset, strTable, strByType, strByProp } = ctx;
console.log(strTable);
for (let i = 0; i < strByType.length; i++) {
const v = strByType[i];
if (v > 0) console.log(" > type:", i, getString(ctx.strTable, v), v);
}
console.log();
for (let i = 0; i < strByProp.length; i++) {
const v = strByProp[i];
if (v > 0) console.log(" > prop:", i, getString(ctx.strTable, v), v);
}
console.log();
console.log();
let idx = 0;
while (idx < (strTableOffset / NODE_SIZE)) {
const type = readType(buf, idx);
const child = readChild(buf, idx);
const next = readNext(buf, idx);
const parent = readParent(buf, idx);
const range = readSpan(ctx, idx);
const name = type === AST_IDX_INVALID
? "<invalid>"
: type === AST_GROUP_TYPE
? "<group>"
: getString(ctx.strTable, ctx.strByType[type]);
console.log(`${name}, idx: ${idx}, type: ${type}`);
console.log(` child: ${child}, next: ${next}, parent: ${parent}`);
console.log(` range: ${range[0]}, ${range[1]}`);
const rawOffset = readRawPropOffset(ctx.buf, idx);
let propOffset = readPropOffset(ctx, idx);
const count = buf[propOffset++];
console.log(
` prop count: ${count}, prop offset: ${propOffset} raw offset: ${rawOffset}`,
);
for (let i = 0; i < count; i++) {
const prop = buf[propOffset++];
const kind = buf[propOffset++];
const name = getString(ctx.strTable, ctx.strByProp[prop]);
let kindName = "unknown";
for (const k in PropFlags) {
if (kind === PropFlags[k]) {
kindName = k;
}
}
const v = readU32(buf, propOffset);
propOffset += 4;
if (kind === PropFlags.Ref) {
console.log(` ${name}: ${v} (${kindName}, ${prop})`);
} else if (kind === PropFlags.RefArr) {
console.log(` ${name}: RefArray: ${v}, (${kindName}, ${prop})`);
} else if (kind === PropFlags.Bool) {
console.log(` ${name}: ${v} (${kindName}, ${prop})`);
} else if (kind === PropFlags.String) {
const raw = getString(ctx.strTable, v);
console.log(` ${name}: ${raw} (${kindName}, ${prop})`);
} else if (kind === PropFlags.Number) {
const raw = getString(ctx.strTable, v);
console.log(` ${name}: ${raw} (${kindName}, ${prop})`);
} else if (kind === PropFlags.Regex) {
const raw = getString(ctx.strTable, v);
console.log(` ${name}: ${raw} (${kindName}, ${prop})`);
} else if (kind === PropFlags.Null) {
console.log(` ${name}: null (${kindName}, ${prop})`);
} else if (kind === PropFlags.Undefined) {
console.log(` ${name}: undefined (${kindName}, ${prop})`);
} else if (kind === PropFlags.BigInt) {
const raw = getString(ctx.strTable, v);
console.log(` ${name}: ${raw} (${kindName}, ${prop})`);
} else if (kind === PropFlags.Obj) {
let offset = v + ctx.propsOffset;
const count = readU32(ctx.buf, offset);
offset += 4;
console.log(
` ${name}: Object (${count}) (${kindName}, ${prop}), raw offset ${v}`,
);
}
}
idx++;
}
}
internals.installPlugins = installPlugins;
internals.runPluginsForFile = runPluginsForFile;
internals.resetState = resetState;
function runLintPlugin(plugin, fileName, sourceText) {
installPlugin(plugin);
const diagnostics = [];
doReport = (id, message, hint, start, end, fix) => {
diagnostics.push({
id,
message,
hint,
range: [start, end],
fix,
});
};
doGetSource = () => {
return sourceText;
};
try {
const serializedAst = op_lint_create_serialized_ast(fileName, sourceText);
runPluginsForFile(fileName, serializedAst);
} finally {
resetState();
}
doReport = op_lint_report;
doGetSource = op_lint_get_source;
return diagnostics;
}
Deno.lint.runPlugin = runLintPlugin;