UNPKG

16.4 kBJavaScriptView Raw
1const MODES = (hljs) => {
2 return {
3 IMPORTANT: {
4 scope: 'meta',
5 begin: '!important'
6 },
7 BLOCK_COMMENT: hljs.C_BLOCK_COMMENT_MODE,
8 HEXCOLOR: {
9 scope: 'number',
10 begin: /#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/
11 },
12 FUNCTION_DISPATCH: {
13 className: "built_in",
14 begin: /[\w-]+(?=\()/
15 },
16 ATTRIBUTE_SELECTOR_MODE: {
17 scope: 'selector-attr',
18 begin: /\[/,
19 end: /\]/,
20 illegal: '$',
21 contains: [
22 hljs.APOS_STRING_MODE,
23 hljs.QUOTE_STRING_MODE
24 ]
25 },
26 CSS_NUMBER_MODE: {
27 scope: 'number',
28 begin: hljs.NUMBER_RE + '(' +
29 '%|em|ex|ch|rem' +
30 '|vw|vh|vmin|vmax' +
31 '|cm|mm|in|pt|pc|px' +
32 '|deg|grad|rad|turn' +
33 '|s|ms' +
34 '|Hz|kHz' +
35 '|dpi|dpcm|dppx' +
36 ')?',
37 relevance: 0
38 },
39 CSS_VARIABLE: {
40 className: "attr",
41 begin: /--[A-Za-z][A-Za-z0-9_-]*/
42 }
43 };
44};
45
46const TAGS = [
47 'a',
48 'abbr',
49 'address',
50 'article',
51 'aside',
52 'audio',
53 'b',
54 'blockquote',
55 'body',
56 'button',
57 'canvas',
58 'caption',
59 'cite',
60 'code',
61 'dd',
62 'del',
63 'details',
64 'dfn',
65 'div',
66 'dl',
67 'dt',
68 'em',
69 'fieldset',
70 'figcaption',
71 'figure',
72 'footer',
73 'form',
74 'h1',
75 'h2',
76 'h3',
77 'h4',
78 'h5',
79 'h6',
80 'header',
81 'hgroup',
82 'html',
83 'i',
84 'iframe',
85 'img',
86 'input',
87 'ins',
88 'kbd',
89 'label',
90 'legend',
91 'li',
92 'main',
93 'mark',
94 'menu',
95 'nav',
96 'object',
97 'ol',
98 'p',
99 'q',
100 'quote',
101 'samp',
102 'section',
103 'span',
104 'strong',
105 'summary',
106 'sup',
107 'table',
108 'tbody',
109 'td',
110 'textarea',
111 'tfoot',
112 'th',
113 'thead',
114 'time',
115 'tr',
116 'ul',
117 'var',
118 'video'
119];
120
121const MEDIA_FEATURES = [
122 'any-hover',
123 'any-pointer',
124 'aspect-ratio',
125 'color',
126 'color-gamut',
127 'color-index',
128 'device-aspect-ratio',
129 'device-height',
130 'device-width',
131 'display-mode',
132 'forced-colors',
133 'grid',
134 'height',
135 'hover',
136 'inverted-colors',
137 'monochrome',
138 'orientation',
139 'overflow-block',
140 'overflow-inline',
141 'pointer',
142 'prefers-color-scheme',
143 'prefers-contrast',
144 'prefers-reduced-motion',
145 'prefers-reduced-transparency',
146 'resolution',
147 'scan',
148 'scripting',
149 'update',
150 'width',
151 // TODO: find a better solution?
152 'min-width',
153 'max-width',
154 'min-height',
155 'max-height'
156];
157
158// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
159const PSEUDO_CLASSES = [
160 'active',
161 'any-link',
162 'blank',
163 'checked',
164 'current',
165 'default',
166 'defined',
167 'dir', // dir()
168 'disabled',
169 'drop',
170 'empty',
171 'enabled',
172 'first',
173 'first-child',
174 'first-of-type',
175 'fullscreen',
176 'future',
177 'focus',
178 'focus-visible',
179 'focus-within',
180 'has', // has()
181 'host', // host or host()
182 'host-context', // host-context()
183 'hover',
184 'indeterminate',
185 'in-range',
186 'invalid',
187 'is', // is()
188 'lang', // lang()
189 'last-child',
190 'last-of-type',
191 'left',
192 'link',
193 'local-link',
194 'not', // not()
195 'nth-child', // nth-child()
196 'nth-col', // nth-col()
197 'nth-last-child', // nth-last-child()
198 'nth-last-col', // nth-last-col()
199 'nth-last-of-type', //nth-last-of-type()
200 'nth-of-type', //nth-of-type()
201 'only-child',
202 'only-of-type',
203 'optional',
204 'out-of-range',
205 'past',
206 'placeholder-shown',
207 'read-only',
208 'read-write',
209 'required',
210 'right',
211 'root',
212 'scope',
213 'target',
214 'target-within',
215 'user-invalid',
216 'valid',
217 'visited',
218 'where' // where()
219];
220
221// https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
222const PSEUDO_ELEMENTS = [
223 'after',
224 'backdrop',
225 'before',
226 'cue',
227 'cue-region',
228 'first-letter',
229 'first-line',
230 'grammar-error',
231 'marker',
232 'part',
233 'placeholder',
234 'selection',
235 'slotted',
236 'spelling-error'
237];
238
239const ATTRIBUTES = [
240 'align-content',
241 'align-items',
242 'align-self',
243 'all',
244 'animation',
245 'animation-delay',
246 'animation-direction',
247 'animation-duration',
248 'animation-fill-mode',
249 'animation-iteration-count',
250 'animation-name',
251 'animation-play-state',
252 'animation-timing-function',
253 'backface-visibility',
254 'background',
255 'background-attachment',
256 'background-clip',
257 'background-color',
258 'background-image',
259 'background-origin',
260 'background-position',
261 'background-repeat',
262 'background-size',
263 'border',
264 'border-bottom',
265 'border-bottom-color',
266 'border-bottom-left-radius',
267 'border-bottom-right-radius',
268 'border-bottom-style',
269 'border-bottom-width',
270 'border-collapse',
271 'border-color',
272 'border-image',
273 'border-image-outset',
274 'border-image-repeat',
275 'border-image-slice',
276 'border-image-source',
277 'border-image-width',
278 'border-left',
279 'border-left-color',
280 'border-left-style',
281 'border-left-width',
282 'border-radius',
283 'border-right',
284 'border-right-color',
285 'border-right-style',
286 'border-right-width',
287 'border-spacing',
288 'border-style',
289 'border-top',
290 'border-top-color',
291 'border-top-left-radius',
292 'border-top-right-radius',
293 'border-top-style',
294 'border-top-width',
295 'border-width',
296 'bottom',
297 'box-decoration-break',
298 'box-shadow',
299 'box-sizing',
300 'break-after',
301 'break-before',
302 'break-inside',
303 'caption-side',
304 'caret-color',
305 'clear',
306 'clip',
307 'clip-path',
308 'clip-rule',
309 'color',
310 'column-count',
311 'column-fill',
312 'column-gap',
313 'column-rule',
314 'column-rule-color',
315 'column-rule-style',
316 'column-rule-width',
317 'column-span',
318 'column-width',
319 'columns',
320 'contain',
321 'content',
322 'content-visibility',
323 'counter-increment',
324 'counter-reset',
325 'cue',
326 'cue-after',
327 'cue-before',
328 'cursor',
329 'direction',
330 'display',
331 'empty-cells',
332 'filter',
333 'flex',
334 'flex-basis',
335 'flex-direction',
336 'flex-flow',
337 'flex-grow',
338 'flex-shrink',
339 'flex-wrap',
340 'float',
341 'flow',
342 'font',
343 'font-display',
344 'font-family',
345 'font-feature-settings',
346 'font-kerning',
347 'font-language-override',
348 'font-size',
349 'font-size-adjust',
350 'font-smoothing',
351 'font-stretch',
352 'font-style',
353 'font-synthesis',
354 'font-variant',
355 'font-variant-caps',
356 'font-variant-east-asian',
357 'font-variant-ligatures',
358 'font-variant-numeric',
359 'font-variant-position',
360 'font-variation-settings',
361 'font-weight',
362 'gap',
363 'glyph-orientation-vertical',
364 'grid',
365 'grid-area',
366 'grid-auto-columns',
367 'grid-auto-flow',
368 'grid-auto-rows',
369 'grid-column',
370 'grid-column-end',
371 'grid-column-start',
372 'grid-gap',
373 'grid-row',
374 'grid-row-end',
375 'grid-row-start',
376 'grid-template',
377 'grid-template-areas',
378 'grid-template-columns',
379 'grid-template-rows',
380 'hanging-punctuation',
381 'height',
382 'hyphens',
383 'icon',
384 'image-orientation',
385 'image-rendering',
386 'image-resolution',
387 'ime-mode',
388 'isolation',
389 'justify-content',
390 'left',
391 'letter-spacing',
392 'line-break',
393 'line-height',
394 'list-style',
395 'list-style-image',
396 'list-style-position',
397 'list-style-type',
398 'margin',
399 'margin-bottom',
400 'margin-left',
401 'margin-right',
402 'margin-top',
403 'marks',
404 'mask',
405 'mask-border',
406 'mask-border-mode',
407 'mask-border-outset',
408 'mask-border-repeat',
409 'mask-border-slice',
410 'mask-border-source',
411 'mask-border-width',
412 'mask-clip',
413 'mask-composite',
414 'mask-image',
415 'mask-mode',
416 'mask-origin',
417 'mask-position',
418 'mask-repeat',
419 'mask-size',
420 'mask-type',
421 'max-height',
422 'max-width',
423 'min-height',
424 'min-width',
425 'mix-blend-mode',
426 'nav-down',
427 'nav-index',
428 'nav-left',
429 'nav-right',
430 'nav-up',
431 'none',
432 'normal',
433 'object-fit',
434 'object-position',
435 'opacity',
436 'order',
437 'orphans',
438 'outline',
439 'outline-color',
440 'outline-offset',
441 'outline-style',
442 'outline-width',
443 'overflow',
444 'overflow-wrap',
445 'overflow-x',
446 'overflow-y',
447 'padding',
448 'padding-bottom',
449 'padding-left',
450 'padding-right',
451 'padding-top',
452 'page-break-after',
453 'page-break-before',
454 'page-break-inside',
455 'pause',
456 'pause-after',
457 'pause-before',
458 'perspective',
459 'perspective-origin',
460 'pointer-events',
461 'position',
462 'quotes',
463 'resize',
464 'rest',
465 'rest-after',
466 'rest-before',
467 'right',
468 'row-gap',
469 'scroll-margin',
470 'scroll-margin-block',
471 'scroll-margin-block-end',
472 'scroll-margin-block-start',
473 'scroll-margin-bottom',
474 'scroll-margin-inline',
475 'scroll-margin-inline-end',
476 'scroll-margin-inline-start',
477 'scroll-margin-left',
478 'scroll-margin-right',
479 'scroll-margin-top',
480 'scroll-padding',
481 'scroll-padding-block',
482 'scroll-padding-block-end',
483 'scroll-padding-block-start',
484 'scroll-padding-bottom',
485 'scroll-padding-inline',
486 'scroll-padding-inline-end',
487 'scroll-padding-inline-start',
488 'scroll-padding-left',
489 'scroll-padding-right',
490 'scroll-padding-top',
491 'scroll-snap-align',
492 'scroll-snap-stop',
493 'scroll-snap-type',
494 'shape-image-threshold',
495 'shape-margin',
496 'shape-outside',
497 'speak',
498 'speak-as',
499 'src', // @font-face
500 'tab-size',
501 'table-layout',
502 'text-align',
503 'text-align-all',
504 'text-align-last',
505 'text-combine-upright',
506 'text-decoration',
507 'text-decoration-color',
508 'text-decoration-line',
509 'text-decoration-style',
510 'text-emphasis',
511 'text-emphasis-color',
512 'text-emphasis-position',
513 'text-emphasis-style',
514 'text-indent',
515 'text-justify',
516 'text-orientation',
517 'text-overflow',
518 'text-rendering',
519 'text-shadow',
520 'text-transform',
521 'text-underline-position',
522 'top',
523 'transform',
524 'transform-box',
525 'transform-origin',
526 'transform-style',
527 'transition',
528 'transition-delay',
529 'transition-duration',
530 'transition-property',
531 'transition-timing-function',
532 'unicode-bidi',
533 'vertical-align',
534 'visibility',
535 'voice-balance',
536 'voice-duration',
537 'voice-family',
538 'voice-pitch',
539 'voice-range',
540 'voice-rate',
541 'voice-stress',
542 'voice-volume',
543 'white-space',
544 'widows',
545 'width',
546 'will-change',
547 'word-break',
548 'word-spacing',
549 'word-wrap',
550 'writing-mode',
551 'z-index'
552 // reverse makes sure longer attributes `font-weight` are matched fully
553 // instead of getting false positives on say `font`
554].reverse();
555
556// some grammars use them all as a single group
557const PSEUDO_SELECTORS = PSEUDO_CLASSES.concat(PSEUDO_ELEMENTS);
558
559/*
560Language: Less
561Description: It's CSS, with just a little more.
562Author: Max Mikhailov <seven.phases.max@gmail.com>
563Website: http://lesscss.org
564Category: common, css, web
565*/
566
567/** @type LanguageFn */
568function less(hljs) {
569 const modes = MODES(hljs);
570 const PSEUDO_SELECTORS$1 = PSEUDO_SELECTORS;
571
572 const AT_MODIFIERS = "and or not only";
573 const IDENT_RE = '[\\w-]+'; // yes, Less identifiers may begin with a digit
574 const INTERP_IDENT_RE = '(' + IDENT_RE + '|@\\{' + IDENT_RE + '\\})';
575
576 /* Generic Modes */
577
578 const RULES = []; const VALUE_MODES = []; // forward def. for recursive modes
579
580 const STRING_MODE = function(c) {
581 return {
582 // Less strings are not multiline (also include '~' for more consistent coloring of "escaped" strings)
583 className: 'string',
584 begin: '~?' + c + '.*?' + c
585 };
586 };
587
588 const IDENT_MODE = function(name, begin, relevance) {
589 return {
590 className: name,
591 begin: begin,
592 relevance: relevance
593 };
594 };
595
596 const AT_KEYWORDS = {
597 $pattern: /[a-z-]+/,
598 keyword: AT_MODIFIERS,
599 attribute: MEDIA_FEATURES.join(" ")
600 };
601
602 const PARENS_MODE = {
603 // used only to properly balance nested parens inside mixin call, def. arg list
604 begin: '\\(',
605 end: '\\)',
606 contains: VALUE_MODES,
607 keywords: AT_KEYWORDS,
608 relevance: 0
609 };
610
611 // generic Less highlighter (used almost everywhere except selectors):
612 VALUE_MODES.push(
613 hljs.C_LINE_COMMENT_MODE,
614 hljs.C_BLOCK_COMMENT_MODE,
615 STRING_MODE("'"),
616 STRING_MODE('"'),
617 modes.CSS_NUMBER_MODE, // fixme: it does not include dot for numbers like .5em :(
618 {
619 begin: '(url|data-uri)\\(',
620 starts: {
621 className: 'string',
622 end: '[\\)\\n]',
623 excludeEnd: true
624 }
625 },
626 modes.HEXCOLOR,
627 PARENS_MODE,
628 IDENT_MODE('variable', '@@?' + IDENT_RE, 10),
629 IDENT_MODE('variable', '@\\{' + IDENT_RE + '\\}'),
630 IDENT_MODE('built_in', '~?`[^`]*?`'), // inline javascript (or whatever host language) *multiline* string
631 { // @media features (it’s here to not duplicate things in AT_RULE_MODE with extra PARENS_MODE overriding):
632 className: 'attribute',
633 begin: IDENT_RE + '\\s*:',
634 end: ':',
635 returnBegin: true,
636 excludeEnd: true
637 },
638 modes.IMPORTANT
639 );
640
641 const VALUE_WITH_RULESETS = VALUE_MODES.concat({
642 begin: /\{/,
643 end: /\}/,
644 contains: RULES
645 });
646
647 const MIXIN_GUARD_MODE = {
648 beginKeywords: 'when',
649 endsWithParent: true,
650 contains: [
651 {
652 beginKeywords: 'and not'
653 }
654 ].concat(VALUE_MODES) // using this form to override VALUE’s 'function' match
655 };
656
657 /* Rule-Level Modes */
658
659 const RULE_MODE = {
660 begin: INTERP_IDENT_RE + '\\s*:',
661 returnBegin: true,
662 end: /[;}]/,
663 relevance: 0,
664 contains: [
665 {
666 begin: /-(webkit|moz|ms|o)-/
667 },
668 modes.CSS_VARIABLE,
669 {
670 className: 'attribute',
671 begin: '\\b(' + ATTRIBUTES.join('|') + ')\\b',
672 end: /(?=:)/,
673 starts: {
674 endsWithParent: true,
675 illegal: '[<=$]',
676 relevance: 0,
677 contains: VALUE_MODES
678 }
679 }
680 ]
681 };
682
683 const AT_RULE_MODE = {
684 className: 'keyword',
685 begin: '@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b',
686 starts: {
687 end: '[;{}]',
688 keywords: AT_KEYWORDS,
689 returnEnd: true,
690 contains: VALUE_MODES,
691 relevance: 0
692 }
693 };
694
695 // variable definitions and calls
696 const VAR_RULE_MODE = {
697 className: 'variable',
698 variants: [
699 // using more strict pattern for higher relevance to increase chances of Less detection.
700 // this is *the only* Less specific statement used in most of the sources, so...
701 // (we’ll still often loose to the css-parser unless there's '//' comment,
702 // simply because 1 variable just can't beat 99 properties :)
703 {
704 begin: '@' + IDENT_RE + '\\s*:',
705 relevance: 15
706 },
707 {
708 begin: '@' + IDENT_RE
709 }
710 ],
711 starts: {
712 end: '[;}]',
713 returnEnd: true,
714 contains: VALUE_WITH_RULESETS
715 }
716 };
717
718 const SELECTOR_MODE = {
719 // first parse unambiguous selectors (i.e. those not starting with tag)
720 // then fall into the scary lookahead-discriminator variant.
721 // this mode also handles mixin definitions and calls
722 variants: [
723 {
724 begin: '[\\.#:&\\[>]',
725 end: '[;{}]' // mixin calls end with ';'
726 },
727 {
728 begin: INTERP_IDENT_RE,
729 end: /\{/
730 }
731 ],
732 returnBegin: true,
733 returnEnd: true,
734 illegal: '[<=\'$"]',
735 relevance: 0,
736 contains: [
737 hljs.C_LINE_COMMENT_MODE,
738 hljs.C_BLOCK_COMMENT_MODE,
739 MIXIN_GUARD_MODE,
740 IDENT_MODE('keyword', 'all\\b'),
741 IDENT_MODE('variable', '@\\{' + IDENT_RE + '\\}'), // otherwise it’s identified as tag
742 {
743 begin: '\\b(' + TAGS.join('|') + ')\\b',
744 className: 'selector-tag'
745 },
746 modes.CSS_NUMBER_MODE,
747 IDENT_MODE('selector-tag', INTERP_IDENT_RE, 0),
748 IDENT_MODE('selector-id', '#' + INTERP_IDENT_RE),
749 IDENT_MODE('selector-class', '\\.' + INTERP_IDENT_RE, 0),
750 IDENT_MODE('selector-tag', '&', 0),
751 modes.ATTRIBUTE_SELECTOR_MODE,
752 {
753 className: 'selector-pseudo',
754 begin: ':(' + PSEUDO_CLASSES.join('|') + ')'
755 },
756 {
757 className: 'selector-pseudo',
758 begin: ':(:)?(' + PSEUDO_ELEMENTS.join('|') + ')'
759 },
760 {
761 begin: /\(/,
762 end: /\)/,
763 relevance: 0,
764 contains: VALUE_WITH_RULESETS
765 }, // argument list of parametric mixins
766 {
767 begin: '!important'
768 }, // eat !important after mixin call or it will be colored as tag
769 modes.FUNCTION_DISPATCH
770 ]
771 };
772
773 const PSEUDO_SELECTOR_MODE = {
774 begin: IDENT_RE + ':(:)?' + `(${PSEUDO_SELECTORS$1.join('|')})`,
775 returnBegin: true,
776 contains: [ SELECTOR_MODE ]
777 };
778
779 RULES.push(
780 hljs.C_LINE_COMMENT_MODE,
781 hljs.C_BLOCK_COMMENT_MODE,
782 AT_RULE_MODE,
783 VAR_RULE_MODE,
784 PSEUDO_SELECTOR_MODE,
785 RULE_MODE,
786 SELECTOR_MODE
787 );
788
789 return {
790 name: 'Less',
791 case_insensitive: true,
792 illegal: '[=>\'/<($"]',
793 contains: RULES
794 };
795}
796
797export { less as default };