Scaffold: Run ESLint continuously, not just after save (#4738)

Includes API changes in Zotero.Translators in order to support
in-memory translator serialization.

Type changes in docstrings are corrections, not actual changes.
This commit is contained in:
Abe Jellinek 2024-10-09 01:46:10 -04:00 • committed by GitHub
parent 00182e317c
commit 40dd345280
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 76 additions and 32 deletions

View file

@ -293,6 +293,7 @@ var Scaffold = new function () {
monaco.languages.registerCodeLensProvider('javascript', this.createRunCodeLensProvider(monaco, editor));
monaco.languages.registerHoverProvider('javascript', this.createHoverProvider(monaco, editor));
monaco.languages.registerCompletionItemProvider('javascript', this.createCompletionProvider(monaco, editor));
model.onDidChangeContent(() => this.updateModelMarkers());
let tsLib = await Zotero.File.getContentsAsync(
PathUtils.join(Scaffold_Translators.getDirectory(), 'index.d.ts'));
@ -563,9 +564,10 @@ var Scaffold = new function () {
};
};
this.updateModelMarkers = async function (translatorPath) {
let output = await runESLint(translatorPath);
let markers = eslintOutputToModelMarkers(output);
this.updateModelMarkers = async function () {
let modelVersionId = _editors.code.getModel().getVersionId();
let output = await runESLint();
let markers = eslintOutputToModelMarkers(output, modelVersionId);
_editors.codeGlobal.editor.setModelMarkers(_editors.code.getModel(), 'eslint', markers);
};
@ -737,7 +739,6 @@ var Scaffold = new function () {
if (mod) type -= mod;
}
this.updateModelMarkers(translator.path);
_lastModifiedTime = new Date().getTime();
Zotero.Prefs.set('scaffold.lastTranslatorID', translator.translatorID);
@ -804,24 +805,28 @@ var Scaffold = new function () {
return metadata;
}
/*
* save translator to database
*/
this.save = async function (updateZotero) {
function _getCode() {
var code = _editors.code.getValue();
var tests = _editors.tests.getValue().trim();
if (!tests || tests == '[]') tests = '[\n]'; // eslint wants a line break between the brackets
code = code.trimEnd() + '\n\n/** BEGIN TEST CASES **/\nvar testCases = ' + tests + '\n/** END TEST CASES **/';
return code;
}
/*
* save translator to database
*/
this.save = async function (updateZotero) {
var metadata = _getMetadataObject();
var code = _getCode();
if (metadata.label === "Untitled") {
_logOutput("Can't save an untitled translator.");
return;
}
var path = await _translatorProvider.save(metadata, code);
await _translatorProvider.save(metadata, code);
if (updateZotero) {
await Zotero.Translators.save(metadata, code);
@ -830,7 +835,6 @@ var Scaffold = new function () {
_lastModifiedTime = new Date().getTime();
this.updateModelMarkers(path);
await this.reloadTranslators();
};
@ -2263,18 +2267,26 @@ var Scaffold = new function () {
return eslintPath;
}
async function runESLint(translatorPath) {
if (!translatorPath) return [];
async function runESLint() {
let eslintPath = await getESLintPath();
if (!eslintPath) return [];
Zotero.debug('Running ESLint');
try {
let metadata = _getMetadataObject();
let code = _getCode();
let proc = await Subprocess.call({
command: eslintPath,
arguments: ['--format', 'json', '--', translatorPath],
arguments: [
'--format',
'json',
'--stdin',
'--stdin-filename',
_translatorProvider.getSavePath(metadata)
],
});
await proc.stdin.write(_translatorProvider.stringify(metadata, code));
await proc.stdin.close();
let lintOutput = '';
let chunk;
while ((chunk = await proc.stdout.readString())) {
@ -2288,7 +2300,7 @@ var Scaffold = new function () {
return [];
}
function eslintOutputToModelMarkers(output) {
function eslintOutputToModelMarkers(output, modelVersionId) {
let result = output[0];
if (!result) return [];
@ -2300,7 +2312,8 @@ var Scaffold = new function () {
message: message.message,
severity: message.severity * 4,
source: 'ESLint',
code: message.ruleId
code: message.ruleId,
modelVersionId,
}));
}

View file

@ -522,14 +522,25 @@ Zotero.Translators = new function() {
fileName = alternative + ".js";
}
return fileName;
}
};
/**
* @param {Object} metadata
* @return {String}
*/
this.getSavePath = function (metadata) {
return PathUtils.join(
this.getTranslatorsDirectory(),
this.getFileNameFromLabel(metadata.label, metadata.translatorID)
);
};
this.getTranslatorsDirectory = function () {
return Zotero.getTranslatorsDirectory().path;
};
/**
* @param {String} metadata
* @param {Object} metadata
* @param {String} metadata.translatorID Translator GUID
* @param {Integer} metadata.translatorType See TRANSLATOR_TYPES in translate.js
* @param {String} metadata.label Translator title
@ -544,15 +555,16 @@ Zotero.Translators = new function() {
* @param {Boolean} metadata.inRepository
* @param {String} metadata.lastUpdated SQL date
* @param {String} code
* @return {Promise<nsIFile>}
* @return {String}
*/
this.save = Zotero.Promise.coroutine(function* (metadata, code) {
this.stringify = function (metadata, code) {
if (!metadata.translatorID) {
throw ("metadata.translatorID not provided in Zotero.Translators.save()");
throw new Error("metadata.translatorID not provided in Zotero.Translators.save()");
}
if (!metadata.translatorType) {
var found = false;
// FIXME: This seems to be a no-op
for (let type in TRANSLATOR_TYPES) {
if (metadata.translatorType & TRANSLATOR_TYPES[type]) {
found = true;
@ -560,7 +572,7 @@ Zotero.Translators = new function() {
}
}
if (!found) {
throw ("Invalid translatorType '" + metadata.translatorType + "' in Zotero.Translators.save()");
throw new Error("Invalid translatorType '" + metadata.translatorType + "' in Zotero.Translators.save()");
}
}
@ -580,11 +592,6 @@ Zotero.Translators = new function() {
throw new Error("code not provided");
}
var fileName = this.getFileNameFromLabel(
metadata.label, metadata.translatorID
);
var destFile = OS.Path.join(this.getTranslatorsDirectory(), fileName);
// JSON.stringify has the benefit of indenting JSON
var metadataJSON = JSON.stringify(metadata, null, "\t");
@ -595,13 +602,37 @@ Zotero.Translators = new function() {
str += '\n';
}
var translator = this.get(metadata.translatorID);
var sameFile = translator && destFile == translator.path;
return str;
};
/**
* @param {Object} metadata
* @param {String} metadata.translatorID Translator GUID
* @param {Integer} metadata.translatorType See TRANSLATOR_TYPES in translate.js
* @param {String} metadata.label Translator title
* @param {String} metadata.creator Translator author
* @param {String|Null} metadata.target Target regexp
* @param {String|Null} metadata.minVersion
* @param {String} metadata.maxVersion
* @param {String|undefined} metadata.configOptions
* @param {String|undefined} metadata.displayOptions
* @param {Integer} metadata.priority
* @param {String} metadata.browserSupport
* @param {Boolean} metadata.inRepository
* @param {String} metadata.lastUpdated SQL date
* @param {String} code
* @return {Promise<String>}
*/
this.save = Zotero.Promise.coroutine(function* (metadata, code) {
var str = this.stringify(metadata, code);
var destFile = this.getSavePath(metadata);
var existingTranslator = this.get(metadata.translatorID);
var sameFile = existingTranslator && destFile == existingTranslator.path;
var exists = yield OS.File.exists(destFile);
if (!sameFile && exists) {
var msg = "Overwriting translator with same filename '"
+ fileName + "'";
var msg = `Overwriting translator with same filename '${PathUtils.filename(destFile)}'`;
Zotero.debug(msg, 1);
Zotero.debug(metadata, 1);
Components.utils.reportError(msg);