mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Edit book: Switch to a new library (stylehint) for find problems in CSS as the old library was no longer maintained.
This commit is contained in:
parent
036bbdb7ff
commit
c7a61b2942
10898
resources/csslint.js
10898
resources/csslint.js
File diff suppressed because it is too large
Load Diff
17
resources/stylelint-bundle.min.js
vendored
Normal file
17
resources/stylelint-bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
88
resources/stylelint.js
Normal file
88
resources/stylelint.js
Normal file
@ -0,0 +1,88 @@
|
||||
/* vim:fileencoding=utf-8
|
||||
*
|
||||
* Copyright (C) 2023 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPLv3 license
|
||||
*/
|
||||
/*jshint esversion: 6 */
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
window.stylelint_results = [];
|
||||
|
||||
window.check_css = function(src) {
|
||||
stylelint.lint({
|
||||
code: src,
|
||||
config: {
|
||||
rules: {
|
||||
'annotation-no-unknown': true,
|
||||
'at-rule-no-unknown': true,
|
||||
'block-no-empty': true,
|
||||
'color-no-invalid-hex': true,
|
||||
'comment-no-empty': true,
|
||||
'custom-property-no-missing-var-function': true,
|
||||
'declaration-block-no-duplicate-custom-properties': true,
|
||||
'declaration-block-no-duplicate-properties': [
|
||||
true,
|
||||
{
|
||||
ignore: ['consecutive-duplicates-with-different-values'],
|
||||
},
|
||||
],
|
||||
'declaration-block-no-shorthand-property-overrides': true,
|
||||
'font-family-no-duplicate-names': true,
|
||||
'font-family-no-missing-generic-family-keyword': true,
|
||||
'function-calc-no-unspaced-operator': true,
|
||||
'function-linear-gradient-no-nonstandard-direction': true,
|
||||
'function-no-unknown': true,
|
||||
'keyframe-block-no-duplicate-selectors': true,
|
||||
'keyframe-declaration-no-important': true,
|
||||
'media-feature-name-no-unknown': true,
|
||||
'named-grid-areas-no-invalid': true,
|
||||
'no-descending-specificity': true,
|
||||
'no-duplicate-at-import-rules': true,
|
||||
'no-duplicate-selectors': true,
|
||||
'no-empty-source': true,
|
||||
'no-extra-semicolons': true,
|
||||
'no-invalid-double-slash-comments': true,
|
||||
'no-invalid-position-at-import-rule': true,
|
||||
'no-irregular-whitespace': true,
|
||||
'property-no-unknown': true,
|
||||
'selector-pseudo-class-no-unknown': true,
|
||||
'selector-pseudo-element-no-unknown': true,
|
||||
'selector-type-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignore: ['custom-elements'],
|
||||
},
|
||||
],
|
||||
'string-no-newline': true,
|
||||
'unit-no-unknown': true,
|
||||
},
|
||||
},
|
||||
formatter: (results, returnValue) => {
|
||||
var r = results[0];
|
||||
r._postcssResult = undefined;
|
||||
r.source = undefined;
|
||||
return JSON.stringify({results:r, rule_metadata: returnValue.ruleMetadata});
|
||||
},
|
||||
})
|
||||
.then((results) => {
|
||||
window.stylelint_results.push({type: 'results', 'results':results});
|
||||
document.title = 'checked:' + window.performance.now();
|
||||
})
|
||||
.catch((err) => {
|
||||
window.stylelint_results.push({type: 'error', 'error':'' + err});
|
||||
console.error(err.stack);
|
||||
document.title = 'checked:' + window.performance.now();
|
||||
}) ;
|
||||
};
|
||||
|
||||
window.get_css_results = function(src) {
|
||||
var ans = window.stylelint_results;
|
||||
window.stylelint_results = [];
|
||||
return ans;
|
||||
};
|
||||
document.title = 'ready:' + window.performance.now();
|
||||
})();
|
||||
|
||||
|
@ -20,7 +20,7 @@ __all__ = [
|
||||
'upload_user_manual', 'upload_demo', 'reupload',
|
||||
'stage1', 'stage2', 'stage3', 'stage4', 'stage5', 'publish', 'publish_betas',
|
||||
'linux', 'linux64', 'linuxarm64', 'win', 'win64', 'osx', 'build_dep',
|
||||
'export_packages', 'hyphenation', 'liberation_fonts', 'csslint'
|
||||
'export_packages', 'hyphenation', 'liberation_fonts', 'stylelint'
|
||||
]
|
||||
|
||||
from setup.installers import Linux, Win, OSX, Linux64, LinuxArm64, Win64, ExtDev, BuildDep, ExportPackages
|
||||
@ -39,7 +39,7 @@ iso639 = ISO639()
|
||||
iso3166 = ISO3166()
|
||||
|
||||
from setup.csslint import CSSLint
|
||||
csslint = CSSLint()
|
||||
stylelint = CSSLint()
|
||||
|
||||
from setup.build import Build
|
||||
build = Build()
|
||||
|
@ -9,12 +9,9 @@ from setup import Command
|
||||
|
||||
|
||||
class CSSLint(Command):
|
||||
# We can't use the released copy since it has not had a release in years and
|
||||
# there are several critical bug fixes we need
|
||||
|
||||
description = 'Update the bundled copy of csslint'
|
||||
NAME = 'csslint.js'
|
||||
DOWNLOAD_URL = 'https://github.com/CSSLint/csslint.git'
|
||||
description = 'Update the bundled copy of stylelint'
|
||||
NAME = 'stylelint-bundle.min.js'
|
||||
DOWNLOAD_URL = 'https://github.com/openstyles/stylelint-bundle.git'
|
||||
|
||||
@property
|
||||
def vendored_file(self):
|
||||
@ -25,8 +22,9 @@ class CSSLint(Command):
|
||||
|
||||
with self.temp_dir() as dl_src:
|
||||
subprocess.check_call(['git', 'clone', '--depth=1', self.DOWNLOAD_URL], cwd=dl_src)
|
||||
src = self.j(dl_src, 'csslint')
|
||||
src = self.j(dl_src, 'stylelint-bundle')
|
||||
subprocess.check_call(['npm', 'install'], cwd=src)
|
||||
subprocess.check_call(['npm', 'run', 'build'], cwd=src)
|
||||
shutil.copyfile(self.j(src, 'dist', self.NAME), self.vendored_file)
|
||||
|
||||
def clean(self):
|
||||
|
@ -41,74 +41,36 @@ def as_int_or_none(x):
|
||||
return x
|
||||
|
||||
|
||||
def message_to_error(message, name, line_offset=0):
|
||||
rule = message.get('rule', {})
|
||||
rule_id = rule.get('id') or ''
|
||||
cls = CSSWarning
|
||||
if message.get('type') == 'error':
|
||||
cls = CSSParseError if rule.get('name') == 'Parsing Errors' else CSSError
|
||||
title = message.get('message') or _('Unknown error')
|
||||
def message_to_error(message, name, line_offset, rule_metadata):
|
||||
rule_id = message.get('rule')
|
||||
if rule_id == 'CssSyntaxError':
|
||||
cls = CSSParseError
|
||||
else:
|
||||
cls = CSSError if message.get('severity') == 'error' else CSSWarning
|
||||
title = message.get('text') or _('Unknown error')
|
||||
title = title.rpartition('(')[0].strip()
|
||||
line = as_int_or_none(message.get('line'))
|
||||
col = as_int_or_none(message.get('col'))
|
||||
col = as_int_or_none(message.get('column'))
|
||||
if col is not None:
|
||||
col -= 1
|
||||
if line is not None:
|
||||
line += line_offset
|
||||
ans = cls(title, name, line, col)
|
||||
ans.HELP = rule.get('desc') or ''
|
||||
ans.HELP = message.get('text') or ''
|
||||
ans.css_rule_id = rule_id
|
||||
if ans.HELP and 'url' in rule:
|
||||
ans.HELP += ' ' + _('See <a href="{}">detailed description</a>.').format(rule['url'])
|
||||
m = rule_metadata.get(rule_id)
|
||||
if m and 'url' in m:
|
||||
ans.HELP += '. ' + _('See <a href="{}">detailed description</a>.').format(m['url'])
|
||||
return ans
|
||||
|
||||
|
||||
def csslint_js():
|
||||
ans = getattr(csslint_js, 'ans', None)
|
||||
def stylelint_js():
|
||||
ans = getattr(stylelint_js, 'ans', None)
|
||||
if ans is None:
|
||||
ans = csslint_js.ans = P('csslint.js', data=True, allow_user_override=False).decode('utf-8') + '''
|
||||
|
||||
window.check_css = function(src) {
|
||||
var rules = CSSLint.getRules();
|
||||
var ruleset = {};
|
||||
var ignored_rules = {
|
||||
'order-alphabetical': 1,
|
||||
'font-sizes': 1,
|
||||
'zero-units': 1,
|
||||
'bulletproof-font-face': 1,
|
||||
'import': 1,
|
||||
'box-model': 1,
|
||||
'adjoining-classes': 1,
|
||||
'box-sizing': 1,
|
||||
'compatible-vendor-prefixes': 1,
|
||||
'text-indent': 1,
|
||||
'unique-headings': 1,
|
||||
'fallback-colors': 1,
|
||||
'font-faces': 1,
|
||||
'regex-selectors': 1,
|
||||
'universal-selector': 1,
|
||||
'unqualified-attributes': 1,
|
||||
'overqualified-elements': 1,
|
||||
'shorthand': 1,
|
||||
'duplicate-background-images': 1,
|
||||
'floats': 1,
|
||||
'ids': 1,
|
||||
'gradients': 1
|
||||
};
|
||||
var error_rules = {
|
||||
'known-properties': 1,
|
||||
'duplicate-properties': 1,
|
||||
'vendor-prefix': 1
|
||||
};
|
||||
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
var rule = rules[i];
|
||||
if (!ignored_rules[rule.id] && rule.browsers === "All") ruleset[rule.id] = error_rules[rule.id] ? 2 : 1;
|
||||
}
|
||||
var result = CSSLint.verify(src, ruleset);
|
||||
return result;
|
||||
}
|
||||
document.title = 'ready';
|
||||
'''
|
||||
ans = stylelint_js.ans = (
|
||||
('stylelint-bundle.min.js', P('stylelint-bundle.min.js', data=True, allow_user_override=False).decode('utf-8')),
|
||||
('stylelint.js', P('stylelint.js', data=True, allow_user_override=False).decode('utf-8')),
|
||||
)
|
||||
return ans
|
||||
|
||||
|
||||
@ -117,11 +79,12 @@ def create_profile():
|
||||
if ans is None:
|
||||
ans = create_profile.ans = QWebEngineProfile(QApplication.instance())
|
||||
setup_profile(ans)
|
||||
s = QWebEngineScript()
|
||||
s.setName('csslint.js')
|
||||
s.setSourceCode(csslint_js())
|
||||
s.setWorldId(QWebEngineScript.ScriptWorldId.ApplicationWorld)
|
||||
ans.scripts().insert(s)
|
||||
for (name, code) in stylelint_js():
|
||||
s = QWebEngineScript()
|
||||
s.setName(name)
|
||||
s.setSourceCode(code)
|
||||
s.setWorldId(QWebEngineScript.ScriptWorldId.ApplicationWorld)
|
||||
ans.scripts().insert(s)
|
||||
return ans
|
||||
|
||||
|
||||
@ -134,22 +97,23 @@ class Worker(QWebEnginePage):
|
||||
QWebEnginePage.__init__(self, create_profile(), QApplication.instance())
|
||||
self.titleChanged.connect(self.title_changed)
|
||||
secure_webengine(self.settings())
|
||||
self.console_messages = []
|
||||
self.ready = False
|
||||
self.working = False
|
||||
self.pending = None
|
||||
self.setHtml('')
|
||||
|
||||
def title_changed(self, new_title):
|
||||
new_title = new_title.partition(':')[0]
|
||||
if new_title == 'ready':
|
||||
self.ready = True
|
||||
if self.pending is not None:
|
||||
self.check_css(self.pending)
|
||||
self.pending = None
|
||||
elif new_title == 'checked':
|
||||
self.runJavaScript('window.get_css_results()', QWebEngineScript.ScriptWorldId.ApplicationWorld, self.check_done)
|
||||
|
||||
def javaScriptConsoleMessage(self, level, msg, lineno, source_id):
|
||||
msg = f'{source_id}:{lineno}:{msg}'
|
||||
self.console_messages.append(msg)
|
||||
try:
|
||||
print(msg)
|
||||
except Exception:
|
||||
@ -157,9 +121,8 @@ class Worker(QWebEnginePage):
|
||||
|
||||
def check_css(self, src):
|
||||
self.working = True
|
||||
self.console_messages = []
|
||||
self.runJavaScript(
|
||||
f'window.check_css({json.dumps(src)})', QWebEngineScript.ScriptWorldId.ApplicationWorld, self.check_done)
|
||||
f'window.check_css({json.dumps(src)})', QWebEngineScript.ScriptWorldId.ApplicationWorld)
|
||||
|
||||
def check_css_when_ready(self, src):
|
||||
if self.ready:
|
||||
@ -168,9 +131,10 @@ class Worker(QWebEnginePage):
|
||||
self.working = True
|
||||
self.pending = src
|
||||
|
||||
def check_done(self, result):
|
||||
def check_done(self, results):
|
||||
self.working = False
|
||||
self.work_done.emit(self, result)
|
||||
for result in results:
|
||||
self.work_done.emit(self, result)
|
||||
|
||||
|
||||
class Pool:
|
||||
@ -208,11 +172,9 @@ class Pool:
|
||||
break
|
||||
|
||||
def work_done(self, worker, result):
|
||||
if not isinstance(result, dict):
|
||||
result = worker.console_messages
|
||||
self.results[worker.result_idx] = result
|
||||
self.assign_work()
|
||||
if not self.pending and not [w for w in self.workers if w.working]:
|
||||
if not self.pending and not any(w for w in self.workers if w.working):
|
||||
self.working = False
|
||||
|
||||
def shutdown(self):
|
||||
@ -245,16 +207,16 @@ def check_css(jobs):
|
||||
return errors
|
||||
results = pool.check_css([j.css for j in jobs])
|
||||
for job, result in zip(jobs, results):
|
||||
if isinstance(result, dict):
|
||||
for msg in result['messages']:
|
||||
err = message_to_error(msg, job.name, job.line_offset)
|
||||
if err is not None:
|
||||
errors.append(err)
|
||||
elif isinstance(result, list) and result:
|
||||
if result['type'] == 'error':
|
||||
errors.append(CSSParseError(_('Failed to process CSS in {name} with errors: {errors}').format(
|
||||
name=job.name, errors='\n'.join(result)), job.name))
|
||||
else:
|
||||
errors.append(CSSParseError(_('Failed to process CSS in {name}').format(name=job.name), job.name))
|
||||
name=job.name, errors=result['error']), job.name))
|
||||
continue
|
||||
result = json.loads(result['results']['output'])
|
||||
rule_metadata = result['rule_metadata']
|
||||
for msg in result['results']['warnings']:
|
||||
err = message_to_error(msg, job.name, job.line_offset, rule_metadata)
|
||||
if err is not None:
|
||||
errors.append(err)
|
||||
return errors
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user