PostCSS Deep Dive maak uw eigen plug-in

Zoals je vast en zeker op dit punt hebt verzameld, is PostCSS verbazingwekkend door zijn bloeiende plugin-ecosysteem. En een enorme reden waarom er zoveel geweldige plug-ins zijn, met meer coming-out de hele tijd, is dat PostCSS maakt het maken van uw eigen plug-in zo toegankelijk voor iedereen die enige ervaring met JavaScript heeft.

U hebt geen speciale toestemming nodig om een ​​PostCSS-plug-in te maken; als je er een wilt maken, ga je gewoon door en haal je het. Door deze vrijheid heb je de mogelijkheid om je CSS-ontwikkelingsprocessen om te zetten in alles wat je maar wilt, om nog maar te zwijgen van de mogelijkheid om je werk te delen met andere leden van de snelgroeiende PostCSS-gemeenschap.

In deze zelfstudie leert u hoe u een eigen eigen PostCSS-plug-in kunt maken. We zullen niet al te zwaar op de plug-in-API ingaan en we zullen geen super-hardcore codering gebruiken. Ik ben zelf een front-end developer en mijn JavaScript-vaardigheden liggen op het niveau dat je van hen verwacht voor een front-end persoon, maar dat weerhield me er niet van om mijn eerste PostCSS-plug-in in slechts enkele uren te maken.

Volg en zie zelf hoe benaderbaar PostCSS-plug-ins kunnen zijn!

Wat we gaan bouwen

We zullen een plug-in maken die het gemakkelijk maakt om lettertypestapels in te voegen font-family Verklaringen via de volgende syntaxis:

h1 font-family: "Open Sans", fontstack ("Arial"); 

Na compilatie zal de bovenstaande code veranderen in:

h1 font-family: "Open Sans", Arial, "Helvetica Neue", Helvetica, sans-serif; 

Stel een project op om binnenin te werken

Hoewel je je eigen plug-in maakt, moet je nog steeds beginnen met het maken van een leeg Gulp- of Grunt-project. Je laadt je plug-in in dit project op dezelfde manier waarop je de plug-ins van anderen in deze serie hebt gebruikt.

U kunt lezen over hoe u Gulp- of Grunt-projecten voor PostCSS in de vorige zelfstudies kunt instellen:

  • PostCSS-snelstartgids: Gulp-instellingen
  • PostCSS-snelstartgids: Gromde opstelling

Als u uw project echter niet helemaal opnieuw wilt instellen, kunt u de bronbestanden downloaden die aan deze zelfstudie zijn gekoppeld en het meegeleverde Gulp- of Grunt-startersproject uitpakken in een lege projectmap. Voer vervolgens de opdracht uit met een terminal- of opdrachtprompt naar de map gericht npm installeren.

Maak een Basic Plugin Shell

Maak een map in "node_modules" met de naam "postcss-myplugin". Het is gebruikelijk om het voorvoegsel te gebruiken postcss- om duidelijk te maken dat uw plug-in voor PostCSS is.

Alle PostCSS-plug-ins zijn knoopmodules, dus we zullen uw nieuwe map in één moeten veranderen. Open een terminal / opdrachtprompt, wijs naar de zojuist gemaakte map en voer uit npm init. Hiermee voer je de basisinstellingen van een knooppuntmodule uit, dus volg gewoon de aanwijzingen die in je terminal verschijnen, waarbij het veld "beginpunt" de standaardindex "index.js" blijft..

Wanneer dit is gebeurd, met uw terminal nog steeds op de map gericht, voert u de opdracht uit npm installeer postcss --save. Hiermee wordt PostCSS geïnstalleerd als een afhankelijkheid voor deze module, iets wat alle PostCSS-plug-ins moeten doen.

Maak een bestand met de naam "index.js" in de map "postcss-myplugin". Voeg deze code toe om te laden in de hoofdpostcss-module:

var postcss = require ('postcss');

Voeg hieronder deze standaardwrapper toe die de verwerkingscode van onze plug-in zal omringen:

var postcss = require ('postcss'); module.exports = postcss.plugin ('myplugin', functie myplugin (opties) return function (css) options = options || ; // Verwerkingscode wordt hier toegevoegd);

Laad je nieuwe plug-in

Nu zijn we klaar om uw zojuist gemaakte plug-in in uw project te laden. Het zal nog niets doen, maar we willen gewoon de essentiële setup op zijn plaats krijgen.

Laad de plug-in via Gulp

Als u Gulp gebruikt, voegt u deze variabele toe aan degene die al in het bestand staat:

var myplugin = require ('postcss-myplugin');

Voeg nu de nieuwe variabelenaam toe aan uw processors array:

 var processors = [myplugin];

Voer een snelle test uit dat alles werkt door de opdracht uit te voeren slok css en controleren of er een nieuw "style.css" -bestand is verschenen in de map "dest" van uw project.

Laad de plug-in via Grunt

Als u Grunt gebruikt, werk dan het processors object, dat is genest onder de opties object, naar het volgende:

 processors: [require ('postcss-myplugin') ()]

Voer een snelle test uit dat alles werkt door de opdracht uit te voeren grunt postcss en controleren of er een nieuw "style.css" -bestand is verschenen in de map "dest" van uw project.

Voeg de plug-infunctionaliteit toe

Test CSS toevoegen

Voordat we beginnen met het toevoegen van verwerkingscode aan onze plug-in, gaan we een aantal testcode toevoegen aan ons stylesheet dat de plug-in kan werken.

Voeg aan je "src / style.css" bestand deze CSS toe:

h1 font-family: "Open Sans", fontstack ("Arial"); 

Op dit moment, omdat onze plug-in nog niets doet, zal je, als je je CSS compileert, precies dezelfde code zien die rechtstreeks in het "style.css" -bestand van je "dest" map is gekopieerd.

Doorloop de regels en verklaringen

Nu willen we beginnen met het scannen van de CSS van ons bestand, zodat we eventuele instanties kunnen vinden fontstack () en verwerk ze. Om hieraan te beginnen, voegt u de volgende code toe na de opties = opties || ; lijn:

 css.walkRules (function (rule) rule.walkDecls (function (decl, i) ););

Het gebruik van walkRules () in de eerste regel doorloopt elke regel in uw CSS; een regel is in feite uw selector en de stijlen die u hebt ingesteld tussen accolades. In onze test CSS zou een regel zijn:

h1 font-family: "Open Sans", fontstack ("Arial"); 

Binnen elke regel doorloopt walkDecls () elke verklaring; een verklaring is in wezen elke regel in de stijl. In de bovenstaande CSS zou een verklaring zijn:

font-family: "Open Sans", fontstack ("Arial");

Controleer of fontstack () Syntaxis wordt gebruikt

Terwijl we elke verklaring doorlopen met behulp van de code die we zojuist hebben toegevoegd, wordt de huidige verklaring vertegenwoordigd door decl, die ons toegang geeft tot zowel het eigendom van de verklaring als de waarde ervan via decl.prop en decl.value respectievelijk.

Met ons voorbeeld CSS, decl.prop zou ons geven font-family en decl.value zou ons geven  "Open Sans", fontstack ("Arial").

We willen elk controleren decl.value in ons stylesheet om te zien of het de string bevat fontstack (. Als dit het geval is, weten we dat iemand onze plug-in probeert te gebruiken om een ​​lettertypestapel aan zijn CSS toe te voegen.

Binnen in de walkDecl () loop, voeg toe:

 var value = decl.value; if (value.indexOf ('fontstack (')! == -1) console.log ("found fontstack");

Eerst nemen we decl.value en het opslaan in de variabele waarde. Eventuele wijzigingen in decl.value zal worden verzonden naar de gecompileerde stylesheet; we bewaren de inhoud ervan in de variabele waarde dus we kunnen er mee rotzooien.

Vervolgens gebruiken we de methode indexOf () om ons nieuwe te doorzoeken waarde variabele voor de string fontstack (. Als dit wordt gevonden, loggen we 'found fontstack' in op de console, zodat we kunnen controleren of alles werkt tot nu toe.

Rennen slok css of grunt postcss en je zou de "found fontstack" uitvoer eens moeten zien in je terminal / opdrachtprompt.

Definieer enkele lettertypen

Nu dat onze plug-in gevallen van kan lokaliseren fontstack () in ons stylesheet kunnen we ons voorbereiden om dat exemplaar om te zetten in een lettertypestapel, d.w.z. een lijst met lettertypenamen. Maar voordat we dat kunnen doen, moeten we eerst deze lettertypestapels definiëren.

Aan de bovenkant van uw bestand, onder de bestaande postcss variabele, maak een variabele met de naam fontstacks_config. We gaan deze variabele omzetten in een object met sleutel / waarde-paren.

Voor elk item in het object moet de sleutel het eerste lettertype in de lettertypestack zijn, bijvoorbeeld. 'Arial'. Dit is de tekenreeks die een gebruiker doorgeeft om de lettertypestapel op te geven die ze willen gebruiken, bijvoorbeeld. fontstack ( "Arial") of fontstack ("Times New Roman").

De waarde in elk paar moet een tekenreeks zijn van de volledige lijst met lettertypen in de lettertypestack, bijvoorbeeld. 'Arial', Helvetica Neue ', Helvetica, sans-serif'.

Voeg twee items toe aan uw fontstacks_config object, één voor 'Arial' en één voor 'Times New Roman', met behulp van de lettertypestapels van CSS Font Stack.

Jouw fontstacks_config variabele zou er als volgt uit moeten zien:

// Lettertypestapels van http://www.cssfontstack.com/ var fontstacks_config = 'Arial': 'Arial', Helvetica Neue ', Helvetica, sans-serif', 'Times New Roman': 'TimesNewRoman,' Times New Roman ", Times, Baskerville, Georgia, serif '

Controleer welk lettertype is aangevraagd

Het eerste dat we moeten doen als we er een voorbeeld van vinden fontstack () wordt gebruikt om erachter te komen welke lettertypestapel de gebruiker heeft aangevraagd, d.w.z. welke reeks ze hebben ingesteld tussen de haakjes. 

Bijvoorbeeld als een gebruiker heeft ingevoerd fontstack ( "Arial") we zouden de string eruit willen halen Arial. De reden dat we deze string willen is dat het ons een sleutel geeft die we kunnen gebruiken om de corresponderende lettertypestapel op te zoeken van onze fontstacks_config voorwerp.

Voeg deze code direct toe binnen de als verklaring die we eerder hebben toegevoegd, ter vervanging van de console.log ("found fontstack"); lijn:

// Verkrijg de naam van het gevraagde lettertype door de tekenreeks tussen de haakjes van fontstack () te plaatsen. // Vervang vervolgens eventuele dubbele of enkele aanhalingstekens. var fontstack_requested = value.match (/ \ (([^)] +) \) /) [1] .replace (/ ["'] / g," ");

We voeren hier twee stappen uit om de naam van de fontstack uit te pakken als een tekenreeks.

Eerst gebruiken we de match () -methode om te vinden welke tekenreeks zich tussen de haakjes in onze waarde bevindt. Dit zou ons een string geven zoals "Arial" of 'Arial'.

We willen alleen de naam van het lettertype, zonder dubbele of enkele aanhalingstekens, dus gebruiken we de methode replace () om ze uit de tekenreeks te verwijderen, waardoor we een niet-genoteerde tekenreeks zoals Arial.

Deze tekenreeks wordt opgeslagen in de fontstack_requested veranderlijk.

Titel Plaats de gevraagde lettertypen

We gaan onze nieuw gecreëerde gebruiken fontstack_requested variabele om een ​​lettertypestapel op te zoeken uit onze fontstack_config keuze. Het lastige is dat de toetsen in dit object hoofdlettergevoelig zijn, dus als we de Arial invoer met de sleutel arial het zal mislukken.

Om dit op te lossen, gaan we naar "Title Case" de string, dus bijvoorbeeld keer nieuw Romeins zou worden omgezet in Times New Roman. We doen dit via een korte aangepaste functie.

Onder jouw fontstacks_config variabele voeg dit toe toTitleCase () functie:

// Krediet voor deze functie naar http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript/196991#196991 function toTitleCase (str) return str.replace (/ \ w \ S * / g, functie (txt) retourneer txt.charAt (0) .toUpperCase () + txt.substr (1) .toLowerCase ();); 

Nu zullen we deze functie toepassen op onze fontstack_requested variabel. Onder de regel waar u de fontstack_requested variabele, voeg deze code toe:

// Title beweer de woorden in de naam van het lettertype, voor het geval de gebruiker het zelf niet deed fontstack_requested = toTitleCase (fontstack_requested);

Dit verstrijkt de fontstack_requested veranderlijk via onze toTitleCase () functie, waarbij de waarde wordt bijgewerkt.

Lookup Fontstack Vanaf Config

Nu hebben we onze fonstack_requested variabele correct ingesteld, kunnen we deze gebruiken om de bijbehorende lettertypestapel op te zoeken. Voeg na de regel die u zojuist hebt toegevoegd deze code toe:

// Zoek de gewenste fontstack op in het fontstack_config-object var fontstack = fontstacks_config [fontstack_requested];

Dit vindt de waarde in de fontstacks_config object met een sleutel die overeenkomt met de reeks in onze fontstack_requested veranderlijk. 

Bijvoorbeeld, als fontstack_requested bevat de string Arial, de inzending in fontstacks_config met de sleutel Arial zal worden gevonden en de waarde 'Arial', Helvetica Neue ', Helvetica, sans-serif' zal worden teruggegeven.

Deze geretourneerde waarde wordt vervolgens opgeslagen in de variabele fontstack.

Controleren op lettertypen die vóór fontstack () zijn ingesteld

Nu hebben we onze lettertypestapelreeks teruggehaald en klaar om in de CSS te worden ingevoegd, maar er is nog een ding dat we moeten doen. U herinnert zich in onze testcode dat we het lettertype "Open Sans" als het gewenste lettertype hebben opgenomen, waarbij de lettertypestapel als een terugval fungeerde. We moeten ook deze fontnaam ophalen uit de waarde, zodat deze kan worden toegevoegd aan de CSS die we in de verwerkte stylesheet invoegen.

Onder de fontstack variabele regel, voeg deze code toe:

// Zoek en bewaar lettertypenamen die al in de waarde zitten, voordat de fontstack () call var first_font = value.substr (0, value.indexOf ('fontstack ('));

Deze code gebruikt de methode substr () om inhoud te vinden tussen het begin van onze waarde, (vertegenwoordigd door 0), en onze fontstack () instance (bevindt zich met behulp van de methode indexOf ()). Welke inhoud ook wordt gevonden, wordt opgeslagen in de variabele first_font.

Bijvoorbeeld in onze testcode waarde is gelijk aan  "Open Sans", fontstack ("Arial"), dus de first_font variabele wordt ingesteld als  "Open Sans", .

Maak een nieuwe waarde

We hebben nu alle stukjes die we nodig hebben om een ​​nieuwe waarde te creëren waarmee we de oorspronkelijke waarde van onze testcode kunnen vervangen  "Open Sans", fontstack ("Arial").

Na de laatste code die je hebt toegevoegd, voeg je deze code toe:

// Maak de nieuwe waarde voor deze regel door de variabelen first_font en fontstack te combineren var new_value = first_font + fontstack;

Hier combineren we onze first_font en fontstack variabelen in een enkele reeks en sla deze op in de variabele nieuwe waarde

In onze testcode zou dit combineren betekenen  "Open Sans",  en Arial, "Helvetica Neue", Helvetica, sans-serif.

Onze nieuwe waarde variabele zou dan de string bevatten  "Open Sans", "Arial," Helvetica Neue ", Helvetica, sans-serif '.

Dit geeft ons nu de volledige waarde die we willen toevoegen aan de verwerkte stylesheet, zodat: 

font-family: "Open Sans", fontstack ("Arial"); 

... wordt getransformeerd in:

font-family: "Open Sans", "Arial," Helvetica Neue ", Helvetica, sans-serif ';

Stuur de nieuwe waarde terug naar het stylesheet

Nu we onze nieuwe waarde gereed hebben om te worden ingevoegd in de verwerkte stylesheet, hoeven we alleen maar bij te werken decl.value. PostCSS zorgt voor de rest en voegt de nieuwe waarde toe aan de verwerkte CSS voor ons.

Voeg deze code toe na de laatste regel die u heeft toegevoegd:

// Verzend de nieuwe waarde terug naar de stylesheet decl.value = new_value;

Dit stelt in decl.value om de inhoud van onze te evenaren nieuwe waarde veranderlijk.

Test je plugin

Uw plug-in is nu goed om te gebruiken. Geef het een werveling door uw stylesheet samen te stellen slok css of grunt postcss (met uw terminal gericht op uw projectmap, niet op uw map voor plug-ins).

Uw "dest / style.css" -bestand zou nu een volledige lettertypestapel moeten tonen:

h1 font-family: "Open Sans", Arial, "Helvetica Neue", Helvetica, sans-serif; 

(Optioneel) Voeg door de gebruiker configureerbare Fontstacks-opties toe

Mogelijk wilt u gebruikers van uw plug-in toestaan ​​hun eigen opties in te stellen, op dezelfde manier waarop u opties hebt ingesteld terwijl u PostCSS-plug-ins in deze serie heeft gebruikt.

We willen dat gebruikers een fontstacks optie, het toevoegen van extra lettertypestapels of het herdefiniëren van bestaande lettertypestapels, bijvoorbeeld:

fontstacks: 'Extra Stack': '"Extra Stack", "Moar Fonts", Extra, serif', 'Arial': 'Arial, "Comic Sans"'

Notitie: deze stap is optioneel. Als u wilt, kunt u dit overslaan en uw plug-in zal perfect werken, zonder enige configuratie van de gebruiker.

We hebben al het meest essentiële onderdeel van het inschakelen van gebruikersinstellingen in onze plug-in. In onze module.exports regel je zult een opties argument wordt gepasseerd. 

module.exports = postcss.plugin ('myplugin', functie (opties) 

We ontvangen alle gebruikersopties die een gebruiker hierdoor doorloopt.

Je ziet ook dat we de regel hebben:

opties = opties || ;

Dit controleert om te zien of opties heeft een waarde, en als dat niet het geval is, wordt het ingesteld op een leeg object. Dit zorgt ervoor dat we geen fouten krijgen als we beginnen met werken opties dat kan komen doordat het ongedefinieerd is.

Om aan de slag te gaan, gaan we Underscore.js installeren in ons project, omdat we de handige methode extend () gebruiken. Voer deze opdracht uit om het te installeren in de plug-in die u aan het bouwen bent:

npm installeer underscore - opslaan

Laad nu Underscore in uw plug-in door een _ variabele om het te vereisen, onder je bestaande postcss variabele:

var postcss = require ('postcss'); var _ = vereisen ('onderstrepingsteken');

Wat we hierna gaan doen, is de fontstacks_config object dat we al in de plug-in hebben gemaakt en "uitbreiden" met elke lettertypestapel die de gebruiker via de configuratie van de opties heeft ingesteld.

Voeg deze code rechtstreeks toe onder de opties = opties || ; lijn:

 // Verleng de standaardoptie fontstacks_config met eventuele aangepaste lettertypen ingesteld in de plugin-opties fontstacks_config = _.extend (fontstacks_config, options.fontstacks);

De fontstacks optie die is ingesteld door de gebruiker wordt weergegeven door options.fontstacks.

Door Underscore's te gebruiken uitbreiden() methode, alle lettertypestapels in options.fontstacks worden toegevoegd aan degenen die al in fontstacks_config. Waar een sleutel is, de waarde van options.fontstacks zal de erin overschrijven fontstacks_config. Hiermee kunnen gebruikers bestaande lettertypenstapels opnieuw definiëren.

In je Gulpfile of Gruntfile, stel a fontstacks optie en een nieuwe lettertypestapel doorgeven en een bestaande lettertypenstapel opnieuw definiëren:

/ * Gulpfile * / var processors = [myplugin (fontstacks: 'Extra Stack': '"Extra Stack", "Moar Fonts", Extra, serif', 'Arial': 'Arial, "Comic Sans"' )]; / * Gruntfile * / processors: [require ('postcss-myplugin') (fontstacks: 'Extra Stack': '"Extra Stack", "Moar Fonts", Extra, serif', 'Arial': 'Arial', Comic Sans "')]

Voeg nu wat extra CSS toe aan uw "src / style.css" -bestand, zodat we de nieuwe lettertypestapel kunnen testen die we zojuist hebben toegevoegd via onze opties:

h2 font-family: "Droid Sans", fontstack ("Extra Stack"); 

Hercompileer uw CSS en u zou moeten zien dat uw 'Arial' lettertypestapel nu verschillende uitvoer heeft en dat uw 'Extra Stack'-lettertypestapel correct is uitgevoerd:

h1 font-family: "Open Sans", Arial, "Comic Sans";  h2 font-family: "Droid Sans", "Extra Stack", "Moar Fonts", Extra, serif; 

Uw voltooide plug-in

Dat is het! Je bent helemaal klaar. Je hebt je eerste PostCSS-plug-in voltooid.

Hier is het hele ding op GitHub, mocht je jouw code als referentie willen vergelijken.

Laten we samenvatten

U hebt zojuist een volledige PostCSS-plug-in gemaakt en ik hoop dat sommige ideeën bij u opkomen over andere plug-ins die u graag zou willen maken. Misschien is er dat ene kleine ding dat je altijd lastigvalt als je CSS schrijft, en misschien kun je nu met je eigen oplossing komen om er voorgoed van af te geraken. Of misschien is er iets extra dat je echt denkt dat CSS out of the box zou moeten hebben - nou, je kunt het zelf toevoegen!

Om samen te vatten wat we hebben behandeld:

  • Begin met het ontwikkelen van een nieuwe plug-in door een Gulp- of Grunt-project op te zetten om in te werken.
  • Maak een nieuwe knooppuntmodule in uw project, die uw plug-in wordt.
  • Laad uw nieuwe plug-in in uw project.
  • Voeg wat test-CSS toe aan de syntaxis die u voor uw plug-in wilt gebruiken.
  • Gebruik methoden uit de PostCSS API om door een stylesheet te scannen.
  • Zoek naar instanties van de syntaxis van uw plug-in die wordt gebruikt.
  • Schrijf JavaScript en gebruik de PostCSS API om de juiste transformaties (en / of toevoegingen) aan de originele code te maken en deze naar de verwerkte CSS te verzenden.

Voor TypeScript-gebruikers

Als onderdeel van de 5.0-release van PostCSS heeft Jed Mao een geweldige set van TypeScript-definities bijgedragen die een grote bijdrage kunnen leveren aan de ontwikkeling van plug-ins door tijdens het typen autocompletion en inline-documentatie aan te bieden.

Als u merkt dat u PostCSS-plug-ins ontwikkelt, is dit echt de moeite waard om te integreren in uw workflow. Ikzelf ben geen typerende TypeScript-hand, maar ga er toch mee in coderen, bijna puur zodat ik deze functionaliteit kan gebruiken.

Als u dit wilt uitproberen, hoeft u niet op Windows te zijn of Visual Studio te gebruiken, omdat u de gratis open-source Visual Studio-code kunt gebruiken die op Windows, Mac en Linux draait en op Electron is gebouwd , dezelfde shell die Atom Editor aanstuurt.

Bekijk de invoegtoepassing postcss-font-pack voor een voorbeeld van het opnemen van deze TypeScript-definities in uw project. Vork het en speel in Visual Studio Code om te zien hoe de autocompletion en inline documentatie werken.

PostCSS Deep Dive: Wrapping Up

Heel erg bedankt voor het volgen van deze PostCSS Deep Dive-serie. Ik hoop dat je het net zo leuk vond om het te lezen als ik het leuk vond om het te maken! Wat nog belangrijker is, ik hoop dat je een hoofd vol ideeën hebt over hoe je PostCSS aan het werk kunt zetten in je dagelijkse leven op het web..

PostCSS is echt een ongelooflijke nieuwe toevoeging aan de front-end wereld, omdat de manier waarop plug-ins worden vergemakkelijkt deuren opent naar mogelijkheden die we nog nooit eerder in CSS-ontwikkeling hebben gehad. Het bereik van plug-ins dat nu beschikbaar is, is al voldoende om de dagelijkse workflows van een persoon volledig opnieuw vorm te geven, en dat is precies wat er in een tijdsbestek van een paar jaar is gemaakt. 

Ik zou willen voorstellen dat PostCSS nog moet pieken, en dat het iets is waar de meerderheid van de CSS-ontwikkelaars op zijn minst van op de hoogte is, zo niet vloeken, we zullen zien dat het echt op gang komt. En met meer front-end ontwikkelaars aan boord, zullen we meer bijdragen aan het plugin-ecosysteem zien, nieuwe plug-ins toevoegen en bestaande plug-ins helpen opbouwen.

!