Skip to content

Commit

Permalink
Merge pull request #16 from homer0/next
Browse files Browse the repository at this point in the history
v4.0.0
  • Loading branch information
Leonardo Apiwan committed Apr 3, 2018
2 parents 552d9f2 + 805ba9b commit 4c33033
Show file tree
Hide file tree
Showing 20 changed files with 2,456 additions and 451 deletions.
14 changes: 9 additions & 5 deletions README.md
Expand Up @@ -32,10 +32,10 @@ Since webpack is the default build engine for projext, after you install the plu
projext build [target-name]
```

In the case you changed the engine and you want to restore it to webpack, you just need to go to your project configuration file (`config/project.config.js`), on your target settings, change `engine` to `webpack`:
In the case you changed the engine and you want to restore it to webpack, you just need to go to your projext configuration file, on your target settings, change `engine` to `webpack`:

```js
// config/project.config.js
// projext.config.js

module.exports = {
targets: {
Expand All @@ -55,7 +55,7 @@ You can implement both the [`webpack-dev-middleware`](https://yarnpkg.com/en/pac

```js
// Require the function for the implementation
const useExpress = require('projext-plugin-plugin/express');
const useExpress = require('projext-plugin-webpack/express');

// Require Express to create a dummy app
const express = require('express');
Expand All @@ -78,10 +78,14 @@ app.listen(...);
const useJimpex = require('projext-plugin-webpack/jimpex');

// Require Jimpex to create a dummy app
const Jimpex = require('jimpex');
const { Jimpex } = require('jimpex');

// Define the Jimpex app
class DevApp extends Jimpex {}
class DevApp extends Jimpex {
boot() {
// This method needs to be created.
}
}

// Create the app
const app = new DevApp();
Expand Down
8 changes: 4 additions & 4 deletions package.json
Expand Up @@ -2,20 +2,20 @@
"name": "projext-plugin-webpack",
"description": "Allows projext to use webpack as a build engine.",
"homepage": "https://homer0.github.io/projext-plugin-webpack/",
"version": "3.0.0",
"version": "4.0.0",
"repository": "homer0/projext-plugin-webpack",
"author": "Leonardo Apiwan (@homer0) <me@homer0.com>",
"license": "MIT",
"dependencies": {
"projext": "^2.0.0",
"projext": "^3.0.0",
"wootils": "^1.1.1",
"jimple": "homer0/jimple",
"fs-extra": "5.0.0",
"extend": "3.0.1",

"webpack": "3.10.0",
"webpack-dev-server": "2.11.1",
"webpack-node-utils": "2.1.0",
"webpack-node-utils": "3.0.0",
"extract-text-webpack-plugin": "3.0.2",
"html-webpack-plugin": "2.30.1",
"script-ext-html-webpack-plugin": "1.8.8",
Expand Down Expand Up @@ -52,7 +52,7 @@
"jest-ex": "4.0.0",
"jest-cli": "22.1.4",
"jasmine-expect": "3.8.3",
"jimpex": "^2.0.0",
"jimpex": "^2.1.0",
"esdoc": "1.0.4",
"esdoc-standard-plugin": "1.0.0",
"esdoc-node": "1.0.3",
Expand Down
13 changes: 13 additions & 0 deletions src/index.js
Expand Up @@ -15,16 +15,29 @@ const {
const {
webpackMiddlewares,
} = require('./services/server');

const { name: pluginName } = require('../package.json');
/**
* This is the method called by projext when loading the plugin and it takes care of registering
* the Webpack build engine service and all the other services the engine depends on.
* @param {Projext} app The projext main container.
* @ignore
*/
const loadPlugin = (app) => {
/**
* These will be used when defining the external dependencies of a Node target. Since their names
* don't match a dependency of the `package.json`, if not defined, webpack would try to bundle
* the plugin and all its dependencies.
*/
app.set('webpackDefaultExternals', () => [
`${pluginName}/express`,
`${pluginName}/jimpex`,
]);
// Register the main services of the build engine.
app.register(webpackConfiguration);
app.register(webpackBuildEngine);

// Register the services for building the targets confirmations.
app.register(webpackBaseConfiguration);
app.register(webpackBrowserDevelopmentConfiguration);
app.register(webpackBrowserProductionConfiguration);
Expand Down
2 changes: 1 addition & 1 deletion src/jimpex.js
Expand Up @@ -42,7 +42,7 @@ const useJimpex = (jimpexApp, targetToBuild, targetToServe) => {
});
// Add an event listener that shows a _'waiting'_ message when the server starts.
jimpexApp.get('events').once('after-start', () => {
jimpexApp.get('appLogger').warning('waiting for Webpack...');
jimpexApp.get('appLogger').warning('waiting for webpack...');
});

return info;
Expand Down
2 changes: 1 addition & 1 deletion src/jimpex/index.js
@@ -1,5 +1,5 @@
const { webpackFrontendFs } = require('./frontendFs');
const { webpackSendFile } = require('./SendFile');
const { webpackSendFile } = require('./sendFile');

module.exports = {
webpackFrontendFs,
Expand Down
29 changes: 14 additions & 15 deletions src/services/building/configuration.js
Expand Up @@ -75,27 +75,26 @@ class WebpackConfiguration {
return definitions;
}
/**
* Generate the output paths for a target files.
* @param {Target} target The target information.
* @param {string} buildType The intended build type: `production` or `development`.
* @return {WebpackConfigurationTargetOutput}
*/
getOutput(target, buildType) {
return target.is.node ?
{ js: target.output[buildType] } :
Object.assign({}, target.output[buildType]);
}
/**
* In case the target is a library, this method will be called to generate the library options
* for Webpack.
* In case the target is a library, this method will be called in order to get the extra output
* settings webpack needs.
* @param {Target} target The target information.
* @return {Object}
*/
getLibraryOptions(target) {
const { libraryOptions } = target;
return Object.assign({
// Create the object for webpack.
const newOptions = Object.assign({
libraryTarget: 'commonjs2',
}, libraryOptions);

// Remove any option unsupported by the webpack schema
[
'compress',
].forEach((invalidOption) => {
delete newOptions[invalidOption];
});

return newOptions;
}
/**
* This method generates a complete Webpack configuration for a target.
Expand Down Expand Up @@ -125,7 +124,7 @@ class WebpackConfiguration {
[target.name]: entries,
},
definitions: this.getDefinitions(target, buildType),
output: this.getOutput(target, buildType),
output: target.output[buildType],
buildType,
};

Expand Down
125 changes: 103 additions & 22 deletions src/services/configurations/browserDevelopmentConfiguration.js
@@ -1,4 +1,3 @@
const path = require('path');
const extend = require('extend');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
Expand All @@ -19,20 +18,23 @@ const ConfigurationFile = require('../../abstracts/configurationFile');
class WebpackBrowserDevelopmentConfiguration extends ConfigurationFile {
/**
* Class constructor.
* @param {Logger} appLogger To inform the user when the
* build is running on the dev
* server.
* @param {Events} events To reduce the configuration.
* @param {PathUtils} pathUtils Required by `ConfigurationFile`
* in order to build the path to
* the overwrite file.
* @param {WebpackBaseConfiguration} webpackBaseConfiguration The configuration this one will
* extend.
* @param {Logger} appLogger To inform the user when the build
* is running on the dev server.
* @param {Events} events To reduce the configuration.
* @param {PathUtils} pathUtils Required by `ConfigurationFile`
* in order to build the path to the
* overwrite file.
* @param {TargetsHTML} targetsHTML The service in charge of generating
* a default HTML file in case the
* target doesn't have one.
* @param {WebpackBaseConfiguration} webpackBaseConfiguration The configuration this one will
* extend.
*/
constructor(
appLogger,
events,
pathUtils,
targetsHTML,
webpackBaseConfiguration
) {
super(
Expand All @@ -51,6 +53,11 @@ class WebpackBrowserDevelopmentConfiguration extends ConfigurationFile {
* @type {Events}
*/
this.events = events;
/**
* A local reference for the `targetsHTML` service.
* @type {TargetsHTML}
*/
this.targetsHTML = targetsHTML;
}
/**
* Create the configuration with the `entry`, the `output` and the plugins specifics for a
Expand Down Expand Up @@ -91,7 +98,7 @@ class WebpackBrowserDevelopmentConfiguration extends ConfigurationFile {
new ExtractTextPlugin(output.css),
// To automatically inject the `script` tag on the target `html` file.
new HtmlWebpackPlugin(Object.assign({}, target.html, {
template: path.join(target.paths.source, target.html.template),
template: this.targetsHTML.getFilepath(target),
inject: 'body',
})),
// To add the `async` attribute to the `script` tag.
Expand All @@ -111,16 +118,42 @@ class WebpackBrowserDevelopmentConfiguration extends ConfigurationFile {
const hotEntries = [];
// If the target needs to run on development...
if (target.runOnDevelopment) {
const devServerConfig = this._normalizeTargetDevServerSettings(target);
// Add the dev server information to the configuration.
const { devServer } = target;
const devServerHost = devServer.host || 'localhost';
config.devServer = {
port: devServer.port || 2509,
inline: !!devServer.reload,
port: devServerConfig.port,
inline: !!devServerConfig.reload,
open: true,
};
if (devServerHost !== 'localhost') {
config.devServer.public = devServerHost;
/**
* This setting is specific to the webpack dev server and it allows web apps that use
* the history API to fallback to the server's root in case the app is loaded on a sub
* route, that way the custom routing can redirect the user.
* This is not yet documented on the projext configuration because I'm not entirely sure
* other dev servers can support it that easily, so for the moment if will be like a
* _"hidden option"_ for this plugin; While implementing the next build engine I'll go
* back and either document it as special setting for this plugin or adding to the
* projext main configuration.
* @todo Validate historyApiFallback
*/
if (devServerConfig.historyApiFallback) {
config.devServer.historyApiFallback = devServerConfig.historyApiFallback;
}
// If the configuration has a custom host, set it.
if (devServerConfig.host !== 'localhost') {
config.devServer.host = devServerConfig.host;
}
// If there are SSL files, set them on the server.
if (devServerConfig.ssl) {
config.devServer.https = {
key: devServerConfig.ssl.key,
cert: devServerConfig.ssl.cert,
ca: devServerConfig.ssl.ca,
};
}
// If the server is being proxied, add the public host.
if (devServerConfig.proxied) {
config.devServer.public = devServerConfig.proxied.host;
}
// If the target will run with the dev server and it requires HMR...
if (target.hot) {
Expand All @@ -130,12 +163,9 @@ class WebpackBrowserDevelopmentConfiguration extends ConfigurationFile {
config.devServer.publicPath = '/';
// Enable the dev server `hot` setting.
config.devServer.hot = true;
// Build the host URL for the dev server as it will be needed for the hot entries.
const protocol = devServer.https ? 'https' : 'http';
const host = `${protocol}://${devServerHost}:${config.devServer.port}`;
// Push the required entries to enable HMR on the dev server.
hotEntries.push(...[
`webpack-dev-server/client?${host}`,
`webpack-dev-server/client?${devServerConfig.url}`,
'webpack/hot/only-dev-server',
]);
}
Expand Down Expand Up @@ -174,6 +204,56 @@ class WebpackBrowserDevelopmentConfiguration extends ConfigurationFile {
params
);
}
/**
* Check a target dev server settings in order to validate those that needs to be removed or
* completed with their default values.
* @param {Target} target The target information.
* @return {TargetDevServerSettings}
*/
_normalizeTargetDevServerSettings(target) {
// Get a new copy of the config to work with.
const config = extend(true, {}, target.devServer);
/**
* Set a flag to know if at least one SSL file was sent.
* This flag is also used when reading the `proxied` settings to determine the default
* behaviour of `proxied.https`.
*/
let hasASSLFile = false;
// Loop all the SSL files...
Object.keys(config.ssl).forEach((name) => {
const file = config.ssl[name];
// If there's an actual path...
if (typeof file === 'string') {
// ...set the flag to `true`.
hasASSLFile = true;
// Generate the path to the file.
config.ssl[name] = this.pathUtils.join(file);
}
});
// If no SSL file was sent, just remove the settings.
if (!hasASSLFile) {
delete config.ssl;
}
// If the server is being proxied...
if (config.proxied.enabled) {
// ...if no `host` was specified, use the one defined for the server.
if (config.proxied.host === null) {
config.proxied.host = config.host;
}
// If no `https` option was specified, set it to `true` if at least one SSL file was sent.
if (config.proxied.https === null) {
config.proxied.https = hasASSLFile;
}
} else {
// ...otherwise, just remove the setting.
delete config.proxied;
}

const protocol = config.ssl ? 'https' : 'http';
config.url = `${protocol}://${config.host}:${config.port}`;

return config;
}
/**
* Creates a _'fake Webpack plugin'_ that detects when the bundle is being compiled in order to
* log messages with the dev server information.
Expand All @@ -195,7 +275,7 @@ class WebpackBrowserDevelopmentConfiguration extends ConfigurationFile {
compiler.plugin('done', () => {
// Awful hack, but the webpack output gets on the same line
setTimeout(() => {
this.appLogger.success(`You app is running on the port ${port}`);
this.appLogger.success(`Your app is running on the port ${port}`);
}, 0);
});
},
Expand All @@ -219,6 +299,7 @@ const webpackBrowserDevelopmentConfiguration = provider((app) => {
app.get('appLogger'),
app.get('events'),
app.get('pathUtils'),
app.get('targetsHTML'),
app.get('webpackBaseConfiguration')
)
);
Expand Down

0 comments on commit 4c33033

Please sign in to comment.