Skip to content

Commit

Permalink
fix: Fix TypeScript for webpack 5 (#155)
Browse files Browse the repository at this point in the history
Closes #141.
Closes #154.
  • Loading branch information
bebraw committed Oct 30, 2020
1 parent fdb2968 commit 2c2ec24
Show file tree
Hide file tree
Showing 7 changed files with 772 additions and 2,405 deletions.
17 changes: 15 additions & 2 deletions README.md
Expand Up @@ -2,9 +2,9 @@

# webpack-merge - Merge designed for Webpack

_webpack-merge_ provides a `merge` function that concatenates arrays and merges objects creating a new object. If functions are encountered, it will execute them, run the results through the algorithm, and then wrap the returned values within a function again.
**webpack-merge** provides a `merge` function that concatenates arrays and merges objects creating a new object. If functions are encountered, it will execute them, run the results through the algorithm, and then wrap the returned values within a function again.

This behavior is particularly useful in configuring webpack although it has uses beyond it. Whenever you need to merge configuration objects, _webpack-merge_ can come in handy.
This behavior is particularly useful in configuring webpack although it has uses beyond it. Whenever you need to merge configuration objects, **webpack-merge** can come in handy.

## **`merge(...configuration | [...configuration])`**

Expand Down Expand Up @@ -241,6 +241,19 @@ assert.deepStrictEqual(

The way it works is that you should annotate fields to match using `match` (or `CustomizeRule.Match` if you are using TypeScript) matching your configuration structure and then use specific strategies to define how particular fields should be transformed.

## Using with TypeScript

**webpack-merge** supports TypeScript out of the box. You should pass `Configuration` type from webpack to it as follows:

```typescript
import { Configuration } from "webpack";
import { merge } from "webpack-merge";

const config = merge<Configuration>({...}, {...});

...
```

## Development

1. `nvm use`
Expand Down
7 changes: 6 additions & 1 deletion helpers/merge-tests.ts
Expand Up @@ -291,7 +291,12 @@ function mergeTests(merge) {
}
};
const enhance = {
plugins: [new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)]
plugins: [
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/
})
]
};

const result1 = merge(config1, enhance);
Expand Down
3,051 changes: 695 additions & 2,356 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Expand Up @@ -20,12 +20,12 @@
"wildcard": "^2.0.0"
},
"devDependencies": {
"@types/webpack": "^4.41.22",
"@types/estree": "0.0.45",
"husky": "^4.3.0",
"tsdx": "^0.14.0",
"tslib": "^2.0.1",
"typescript": "^4.0.3",
"webpack": "^4.44.2"
"tsdx": "^0.14.1",
"tslib": "^2.0.3",
"typescript": "^4.0.5",
"webpack": "^5.3.2"
},
"repository": {
"type": "git",
Expand Down
23 changes: 15 additions & 8 deletions src/index.ts
@@ -1,26 +1,30 @@
import { Configuration } from "webpack";
import wildcard from "wildcard";
import mergeWith from "./merge-with";
import joinArrays from "./join-arrays";
import unique from "./unique";
import { CustomizeRule, ICustomizeOptions, Key } from "./types";
import { isPlainObject } from "./utils";

function merge(
function merge<Configuration extends object>(
firstConfiguration: Configuration | Configuration[],
...configurations: Configuration[]
): Configuration {
return mergeWithCustomize({})(firstConfiguration, ...configurations);
return mergeWithCustomize<Configuration>({})(
firstConfiguration,
...configurations
);
}

function mergeWithCustomize(options: ICustomizeOptions) {
function mergeWithCustomize<Configuration extends object>(
options: ICustomizeOptions
) {
return function mergeWithOptions(
firstConfiguration: Configuration | Configuration[],
...configurations: Configuration[]
): Configuration {
// No configuration at all
if (!firstConfiguration) {
return {};
return {} as Configuration;
}

// @ts-ignore
Expand All @@ -32,15 +36,18 @@ function mergeWithCustomize(options: ICustomizeOptions) {
if (Array.isArray(firstConfiguration)) {
// Empty array
if (firstConfiguration.length === 0) {
return {};
return {} as Configuration;
}

// @ts-ignore
if (firstConfiguration[0].then) {
throw new TypeError("Promises are not supported");
}

return mergeWith(firstConfiguration, joinArrays(options));
return mergeWith(
firstConfiguration,
joinArrays(options)
) as Configuration;
}

return firstConfiguration;
Expand All @@ -49,7 +56,7 @@ function mergeWithCustomize(options: ICustomizeOptions) {
return mergeWith(
[firstConfiguration].concat(configurations),
joinArrays(options)
);
) as Configuration;
};
}

Expand Down
4 changes: 2 additions & 2 deletions src/merge-with.ts
Expand Up @@ -2,7 +2,7 @@ function mergeWith(objects: object[], customizer) {
const [first, ...rest] = objects;
let ret = first;

rest.forEach((a) => {
rest.forEach(a => {
ret = mergeTo(ret, a, customizer);
});

Expand All @@ -14,7 +14,7 @@ function mergeTo(a, b, customizer) {

Object.keys(a)
.concat(Object.keys(b))
.forEach((k) => {
.forEach(k => {
const v = customizer(a[k], b[k], k);

ret[k] = typeof v === "undefined" ? a[k] : v;
Expand Down
65 changes: 34 additions & 31 deletions test/merge-with-customize.test.ts
Expand Up @@ -2,121 +2,124 @@ import assert from "assert";
import webpack from "webpack";
import { mergeWithCustomize } from "../resolve";

describe("Merge", function () {
describe("Merge", function() {
customizeMergeTests(mergeWithCustomize);
});

function customizeMergeTests(merge) {
it("should allow overriding array behavior", function () {
it("should allow overriding array behavior", function() {
const first = {
entry: ["a"],
entry: ["a"]
};
const second = {
entry: ["b"],
entry: ["b"]
};

assert.deepStrictEqual(
merge({
customizeArray(a) {
return a;
},
}
})(first, second),
first
);
});

it("should pass key to array customizer", function () {
it("should pass key to array customizer", function() {
let receivedKey;
const first = {
entry: ["a"],
entry: ["a"]
};
const second = {
entry: ["b"],
entry: ["b"]
};
const result = merge({
customizeArray(a, _, key) {
receivedKey = key;

return a;
},
}
})(first, second);

assert.strictEqual(receivedKey, "entry");
assert.deepStrictEqual(result, first);
});

it("should allow overriding object behavior", function () {
it("should allow overriding object behavior", function() {
const first = {
entry: {
a: "foo",
},
a: "foo"
}
};
const second = {
entry: {
a: "bar",
},
a: "bar"
}
};

assert.deepStrictEqual(
merge({
customizeObject(a) {
return a;
},
}
})(first, second),
first
);
});

it("should pass key to object customizer", function () {
it("should pass key to object customizer", function() {
let receivedKey;
const first = {
entry: {
a: "foo",
},
a: "foo"
}
};
const second = {
entry: {
a: "bar",
},
a: "bar"
}
};
const result = merge({
customizeObject(a, _, key) {
receivedKey = key;

return a;
},
}
})(first, second);

assert.strictEqual(receivedKey, "entry");
assert.deepStrictEqual(result, first);
});

it("should customize plugins", function () {
it("should customize plugins", function() {
let receivedKey;
const config1 = {
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("development"),
},
NODE_ENV: JSON.stringify("development")
}
}),
new webpack.HotModuleReplacementPlugin(),
],
new webpack.HotModuleReplacementPlugin()
]
};
const config2 = {
plugins: [
new webpack.DefinePlugin({
__CLIENT__: true,
__CLIENT__: true
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new webpack.HotModuleReplacementPlugin(),
],
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/
}),
new webpack.HotModuleReplacementPlugin()
]
};

merge({
customizeArray(_, __, key) {
receivedKey = key;
},
}
})(config1, config2);

assert.strictEqual(receivedKey, "plugins");
Expand Down

0 comments on commit 2c2ec24

Please sign in to comment.