| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- /**
- * Defines the css token callbacks type used by this module.
- * @typedef {object} CssTokenCallbacks
- * @property {((input: string, start: number, end: number) => number)=} comment
- * @property {((input: string, start: number, end: number) => number)=} whitespace
- * @property {((input: string, start: number, end: number) => number)=} string
- * @property {((input: string, start: number, end: number) => number)=} leftCurlyBracket
- * @property {((input: string, start: number, end: number) => number)=} rightCurlyBracket
- * @property {((input: string, start: number, end: number) => number)=} leftParenthesis
- * @property {((input: string, start: number, end: number) => number)=} rightParenthesis
- * @property {((input: string, start: number, end: number) => number)=} leftSquareBracket
- * @property {((input: string, start: number, end: number) => number)=} rightSquareBracket
- * @property {((input: string, start: number, end: number) => number)=} function
- * @property {((input: string, start: number, end: number, innerStart: number, innerEnd: number) => number)=} url
- * @property {((input: string, start: number, end: number) => number)=} colon
- * @property {((input: string, start: number, end: number) => number)=} atKeyword
- * @property {((input: string, start: number, end: number) => number)=} delim
- * @property {((input: string, start: number, end: number) => number)=} identifier
- * @property {((input: string, start: number, end: number) => number)=} percentage
- * @property {((input: string, start: number, end: number) => number)=} number
- * @property {((input: string, start: number, end: number) => number)=} dimension
- * @property {((input: string, start: number, end: number, isId: boolean) => number)=} hash
- * @property {((input: string, start: number, end: number) => number)=} semicolon
- * @property {((input: string, start: number, end: number) => number)=} comma
- * @property {((input: string, start: number, end: number) => number)=} cdo
- * @property {((input: string, start: number, end: number) => number)=} cdc
- * @property {((input: string, start: number, end: number) => number)=} badStringToken
- * @property {((input: string, start: number, end: number) => number)=} badUrlToken
- * @property {(() => boolean)=} needTerminate
- */
- /** @typedef {(input: string, pos: number, callbacks: CssTokenCallbacks) => number} CharHandler */
- // spec: https://drafts.csswg.org/css-syntax/
- const CC_LINE_FEED = "\n".charCodeAt(0);
- const CC_CARRIAGE_RETURN = "\r".charCodeAt(0);
- const CC_FORM_FEED = "\f".charCodeAt(0);
- const CC_TAB = "\t".charCodeAt(0);
- const CC_SPACE = " ".charCodeAt(0);
- const CC_SOLIDUS = "/".charCodeAt(0);
- const CC_REVERSE_SOLIDUS = "\\".charCodeAt(0);
- const CC_ASTERISK = "*".charCodeAt(0);
- const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
- const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
- const CC_LEFT_CURLY = "{".charCodeAt(0);
- const CC_RIGHT_CURLY = "}".charCodeAt(0);
- const CC_LEFT_SQUARE = "[".charCodeAt(0);
- const CC_RIGHT_SQUARE = "]".charCodeAt(0);
- const CC_QUOTATION_MARK = '"'.charCodeAt(0);
- const CC_APOSTROPHE = "'".charCodeAt(0);
- const CC_FULL_STOP = ".".charCodeAt(0);
- const CC_COLON = ":".charCodeAt(0);
- const CC_SEMICOLON = ";".charCodeAt(0);
- const CC_COMMA = ",".charCodeAt(0);
- const CC_PERCENTAGE = "%".charCodeAt(0);
- const CC_AT_SIGN = "@".charCodeAt(0);
- const CC_LOW_LINE = "_".charCodeAt(0);
- const CC_LOWER_A = "a".charCodeAt(0);
- const CC_LOWER_F = "f".charCodeAt(0);
- const CC_LOWER_E = "e".charCodeAt(0);
- const CC_LOWER_U = "u".charCodeAt(0);
- const CC_LOWER_Z = "z".charCodeAt(0);
- const CC_UPPER_A = "A".charCodeAt(0);
- const CC_UPPER_F = "F".charCodeAt(0);
- const CC_UPPER_E = "E".charCodeAt(0);
- const CC_UPPER_U = "U".charCodeAt(0);
- const CC_UPPER_Z = "Z".charCodeAt(0);
- const CC_0 = "0".charCodeAt(0);
- const CC_9 = "9".charCodeAt(0);
- const CC_NUMBER_SIGN = "#".charCodeAt(0);
- const CC_PLUS_SIGN = "+".charCodeAt(0);
- const CC_HYPHEN_MINUS = "-".charCodeAt(0);
- const CC_LESS_THAN_SIGN = "<".charCodeAt(0);
- const CC_GREATER_THAN_SIGN = ">".charCodeAt(0);
- /** @type {CharHandler} */
- const consumeSpace = (input, pos, callbacks) => {
- const start = pos - 1;
- // Consume as much whitespace as possible.
- while (_isWhiteSpace(input.charCodeAt(pos))) {
- pos++;
- }
- // Return a <whitespace-token>.
- if (callbacks.whitespace !== undefined) {
- return callbacks.whitespace(input, start, pos);
- }
- return pos;
- };
- // U+000A LINE FEED. Note that U+000D CARRIAGE RETURN and U+000C FORM FEED are not included in this definition,
- // as they are converted to U+000A LINE FEED during preprocessing.
- //
- // Replace any U+000D CARRIAGE RETURN (CR) code points, U+000C FORM FEED (FF) code points, or pairs of U+000D CARRIAGE RETURN (CR) followed by U+000A LINE FEED (LF) in input by a single U+000A LINE FEED (LF) code point.
- /**
- * Checks whether newline true, if cc is a newline.
- * @param {number} cc char code
- * @returns {boolean} true, if cc is a newline
- */
- const _isNewline = (cc) =>
- cc === CC_LINE_FEED || cc === CC_CARRIAGE_RETURN || cc === CC_FORM_FEED;
- /**
- * Consume extra newline.
- * @param {number} cc char code
- * @param {string} input input
- * @param {number} pos position
- * @returns {number} position
- */
- const consumeExtraNewline = (cc, input, pos) => {
- if (cc === CC_CARRIAGE_RETURN && input.charCodeAt(pos) === CC_LINE_FEED) {
- pos++;
- }
- return pos;
- };
- /**
- * Checks whether space true, if cc is a space (U+0009 CHARACTER TABULATION or U+0020 SPACE).
- * @param {number} cc char code
- * @returns {boolean} true, if cc is a space (U+0009 CHARACTER TABULATION or U+0020 SPACE)
- */
- const _isSpace = (cc) => cc === CC_TAB || cc === CC_SPACE;
- /**
- * Checks whether white space true, if cc is a whitespace.
- * @param {number} cc char code
- * @returns {boolean} true, if cc is a whitespace
- */
- const _isWhiteSpace = (cc) => _isNewline(cc) || _isSpace(cc);
- /**
- * ident-start code point
- *
- * A letter, a non-ASCII code point, or U+005F LOW LINE (_).
- * @param {number} cc char code
- * @returns {boolean} true, if cc is a start code point of an identifier
- */
- const isIdentStartCodePoint = (cc) =>
- (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
- (cc >= CC_UPPER_A && cc <= CC_UPPER_Z) ||
- cc === CC_LOW_LINE ||
- cc >= 0x80;
- /** @type {CharHandler} */
- const consumeDelimToken = (input, pos, callbacks) => {
- // Return a <delim-token> with its value set to the current input code point.
- if (callbacks.delim) {
- pos = callbacks.delim(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeComments = (input, pos, callbacks) => {
- // This section describes how to consume comments from a stream of code points. It returns nothing.
- // If the next two input code point are U+002F SOLIDUS (/) followed by a U+002A ASTERISK (*),
- // consume them and all following code points up to and including the first U+002A ASTERISK (*)
- // followed by a U+002F SOLIDUS (/), or up to an EOF code point.
- // Return to the start of this step.
- while (
- input.charCodeAt(pos) === CC_SOLIDUS &&
- input.charCodeAt(pos + 1) === CC_ASTERISK
- ) {
- const start = pos;
- pos += 2;
- for (;;) {
- if (pos === input.length) {
- // If the preceding paragraph ended by consuming an EOF code point, this is a parse error.
- return pos;
- }
- if (
- input.charCodeAt(pos) === CC_ASTERISK &&
- input.charCodeAt(pos + 1) === CC_SOLIDUS
- ) {
- pos += 2;
- if (callbacks.comment) {
- pos = callbacks.comment(input, start, pos);
- }
- break;
- }
- pos++;
- }
- }
- return pos;
- };
- /**
- * Checks whether hex digit true, if cc is a hex digit.
- * @param {number} cc char code
- * @returns {boolean} true, if cc is a hex digit
- */
- const _isHexDigit = (cc) =>
- _isDigit(cc) ||
- (cc >= CC_UPPER_A && cc <= CC_UPPER_F) ||
- (cc >= CC_LOWER_A && cc <= CC_LOWER_F);
- /**
- * Consume an escaped code point.
- * @param {string} input input
- * @param {number} pos position
- * @returns {number} position
- */
- const _consumeAnEscapedCodePoint = (input, pos) => {
- // This section describes how to consume an escaped code point.
- // It assumes that the U+005C REVERSE SOLIDUS (\) has already been consumed and that the next input code point has already been verified to be part of a valid escape.
- // It will return a code point.
- // Consume the next input code point.
- const cc = input.charCodeAt(pos);
- pos++;
- // EOF
- // This is a parse error. Return U+FFFD REPLACEMENT CHARACTER (�).
- if (pos === input.length) {
- return pos;
- }
- // hex digit
- // Consume as many hex digits as possible, but no more than 5.
- // Note that this means 1-6 hex digits have been consumed in total.
- // If the next input code point is whitespace, consume it as well.
- // Interpret the hex digits as a hexadecimal number.
- // If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point, return U+FFFD REPLACEMENT CHARACTER (�).
- // Otherwise, return the code point with that value.
- if (_isHexDigit(cc)) {
- for (let i = 0; i < 5; i++) {
- if (_isHexDigit(input.charCodeAt(pos))) {
- pos++;
- }
- }
- const cc = input.charCodeAt(pos);
- if (_isWhiteSpace(cc)) {
- pos++;
- pos = consumeExtraNewline(cc, input, pos);
- }
- return pos;
- }
- // anything else
- // Return the current input code point.
- return pos;
- };
- /** @type {CharHandler} */
- const consumeAStringToken = (input, pos, callbacks) => {
- // This section describes how to consume a string token from a stream of code points.
- // It returns either a <string-token> or <bad-string-token>.
- //
- // This algorithm may be called with an ending code point, which denotes the code point that ends the string.
- // If an ending code point is not specified, the current input code point is used.
- const start = pos - 1;
- const endingCodePoint = input.charCodeAt(pos - 1);
- // Initially create a <string-token> with its value set to the empty string.
- // Repeatedly consume the next input code point from the stream:
- for (;;) {
- // EOF
- // This is a parse error. Return the <string-token>.
- if (pos === input.length) {
- if (callbacks.string !== undefined) {
- return callbacks.string(input, start, pos);
- }
- return pos;
- }
- const cc = input.charCodeAt(pos);
- pos++;
- // ending code point
- // Return the <string-token>.
- if (cc === endingCodePoint) {
- if (callbacks.string !== undefined) {
- return callbacks.string(input, start, pos);
- }
- return pos;
- }
- // newline
- // This is a parse error.
- // Reconsume the current input code point, create a <bad-string-token>, and return it.
- else if (_isNewline(cc)) {
- pos--;
- if (callbacks.badStringToken !== undefined) {
- return callbacks.badStringToken(input, start, pos);
- }
- // bad string
- return pos;
- }
- // U+005C REVERSE SOLIDUS (\)
- else if (cc === CC_REVERSE_SOLIDUS) {
- // If the next input code point is EOF, do nothing.
- if (pos === input.length) {
- return pos;
- }
- // Otherwise, if the next input code point is a newline, consume it.
- else if (_isNewline(input.charCodeAt(pos))) {
- const cc = input.charCodeAt(pos);
- pos++;
- pos = consumeExtraNewline(cc, input, pos);
- }
- // Otherwise, (the stream starts with a valid escape) consume an escaped code point and append the returned code point to the <string-token>’s value.
- else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
- pos = _consumeAnEscapedCodePoint(input, pos);
- }
- }
- // anything else
- // Append the current input code point to the <string-token>’s value.
- else {
- // Append
- }
- }
- };
- /**
- * Checks whether this object is non ascii code point.
- * @param {number} cc char code
- * @param {number} q char code
- * @returns {boolean} is non-ASCII code point
- */
- const isNonASCIICodePoint = (cc, q) =>
- // Simplify
- cc > 0x80;
- /**
- * Checks whether this object is letter.
- * @param {number} cc char code
- * @returns {boolean} is letter
- */
- const isLetter = (cc) =>
- (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
- (cc >= CC_UPPER_A && cc <= CC_UPPER_Z);
- /**
- * Is ident start code point.
- * @param {number} cc char code
- * @param {number} q char code
- * @returns {boolean} is identifier start code
- */
- const _isIdentStartCodePoint = (cc, q) =>
- isLetter(cc) || isNonASCIICodePoint(cc, q) || cc === CC_LOW_LINE;
- /**
- * Is ident code point.
- * @param {number} cc char code
- * @param {number} q char code
- * @returns {boolean} is identifier code
- */
- const _isIdentCodePoint = (cc, q) =>
- _isIdentStartCodePoint(cc, q) || _isDigit(cc) || cc === CC_HYPHEN_MINUS;
- /**
- * Checks whether digit is digit.
- * @param {number} cc char code
- * @returns {boolean} is digit
- */
- const _isDigit = (cc) => cc >= CC_0 && cc <= CC_9;
- /**
- * If two code points are valid escape.
- * @param {string} input input
- * @param {number} pos position
- * @param {number=} f first code point
- * @param {number=} s second code point
- * @returns {boolean} true if two code points are a valid escape
- */
- const _ifTwoCodePointsAreValidEscape = (input, pos, f, s) => {
- // This section describes how to check if two code points are a valid escape.
- // The algorithm described here can be called explicitly with two code points, or can be called with the input stream itself.
- // In the latter case, the two code points in question are the current input code point and the next input code point, in that order.
- // Note: This algorithm will not consume any additional code point.
- const first = f || input.charCodeAt(pos - 1);
- const second = s || input.charCodeAt(pos);
- // If the first code point is not U+005C REVERSE SOLIDUS (\), return false.
- if (first !== CC_REVERSE_SOLIDUS) return false;
- // Otherwise, if the second code point is a newline, return false.
- if (_isNewline(second)) return false;
- // Otherwise, return true.
- return true;
- };
- /**
- * If three code points would start an ident sequence.
- * @param {string} input input
- * @param {number} pos position
- * @param {number=} f first
- * @param {number=} s second
- * @param {number=} t third
- * @returns {boolean} true, if input at pos starts an identifier
- */
- const _ifThreeCodePointsWouldStartAnIdentSequence = (input, pos, f, s, t) => {
- // This section describes how to check if three code points would start an ident sequence.
- // The algorithm described here can be called explicitly with three code points, or can be called with the input stream itself.
- // In the latter case, the three code points in question are the current input code point and the next two input code points, in that order.
- // Note: This algorithm will not consume any additional code points.
- const first = f || input.charCodeAt(pos - 1);
- const second = s || input.charCodeAt(pos);
- const third = t || input.charCodeAt(pos + 1);
- // Look at the first code point:
- // U+002D HYPHEN-MINUS
- if (first === CC_HYPHEN_MINUS) {
- // If the second code point is an ident-start code point or a U+002D HYPHEN-MINUS
- // or a U+002D HYPHEN-MINUS, or the second and third code points are a valid escape, return true.
- if (
- _isIdentStartCodePoint(second, pos) ||
- second === CC_HYPHEN_MINUS ||
- _ifTwoCodePointsAreValidEscape(input, pos, second, third)
- ) {
- return true;
- }
- return false;
- }
- // ident-start code point
- else if (_isIdentStartCodePoint(first, pos - 1)) {
- return true;
- }
- // U+005C REVERSE SOLIDUS (\)
- // If the first and second code points are a valid escape, return true. Otherwise, return false.
- else if (first === CC_REVERSE_SOLIDUS) {
- if (_ifTwoCodePointsAreValidEscape(input, pos, first, second)) {
- return true;
- }
- return false;
- }
- // anything else
- // Return false.
- return false;
- };
- /**
- * If three code points would start a number.
- * @param {string} input input
- * @param {number} pos position
- * @param {number=} f first
- * @param {number=} s second
- * @param {number=} t third
- * @returns {boolean} true, if input at pos starts an identifier
- */
- const _ifThreeCodePointsWouldStartANumber = (input, pos, f, s, t) => {
- // This section describes how to check if three code points would start a number.
- // The algorithm described here can be called explicitly with three code points, or can be called with the input stream itself.
- // In the latter case, the three code points in question are the current input code point and the next two input code points, in that order.
- // Note: This algorithm will not consume any additional code points.
- const first = f || input.charCodeAt(pos - 1);
- const second = s || input.charCodeAt(pos);
- const third = t || input.charCodeAt(pos + 1);
- // Look at the first code point:
- // U+002B PLUS SIGN (+)
- // U+002D HYPHEN-MINUS (-)
- //
- // If the second code point is a digit, return true.
- // Otherwise, if the second code point is a U+002E FULL STOP (.) and the third code point is a digit, return true.
- // Otherwise, return false.
- if (first === CC_PLUS_SIGN || first === CC_HYPHEN_MINUS) {
- if (_isDigit(second)) {
- return true;
- } else if (second === CC_FULL_STOP && _isDigit(third)) {
- return true;
- }
- return false;
- }
- // U+002E FULL STOP (.)
- // If the second code point is a digit, return true. Otherwise, return false.
- else if (first === CC_FULL_STOP) {
- if (_isDigit(second)) {
- return true;
- }
- return false;
- }
- // digit
- // Return true.
- else if (_isDigit(first)) {
- return true;
- }
- // anything else
- // Return false.
- return false;
- };
- /** @type {CharHandler} */
- const consumeNumberSign = (input, pos, callbacks) => {
- // If the next input code point is an ident code point or the next two input code points are a valid escape, then:
- // - Create a <hash-token>.
- // - If the next 3 input code points would start an ident sequence, set the <hash-token>’s type flag to "id".
- // - Consume an ident sequence, and set the <hash-token>’s value to the returned string.
- // - Return the <hash-token>.
- const start = pos - 1;
- const first = input.charCodeAt(pos);
- const second = input.charCodeAt(pos + 1);
- if (
- _isIdentCodePoint(first, pos - 1) ||
- _ifTwoCodePointsAreValidEscape(input, pos, first, second)
- ) {
- const third = input.charCodeAt(pos + 2);
- let isId = false;
- if (
- _ifThreeCodePointsWouldStartAnIdentSequence(
- input,
- pos,
- first,
- second,
- third
- )
- ) {
- isId = true;
- }
- pos = _consumeAnIdentSequence(input, pos, callbacks);
- if (callbacks.hash !== undefined) {
- return callbacks.hash(input, start, pos, isId);
- }
- return pos;
- }
- if (callbacks.delim !== undefined) {
- return callbacks.delim(input, start, pos);
- }
- // Otherwise, return a <delim-token> with its value set to the current input code point.
- return pos;
- };
- /** @type {CharHandler} */
- const consumeHyphenMinus = (input, pos, callbacks) => {
- // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
- if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
- pos--;
- return consumeANumericToken(input, pos, callbacks);
- }
- // Otherwise, if the next 2 input code points are U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->), consume them and return a <CDC-token>.
- else if (
- input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
- input.charCodeAt(pos + 1) === CC_GREATER_THAN_SIGN
- ) {
- if (callbacks.cdc !== undefined) {
- return callbacks.cdc(input, pos - 1, pos + 2);
- }
- return pos + 2;
- }
- // Otherwise, if the input stream starts with an ident sequence, reconsume the current input code point, consume an ident-like token, and return it.
- else if (_ifThreeCodePointsWouldStartAnIdentSequence(input, pos)) {
- pos--;
- return consumeAnIdentLikeToken(input, pos, callbacks);
- }
- if (callbacks.delim !== undefined) {
- return callbacks.delim(input, pos - 1, pos);
- }
- // Otherwise, return a <delim-token> with its value set to the current input code point.
- return pos;
- };
- /** @type {CharHandler} */
- const consumeFullStop = (input, pos, callbacks) => {
- const start = pos - 1;
- // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
- if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
- pos--;
- return consumeANumericToken(input, pos, callbacks);
- }
- // Otherwise, return a <delim-token> with its value set to the current input code point.
- if (callbacks.delim !== undefined) {
- return callbacks.delim(input, start, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumePlusSign = (input, pos, callbacks) => {
- const start = pos - 1;
- // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
- if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
- pos--;
- return consumeANumericToken(input, pos, callbacks);
- }
- // Otherwise, return a <delim-token> with its value set to the current input code point.
- if (callbacks.delim !== undefined) {
- return callbacks.delim(input, start, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const _consumeANumber = (input, pos) => {
- // This section describes how to consume a number from a stream of code points.
- // It returns a numeric value, and a type which is either "integer" or "number".
- // Execute the following steps in order:
- // Initially set type to "integer". Let repr be the empty string.
- // If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), consume it and append it to repr.
- if (
- input.charCodeAt(pos) === CC_HYPHEN_MINUS ||
- input.charCodeAt(pos) === CC_PLUS_SIGN
- ) {
- pos++;
- }
- // While the next input code point is a digit, consume it and append it to repr.
- while (_isDigit(input.charCodeAt(pos))) {
- pos++;
- }
- // If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then:
- // 1. Consume the next input code point and append it to number part.
- // 2. While the next input code point is a digit, consume it and append it to number part.
- // 3. Set type to "number".
- if (
- input.charCodeAt(pos) === CC_FULL_STOP &&
- _isDigit(input.charCodeAt(pos + 1))
- ) {
- pos++;
- while (_isDigit(input.charCodeAt(pos))) {
- pos++;
- }
- }
- // If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e), optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+), followed by a digit, then:
- // 1. Consume the next input code point.
- // 2. If the next input code point is "+" or "-", consume it and append it to exponent part.
- // 3. While the next input code point is a digit, consume it and append it to exponent part.
- // 4. Set type to "number".
- if (
- (input.charCodeAt(pos) === CC_LOWER_E ||
- input.charCodeAt(pos) === CC_UPPER_E) &&
- (((input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS ||
- input.charCodeAt(pos + 1) === CC_PLUS_SIGN) &&
- _isDigit(input.charCodeAt(pos + 2))) ||
- _isDigit(input.charCodeAt(pos + 1)))
- ) {
- pos++;
- if (
- input.charCodeAt(pos) === CC_PLUS_SIGN ||
- input.charCodeAt(pos) === CC_HYPHEN_MINUS
- ) {
- pos++;
- }
- while (_isDigit(input.charCodeAt(pos))) {
- pos++;
- }
- }
- // Let value be the result of interpreting number part as a base-10 number.
- // If exponent part is non-empty, interpret it as a base-10 integer, then raise 10 to the power of the result, multiply it by value, and set value to that result.
- // Return value and type.
- return pos;
- };
- /** @type {CharHandler} */
- const consumeANumericToken = (input, pos, callbacks) => {
- // This section describes how to consume a numeric token from a stream of code points.
- // It returns either a <number-token>, <percentage-token>, or <dimension-token>.
- const start = pos;
- // Consume a number and let number be the result.
- pos = _consumeANumber(input, pos, callbacks);
- // If the next 3 input code points would start an ident sequence, then:
- //
- // - Create a <dimension-token> with the same value and type flag as number, and a unit set initially to the empty string.
- // - Consume an ident sequence. Set the <dimension-token>’s unit to the returned value.
- // - Return the <dimension-token>.
- const first = input.charCodeAt(pos);
- const second = input.charCodeAt(pos + 1);
- const third = input.charCodeAt(pos + 2);
- if (
- _ifThreeCodePointsWouldStartAnIdentSequence(
- input,
- pos,
- first,
- second,
- third
- )
- ) {
- pos = _consumeAnIdentSequence(input, pos, callbacks);
- if (callbacks.dimension !== undefined) {
- return callbacks.dimension(input, start, pos);
- }
- return pos;
- }
- // Otherwise, if the next input code point is U+0025 PERCENTAGE SIGN (%), consume it.
- // Create a <percentage-token> with the same value as number, and return it.
- else if (first === CC_PERCENTAGE) {
- if (callbacks.percentage !== undefined) {
- return callbacks.percentage(input, start, pos + 1);
- }
- return pos + 1;
- }
- // Otherwise, create a <number-token> with the same value and type flag as number, and return it.
- if (callbacks.number !== undefined) {
- return callbacks.number(input, start, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeColon = (input, pos, callbacks) => {
- // Return a <colon-token>.
- if (callbacks.colon !== undefined) {
- return callbacks.colon(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeLeftParenthesis = (input, pos, callbacks) => {
- // Return a <(-token>.
- if (callbacks.leftParenthesis !== undefined) {
- return callbacks.leftParenthesis(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeRightParenthesis = (input, pos, callbacks) => {
- // Return a <)-token>.
- if (callbacks.rightParenthesis !== undefined) {
- return callbacks.rightParenthesis(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeLeftSquareBracket = (input, pos, callbacks) => {
- // Return a <]-token>.
- if (callbacks.leftSquareBracket !== undefined) {
- return callbacks.leftSquareBracket(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeRightSquareBracket = (input, pos, callbacks) => {
- // Return a <]-token>.
- if (callbacks.rightSquareBracket !== undefined) {
- return callbacks.rightSquareBracket(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeLeftCurlyBracket = (input, pos, callbacks) => {
- // Return a <{-token>.
- if (callbacks.leftCurlyBracket !== undefined) {
- return callbacks.leftCurlyBracket(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeRightCurlyBracket = (input, pos, callbacks) => {
- // Return a <}-token>.
- if (callbacks.rightCurlyBracket !== undefined) {
- return callbacks.rightCurlyBracket(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeSemicolon = (input, pos, callbacks) => {
- // Return a <semicolon-token>.
- if (callbacks.semicolon !== undefined) {
- return callbacks.semicolon(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeComma = (input, pos, callbacks) => {
- // Return a <comma-token>.
- if (callbacks.comma !== undefined) {
- return callbacks.comma(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const _consumeAnIdentSequence = (input, pos) => {
- // This section describes how to consume an ident sequence from a stream of code points.
- // It returns a string containing the largest name that can be formed from adjacent code points in the stream, starting from the first.
- // Note: This algorithm does not do the verification of the first few code points that are necessary to ensure the returned code points would constitute an <ident-token>.
- // If that is the intended use, ensure that the stream starts with an ident sequence before calling this algorithm.
- // Let result initially be an empty string.
- // Repeatedly consume the next input code point from the stream:
- for (;;) {
- const cc = input.charCodeAt(pos);
- pos++;
- // ident code point
- // Append the code point to result.
- if (_isIdentCodePoint(cc, pos - 1)) {
- // Nothing
- }
- // the stream starts with a valid escape
- // Consume an escaped code point. Append the returned code point to result.
- else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
- pos = _consumeAnEscapedCodePoint(input, pos);
- }
- // anything else
- // Reconsume the current input code point. Return result.
- else {
- return pos - 1;
- }
- }
- };
- /**
- * Is non printable code point.
- * @param {number} cc char code
- * @returns {boolean} true, when cc is the non-printable code point, otherwise false
- */
- const _isNonPrintableCodePoint = (cc) =>
- (cc >= 0x00 && cc <= 0x08) ||
- cc === 0x0b ||
- (cc >= 0x0e && cc <= 0x1f) ||
- cc === 0x7f;
- /**
- * Consume the remnants of a bad url.
- * @param {string} input input
- * @param {number} pos position
- * @returns {number} position
- */
- const consumeTheRemnantsOfABadUrl = (input, pos) => {
- // This section describes how to consume the remnants of a bad url from a stream of code points,
- // "cleaning up" after the tokenizer realizes that it’s in the middle of a <bad-url-token> rather than a <url-token>.
- // It returns nothing; its sole use is to consume enough of the input stream to reach a recovery point where normal tokenizing can resume.
- // Repeatedly consume the next input code point from the stream:
- for (;;) {
- // EOF
- // Return.
- if (pos === input.length) {
- return pos;
- }
- const cc = input.charCodeAt(pos);
- pos++;
- // U+0029 RIGHT PARENTHESIS ())
- // Return.
- if (cc === CC_RIGHT_PARENTHESIS) {
- return pos;
- }
- // the input stream starts with a valid escape
- // Consume an escaped code point.
- // This allows an escaped right parenthesis ("\)") to be encountered without ending the <bad-url-token>.
- // This is otherwise identical to the "anything else" clause.
- else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
- pos = _consumeAnEscapedCodePoint(input, pos);
- }
- // anything else
- // Do nothing.
- else {
- // Do nothing.
- }
- }
- };
- /**
- * Consume a url token.
- * @param {string} input input
- * @param {number} pos position
- * @param {number} fnStart start
- * @param {CssTokenCallbacks} callbacks callbacks
- * @returns {pos} pos
- */
- const consumeAUrlToken = (input, pos, fnStart, callbacks) => {
- // This section describes how to consume a url token from a stream of code points.
- // It returns either a <url-token> or a <bad-url-token>.
- // Note: This algorithm assumes that the initial "url(" has already been consumed.
- // This algorithm also assumes that it’s being called to consume an "unquoted" value, like url(foo).
- // A quoted value, like url("foo"), is parsed as a <function-token>.
- // Consume an ident-like token automatically handles this distinction; this algorithm shouldn’t be called directly otherwise.
- // Initially create a <url-token> with its value set to the empty string.
- // Consume as much whitespace as possible.
- while (_isWhiteSpace(input.charCodeAt(pos))) {
- pos++;
- }
- const contentStart = pos;
- // Repeatedly consume the next input code point from the stream:
- for (;;) {
- // EOF
- // This is a parse error. Return the <url-token>.
- if (pos === input.length) {
- if (callbacks.url !== undefined) {
- return callbacks.url(input, fnStart, pos, contentStart, pos - 1);
- }
- return pos;
- }
- const cc = input.charCodeAt(pos);
- pos++;
- // U+0029 RIGHT PARENTHESIS ())
- // Return the <url-token>.
- if (cc === CC_RIGHT_PARENTHESIS) {
- if (callbacks.url !== undefined) {
- return callbacks.url(input, fnStart, pos, contentStart, pos - 1);
- }
- return pos;
- }
- // whitespace
- // Consume as much whitespace as possible.
- // If the next input code point is U+0029 RIGHT PARENTHESIS ()) or EOF, consume it and return the <url-token>
- // (if EOF was encountered, this is a parse error); otherwise, consume the remnants of a bad url, create a <bad-url-token>, and return it.
- else if (_isWhiteSpace(cc)) {
- const end = pos - 1;
- while (_isWhiteSpace(input.charCodeAt(pos))) {
- pos++;
- }
- if (pos === input.length) {
- if (callbacks.url !== undefined) {
- return callbacks.url(input, fnStart, pos, contentStart, end);
- }
- return pos;
- }
- if (input.charCodeAt(pos) === CC_RIGHT_PARENTHESIS) {
- pos++;
- if (callbacks.url !== undefined) {
- return callbacks.url(input, fnStart, pos, contentStart, end);
- }
- return pos;
- }
- // Don't handle bad urls
- pos = consumeTheRemnantsOfABadUrl(input, pos);
- if (callbacks.badUrlToken !== undefined) {
- return callbacks.badUrlToken(input, fnStart, pos);
- }
- return pos;
- }
- // U+0022 QUOTATION MARK (")
- // U+0027 APOSTROPHE (')
- // U+0028 LEFT PARENTHESIS (()
- // non-printable code point
- // This is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
- else if (
- cc === CC_QUOTATION_MARK ||
- cc === CC_APOSTROPHE ||
- cc === CC_LEFT_PARENTHESIS ||
- _isNonPrintableCodePoint(cc)
- ) {
- // Don't handle bad urls
- pos = consumeTheRemnantsOfABadUrl(input, pos);
- if (callbacks.badUrlToken !== undefined) {
- return callbacks.badUrlToken(input, fnStart, pos);
- }
- return pos;
- }
- // // U+005C REVERSE SOLIDUS (\)
- // // If the stream starts with a valid escape, consume an escaped code point and append the returned code point to the <url-token>’s value.
- // // Otherwise, this is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
- else if (cc === CC_REVERSE_SOLIDUS) {
- if (_ifTwoCodePointsAreValidEscape(input, pos)) {
- pos = _consumeAnEscapedCodePoint(input, pos);
- } else {
- // Don't handle bad urls
- pos = consumeTheRemnantsOfABadUrl(input, pos);
- if (callbacks.badUrlToken !== undefined) {
- return callbacks.badUrlToken(input, fnStart, pos);
- }
- return pos;
- }
- }
- // anything else
- // Append the current input code point to the <url-token>’s value.
- else {
- // Nothing
- }
- }
- };
- /** @type {CharHandler} */
- const consumeAnIdentLikeToken = (input, pos, callbacks) => {
- const start = pos;
- // This section describes how to consume an ident-like token from a stream of code points.
- // It returns an <ident-token>, <function-token>, <url-token>, or <bad-url-token>.
- pos = _consumeAnIdentSequence(input, pos, callbacks);
- // If string’s value is an ASCII case-insensitive match for "url", and the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
- // While the next two input code points are whitespace, consume the next input code point.
- // If the next one or two input code points are U+0022 QUOTATION MARK ("), U+0027 APOSTROPHE ('), or whitespace followed by U+0022 QUOTATION MARK (") or U+0027 APOSTROPHE ('), then create a <function-token> with its value set to string and return it.
- // Otherwise, consume a url token, and return it.
- if (
- input.slice(start, pos).toLowerCase() === "url" &&
- input.charCodeAt(pos) === CC_LEFT_PARENTHESIS
- ) {
- pos++;
- const end = pos;
- while (
- _isWhiteSpace(input.charCodeAt(pos)) &&
- _isWhiteSpace(input.charCodeAt(pos + 1))
- ) {
- pos++;
- }
- if (
- input.charCodeAt(pos) === CC_QUOTATION_MARK ||
- input.charCodeAt(pos) === CC_APOSTROPHE ||
- (_isWhiteSpace(input.charCodeAt(pos)) &&
- (input.charCodeAt(pos + 1) === CC_QUOTATION_MARK ||
- input.charCodeAt(pos + 1) === CC_APOSTROPHE))
- ) {
- if (callbacks.function !== undefined) {
- return callbacks.function(input, start, end);
- }
- return pos;
- }
- return consumeAUrlToken(input, pos, start, callbacks);
- }
- // Otherwise, if the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
- // Create a <function-token> with its value set to string and return it.
- if (input.charCodeAt(pos) === CC_LEFT_PARENTHESIS) {
- pos++;
- if (callbacks.function !== undefined) {
- return callbacks.function(input, start, pos);
- }
- return pos;
- }
- // Otherwise, create an <ident-token> with its value set to string and return it.
- if (callbacks.identifier !== undefined) {
- return callbacks.identifier(input, start, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeLessThan = (input, pos, callbacks) => {
- // If the next 3 input code points are U+0021 EXCLAMATION MARK U+002D HYPHEN-MINUS U+002D HYPHEN-MINUS (!--), consume them and return a <CDO-token>.
- if (input.slice(pos, pos + 3) === "!--") {
- if (callbacks.cdo !== undefined) {
- return callbacks.cdo(input, pos - 1, pos + 3);
- }
- return pos + 3;
- }
- if (callbacks.delim !== undefined) {
- return callbacks.delim(input, pos - 1, pos);
- }
- // Otherwise, return a <delim-token> with its value set to the current input code point.
- return pos;
- };
- /** @type {CharHandler} */
- const consumeCommercialAt = (input, pos, callbacks) => {
- const start = pos - 1;
- // If the next 3 input code points would start an ident sequence, consume an ident sequence, create an <at-keyword-token> with its value set to the returned value, and return it.
- if (
- _ifThreeCodePointsWouldStartAnIdentSequence(
- input,
- pos,
- input.charCodeAt(pos),
- input.charCodeAt(pos + 1),
- input.charCodeAt(pos + 2)
- )
- ) {
- pos = _consumeAnIdentSequence(input, pos, callbacks);
- if (callbacks.atKeyword !== undefined) {
- pos = callbacks.atKeyword(input, start, pos);
- }
- return pos;
- }
- // Otherwise, return a <delim-token> with its value set to the current input code point.
- if (callbacks.delim !== undefined) {
- return callbacks.delim(input, start, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeReverseSolidus = (input, pos, callbacks) => {
- // If the input stream starts with a valid escape, reconsume the current input code point, consume an ident-like token, and return it.
- if (_ifTwoCodePointsAreValidEscape(input, pos)) {
- pos--;
- return consumeAnIdentLikeToken(input, pos, callbacks);
- }
- // Otherwise, this is a parse error. Return a <delim-token> with its value set to the current input code point.
- if (callbacks.delim !== undefined) {
- return callbacks.delim(input, pos - 1, pos);
- }
- return pos;
- };
- /** @type {CharHandler} */
- const consumeAToken = (input, pos, callbacks) => {
- const cc = input.charCodeAt(pos - 1);
- // https://drafts.csswg.org/css-syntax/#consume-token
- switch (cc) {
- // whitespace
- case CC_LINE_FEED:
- case CC_CARRIAGE_RETURN:
- case CC_FORM_FEED:
- case CC_TAB:
- case CC_SPACE:
- return consumeSpace(input, pos, callbacks);
- // U+0022 QUOTATION MARK (")
- case CC_QUOTATION_MARK:
- return consumeAStringToken(input, pos, callbacks);
- // U+0023 NUMBER SIGN (#)
- case CC_NUMBER_SIGN:
- return consumeNumberSign(input, pos, callbacks);
- // U+0027 APOSTROPHE (')
- case CC_APOSTROPHE:
- return consumeAStringToken(input, pos, callbacks);
- // U+0028 LEFT PARENTHESIS (()
- case CC_LEFT_PARENTHESIS:
- return consumeLeftParenthesis(input, pos, callbacks);
- // U+0029 RIGHT PARENTHESIS ())
- case CC_RIGHT_PARENTHESIS:
- return consumeRightParenthesis(input, pos, callbacks);
- // U+002B PLUS SIGN (+)
- case CC_PLUS_SIGN:
- return consumePlusSign(input, pos, callbacks);
- // U+002C COMMA (,)
- case CC_COMMA:
- return consumeComma(input, pos, callbacks);
- // U+002D HYPHEN-MINUS (-)
- case CC_HYPHEN_MINUS:
- return consumeHyphenMinus(input, pos, callbacks);
- // U+002E FULL STOP (.)
- case CC_FULL_STOP:
- return consumeFullStop(input, pos, callbacks);
- // U+003A COLON (:)
- case CC_COLON:
- return consumeColon(input, pos, callbacks);
- // U+003B SEMICOLON (;)
- case CC_SEMICOLON:
- return consumeSemicolon(input, pos, callbacks);
- // U+003C LESS-THAN SIGN (<)
- case CC_LESS_THAN_SIGN:
- return consumeLessThan(input, pos, callbacks);
- // U+0040 COMMERCIAL AT (@)
- case CC_AT_SIGN:
- return consumeCommercialAt(input, pos, callbacks);
- // U+005B LEFT SQUARE BRACKET ([)
- case CC_LEFT_SQUARE:
- return consumeLeftSquareBracket(input, pos, callbacks);
- // U+005C REVERSE SOLIDUS (\)
- case CC_REVERSE_SOLIDUS:
- return consumeReverseSolidus(input, pos, callbacks);
- // U+005D RIGHT SQUARE BRACKET (])
- case CC_RIGHT_SQUARE:
- return consumeRightSquareBracket(input, pos, callbacks);
- // U+007B LEFT CURLY BRACKET ({)
- case CC_LEFT_CURLY:
- return consumeLeftCurlyBracket(input, pos, callbacks);
- // U+007D RIGHT CURLY BRACKET (})
- case CC_RIGHT_CURLY:
- return consumeRightCurlyBracket(input, pos, callbacks);
- default:
- // digit
- // Reconsume the current input code point, consume a numeric token, and return it.
- if (_isDigit(cc)) {
- pos--;
- return consumeANumericToken(input, pos, callbacks);
- } else if (cc === CC_LOWER_U || cc === CC_UPPER_U) {
- // If unicode ranges allowed is true and the input stream would start a unicode-range,
- // reconsume the current input code point, consume a unicode-range token, and return it.
- // Skip now
- // if (_ifThreeCodePointsWouldStartAUnicodeRange(input, pos)) {
- // pos--;
- // return consumeAUnicodeRangeToken(input, pos, callbacks);
- // }
- // Otherwise, reconsume the current input code point, consume an ident-like token, and return it.
- pos--;
- return consumeAnIdentLikeToken(input, pos, callbacks);
- }
- // ident-start code point
- // Reconsume the current input code point, consume an ident-like token, and return it.
- else if (isIdentStartCodePoint(cc)) {
- pos--;
- return consumeAnIdentLikeToken(input, pos, callbacks);
- }
- // EOF, but we don't have it
- // anything else
- // Return a <delim-token> with its value set to the current input code point.
- return consumeDelimToken(input, pos, callbacks);
- }
- };
- /**
- * Returns pos.
- * @param {string} input input css
- * @param {number=} pos pos
- * @param {CssTokenCallbacks=} callbacks callbacks
- * @returns {number} pos
- */
- module.exports = (input, pos = 0, callbacks = {}) => {
- // This section describes how to consume a token from a stream of code points. It will return a single token of any type.
- while (pos < input.length) {
- // Consume comments.
- pos = consumeComments(input, pos, callbacks);
- // Consume the next input code point.
- pos++;
- pos = consumeAToken(input, pos, callbacks);
- if (callbacks.needTerminate && callbacks.needTerminate()) {
- break;
- }
- }
- return pos;
- };
- /**
- * Returns pos.
- * @param {string} input input css
- * @param {number} pos pos
- * @param {CssTokenCallbacks} callbacks callbacks
- * @param {CssTokenCallbacks=} additional additional callbacks
- * @param {{ onlyTopLevel?: boolean, declarationValue?: boolean, atRulePrelude?: boolean, functionValue?: boolean }=} options options
- * @returns {number} pos
- */
- const consumeUntil = (input, pos, callbacks, additional, options = {}) => {
- let needHandle = true;
- let needTerminate = false;
- /** @type {CssTokenCallbacks} */
- const servicedCallbacks = {};
- let balanced = 0;
- if (options.onlyTopLevel) {
- servicedCallbacks.function = (input, start, end) => {
- balanced++;
- if (!options.functionValue) {
- needHandle = false;
- }
- if (additional && additional.function !== undefined) {
- return additional.function(input, start, end);
- }
- return end;
- };
- servicedCallbacks.leftParenthesis = (_input, _start, end) => {
- balanced++;
- needHandle = false;
- return end;
- };
- servicedCallbacks.rightParenthesis = (_input, _start, end) => {
- balanced--;
- if (balanced === 0) {
- needHandle = true;
- }
- return end;
- };
- }
- if (options.declarationValue) {
- servicedCallbacks.semicolon = (_input, _start, end) => {
- needTerminate = true;
- return end;
- };
- servicedCallbacks.rightCurlyBracket = (_input, _start, end) => {
- needTerminate = true;
- return end;
- };
- } else if (options.functionValue) {
- servicedCallbacks.rightParenthesis = (_input, _start, end) => {
- balanced--;
- if (balanced === 0) {
- needTerminate = true;
- }
- return end;
- };
- } else if (options.atRulePrelude) {
- servicedCallbacks.leftCurlyBracket = (_input, _start, end) => {
- needTerminate = true;
- return end;
- };
- servicedCallbacks.semicolon = (_input, _start, end) => {
- needTerminate = true;
- return end;
- };
- }
- const mergedCallbacks = { ...servicedCallbacks, ...callbacks };
- while (pos < input.length) {
- // Consume comments.
- pos = consumeComments(
- input,
- pos,
- needHandle ? mergedCallbacks : servicedCallbacks
- );
- const start = pos;
- // Consume the next input code point.
- pos++;
- pos = consumeAToken(
- input,
- pos,
- needHandle ? mergedCallbacks : servicedCallbacks
- );
- if (needTerminate) {
- return start;
- }
- }
- return pos;
- };
- /**
- * Returns position after comments.
- * @param {string} input input
- * @param {number} pos position
- * @returns {number} position after comments
- */
- const eatComments = (input, pos) => {
- for (;;) {
- const originalPos = pos;
- pos = consumeComments(input, pos, {});
- if (originalPos === pos) {
- break;
- }
- }
- return pos;
- };
- /**
- * Returns position after whitespace.
- * @param {string} input input
- * @param {number} pos position
- * @returns {number} position after whitespace
- */
- const eatWhitespace = (input, pos) => {
- while (_isWhiteSpace(input.charCodeAt(pos))) {
- pos++;
- }
- return pos;
- };
- /**
- * Eat whitespace and comments.
- * @param {string} input input
- * @param {number} pos position
- * @returns {[number, boolean]} position after whitespace and comments
- */
- const eatWhitespaceAndComments = (input, pos) => {
- let foundWhitespace = false;
- for (;;) {
- const originalPos = pos;
- pos = consumeComments(input, pos, {});
- while (_isWhiteSpace(input.charCodeAt(pos))) {
- if (!foundWhitespace) {
- foundWhitespace = true;
- }
- pos++;
- }
- if (originalPos === pos) {
- break;
- }
- }
- return [pos, foundWhitespace];
- };
- /**
- * Returns position after whitespace.
- * @param {string} input input
- * @param {number} pos position
- * @returns {number} position after whitespace
- */
- const eatWhiteLine = (input, pos) => {
- for (;;) {
- const cc = input.charCodeAt(pos);
- if (_isSpace(cc)) {
- pos++;
- continue;
- }
- if (_isNewline(cc)) pos++;
- pos = consumeExtraNewline(cc, input, pos);
- break;
- }
- return pos;
- };
- /**
- * Skip comments and eat ident sequence.
- * @param {string} input input
- * @param {number} pos position
- * @returns {[number, number] | undefined} positions of ident sequence
- */
- const skipCommentsAndEatIdentSequence = (input, pos) => {
- pos = eatComments(input, pos);
- const start = pos;
- if (
- _ifThreeCodePointsWouldStartAnIdentSequence(
- input,
- pos,
- input.charCodeAt(pos),
- input.charCodeAt(pos + 1),
- input.charCodeAt(pos + 2)
- )
- ) {
- return [start, _consumeAnIdentSequence(input, pos, {})];
- }
- return undefined;
- };
- /**
- * Returns positions of ident sequence.
- * @param {string} input input
- * @param {number} pos position
- * @returns {[number, number] | undefined} positions of ident sequence
- */
- const eatString = (input, pos) => {
- pos = eatWhitespaceAndComments(input, pos)[0];
- const start = pos;
- if (
- input.charCodeAt(pos) === CC_QUOTATION_MARK ||
- input.charCodeAt(pos) === CC_APOSTROPHE
- ) {
- return [start, consumeAStringToken(input, pos + 1, {})];
- }
- return undefined;
- };
- /**
- * Eat image set strings.
- * @param {string} input input
- * @param {number} pos position
- * @param {CssTokenCallbacks} cbs callbacks
- * @returns {[number, number][]} positions of ident sequence
- */
- const eatImageSetStrings = (input, pos, cbs) => {
- /** @type {[number, number][]} */
- const result = [];
- let isFirst = true;
- let needStop = false;
- // We already in `func(` token
- let balanced = 1;
- /** @type {CssTokenCallbacks} */
- const callbacks = {
- ...cbs,
- string: (_input, start, end) => {
- if (isFirst && balanced === 1) {
- result.push([start, end]);
- isFirst = false;
- }
- return end;
- },
- comma: (_input, _start, end) => {
- if (balanced === 1) {
- isFirst = true;
- }
- return end;
- },
- leftParenthesis: (input, start, end) => {
- balanced++;
- return end;
- },
- function: (_input, start, end) => {
- balanced++;
- return end;
- },
- rightParenthesis: (_input, _start, end) => {
- balanced--;
- if (balanced === 0) {
- needStop = true;
- }
- return end;
- }
- };
- while (pos < input.length) {
- // Consume comments.
- pos = consumeComments(input, pos, callbacks);
- // Consume the next input code point.
- pos++;
- pos = consumeAToken(input, pos, callbacks);
- if (needStop) {
- break;
- }
- }
- return result;
- };
- /**
- * Returns positions of top level tokens.
- * @param {string} input input
- * @param {number} pos position
- * @param {CssTokenCallbacks} cbs callbacks
- * @returns {[[number, number, number, number] | undefined, [number, number] | undefined, [number, number] | undefined, [number, number] | undefined]} positions of top level tokens
- */
- const eatImportTokens = (input, pos, cbs) => {
- const result =
- /** @type {[[number, number, number, number] | undefined, [number, number] | undefined, [number, number] | undefined, [number, number] | undefined]} */
- (Array.from({ length: 4 }));
- /** @type {0 | 1 | 2 | undefined} */
- let scope;
- let needStop = false;
- let balanced = 0;
- /** @type {CssTokenCallbacks} */
- const callbacks = {
- ...cbs,
- url: (_input, start, end, contentStart, contentEnd) => {
- if (
- result[0] === undefined &&
- balanced === 0 &&
- result[1] === undefined &&
- result[2] === undefined &&
- result[3] === undefined
- ) {
- result[0] = [start, end, contentStart, contentEnd];
- scope = undefined;
- }
- return end;
- },
- string: (_input, start, end) => {
- if (
- balanced === 0 &&
- result[0] === undefined &&
- result[1] === undefined &&
- result[2] === undefined &&
- result[3] === undefined
- ) {
- result[0] = [start, end, start + 1, end - 1];
- scope = undefined;
- } else if (result[0] !== undefined && scope === 0) {
- result[0][2] = start + 1;
- result[0][3] = end - 1;
- }
- return end;
- },
- leftParenthesis: (_input, _start, end) => {
- balanced++;
- return end;
- },
- rightParenthesis: (_input, _start, end) => {
- balanced--;
- if (balanced === 0 && scope !== undefined) {
- /** @type {[number, number]} */
- (result[scope])[1] = end;
- scope = undefined;
- }
- return end;
- },
- function: (input, start, end) => {
- if (balanced === 0) {
- const name = input
- .slice(start, end - 1)
- .replace(/\\/g, "")
- .toLowerCase();
- if (
- name === "url" &&
- result[0] === undefined &&
- result[1] === undefined &&
- result[2] === undefined &&
- result[3] === undefined
- ) {
- scope = 0;
- result[scope] = [start, end + 1, end + 1, end + 1];
- } else if (
- name === "layer" &&
- result[1] === undefined &&
- result[2] === undefined
- ) {
- scope = 1;
- result[scope] = [start, end];
- } else if (name === "supports" && result[2] === undefined) {
- scope = 2;
- result[scope] = [start, end];
- } else {
- scope = undefined;
- }
- }
- balanced++;
- return end;
- },
- identifier: (input, start, end) => {
- if (
- balanced === 0 &&
- result[1] === undefined &&
- result[2] === undefined
- ) {
- const name = input.slice(start, end).replace(/\\/g, "").toLowerCase();
- if (name === "layer") {
- result[1] = [start, end];
- scope = undefined;
- }
- }
- return end;
- },
- semicolon: (_input, start, end) => {
- if (balanced === 0) {
- needStop = true;
- result[3] = [start, end];
- }
- return end;
- }
- };
- while (pos < input.length) {
- // Consume comments.
- pos = consumeComments(input, pos, callbacks);
- // Consume the next input code point.
- pos++;
- pos = consumeAToken(input, pos, callbacks);
- if (needStop) {
- break;
- }
- }
- return result;
- };
- /**
- * Eat ident sequence.
- * @param {string} input input
- * @param {number} pos position
- * @returns {[number, number] | undefined} positions of ident sequence
- */
- const eatIdentSequence = (input, pos) => {
- pos = eatWhitespaceAndComments(input, pos)[0];
- const start = pos;
- if (
- _ifThreeCodePointsWouldStartAnIdentSequence(
- input,
- pos,
- input.charCodeAt(pos),
- input.charCodeAt(pos + 1),
- input.charCodeAt(pos + 2)
- )
- ) {
- return [start, _consumeAnIdentSequence(input, pos, {})];
- }
- return undefined;
- };
- /**
- * Eat ident sequence or string.
- * @param {string} input input
- * @param {number} pos position
- * @returns {[number, number, boolean] | undefined} positions of ident sequence or string
- */
- const eatIdentSequenceOrString = (input, pos) => {
- pos = eatWhitespaceAndComments(input, pos)[0];
- const start = pos;
- if (
- input.charCodeAt(pos) === CC_QUOTATION_MARK ||
- input.charCodeAt(pos) === CC_APOSTROPHE
- ) {
- return [start, consumeAStringToken(input, pos + 1, {}), false];
- } else if (
- _ifThreeCodePointsWouldStartAnIdentSequence(
- input,
- pos,
- input.charCodeAt(pos),
- input.charCodeAt(pos + 1),
- input.charCodeAt(pos + 2)
- )
- ) {
- return [start, _consumeAnIdentSequence(input, pos, {}), true];
- }
- return undefined;
- };
- /**
- * Returns function to eat characters.
- * @param {string} chars characters
- * @returns {(input: string, pos: number) => number} function to eat characters
- */
- const eatUntil = (chars) => {
- const charCodes = Array.from({ length: chars.length }, (_, i) =>
- chars.charCodeAt(i)
- );
- const arr = Array.from(
- { length: Math.max(...charCodes, 0) + 1 },
- () => false
- );
- for (const cc of charCodes) {
- arr[cc] = true;
- }
- return (input, pos) => {
- for (;;) {
- const cc = input.charCodeAt(pos);
- if (cc < arr.length && arr[cc]) {
- return pos;
- }
- pos++;
- if (pos === input.length) return pos;
- }
- };
- };
- module.exports.consumeUntil = consumeUntil;
- module.exports.eatComments = eatComments;
- module.exports.eatIdentSequence = eatIdentSequence;
- module.exports.eatIdentSequenceOrString = eatIdentSequenceOrString;
- module.exports.eatImageSetStrings = eatImageSetStrings;
- module.exports.eatImportTokens = eatImportTokens;
- module.exports.eatString = eatString;
- module.exports.eatUntil = eatUntil;
- module.exports.eatWhiteLine = eatWhiteLine;
- module.exports.eatWhitespace = eatWhitespace;
- module.exports.eatWhitespaceAndComments = eatWhitespaceAndComments;
- module.exports.isIdentStartCodePoint = isIdentStartCodePoint;
- module.exports.isWhiteSpace = _isWhiteSpace;
- module.exports.skipCommentsAndEatIdentSequence =
- skipCommentsAndEatIdentSequence;
|