diff --git a/background/background-worker.js b/background/background-worker.js index ddb33d53..1e6126f6 100644 --- a/background/background-worker.js +++ b/background/background-worker.js @@ -37,7 +37,7 @@ function compileUsercss(preprocessor, code, vars) { const builder = getUsercssCompiler(preprocessor); vars = simpleVars(vars); return Promise.resolve(builder.preprocess ? builder.preprocess(code, vars) : code) - .then(code => parseMozFormat({code})) + .then(code => parseMozFormat({code, emptyDocument: preprocessor === 'stylus'})) .then(({sections, errors}) => { if (builder.postprocess) { builder.postprocess(sections, vars); diff --git a/js/moz-parser.js b/js/moz-parser.js index d79c4789..6ffb3b67 100644 --- a/js/moz-parser.js +++ b/js/moz-parser.js @@ -7,10 +7,12 @@ * Puts the global comments into the following section to minimize the amount of global sections. * Doesn't move the comment with ==UserStyle== inside. * @param {string} code + * @param {boolean} emptyDocument - https://github.com/stylus/stylus/issues/2415, + * TODO: update stylus-lang and remove emptyDocument everywhere * @param {number} styleId - used to preserve parserCache on subsequent runs over the same style * @returns {{sections: Array, errors: Array}} */ -function parseMozFormat({code, styleId}) { +function parseMozFormat({code, emptyDocument, styleId}) { const CssToProperty = { 'url': 'urls', 'url-prefix': 'urlPrefixes', @@ -18,7 +20,7 @@ function parseMozFormat({code, styleId}) { 'regexp': 'regexps', }; const hasSingleEscapes = /([^\\]|^)\\([^\\]|$)/; - const parser = new parserlib.css.Parser({starHack: true}); + const parser = new parserlib.css.Parser({starHack: true, emptyDocument}); const sectionStack = [{code: '', start: 0}]; const errors = []; const sections = []; @@ -70,6 +72,13 @@ function parseMozFormat({code, styleId}) { doAddSection(section); }); + parser.addListener('emptydocument', e => { + const token = parser._tokenStream._token; + const section = sectionStack[sectionStack.length - 1]; + section.code += mozStyle.slice(section.start, e.offset); + section.start = token.offset + token.value.length; + }); + parser.addListener('endstylesheet', () => { // add nonclosed outer sections (either broken or the last global one) const lastSection = sectionStack[sectionStack.length - 1]; diff --git a/js/usercss.js b/js/usercss.js index 85937ec6..b2afea36 100644 --- a/js/usercss.js +++ b/js/usercss.js @@ -71,7 +71,7 @@ const usercss = (() => { .then(({sections, errors}) => { if (!errors.length) errors = false; if (!sections.length || errors && !allowErrors) { - throw errors; + throw errors || 'Style does not contain any actual CSS to apply.'; } style.sections = sections; return allowErrors ? {style, errors} : style; diff --git a/vendor-overwrites/csslint/parserlib.js b/vendor-overwrites/csslint/parserlib.js index 7ab9b454..60ffc3a1 100644 --- a/vendor-overwrites/csslint/parserlib.js +++ b/vendor-overwrites/csslint/parserlib.js @@ -3865,6 +3865,7 @@ self.parserlib = (() => { * @param {Boolean} [options.starHack] - allows IE6 star hack * @param {Boolean} [options.underscoreHack] - interprets leading underscores as IE6-7 for known properties * @param {Boolean} [options.ieFilters] - accepts IE < 8 filters instead of throwing syntax errors + * @param {Boolean} [options.emptyDocument] - accepts @document without {} block produced by stylus-lang */ constructor(options) { super(); @@ -4333,6 +4334,11 @@ self.parserlib = (() => { functions.push(this._documentFunction()); } while (stream.match(Tokens.COMMA)); + this._ws(); + if (this.options.emptyDocument && stream.peek() !== Tokens.LBRACE) { + this.fire({type: 'emptydocument', functions, prefix}, start); + return; + } stream.mustMatch(Tokens.LBRACE); this.fire({