Upon reflection on this experience, I feel that the build process using tools like Grunt and Gulp is a step backwards. I have nothing against those projects, but I feel that the process could be a lot simpler and not require learning extensive build apis/plugins/configs. (I have nothing against learning new things... in fact I love to! But the end result has to be worth it.)
My idea is that the build configuration is the source code... specifically, it is the index.html page. If you take a hard look at index.html, you realize that it is already a configuration document for your app. It glues the parts of the application together (css, scripts, images). Here is an example index.html using angular that could be run without a build step.
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
<link rel="shortcut icon" href="assets/favicon.ico" />
<!-- css -->
<!-- framework -->
<link href="../bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
<link href="../bower_components/bootswatch/lumen/bootstrap.min.css" rel="stylesheet" />
<!-- app -->
<link href="app.css" rel="stylesheet" />
</head>
<body>
<!-- content -->
<ui-view></ui-view>
<!-- js -->
<!-- framework -->
<script src="../bower_components/angular/angular.min.js"></script>
<script src="../bower_components/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="../bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
<!-- app -->
<script src="app.module.js"></script>
<script src="app.config.js"></script>
<script src="app.routes.js"></script>
</body>
</html>
<html ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
<link rel="shortcut icon" href="assets/favicon.ico" />
<!-- css -->
<!-- framework -->
<link href="../bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
<link href="../bower_components/bootswatch/lumen/bootstrap.min.css" rel="stylesheet" />
<!-- app -->
<link href="app.css" rel="stylesheet" />
</head>
<body>
<!-- content -->
<ui-view></ui-view>
<!-- js -->
<!-- framework -->
<script src="../bower_components/angular/angular.min.js"></script>
<script src="../bower_components/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="../bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
<!-- app -->
<script src="app.module.js"></script>
<script src="app.config.js"></script>
<script src="app.routes.js"></script>
</body>
</html>
So my idea is that the build process reads this file and uses it as a configuration. For a developer build, the app could be copied as-is (since it is valid, run-able html as-is). For release, my imaginary web compiler would bundle and minify referenced items. CSS urls will also have to be resolved for things like external font assets. Then the contents would be included inline in the output index.html file. It's like compiling the application to a single executable in the desktop world. That result might look something like this (whitespace included for readability):
<!DOCTYPE html>
<html ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
<link rel="shortcut icon" href="assets/favicon.ico" />
<style>/* all css here */</style>
</head>
<body>
<!-- content -->
<ui-view></ui-view>
<script>/* all js here */</script>
</body>
</html>
<html ng-app>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
<link rel="shortcut icon" href="assets/favicon.ico" />
<style>/* all css here */</style>
</head>
<body>
<!-- content -->
<ui-view></ui-view>
<script>/* all js here */</script>
</body>
</html>
xkcd |
One issue with "compiling" this is that angular (and likely other frameworks) partial page templates are wired up in javascript code, not from HTML. But there is a convenient way to specify your templates in HTML using a $templateCache feature in angular. Just include them as script tags.
<!-- templates -->
<script type="text/ng-template" src="module/template.html"></script>
<script type="text/ng-template" src="module/template.html"></script>
This is a pretty powerful convention. During dev, this has the effect of preloading the template into $templateCache. Then when your module requests this URI, it is loaded from cache instead of issuing a new GET request. During build, this would tell my imaginary web compiler where to locate the template so its contents can be included inline.
The dream here is that the process of developing the web application also defines the build process for free. I believe this is imminently possible because the main html page already serves as a configuration for the application. Some conventions may be needed so that run-able HTML can serve as build configuration. One convention might be compiling on save for TypeScript, Coffee, LESS, SASS, etc (since browsers can't process these uncompiled... yet). Another could be pre-loading templates.
Sometimes these conventions would not be desirable. And I'm sure there are other issues that could be mentioned -- like the annoyance of having to manually add references to index.html. But in the end, I think this could get you 90% of the way towards a working build setup, and a combination of IDE tooling (to auto-add references, among other things) and other build tools can be applied to get the remaining 10%. As it stands now, we are using the complex build tools to solve 100% of a problem which is 90% easy.