PostCSS Deep Dive Skapa ditt eget plugin

Eftersom du är säker på att du verkligen har samlat dig vid den här punkten, som gör PostCSS fantastiskt, är det blomstrande plugin-ekosystemet. Och en stor anledning till att det finns så många bra plugins, med mer som kommer ut hela tiden, är att PostCSS gör att skapa din egen plugin så tillgänglig för alla som har viss erfarenhet av JavaScript.

Du behöver inte särskilt tillstånd för att göra ett PostCSS-plugin. Om du vill göra en, går du bara fram och gör det. Genom denna frihet har du möjlighet att vända dina CSS-utvecklingsprocesser till allt du vill att de ska vara, för att inte tala om möjligheten att dela ditt arbete med andra medlemmar i det snabbt växande PostCSS-samhället.

I denna handledning lär du dig hur du skapar ett eget eget plugin för PostCSS. Vi kommer inte att gå för mycket i plugin API, och vi kommer inte använda någon super hardcore-kodning. Jag är själv frontenutvecklare och mina JavaScript-färdigheter ligger på den nivå som du förväntar dig att de är för en front-end-person, men det hindrade inte mig att göra mitt första PostCSS-plugin på bara några timmar.

Följ med och se själv hur tillförlitlig PostCSS-pluginutveckling kan vara!

Vad vi ska bygga

Vi skapar ett plugin som gör det möjligt att enkelt infoga fontstaplar i typsnittsfamilj deklarationer via följande syntax:

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

Efter sammansättningen kommer ovanstående kod att bli:

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

Ställ in ett projekt för att arbeta inuti

Även om du skapar ett eget plugin måste du börja med att skapa ett tomt Gulp eller Grunt-projekt. Du laddar in ditt plugin i det här projektet på samma sätt som du har använt andras plugins i hela serien.

Du kan läsa om hur du konfigurerar Gulp eller Grunt-projekt för PostCSS i de tidigare handledningarna:

  • PostCSS Quickstart Guide: Gulp Setup
  • PostCSS Quickstart Guide: Grunt Setup

Om du inte vill installera ditt projekt manuellt från början, kan du ladda ner källfilerna som bifogas denna handledning och extrahera antingen det angivna Gulp eller Grunt-startprojektet i en tom projektmapp. Sedan, med en terminal eller kommandotolk som pekas på mappen, kör kommandot npm installera.

Skapa ett Basic Plugin Shell

Skapa en mapp i "node_modules" med namnet "postcss-myplugin". Det är vanligt att använda prefixet postcss- för att klargöra din plugin är för PostCSS.

Alla PostCSS-plugins är nodmoduler, så vi måste ändra din nya mapp till en. Öppna en terminal / kommandotolk, pekad på den nyskapade mappen och kör npm init. Detta kommer att utföra den grundläggande inställningen för en nodmodul, så följ bara de instruktioner som kommer upp i din terminal, och lämna "Ingångspunkt" -fältet som standard "index.js".

När detta är klart, kör din kommando med din terminal fortfarande i mappen npm installera postcss - save. Detta installerar PostCSS som ett beroende för den här modulen, något som alla PostCSS-plugins behöver göra.

Skapa en fil med namnet "index.js" i mappen "postcss-myplugin". Lägg till den här koden för att ladda i den viktigaste postcss-modulen:

var postcss = kräver ("postcss");

Sedan nedanför lägger du till det här grundläggande inslaget som omger vårt pluginets bearbetningskod:

var postcss = kräver ("postcss"); module.exports = postcss.plugin ('myplugin', funktion myplugin (alternativ) return function (css) options = options || ; // Behandlingskoden läggs till här);

Ladda din nya plugin

Nu är vi redo att ladda ditt nyskapade plugin till ditt projekt. Det kommer inte göra någonting än, men vi vill bara få den väsentliga inställningen på plats.

Ladda pluggen via Gulp

Om du använder Gulp, lägg till den här variabeln under den som redan finns i filen:

var myplugin = kräver ('postcss-myplugin');

Lägg nu till det nya variabelnamnet i din processorer array:

 var processorer = [myplugin];

Gör ett snabbt test att allt fungerar genom att köra kommandot gulp css och kontrollera att en ny "style.css" -fil har dykt upp i ditt projekts "dest" -mapp.

Ladda pluggen via Grunt

Om du använder Grunt, uppdatera processorer objekt som är nestat under alternativ objekt till följande:

 processorer: [kräver ("postcss-myplugin") ()]

Gör ett snabbt test att allt fungerar genom att köra kommandot grunt postcss och kontrollera att en ny "style.css" -fil har dykt upp i ditt projekts "dest" -mapp.

Lägg till pluginfunktionalitet

Lägg till test CSS

Innan vi börjar lägga till bearbetningskod i vårt plugin ska vi lägga till en testkod i vårt stylesheet att plugin kan fungera på.

Till din "src / style.css" -fil lägg till den här CSS:

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

Just nu, eftersom vår plugin inte gör någonting än, om du sammanställer din CSS ser du bara exakt samma kod kopieras direkt i din "dest" -mapps "style.css" -fil.

Gå igenom reglerna och deklarationerna

Nu vill vi börja skanna CSS i vår fil så att vi kan hitta några exempel på fontstack () och bearbeta dem. För att komma igång med det här, lägg till följande kod efter alternativ = alternativ || ; linje:

 css.walkRules (funktion (regel) rule.walkDecls (funktion (decl, i) ););

Använda walkRules () i första raden iterates genom varje regel i din CSS; en regel är i grund och botten din väljare och de stilar du har ställt mellan sina lockiga axlar. I vårt test CSS skulle en regel vara:

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

Sedan, inuti varje regel, går walkDecls () genom varje deklaration; en deklaration är i huvudsak varje linje i stilen. I ovanstående CSS skulle en deklaration vara:

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

Kolla om fontstack () Syntax används

När vi repeterar genom varje deklaration med koden som vi just lagt till, är den nuvarande deklarationen representerad av decl, vilket ger oss tillgång till både deklarationens egendom och dess värde via decl.prop och decl.value respektive.

Med vårt exempel CSS, decl.prop skulle ge oss typsnittsfamilj och decl.value skulle ge oss  "Open Sans", fontstack ("Arial").

Vi vill kolla alla decl.value i vårt stylesheet för att se om det innehåller strängen fontstack (. Om det gör vet vi att någon försöker använda vårt plugin för att lägga till en fontstack till deras CSS.

Inuti walkDecl () loop, lägg till:

 varvärde = decl.value; om (value.indexOf ('fontstack (')! == -1) console.log ("found fontstack");

Först tar vi decl.value och lagra den i variabeln värde. Eventuella ändringar till decl.value kommer att skickas till det sammanställda formatarket; Vi lagrar innehållet i variabeln värde så vi kan röra runt med det.

Då använder vi indexOf () -metoden för att söka i vårt nya värde variabel för strängen fontstack (. Om det hittas loggar vi "found fontstack" till konsolen så vi kan kontrollera om allt fungerar så långt.

Springa gulp css eller grunt postcss och du bör se "hittade fontstack" -utdata en gång i din terminal / kommandotolpekdator.

Definiera vissa fontstacker

Nu när vårt plugin kan hitta instanser av fontstack () i vårt stilark kan vi göra oss redo att konvertera den här instansen till en fontstack, dvs en lista med typsnitt. Men innan vi kan göra det måste vi först definiera dessa typsnittstaplar.

Överst i din fil, under det befintliga postcss variabel, skapa en variabel som heter fontstacks_config. Vi kommer att ändra denna variabel till ett objekt som innehåller nyckelvärdespar.

För varje post i objektet ska nyckeln vara det första tecknet i teckensnittsstacken, t.ex.. 'Arial'. Det blir den sträng som en användare skickar för att ange den typsnittstapel som de vill använda, t.ex.. fontstack ( "Arial") eller fontstack ("Times New Roman").

Värdet i varje par ska vara en sträng av hela listan med teckensnitt som finns i teckensnittsstacken, t.ex.. 'Arial,' Helvetica Neue ', Helvetica, sans-serif'.

Lägg till två poster till din fontstacks_config objekt, en för "Arial" och en för "Times New Roman", med hjälp av typsnittstaplarna som tillhandahålls av CSS Font Stack.

Din fontstacks_config variabel ska se ut så här:

// Font staplar från http://www.cssfontstack.com/ var fontstacks_config = 'Arial': 'Arial,' Helvetica Neue ', Helvetica, sans-serif', 'Times New Roman': 'TimesNewRoman, Times Times Romerska ", Times, Baskerville, Georgia, Serif '

Kontrollera vilken typsnitt som begärs

Det första vi behöver göra när vi hittar en förekomst av fontstack () används för att räkna ut vilken typ av teckensnittsstack användaren har begärt, dvs vilken sträng som de har satt mellan parenteserna. 

Till exempel, om en användare skrev in fontstack ( "Arial") vi skulle vilja extrahera strängen Arial. Anledningen till att vi vill ha den här strängen är det kommer att ge oss en nyckel som vi kan använda för att leta upp motsvarande typsnittstapel från vår fontstacks_config objekt.

Lägg till denna kod direkt inuti om uttalande vi lagt till tidigare, ersätter console.log ("found fontstack"); linje:

// Hämta namnet på den fontstack som begärs genom att matcha strängen inuti parentesen för fontstack (). // Byt sedan ut några dubbla eller enkla citattecken. var fontstack_requested = value.match (/ \ (([^)] +) \) /) [1] .replace (/ ["'] / g," ");

Vi utför två steg här för att extrahera namnet på typsnittet som en sträng.

Först använder vi metoden match () för att hitta vilken sträng som helst mellan parentesen i vårt värde. Detta skulle ge oss en sträng som "Arial" eller 'Arial'.

Vi vill bara ha teckensnittsnamnet, utan några dubbla eller enkla citat, så använder vi sedan ersättmetoden () för att ta bort dem från strängen och lämnar oss med en obotad sträng som t.ex. Arial.

Denna sträng lagras i fontstack_requested variabel.

Titel Fyll i önskad fontstack

Vi ska använda vår nyskapade fontstack_requested variabel för att leta upp en fontstack från vår fontstack_config alternativ. Den knepiga delen är nycklarna i det här objektet är skiftlägeskänsliga, så om vi försöker slå upp Arial inmatning med nyckeln arial det kommer att misslyckas.

För att lösa detta, kommer vi att "Titel Case" strängen, så till exempel Times New Roman skulle konverteras till Times New Roman. Vi gör det via en kort anpassad funktion.

Under din fontstacks_config variabel lägg till detta toTitleCase () fungera:

// Kredit för denna funktion till http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript/196991#196991 function toTitleCase (str) return str.replace (/ \ w \ S * / g, funktion (txt) return txt.charAt (0) .toUpperCase () + txt.substr (1) .toLowerCase ();); 

Nu ska vi använda denna funktion till vår fontstack_requested variabel. Under raden där du skapade fontstack_requested variabel, lägg till den här koden:

// Titel faller orden i teckensnittsnamnet, bara om användaren inte gjorde det själv fontstack_requested = toTitleCase (fontstack_requested);

Detta passerar fontstack_requested variabel genom vår toTitleCase () funktion, uppdatering av dess värde.

Lookup Fontstack från Config

Nu har vi vår fonstack_requested variabel inställd korrekt kan vi använda den för att leta upp motsvarande fontstack. Efter den linje som du just lagt till, sätt in den här koden:

// Sök efter den begärda teckensnitten i fontstack_config-objektet var fontstack = fontstacks_config [fontstack_requested];

Detta finner värdet i fontstacks_config objekt som har en nyckel som matchar strängen i vår fontstack_requested variabel. 

Till exempel, om fontstack_requested innehåller strängen Arial, posten i fontstacks_config med nyckeln Arial kommer att hittas och värdet 'Arial,' Helvetica Neue ', Helvetica, sans-serif' kommer att returneras.

Detta returnerade värde lagras sedan i variabeln fontstack.

Kontrollera efter typsnittet Innan fontstack ()

Nu har vi vår fontstacksträng återhämtad och redo att införas i CSS, men det finns fortfarande en sak vi behöver göra. Du kommer ihåg i vår testkod att vi inkluderade teckensnittet "Open Sans" som det föredragna tecknet, med teckensnittsstacken som fungerar som en återgång. Vi måste också hämta det här teckensnittsnamnet från värdet så att det kan läggas till CSS som vi lägger in i det behandlade formatarket.

Under fontstack variabel rad, lägg till den här koden:

// Hitta och spara alla teckensnittsnamn som redan kan vara i värdet innan fontstacken () samtal var first_font = value.substr (0, value.indexOf ('fontstack ('));

Denna kod använder metoden substr () för att hitta något innehåll mellan början av vår värde, (representerad av 0), och vår fontstack () instans (lokaliseras med hjälp av indexOf () -metoden). Oavsett innehållet finns lagrat i variabeln first_font.

Till exempel i vår testkod värde är lika med  "Open Sans", fontstack ("Arial"), så first_font variabel kommer att ställas in som  "Open Sans", .

Skapa ett nytt värde

Vi har nu alla bitar som vi behöver för att skapa ett nytt värde för att ersätta vår testkods ursprungliga värde av  "Open Sans", fontstack ("Arial").

Efter den sista koden du lade till, sätt in den här koden:

// Skapa det nya värdet för denna regel genom att kombinera variablerna first_font och fontstack var new_value = first_font + fontstack;

Här kombinerar vi vår first_font och fontstack variablerna i en enda sträng och lagra den i variabeln new_value

I vår testkod skulle detta innebära att man kombinerar  "Open Sans",  och Arial, "Helvetica Neue", Helvetica, sans-serif.

Vår new_value variabeln skulle då hålla strängen  "Open Sans", "Arial," Helvetica Neue ", Helvetica, sans-serif".

Detta ger oss nu det fullständiga värdet som vi vill lägga till i det bearbetade formatet så att: 

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

... omvandlas till:

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

Skicka det nya värdet tillbaka till stilarket

Nu när vi har vårt nya värde redo att införas i det bearbetade formatarket, behöver vi bara uppdatera decl.value. PostCSS tar hand om resten, lägger till det nya värdet i den bearbetade CSS för oss.

Lägg till den här koden efter den sista raden du lagt till:

// Skicka det nya värdet tillbaka till stilarket decl.value = new_value;

Detta sätter decl.value att jämföra innehållet i vårt new_value variabel.

Testa din plugin

Din plugin är nu bra att gå. Ge det en virvel genom att sammanställa ditt stylesheet med gulp css eller grunt postcss (med din terminal pekad på din projektmapp, inte din plugin-mapp).

Din "dest / style.css" -fil ska nu visa en komplett typsnittstapel:

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

(Valfritt) Lägg till användarkonfigurerbara Fontstacks-alternativ

Du kanske vill tillåta användare av ditt plugin att ställa in egna alternativ, på samma sätt som du har ställt in alternativ eftersom du har använt PostCSS-plugins under hela serien.

Vi vill att användarna ska kunna ställa in en fontstacks alternativ, antingen lägga till extra teckensnittsstackar eller omdefiniera befintliga teckensnittsstackar, till exempel:

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

Notera: detta steg är valfritt. Om du önskar kan du hoppa över det och din plugin fungerar perfekt, bara utan någon användarsättning konfiguration.

Vi har redan den viktigaste delen av att möjliggöra användarsetalternativ på plats i vårt plugin. I vår module.exports linje märker du en alternativ argumentet överförs. 

module.exports = postcss.plugin ('myplugin', funktion (alternativ) 

Vi får alla användaralternativ som en användare ställer in genom det här.

Du ser också att vi har linjen:

alternativ = alternativ || ;

Detta kontrollerar för att se om alternativ har något värde, och om det inte gör det, sätter det till ett tomt objekt. Detta gör att vi inte får några fel när vi börjar jobba med alternativ Det kan komma ifrån att det är odefinierat.

För att komma igång, kommer vi att installera Underscore.js i vårt projekt, eftersom vi använder den praktiska extend () -metoden. Kör det här kommandot för att installera det i plugin du bygger:

npm installera understrykning - spara

Lägg nu Underscore i ditt plugin genom att lägga till en _ variabel för att kräva det, under din befintliga postcss variabel:

var postcss = kräver ("postcss"); var _ = kräver ("understrykning");

Nästa vad vi ska göra är att ta fontstacks_config objekt som vi redan skapat inuti plugin, och "förlänga" det med alla teckensnittsstackar som användaren har ställt in genom sin alternativkonfiguration.

Lägg till den här koden direkt under alternativ = alternativ || ; linje:

 // Utöka standard fontstacks_config alternativet med alla anpassade typsnitt som anges i plugins alternativ fontstacks_config = _.extend (fontstacks_config, options.fontstacks);

De fontstacks alternativet som har ställts in av användaren representeras av options.fontstacks.

Genom att använda Underscore s förlänga() metod, alla teckensnittsstaplar i options.fontstacks läggs till de som redan finns i fontstacks_config. Varhelst det finns en matchande nyckel, värdet från options.fontstacks kommer att skriva över den i fontstacks_config. Detta gör det möjligt för användare att omdefiniera befintliga typsnittstaplar.

I ditt Gulpfile eller Gruntfile, sätt a fontstacks alternativ och skicka en ny teckensnittsstapel samt omdefiniera en befintlig:

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

Lägg nu till lite extra CSS till din "src / style.css" -fil så att vi kan testa den nya teckensnittsstacken som vi just har lagt till via våra alternativ:

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

Kompilera din CSS och du bör se att din Arial-teckensnittsstack nu har annorlunda utdata, och att din "Extra Stack" -fontsstapel har skrivit ut korrekt:

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

Ditt slutförda plugin

Det är allt! Du är helt klar. Du har slutfört ditt första PostCSS-plugin.

Här är hela grejen på GitHub om du behöver jämföra din kod till den som referens.

Låt oss läsa om

Du har just skapat ett helt PostCSS-plugin, och jag hoppas att några idéer kommer att springa in i ditt sinne om andra plugins du skulle vilja göra. Kanske finns det en liten sak som alltid buggar dig när du skriver CSS, och kanske nu kan du komma med din egen lösning för att bli av med det för gott. Eller kanske finns det något extra du tycker verkligen att CSS borde ha ur lådan-ja, nu kan du lägga till det själv!

För att sammanfatta vad vi har täckt:

  • Börja med att utveckla ett nytt plugin genom att skapa ett Gulp eller Grunt-projekt för att arbeta i.
  • Skapa en ny nodmodul i ditt projekt, vilket blir ditt plugin.
  • Ladda din nya plugin till ditt projekt.
  • Lägg till några test-CSS i den syntax du vill att din plugin ska använda.
  • Använd metoder från PostCSS API för att skanna igenom ett stylesheet.
  • Leta reda på om din plugin syntax används.
  • Skriv JavaScript och använd PostCSS API för att göra lämpliga transformationer (och / eller tillägg) till originalkoden och skicka den till den bearbetade CSS.

För TypeScript-användare

Som en del av 5.0-versionen av PostCSS har Jed Mao bidragit till en bra uppsättning typScript-definitioner som kan hjälpa till mycket med plugin-utveckling genom att tillhandahålla autofullständig och inline-dokumentation när du skriver.

Om du befinner dig i PostCSS-pluginutveckling, är det verkligen något värt att se på att integrera i ditt arbetsflöde. Jag är själv inte en dab TypeScript-hand, men kommer ändå att hoppa in i kodning med det, nästan rent så jag kan utnyttja denna funktion.

Om du vill prova detta behöver du inte vara på Windows eller använda Visual Studio, eftersom du kan använda den fria, öppna källkoden Visual Studio Code, som körs på Windows, Mac och Linux och är byggt på Electron , samma skal som driver Atom Editor.

För ett exempel på hur du införlivar dessa TypeScript-definitioner i ditt projekt, kolla in plug-in-plug-in-plug-in. Gaffel och spela i Visual Studio Code för att se hur autocompletion och inline dokumentation fungerar.

PostCSS Deep Dive: Wrapping Up

Tack så mycket för att följa med denna PostCSS Deep Dive-serie. Jag hoppas att du njöt av att läsa den så mycket som jag tyckte om att skapa den! Ännu viktigare, jag hoppas att du har ett huvud fullt av idéer om hur du kan få PostCSS att fungera i ditt dagliga webbutvecklingsliv.

PostCSS är verkligen ett otroligt nytt tillskott till fronten-världen, eftersom sättet att underlätta plugins öppnar dörrarna för möjligheter som vi aldrig tidigare haft i CSS-utveckling. Utbudet av plugins som är tillgängliga nu är redan tillräckligt för att omforma en persons dagliga arbetsflöden fullständigt, och det är bara med vad som har skapats inom ett par år. 

Jag skulle föreslå att PostCSS ännu inte har topp, och som det börjar vara något som majoriteten av CSS-utvecklarna åtminstone vet om, om inte svär på, så ser vi att det verkligen kommer in i striden. Och med fler front-end-utvecklare som kommer ombord ser vi fler bidrag till plugin-ekosystemet, lägger till nya plugins och hjälper till att bygga upp befintliga.

!