Automated Chrome extension Workflow with gulp

This time i revisit my chrome extension because i'm gonna create a new one. It still a work in progress, and i might update this post as i progress since there's a couple more gulp trick i like to try out. But the main focus here i wanted to automate my workflow from writing the source code in a single project folder to zipping it into an extension and ultimately upload it using gulp as well.I only got to the zipping, but haven't had the confident yet to automate the publishing part yet since the extension is still work in progress. The cons i find here is, there's a lot of package. But still using npm or yar we can get those in one single command. You can find all the package readme on npm or github.

Also worth to mention that i'm using gulp 4 and i din't use yeoman, so i create all the file and folder in the src folder manually including the manifest, and jsconfig. Everything inside build folder are create by gulp.

Btw, if you have question, need some help or feedback, don't hesitate to reach me on twitter.

The list of package:

  • gulp
  • autoprefixer
  • cssnano
  • gulp-htmlmin
  • gulp-postcss
  • gulp-rename
  • gulp-replace
  • gulp-sass
  • gulp-sourcemaps
  • gulp-terser-js
  • gulp-zip
  • yargs - this allow us to use arguments like gulp --production

Folder Structure

.
└── project_folder/
    ├── build/
    │   ├── debug/
    │   │   ├── img
    │   │   ├── html/
    │   │   │   ├── index.html
    │   │   │   ├── css/
    │   │   │   │   └── style.css
    │   │   │   └── js/
    │   │   │       ├── popup.js
    │   │   │       ├── content.js
    │   │   │       └── background.js
    │   │   └── my_extension/
    │   │       └── manifest.json
    │   └── release/
    │       └── my_extension.zip
    ├── src/
    │   ├── sass
    │   ├── html/
    │   │   └── popup.html
    │   └── js/
    │       ├── popup.js
    │       ├── content.js
    │       └── background.js
    ├── gulpfile.js
    ├── jsconfig.json
    ├── package.json
    ├── package-lock.json
    └── .gitignore

Steps

  1. mkdir <PROJECT_FOLDER>

  2. cd <PROJECT_FOLDER>

  3. npm init

  4. npm i -D - gulp autoprefixer gulp cssnano gulp-htmlmin gulp-postcss gulp-rename gulp-replace gulp-sass gulp-sourcemaps gulp-terser-js gulp-zip

  5. Write the gulp file

The gulpfile

const { src, dest, watch, series, parallel } = require('gulp');
const terser = require('gulp-terser-js');
const htmlmin = require('gulp-htmlmin');
const zip = require('gulp-zip');

const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const postcss = require('gulp-postcss');
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');

const argv = require('yargs').argv;

const appName = 'my_project_name';
const appVersion = '0.1.0';

const files = {
  jsPath: 'src/**/*.js',
  htmlPath: 'src/**/*.html',
  scssPath: 'src/scss/**/*.scss',
  imgPath: 'src/img/*.*'
}

const outputPath = {
  mainPath: 'build/debug',
  cssPath: 'build/debug/css',
  jsPath: 'build/debug',
  imgPath: 'build/debug/img',
}

function scssTask() {
  return src(files.scssPath)
    .pipe(sourcemaps.init())
    .pipe(sass())
    // .pipe(postcss([autoprefixer(), cssnano()]))
    .pipe(postcss([autoprefixer()]))
    .pipe(sourcemaps.write('.'))
    .pipe(dest(outputPath.cssPath));
};

function htmlTask() {
  return src(files.htmlPath)
    .pipe(htmlmin({ collapseWhitespace: true, removeComments: true, minifyCSS: true }))
    .pipe(dest(`${outputPath.mainPath}`));
};

function jsTask() {
  return src(files.jsPath)
    // .pipe(terser()) // uncomment when ready to minify
    .pipe(dest(`${outputPath.mainPath}`)); 
};

function manifestTask() {
  return src('src/*.*')
    .pipe(dest(`${outputPath.mainPath}`));
}

function imgTask() {
  return src(files.imgPath)
    .pipe(dest(`${outputPath.imgPath}`));
}

function zipTask() {
  return src(`${outputPath.mainPath}/**/*`)
    .pipe(zip(`${appName}_${appVersion}.zip`))
    .pipe(dest(`build/release`));
}

function uploadTask() {
  // TODO
}

function watchTask() {
  watch([files.scssPath, files.jsPath, files.htmlPath],
    parallel(scssTask, jsTask, htmlTask, manifestTask));
}


if (!(argv.production === undefined)) {
  // this only run whem we use `gulp --production`
  console.info('PRODUCTION_MODE');
  exports.default = series(parallel(scssTask, jsTask, htmlTask, imgTask), zipTask);
} else {
  // without argument
  console.info('DEBUG_MODE');
  exports.default = series(parallel(scssTask, jsTask, htmlTask, imgTask, manifestTask), watchTask);
}

References