eslint, require-no-cache now deletes all requires from plugin directory from cache

This commit is contained in:
Evert Prants 2021-12-18 10:32:30 +02:00
parent f2af1891ee
commit 81b8e7581a
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
21 changed files with 2747 additions and 361 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
*.js
*.d.ts

46
.eslintrc.js Normal file
View File

@ -0,0 +1,46 @@
module.exports = {
'env': {
'es2021': true,
'node': true
},
'extends': [
'eslint:recommended',
'plugin:@typescript-eslint/recommended'
],
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 13,
'sourceType': 'module',
'project': 'tsconfig.json',
'tsconfigRootDir': __dirname,
},
'plugins': [
'@typescript-eslint'
],
'rules': {
'no-empty': [
'error',
{
'allowEmptyCatch': true,
}
],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'indent': [
'error',
2
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
]
}
};

2776
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@squeebot/core",
"version": "3.3.5",
"version": "3.3.6",
"description": "Squeebot v3 core for the execution environment",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -23,7 +23,8 @@
"@types/node": "^16.7.10",
"@types/semver": "^7.3.8",
"@types/tar": "^4.0.5",
"tslint": "^6.1.3",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"eslint": "^8.4.1",
"typescript": "^4.4.2"
},
"dependencies": {

View File

@ -52,11 +52,11 @@ export function httpGET(
}
return httpGET(res.headers.location as string,
headers,
restrictToText,
saveTo,
lback,
).then(resolve, reject);
headers,
restrictToText,
saveTo,
lback,
).then(resolve, reject);
}
if (saveTo) {

View File

@ -6,10 +6,10 @@
*/
export function sanitizeEscapedText(text: string): string {
return text.replace(/\n/g, ' ')
.replace(/&/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&apos;/g, '\'')
.trim();
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&apos;/g, '\'')
.trim();
}

View File

@ -15,7 +15,7 @@ const dirs: {[key: string]: string} = {
* @param chroot Change bot root to this instead of the path in the environment
* @returns Squeebot environment
*/
export async function loadEnvironment(enviroFile: string = 'squeebot.env.json', chroot?: string): Promise<IEnvironment> {
export async function loadEnvironment(enviroFile = 'squeebot.env.json', chroot?: string): Promise<IEnvironment> {
if (!await fs.pathExists(enviroFile)) {
throw new Error('Environment file does not exist.');
}

View File

@ -22,7 +22,7 @@ export class Logger {
const old = this.console[index];
this.console[index] = (...data: any[]): void => {
rl.output.write('\x1b[2K\r');
old.apply(null, data);
old(...data);
rl.prompt(true);
};
}
@ -42,20 +42,20 @@ export class Logger {
}
switch (ltype) {
case 'info':
message.push('[ INFO]');
break;
case 'debug':
message.push('[DEBUG]');
break;
case 'warn':
message.push('[ WARN]');
cfunc = this.console[1];
break;
case 'error':
message.push('[ERROR]');
cfunc = this.console[2];
break;
case 'info':
message.push('[ INFO]');
break;
case 'debug':
message.push('[DEBUG]');
break;
case 'warn':
message.push('[ WARN]');
cfunc = this.console[1];
break;
case 'error':
message.push('[ERROR]');
cfunc = this.console[2];
break;
}
// Short dance to apply formatting
@ -65,7 +65,7 @@ export class Logger {
final = util.format(data[0], ...fargs);
}
message.push(final);
cfunc.apply(null, message);
cfunc(...message);
}
/**

View File

@ -102,6 +102,6 @@ export class NPMExecutor {
}
private removeVersionWildcard(str: string): string {
return str.replace(/\^|\>|\=/, '');
return str.replace(/\^|>|=/, '');
}
}

View File

@ -3,8 +3,8 @@
* Declare this plugin as one that the user can configure.
* @param defconf Default configuration
*/
export function Configurable(defconf: any): Function {
return (constructor: Function): void => {
export function Configurable(defconf: any): (...arg: any[]) => any {
return (constructor: (...arg: any[]) => any): void => {
constructor.prototype.__defconf = defconf;
};
}

View File

@ -3,8 +3,8 @@
* Declare this plugin as one that utilises services.
* @param servtype Service class, usually a Protocol
*/
export function InjectService(servtype: any): Function {
return (constructor: Function): void => {
export function InjectService(servtype: any): (...arg: any[]) => any {
return (constructor: (...arg: any[]) => any): void => {
constructor.prototype.__service = servtype;
};
}

View File

@ -36,7 +36,7 @@ export class PluginMetaLoader {
throw new Error('Plugin metadata does not specify a name, for some reason');
}
if (json.name === 'squeebot' || !(/^[a-zA-Z0-9_\-]+$/.test(json.name))) {
if (json.name === 'squeebot' || !(/^[a-zA-Z0-9_-]+$/.test(json.name))) {
throw new Error('Illegal name.');
}
@ -76,12 +76,13 @@ export class PluginMetaLoader {
public async loadAll(ignoreErrors = false): Promise<IPluginManifest[]> {
const dirlist = await fs.readdir(this.env.pluginsPath);
const plugins: IPluginManifest[] = [];
for (const file of dirlist) {
// Ignore hidden files and non-directories
const fpath = path.join(this.env.pluginsPath, file);
if (file.indexOf('.') === 0 ||
file === 'node_modules' ||
!(await fs.lstat(fpath)).isDirectory()) {
const isDirectory = (await fs.lstat(fpath)).isDirectory();
if (!isDirectory || file.startsWith('.') || file === 'node_modules') {
continue;
}
@ -89,10 +90,9 @@ export class PluginMetaLoader {
const plugin = await this.load(file);
plugins.push(plugin);
} catch (e) {
if (ignoreErrors) {
continue;
if (!ignoreErrors) {
logger.error(e);
}
logger.error(e);
}
}
return plugins;

View File

@ -84,7 +84,7 @@ export class PluginManager {
return true;
}
if (!(/^[a-zA-Z0-9_\-]+$/.test(manifest.name))) {
if (!(/^[a-zA-Z0-9_-]+$/.test(manifest.name))) {
throw new Error('Illegal name for a plugin!');
}
@ -284,8 +284,9 @@ export class PluginManager {
/**
* Restart a loaded plugin.
* @param mf Plugin instance, plugin manifest or plugin name
* @param wait Waits for the plugin to be available before resolving
*/
public async restart(mf: IPluginManifest | IPlugin | string): Promise<void> {
public async restart(mf: IPluginManifest | IPlugin | string, wait = false): Promise<void> {
let manifest;
if (typeof mf === 'string') {
manifest = this.getAvailableByName(mf);
@ -299,13 +300,34 @@ export class PluginManager {
throw new Error('Plugin not found');
}
if (!this.getLoadedByName(manifest.name)) {
const pluginName = manifest.name;
if (!this.getLoadedByName(pluginName)) {
this.load(manifest);
return;
}
this.restartQueue.set(manifest.name, manifest);
this.stream.emitTo(manifest.name, 'pluginUnload', manifest.name);
this.restartQueue.set(pluginName, manifest);
this.stream.emitTo(pluginName, 'pluginUnload', pluginName);
if (wait) {
return new Promise((resolve, reject) => {
let retries = 0;
const checkInterval = setInterval(() => {
// Plugin has been loaded, resolve the promise
if (this.getLoadedByName(pluginName)) {
clearInterval(checkInterval);
return resolve();
}
// Increment retry count and wait for next iteration
retries++;
if (retries >= 20) {
// Give up after 10 seconds
clearInterval(checkInterval);
reject(new Error('Could not determine loaded status within a reasonable time frame.'));
}
}, 500);
});
}
}
/**

View File

@ -24,6 +24,7 @@ export class Plugin implements IPlugin {
* Called when plugin first starts.
* Please use this instead of the constructor.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-function
public initialize(): void {}
public get name(): string {

View File

@ -163,7 +163,7 @@ export class RepositoryManager {
throw new Error('Invalid metadata file for repository.');
}
if (!(/^[a-zA-Z0-9_\-\+]+$/.test(meta.name))) {
if (!(/^[a-zA-Z0-9_\-+]+$/.test(meta.name))) {
throw new Error('Illegal name for repository!');
}
@ -286,7 +286,7 @@ export class RepositoryManager {
throw new Error('Invalid repository file ' + rf);
}
if (!(/^[a-zA-Z0-9_\-\+]+$/.test(contents.name))) {
if (!(/^[a-zA-Z0-9_\-+]+$/.test(contents.name))) {
throw new Error(`"${rf}" is an illegal name for a repository!`);
}

View File

@ -28,8 +28,12 @@ export class Configuration {
const json = await fs.readJson(this.file);
this.config = json;
// Assign default values to existing configuration and resave.
// Eliminates the need for plugins to overwrite the configuration file
// when exiting.
if (this.defaults) {
this.config = Object.assign({}, this.defaults, json);
await this.save();
}
}

View File

@ -184,18 +184,18 @@ export class Formatter {
// Special types
if (elemParams && elemParams.type) {
switch (elemParams.type) {
case 'time':
elemValue = new Date(elemValue).toString();
break;
case 'metric':
elemValue = thousandsSeparator(elemValue);
break;
case 'timesince':
elemValue = timeSince(elemValue);
break;
case 'duration':
elemValue = toHHMMSS(elemValue);
break;
case 'time':
elemValue = new Date(elemValue).toString();
break;
case 'metric':
elemValue = thousandsSeparator(elemValue);
break;
case 'timesince':
elemValue = timeSince(elemValue);
break;
case 'duration':
elemValue = toHHMMSS(elemValue);
break;
}
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import { EMessageType, IMessage } from './message';
import { Protocol } from './protocol';

View File

@ -11,7 +11,7 @@ export interface IMessageData {
value: any;
}
export type IMessageHandler = (data: IMessageData | string, reject: Function, next: IMessageHandler) => void;
export type IMessageHandler = (data: IMessageData | string, reject: (error: Error) => void, next: IMessageHandler) => void;
/**
* Staged messaging handler

View File

@ -10,9 +10,18 @@ export { IProcessData, spawnProcess, execProcess } from './run';
*/
export function requireNoCache(file: string): object | undefined {
const fullPath = path.resolve(file);
// TODO: maybe use new "import" function?
// eslint-disable-next-line @typescript-eslint/no-var-requires
const mod = require(fullPath);
if (require.cache && require.cache[fullPath]) {
delete require.cache[fullPath];
// Delete in-plugin-folder requires from cache as well
const dirname = path.dirname(fullPath);
const cacheEntries = Object.keys(require.cache).filter((entry) => entry.startsWith(dirname));
cacheEntries.forEach((entry) => {
delete require.cache[entry];
});
}
return mod;
}

View File

@ -1,122 +0,0 @@
{
"extends": "tslint:recommended",
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-return-shorthand": true,
"curly": true,
"deprecation": {
"severity": "warning"
},
"eofline": true,
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"quotemark": [
true,
"single"
],
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"typedef": [
true,
"call-signature"
],
"forin": false,
"ban-types": {
"function": false
},
"typedef-whitespace": {
"options": [
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
]
},
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
}
}
}