Skip to content

Commit

Permalink
feat: allow to preserve previous builds for maxAge seconds
Browse files Browse the repository at this point in the history
  • Loading branch information
Dattaya committed Dec 14, 2020
1 parent 021d2b6 commit 7db8044
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 17 deletions.
37 changes: 37 additions & 0 deletions src/clean-webpack-plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,43 @@ describe('verbose option', () => {
});
});

describe('preserve option', () => {
test('does not remove files with `maxAge` set', async () => {
createSrcBundle(2);

const compiler = webpack({
entry: entryFileFull,
output: {
path: outputPathFull,
filename: 'bundle.js',
chunkFilename: '[name].bundle.js',
},
plugins: [new CleanWebpackPlugin({ preserve: { maxAge: 10000 } })],
});

await compiler.run();

const compiler1 = webpack({
entry: entryFileFull,
output: {
path: outputPathFull,
filename: 'bundle.js',
chunkFilename: '[name].bundle.js',
},
plugins: [new CleanWebpackPlugin({ preserve: { maxAge: 10000 } })],
});

createSrcBundle(1);

await compiler1.run();

expect(sandbox.getFileListSync(outputPathFull)).toEqual([
'1.bundle.js',
'bundle.js',
]);
});
});

describe('webpack errors', () => {
test('does nothing when webpack errors are present on initial build', async () => {
createSrcBundle(2);
Expand Down
131 changes: 114 additions & 17 deletions src/clean-webpack-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'path';
import { sync as delSync } from 'del';
import { Compiler, Stats, compilation as compilationType } from 'webpack';
import fs from 'fs';

type Compilation = compilationType.Compilation;

Expand Down Expand Up @@ -62,6 +63,29 @@ export interface Options {
* default: false
*/
dangerouslyAllowCleanPatternsOutsideProject?: boolean;

preserve?: PreserveOption;
}

export interface PreserveOption {
maxAge: number;
/**
* default: 'cwp-stats.json'
*/
filename?: string;
/**
* default: output.path
*/
path?: string;
}

interface StatsSchemaItem {
date: string;
assets: string[];
}

interface StatsSchema {
data: StatsSchemaItem[];
}

// Copied from https://github.com/sindresorhus/is-plain-obj/blob/97480673cf12145b32ec2ee924980d66572e8a86/index.js
Expand All @@ -74,6 +98,36 @@ function isPlainObject(value: unknown): boolean {
return prototype === null || prototype === Object.getPrototypeOf({});
}

function genDateWithAddedSeconds(dateStr: string, seconds: number): Date {
const date = new Date(dateStr);
date.setSeconds(date.getSeconds() + seconds);
return date;
}

function genStatsItem(assets: string[]): StatsSchemaItem {
return { date: new Date().toISOString(), assets };
}

function stringifyStats(stats: StatsSchema): string {
return JSON.stringify(stats, null, 2);
}

/**
* Fetch Webpack's output asset files
*/
function webpackStatsToAssetList(stats: Stats): string[] {
const assets =
stats.toJson(
{
assets: true,
},
true,
).assets || [];
return assets.map((asset: { name: string }) => {
return asset.name;
});
}

class CleanWebpackPlugin {
private readonly dry: boolean;
private readonly verbose: boolean;
Expand All @@ -82,6 +136,11 @@ class CleanWebpackPlugin {
private readonly cleanAfterEveryBuildPatterns: string[];
private readonly cleanOnceBeforeBuildPatterns: string[];
private readonly dangerouslyAllowCleanPatternsOutsideProject: boolean;
private readonly preserve?: {
maxAge: number;
filename: string;
path: string;
};
private currentAssets: string[];
private initialClean: boolean;
private outputPath: string;
Expand Down Expand Up @@ -122,6 +181,14 @@ class CleanWebpackPlugin {

this.verbose = this.dry === true || options.verbose === true || false;

this.preserve = options.preserve
? {
maxAge: options.preserve.maxAge,
filename: options.preserve.filename ?? 'cwp-stats.json',
path: options.preserve.path ?? process.cwd(),
}
: undefined;

this.cleanStaleWebpackAssets =
options.cleanStaleWebpackAssets === true ||
options.cleanStaleWebpackAssets === false
Expand Down Expand Up @@ -236,7 +303,45 @@ class CleanWebpackPlugin {

this.initialClean = true;

this.removeFiles(this.cleanOnceBeforeBuildPatterns);
let assetList: string[] = [];

if (this.preserve?.maxAge) {
assetList = webpackStatsToAssetList(stats);
const fullPath = path.join(
this.preserve.path,
this.preserve.filename,
);
const currentDate = new Date();

if (fs.existsSync(fullPath)) {
const cwpStats: StatsSchema = require(fullPath);
if (cwpStats && cwpStats.data) {
const currentAssets = assetList.slice();
const filtered = cwpStats.data.filter((item) => {
const builtDate = genDateWithAddedSeconds(
item.date,
this.preserve!.maxAge,
);
if (builtDate > currentDate) {
assetList.push(...item.assets);
return true;
}
return false;
});
filtered.unshift(genStatsItem(currentAssets));
fs.writeFileSync(
fullPath,
stringifyStats({ data: filtered }),
);
}
} else if (assetList.length > 0) {
fs.writeFileSync(
fullPath,
stringifyStats({ data: [genStatsItem(assetList)] }),
);
}
}
this.removeFiles(this.cleanOnceBeforeBuildPatterns, assetList.sort());
}

handleDone(stats: Stats) {
Expand All @@ -254,19 +359,7 @@ class CleanWebpackPlugin {
return;
}

/**
* Fetch Webpack's output asset files
*/
const assets =
stats.toJson(
{
assets: true,
},
true,
).assets || [];
const assetList = assets.map((asset: { name: string }) => {
return asset.name;
});
const assetList = webpackStatsToAssetList(stats);

/**
* Get all files that were in the previous build but not the current
Expand Down Expand Up @@ -301,19 +394,23 @@ class CleanWebpackPlugin {
}

if (removePatterns.length !== 0) {
this.removeFiles(removePatterns);
this.removeFiles(removePatterns, this.currentAssets);
}
}

removeFiles(patterns: string[]) {
removeFiles(patterns: string[], ignore: string[] = []) {
try {
const finalIgnore = this.preserve
? [this.preserve.filename].concat(ignore)
: ignore;

const deleted = delSync(patterns, {
force: this.dangerouslyAllowCleanPatternsOutsideProject,
// Change context to build directory
cwd: this.outputPath,
dryRun: this.dry,
dot: true,
ignore: this.protectWebpackAssets ? this.currentAssets : [],
ignore: this.protectWebpackAssets ? finalIgnore : [],
});

/**
Expand Down

0 comments on commit 7db8044

Please sign in to comment.