An async postcss plugin to copy all assets referenced in CSS files to a custom destination folder and updating the URLs.
Sections |
---|
Install |
Quick Start |
Options |
Custom Hash Function |
Transform |
Using postcss-import |
About lifecyle and the fileMeta object |
Roadmap |
Credits |
With npm do:
$ npm install postcss-copy
Using postcss-cli
// postcss.config.js
module.exports = {
plugins: [
require('postcss-copy')({
dest: 'dist'
})
]
};
$ postcss src/index.css
Using Gulp
var gulp = require('gulp');
var postcss = require('gulp-postcss');
var postcssCopy = require('postcss-copy');
gulp.task('buildCss', function () {
var processors = [
postcssCopy({
basePath: ['src', 'otherSrc']
dest: 'dist'
})
];
return gulp
.src(['src/**/*.css', 'otherSrc/**/*.css'])
.pipe(postcss(processors))
.pipe(gulp.dest('dist'));
});
Define one/many base path for your CSS files.
Define the dest path of your CSS files and assets.
Define a template name for your final url assets.
- string template
- [hash]: Let you use a hash name based on the contents of the file.
- [name]: Real name of your asset.
- [path]: Original relative path of your asset.
- [ext]: Extension of the asset.
- [query]: Query string.
- [qparams]: Query string params without the
?
. - [qhash]: Query string hash without the
#
.
- function template
var copyOpts = {
...,
template(fileMeta) {
return 'assets/custom-name-' + fileMeta.name + '.' + fileMeta.ext;
}
}
Flag option to notify to postcss-copy that your CSS files destination are going to preserve the directory structure.
It's helpful if you are using postcss-cli
with the --base option or gulp-postcss
with multiple files (e.g: gulp.src('src/**/*.css')
)
Option to ignore assets in your CSS file.
.btn {
background-image: url('!images/button.jpg');
}
.background {
background-image: url('!images/background.jpg');
}
Using a string or array with micromatch support to ignore files:
// ignore with string
var copyOpts = {
...,
ignore: 'images/*.jpg'
}
// ignore with array
var copyOpts = {
...,
ignore: ['images/button.+(jpg|png)', 'images/background.jpg']
}
// ignore function
var copyOpts = {
...,
ignore(fileMeta, opts) {
return (fileMeta.filename.indexOf('images/button.jpg') ||
fileMeta.filename.indexOf('images/background.jpg'));
}
}
Define a custom function to create the hash name.
var copyOpts = {
...,
hashFunction(contents) {
// borschik
return crypto.createHash('sha1')
.update(contents)
.digest('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '')
.replace(/^[+-]+/g, '');
}
};
Extend the copy method to apply a transform in the contents (e.g: optimize images).
IMPORTANT: The function must return the fileMeta (modified) or a promise using resolve(fileMeta)
.
var Imagemin = require('imagemin');
var imageminJpegtran = require('imagemin-jpegtran');
var imageminPngquant = require('imagemin-pngquant');
var copyOpts = {
...,
transform(fileMeta) {
if (['jpg', 'png'].indexOf(fileMeta.ext) === -1) {
return fileMeta;
}
return Imagemin.buffer(fileMeta.contents, {
plugins: [
imageminPngquant(),
imageminJpegtran({
progressive: true
})
]
})
.then(result => {
fileMeta.contents = result;
return fileMeta; // <- important
});
}
};
postcss-import is a great plugin that allow us work our css files in a modular way with the same behavior of CommonJS.
One thing more...
postcss-import has the ability of load files from node_modules. If you are using a custom basePath
and you want to
track your assets from node_modules
you need to add the node_modules
folder in the basePath
option:
myProject/
|-- node_modules/
|-- dest/
|-- src/
var gulp = require('gulp');
var postcss = require('gulp-postcss');
var postcssCopy = require('postcss-copy');
var postcssImport = require('postcss-import');
var path = require('path');
gulp.task('buildCss', function () {
var processors = [
postcssImport(),
postcssCopy({
basePath: ['src', 'node_modules'],
preservePath: true,
dest: 'dist'
})
];
return gulp
.src('src/**/*.css')
.pipe(postcss(processors, {to: 'dist/css/index.css'}))
.pipe(gulp.dest('dist/css'));
});
The fileMeta is a literal object with meta information about the copy process. Its information grows with the progress of the copy process.
The lifecyle of the copy process is:
-
Detect the url in the CSS files
-
Validate url
-
Initialize the fileMeta:
{ sourceInputFile, // path to the origin CSS file sourceValue, // origin asset value taked from the CSS file filename, // filename normalized without query string absolutePath, // absolute path of the asset file fullName, // name of the asset file path, // relative path of the asset file name, // name without extension ext, // extension name query, // full query string qparams, // query string params without the char '?' qhash, // query string hash without the char '#' basePath // basePath found }
-
Check ignore function
-
Read the asset file (using a cache buffer if exists)
-
Add
content
property in the fileMeta object -
Execute custom transform
-
Create hash name based on the custom transform
-
Add
hash
property in the fileMeta object -
Define template for the new asset
-
Add
resultAbsolutePath
andextra
properties in the fileMeta object -
Write in destination
-
Write the new URL in the PostCSS node value.
nothing for now :)
- Thanks to @conradz and his rework plugin rework-assets my inspiration in this plugin.
- Thanks to @MoOx for let me create the copy function in his postcss-url plugin.
- Thanks to @webpack, i take the idea of define templates from his awesome file-loader
- Huge thanks to @TrySound for his work in this project.
MIT