csslint: allow globals like @import inside sections
This commit is contained in:
parent
be4fd17113
commit
d736a00bc1
|
@ -131,6 +131,7 @@ linterMan.DEFAULTS = {
|
||||||
'duplicate-properties': 1,
|
'duplicate-properties': 1,
|
||||||
'empty-rules': 1,
|
'empty-rules': 1,
|
||||||
'errors': 1,
|
'errors': 1,
|
||||||
|
'globals-in-document': 1,
|
||||||
'known-properties': 1,
|
'known-properties': 1,
|
||||||
'selector-newline': 1,
|
'selector-newline': 1,
|
||||||
'shorthand-overrides': 1,
|
'shorthand-overrides': 1,
|
||||||
|
|
|
@ -1003,6 +1003,26 @@ CSSLint.addRule['font-sizes'] = [{
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
CSSLint.addRule['globals-in-document'] = [{
|
||||||
|
name: 'Warn about global @ rules inside @-moz-document',
|
||||||
|
desc: 'Warn about @import, @charset, @namespace inside @-moz-document',
|
||||||
|
browsers: 'All',
|
||||||
|
}, (rule, parser, reporter) => {
|
||||||
|
let level = 0;
|
||||||
|
let index = 0;
|
||||||
|
parser.addListener('startdocument', () => level++);
|
||||||
|
parser.addListener('enddocument', () => level-- * index++);
|
||||||
|
const check = event => {
|
||||||
|
if (level && index) {
|
||||||
|
reporter.report(`A nested @${event.type} is valid only if this @-moz-document section ` +
|
||||||
|
'is the first one matched for any given URL.', event, rule);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
parser.addListener('import', check);
|
||||||
|
parser.addListener('charset', check);
|
||||||
|
parser.addListener('namespace', check);
|
||||||
|
}];
|
||||||
|
|
||||||
CSSLint.addRule['gradients'] = [{
|
CSSLint.addRule['gradients'] = [{
|
||||||
name: 'Require all gradient definitions',
|
name: 'Require all gradient definitions',
|
||||||
desc: 'When using a vendor-prefixed gradient, make sure to use them all.',
|
desc: 'When using a vendor-prefixed gradient, make sure to use them all.',
|
||||||
|
|
|
@ -3439,31 +3439,16 @@ self.parserlib = (() => {
|
||||||
_stylesheet() {
|
_stylesheet() {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
this.fire('startstylesheet');
|
this.fire('startstylesheet');
|
||||||
this._skipCruft();
|
this._sheetGlobals();
|
||||||
for (const [type, fn, max = Infinity] of [
|
|
||||||
[Tokens.CHARSET_SYM, this._charset, 1],
|
|
||||||
[Tokens.IMPORT_SYM, this._import],
|
|
||||||
[Tokens.NAMESPACE_SYM, this._namespace],
|
|
||||||
]) {
|
|
||||||
for (let i = 0; i++ < max && stream.peek() === type;) {
|
|
||||||
fn.call(this, stream.get(true));
|
|
||||||
this._skipCruft();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const {topDocOnly} = this.options;
|
const {topDocOnly} = this.options;
|
||||||
const allowedActions = topDocOnly ? Parser.ACTIONS.topDoc : Parser.ACTIONS.stylesheet;
|
const allowedActions = topDocOnly ? Parser.ACTIONS.topDoc : Parser.ACTIONS.stylesheet;
|
||||||
for (let tt, token; (tt = (token = stream.get(true)).type); this._skipCruft()) {
|
for (let tt, token; (tt = (token = stream.get(true)).type); this._skipCruft()) {
|
||||||
try {
|
try {
|
||||||
let action = allowedActions.get(tt);
|
const action = allowedActions.get(tt);
|
||||||
if (action) {
|
if (action) {
|
||||||
action.call(this, token);
|
action.call(this, token);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
action = Parser.ACTIONS.stylesheetMisplaced.get(tt);
|
|
||||||
if (action) {
|
|
||||||
action.call(this, token, true);
|
|
||||||
throw new SyntaxError(Tokens[tt].text + ' not allowed here.', token);
|
|
||||||
}
|
|
||||||
if (topDocOnly) {
|
if (topDocOnly) {
|
||||||
stream.readDeclValue({stopOn: '{}'});
|
stream.readDeclValue({stopOn: '{}'});
|
||||||
if (stream._reader.peek() === '{') {
|
if (stream._reader.peek() === '{') {
|
||||||
|
@ -3489,25 +3474,40 @@ self.parserlib = (() => {
|
||||||
this.fire('endstylesheet');
|
this.fire('endstylesheet');
|
||||||
}
|
}
|
||||||
|
|
||||||
_charset(start, misplaced) {
|
_sheetGlobals() {
|
||||||
|
const stream = this._tokenStream;
|
||||||
|
this._skipCruft();
|
||||||
|
for (const [type, fn, max = Infinity] of [
|
||||||
|
[Tokens.CHARSET_SYM, this._charset, 1],
|
||||||
|
[Tokens.IMPORT_SYM, this._import],
|
||||||
|
[Tokens.NAMESPACE_SYM, this._namespace],
|
||||||
|
]) {
|
||||||
|
for (let i = 0; i++ < max && stream.peek() === type;) {
|
||||||
|
fn.call(this, stream.get(true));
|
||||||
|
this._skipCruft();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_charset(start) {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
const charset = stream.mustMatch(Tokens.STRING).value;
|
const charset = stream.mustMatch(Tokens.STRING).value;
|
||||||
stream.mustMatch(Tokens.SEMICOLON);
|
stream.mustMatch(Tokens.SEMICOLON);
|
||||||
if (!misplaced) this.fire({type: 'charset', charset}, start);
|
this.fire({type: 'charset', charset}, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
_import(start, misplaced) {
|
_import(start) {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
const token = stream.mustMatch(TT.stringUri);
|
const token = stream.mustMatch(TT.stringUri);
|
||||||
const uri = token.uri || token.value.replace(/^["']|["']$/g, '');
|
const uri = token.uri || token.value.replace(/^["']|["']$/g, '');
|
||||||
this._ws();
|
this._ws();
|
||||||
const media = this._mediaQueryList();
|
const media = this._mediaQueryList();
|
||||||
stream.mustMatch(Tokens.SEMICOLON);
|
stream.mustMatch(Tokens.SEMICOLON);
|
||||||
if (!misplaced) this.fire({type: 'import', media, uri}, start);
|
this.fire({type: 'import', media, uri}, start);
|
||||||
this._ws();
|
this._ws();
|
||||||
}
|
}
|
||||||
|
|
||||||
_namespace(start, misplaced) {
|
_namespace(start) {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
this._ws();
|
this._ws();
|
||||||
const prefix = stream.match(Tokens.IDENT).value;
|
const prefix = stream.match(Tokens.IDENT).value;
|
||||||
|
@ -3515,16 +3515,16 @@ self.parserlib = (() => {
|
||||||
const token = stream.mustMatch(TT.stringUri);
|
const token = stream.mustMatch(TT.stringUri);
|
||||||
const uri = token.uri || token.value.replace(/^["']|["']$/g, '');
|
const uri = token.uri || token.value.replace(/^["']|["']$/g, '');
|
||||||
stream.mustMatch(Tokens.SEMICOLON);
|
stream.mustMatch(Tokens.SEMICOLON);
|
||||||
if (!misplaced) this.fire({type: 'namespace', prefix, uri}, start);
|
this.fire({type: 'namespace', prefix, uri}, start);
|
||||||
this._ws();
|
this._ws();
|
||||||
}
|
}
|
||||||
|
|
||||||
_supports(start, misplaced) {
|
_supports(start) {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
this._ws();
|
this._ws();
|
||||||
this._supportsCondition();
|
this._supportsCondition();
|
||||||
stream.mustMatch(Tokens.LBRACE);
|
stream.mustMatch(Tokens.LBRACE);
|
||||||
if (!misplaced) this.fire('startsupports', start);
|
this.fire('startsupports', start);
|
||||||
this._ws();
|
this._ws();
|
||||||
for (;; stream.skipComment()) {
|
for (;; stream.skipComment()) {
|
||||||
const action = Parser.ACTIONS.supports.get(stream.peek());
|
const action = Parser.ACTIONS.supports.get(stream.peek());
|
||||||
|
@ -3535,7 +3535,7 @@ self.parserlib = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream.mustMatch(Tokens.RBRACE);
|
stream.mustMatch(Tokens.RBRACE);
|
||||||
if (!misplaced) this.fire('endsupports');
|
this.fire('endsupports');
|
||||||
this._ws();
|
this._ws();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3747,6 +3747,9 @@ self.parserlib = (() => {
|
||||||
if (this.options.topDocOnly) {
|
if (this.options.topDocOnly) {
|
||||||
stream.readDeclValue({stopOn: '}'});
|
stream.readDeclValue({stopOn: '}'});
|
||||||
} else {
|
} else {
|
||||||
|
/* We allow @import and such inside document sections because the final generated CSS for
|
||||||
|
* a given page may be valid e.g. if this section is the first one that matched the URL */
|
||||||
|
this._sheetGlobals();
|
||||||
this._ws();
|
this._ws();
|
||||||
let action;
|
let action;
|
||||||
do action = Parser.ACTIONS.document.get(stream.peek());
|
do action = Parser.ACTIONS.document.get(stream.peek());
|
||||||
|
@ -4528,12 +4531,6 @@ self.parserlib = (() => {
|
||||||
[Tokens.S, Parser.prototype._ws],
|
[Tokens.S, Parser.prototype._ws],
|
||||||
]),
|
]),
|
||||||
|
|
||||||
stylesheetMisplaced: new Map([
|
|
||||||
[Tokens.CHARSET_SYM, Parser.prototype._charset],
|
|
||||||
[Tokens.IMPORT_SYM, Parser.prototype._import],
|
|
||||||
[Tokens.NAMESPACE_SYM, Parser.prototype._namespace],
|
|
||||||
]),
|
|
||||||
|
|
||||||
topDoc: new Map([
|
topDoc: new Map([
|
||||||
symDocument,
|
symDocument,
|
||||||
symUnknown,
|
symUnknown,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user