Enable top-level lint

This commit is contained in:
Jordan Eldredge 2022-03-06 23:05:11 -08:00
parent 1880070205
commit 4ce89495cb
17 changed files with 78 additions and 3172 deletions

4
.eslintignore Normal file
View file

@ -0,0 +1,4 @@
**/node_modules/
**/built/
**/dist/
*.min.js

46
.eslintrc Normal file
View file

@ -0,0 +1,46 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"jsx": true,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true,
"experimentalObjectRestSpread": true
}
},
"plugins": ["prettier"],
"settings": {
"react": {
"version": "15.2"
},
"import/resolver": {
"node": {
"extensions": [".js", ".ts", ".tsx"]
}
}
},
"env": {
"node": true,
"amd": true,
"es6": true,
"jest": true
},
"globals": {
"window": true,
"document": true,
"console": true,
"navigator": true,
"alert": true,
"Blob": true,
"fetch": true,
"FileReader": true,
"Element": true,
"AudioNode": true,
"MutationObserver": true,
"Image": true,
"location": true
},
"rules": {
// "prettier/prettier": "error"
}
}

View file

@ -1,4 +1,4 @@
import Webamp from "webamp"; // eslint-disable-line import/no-unresolved
import Webamp from "webamp";
new Webamp({
initialTracks: [

View file

@ -1,4 +1,4 @@
import Webamp from "webamp"; // eslint-disable-line import/no-unresolved
import Webamp from "webamp";
new Webamp({
initialTracks: [

View file

@ -7,10 +7,13 @@
],
"scripts": {
"test": "jest",
"lint": "eslint . --ext ts,tsx,js,jsx --rulesdir=packages/webamp-modern-2/tools/eslint-rules",
"deploy": "sh deploy.sh"
},
"devDependencies": {
"@babel/preset-typescript": "^7.16.7",
"@typescript-eslint/parser": "^5.13.0",
"eslint": "^8.10.0",
"jest": "^27.5.1",
"prettier": "^2.3.2"
},

View file

@ -18,7 +18,7 @@ module.exports = {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
},
ignorePatterns: ["dist/**"],
};

View file

@ -1,4 +1,3 @@
/* global window */
const path = require("path");
const puppeteer = require("puppeteer");
const imagemin = require("imagemin");
@ -29,12 +28,12 @@ export default class Shooter {
}
async init() {
this._log("init()")
this._log("Going to launch puppeteer")
this._log("init()");
this._log("Going to launch puppeteer");
this._browser = await puppeteer.launch({
args: ["--disable-dev-shm-usage"],
});
this._log("Opening new page")
this._log("Opening new page");
this._page = await this._browser.newPage();
this._page.setViewport({ width: 275, height: 116 * 3 });
@ -49,17 +48,17 @@ export default class Shooter {
this._log("page log:", consoleMessage.text());
});
const url = `${this._url}/?screenshot=1`;
this._log(`Goto url ${url}`)
this._log(`Goto url ${url}`);
await this._page.goto(url);
this._log(`Waiting for selector... #main-window...`)
this._log(`Waiting for selector... #main-window...`);
await this._page.waitForSelector("#main-window", { timeout: 2000 });
this._log(`Setting background to none`)
this._log(`Setting background to none`);
await this._page.evaluate(() => {
// Needed to allow for transparent screenshots
window.document.body.style.background = "none";
});
this._initialized = true;
this._log(`Initialization complete!`)
this._log(`Initialization complete!`);
}
async _ensureInitialized() {
@ -93,7 +92,7 @@ export default class Shooter {
}
async _takeScreenshot(skin, screenshotPath, { minify = false }) {
this._log(`_takeScreenshot`)
this._log(`_takeScreenshot`);
await this._ensureInitialized();
try {
this._log(`Getting handle...`);
@ -149,26 +148,26 @@ export default class Shooter {
min(screenshotPath);
}
} catch (e) {
this._log(`Caught an error, cleaning up: ${e}`)
this._log(`Caught an error, cleaning up: ${e}`);
await this.dispose();
await this.init();
reject(e);
throw e;
}
}
async dispose() {
this._log(`Cleaning up shooter...`)
this._log(`Cleaning up shooter...`);
await this._ensureInitialized();
this._log(`Removing page listeners`)
this._log(`Removing page listeners`);
this._page.removeAllListeners();
this._log(`Closing page`)
this._log(`Closing page`);
await this._page.close();
this._log(`Removing browser listeners`)
this._log(`Removing browser listeners`);
this._browser.removeAllListeners();
this._log(`Closing browser`)
this._log(`Closing browser`);
await this._browser.close();
this._log(`Nulling values`)
this._log(`Nulling values`);
this._page = null;
this._browser = null;
this._initialized = false;

View file

@ -1,5 +1,5 @@
const rulesDirPlugin = require("eslint-plugin-rulesdir");
rulesDirPlugin.RULES_DIR = "tools/eslint-rules/dist";
rulesDirPlugin.RULES_DIR = "packages/webamp-modern-2/tools/eslint-rules/dist";
module.exports = {
root: true,
@ -8,6 +8,6 @@ module.exports = {
extends: [],
rules: {
eqeqeq: "off",
"rulesdir/proper-maki-types": "error",
"rulesdir/proper-maki-types": "warn",
},
};

File diff suppressed because one or more lines are too long

View file

@ -150,6 +150,8 @@ function makiTypeToTsType(makiType: string): string {
return "Group";
case "wac":
return "Wac";
case "configitem":
return "ConfigItem";
default:
throw new Error(`Missing maki type: ${makiType}`);
}

View file

@ -1,9 +0,0 @@
const makiClassRule = require("./eslint/maki-class");
const makiMissingMethods = require("./eslint/maki-missing-methods");
const makiMethodTypes = require("./eslint/maki-method-types");
module.exports = {
"maki-class": makiClassRule,
"maki-missing-methods": makiMissingMethods,
"maki-method-types": makiMethodTypes,
};

View file

@ -1,60 +0,0 @@
const {
getMakiObjectfromClassDeclarationNode,
normalizeClassName,
} = require("./maki-eslint-utils");
function isJsMakiNode(node) {
const className = node.id.name;
// https://github.com/captbaritone/webamp/pull/828#issuecomment-518023519
return className.startsWith("Js");
}
module.exports = {
meta: {
docs: {
description: "Ensure Maki Classes match std.mi",
category: "Possible Errors",
recommended: false,
},
schema: [],
fixable: "code",
},
create: function (context) {
return {
ClassDeclaration: function (node) {
const className = node.id.name;
if (isJsMakiNode(node)) {
return;
}
const currentObject = getMakiObjectfromClassDeclarationNode(node);
if (currentObject == null) {
context.report({
node: node.id,
message: `Unknown Maki Class \`${className}\`.`,
});
return;
}
const expectedParentClassName = normalizeClassName(
currentObject.parent
);
if (expectedParentClassName == null) {
if (node.superClass !== null) {
context.report({
node: node.id,
message: `Unexpected parent class for \`${className}\`. \`${className}\` should not extend any class.`,
});
}
// This is probably MakiObject which does not inherit from anything
return;
}
const parentClassName = node.superClass.name;
if (parentClassName !== expectedParentClassName) {
context.report({
node: node.superClass,
message: `Incorrect parent class \`${parentClassName}\`. Expected \`${expectedParentClassName}\`.`,
});
}
},
};
},
};

View file

@ -1,27 +0,0 @@
const { objects } = require("../src/maki-interpreter/objects");
const classNameMappings = {
Object: "MakiObject",
Map: "MakiMap",
"@{00000000-0000-0000-0000-000000000000}@": null,
};
function normalizeClassName(className) {
const normalized = classNameMappings[className];
return normalized === undefined ? className : normalized;
}
const objectsByName = {};
for (const value of Object.values(objects)) {
objectsByName[normalizeClassName(value.name)] = value;
}
function getMakiObjectfromClassDeclarationNode(node) {
const className = node.id.name;
return objectsByName[className];
}
module.exports = {
getMakiObjectfromClassDeclarationNode,
normalizeClassName,
};

View file

@ -1,179 +0,0 @@
const {
getMakiObjectfromClassDeclarationNode,
} = require("./maki-eslint-utils");
const TYPE_MAP = {
// This might be wrong. Maybe it really is an empty string? Or Null?
"": {
typeScriptName: "TSVoidKeyword",
stringRepresentation: "void",
},
string: {
typeScriptName: "TSStringKeyword",
stringRepresentation: "string",
},
double: {
typeScriptName: "TSNumberKeyword",
stringRepresentation: "number",
},
int: {
typeScriptName: "TSNumberKeyword",
stringRepresentation: "number",
},
boolean: {
typeScriptName: "TSBooleanKeyword",
stringRepresentation: "boolean",
},
float: {
typeScriptName: "TSNumberKeyword",
stringRepresentation: "number",
},
any: {
typeScriptName: "TSAnyKeyword",
stringRepresentation: "any",
},
};
function getTypeData(makiType) {
const type = TYPE_MAP[makiType.toLowerCase()];
if (type == null) {
// console.warn(`Could not find type for "${makiType}"`);
}
return type;
}
module.exports = {
meta: {
docs: {
description: "Ensure Maki object methods have the corret type",
category: "Possible Errors",
recommended: false,
},
schema: [],
fixable: "code",
},
create: function (context) {
return {
MethodDefinition: function (node) {
const currentObject = getMakiObjectfromClassDeclarationNode(
node.parent.parent
);
if (currentObject == null) {
return;
}
const methods = {};
currentObject.functions.forEach((func) => {
methods[func.name.toLowerCase()] = func;
});
const methodName = node.key.name;
// Theoretically this should only be implemented on Object, but it's
// easier to let each class implement it themselves.
if (methodName === "getclassname") {
return;
}
if (methodName === "constructor") {
return;
}
// Non-maki methods may be implemented using the `js_` prefix.
if (methodName.startsWith("js_")) {
return;
}
if (methodName.startsWith("_")) {
return;
}
const func = methods[methodName];
if (func == null) {
context.report({
node: node.key,
message: `Invalid Maki method name \`${methodName}\``,
});
return;
}
const { params, returnType, body } = node.value;
if (returnType == null) {
const expectedTypeData = getTypeData(func.result);
if (expectedTypeData != null) {
context.report({
node: body,
message: `Missing return type for Maki method. Expected \`${expectedTypeData.stringRepresentation}\`.`,
fix: (fixer) => {
return fixer.insertTextBefore(
body,
`: ${expectedTypeData.stringRepresentation}`
);
},
});
}
} else {
const expectedTypeData = TYPE_MAP[func.result];
if (
expectedTypeData != null &&
expectedTypeData.typeScriptName !== returnType.typeAnnotation.type
) {
context.report({
node: returnType,
message: `Incorrect return type for Maki method. Expected \`${expectedTypeData.stringRepresentation}\`.`,
});
}
}
func.parameters.forEach(([type, name], i) => {
const expectedTypeData = TYPE_MAP[type.toLowerCase()];
const actual = params[i];
if (actual == null) {
context.report({
node: node.value,
message: `Missing Maki method argument. Expected \`${name}\`.`,
});
return;
}
if (actual.name !== name) {
// Turned off since some of the maki names are bad.
/*
context.report({
node: node.value.params[i],
message: `Invalid Maki method argument name \`${actual}\`. Expected \`${name}\`.`,
});
*/
}
if (expectedTypeData == null) {
// console.warn(`Missing type data for ${type}.`);
return;
}
const fix = (fixer) => {
return fixer.replaceText(
actual,
`${actual.name}: ${expectedTypeData.stringRepresentation}`
);
};
if (actual.typeAnnotation == null) {
context.report({
node: actual,
message: `Missing type for Maki argument. Expected \`${expectedTypeData.stringRepresentation}\`.`,
fix,
});
return;
}
const actualTypeScriptName =
actual.typeAnnotation.typeAnnotation.type;
if (actualTypeScriptName !== expectedTypeData.typeScriptName) {
context.report({
node: actual,
message: `Invalid type for Maki argument. Expected \`${expectedTypeData.typeScriptName}\`.`,
fix,
});
}
});
},
};
},
};

View file

@ -1,58 +0,0 @@
const {
getMakiObjectfromClassDeclarationNode,
} = require("./maki-eslint-utils");
module.exports = {
meta: {
docs: {
description: "Ensure Maki objects match std.mi",
category: "Possible Errors",
recommended: false,
},
schema: [],
fixable: "code",
},
create: function (context) {
return {
ClassBody: function (node) {
const currentObject = getMakiObjectfromClassDeclarationNode(
node.parent
);
if (currentObject == null) {
return;
}
const implementedMethodNames = new Set(
node.body
.filter((prop) => prop.type === "MethodDefinition")
.map((method) => method.key.name)
);
currentObject.functions.forEach((func) => {
const methodName = func.name.toLowerCase();
if (implementedMethodNames.has(methodName)) {
return;
}
const args = func.parameters.map(([, name]) => name).join(", ");
// We rely on Prettier to clean this up.
// We also expect `unimplementedWarning` to already be imported.
const methodString = `
${methodName}(${args}) {
return unimplementedWarning("${methodName}");
}
`;
const lastChild = node.body[node.body.length - 1];
context.report({
node: node,
message: `Missing method ${methodName}`,
fix: (fixer) => {
return fixer.insertTextAfter(lastChild, methodString);
},
});
});
},
};
},
};

View file

@ -72,7 +72,6 @@
"data-uri-to-buffer": "^2.0.0",
"eslint": "^6.5.1",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-react": "^7.16.0",
"file-loader": "^2.0.0",
"git-revision-webpack-plugin": "^3.0.3",

View file

@ -2,9 +2,6 @@
"rules": {
// TODO: Turn these all back on
// For now we want to be able to define maki method arguments even though we don't use them.
"@typescript-eslint/no-unused-vars": "off",
"maki-missing-methods": "error",
"maki-class": "error",
"maki-method-types": "error"
"@typescript-eslint/no-unused-vars": "off"
}
}