Skip to content

Commit

Permalink
feat(custom-webpack): support Webpack config written in TS (#626)
Browse files Browse the repository at this point in the history
  • Loading branch information
arturovt authored and just-jeb committed Nov 19, 2019
1 parent bed644e commit 479fa0b
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 34 deletions.
77 changes: 52 additions & 25 deletions packages/custom-webpack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Allow customizing build configuration without ejecting webpack configuration (`n
"[architect-target]": {
"builder": "@angular-builders/custom-webpack:[browser|server|karma|dev-server]"
"options": {
...
...
}
```
Where:
Expand All @@ -36,7 +36,7 @@ Allow customizing build configuration without ejecting webpack configuration (`n
`ng run [project]:[architect-target]`
If it is one of the predefined targets, you can run it with `ng [architect-target]`
## For example
## For Example
- angular.json:
```js
Expand All @@ -49,7 +49,7 @@ Allow customizing build configuration without ejecting webpack configuration (`n
"build": {
"builder": "@angular-builders/custom-webpack:browser"
"options": {
...
...
}
```
- Run the build: `ng build`
Expand All @@ -61,7 +61,7 @@ Allow customizing build configuration without ejecting webpack configuration (`n
- [@angular-builders/custom-webpack:karma](#Custom-webpack-Karma)
- [@angular-builders/custom-webpack:dev-server](#Custom-webpack-dev-server)
## Custom webpack browser
## Custom Webpack `browser`
Extended `@angular-devkit/build-angular:browser` builder that allows to specify additional webpack configuration (on top of the existing under the hood) and `index.html` tranformations.
The builder will run the same build as `@angular-devkit/build-angular:browser` does with extra parameters that are specified in the provided webpack configuration. It will also run transformation on `index.html` if specified.
Expand All @@ -82,7 +82,9 @@ Builder options:
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js",
"mergeStrategies": { "externals": "replace" }
"mergeStrategies": {
"externals": "replace"
}
},
"indexTransform": "./index-html-transform.js",
"outputPath": "dist/my-cool-client",
Expand All @@ -95,7 +97,7 @@ Builder options:
In this example `externals` entry from `extra-webpack.config.js` will replace `externals` entry from Angular CLI underlying webpack config while all the rest will be appended. In addition `index.html` will be modified by the function exported from `./index-html-transform.js`.
## Custom webpack dev-server
## Custom Webpack `dev-server`
Enhanced `@angular-devkit/build-angular:dev-server` builder that leverages the custom webpack builder to get webpack configuration.
Expand All @@ -111,25 +113,25 @@ Thus, if you use `@angular-builders/custom-webpack:dev-server` along with `@angu
"architect": {
...
"build": {
"builder": "@angular-builders/custom-webpack:browser"
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
path: "./extra-webpack.config.js"
}
"path": "./extra-webpack.config.js"
},
...
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "my-project:build"
"browserTarget": "my-project:build"
}
}
```
In this example `dev-server` will use `custom-webpack:browser` builder, hence modified webpack config, when invoking the serve target.
## Custom webpack server
## Custom Webpack `server`
Extended `@angular-devkit/build-angular:server` builder that allows to specify additional webpack configuration (on top of the existing under the hood) and `index.html` tranformations.
The builder will run the same build as `@angular-devkit/build-angular:server` does with extra parameters that are specified in the provided webpack configuration.
Expand All @@ -149,7 +151,9 @@ Builder options:
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js",
"mergeStrategies": { "module.rules": "prepend" },
"mergeStrategies": {
"module.rules": "prepend"
},
"replaceDuplicatePlugins": true
},
"outputPath": "dist/my-cool-server",
Expand All @@ -161,7 +165,7 @@ Builder options:
In this example `module.rules` entry from `extra-webpack.config.js` will be prepended to `module.rules` entry from Angular CLI underlying webpack config while all the rest will be appended.
Since loaders are evaluated [from right to left](https://webpack.js.org/concepts/loaders/#configuration) this will effectively mean that the loaders you define in your custom configuration will be applied **after** the loaders defined by Angular CLI.
## Custom webpack Karma
## Custom Webpack `karma`
Extended `@angular-devkit/build-angular:karma` builder that allows to specify additional webpack configuration (on top of the existing under the hood) and `index.html` tranformations.
The builder will run the same build as `@angular-devkit/build-angular:karma` does with extra parameters that are specified in the provided webpack configuration.
Expand All @@ -186,11 +190,10 @@ Builder options:
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
...
}
```
# Custom webpack config object
# Custom Webpack Config Object
This option defines your custom webpack configuration. If not specified at all, plain Angular build will run.
The following properties are available:
Expand Down Expand Up @@ -221,25 +224,49 @@ The following properties are available:
- `replaceDuplicatePlugins`: Defaults to `false`. If `true`, the plugins in custom webpack config will replace the corresponding plugins in default Angular CLI webpack configuration. If `false`, the [default behavior](#merging-plugins-configuration) will be applied.
**Note that if `true`, this option will override `mergeStrategies` for `plugins` field.**
## Merging plugins configuration:
Webpack configuration can be also written in TypeScript. Given the following example:
```ts
// extra-webpack.config.ts
import * as webpack from 'webpack';

export default {
output: {
library: 'shop',
libraryTarget: 'umd',
},
} as webpack.Configuration;
```
Do not forget to specify the correct path to this file:
```js
"customWebpackConfig": {
"path": "./extra-webpack.config.ts"
},
```
## Merging Plugins Configuration:
If in your custom configuration you specify a plugin that is already added by Angular CLI then by default the two instances will be merged.
In case of the conflicts your configuration will override the existing one.
Thus, if you'd like to modify an existing plugin configuration, all you have to do is specify the _delta_ you want to change.
For example, if you'd like to allow cyclic dependencies that include dynamic imports you only have to specify this single entry:
```js
plugins: [
new CircularDependencyPlugin({
allowAsyncCycles: true,
}),
];
module.exports = {
plugins: [
new CircularDependencyPlugin({
allowAsyncCycles: true,
}),
],
};
```
Keep in mind though that if there are default values in the plugin's constructor, they would override the corresponding values in the existing instance. So these you have to set explicitly to the same values Angular sets.
You can check out an example for plugins merge in the [unit tests](./src/webpack-config-merger.spec.ts) and in [this](https://github.com/meltedspark/angular-builders/issues/13) issue.
## Custom Webpack promisified config
## Custom Webpack Promisified Config
Webpack config can also export a `Promise` object that resolves custom config. Given the following example:
Expand All @@ -260,7 +287,7 @@ module.exports = readFile('./LICENSE', {
In this case, the behavior will be the same as when exporting a plain object — the resolved configuration will be merged with the base one.
## Custom Webpack config function
## Custom Webpack Config Function
If `customWebpackConfig.path` file exports a function, the behaviour of the builder changes : no more automatic merge is applied, instead the function
is called with the base Webpack configuration and must return the new configuration.
Expand Down Expand Up @@ -311,7 +338,7 @@ module.exports = async config => {
};
```
# Index transform
# Index Transform
### Important:
Expand Down Expand Up @@ -369,6 +396,6 @@ In the example we add a paragraph with build configuration to your `index.html`.
Full example [here](https://github.com/meltedspark/angular-builders/tree/master/packages/custom-webpack/examples/full-cycle-app).
# Further reading
# Further Reading
- [Customizing Angular CLI 6 build  -  an alternative to ng eject](https://medium.com/@meltedspark/customizing-angular-cli-6-build-an-alternative-to-ng-eject-a48304cd3b21)
4 changes: 2 additions & 2 deletions packages/custom-webpack/examples/full-cycle-app/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
"path": "./extra-webpack.config.ts"
},
"indexTransform": "./index-html.transform.js",
"outputPath": "dist/full-cycle-app",
Expand Down Expand Up @@ -137,4 +137,4 @@
"cli": {
"packageManager": "yarn"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
import * as webpack from 'webpack';
import * as HtmlWebpackPlugin from 'html-webpack-plugin';

/**
* This is where you define your additional webpack configuration items to be appended to
* the end of the webpack config.
*/
module.exports = {
export default {
plugins: [
new HtmlWebpackPlugin({
filename: 'footer.html',
template: 'src/footer-template.html',
}),
],
};
} as webpack.Configuration;
3 changes: 2 additions & 1 deletion packages/custom-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"builders": "builders.json",
"dependencies": {
"lodash": "^4.17.10",
"webpack-merge": "^4.2.1"
"webpack-merge": "^4.2.1",
"ts-node": "^8.5.2"
},
"peerDependencies": {
"@angular-devkit/build-angular": ">=0.801.0"
Expand Down
28 changes: 25 additions & 3 deletions packages/custom-webpack/src/custom-webpack-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ export class CustomWebpackBuilder {
}

const webpackConfigPath = config.path || defaultWebpackConfigPath;
const configOrFactoryOrPromise: CustomWebpackConfig = require(`${getSystemPath(
root
)}/${webpackConfigPath}`);
const path = `${getSystemPath(root)}/${webpackConfigPath}`;
const configOrFactoryOrPromise = resolveCustomWebpackConfig(path);

if (typeof configOrFactoryOrPromise === 'function') {
// That exported function can be synchronous either
Expand All @@ -51,3 +50,26 @@ export class CustomWebpackBuilder {
);
}
}

function resolveCustomWebpackConfig(path: string): CustomWebpackConfig {
if (path.endsWith('.ts')) {
// Register TS compiler lazily
require('ts-node').register({
compilerOptions: {
module: 'commonjs',
},
});
}

const customWebpackConfig = require(path);
// If the user provides a configuration in TS file
// then there are 2 cases for exporing an object. The first one is:
// `module.exports = { ... }`. And the second one is:
// `export default { ... }`. The ESM format is compiled into:
// `{ default: { ... } }`
if (typeof customWebpackConfig.default === 'object') {
return customWebpackConfig.default;
}

return customWebpackConfig;
}

0 comments on commit 479fa0b

Please sign in to comment.