UNPKG

@gechiui/block-editor

Version:
702 lines (567 loc) 11.5 kB
/* eslint-disable @gechiui/no-unused-vars-before-return */ // Adapted from https://github.com/reworkcss/css // because we needed to remove source map support. // http://www.w3.org/TR/CSS21/grammar.htm // https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027 const commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g; export default function ( css, options ) { options = options || {}; /** * Positional. */ let lineno = 1; let column = 1; /** * Update lineno and column based on `str`. */ function updatePosition( str ) { const lines = str.match( /\n/g ); if ( lines ) { lineno += lines.length; } const i = str.lastIndexOf( '\n' ); // eslint-disable-next-line no-bitwise column = ~i ? str.length - i : column + str.length; } /** * Mark position and patch `node.position`. */ function position() { const start = { line: lineno, column }; return function ( node ) { node.position = new Position( start ); whitespace(); return node; }; } /** * Store position information for a node */ function Position( start ) { this.start = start; this.end = { line: lineno, column }; this.source = options.source; } /** * Non-enumerable source string */ Position.prototype.content = css; /** * Error `msg`. */ const errorsList = []; function error( msg ) { const err = new Error( options.source + ':' + lineno + ':' + column + ': ' + msg ); err.reason = msg; err.filename = options.source; err.line = lineno; err.column = column; err.source = css; if ( options.silent ) { errorsList.push( err ); } else { throw err; } } /** * Parse stylesheet. */ function stylesheet() { const rulesList = rules(); return { type: 'stylesheet', stylesheet: { source: options.source, rules: rulesList, parsingErrors: errorsList, }, }; } /** * Opening brace. */ function open() { return match( /^{\s*/ ); } /** * Closing brace. */ function close() { return match( /^}/ ); } /** * Parse ruleset. */ function rules() { let node; const accumulator = []; whitespace(); comments( accumulator ); while ( css.length && css.charAt( 0 ) !== '}' && ( node = atrule() || rule() ) ) { if ( node !== false ) { accumulator.push( node ); comments( accumulator ); } } return accumulator; } /** * Match `re` and return captures. */ function match( re ) { const m = re.exec( css ); if ( ! m ) { return; } const str = m[ 0 ]; updatePosition( str ); css = css.slice( str.length ); return m; } /** * Parse whitespace. */ function whitespace() { match( /^\s*/ ); } /** * Parse comments; */ function comments( accumulator ) { let c; accumulator = accumulator || []; // eslint-disable-next-line no-cond-assign while ( ( c = comment() ) ) { if ( c !== false ) { accumulator.push( c ); } } return accumulator; } /** * Parse comment. */ function comment() { const pos = position(); if ( '/' !== css.charAt( 0 ) || '*' !== css.charAt( 1 ) ) { return; } let i = 2; while ( '' !== css.charAt( i ) && ( '*' !== css.charAt( i ) || '/' !== css.charAt( i + 1 ) ) ) { ++i; } i += 2; if ( '' === css.charAt( i - 1 ) ) { return error( 'End of comment missing' ); } const str = css.slice( 2, i - 2 ); column += 2; updatePosition( str ); css = css.slice( i ); column += 2; return pos( { type: 'comment', comment: str, } ); } /** * Parse selector. */ function selector() { const m = match( /^([^{]+)/ ); if ( ! m ) { return; } // FIXME: Remove all comments from selectors http://ostermiller.org/findcomment.html return trim( m[ 0 ] ) .replace( /\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '' ) .replace( /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, function ( matched ) { return matched.replace( /,/g, '\u200C' ); } ) .split( /\s*(?![^(]*\)),\s*/ ) .map( function ( s ) { return s.replace( /\u200C/g, ',' ); } ); } /** * Parse declaration. */ function declaration() { const pos = position(); // prop let prop = match( /^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/ ); if ( ! prop ) { return; } prop = trim( prop[ 0 ] ); // : if ( ! match( /^:\s*/ ) ) { return error( "property missing ':'" ); } // val const val = match( /^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/ ); const ret = pos( { type: 'declaration', property: prop.replace( commentre, '' ), value: val ? trim( val[ 0 ] ).replace( commentre, '' ) : '', } ); // ; match( /^[;\s]*/ ); return ret; } /** * Parse declarations. */ function declarations() { const decls = []; if ( ! open() ) { return error( "missing '{'" ); } comments( decls ); // declarations let decl; // eslint-disable-next-line no-cond-assign while ( ( decl = declaration() ) ) { if ( decl !== false ) { decls.push( decl ); comments( decls ); } } if ( ! close() ) { return error( "missing '}'" ); } return decls; } /** * Parse keyframe. */ function keyframe() { let m; const vals = []; const pos = position(); // eslint-disable-next-line no-cond-assign while ( ( m = match( /^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/ ) ) ) { vals.push( m[ 1 ] ); match( /^,\s*/ ); } if ( ! vals.length ) { return; } return pos( { type: 'keyframe', values: vals, declarations: declarations(), } ); } /** * Parse keyframes. */ function atkeyframes() { const pos = position(); let m = match( /^@([-\w]+)?keyframes\s*/ ); if ( ! m ) { return; } const vendor = m[ 1 ]; // identifier m = match( /^([-\w]+)\s*/ ); if ( ! m ) { return error( '@keyframes missing name' ); } const name = m[ 1 ]; if ( ! open() ) { return error( "@keyframes missing '{'" ); } let frame; let frames = comments(); // eslint-disable-next-line no-cond-assign while ( ( frame = keyframe() ) ) { frames.push( frame ); frames = frames.concat( comments() ); } if ( ! close() ) { return error( "@keyframes missing '}'" ); } return pos( { type: 'keyframes', name, vendor, keyframes: frames, } ); } /** * Parse supports. */ function atsupports() { const pos = position(); const m = match( /^@supports *([^{]+)/ ); if ( ! m ) { return; } const supports = trim( m[ 1 ] ); if ( ! open() ) { return error( "@supports missing '{'" ); } const style = comments().concat( rules() ); if ( ! close() ) { return error( "@supports missing '}'" ); } return pos( { type: 'supports', supports, rules: style, } ); } /** * Parse host. */ function athost() { const pos = position(); const m = match( /^@host\s*/ ); if ( ! m ) { return; } if ( ! open() ) { return error( "@host missing '{'" ); } const style = comments().concat( rules() ); if ( ! close() ) { return error( "@host missing '}'" ); } return pos( { type: 'host', rules: style, } ); } /** * Parse media. */ function atmedia() { const pos = position(); const m = match( /^@media *([^{]+)/ ); if ( ! m ) { return; } const media = trim( m[ 1 ] ); if ( ! open() ) { return error( "@media missing '{'" ); } const style = comments().concat( rules() ); if ( ! close() ) { return error( "@media missing '}'" ); } return pos( { type: 'media', media, rules: style, } ); } /** * Parse custom-media. */ function atcustommedia() { const pos = position(); const m = match( /^@custom-media\s+(--[^\s]+)\s*([^{;]+);/ ); if ( ! m ) { return; } return pos( { type: 'custom-media', name: trim( m[ 1 ] ), media: trim( m[ 2 ] ), } ); } /** * Parse paged media. */ function atpage() { const pos = position(); const m = match( /^@page */ ); if ( ! m ) { return; } const sel = selector() || []; if ( ! open() ) { return error( "@page missing '{'" ); } let decls = comments(); // declarations let decl; // eslint-disable-next-line no-cond-assign while ( ( decl = declaration() ) ) { decls.push( decl ); decls = decls.concat( comments() ); } if ( ! close() ) { return error( "@page missing '}'" ); } return pos( { type: 'page', selectors: sel, declarations: decls, } ); } /** * Parse document. */ function atdocument() { const pos = position(); const m = match( /^@([-\w]+)?document *([^{]+)/ ); if ( ! m ) { return; } const vendor = trim( m[ 1 ] ); const doc = trim( m[ 2 ] ); if ( ! open() ) { return error( "@document missing '{'" ); } const style = comments().concat( rules() ); if ( ! close() ) { return error( "@document missing '}'" ); } return pos( { type: 'document', document: doc, vendor, rules: style, } ); } /** * Parse font-face. */ function atfontface() { const pos = position(); const m = match( /^@font-face\s*/ ); if ( ! m ) { return; } if ( ! open() ) { return error( "@font-face missing '{'" ); } let decls = comments(); // declarations let decl; // eslint-disable-next-line no-cond-assign while ( ( decl = declaration() ) ) { decls.push( decl ); decls = decls.concat( comments() ); } if ( ! close() ) { return error( "@font-face missing '}'" ); } return pos( { type: 'font-face', declarations: decls, } ); } /** * Parse import */ const atimport = _compileAtrule( 'import' ); /** * Parse charset */ const atcharset = _compileAtrule( 'charset' ); /** * Parse namespace */ const atnamespace = _compileAtrule( 'namespace' ); /** * Parse non-block at-rules */ function _compileAtrule( name ) { const re = new RegExp( '^@' + name + '\\s*([^;]+);' ); return function () { const pos = position(); const m = match( re ); if ( ! m ) { return; } const ret = { type: name }; ret[ name ] = m[ 1 ].trim(); return pos( ret ); }; } /** * Parse at rule. */ function atrule() { if ( css[ 0 ] !== '@' ) { return; } return ( atkeyframes() || atmedia() || atcustommedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() || atfontface() ); } /** * Parse rule. */ function rule() { const pos = position(); const sel = selector(); if ( ! sel ) { return error( 'selector missing' ); } comments(); return pos( { type: 'rule', selectors: sel, declarations: declarations(), } ); } return addParent( stylesheet() ); } /** * Trim `str`. */ function trim( str ) { return str ? str.replace( /^\s+|\s+$/g, '' ) : ''; } /** * Adds non-enumerable parent node reference to each node. */ function addParent( obj, parent ) { const isNode = obj && typeof obj.type === 'string'; const childParent = isNode ? obj : parent; for ( const k in obj ) { const value = obj[ k ]; if ( Array.isArray( value ) ) { value.forEach( function ( v ) { addParent( v, childParent ); } ); } else if ( value && typeof value === 'object' ) { addParent( value, childParent ); } } if ( isNode ) { Object.defineProperty( obj, 'parent', { configurable: true, writable: true, enumerable: false, value: parent || null, } ); } return obj; } /* eslint-enable @gechiui/no-unused-vars-before-return */