UNPKG

liferay-theme-tasks

Version:

A set of tasks for building and deploying Liferay Portal themes.

659 lines (517 loc) 9.45 kB
/** * SPDX-FileCopyrightText: © 2017 Liferay, Inc. <https://liferay.com> * SPDX-License-Identifier: MIT */ // http://www.w3.org/TR/CSS21/grammar.html // https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027 var commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g; module.exports = function (css, options) { options = options || {}; options.position = options.position === false ? false : true; /** * Positional. */ var filename = options.filename || '(unknown)'; var lineno = 1; var column = 1; /** * Update lineno and column based on `str`. */ function updatePosition(str) { var lines = str.match(/\n/g); if (lines) { lineno += lines.length; } var i = str.lastIndexOf('\n'); column = ~i ? str.length - i : column + str.length; } /** * Mark position and patch `node.position`. */ function position() { if (!options.position) { return positionNoop; } var start = {column, line: lineno}; return function (node) { node.position = new Position(start); whitespace(); return node; }; } function Position(start) { this.start = start; this.end = {column, line: lineno}; this.filename = options.filename; } /** * Non-enumerable source string. */ Position.prototype.source = css; /** * Return `node`. */ function positionNoop(node) { whitespace(); return node; } /** * Error `msg`. */ function error(msg, start) { var errorThrown = new Error(`${filename} (${lineno}:${column}) ${msg}`); errorThrown.position = new Position(start); errorThrown.filename = filename; errorThrown.description = msg; throw errorThrown; } /** * Parse stylesheet. */ function stylesheet() { return { stylesheet: { rules: rules(), }, type: 'stylesheet', }; } /** * Opening brace. */ function open() { return match(/^{\s*/); } /** * Closing brace. */ function close() { return match(/^}/); } /** * Parse ruleset. */ function rules() { var node; var rules = []; whitespace(); comments(rules); while ( css.length && css.charAt(0) !== '}' && (node = atrule() || rule()) ) { rules.push(node); comments(rules); } return rules; } /** * Match `re` and return captures. */ function match(re) { var m = re.exec(css); if (!m) { return; } var str = m[0]; updatePosition(str); css = css.slice(str.length); return m; } /** * Parse whitespace. */ function whitespace() { match(/^\s*/); } /** * Parse comments; */ function comments(rules) { var c; rules = rules || []; while ((c = comment())) { rules.push(c); } return rules; } /** * Parse comment. */ function comment() { var pos = position(); if ('/' !== css.charAt(0) || '*' !== css.charAt(1)) { return; } var i = 2; while ( null !== css.charAt(i) && ('*' !== css.charAt(i) || '/' !== css.charAt(i + 1)) ) { ++i; } i += 2; var str = css.slice(2, i - 2); column += 2; updatePosition(str); css = css.slice(i); column += 2; return pos({ comment: str, type: 'comment', }); } /** * Parse selector. */ function selector() { var m = match(/^([^{]+)/); if (!m) { return; } /* @fix Remove all comments from selectors * http://ostermiller.org/findcomment.html */ return trim(m[0]) .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '') .split(/\s*,\s*/); } /** * Parse declaration. */ function declaration() { var pos = position(); // prop var prop = match(/^(\*?[-#/*\w]+(\[[0-9a-z_-]+\])?)\s*/); if (!prop) { return; } prop = trim(prop[0]); // : if (!match(/^:\s*/)) { return error("property missing ':'"); } // val var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/); if (!val) { return error('property missing value'); } var ret = pos({ property: prop.replace(commentre, ''), type: 'declaration', value: trim(val[0]).replace(commentre, ''), }); // ; match(/^[;\s]*/); return ret; } /** * Parse declarations. */ function declarations() { var decls = []; if (!open()) { return error("missing '{'"); } comments(decls); // declarations var decl; var rule; while ((decl = declaration()) || (rule = expandedatrule())) { if (decl) { decls.push(decl); comments(decls); } if (rule) { comments(); rule = null; } } if (!close()) { return error("missing '}'"); } return decls; } /** * Parse keyframe. */ function keyframe() { var m; var vals = []; var pos = position(); while ((m = match(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/))) { vals.push(m[1]); match(/^,\s*/); } if (!vals.length) { return; } return pos({ declarations: declarations(), type: 'keyframe', values: vals, }); } /** * Parse keyframes. */ function atkeyframes() { var pos = position(); var m = match(/^@([-\w]+)?keyframes */); if (!m) { return; } var vendor = m[1]; // identifier m = match(/^([-\w]+)\s*/); if (!m) { return error('@keyframes missing name'); } var name = m[1]; if (!open()) { return error("@keyframes missing '{'"); } var frame; var frames = comments(); while ((frame = keyframe())) { frames.push(frame); frames = frames.concat(comments()); } if (!close()) { return error("@keyframes missing '}'"); } return pos({ keyframes: frames, name, type: 'keyframes', vendor, }); } /** * Parse supports. */ function atsupports() { var pos = position(); var m = match(/^@supports *([^{]+)/); if (!m) { return; } var supports = trim(m[1]); if (!open()) { return error("@supports missing '{'"); } var style = comments().concat(rules()); if (!close()) { return error("@supports missing '}'"); } return pos({ rules: style, supports, type: 'supports', }); } /** * Parse host. */ function athost() { var pos = position(); var m = match(/^@host */); if (!m) { return; } if (!open()) { return error("@host missing '{'"); } var style = comments().concat(rules()); if (!close()) { return error("@host missing '}'"); } return pos({ rules: style, type: 'host', }); } /** * Parse media. */ function atmedia() { var pos = position(); var m = match(/^@media *([^{]+)/); if (!m) { return; } var media = trim(m[1]); if (!open()) { return error("@media missing '{'"); } var style = comments().concat(rules()); if (!close()) { return error("@media missing '}'"); } return pos({ media, rules: style, type: 'media', }); } /** * Parse paged media. */ function atpage() { var pos = position(); var m = match(/^@page */); if (!m) { return; } var sel = selector() || []; if (!open()) { return error("@page missing '{'"); } var decls = comments(); // declarations var decl; while ((decl = declaration())) { decls.push(decl); decls = decls.concat(comments()); } if (!close()) { return error("@page missing '}'"); } return pos({ declarations: decls, selectors: sel, type: 'page', }); } /** * Parse document. */ function atdocument() { var pos = position(); var m = match(/^@([-\w]+)?document *([^{]+)/); if (!m) { return; } var vendor = trim(m[1]); var doc = trim(m[2]); if (!open()) { return error("@document missing '{'"); } var style = comments().concat(rules()); if (!close()) { return error("@document missing '}'"); } return pos({ document: doc, rules: style, type: 'document', vendor, }); } function atviewport() { var pos = position(); var m = match(/^@([-\w]+)?viewport */); if (!m) { return; } var vendor = m[1]; return pos({ declarations: declarations(), type: 'viewport', vendor, }); } function atfontface() { var pos = position(); var m = match(/^@([-\w]+)?font-face */); if (!m) { return; } return pos({ declarations: declarations(), type: 'fontface', }); } /** * Parse import */ function atimport() { return _atrule('import'); } /** * Parse charset */ function atcharset() { return _atrule('charset'); } /** * Parse namespace */ function atnamespace() { return _atrule('namespace'); } /** * Parse non-block at-rules */ function _atrule(name) { var pos = position(); var m = match( new RegExp( '^@' + name + ' *(?:url\\(([^)]+)\\)|([^;\\n]*))(?: *([^;\\n]*))?;' ) ); if (!m) { return; } var ret = {type: name}; ret[name] = trim(m[1]); return pos(ret); } /** * Parse at rule. */ function atrule() { if (css[0] !== '@') { return; } return ( atkeyframes() || atmedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() ); } function expandedatrule() { return atrule() || atviewport() || atfontface(); } /** * Parse rule. */ function rule() { var pos = position(); var sel = selector(); if (!sel) { return error('selector missing'); } comments(); return pos({ declarations: declarations(), selectors: sel, type: 'rule', }); } return stylesheet(); }; /** * Trim `str`. */ function trim(str) { return str ? str.replace(/^\s+|\s+$/g, '') : ''; }