Tools used:
- Angular.js
- ui-router
- Webpack
- Less
- Bower
- npm
Goals:
- Filenames are not too ambiguous. Ie: avoid
index.js
and favoursearchBox.js
to ease IDE usage. - Files are organized by their nature.
- Rapid prototyping.
All client-side source files live in the ./src
folder. Webpack is used to put everything together in the plunker.js
file of the ./static
directory (could easily be called dist
).
Vendor client-side libraries are put in the bower_components
folder by bower (which is committed to git).
Vendor npm
modules are used when available and well-adapted for client-side usage. Examples include semver
and lodash
.
Webpack
is started in the watch mode at server startup. The server will only listen()
upon successful build of the assets. Rebuilds (which are incremental thanks to Webpack) are only logged and do not affect the server.
Important: All Angular.js
modules have their module definition exported in a CommonJS
way. For vendor angular stuff, I built a custom loader to do this for modules outside my control (see below).
webpack.config.js
var NgAnnotatePlugin = require("ng-annotate-webpack-plugin");
var Path = require("path");
var Webpack = require("webpack");
module.exports = function (config) {
return {
cache: true,
entry: {
plunker: [__dirname + "/src/apps/plunker.js"],
},
output: {
path: Path.join(__dirname, "static", "build"),
filename: "[name].js",
publicPath: "/static/",
},
module: {
loaders: [
{ test: /[\/\\]ace\.js$/, loader: "exports-loader?window.ace" },
{ test: /[\/\\]angular\.js$/, loader: "exports-loader?window.angular" },
{ test: /[\/\\]angular-cookie\.js$/, loader: "ng-loader?ipCookie" },
{ test: /[\/\\]angular-deckgrid\.js$/, loader: "ng-loader?akoenig.deckgrid" },
{ test: /[\/\\]angular-ui-router\.js$/, loader: "ng-loader?ui.router" },
{ test: /[\/\\]timeAgo\.js$/, loader: "ng-loader?yaru22.angular-timeago" },
{ test: /[\/\\]ui-bootstrap-tpls\.js$/, loader: "ng-loader?ui.bootstrap" },
{ test: /\.css$/, loader: "style-loader!css-loader" },
{ test: /\.less$/, loader: "style-loader!css-loader!less-loader" },
{ test: /\.woff$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
{ test: /\.ttf$/, loader: "file-loader" },
{ test: /\.eot$/, loader: "file-loader" },
{ test: /\.svg$/, loader: "file-loader" },
{ test: /\.html$/, loader: "raw-loader" },
{ test: /\.json$/, loader: "json-loader" },
],
noParse: [
/[\/\\]angular\.js$/,
/[\/\\]ace\.js$/,
/[\/\\]angular-cookie\.js$/,
/[\/\\]angular-deckgrid\.js$/,
/[\/\\]angular-ui-router\.js$/,
/[\/\\]timeAgo\.js$/,
/[\/\\]ui-bootstrap-tpls\.js$/,
]
},
plugins: [
new PlunkerModuleReplacementPlugin(),
new Webpack.DefinePlugin({
CONFIG: JSON.stringify(config)
}),
// new Webpack.optimize.DedupePlugin(),
// new NgAnnotatePlugin(),
// new Webpack.optimize.UglifyJsPlugin({
// mangle: false,
// compress: false,
// }),
],
resolve: {
modulesDirectories: ["node_modules", "bower_components", "src"],
root: __dirname,
alias: {
'ace': "ace-builds/src-noconflict/ace.js",
'angular': "angular/angular.js",
'angular-cookie': "angular-cookie/angular-cookie.js",
'angular-deckgrid': "angular-deckgrid/angular-deckgrid.js",
'angular-timeago': "angular-timeago/src/timeAgo.js",
'angular-ui-router': "angular-ui-router/release/angular-ui-router.js",
'angular-ui-bootstrap': "angular-bootstrap/ui-bootstrap-tpls.js",
},
},
};
};
function PlunkerModuleReplacementPlugin () {
this.resourceRegExp = /^plunker(?:\.(\w+))+$/;
}
PlunkerModuleReplacementPlugin.prototype.apply = function (compiler) {
var resourceRegExp = this.resourceRegExp;
compiler.plugin("normal-module-factory", function (nmf) {
nmf.plugin("before-resolve", function (result, callback) {
if (!result) return callback();
if (resourceRegExp.test(result.request)) {
var parts = result.request.split(".").slice(1);
result.request = parts.join("/");
}
return callback(null, result);
});
});
};
I've added a couple custom bits to this webpack config.
PlunkerModuleReplacementPlugin
ng-loader
PlunkerModuleReplacementPlugin is used to map semantic module names to their filesystem paths. It will only affect requires with package names having the plunker.
prefix. For example: require('plunker.states.layout')
will be re-mapped to require('states/layout')
.
ng-loader is a webpack loader that will basically allow me to inject some code at the end of a vendor module so that I can export the module as per CommonJS
. In the loaders section of the webpack.config.js
file, note the ng-loader?ui-router
; what that does is that it will inject module.exports = window.angular.module('ui-router');
at the end of the file.
I don't like having two mechanisms for locating and managing dependencies: 1) physical location of package (and filesystem name); 2) angular module name. This system lets me keep things very clean and self-contained:
require("./layout/layout.less");
var Angular = require("angular");
module.exports =
Angular.module("plunker.states.layout", [
require("plunker.components.searchBox").name,
require("plunker.components.userMenu").name,
require("plunker.controllers.search").name,
])
.config(["$stateProvider", function ($stateProvider) {
$stateProvider.state("layout", {
virtual: true,
views: {
'': {
template: require("./layout/layout.html"),
},
},
});
}])
;
plunker-web-rewrite/src
___ apps
___ ___ plunker.js
___ common
___ ___ colors.less
___ ___ flexbox.less
___ ___ material.less
___ components
___ ___ markdown
___ ___ ___ markdown.less
___ ___ markdown.js
___ ___ plunkCard
___ ___ ___ plunkCard.html
___ ___ ___ plunkCard.less
___ ___ plunkCard.js
___ ___ searchBox
___ ___ ___ searchBox.html
___ ___ ___ searchBox.less
___ ___ searchBox.js
___ ___ userMenu
___ ___ ___ userMenu.html
___ ___ userMenu.js
___ controllers
___ ___ search.js
___ services
___ ___ api.js
___ ___ visitor.js
___ states
___ layout
___ ___ explore
___ ___ ___ explore.html
___ ___ ___ explore.less
___ ___ explore.js
___ ___ landing
___ ___ ___ landing.html
___ ___ ___ landing.less
___ ___ ___ tb-left.html
___ ___ landing.js
___ ___ layout.html
___ ___ layout.less
___ ___ plunk
___ ___ ___ plunk.html
___ ___ ___ plunk.less
___ ___ ___ runner.html
___ ___ ___ tb-left.html
___ ___ plunk.js
___ layout.js