Skip to content

Commit

Permalink
Merge pull request #111 from etlovett/master
Browse files Browse the repository at this point in the history
Sort properties of `assets` and `chunks` objects.
  • Loading branch information
fjsj authored Nov 10, 2022
2 parents 4e25ec7 + 4bbfc7f commit 5e6213c
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 2 deletions.
21 changes: 19 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const defaults = require('lodash.defaults');
const assign = require('lodash.assign');
const get = require('lodash.get');
const each = require('lodash.foreach');
const fromPairs = require('lodash.frompairs');
const toPairs = require('lodash.topairs');
const stripAnsi = require('strip-ansi');

function getAssetPath(compilation, name) {
Expand All @@ -29,6 +31,21 @@ function getSource(compilation, name) {
return fs.readFileSync(path, { encoding: 'utf-8' });
}

/**
* Merges the provided objects, ensuring that the resulting object has its properties in sorted order.
* @template T
* @param {T} obj1
* @param {Partial<T> | undefined} obj2
* @returns {T}
*/
function mergeObjects(obj1, obj2) {
const mergedObj = assign({}, obj1, obj2);
const sortedPairs = toPairs(mergedObj).sort((e1, e2) => e1[0].localeCompare(e2[0]));
// @ts-ignore: 2322 The Lodash typedefs aren't smart enough to be able to tell TS that we're
// regenerating the object from the original key-value pairs.
return fromPairs(sortedPairs);
}

class BundleTrackerPlugin {
/**
* Track assets file location per bundle
Expand Down Expand Up @@ -83,8 +100,8 @@ class BundleTrackerPlugin {
*/
_writeOutput(compiler, contents) {
assign(this.contents, contents, {
assets: assign(this.contents.assets, contents.assets),
chunks: assign(this.contents.chunks, contents.chunks),
assets: mergeObjects(this.contents.assets, contents.assets),
chunks: mergeObjects(this.contents.chunks, contents.chunks),
});

if (this.options.publicPath) {
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@
"lodash.assign": "^4.2.0",
"lodash.defaults": "^4.2.0",
"lodash.foreach": "^4.5.0",
"lodash.frompairs": "^4.0.1",
"lodash.get": "^4.4.2",
"lodash.topairs": "^4.3.0",
"strip-ansi": "^6.0.0"
},
"devDependencies": {
"@types/lodash.assign": "^4.2.6",
"@types/lodash.defaults": "^4.2.6",
"@types/lodash.foreach": "^4.5.6",
"@types/lodash.frompairs": "^4.0.7",
"@types/lodash.get": "^4.4.6",
"@types/lodash.topairs": "^4.3.7",
"@types/node": "^13.13.52",
"@types/webpack": "^4.41.28",
"@typescript-eslint/eslint-plugin": "^2.34.0",
Expand Down
70 changes: 70 additions & 0 deletions tests/base.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
'use strict';

const fs = require('fs');
const toPairs = require('lodash.topairs');
const zlib = require('zlib');
const path = require('path');
const rimraf = require('rimraf');
Expand Down Expand Up @@ -702,4 +703,73 @@ describe('BundleTrackerPlugin bases tests', () => {
},
);
});

it('sorts assets and chunks properties in alphabetical order', done => {
const expectErrors = null;
const expectWarnings = getWebpack4WarningMessage();

testPlugin(
webpack,
{
context: __dirname,
entry: {
appZ: path.resolve(__dirname, 'fixtures', 'app1.js'),
appA: path.resolve(__dirname, 'fixtures', 'appWithAssets.js'),
},
output: {
path: OUTPUT_DIR,
filename: 'js/[name].js',
publicPath: 'http://localhost:3000/assets/',
},
module: {
rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }],
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
test: /[\\/]?commons/,
enforce: true,
priority: -20,
chunks: 'all',
reuseExistingChunk: true,
},
default: {
name: 'shared',
reuseExistingChunk: true,
},
},
},
},
plugins: [
new MiniCssExtractPlugin({ filename: 'css/[name].css' }),
new BundleTrackerPlugin({
path: OUTPUT_DIR,
relativePath: true,
includeParents: true,
filename: path.join(OUTPUT_DIR, 'webpack-stats.json'),
}),
],
},
{
// This object is deliberately left empty because the real test happens below,
// not in the comparison inside testPlugin.
},
'webpack-stats.json',
() => {
const statsStr = fs.readFileSync(path.join(OUTPUT_DIR, 'webpack-stats.json'), 'utf8');
const stats = JSON.parse(statsStr);
const assetsKeys = toPairs(stats.assets).map(pair => pair[0]);
const chunksKeys = toPairs(stats.chunks).map(pair => pair[0]);

expect(assetsKeys).toEqual(['css/appA.css', 'js/1.js', 'js/appA.js', 'js/appZ.js', 'js/commons.js']);
expect(chunksKeys).toEqual(['appA', 'appZ']);

done();
},
expectErrors,
expectWarnings,
);
});
});
70 changes: 70 additions & 0 deletions tests/webpack5.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
'use strict';

const fs = require('fs');
const toPairs = require('lodash.topairs');
const zlib = require('zlib');
const path = require('path');
const rimraf = require('rimraf');
Expand Down Expand Up @@ -702,4 +703,73 @@ describe('BundleTrackerPlugin bases tests', () => {
},
);
});

it('sorts assets and chunks properties in alphabetical order', done => {
const expectErrors = null;
const expectWarnings = getWebpack5WarningMessage();

testPlugin(
webpack5,
{
context: __dirname,
entry: {
appZ: path.resolve(__dirname, 'fixtures', 'app1.js'),
appA: path.resolve(__dirname, 'fixtures', 'appWithAssets.js'),
},
output: {
path: OUTPUT_DIR,
filename: 'js/[name].js',
publicPath: 'http://localhost:3000/assets/',
},
module: {
rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }],
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
test: /[\\/]?commons/,
enforce: true,
priority: -20,
chunks: 'all',
reuseExistingChunk: true,
},
default: {
name: 'shared',
reuseExistingChunk: true,
},
},
},
},
plugins: [
new MiniCssExtractPlugin({ filename: 'css/[name].css' }),
new BundleTrackerPlugin({
path: OUTPUT_DIR,
relativePath: true,
includeParents: true,
filename: path.join(OUTPUT_DIR, 'webpack-stats.json'),
}),
],
},
{
// This object is deliberately left empty because the real test happens below,
// not in the comparison inside testPlugin.
},
'webpack-stats.json',
() => {
const statsStr = fs.readFileSync(path.join(OUTPUT_DIR, 'webpack-stats.json'), 'utf8');
const stats = JSON.parse(statsStr);
const assetsKeys = toPairs(stats.assets).map(pair => pair[0]);
const chunksKeys = toPairs(stats.chunks).map(pair => pair[0]);

expect(assetsKeys).toEqual(['css/appA.css', 'js/862.js', 'js/appA.js', 'js/appZ.js', 'js/commons.js']);
expect(chunksKeys).toEqual(['appA', 'appZ']);

done();
},
expectErrors,
expectWarnings,
);
});
});

0 comments on commit 5e6213c

Please sign in to comment.