PostCSS Deep Dive Lag ditt eget plugin

Som jeg er sikker på at du har blitt godt samlet på dette punktet, gjør PostCSS fantastisk, det er det blomstrende plugin-økosystemet. Og en stor grunn til at det er så mange flotte plugins, med flere som kommer ut hele tiden, er at PostCSS gjør at du lager din egen plugin så lett tilgjengelig for alle som har erfaring med JavaScript.

Du trenger ikke spesiell tillatelse til å lage et PostCSS-plugin; Hvis du vil lage en, går du bare rett og gjør det. Gjennom denne friheten har du evnen til å gjøre CSS utviklingsprosesser til alt du vil at de skal være, for ikke å nevne muligheten til å dele arbeidet ditt med andre medlemmer av det raskt voksende PostCSS-samfunnet.

I denne opplæringen lærer du hvordan du lager et eget eget plugin for PostCSS. Vi vil ikke gå for tungt inn i plugin-API, og vi vil ikke bruke noen super hardcore-koding. Jeg er selv en frontend-utvikler, og mine JavaScript-ferdigheter er på det nivået du forventer at de skal være for en front-end-person, men det stoppet ikke meg å lage mitt første PostCSS-plugin på bare noen få timer.

Følg med og se selv hvordan tilnærming til PostCSS-pluginutvikling kan være!

Hva vi skal bygge

Vi lager et plugin som gjør det enkelt å sette inn skrifttypestabler i font-family erklæringer via følgende syntaks:

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

Etter samling vil ovennevnte kode bli til:

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

Sett opp et prosjekt som skal fungere inni

Selv om du lager din egen plugin, må du fortsatt starte med å lage et tomt Gulp eller Grunt-prosjekt. Du laster inn pluginet ditt i dette prosjektet på samme måte som du har brukt andre folks plugins i hele denne serien.

Du kan lese om hvordan du konfigurerer Gulp- eller Grunt-prosjekter for PostCSS i de tidligere opplæringene:

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

Hvis du ikke vil opprette prosjektet manuelt fra begynnelsen, kan du laste ned kildefilene som er vedlagt denne opplæringen, og trekke ut enten det forsynte Gulp eller Grunt-startprosjektet i en tom prosjektmappe. Deretter kjører kommandoen med en terminal eller kommandoprompt i mappen npm installasjon.

Opprett et grunnleggende plugin Shell

Opprett en mappe i "node_modules" kalt "postcss-myplugin". Det er vanlig å bruke prefikset postcss- for å gjøre det klart at plugin er for PostCSS.

Alle PostCSS-plugins er nodemoduler, så vi må slå den nye mappen til en. Åpne en terminal / kommandoprompt, pek på den nylig opprettede mappen, og kjør npm init. Dette vil utføre det grunnleggende oppsettet til en nodemodul, så følg bare instruksjonene som kommer opp i terminalen din, og la "inngangspunkt" -feltet som standard "index.js".

Når dette er gjort, kjører du med terminalen din fremdeles i mappen. Kjør kommandoen npm installere postcss - save. Dette vil installere PostCSS som en avhengighet for denne modulen, noe som alle PostCSS-plugins må gjøre.

Opprett en fil med navnet "index.js" i "postcss-myplugin" -mappen. Legg til denne koden for å laste inn hovedpostmodulen:

var postcss = krever ('postcss');

Deretter legger du til denne grunnleggende innpakningen som vil omgjøre prosesskoden vår:

var postcss = krever ('postcss'); module.exports = postcss.plugin ('myplugin', funksjon myplugin (alternativer) return function (css) options = options || ; // Behandlingskoden vil bli lagt til her);

Legg inn nytt plugin

Nå er vi klare til å laste det nyopprettede pluginet i prosjektet ditt. Det vil ikke gjøre noe enda, men vi vil bare få det nødvendige oppsettet på plass.

Legg pluggen via Gulp

Hvis du bruker Gulp, legg til denne variabelen under den som allerede er i filen:

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

Legg nå det nye variabelenavnet i din prosessorer matrise:

 var prosessorer = [myplugin];

Gjør en rask test at alt fungerer ved å kjøre kommandoen gulp css og sjekker at en ny "style.css" -fil har dukket opp i prosjektets "dest" -mappe.

Legg pluggen via Grunt

Hvis du bruker Grunt, oppdaterer du prosessorer objekt, som er nestet under opsjoner objekt til følgende:

 prosessorer: [krever ('postcss-myplugin') ()]

Gjør en rask test at alt fungerer ved å kjøre kommandoen grunt postcss og sjekker at en ny "style.css" -fil har dukket opp i prosjektets "dest" -mappe.

Legg til pluginfunksjonaliteten

Legg til test CSS

Før vi begynner å legge til behandlingskode i pluginet, skal vi legge til noen testkode i vårt stilark som pluginet kan fungere på.

Til filen "src / style.css" legg til dette CSS:

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

Akkurat nå, fordi vårt plugin ikke gjør noe enda, hvis du kompilerer CSS, ser du akkurat nøyaktig samme kode kopiert rett inn i "dest" -mappen sin "style.css" -fil.

Gå gjennom reglene og deklarasjonene

Nå vil vi begynne å skanne CSS av filen, slik at vi kan finne noen forekomster av fontstack () og behandle dem. For å komme i gang på dette, legg til følgende kode etter alternativer = alternativer || ; linje:

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

Ved å bruke walkRules () i den første linjen iterates gjennom hver regel i CSS; en regel er i utgangspunktet din selector og stilene du har satt mellom sine krøllete braces. I vår test CSS vil en regel være:

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

Deretter, i hver regel, går walkDecls () gjennom hver deklarasjon; en erklæring er i hovedsak hver linje i stilen. I ovennevnte CSS vil en erklæring være:

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

Sjekk om fontstack () Syntaks er brukt

Når vi gjentar gjennom hver deklarasjon ved hjelp av koden vi nettopp har lagt til, er gjeldende deklarasjon representert av decl, som gir oss tilgang til både deklarasjonens eiendom og dens verdi via decl.prop og decl.value henholdsvis.

Med vårt eksempel CSS, decl.prop ville gi oss font-family og decl.value ville gi oss  "Open Sans", fontstack ("Arial").

Vi vil sjekke hver decl.value i stilarket vårt for å se om det inneholder strengen fontstack (. Hvis det gjør, vet vi at noen prøver å bruke pluginet vårt for å legge til en fontstabel til deres CSS.

Inne i walkDecl () sløyfe, legg til:

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

Først tar vi decl.value og lagre den i variabelen verdi. Eventuelle endringer i decl.value vil bli sendt inn i det sammensatte stilarket; Vi lagrer innholdet i variabelen verdi slik at vi kan rote rundt med det.

Så bruker vi indeksen indexOf () for å søke i vårt nye verdi variabel for strengen fontstack (. Hvis den er funnet, logger vi "funnet fontstack" til konsollen, så vi kan sjekke om alt jobber så langt.

Løpe gulp css eller grunt postcss og du bør se "funnet fontstack" -utgang en gang i terminal / kommandoprompt.

Definer noen fontstacker

Nå som vår plugin er i stand til å finne forekomster av fontstack () I vårt stilark kan vi gjøre oss klar til å konvertere den forekomsten til en fontstabel, det vil si en liste over skriftnavn. Men før vi kan gjøre det, må vi først definere disse skriftstablene.

Øverst på filen din, under eksisterende postcss variabel, opprett en variabel som heter fontstacks_config. Vi skal omdanne denne variabelen til et objekt som inneholder nøkkelverdier.

For hver oppføring i objektet, bør nøkkelen være den første skrifttypen i fontstakken, f.eks. 'Arial'. Det blir strengen en bruker passerer for å spesifisere skrifttypestakken de vil bruke, f.eks. fontstack ( "Arial") eller fontstack ("Times New Roman").

Verdien i hvert par skal være en streng av den fulle listen over skrifttyper som finnes i fontstakken, f.eks. 'Arial,' Helvetica Neue ', Helvetica, sans-serif'.

Legg til to oppføringer til din fontstacks_config objekt, en for 'Arial' og en for 'Times New Roman', ved hjelp av skriftstablene som tilbys av CSS Font Stack.

Din fontstacks_config variabel skal se slik ut:

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

Kontroller hvilken fontstack som kreves

Det første vi må gjøre når vi finner en forekomst av fontstack () blir brukt til å finne ut hvilken skrifttypestabel brukeren har bedt om, dvs. hvilken streng de har satt mellom parentesene. 

For eksempel, hvis en bruker angitt fontstack ( "Arial") vi ønsker å trekke ut strengen arial. Grunnen til at vi vil ha denne strengen er at den vil gi oss en nøkkel vi kan bruke til å slå opp den tilsvarende skriftstakken fra vår fontstacks_config gjenstand.

Legg til denne koden umiddelbart inne i hvis uttalelse vi la til tidligere, erstatte console.log ("funnet fontstack"); linje:

// Få navnet på fontstacken som er forespurt, ved å matche strengen inne i parentesen til fontstack (). // Deretter erstattes eventuelle doble eller enkle sitater deri. var fontstack_requested = value.match (/ \ (([^)] +) \) /) [1] .replace (/ ["'] / g," ");

Vi utfører to trinn her for å trekke ut skrifttypens navn som en streng.

Først bruker vi metoden match () for å finne hvilken streng som er mellom parentesene i vår verdi. Dette ville gi oss en streng som "Arial" eller 'Arial'.

Vi vil bare ha skrifttypenavnet, uten noen dobbelte eller enkle anførselstegn, så bruker vi erstatningen () -metoden for å fjerne dem fra strengen, og gir oss en unotert streng som arial.

Denne strengen er lagret i fontstack_requested variabel.

Tittel Saks den ønskede fontstakken

Vi skal bruke vår nyopprettede fontstack_requested variabel for å se opp en fontstabel fra vår fontstack_config alternativ. Den vanskelige delen er nøklene i dette objektet er saksfølsomme, så hvis vi prøver å slå opp arial inngang med nøkkelen arial det vil mislykkes.

For å løse dette, skal vi "Tittel Case" strengen, så for eksempel Times New Roman ville bli konvertert til Times New Roman. Vi gjør dette via en kort tilpasset funksjon.

Under din fontstacks_config variabel legg til dette toTitleCase () funksjon:

// Kreditt for denne funksjonen til http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript/196991#196991 funksjonen tilTitleCase (str) return str.replace (/ \ w \ S * / g, funksjon (txt) return txt.charAt (0) .toUpperCase () + txt.substr (1) .toLowerCase ();); 

Nå skal vi bruke denne funksjonen til vår fontstack_requested variabel. Under linjen der du opprettet fontstack_requested variabel, legg til denne koden:

// Tittel faller ordene i skrifttypenavnet, bare hvis brukeren ikke gjorde det selv fontstack_requested = toTitleCase (fontstack_requested);

Dette går forbi fontstack_requested variabel gjennom vår toTitleCase () funksjon, oppdatering av verdien.

Oppslag Fontstack Fra Config

Nå har vi vår fonstack_requested variabel satt riktig, kan vi bruke den til å slå opp den tilsvarende skrifttypestakken. Etter linjen du nettopp har lagt til, sett inn denne koden:

// Søk etter den ønskede fontstakken i fontstack_config objektet var fontstack = fontstacks_config [fontstack_requested];

Dette finner verdien i fontstacks_config objekt som har en nøkkel som samsvarer med strengen som finnes i vår fontstack_requested variabel. 

For eksempel, hvis fontstack_requested inneholder strengen arial, oppføringen i fontstacks_config med nøkkelen arial vil bli funnet og verdien 'Arial,' Helvetica Neue ', Helvetica, sans-serif' vil bli returnert.

Denne returnerte verdien lagres deretter i variabelen fontstack.

Kontroller om skrifttyper er satt før fontstack ()

Nå har vi vår skriftstabelstreng hentet og klar til å bli satt inn i CSS, men det er fortsatt en ting vi må gjøre. Du vil huske i vår testkode vi inkluderte skrifttypen "Open Sans" som den foretrukne skrifttypen, med skrifttypestakken som fungerer som en tilbakebetaling. Vi må også hente dette skriftnavnet fra verdien slik at den kan legges til CSS vi legger inn i det behandlede stilarket.

Under fontstack variabel linje, legg til denne koden:

// Finn og lagre eventuelle skrifttypenavn som allerede kan være i verdien, før fontstacken () samtale var first_font = value.substr (0, value.indexOf ('fontstack ('));

Denne koden bruker metoden substr () for å finne noe innhold mellom begynnelsen av vår verdi, (representert av 0), og vår fontstack () forekomst (lokalisert ved hjelp av indexOf () -metoden). Uansett innhold er funnet, lagres i variabelen first_font.

For eksempel, i vår testkode verdi er lik  "Open Sans", fontstack ("Arial"), så first_font variabel vil bli satt som  "Open Sans", .

Opprett en ny verdi

Vi har nå alle brikkene vi trenger for å skape en ny verdi som erstatter vår testkode sin opprinnelige verdi av  "Open Sans", fontstack ("Arial").

Etter den siste koden du la til, legg inn denne koden:

// Opprett den nye verdien for denne regelen ved å kombinere first_font og fontstack-variablene var new_value = first_font + fontstack;

Her kombinerer vi vår first_font og fontstack variabler i en enkelt streng og lagrer den i variabelen new_value

I vår testkode vil dette bety å kombinere  "Open Sans",  og Arial, "Helvetica Neue", Helvetica, sans-serif.

Våre new_value variabel vil da holde strengen  "Open Sans", "Arial," Helvetica Neue ", Helvetica, sans-serif '.

Dette gir oss nå den fullstendige verdien som vi ønsker å legge til i det behandlede stilarket slik at: 

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

... forvandles til:

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

Send den nye verdien tilbake til stilarket

Nå som vi har vår nye verdi klar til å bli satt inn i det behandlede stilarket, er alt vi trenger å oppdatere decl.value. PostCSS vil ta vare på resten, legge til den nye verdien i den behandlede CSS for oss.

Legg til denne koden etter siste linje du la til:

// Send den nye verdien tilbake til stilarket decl.value = new_value;

Dette setter decl.value å likne innholdet i vår new_value variabel.

Test pluggen din

Pluggen din er nå god å gå. Gi det en virvel ved å kompilere stilarket med gulp css eller grunt postcss (med din terminal peket på prosjektmappen din, ikke din plugin-mappe).

Din "dest / style.css" -fil skal nå vise en fullstendig skriftstabel:

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

(Valgfritt) Legg til brukerkonfigurerbare Fontstacks-alternativer

Du vil kanskje tillate brukere av pluginet ditt å angi egne alternativer, på samme måte som du har satt inn alternativer som du har brukt PostCSS-plugins gjennom hele denne serien.

Vi vil at brukerne skal kunne sette en fontstacks alternativ, enten du legger til ekstra skriftstabler eller omdefinerer eksisterende skriftstabler, for eksempel:

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

Merk: dette trinnet er valgfritt. Hvis du ønsker det, kan du hoppe over det, og pluginet ditt vil fungere perfekt, bare uten noen konfigurasjon av brukeren.

Vi har allerede den mest avgjørende delen av å gjøre det mulig for brukerinnstillingene på plass i plugin-modulen. I vår module.exports linje vil du legge merke til en opsjoner argumentet blir vedtatt. 

module.exports = postcss.plugin ('myplugin', funksjon (alternativer) 

Vi mottar alle brukeralternativer en bruker angir gjennom dette.

Du ser også at vi har linjen:

alternativer = alternativer || ;

Dette sjekker for å se om opsjoner har noen verdi, og hvis den ikke gjør det, setter den til en tom gjenstand. Dette sørger for at vi ikke får noen feil når vi begynner å jobbe med opsjoner Det kan komme fra at det er udefinert.

For å komme i gang, skal vi installere Underscore.js inn i vårt prosjekt, da vi bruker den praktiske extend () -metoden. Kjør denne kommandoen for å installere den i plugin du bygger:

npm installere understreking - lagre

Legg nå Underscore i plugin ved å legge til en _ variabel for å kreve det, under din eksisterende postcss variabel:

var postcss = krever ('postcss'); var _ = krever ('understreking');

Neste hva vi skal gjøre er å ta fontstacks_config objekt vi allerede opprettet i pluginet, og "forlenge" det med eventuelle fontstabler som brukeren har angitt gjennom opsjonskonfigurasjonen.

Legg til denne koden direkte under alternativer = alternativer || ; linje:

 // Utvid standard fontstacks_config-alternativet med noen egendefinerte fontstabler som er angitt i alternativene for plugin fontstacks_config = _.extend (fontstacks_config, options.fontstacks);

De fontstacks alternativet som er angitt av brukeren er representert av options.fontstacks.

Ved å bruke Underscore's forlenge() metode, alle font stablene i options.fontstacks legges til de som allerede er i fontstacks_config. Uansett hvor det er en matchende nøkkel, er verdien fra options.fontstacks vil overskrive den i fontstacks_config. Dette lar brukerne omdefinere eksisterende skriftstabler.

I din Gulpfile eller Gruntfile, sett a fontstacks alternativ og send en ny skriftstapel samt omdefinere en eksisterende:

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

Legg nå litt ekstra CSS i filen "src / style.css", så vi kan teste den nye skrifttypestakken vi nettopp har lagt til via våre alternativer:

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

Kompilér CSS-en din, og du bør se at din Arial-fontstapel nå har forskjellig utgang, og at fontstapelen for ekstra stack har riktig utskrift:

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

Ditt fullførte plugin

Det er det! Du er ferdig. Du har fullført ditt første PostCSS-plugin.

Her er hele greia på GitHub, må du sammenligne koden til den som referanse.

La oss gjenskape

Du har nettopp opprettet et helt PostCSS-plugin, og jeg håper noen ideer kommer til å tenke på andre plugins du vil gjerne gjøre. Kanskje det er en liten ting som alltid har bugget deg når du skriver CSS, og kanskje nå kan du komme med din egen løsning for å bli kvitt det for godt. Eller kanskje det er noe ekstra du tror CSS burde ha ut av boksen - vel, nå kan du legge det inn for deg selv!

For å oppsummere hva vi har dekket:

  • Begynn å utvikle en ny plugin ved å sette opp et Gulp eller Grunt-prosjekt for å jobbe i.
  • Opprett en ny nodemodul inne i prosjektet ditt, som blir din plugin.
  • Legg inn det nye pluginet i prosjektet ditt.
  • Legg til noen test CSS i syntaksen du vil ha pluginet ditt å bruke.
  • Bruk metoder fra PostCSS API til å skanne gjennom et stilark.
  • Finn forekomster av pluginens syntaks som brukes.
  • Skriv JavaScript og bruk PostCSS API for å gjøre de riktige transformasjonene (og / eller tilleggene) til den opprinnelige koden og send den inn i den behandlede CSS.

For TypeScript-brukere

Som en del av 5.0-utgivelsen av PostCSS, har Jed Mao bidratt med et flott sett med TypeScript-definisjoner som kan hjelpe mye med plugin-utvikling gjennom å gi autofullføring og inline-dokumentasjon mens du skriver.

Hvis du finner deg selv i PostCSS-pluginutvikling, er dette virkelig noe verdt å se på å inkorporere i arbeidsflyten din. Jeg er selv ikke en dab TypeScript-hånd, men kommer til å hoppe inn i kodingen med det uansett, nesten rent slik at jeg kan utnytte denne funksjonaliteten.

Hvis du vil prøve dette ut, trenger du ikke å være på Windows eller ved hjelp av Visual Studio, da du kan bruke den gratis, åpen kildekode Visual Studio-koden, som kjører på Windows, Mac og Linux, og er bygget på Electron , det samme skallet som driver Atom Editor.

For et eksempel på hvordan du innlemmer disse TypeScript-definisjonene i prosjektet, sjekk ut plugin-font-pack-plugin. Fork det og ha et spill i Visual Studio Code for å se hvordan autocompletion og inline dokumentasjon fungerer.

PostCSS Deep Dive: Wrapping Up

Tusen takk for at du følger med denne PostCSS Deep Dive-serien. Jeg håper du likte å lese det så mye som jeg likte å skape det! Enda viktigere, jeg håper du har et hode fullt av ideer om hvordan du kan sette PostCSS på jobb i ditt daglige webutviklingsliv.

PostCSS er virkelig et utrolig nytt tillegg til front-end-verdenen, slik måten det letter plugins åpner dører for muligheter vi aldri har hatt før i CSS-utvikling. Utvalget av plugins som er tilgjengelig akkurat nå, er allerede nok til å omforme en persons daglige arbeidsflyter helt, og det er bare med det som er opprettet i løpet av et par år. 

Jeg vil foreslå at PostCSS har ennå ikke topp, og som det begynner å være noe de fleste CSS utviklere i det minste vet om, om ikke sverger ved, ser vi det virkelig kommer inn i sin skritt. Og med flere fremtidsutviklere som kommer ombord, ser vi flere bidrag til plugin-økosystemet, legger til nye plugins og bidrar til å bygge opp eksisterende.

!