folder-clone
Copies every file of the stream into one or more extra folders, relative to file.base, so a single dest writes them all. The originals are kept unless you turn them off, and a hook can change, replace or skip each copy. It replaces gulp-clone and the second dest that goes with it.
npm install --save-dev @zilero/gulp-folder-cloneUsage
import { folderClone } from '@zilero/gulp-folder-clone';
import { dest, src } from 'gulp';
import { pipeline } from 'node:stream/promises';
// dist/images/logo.png, dist/backup/images/logo.png and dist/v2/images/logo.png
export const images = (): Promise<void> =>
pipeline(
src('src/images/**', { base: 'src', encoding: false }),
folderClone({ dirs: ['backup', 'v2'] }),
dest('dist')
);onClone runs for every copy:
folderClone({
dirs: ['legacy', 'drafts'],
onClone: ({ file, dir, original }) => {
if (dir === 'drafts') {
return null; // skip this copy
}
file.stem = `${file.stem}-${dir}`; // change it in place
}
});Options
| Option | Type | Default | Description |
|---|---|---|---|
dirs | string | string[] | — | One or more folders inside file.base that receive a copy of every file. Required. |
keepOriginal | boolean | true | Emit the original file as well as the copies. |
onClone | ({ file, dir, original }) => Vinyl | null | void | Promise<Vinyl | null | void> | — | Runs for every copy, may be async: change it in place, return another file, or null to skip it. |
onClone gets:
| Key | Description |
|---|---|
file | The copy, already moved into dir; change it in place. |
dir | The folder this copy goes to, as given in dirs. |
original | The file the copy was made from. |
Return nothing to keep the copy, null to skip it, or another Vinyl file to emit instead. The source map of the emitted file is pointed at its path after the hook, so a copy the hook renames or moves keeps a correct map.
Each folder in dirs must be a relative path inside file.base: an absolute path, '.' (the base itself), a path that leaves the base such as '../out', and an empty string are errors, and so is an empty dirs list (Add at least one folder). A folder listed twice, also written differently ('backup' and './backup/'), is an error (Each folder may be listed only once): both copies would be written to the same path. Unknown keys are an error too. Options are checked when the plugin is created.
The package also exports the types FolderCloneOptions, CloneHook and CloneInput.
Notes
- A copy keeps the original's
baseand gets the pathbase/dir/relative:img/logo.pngwithdirs: 'backup'becomesbackup/img/logo.png. - Copies are deep clones: each has its own contents and its own source map object, so changing a copy never touches the original or another copy.
- With
src(…, { sourcemaps: true }), each copy'ssourceMap.filepoints at the copy's relative path, afteronClone; the original's map is left as it was. - For each file the original comes first, then one copy per folder in the order of
dirs. WithkeepOriginal: falseand every copy skipped, nothing is emitted for that file. - Every file is cloned, including files without contents (
read: false) and directories. - Read images and other binary files with
src(…, { encoding: false }); gulp 5 reads files as UTF-8 by default. - An error thrown by
onCloneis emitted as aPluginErrorwith the file's path.
Recipes
Mirror the build into dist/latest:
pipeline(src('dist/**', { encoding: false }), folderClone({ dirs: 'latest' }), dest('dist'));Publish into two release folders, without the original:
folderClone({ dirs: ['releases/stable', 'releases/next'], keepOriginal: false });Leave source maps out of one folder:
folderClone({
dirs: 'public',
onClone: ({ file }) => (file.extname === '.map' ? null : undefined)
});Migrating
From gulp-clone
| gulp-clone | folder-clone |
|---|---|
clone() / clone.sink(), a second branch with its own dest | folderClone({ dirs }) and one dest |
| change the cloned files in the second branch | onClone({ file, dir, original }) |
| drop the originals by not writing the first branch | keepOriginal: false |
From 1.x
- 1.x only had an
onBeforeCopy(file)hook and passed files through unchanged. 2.0 really copies each file into the folders indirs. keepOriginal(defaulttrue) is new.onBeforeCopyis replaced byonClone({ file, dir, original }), which can change, replace or skip (null) each copy.
See migrating from 1.x for every package.