5.8.114 • Published 11 months ago

@devtea2026/quis-recusandae-natus-distinctio v5.8.114

Weekly downloads
-
License
MIT
Repository
github
Last release
11 months ago

npm cov NPM Downloads

css-parser

CSS parser and minifier for node and the browser

Installation

$ npm install @devtea2026/quis-recusandae-natus-distinctio

Features

  • no dependency
  • fault-tolerant parser, will try to fix invalid tokens according to the CSS syntax module 3 recommendations.
  • fast and efficient minification without unsafe transforms, see benchmark
  • minify colors.
  • support css color level 4 & 5: color(), lab(), lch(), oklab(), oklch(), color-mix() and relative color
  • generate nested css rules
  • convert nested css rules to legacy syntax
  • generate sourcemap
  • compute css shorthands. see supported properties list below
  • evaluate calc()
  • inline css variables
  • remove duplicate properties
  • flatten @import rules

Playground

Try it online

Exports

There are several ways to import the library into your application.

Node exports

import as a module

import {transform} from '@devtea2026/quis-recusandae-natus-distinctio';

// ...

Deno exports

import as a module

import {transform} from 'npm:@devtea2026/quis-recusandae-natus-distinctio';

// ...

import as a CommonJS module

const {transform} = require('@devtea2026/quis-recusandae-natus-distinctio/cjs');

// ...

Web export

Programmatic import

import {transform} from '@devtea2026/quis-recusandae-natus-distinctio/web';

// ...

Javascript module from cdn

<script type="module">

    import {transform} from 'https://esm.sh/@devtea2026/quis-recusandae-natus-distinctio@0.4.0/web';


    const css = `
    .s {

    background: color-mix(in hsl, color(display-p3 0 1 0) 80%, yellow);
}
    `;

    console.debug(await transform(css).then(r => r.code));

</script>

Javascript module

<script src="dist/web/index.js" type="module"></script>

Single Javascript file

<script src="dist/index-umd-web.js"></script>

Transform

Parse and render css in a single pass.

Usage

transform(css, transformOptions: TransformOptions = {}): TransformResult

Example

import {transform} from '@devtea2026/quis-recusandae-natus-distinctio';

const {ast, code, map, errors, stats} = await transform(css, {minify: true, resolveImport: true, cwd: 'files/css'});

TransformOptions

Include ParseOptions and RenderOptions

ParseOptions

Minify Options

  • minify: boolean, optional. default to true. optimize ast.
  • nestingRules: boolean, optional. automatically generated nested rules.
  • expandNestingRules: boolean, optional. convert nesting rules into separate rules. will automatically set nestingRules to false.
  • removeDuplicateDeclarations: boolean, optional. remove duplicate declarations.
  • computeShorthand: boolean, optional. compute shorthand properties.
  • computeCalcExpression: boolean, optional. evaluate calc() expression
  • inlineCssVariables: boolean, optional. replace some css variables with their actual value. they must be declared once in the :root {} or html {} rule.
  • removeEmpty: boolean, optional. remove empty rule lists from the ast.

Sourcemap Options

  • src: string, optional. original css file location to be used with sourcemap.
  • sourcemap: boolean, optional. preserve node location data.

Misc Options

  • resolveUrls: boolean, optional. resolve css 'url()' according to the parameters 'src' and 'cwd'
  • resolveImport: boolean, optional. replace @import rule by the content of its referenced stylesheet.
  • removeCharset: boolean, optional. remove @charset.
  • cwd: string, optional. the current working directory. when specified url() are resolved using this value
  • visitor: VisitorNodeMap, optional. node visitor used to transform the ast.
  • signal: AbortSignal, optional. abort parsing.

RenderOptions

Minify Options

  • minify: boolean, optional. default to true. minify css output.
  • withParents: boolean, optional. render this node and its parents.
  • expandNestingRules: boolean, optional. expand nesting rules.
  • preserveLicense: boolean, force preserving comments starting with '/*!' when minify is enabled.
  • removeComments: boolean, remove comments in generated css.
  • convertColor: boolean, convert colors to hex.

Sourcemap Options

  • sourcemap: boolean, optional. generate sourcemap

Misc Options

  • indent: string, optional. css indention string. uses space character by default.
  • newLine: string, optional. new line character.
  • output: string, optional. file where to store css. url() are resolved according to the specified value. no file is created though.
  • cwd: string, optional. value used as current working directory. when output is not provided, urls are resolved according to this value.

Parsing

Usage

parse(css, parseOptions = {})

Example

const {ast, errors, stats} = await parse(css);

Rendering

Usage

render(ast, RenderOptions = {});

Examples

Rendering ast

import {parse, render} from '@devtea2026/quis-recusandae-natus-distinctio';

const css = `
@media screen and (min-width: 40em) {
    .featurette-heading {
        font-size: 50px;
    }
    .a {
        color: red;
        width: 3px;
    }
}
`;

const result = await parse(css, options);

// print declaration without parents
console.error(render(result.ast.chi[0].chi[1].chi[1], {withParents: false}));
// -> width:3px

// print declaration with parents
console.debug(render(result.ast.chi[0].chi[1].chi[1], {withParents: true}));
// -> @media screen and (min-width:40em){.a{width:3px}}

Merge similar rules

CSS

.clear {
  width: 0;
  height: 0;
  color: transparent;
}

.clearfix:before {

  height: 0;
  width: 0;
}
import {transform} from '@devtea2026/quis-recusandae-natus-distinctio';

const result = await transform(css);

Result

.clear,.clearfix:before{height:0;width:0}.clear{color:#0000}

Automatic CSS Nesting

CSS

const {parse, render} = require("@devtea2026/quis-recusandae-natus-distinctio/cjs");

const css = `
table.colortable td {
 text-align:center;
}
table.colortable td.c {
 text-transform:uppercase;
}
table.colortable td:first-child, table.colortable td:first-child+td {
 border:1px solid black;
}
table.colortable th {
 text-align:center;
 background:black;
 color:white;
}
`;

const result = await parse(css, {nestingRules:true}).then(result => render(result.ast, {minify:false}).code);

Result

table.colortable {
 & td {
  text-align: center;
  &.c {
   text-transform: uppercase
  }
  &:first-child,&:first-child+td {
   border: 1px solid #000
  }
 }
 & th {
  text-align: center;
  background: #000;
  color: #fff
 }
}

Nested CSS Expansion

CSS

table.colortable {
 & td {
  text-align: center;
  &.c {
   text-transform: uppercase
  }
  &:first-child,&:first-child+td {
   border: 1px solid #000
  }
 }
 & th {
  text-align: center;
  background: #000;
  color: #fff
 }
}

Javascript

import {parse, render} from '@devtea2026/quis-recusandae-natus-distinctio';

const options = {minify: true};
const {code} = await parse(css, options).then(result => render(result.ast, {minify: false, expandNestingRules: true}));
//
console.debug(code);

Result

table.colortable td {
  text-align:center;
}
table.colortable td.c {
  text-transform:uppercase;
}
table.colortable td:first-child, table.colortable td:first-child+td {
  border:1px solid black;
}
table.colortable th {
  text-align:center;
  background:black;
  color:white;
}

Calc() resolution

import {parse, render} from '@devtea2026/quis-recusandae-natus-distinctio';

const css = `

.foo-bar {
    width: calc(100px * 2);
    height: calc(((75.37% - 63.5px) - 900px) + (2 * 100px));
    max-width: calc(3.5rem + calc(var(--bs-border-width) * 2));
}
`;

const prettyPrint = await parse(css).then(result => render(result.ast, {minify: false}).code);

result

.foo-bar {
    width: 200px;
    height: calc(75.37% - 763.5px);
    max-width: calc(3.5rem + var(--bs-border-width)*2)
}

CSS variable inlining

import {parse, render} from '@devtea2026/quis-recusandae-natus-distinctio';

const css = `

:root {

--preferred-width: 20px;
}
.foo-bar {

    width: calc(calc(var(--preferred-width) + 1px) / 3 + 5px);
    height: calc(100% / 4);}
`

const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);

result

.foo-bar {
    width: 12px;
    height: 25%
}

CSS variable inlining and relative color

import {parse, render} from '@devtea2026/quis-recusandae-natus-distinctio';

const css = `

:root {
--color: green;
}
._19_u :focus {
    color:  hsl(from var(--color) calc(h * 2) s l);

}
`

const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);

result

._19_u :focus {
    color: navy
}

CSS variable inlining and relative color

import {parse, render} from '@devtea2026/quis-recusandae-natus-distinctio';

const css = `

html { --bluegreen:  oklab(54.3% -22.5% -5%); }
.overlay {
  background:  oklab(from var(--bluegreen) calc(1.0 - l) calc(a * 0.8) b);
}
`

const prettyPrint = await parse(css, {inlineCssVariables: true}).then(result => render(result.ast, {minify: false}).code);

result

.overlay {
    background: #0c6464
}

Node Walker

import {walk} from '@devtea2026/quis-recusandae-natus-distinctio';

for (const {node, parent, root} of walk(ast)) {
    
    // do something
}

AST

Comment

  • typ: string 'Comment'
  • val: string, the comment

Declaration

  • typ: string 'Declaration'
  • nam: string, declaration name
  • val: array of tokens

Rule

  • typ: string 'Rule'
  • sel: string, css selector
  • chi: array of children

AtRule

  • typ: string 'AtRule'
  • nam: string. AtRule name
  • val: rule prelude

AtRuleStyleSheet

  • typ: string 'Stylesheet'
  • chi: array of children

Sourcemap

  • sourcemap generation

Minification

  • reduce calc()
  • inline css variables
  • merge identical rules
  • merge adjacent rules
  • minify colors
  • minify numbers and Dimensions tokens
  • compute shorthand: see the list below
  • remove redundant declarations
  • conditionally unwrap :is()
  • automatic css nesting
  • automatically wrap selectors using :is()
  • avoid reparsing (declarations, selectors, at-rule)
  • node and browser versions
  • decode and replace utf-8 escape sequence

Computed shorthands properties

  • ~all~
  • animation
  • background
  • border
  • border-block-end
  • border-block-start
  • border-bottom
  • border-color
  • border-image
  • border-inline-end
  • border-inline-start
  • border-left
  • border-radius
  • border-right
  • border-style
  • border-top
  • border-width
  • column-rule
  • columns
  • container
  • contain-intrinsic-size
  • flex
  • flex-flow
  • font
  • font-synthesis
  • font-variant
  • gap
  • grid
  • grid-area
  • grid-column
  • grid-row
  • grid-template
  • inset
  • list-style
  • margin
  • mask
  • offset
  • outline
  • overflow
  • padding
  • place-content
  • place-items
  • place-self
  • scroll-margin
  • scroll-padding
  • scroll-timeline
  • text-decoration
  • text-emphasis
  • transition

Performance

  • flatten @import

Node Transformation

Ast can be transformed using node visitors

Exemple 1: Declaration

the visitor is called for any declaration encountered

import {AstDeclaration, ParserOptions} from "../src/@types";

const options: ParserOptions = {

    visitor: {

        Declaration: (node: AstDeclaration) => {

            if (node.nam == '-webkit-transform') {

                node.nam = 'transform'
            }
        }
    }
}

const css = `

.foo {
    -webkit-transform: scale(calc(100 * 2/ 15));
}
`;

console.debug(await transform(css, options));

// .foo{transform:scale(calc(40/3))}

Exemple 2: Declaration

the visitor is called only on 'height' declarations

import {AstDeclaration, LengthToken, ParserOptions} from "../src/@types";
import {EnumToken, EnumToken} from "../src/lib";
import {transform} from "../src/node";

const options: ParserOptions = {

    visitor: {

        Declaration: {

            // called only for height declaration
            height: (node: AstDeclaration): AstDeclaration[] => {


                return [
                    node,
                    {

                        typ: EnumToken.DeclarationNodeType,
                        nam: 'width',
                        val: [
                            <LengthToken>{
                                typ: EnumToken.Length,
                                val: '3',
                                unit: 'px'
                            }
                        ]
                    }
                ];
            }
        }
    }
};

const css = `

.foo {
    height: calc(100px * 2/ 15);
}
.selector {
color: lch(from peru calc(l * 0.8) calc(c * 0.7) calc(h + 180)) 
}
`;

console.debug(await transform(css, options));

// .foo{height:calc(40px/3);width:3px}.selector{color:#0880b0}

Exemple 3: At-Rule

the visitor is called on any at-rule

import {AstAtRule, ParserOptions} from "../src/@types";
import {transform} from "../src/node";


const options: ParserOptions = {

    visitor: {

        AtRule: (node: AstAtRule): AstAtRule => {
            
            if (node.nam == 'media') {

                return {...node, val: 'all'}
            }
        }
    }
};

const css = `

@media screen {
       
    .foo {

            height: calc(100px * 2/ 15);    
    } 
}
`;

console.debug(await transform(css, options));

// .foo{height:calc(40px/3)}

Exemple 4: At-Rule

the visitor is called only for at-rule media

import {AstAtRule, ParserOptions} from "../src/@types";
import {transform} from "../src/node";

const options: ParserOptions = {

    visitor: {

        AtRule: {

            media: (node: AstAtRule): AstAtRule => {

                return {...node, val: 'all'}
            }
        }
    }
};

const css = `

@media screen {
       
    .foo {

            height: calc(100px * 2/ 15);    
    } 
}
`;

console.debug(await transform(css, options));

// .foo{height:calc(40px/3)}

Exemple 5: Rule

the visitor is called on any Rule

import {AstAtRule, ParserOptions} from "../src/@types";
import {transform} from "../src/node";

const options: ParserOptions = {

    visitor: {


        Rule (node: AstRule): AstRule {

            return {...node, sel: '.foo,.bar,.fubar'};
        }
    }
};

const css = `

    .foo {

            height: calc(100px * 2/ 15);    
    } 
`;

console.debug(await transform(css, options));

// .foo,.bar,.fubar{height:calc(40px/3)}

Exemple 6: Rule

Adding declarations to any rule

import {transform} from "../src/node";
import {AstRule, ParserOptions} from "../src/@types";
import {parseDeclarations} from "../src/lib";

const options: ParserOptions = {

    removeEmpty: false,
    visitor: {

        Rule: async (node: AstRule): Promise<AstRule | null> => {

            if (node.sel == '.foo') {

                node.chi.push(...await parseDeclarations('width: 3px'));
                return node;
            }

            return null;
        }
    }
};

const css = `

.foo {
}
`;

console.debug(await transform(css, options));

// .foo{width:3px}

Thanks to Jetbrains for sponsoring this project with a free license

StyleSheetglobalsJSONiterateObjectsomeenvefficientvarscomputed-typeses2018MicrosofteslintpluginmergesettingsWebSocketskarmaebswebdataviewgetoptUint8ClampedArraystreamsomitformsserializeoptionECMAScript 7es5l10nweakmappropertyECMAScript 2015lesscssmapreduceassignarrayArray.prototype.flattenzodfigletless.jsansiregularcachesqsidlefunctioncallbackassertionIteratorinvariantWeakSetkoreanextendisjsdiffFloat64Arraybreakcryptreal-timesuperstructgetintrinsicargparsewritefindLastwatchnativeenderpersistentfull-widthwidthCSSStyleDeclarationminimalbusydotenvcloudsearchtrimRightmulti-packagehookformcurldirectorypyyamlcss-in-jsjsxWebSocketregexpjavascriptartsestoArraylivetypesafereact-hook-formwindowspackagethroatoptimistless mixinscolourECMAScript 2021rangeerrortypeisConcatSpreadableObservablestslibECMAScript 2022Uint32ArraywatcherReflect.getPrototypeOflinkuuidObservablehookstypeofvariableskinesisfinduppreserve-symlinksRFC-6455cloudformationpathECMAScript 2020wordwrapboundspinnerhelpersFloat32Arrayrmdirclassnamebufferless compilercss nestingtostringtagextensionurlutilbddmodulesworkspace:*callbindreadableindicatorfunctionallistenersInt16Arrayajaxasyncduplexcomparetraverseutil.inspectprototypestylesheetieenumerablebyteLengthcolorsdataViewsameValueZeroArrayBuffer#slicebrowserlistmatchAllponyfilltestcommand-linecryptoperformantpatchprivate dataescapeautoprefixerArray.prototype.findLastIndexsyntaxerrortapemkdirsconfigurableconnectdataio-tsauthES2017loadbalancinghasOwnPropertysigtermcalldeepcompilerdayjsstatelessexit-codeRegExp.prototype.flagsmiddlewareconfigmrucolumnworkerwrapruntimeeverygetECMAScript 2016electronexpressionArray.prototype.filterthrottlecolumnses-abstractspinnersrequireownvaluecloudwatchTypedArraygradients css0datastructurebrowserInt32ArraymochaqueueMicrotaskMaprobustassertsES2020ES2019Object.keysec2objrmfolderprettyES3formexecmomentoperating-systemObject.iscloudtrailArray.prototype.flatMapjsonpathsettercolortsperformancelengthwhichweaksetrfc4122japaneseObject.definePropertytypedarraytouchdescriptionwalkingtypedarraystelephonelogginglimitedzeroargumentvpcvariables in cssforEachnested cssserializationlesslastttyreact-hooksdiffSymbol.toStringTaggetPrototypeOfgetOwnPropertyDescriptorclassnamestestingajvlinewrapinspecttypeerrorfpsnumberencryptionexitsortbootstrap csswalkscheme-validationmkdirjsprunemodulenpmargvexpresstesterformattingreadablestreamECMAScript 2017arktypeECMAScript 6interruptsYAMLES5qscollection.es6libphonenumberglobalshrinkwrapstarterstringifydependency managerdeepcloneaccessibilitypasswordnamea11yfullwidthES2018optimizeryamlregular expressionflagunicodepreprocessorchinesetermglacierUint8ArrayfsserializercjkenvironmentsconcatlazyimportexportRegExp#flagsstringifieriteratorhas-ownlimitjsdomprogressWeakMapimmutablemetadatatc39class-validatorES6environmentdefineautoscalingreactconsoleArray.prototype.flatnegative zerofind-uppositiverestfulsharedarraybufferpnpm9emitECMAScript 2018getterkeysRxJSarraybufferES8look-upargsshebanguninstallguidArrayBufferdescriptorsmkdirp$.extendtypescriptjoiproxyratecore-jsdynamodbconcatMapframeworkregexless cssrdsTypeBoxESStreamsflattenECMAScript 2023outputmatcheshttptoobjectclassesapisyntaxtoStringTagpredictableUint16Arraysignalscss variableformatassertelasticachefastcloneiamremovevaliddependenciesFunction.prototype.namesequencebluebirdcss lesspipejshintSetbindpushfluxinternaldefinePropertyfilterconsumesideairbnbregular expressionstrimEndmobilemime-dbESnextinputsortedsimpledbimportvesttrimLeftschemaprivateURLstatusAsyncIteratorvisualtddresolvedescriptorloadingObject.entriesi18n@@toStringTagsignaltypes-0ES2023JSON-Schemastdlibes-shimsfullStreammake dirtaskaccessoremojiesmapfunctions[[Prototype]]requestquerywgetclonerapidbootstrap lessPushstyleguideapollospeedtyped arrayhardlinkses2015trimStartpackage.jsonwatchingcall-bind__proto__swfcensorutilitiesstatemimefast-deep-copyprotocol-buffersmacosslotpostcss256corsparentxhrvalidationinstallSymbolUnderscoreparentspropertiesgroupcsssigintfixed-widthhashtapfromparsingcopyextrabufferslanguagecloudfronthigher-orderschemefast-cloneentriesshamtakelinuxlintastchromenodejsbyteString.prototype.matchAllmoveprefixidentifiersprocesswarningirqsafeeventsreuseinternal slot_.extendString.prototype.triminferencetoolkitflagspackagesdom-testing-libraryArray.prototype.includesawesomesaucetrimES2016es2016fast-deep-cloneavasymlinkES2022debugstoragegatewayrecursivematchfetchbundlingeslintconfigviewoffsetes8CSSfilevaluesupredactcodeswritablepromisewhatwgdeterministiccallboundendpointstylenopefile systemsnsdeletesetImmediateprotostylestypedform-validationvalidateObject.assigneslint-plugincommandes6channelPromisestyled-componentsgdprdatelogwordbreakeventEmitterfastcopyrm -rfHyBiappECMAScript 2019gradients css3dropbatches-shim APIArrayBuffer.prototype.slicepolyfillwafreplaytoolssymboleslintreduceworkflowutilitycharacterReactiveExtensionss3jsonroute53ReactiveXparselruplugindomgraphqlcollectiontypanionqueuestructuredCloneArraycode pointshottoSortedsuperagentfast-copyxtermamazonchromiumawait.envreact-testing-librarybannerbcryptarraysgroupBylockfilecoercibleObject.fromEntriesdeep-clonenegativequerystringprotobufsetdiremrsymlinksincludesURLSearchParamsBigUint64ArrayreducerdeepcopyECMAScript 5genericsequalArray.prototype.findLastrm -frstablerandomawsfastifyidauthenticationpostcss-plugininstallerparsersearchelmcompile lessbrowserslistiterationtimecommandercall-boundoncemixinsflatpromisescliratelimitmonorepoerror-handlingES2015es7jasminedebuggerrgbflatMaprouteres2017symbolsArray.prototype.containscharactersreadrestintrinsiccoreestreefindagentsinatracircularyupcreateRxTypeScriptstreamECMAScript 3bundlerclienttexteventDispatcherhasOwnpackage manageropenterminalES2021picomatchbyteOffsetconcurrencyinimmernodeES7httpsfindLastIndexstreams2sharedfseventsdeep-copychaierrorObject.valuescontainsphonelookBigInt64Arraywaitecmascriptreduxredux-toolkiteast-asian-widthasciijQueryspeckeywatchFilestringInt8ArraybeanstalkequalityjestcheckshimquoteelbslicesetPrototypeOffasthasshell
5.8.114

11 months ago

5.8.113

11 months ago

5.8.112

11 months ago

5.8.111

11 months ago

5.8.110

11 months ago

5.8.109

11 months ago

5.8.108

11 months ago

5.8.107

11 months ago

5.6.94

11 months ago

5.6.93

11 months ago

5.6.92

11 months ago

4.1.30

1 year ago

4.1.31

1 year ago

4.1.32

1 year ago

4.1.33

1 year ago

4.5.78

12 months ago

4.5.79

12 months ago

4.3.59

1 year ago

4.3.58

1 year ago

4.1.27

1 year ago

4.1.28

1 year ago

4.1.29

1 year ago

4.1.26

1 year ago

5.8.99

11 months ago

5.5.91

11 months ago

5.5.92

11 months ago

5.5.90

11 months ago

4.5.80

12 months ago

4.3.60

1 year ago

4.3.64

1 year ago

4.3.63

1 year ago

4.3.62

1 year ago

4.3.61

1 year ago

4.3.66

1 year ago

4.3.65

1 year ago

5.5.84

12 months ago

5.5.85

12 months ago

5.5.82

12 months ago

5.5.83

12 months ago

5.5.80

12 months ago

5.5.81

12 months ago

4.2.42

1 year ago

4.2.43

1 year ago

4.2.44

1 year ago

4.2.45

1 year ago

4.4.69

1 year ago

4.4.68

1 year ago

4.4.67

1 year ago

4.2.40

1 year ago

4.4.66

1 year ago

4.2.41

1 year ago

5.5.88

12 months ago

4.2.46

1 year ago

5.5.89

12 months ago

4.2.47

1 year ago

5.5.86

12 months ago

4.2.48

1 year ago

5.5.87

12 months ago

4.2.49

1 year ago

5.7.96

11 months ago

3.1.23

1 year ago

5.7.95

11 months ago

5.7.98

11 months ago

3.1.25

1 year ago

5.7.97

11 months ago

3.1.24

1 year ago

3.1.26

1 year ago

5.7.94

11 months ago

4.2.33

1 year ago

4.2.34

1 year ago

4.2.39

1 year ago

4.2.35

1 year ago

5.7.99

11 months ago

4.2.36

1 year ago

4.2.37

1 year ago

4.2.38

1 year ago

3.0.23

1 year ago

3.0.21

1 year ago

3.0.22

1 year ago

3.0.20

1 year ago

5.8.106

11 months ago

5.8.102

11 months ago

5.8.103

11 months ago

5.8.104

11 months ago

5.8.105

11 months ago

4.4.72

12 months ago

4.4.71

1 year ago

5.8.100

11 months ago

4.4.70

1 year ago

5.8.101

11 months ago

4.4.76

12 months ago

4.2.53

1 year ago

4.4.75

12 months ago

4.2.54

1 year ago

4.4.74

12 months ago

4.2.55

1 year ago

4.4.73

12 months ago

4.2.56

1 year ago

4.2.50

1 year ago

4.4.78

12 months ago

4.2.51

1 year ago

4.4.77

12 months ago

4.2.52

1 year ago

4.2.57

1 year ago

4.2.58

1 year ago

3.0.19

1 year ago

3.0.18

1 year ago

3.0.16

1 year ago

3.0.17

1 year ago

3.0.15

1 year ago

3.0.14

1 year ago

3.0.13

1 year ago

3.0.12

1 year ago

2.0.12

1 year ago

2.0.11

1 year ago

2.0.10

1 year ago

2.0.9

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago