Giter VIP home page Giter VIP logo

gulp-auto-imports's Introduction

Gulp Auto Imports

Auto generate import-only files for any file type. SCSS, JS, Pug, whatever you want.

Supported Node version

Are you sick of having to manually manage files that are purely just a bunch of import statements?

Wouldn't it be awesome if Gulp could just look at your file system and manage this busy work for you?

That is where Gulp Auto Imports comes in. Gulp Auto Imports will automatically manage these import-only files for you giving you more time to worry about the important stuff.

Due to it's high level of customization, Gulp Auto Imports is able to generate any import file you can imagine. SCSS, JS, Pug, PHP, you name it, it can create an import file for it (assuming the language supports import functionality in some way).

Gulp Auto Imports also has the ability to remember the order that imports are declared in. If you have ever had any experience with glob-loading SCSS files, you will know the pain of trying to get alpha.scss to override styles in beta.scss. With Gulp Auto Imports, simply rearrange the import statements and you're done!

Note: if using Gulp Auto Imports with Sass files, it is best used in combination with Gulp Sass Glob, not as a complete replacement for it.

Contents

Before and after Gulp Auto Imports

SCSS Before

// main.scss (manually edited)

@import '../components/component-A/A.scss';
@import '../components/component-B/B.scss';
@import '../components/component-C/C.scss';
@import '../components/component-D/D.scss';

SCSS After

// main.scss (manually edited)

@import './auto-imports.scss';
// auto-imports.scss (auto-generated)
// retains the order that imports are declared in if edited manually

@import '../components/component-A/A.scss';
@import '../components/component-B/B.scss';
@import '../components/component-C/C.scss';
@import '../components/component-D/D.scss';

JS before

// main.js (manually edited)

import $ from 'jquery'

import A from '../components/component-A/A.js'
import B from '../components/component-B/B.js'
import C from '../components/component-C/C.js'
import D from '../components/component-D/D.js'

$(() => {
   A()
   B()
   C()
   D()
})

JS after

// main.js (manually edited)

import $ from 'jquery'

import autoImports from './auto-imports.js'

$(() => {
   autoImports()
})
// auto-imports.js (auto-generated)

import A from "../components/component-A/A.js";
import B from "../components/component-B/B.js";
import C from "../components/component-C/C.js";
import D from "../components/component-D/D.js";

export default function() {
  A();
  B();
  C();
  D();
})

Install

Install Gulp Auto Imports using the following command:

npm install gulp-auto-imports --save-dev

For my examples, I am assuming that the project folder structure looks like this:

[root]
|  source
|  |  components
|  |  |  [component-folders]
|  |  |  |  [component-name].js
|  |  |  |  [component-name].scss
|  |  js
|  |  |  auto-imports.js
|  |  |  main.js
|  |  scss
|  |  |  config
|  |  |  |  [scss-config-files]
|  |  |  auto-imports.scss
|  |  |  main.scss
|  build
|  |  assets
|  |  |  css
|  |  |  |  main.css
|  |  |  js
|  |  |  |  main.js
| gulpfile.js

Using the SCSS preset

Writing out all of the required settings manually for this plugin can be a bit tiresome. As of version 2.0.0 I've included some common preset settings that you can use as defaults. All you need to do for a basic SCSS set up is the following. You will need the gulp-sass plugin for this to fully work. npm i gulp-sass -D).

var gulp = require('gulp')
var autoImports = require('gulp-auto-imports')
var sass = require('gulp-sass')

// Preset SCSS gulp-auto-imports task
gulp.task('sass:load', function () {
   // Always relative to gulpfile.js even if this code is inside a folder
   var dest = 'source/scss'
   // Do not leave off the "return", it is vital!
   return (
      gulp
         .src('./source/components/**/*.scss')
         // Using the "scss" preset ("dest" must be provided here as well)
         .pipe(autoImports({ preset: 'scss', dest: dest }))
         .pipe(gulp.dest(dest))
   )
})

/************\
    Gulp 4
\************/

// Define a separate compile task
gulp.task('sass:compile', function () {
   return gulp
      .src('source/scss/main.scss')
      .pipe(sass())
      .pipe(gulp.dest('build/assets/css'))
})

// make "sass:load" run before "sass:compile" when "sass" is run
gulp.task('sass', gulp.series('sass:load', 'sass:compile'))

// Watch for changes
gulp.task('watch', function (done) {
   gulp.watch('source/**/*.scss', gulp.series('sass'))
   done()
})

/************\
    Gulp 3
\************/

// Make "sass" dependent on "sass:load"
gulp.task('sass', ['sass:load'], function () {
   return gulp
      .src('source/scss/main.scss')
      .pipe(sass())
      .pipe(gulp.dest('build/assets/css'))
})

// Watch for changes
gulp.task('watch', function () {
   gulp.watch('source/**/*.scss', ['sass'])
})

The scss preset will apply the following default settings:

// scss preset default settings
{
  format: `@import '$path';`,
  fileName: 'auto-imports.scss',
  retainOrder: true,
  header: `
// This file is generated by gulp-auto-imports.
// Save this file into source control.
// You may rearrange the order of the imports however you like.
// You may NOT make any other alterations to this file.
`
};

The output of the example 'sass:load' task will look something like this:

// This file is generated by gulp-auto-imports.
// Save this file into source control.
// You may rearrange the order of the imports however you like.
// You may NOT make any other alterations to this file.

@import '../components/A/A.scss';
@import '../components/B/B.scss';
@import '../components/C/C.scss';
@import '../components/D/D.scss';

Due to the retainOrder: true setting, you can rearrange the output. Gulp Auto Imports will preserve the order when it recompiles.

// Rearrange the output and Gulp Auto Imports will preserve it
// (Requires the `retainOrder` setting to be enabled)

@import '../components/D/D.scss';
@import '../components/A/A.scss';
@import '../components/C/C.scss';
@import '../components/B/B.scss';

If you add a new file to the system (eg. a ../components/A/A-two.scss file) gulp-auto-imports will aim to group it with the other files found in the same folder.

// gulp-auto-imports will aim to group new files with files found in the same folder

@import '../components/D/D.scss';
@import '../components/A/A.scss';
@import '../components/A/A-two.scss'; // New file added
@import '../components/C/C.scss';
@import '../components/B/B.scss';

Add this line to your main scss file to auto-loaded your component styles:

// Import the auto-imports.scss file from main.scss
@import './auto-imports.scss';

You can now auto-load all of you're component scss files while still retaining full control over CSS source order! ๐Ÿ˜ƒ

Using the automated Gulp task generator function

Making a single auto-imports file is ok using the standard method, however when you start making more than one auto-import file, the process can become quite cumbersome. This is why Gulp Auto Imports also comes with a task generator to make generating multiple auto-import files super easy.

For this example, we are going to generate three separate auto-import files. One for vars, one for mixins, and one for components.

Here is the full code:

var gulp = require('gulp')
var { createAutoImportTask } = require('gulp-auto-imports')

// Create a function for generating auto-import gulp tasks
const createScssImporterTask = sourceFolder =>
   createAutoImportTask({
      sourceFolder,
      // [optional] default = *all files*; Restrict imports to only target files with a specific extension name
      fileExtension: 'scss',
      // [optional] use this to resolve task name conflicts
      taskPrefix: 'compile',
      // [optional] default = true; will ignore the generated import file
      ignoreImporterFile: true,
      // Same settings object that you apply to the main gulp task
      importerSettings: {
         preset: 'scss',
         // Note: `dest` will default to the source folder unless you define it here
         // The generated output file will be ignored
      },
   })

// Destructure into separate importer and watcher tasks
const [scssVarsImporter, scssVarsImportWatcher] = createScssImporterTask(
   './source/scss/config/vars'
)
const [scssMixinsImporter, scssMixinsImportWatcher] = createScssImporterTask(
   './source/scss/config/mixins'
)
const [scssComponentsImporter, scssComponentsImportWatcher] =
   createScssImporterTask('./source/scss/config/components')

// Gulp 4
gulp.task(
   'scss-auto-imports',
   gulp.parallel(
      scssVarsImporter,
      scssVarsImportWatcher,
      scssMixinsImporter,
      scssMixinsImportWatcher,
      scssComponentsImporter,
      scssComponentsImportWatcher
   )
)

// Gulp 3
gulp.task('scss-auto-imports', [
   scssVarsImporter,
   scssVarsImportWatcher,
   scssMixinsImporter,
   scssMixinsImportWatcher,
   scssComponentsImporter,
   scssComponentsImportWatcher,
])

If you don't want to write out all the task variables, you can also use destructuring as a shortcut.

/**
 * Use array destructuring as a shortcut
 */

// Gulp 4
gulp.task(
   'scss-auto-imports',
   gulp.parallel(
      ...createScssImporterTask('./source/scss/config/vars'),
      ...createScssImporterTask('./source/scss/config/mixins'),
      ...createScssImporterTask('./source/scss/config/mixins')
   )
)

// Gulp 3
gulp.task('scss-auto-imports', [
   ...createScssImporterTask('./source/scss/config/vars'),
   ...createScssImporterTask('./source/scss/config/mixins'),
   ...createScssImporterTask('./source/scss/config/mixins'),
])

If you don't want to have gulp watch the files, simply don't pass the watcher into your gulp process.

/**
 * Avoid watching files by not passing the generated watcher task into your main gulp process
 */

// Destructure to extract only the build task
const [scssVarsImporter] = createScssImporterTask('./source/scss/config/vars')
const [scssMixinsImporter] = createScssImporterTask(
   './source/scss/config/mixins'
)
const [scssComponetsImporter] = createScssImporterTask(
   './source/scss/config/mixins'
)

// Gulp 4 (no watching of files)
gulp.task(
   'scss-auto-imports',
   gulp.parallel(scssVarsImporter, scssMixinsImporter, scssComponetsImporter)
)

// Gulp 3 (no watching of files)
gulp.task('scss-auto-imports', [
   scssVarsImporter,
   scssMixinsImporter,
   scssComponetsImporter,
])

The values that createAutoImportTask returns are two task names in the format demonstrated below:

const taskNames = createAutoImportTask({
   sourceFolder: './path/to/sourceFolder',
   fileExtension: 'fileExtension',
   taskPrefix: 'taskPrefix',
   importerSettings: {
      preset: 'scss',
   },
})

taskNames ===
   [
      'taskPrefix:fileExtension:auto-imports:sourceFolder',
      'taskPrefix:fileExtension:auto-imports-watcher:sourceFolder',
   ]

More details about the createAutoImportTask function are documented in the createAutoImportTask.types.ts file.

Use in combination with Gulp Sass Glob

If you are using Gulp Auto Imports to load scss files, it works best as a method used for loading your component files since those tend to require a specific order to work correctly.

Most of the time, your config files (files that hold nothing but Sass variables) and helper files (mixins, utility classes etc.) don't need to retain their order. It is both easier and cleaner to use Gulp Sass Glob to auto-load these types of files than it is to set up individual Gulp Auto Imports tasks for each of them.

npm install gulp-sass-glob --save-dev

Gulp 3 combination

// Using Gulp Auto Imports in combination with Gulp Sass Glob in Gulp 3

var gulp = require('gulp')
var autoImports = require('gulp-auto-imports')
var sass = require('gulp-sass')
var sassGlob = require('gulp-sass-glob')

// Gulp Auto Imports task
gulp.task('sass:load', function () {
   var dest = 'source/scss'
   return gulp
      .src('./source/components/**/*.scss')
      .pipe(autoImports({ preset: 'scss', dest: dest }))
      .pipe(gulp.dest(dest))
})

// Sass compile task (depends on 'sass:load' task)
gulp.task('sass', ['sass:load'], function () {
   return gulp
      .src('source/scss/main.scss')
      .pipe(sassGlob()) // Sass Glob
      .pipe(sass())
      .pipe(gulp.dest('build/assets/css'))
})

Gulp 4 combination

// Using Gulp Auto Imports in combination with Gulp Sass Glob in Gulp 4

var gulp = require('gulp')
var autoImports = require('gulp-auto-imports')
var sass = require('gulp-sass')
var sassGlob = require('gulp-sass-glob')

// Gulp Auto Imports task
gulp.task('sass:load', function () {
   var dest = 'source/scss'
   return gulp
      .src('./source/components/**/*.scss')
      .pipe(autoImports({ preset: 'scss', dest: dest }))
      .pipe(gulp.dest(dest))
})

// Sass compile task
gulp.task('sass:compile', function () {
   return gulp
      .src('source/scss/main.scss')
      .pipe(sassGlob()) // Sass Glob
      .pipe(sass())
      .pipe(gulp.dest('build/assets/css'))
})

// Combined sass compile task
gulp.task('sass', gulp.series('sass:load', 'sass:compile'))

Using a combination inside main.scss

/**********************\
    main.scss file
\**********************/

// Use Gulp Sass Glob to load config and helper files
@import 'vars/**/*.scss';
@import 'mixins/**/*.scss';

// Use the output from Gulp Auto Imports to load component files
@import 'auto-imports.scss';
/***************************\
    auto-imports.scss file
  (automatically generated)
\***************************/

@import '../components/one/one.scss';
@import '../components/two/two.scss';
@import '../components/three/three.scss';
@import '../components/four/four.scss';

All available presets

es6
Import a set of functions using ES6 import syntax and then call them on page load (import fileName from '../relative/path/fileName.js'; then after DOM load: fileName()).
es6_default_exports
Import a bunch of ES6 JavaScript default exports and then export them all from one file (export { default as fileName } from '../relative/path/fileName.js').
es6_named_exports
Import a bunch of ES6 JavaScript named exports and then export them all from one file (export { fileName } from '../relative/path/fileName.js').
es5
Import a set of functions using require() and then call them on page load (var fileName = require('../relative/path/fileName.js'); then after DOM load: fileName()).
es5_default_exports
Import a bunch of CommonJS default exports and then export them all from one file (exports.fileName = require('../relative/path/fileName.js')).
es5_named_exports
Import a bunch of CommonJS named exports and then export them all from one file (exports.fileName = require('../relative/path/fileName.js').fileName).
ts
Import a set of functions using TypeScript import syntax and then call them on page load (import fileName from '../relative/path/fileName'; then after DOM load: fileName()).
ts_default_exports
Import a bunch of TypeScript default exports and then export them all from one file (export { default as fileName } from '../relative/path/fileName').
ts_named_exports
Import a bunch of TypeScript named exports and then export them all from one file (export { fileName } from '../relative/path/fileName').
pug
Intended for use with builds that use Pug as the templating language (include ../relative/path/fileName.pug).
jade
For use on projects that haven't upgraded their old Jade powered projects to Pug yet (include ../relative/path/fileName.jade).
scss
Sass import statements that use the newer SCSS style syntax (@import '../relative/path/fileName.scss';).
sass
Sass import statements that use the older indented style syntax (@import ../relative/path/fileName.sass).
stylus
Intended for use with the Stylus CSS generation language (@import '../relative/path/fileName.styl').

You can browse the available presets and see what their settings look like in the presets folder. The presets are named after the file names in that folder.

If you would like other presets to be added, you can log an issue to request for a new preset to be added, or you can make a pull request to add one yourself.

Overriding a preset

The preset setting just tells Gulp Auto Imports what to use as the default settings.

You can override any of the preset settings by providing your own alternative setting. For example, to change the output file name, you can do this:

// Overriding the default preset setting for "fileName"
.pipe(autoImports({ preset: 'es6', fileName: 'different-file-name.js', dest: 'path/to/dest' }))

JS configuration examples

Adding this functionality to your JS compiler can be tricky since JS compilers generally don't run off typical gulp functionality for performance reasons.

Rollup

Rollup has a pretty straight forward integration. It is very similar to the Sass set up. It gets around the performance issues by allowing you to cache the last bundle that was generated.

Running gulp start will generate the auto imports, compile the JS, and then start watching files for changes.

(Rollup by default does not bundle CommonJS require() statements).

Rollup in Gulp 4

'use strict';

// Import the auto imports plugin
var autoImports = require('gulp-auto-imports');

var gulp = require('gulp');
var rollup = require('@rollup/stream');
var sourcemaps = require('gulp-sourcemaps');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');

gulp.task('js:load', function(){
  const dest = './source/js';
  return gulp.src([
    './source/components/**/*.js',
    // exclude files and folders starting with an underscore
    '!./source/components/{**/\_*,**/\_*/**}',
  ])
    // Run the auto imports
    .pipe(autoImports({ preset: 'es6', dest }))
    .pipe(gulp.dest(dest));
})

var cache;
gulp.task('js:compile', function() {
  return rollup({
      // point to the entry file.
      input: './source/js/main.js',

      // use cache for better performance
      cache: cache,

      // Note: these options are placed at the root level in older versions of Rollup
      output: {
        // Output bundle is intended for use in browsers
        // (iife = "Immediately Invoked Function Expression")
        format: 'iife',

        // Show source code when debugging in browser
        sourcemap: true
      }
    })
    .on('bundle', function(bundle) {
      // update cache data after every bundle is created
      cache = bundle;
    })
    // point to the entry file.
    .pipe(source('main.js', './source/js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(sourcemaps.write('.'));

    .pipe(gulp.dest('./build/assets/js'));
});

gulp.task('js', gulp.series('js:load', 'js:compile'));

gulp.task('js:watch', function(done){
  gulp.watch(['./source/**/*.js'], gulp.series('js'));
  done();
})

gulp.task('start', gulp.series('js', 'js:watch'));

Rollup in Gulp 3

'use strict'

// Import the auto imports plugin
var autoImports = require('gulp-auto-imports')

var gulp = require('gulp')
var rollup = require('@rollup/stream')
var sourcemaps = require('gulp-sourcemaps')
var source = require('vinyl-source-stream')
var buffer = require('vinyl-buffer')

gulp.task('js:load', function () {
   const dest = './source/js'
   return (
      gulp
         .src([
            './source/components/**/*.js',
            // exclude files and folders starting with an underscore
            '!./source/components/{**/_*,**/_*/**}',
         ])
         // Run the auto imports
         .pipe(autoImports({ preset: 'es6', dest }))
         .pipe(gulp.dest(dest))
   )
})

var cache
gulp.task('js', ['js:load'], function () {
   return (
      rollup({
         // point to the entry file.
         input: './source/js/main.js',

         // use cache for better performance
         cache: cache,

         // Note: these options are placed at the root level in older versions of Rollup
         output: {
            // Output bundle is intended for use in browsers
            // (iife = "Immediately Invoked Function Expression")
            format: 'iife',

            // Show source code when debugging in browser
            sourcemap: true,
         },
      })
         .on('bundle', function (bundle) {
            cache = bundle
         })
         // point to the entry file.
         .pipe(source('main.js', './source/js'))
         .pipe(buffer())
         .pipe(sourcemaps.init({ loadMaps: true }))
         .pipe(sourcemaps.write('.'))
         .pipe(gulp.dest('./build/assets/js'))
   )
})

gulp.task('js:watch', function () {
   gulp.watch(['./source/**/*.js'], ['js'])
})

gulp.task('start', ['js', 'js:watch'])

Browserify

Below is a modified version of the of the Gulp browserify + watchify recipe that has Gulp Auto Imports installed.

Browserify gets around performance issues by using a special "Watchify" JS library instead of gulp.watch(). You specify an entry file. Watchify watches for any changes to files that are imported from that entry file. It will also watch for changes to files imported from those imported files (and so on and so on forever).

The code below will generate a new auto-imports.js file when it detects a JS file has been added to or removed from the components folder. The auto-generated auto-imports.js file is imported into the main entry js file meaning Watchify is watching it for changes. When the new auto-imports.js file is generated, Watchify detects that the file has changed. This triggers Watchify to initiate a Browserify rebundle.

Most of the code below works in both both Gulp 3 and Gulp 4.

'use strict'

// Import the auto imports plugin
var autoImports = require('gulp-auto-imports')

var watchify = require('watchify')
var browserify = require('browserify')
var gulp = require('gulp')
var source = require('vinyl-source-stream')
var buffer = require('vinyl-buffer')
var log = require('gulplog')
var sourcemaps = require('gulp-sourcemaps')

var customOpts = {
   // entry file defined here
   entries: ['./source/js/main.js'],
   debug: true,
}
var opts = Object.assign({}, watchify.args, customOpts)

// Watch for changes then bundle
var b = watchify(browserify(opts))

b.on('update', bundle) // on any dep update, runs the bundler
b.on('log', log.info) // output build logs to terminal

function bundle() {
   // Then bundle the code
   return b
      .bundle()
      .on('error', log.error.bind(log, 'Browserify Error'))
      .pipe(source('main.js'))
      .pipe(buffer())
      .pipe(sourcemaps.init({ loadMaps: true }))
      .pipe(sourcemaps.write('./'))
      .pipe(gulp.dest('./build/assets/js'))
}

// File loader Gulp task
gulp.task('js:load', function () {
   const dest = './source/js'
   return (
      gulp
         .src([
            './source/components/**/*.js',
            // exclude files and folders starting with an underscore
            '!./source/components/{**/_*,**/_*/**}',
         ])
         // Run the auto imports
         .pipe(autoImports({ preset: 'es5', dest }))
         .pipe(gulp.dest(dest))
   )
})

///////////////////////////
// Gulp 3 specific code //
/////////////////////////
gulp.task('js', ['js:load'], bundle) // so you can run `gulp js` to build the file
gulp.task('watch', function () {
   // Gulp 3 can't distinguish between 'add','unlink', and 'change' events
   // so it also has to run on file changes
   gulp.watch('./source/components/**/*.js', ['js:load'])
})

///////////////////////////
// Gulp 4 specific code //
/////////////////////////
gulp.task('js', gulp.series('js:load', bundle)) // so you can run `gulp js` to build the file
gulp.task('watch', function (done) {
   var watcher = gulp.watch('./source/components/**/*.js')
   //Gulp 4 has the advantage of only running when a file is added/removed, not changed
   watcher.on('add', gulp.series('js:load'))
   watcher.on('unlink', gulp.series('js:load'))
   done()
})

Making use of the generated JS file

Now that Gulp is set up to build a auto-imports JS file for you, import your generated file from main.js and call it as a function.

// Import auto-imports.js inside main.js
import autoImports from './auto-imports.js' // ES6
var autoImports = require('./auto-imports.js') // ES5

document.addEventListener('DOMContentLoaded', function () {
   // Run the auto-imports code on page load
   autoImports()
})

Note that a typical component js file will need to export a function by default for this configuration to work.

// component js file example

/////////
// ES6 //
/////////
export default function on_page_load() {
   // Place code here that you wish to run
   // when the `autoImports()` function is called
}

/////////
// ES5 //
/////////
module.exports = function on_page_load() {
   // Place code here that you wish to run
   // when the `autoImports()` function is called
}

How to do custom configurations

Now that you know how to use presets, lets replicate some of these presets manually to show you how to use the plugin if the files you wish to load are not available as a preset.

Manual SCSS set up

I'll use SCSS as an example first because it is both simple and popular.

Create a gulp task that looks like this:

// Typical SCSS gulp-auto-imports task

var gulp = require('gulp')
var autoImports = require('gulp-auto-imports')

gulp.task('sass:load', function () {
   // Always relative to gulpfile.js even if this code is inside a folder
   var dest = 'source/scss'

   // Do not leave off the "return", it is vital!
   return gulp
      .src([
         // These paths are always relative to gulpfile.js
         './source/components/**/*.scss',
         // Ignore files & folders that start with underscores
         '!./source/{**/_*,**/_*/**}',
      ])
      .pipe(
         autoImports({
            // "$path" is replaced with a relative file path
            format: '@import "$path";',
            // destination folder (must match gulp.dest)
            dest: dest,
            // name of the output file
            fileName: 'auto-imports.scss',
            // Don't change the order that imports are currently in
            retainOrder: true,
            // Add a message to the top of the file
            header: '// output from gulp-auto-imports',
         })
      )
      .pipe(gulp.dest(dest))
})

The output of this Gulp task will look something like this:

// output from gulp-auto-imports
@import '../components/A/A.scss';
@import '../components/B/B.scss';
@import '../components/C/C.scss';
@import '../components/D/D.scss';

Manual JS set up

JS is slightly more complicated.

// Typical JS gulp-auto-imports task

var gulp = require('gulp')
var autoImports = require('gulp-auto-imports')

// Use an ES6 template literal for defining the template
// Node has supported them natively ever since v4.0.0
var template = `
$format[imports]

export default function(){
$format[functions]
}
`

gulp.task('js:load', function () {
   var dest = 'source/js'

   return gulp
      .src([
         './source/components/**/*.js',
         // Ignore files & folders that start with underscores
         '!./source/{**/_*,**/_*/**}',
      ])
      .pipe(
         autoImports({
            // Format is now split into an object holding named format strings
            format: {
               // "$name" is replaced with the name of the file
               // "$path" is replaced with a relative path to the file
               imports: 'import $name from "$path";',
               // The indent is added here, not in the template
               functions: '  $name();',
            },
            dest: dest,
            fileName: 'auto-imports.js',
            template: template,
         })
      )
      .pipe(gulp.dest(dest))
})

The output from this task will look something like this:

// Generated auto-imports.js file

import one from '../components/one/one.js'
import two from '../components/two/two.js'
import three from '../components/three/three.js'
import four from '../components/four/four.js'

export default function () {
   one()
   two()
   three()
   four()
}

Understanding the format and template settings

It is recommended that you use an ES6 Template Literal (the back tick style strings) for creating the template rather than regular strings. Template Literals will allow you to define the markup found inside the output file exactly as written in a single string. Regular strings don't accept new lines so it makes writing the template much more difficult.

Remember that Template Literal's count all white space literally, so any white space you add to the template will appear in the final output. To avoid an odd looking output file, save the template to a template variable outside of the gulp task so that there is no indentation to the side of it.

The template works by replacing each $format[formatName] statement with a full list of imports formated in the specified way provided in the format object.

If the format setting is provided as a string, the template setting is ignored. If format is provided as an object, the template setting is required.

The $name placeholder

The $name placeholder in the format setting is replaced with the file name of the file. Any non-alphabetic and non-numeric characters are converted to underscores to prevent possible syntax errors.

./folder/path/gulp-auto-imports-is-awesome!123.js

 === converts to the $name ===

gulp_auto_imports_is_awesome_123

If there are duplicate file names, a number is added to the end of the name based on how many duplicates it has found to ensure that each name is unique.

./folder/one/thing.js
./folder/two/thing.js

 === converts to the $name ===

thing
thing_1

The $name placeholder is excellent for use cases where you need to assign an import path to a variable name.

You can use the $name placeholder as much as you like. That includes having the $name placeholder appear multiple times in a single format rule. The $name will always refer to the same import path.

The $fileName placeholder

The $fileName placeholder in the format setting holds the file name (excluding the extension) exactly as it is written.

// Assuming the full path is "./path/to/file-name.ext"
$fileName = file-name

The $ext placeholder

The $ext placeholder in the format setting holds the file extension.

// Assuming the full path is "./path/to/file-name.ext"
$ext = ext

Note that you can get the full file name (including extension) by using this pattern in your format setting:

// Assuming the full path is "./path/to/file-name.ext"
$fileName.$ext = file-name.ext

The $path placeholder

The $path placeholder in the format setting is replaced with a relative path that goes from the auto-imports output file to the file that is being loaded in.

$path = ./path/to/file-name.ext

Note 1: only one out of $path, $noExtPath, and $dir can be declared in a single format rule and it can only be declared once.

Note 2: The retainOrder: true setting only works with unaltered $path placeholders.

The $noExtPath placeholder

The $noExtPath placeholder in the format setting is exactly the same as $path except it will not add the file extension to the end.

$noExtPath = ./path/to/file-name

Note 1: only one out of $path, $noExtPath, and $dir can be declared in a single format rule and it can only be declared once.

Note 2: The retainOrder: true setting is not compatible with the $noExtPath placeholder.

The $dir placeholder

The $dir placeholder in the format setting stands for "directory" and is essentially the same as $path except it does not include the file name or the extension.

// Assuming the full path is "./path/to/file-name.ext"
$dir = ./path/to

Note 1: only one out of $path, $noExtPath, and $dir can be declared in a single format rule and it can only be declared once.

Note 2: The retainOrder: true setting is not compatible with the $dir placeholder.

Using indents

If you want indenting, the indenting should be added through the format setting not the template setting. If you indent the template, only the first item in the list will be indented. The rest will press hard up against the edge of the page.

For example, if you use this as your template:

// How NOT to indent your template

var template = `
$format[imports]

export default function(){
    // Notice the indent here
    $format[functions]
}
`

You will end up with a JS file that looks like this:

// The result of incorrect indentation

import one from '../components/one/one.js'
import two from '../components/two/two.js'
import three from '../components/three/three.js'
import four from '../components/four/four.js'

export default function () {
   // Notice the indent here
   one()
   two()
   three()
   four()
}

Instead, apply indentation through the format setting:

//How to apply correct indentation

var template = `
$format[imports]

export default function(){
    // Notice the indent here
$format[functions]
}
`;

// ... other Gulp code ...

.pipe(autoImports({
  format: {
    imports: 'import $name from "$path";',
    // The indent is added here, not in the template
    functions: '    $name();'
  },
  dest: dest,
  fileName: 'auto-imports.js',
  template: template
}))

That will produce the desired output:

// The result of correct indenting

import one from '../components/one/one.js'
import two from '../components/two/two.js'
import three from '../components/three/three.js'
import four from '../components/four/four.js'

export default function () {
   // Notice the indent here
   one()
   two()
   three()
   four()
}

The retainOrder setting

I briefly touched on the retainOrder setting earlier, however there is a bit more to know about it.

In CSS, the order that styles are written in matters significantly. It is important that you are able to alter the order that files are loaded in if you wish to have full control over your CSS specificity.

Other globing methods (eg. @import "../path/to/components/**/*.scss";) do not give you the ability to alter the order that the files are loaded in. You are generally restricted to loading files in alphabetical order. Gulp Auto Imports gives you back the ability to control the order that your CSS loads in with it's retainOrder setting (introduced in v2.0.0).

By default retainOrder is set to false. When retainOrder is set to true, Gulp Auto Imports will not alter the order of the existing $path placeholder import paths if you manually edit them yourself. Make sure that if you enable the retainOrder setting you save the output file into source control. This will ensure that your co-workers don't end up with a CSS file that is in a different order to yours.

Gulp Auto Imports will still delete old files from the list that don't exist any more.

If it detects that a new file is added to the system, Gulp Auto Imports will aim to keep that new file grouped with other files found in the same folder. (Prior to v2.1.0 it just dumped it at the bottom of the file). This means that new scss config file imports will be placed at the top of the auto-imports file with the other config files. This gives all your component files access to the new config settings without you having to make any alterations to the imports file.

It will not retain any comments or other alterations to the file. It will only retain the order that the imports were announced in.

Note: The retainOrder: true setting only works if you use an unaltered $path placeholder. $noExtPath, and $dir are not supported.

Settings reference guide

The settings documentation can now be found in the index.d.ts file.

Change Log

The Change log can be viewed on the Gulp Auto Imports GitHub releases page.

gulp-auto-imports's People

Contributors

dan503 avatar dependabot[bot] avatar

Stargazers

 avatar

Watchers

 avatar

gulp-auto-imports's Issues

Add preset settings

Things like SCSS and JS are extremely common use cases for this plugin so they should have short cuts that make them easier to set up.

This is a pretty easy feature to add but I'm not going to add it until the "Provide the ability to re-order imports" feature is added.

If I add this feature before that one is done then when I finish that feature, it will require a breaking change to integrate it into the typical SCSS workflow.

Provide the ability to re-order imports

In CSS the order that styles are written in matters significantly.

Currently, if you use gulp-file-loader to load scss files. You have no easy way of adjusting the order that the modules are loaded in. This is a pretty big problem for a language where order is so critically important.

Add the ability to write custom headers and footers for files

Headers and footers are useful for giving instructions to team mates about the generated files.

This can be useful for things like telling team mates not to edit the file, that the file is generated gy gulp-file-loader, or informing them that they can rearrange the order of the imports but can't make any other edits.

Clean Import Paths

Hi, Thanks for the plugin! it's awesome ๐Ÿ‘ I'm implementing it and I use sass-lint and one the rules I like to have is Clean Import Paths, I wasn't able to configure this so it prints only the name of the file without extension and leading underscore for partials.

So unless I'm failing real hard here I was wondering if you could implement this in the future? It sure will be nice to be able to customize the "format" setting for teams/personal preferences on code formatting.

Add support for absolute paths

Some tasks require relative paths and some tasks require absolute paths.

Absolute paths aren't really necessary for the purpose I built the plugin for. It's feasible that someone might want to use this plugin for generating things like batch files which require absolute paths.

change default output file header based on retainOrder setting

If the retainOrder setting is used, it should use the following file header by default:

retainOrder: true

// This file is generated by gulp-file-loader.
// Save this file into source control.
// You may rearrange the order of each line however you like.
// You may NOT make any other alterations to this file.

retainOrder: false

// This file is generated by gulp-file-loader.
// Do not edit this file.
// Do not save this file into source control.

Console output not outputting the correct folder paths

This is a note to self issue of a bug I found.

Gulp file portion

var sassDest = local_theme_folder+'/sass';

function load_sass(folder) {
	var dest = sassDest+'/'+folder;
	return gulp.src(local_theme_folder+'/sass/'+folder+'/**/*.scss')
		.pipe(fileLoader({ preset: 'scss', dest, fileName: '__includes.scss' }))
		.pipe(gulp.dest(dest))
}

var scssFolders = ['variables', 'mixins', 'components', 'pages'];
var scssLoadTasks = scssFolders.map(folderName => 'load:scss:'+folderName);

scssFolders.forEach((folderName, i) => {
	gulp.task(scssLoadTasks[i], function(){
		return load_sass(folderName)
	})
})

gulp.task('load:scss', scssLoadTasks);

console output

[10:03:30] Starting 'load:scss:variables'...
[10:03:30] Starting 'load:scss:mixins'...
[10:03:30] Starting 'load:scss:components'...
[10:03:30] Starting 'load:scss:pages'...
gulp-file-loader: Generated ../govcms/sites/all/themes/ipaustralia/sass/pages/__includes.scss
[10:03:30] Finished 'load:scss:variables' after 49 ms
gulp-file-loader: Generated ../govcms/sites/all/themes/ipaustralia/sass/pages/__includes.scss
gulp-file-loader: Generated ../govcms/sites/all/themes/ipaustralia/sass/pages/__includes.scss
[10:03:30] Finished 'load:scss:pages' after 63 ms
[10:03:30] Finished 'load:scss:mixins' after 67 ms
gulp-file-loader: Generated ../govcms/sites/all/themes/ipaustralia/sass/pages/__includes.scss
[10:03:30] Finished 'load:scss:components' after 69 ms
[10:03:30] Starting 'load:scss'...
[10:03:30] Finished 'load:scss' after 33 ฮผs

Take folder order preference into account when order retention is enabled

With a gulp task like this:

gulp.task('load:scss', function () {
    var dest = local_theme_folder+'/sass';
    return gulp.src([
        local_theme_folder+'/sass/variables/**/*.scss',
        local_theme_folder+'/sass/mixins/**/*.scss',
        local_theme_folder+'/sass/components/**/*.scss',
        local_theme_folder+'/sass/pages/**/*.scss',
    ])
        .pipe(fileLoader({ preset: 'scss', dest: dest, fileName: '__includes.scss' }))
        .pipe(gulp.dest(dest))

The user wants the variables import to always be placed toward the top of the import file.

Currently any new variable files that are added are added to the bottom of the file which is annoying because you need to manually move them into position every time.

Only run file generation if circumstances change

At the moment gulp-file-generator will run every time it is called no matter what, even if the file it generates is no different to what it was before.

It would be good if it can do some sort of check to see if it is worth regenerating the file. This will help improve performance.

I'm thinking that it should compare the new list of imports to an old list of imports. If they match then it is not worth regenerating the file.

ignore outlier files when assessing order retention grouping

If you have a starting scss file that looks like this:

@import "./config/A.scss";
@import "./config/B.scss";
@import "./config/C.scss";
@import "./config/D.scss";
@import "./component/A.scss";
@import "./component/B.scss";
@import "./component/C.scss";
@import "./config/E.scss";

And you add a ./config/F.scss file, it should go here:

@import "./config/A.scss";
@import "./config/B.scss";
@import "./config/C.scss";
@import "./config/D.scss";
@import "./config/F.scss"; /* New config file */
@import "./component/A.scss";
@import "./component/B.scss";
@import "./component/C.scss";
@import "./config/E.scss";

It currently goes here instead:

@import "./config/A.scss";
@import "./config/B.scss";
@import "./config/C.scss";
@import "./config/D.scss";
@import "./component/A.scss";
@import "./component/B.scss";
@import "./component/C.scss";
@import "./config/E.scss";
@import "./config/F.scss"; /* New config file */

Add support for back-slash paths

Some systems require the use of back-slashes rather than forward slashes for defining folder paths.

I should add a backSlash setting that, when enabled, uses back-slashes in the paths instead of forward-slashes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.