SlideShare une entreprise Scribd logo
1  sur  67
Télécharger pour lire hors ligne
Advanced JavaScript
Build Pipelines
with Gulp.js
Let's talk a
short bit about
the JS build tool
revolution
HTML
CSS
JavaScript
Sass CoffeeScript LESS
P o s t C S S H A M L J a d e
U g l i f y E S 6 R e a c t J S
B r o w s e r i f y A n g u l a r J S
E m b e r C S S M i n J S L i n t
ESHint ImageOptim Mocha
Jasmine TypeScript
1530 lines of code
original Ant tasks used:
concat — copy — delete — mkdir
Maybe Java isn't the right
tool?
Java is to JavaScript what

Alf is to Gandalf
Grunt started a boom
Gruntfiles get long
Grunt tasks get slow:
lots of reads
and writes
One unmanageable horde
of (sometimes)
low quality plug-ins
And then came Gulp
React is in love with
Webpack
Ember.js digs Broccoli.js
Ember.js digs Broccoli.js
So… every community seems
to have its preference
Stick with your communities
preference!
For all the rest?
Gulp might be a good,
very good option
Disclaimer
I (occasionally) contribute to
Gulp
I'm writing a book on Gulp
http://bit.ly/gulp-tool-book
39% off with 39baumgar

coupon code!
gulp.src(…) gulp.dest(…)
Reads files Writes files
gulp.src(…) .pipe(uglify()) gulp.dest(…)
gulp.src(…) .pipe(uglify()) gulp.dest(…).pipe(concat())
task
manager
streaming
file
system
file watcher
gulp.task('scripts', function() {!
return gulp.src(‘src/**/*.js')!
.pipe(uglify())!
.pipe(concat('main.min.js'))!
.pipe(gulp.dest('dist'));!
});!
!
gulp.task('default', function(done) {!
gulp.watch(‘src/**/*.js', gulp.parallel(‘scripts'));!
done();!
});!
undertaker.task('scripts', function() {!
return vinyl.src(‘src/**/*.js')!
.pipe(uglify())!
.pipe(concat('main.min.js'))!
.pipe(vinyl.dest('dist'));!
});!
!
undertaker.task('default', function(done) {!
chokidar.watch(‘src/**/*.js', undertaker.parallel(‘scripts'));!
done();!
});!
so similar … yet so
different?
Gulp
Streams
Browserify
Streams
var source = require(‘vinyl-source-stream’);!
!
var b = browserify({!
entries: ['_js/main.js']!
});!
!
var bundle = function() {!
return b.bundle()!
.pipe(source(‘main.js’))!
.pipe(gulp.dest('js'));!
}!
!
gulp.task(‘bundle’, bundle);!
This also goes the
other way around…
var app = express();!
var router = express.Router();!
!
router.get('/*', function(req, res) {!
var stream = request('http://host.to.forward.to' + req.originalUrl);!
stream.pipe(res);!
stream!
.pipe(source('.' + req.originalUrl))!
.pipe(gulp.dest('./cachedir'));!
});!
!
app.use(express.static('_site'));!
app.use(express.static('cachedir'));!
app.use(router);!
var app = express();!
var router = express.Router();!
!
router.get('/*', function(req, res) {!
var stream = request('http://host.to.forward.to' + req.originalUrl);!
stream.pipe(res);!
stream!
.pipe(source('.' + req.originalUrl))!
.pipe(gulp.dest('./cachedir'));!
});!
!
app.use(express.static('_site'));!
app.use(express.static('cachedir'));!
app.use(router);!
So start thinking
streams …
Multiple input formats
return gulp.src(‘src/**/*.coffee)!
.pipe(coffee())!
.pipe(uglify())!
.pipe(concat(‘main.js’))!
.pipe(gulp.dest(‘build’))!
Multiple input formats
return gulp.src(‘src/**/*.js)!
.pipe(uglify())!
.pipe(concat(‘main.js’))!
.pipe(gulp.dest(‘build’))!
Multiple input formats
return gulp.src(‘src/**/*.js)!
.pipe(uglify())!
.pipe(concat(‘main.js’))!
.pipe(gulp.dest(‘build’))!
the same!
Multiple input formats
And actually, you just want
one bundle in the end
What if we could
reuse parts of the
stream?
return gulp.src(‘app1/src/**/*.coffee’)!
.pipe(coffee())!
.pipe(uglify()!
.pipe(concat(‘main.js’))!
.pipe(gulp.dest('build'));!
var merge = require(‘merge2’);!
!
return merge(gulp.src(‘app1/src/**/*.coffee’)!
.pipe(coffee()),!
gulp.src(‘app1/src/**/*.js’))!
.pipe(uglify()!
.pipe(concat(‘main.js’))!
.pipe(gulp.dest('build'));!
var merge = require(‘merge2’);!
!
return gulp.src(‘app1/src/**/*.coffee’)!
.pipe(markdown()),!
.pipe(gulp.src(‘app1/src/**/*.js’), {passthrough: true})!
.pipe(rename(blogFn))!
.pipe(wrap(layoutStr))!
.pipe(swig())!
.pipe(gulp.dest('build'));!
Multiple bundles
And now we need this
over and over again
for all our applications…
var elems = [!
{ dir: ‘app1’, bundleName: ‘app1.min.js’ },!
{ dir: ‘app2’, bundleName: ‘app2.min.js’ }!
];!
!
var streams = elems.map(function(el) {!
return gulp.src(el.dir + ‘src/**/*.coffee’)!
.pipe(coffee()),!
.pipe(gulp.src(el.dir + ‘src/**/*.js’), {passthrough: true})!
.pipe(uglify())!
.pipe(concat(el.bundleName))!
});!
return merge(streams)!
.pipe(gulp.dest('build'));!
Incremental
builds
Some tasks take long
gulp.src(‘scripts/*.js’)!
.pipe(uglify())!
.pipe(gulp.dest())!
.pipe(concat())!
Too much is going on!
Each change: Uglify all
the files?
filter files
that have changed
do performance
heavy operations
remember the
old files
and continue
with the other ops
gulp.task('scripts', function () {!
return gulp.src('app/scripts/**/*.js')!
.pipe(cached('ugly'))!
.pipe(uglify())!
.pipe(remember('ugly'))!
.pipe(concat('main.min.js'))!
.pipe(gulp.dest('dist/scripts'));!
});!
gulp.task('scripts', function () {!
return gulp.src('app/scripts/**/*.js')!
.pipe(cached('ugly'))!
.pipe(uglify())!
.pipe(remember('ugly'))!
.pipe(concat('main.min.js'))!
.pipe(gulp.dest('dist/scripts'));!
});!
we use the cache to
check if files have
changed
gulp.task('scripts', function () {!
return gulp.src('app/scripts/**/*.js')!
.pipe(cached('ugly'))!
.pipe(uglify())!
.pipe(remember('ugly'))!
.pipe(concat('main.min.js'))!
.pipe(gulp.dest('dist/scripts'));!
});!
once we are done,
we remember all
the other files
we stored in
the cache
gulp.task('lint', function () {!
return gulp.src(‘src/**/*.js’)!
.pipe(jshint())!
.pipe(jshint.reporter(‘default’))!
.pipe(jshint.reporter(‘fail’));!
});!
gulp.task('lint', function () {!
return gulp.src(‘src/**/*.js’, { since: gulp.lastRun(‘lint’) })!
.pipe(jshint())!
.pipe(jshint.reporter(‘default’))!
.pipe(jshint.reporter(‘fail’));!
});!
gulp.task('images', function () {!
return gulp.src(‘src/images/**/*.*’)!
!
.pipe(imagemin())!
.pipe(gulp.dest(‘dist’));!
});!
gulp.task('images', function () {!
return gulp.src(‘src/images/**/*.*’)!
.pipe(newer(‘dist’))!
.pipe(imagemin())!
.pipe(gulp.dest(‘dist’));!
});!
When architecting
Gulp build pipelines…
Separate the process
from the content
Think about how the
basic pipeline would
look like
Think about how
your data looks like
And then posh your pipes up
with stream tools that
change your data accordingly
@ddprrt
Material
Workshop files
https://github.com/frontend-tooling
https://github.com/frontend-tooling/sample-project-gulp
https://github.com/frontend-tooling/static-site-generator
http://fettblog.eu
http://speakerdeck.com/ddprrt
Reading Material
http://bit.ly/gulp-tool-book
39% off with 39baumgar

coupon code!

Contenu connexe

Tendances

CoffeeScript presentation
CoffeeScript presentationCoffeeScript presentation
CoffeeScript presentation
John Lynch
 
How to develop Jenkins plugin using to ruby and Jenkins.rb
How to develop Jenkins plugin using to ruby and Jenkins.rbHow to develop Jenkins plugin using to ruby and Jenkins.rb
How to develop Jenkins plugin using to ruby and Jenkins.rb
Hiroshi SHIBATA
 
Performance Optimization of Rails Applications
Performance Optimization of Rails ApplicationsPerformance Optimization of Rails Applications
Performance Optimization of Rails Applications
Serge Smetana
 
Rails Application Optimization Techniques & Tools
Rails Application Optimization Techniques & ToolsRails Application Optimization Techniques & Tools
Rails Application Optimization Techniques & Tools
guest05c09d
 
PDXPortland - Dockerize Django
PDXPortland - Dockerize DjangoPDXPortland - Dockerize Django
PDXPortland - Dockerize Django
Hannes Hapke
 
An Introduction to Go
An Introduction to GoAn Introduction to Go
An Introduction to Go
Cloudflare
 

Tendances (20)

CoffeeScript presentation
CoffeeScript presentationCoffeeScript presentation
CoffeeScript presentation
 
CPANTS: Kwalitative website and its tools
CPANTS: Kwalitative website and its toolsCPANTS: Kwalitative website and its tools
CPANTS: Kwalitative website and its tools
 
Middleware as Code with mruby
Middleware as Code with mrubyMiddleware as Code with mruby
Middleware as Code with mruby
 
Intro to-puppet
Intro to-puppetIntro to-puppet
Intro to-puppet
 
How to test code with mruby
How to test code with mrubyHow to test code with mruby
How to test code with mruby
 
20141210 rakuten techtalk
20141210 rakuten techtalk20141210 rakuten techtalk
20141210 rakuten techtalk
 
ZSH and RVM
ZSH and RVMZSH and RVM
ZSH and RVM
 
How to develop Jenkins plugin using to ruby and Jenkins.rb
How to develop Jenkins plugin using to ruby and Jenkins.rbHow to develop Jenkins plugin using to ruby and Jenkins.rb
How to develop Jenkins plugin using to ruby and Jenkins.rb
 
Middleware as Code with mruby
Middleware as Code with mrubyMiddleware as Code with mruby
Middleware as Code with mruby
 
Ansible not only for Dummies
Ansible not only for DummiesAnsible not only for Dummies
Ansible not only for Dummies
 
Performance Optimization of Rails Applications
Performance Optimization of Rails ApplicationsPerformance Optimization of Rails Applications
Performance Optimization of Rails Applications
 
Smalltalk on rubinius
Smalltalk on rubiniusSmalltalk on rubinius
Smalltalk on rubinius
 
Leave end-to-end testing to Capybara
Leave end-to-end testing to CapybaraLeave end-to-end testing to Capybara
Leave end-to-end testing to Capybara
 
Sinatra for REST services
Sinatra for REST servicesSinatra for REST services
Sinatra for REST services
 
Rails Application Optimization Techniques & Tools
Rails Application Optimization Techniques & ToolsRails Application Optimization Techniques & Tools
Rails Application Optimization Techniques & Tools
 
Ansible 202 - sysarmy
Ansible 202 - sysarmyAnsible 202 - sysarmy
Ansible 202 - sysarmy
 
PDXPortland - Dockerize Django
PDXPortland - Dockerize DjangoPDXPortland - Dockerize Django
PDXPortland - Dockerize Django
 
Automated reproducible images on openstack using vagrant and packer
Automated reproducible images on openstack using vagrant and packerAutomated reproducible images on openstack using vagrant and packer
Automated reproducible images on openstack using vagrant and packer
 
Painless Data Storage with MongoDB & Go
Painless Data Storage with MongoDB & Go Painless Data Storage with MongoDB & Go
Painless Data Storage with MongoDB & Go
 
An Introduction to Go
An Introduction to GoAn Introduction to Go
An Introduction to Go
 

Similaire à Advanced JavaScript build pipelines using Gulp.js

Node js presentation
Node js presentationNode js presentation
Node js presentation
martincabrera
 
Socket applications
Socket applicationsSocket applications
Socket applications
João Moura
 
CoffeeScript-Ruby-Tuesday
CoffeeScript-Ruby-TuesdayCoffeeScript-Ruby-Tuesday
CoffeeScript-Ruby-Tuesday
Eddie Kao
 

Similaire à Advanced JavaScript build pipelines using Gulp.js (20)

PSGI/Plack OSDC.TW
PSGI/Plack OSDC.TWPSGI/Plack OSDC.TW
PSGI/Plack OSDC.TW
 
Writing Bullet-Proof Javascript: By Using CoffeeScript
Writing Bullet-Proof Javascript: By Using CoffeeScriptWriting Bullet-Proof Javascript: By Using CoffeeScript
Writing Bullet-Proof Javascript: By Using CoffeeScript
 
Finding Restfulness - Madrid.rb April 2014
Finding Restfulness - Madrid.rb April 2014Finding Restfulness - Madrid.rb April 2014
Finding Restfulness - Madrid.rb April 2014
 
Strangers In The Night: Ruby, Rack y Sinatra - Herramientas potentes para con...
Strangers In The Night: Ruby, Rack y Sinatra - Herramientas potentes para con...Strangers In The Night: Ruby, Rack y Sinatra - Herramientas potentes para con...
Strangers In The Night: Ruby, Rack y Sinatra - Herramientas potentes para con...
 
FunctionalJS - May 2014 - Streams
FunctionalJS - May 2014 - StreamsFunctionalJS - May 2014 - Streams
FunctionalJS - May 2014 - Streams
 
Server side scripting smack down - Node.js vs PHP
Server side scripting smack down - Node.js vs PHPServer side scripting smack down - Node.js vs PHP
Server side scripting smack down - Node.js vs PHP
 
Node js presentation
Node js presentationNode js presentation
Node js presentation
 
Socket applications
Socket applicationsSocket applications
Socket applications
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
About Clack
About ClackAbout Clack
About Clack
 
Akka persistence == event sourcing in 30 minutes
Akka persistence == event sourcing in 30 minutesAkka persistence == event sourcing in 30 minutes
Akka persistence == event sourcing in 30 minutes
 
Web architecture - overview of techniques.
Web architecture - overview of  techniques.Web architecture - overview of  techniques.
Web architecture - overview of techniques.
 
Going crazy with Node.JS and CakePHP
Going crazy with Node.JS and CakePHPGoing crazy with Node.JS and CakePHP
Going crazy with Node.JS and CakePHP
 
WebCamp: Developer Day: Архитектура Web-приложений: обзор современных решений...
WebCamp: Developer Day: Архитектура Web-приложений: обзор современных решений...WebCamp: Developer Day: Архитектура Web-приложений: обзор современных решений...
WebCamp: Developer Day: Архитектура Web-приложений: обзор современных решений...
 
Coffee script throwdown
Coffee script throwdownCoffee script throwdown
Coffee script throwdown
 
Why I will never write JavaScript ever again*
Why I will never write JavaScript ever again*Why I will never write JavaScript ever again*
Why I will never write JavaScript ever again*
 
Rapid API Development with LoopBack/StrongLoop
Rapid API Development with LoopBack/StrongLoopRapid API Development with LoopBack/StrongLoop
Rapid API Development with LoopBack/StrongLoop
 
CoffeeScript-Ruby-Tuesday
CoffeeScript-Ruby-TuesdayCoffeeScript-Ruby-Tuesday
CoffeeScript-Ruby-Tuesday
 
Os Bowkett
Os BowkettOs Bowkett
Os Bowkett
 
Sinatra: прошлое, будущее и настоящее
Sinatra: прошлое, будущее и настоящееSinatra: прошлое, будущее и настоящее
Sinatra: прошлое, будущее и настоящее
 

Plus de Stefan Baumgartner

Plus de Stefan Baumgartner (12)

WASM! WASI! WAGI! WAT?
WASM! WASI! WAGI! WAT?WASM! WASI! WAGI! WAT?
WASM! WASI! WAGI! WAT?
 
Serverless Rust
Serverless RustServerless Rust
Serverless Rust
 
What TypeScript taught me about JavaScript
What TypeScript taught me about JavaScriptWhat TypeScript taught me about JavaScript
What TypeScript taught me about JavaScript
 
Real-world component libraries at scale
Real-world component libraries at scaleReal-world component libraries at scale
Real-world component libraries at scale
 
Jamstack on Azure
Jamstack on AzureJamstack on Azure
Jamstack on Azure
 
TypeScript's Type System
TypeScript's Type SystemTypeScript's Type System
TypeScript's Type System
 
The hero's journey
The hero's journeyThe hero's journey
The hero's journey
 
Sketchmine: Design Systems Tooling
Sketchmine: Design Systems ToolingSketchmine: Design Systems Tooling
Sketchmine: Design Systems Tooling
 
Automating UI development
Automating UI developmentAutomating UI development
Automating UI development
 
A hero's journey in JavaScript frameworks
A hero's journey in JavaScript frameworksA hero's journey in JavaScript frameworks
A hero's journey in JavaScript frameworks
 
[German] Ab mit dem Kopf!
[German] Ab mit dem Kopf![German] Ab mit dem Kopf!
[German] Ab mit dem Kopf!
 
Speed Index, explained!
Speed Index, explained!Speed Index, explained!
Speed Index, explained!
 

Dernier

Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
chiefasafspells
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 

Dernier (20)

VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
WSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaSWSO2CON 2024 Slides - Open Source to SaaS
WSO2CON 2024 Slides - Open Source to SaaS
 
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
WSO2CON 2024 - Navigating API Complexity: REST, GraphQL, gRPC, Websocket, Web...
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
WSO2CON 2024 - API Management Usage at La Poste and Its Impact on Business an...
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 

Advanced JavaScript build pipelines using Gulp.js