├── .babelignore
├── .babelrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jscsrc
├── .jshintignore
├── .jshintrc
├── .npmignore
├── .nvmrc
├── .travis.yml
├── CHANGELOG.MD
├── JSCS.intellij.formatter.xml
├── LICENSE.MD
├── README.md
├── UPGRADE.MD
├── gulp
├── config.js
├── tasks
│ ├── check-js-quality.js
│ ├── check-js-style.js
│ ├── clean.js
│ ├── scripts-javascript-dist.js
│ ├── test-unit.js
│ └── validate-package-json.js
└── utils.js
├── gulpfile.babel.js
├── karma.conf.js
├── package.json
└── src
├── gulp
├── abstractTaskLoader.js
├── config.js
├── tasks
│ ├── check-js-quality.js
│ ├── check-js-style.js
│ ├── clean.js
│ ├── copy.js
│ ├── default.js
│ ├── html.js
│ ├── images.js
│ ├── scripts-javascript-dist.js
│ ├── scripts-javascript.js
│ ├── scripts-typescript.js
│ ├── serve-dist.js
│ ├── serve.js
│ ├── styles-dist.js
│ ├── styles-vendor-dist.js
│ ├── styles.js
│ ├── test-unit.js
│ ├── ts-lint.js
│ └── validate-package-json.js
├── templates
│ └── taskLoaderTemplate.js
└── utils.js
├── index.js
└── tests
└── sanity_test.spec.js
/.babelignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsebastien/modernWebDevBuild/6fb5ddb10bfd56d022d2d746da0d92cc174fb1c6/.babelignore
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"],
3 | "plugins": ["transform-es2015-modules-commonjs"],
4 | "comments": false
5 | }
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs.
2 | # More information at http://editorconfig.org
3 |
4 | # top-most EditorConfig file
5 | root = true
6 |
7 | # Unix-style newlines with a newline ending every file.
8 | # Tab for indentation, whitespace trimming and UTF-8 encoding
9 | [*]
10 | end_of_line = lf
11 | insert_final_newline = true
12 | indent_style = space
13 | indent_size = 4
14 | trim_trailing_whitespace = false
15 | charset = utf-8
16 |
17 | [*.bat]
18 | end_of_line = crlf
19 |
20 | [{package.json,.travis.yml}]
21 | indent_style = space
22 | indent_size = 2
23 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project specific
2 | *.log
3 | dist/
4 |
5 | # NPM
6 | node_modules/
7 |
8 | # JSPM
9 | jspm_packages/
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov/
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage/
16 |
17 | # Windows
18 | Desktop.ini
19 |
20 | # JetBrains IDEs
21 | *.iml
22 | .idea/
23 | .webstorm/
24 |
25 | # OSX
26 | .DS_Store
27 | .AppleDouble
28 | .LSOverride
29 | # Icon must end with two \r
30 | Icon
31 |
32 | # Sublime text
33 | .sublime-gulp.cache
34 |
--------------------------------------------------------------------------------
/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true,
3 | "validateAlignedFunctionParameters": {
4 | "lineBreakAfterOpeningBraces": false,
5 | "lineBreakBeforeClosingBraces": false
6 | },
7 | "validateIndentation": 4,
8 | "validateNewlineAfterArrayElements": {
9 | "maximum": 2
10 | },
11 | "validateParameterSeparator": ", ",
12 | "validateQuoteMarks": {
13 | "mark": "\"",
14 | "escape": true
15 | },
16 | "excludeFiles": [
17 | "node_modules/**",
18 | "jspm_packages/**"
19 | ],
20 | "fileExtensions": [
21 | ".js"
22 | ],
23 | "requireSemicolons": true,
24 | "maximumLineLength": null,
25 | "disallowTrailingComma": true,
26 | "disallowTrailingWhitespace": "ignoreEmptyLines",
27 | "disallowDanglingUnderscores": {
28 | "allExcept": [
29 | "_exception"
30 | ]
31 | },
32 | "disallowEmptyBlocks": null,
33 | "disallowImplicitTypeConversion": null,
34 | "disallowKeywordsOnNewLine": [
35 | "else"
36 | ],
37 | "disallowKeywords": [
38 | "with"
39 | ],
40 | "disallowMixedSpacesAndTabs": true,
41 | "disallowMultipleLineBreaks": true,
42 | "disallowMultipleSpaces": null,
43 | "disallowNamedUnassignedFunctions": true,
44 | "disallowNewlineBeforeBlockStatements": true,
45 | "requirePaddingNewLinesAfterBlocks": {
46 | "allExcept": [
47 | "inCallExpressions",
48 | "inArrayExpressions",
49 | "inProperties"
50 | ]
51 | },
52 | "disallowPaddingNewlinesInBlocks": true,
53 | "requireSpaceAfterBinaryOperators": [
54 | "=",
55 | ",",
56 | "+",
57 | "-",
58 | "/",
59 | "*",
60 | "==",
61 | "===",
62 | "!=",
63 | "!=="
64 | ],
65 | "requireSpaceBeforeBinaryOperators": [
66 | "=",
67 | "+",
68 | "-",
69 | "/",
70 | "*",
71 | "==",
72 | "===",
73 | "!=",
74 | "!=="
75 | ],
76 | "disallowSpaceAfterKeywords": [
77 | "if",
78 | "else",
79 | "for",
80 | "while",
81 | "do",
82 | "switch",
83 | "try",
84 | "catch",
85 | "function"
86 | ],
87 | "disallowSpaceAfterPrefixUnaryOperators": [
88 | "++",
89 | "--",
90 | "+",
91 | "-",
92 | "~",
93 | "!"
94 | ],
95 | "disallowSpaceBeforeBlockStatements": true,
96 | "requireSpaceBeforeKeywords": [
97 | "if",
98 | "else",
99 | "try",
100 | "catch"
101 | ],
102 | "disallowSpaceBeforePostfixUnaryOperators": [
103 | "++",
104 | "--"
105 | ],
106 | "requireSpaceBetweenArguments": true,
107 | "disallowSpacesInAnonymousFunctionExpression": {
108 | "beforeOpeningRoundBrace": true,
109 | "beforeOpeningCurlyBrace": true
110 | },
111 | "disallowSpacesInCallExpression": true,
112 | "disallowSpacesInFunctionDeclaration": {
113 | "beforeOpeningRoundBrace": true,
114 | "beforeOpeningCurlyBrace": true
115 | },
116 | "disallowSpacesInFunctionExpression": {
117 | "beforeOpeningRoundBrace": true,
118 | "beforeOpeningCurlyBrace": true
119 | },
120 | "disallowSpacesInsideParentheses": true,
121 | "disallowSpacesInFunction": {
122 | "beforeOpeningRoundBrace": true
123 | },
124 | "disallowSpacesInNamedFunctionExpression": {
125 | "beforeOpeningRoundBrace": true,
126 | "beforeOpeningCurlyBrace": true
127 | },
128 | "requireSpacesInsideArrayBrackets": "all",
129 | "requireSpacesInsideObjectBrackets": {
130 | "allExcept": [
131 | "}",
132 | ")"
133 | ]
134 | },
135 | "requireSpacesInsideParentheses": null,
136 | "requireYodaConditions": null,
137 | "requireAnonymousFunctions": true,
138 | "requireBlocksOnNewline": true,
139 | "requireCamelCaseOrUpperCaseIdentifiers": true,
140 | "requireCapitalizedConstructors": true,
141 | "requireCommaBeforeLineBreak": true,
142 | "requireCurlyBraces": true,
143 | "requireDotNotation": true,
144 | "requireFunctionDeclarations": null,
145 | "requireLineBreakAfterVariableAssignment": true,
146 | "requireLineFeedAtFileEnd": true,
147 | "disallowMultipleVarDecl": true,
148 | "requireOperatorBeforeLineBreak": [
149 | "?",
150 | "=",
151 | "+",
152 | "-",
153 | "/",
154 | "*",
155 | "==",
156 | "===",
157 | "!=",
158 | "!==",
159 | ">",
160 | ">=",
161 | "<",
162 | "<="
163 | ],
164 | "requirePaddingNewLineAfterVariableDeclaration": true,
165 | "requirePaddingNewLinesAfterUseStrict": true,
166 | "requirePaddingNewLinesBeforeExport": true,
167 | "requirePaddingNewlinesBeforeKeywords": [
168 | "do",
169 | "for",
170 | "if",
171 | "switch",
172 | "case",
173 | "try",
174 | "void",
175 | "while",
176 | "with",
177 | "return",
178 | "typeof",
179 | "function"
180 | ],
181 | "requirePaddingNewLinesBeforeLineComments": null,
182 | "requireParenthesesAroundIIFE": null,
183 | "requireSpaceAfterLineComment": null,
184 | "requireSpacesInConditionalExpression": {
185 | "afterTest": true,
186 | "beforeConsequent": true,
187 | "afterConsequent": true,
188 | "beforeAlternate": true
189 | },
190 | "requireSpacesInForStatement": true,
191 | "requirePaddingNewLinesInObjects": true,
192 | "disallowQuotedKeysInObjects": true,
193 | "requireAlignedObjectValues": null,
194 | "disallowSpaceAfterObjectKeys": true,
195 | "requireSpaceBeforeObjectValues": true,
196 | "disallowSpaceBeforeComma": true,
197 | "disallowSpaceBeforeSemicolon": true,
198 | "requireVarDeclFirst": null,
199 | "requireMatchingFunctionName": true,
200 | "requireObjectKeysOnNewLine": true,
201 | "disallowNodeTypes": [
202 | "LabeledStatement"
203 | ],
204 | "requireArrowFunctions": true,
205 | "requireNumericLiterals": true,
206 | "requireTemplateStrings": null
207 | }
208 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | jspm_packages/**/*
3 | jspm.conf.js
4 | dist/**/*
5 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true,
3 | "strict": "global",
4 | "devel": true,
5 | "browser": true,
6 | "forin": true,
7 | "curly": true,
8 | "bitwise": true,
9 | "eqeqeq": true,
10 | "freeze": true,
11 | "futurehostile": true,
12 | "latedef": "nofunc",
13 | "maxdepth": 5,
14 | "maxparams": 10,
15 | "maxstatements": 100,
16 | "noarg": true,
17 | "nocomma": true,
18 | "nonbsp": true,
19 | "nonew": true,
20 | "singleGroups": true,
21 | "undef": true,
22 | "unused": true,
23 | "jquery": true,
24 | "mocha": true,
25 | "jasmine": true,
26 | "module": true,
27 | "varstmt": true,
28 | "eqnull": true,
29 | "globals": {
30 | "protractor": false,
31 | "module": false,
32 | "require": false,
33 | "process": false,
34 | "someWordYouShouldNotCheck": false
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Reference: https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package
2 |
3 | # Project specific
4 | src/
5 | gulp/
6 | test/
7 | coverage/
8 | .coveralls.yml
9 | .editorconfig
10 | .jscsrc
11 | .jshintignore
12 | .jshintrc
13 | .travis.yml
14 | .nvmrc
15 | gulpfile.babel.js
16 | gulpfile.js
17 |
18 | # Misc
19 | *.log
20 |
21 | # NPM
22 | node_modules/
23 | npm-debug.log
24 | ./.npmignore
25 |
26 | # Windows
27 | Desktop.ini
28 |
29 | # JetBrains IDEs
30 | *.iml
31 | .idea/
32 | .webstorm/
33 |
34 | # Git
35 | .git
36 | .gitattributes
37 | .gitignore
38 | .gitmodules
39 |
40 | # OSX
41 | .DS_Store
42 | .AppleDouble
43 | .LSOverride
44 | # Icon must end with two \r
45 | Icon
46 |
47 | # Sublime text
48 | .sublime-gulp.cache
49 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 4.3.0
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | - jspm_packages
7 | branches:
8 | only:
9 | - master
10 | notifications:
11 | email: true
12 | node_js:
13 | - '4.2'
14 | before_script:
15 | - npm prune
16 | before_install:
17 | - npm run setup
18 |
--------------------------------------------------------------------------------
/CHANGELOG.MD:
--------------------------------------------------------------------------------
1 | * 0.5.4
2 | * fixed #130 (undefined.emit)
3 | * 0.5.3
4 | * now less strict with dependencies (#118)
5 | * updated dependencies
6 | * 0.5.2
7 | * added tsx support for watches (#110)
8 | * replaced gulp-minify-css by gulp-clean-css
9 | * 0.5.1
10 | * fixed issue with the production build (404 at first render)
11 | * 0.5.0
12 | * upgraded dependencies to support TypeScript 1.8.x
13 | * 0.4.1
14 | * added the "browserSync" option, which lets you customize all BrowserSync settings (thanks @aaronroberson)
15 | * 0.4.0
16 | * fixed an issue with production styles bundle (fixes #96)
17 | * upgraded babel dependencies
18 | * check the [upgrade](UPGRADE.MD) notes to know what you need to change
19 | * 0.3.2
20 | * Fixed production bundles paths (css, js) (fixes #91)
21 | * 0.3.1
22 | * made JSPM optional (closes #77)
23 | * JSPM remains used by default but you can now customize the behavior by setting the useJSPM option to false
24 | * if you disable JSPM then you can provide a custom name for the configuration file (the build defaults to jspm.conf.js)
25 | * fixed an issue with the tasks chaining (callbacks were used needlessly) (closes #89)
26 | * upgraded the version of SystemJS-builder (closes #87)
27 | * 0.3.0
28 | * Breaking change: improved integration with tsconfig.json (see #72)
29 | * this might impact existing projects
30 | * before, the list of folders to include in TS compilation were hardcoded. Now, the tsconfig.json file is used, thus you have more control (but also more responsibility)
31 | * removed mentions of tsd. My current recommendation is to use `typings`, although there's nothing forcing you to with the build
32 | * https://github.com/typings/typings
33 | * https://angularclass.com/the-state-of-typescript-packages/
34 | * upgraded dependencies
35 | * Babel dependencies 6.3.x to 6.4.x
36 | * autoprefixer 6.1.x to 6.2.x
37 | * Known issues
38 | * #76: there is an issue with the way that gulp-typescript (the plugin used within the scripts-typescript gulp task) handles the 'rootDir' property, which is inconsistent with tsc.
39 | * to solve this, for now you need to add a reference to the main typings file of your project; Example:
40 | ```
41 | ///
42 | ```
43 | * the final solution might come via the following issues and might require a new release of this project
44 | * https://github.com/ivogabe/gulp-typescript/issues/190
45 | * https://github.com/typings/typings/issues/69
46 | * 0.2.3
47 | * fixed sourcemap generation issues (see #69)
48 | * sourcemaps sometime require a browser refresh before actually working: this might be due to a related issue (timing issue?)
49 | * removed legacy leftovers
50 | * 0.2.2
51 | * added an option to define whether the production HTML should be minified or not: minifyProductionHTML (true by default)
52 | * Angular 2 does not support HTML minification anymore: https://github.com/dsebastien/modernWebDevBuild/issues/67
53 | * 0.2.1
54 | * minify the production bundle by default
55 | * added the gulp-inline-source plugin (https://www.npmjs.com/package/gulp-inline-source)
56 | * allows to inline resources (scripts, stylesheets, images) within HTML files
57 | * for example very useful for cases where some script can't/musn't be loaded through SystemJS (e.g., zone.js, reflect-metadata, Angular 2 polyfills, ...)
58 | * added an option to define whether the production JS bundle must be minified or not: minifyProductionJSBundle (true by default)
59 | * added an option to define whether the production JS bundle must be mangled or not: mangleProductionJSBundle (true by default)
60 | * 0.2.0
61 | * BREAKING CHANGE: scripts-javascript-dist now uses core/boot.js as entry point (vs core/core.bootstrap.js in earlier versions) (fixes #
62 | * this new default behavior can be customized by specifying the `distEntryPoint` option (see README or UPGRADE guide)
63 | * 0.1.1
64 | * added unit testing support with karma & jasmine
65 | * removed gulp-tsd dependency
66 | * removed tsd dependency
67 | * fix for #33 avoid the need for installing global dependencies
68 | * added support for passing options to the build (for now only used internally)
69 | * tagged dependencies that should be in client projects as peerDependencies
70 | * removed babel 6 plugins/presets from the peerDependencies
71 | * client projects are free to choose what they want to use
72 | * 0.1.0
73 | * Upgraded to Babel 6
74 | * now requires a .babelrc configuration file to be present in the project's main folder
75 | * now requires additional Babel 6 dependencies:
76 | * "babel-core": "6.1.x",
77 | * "babel-plugin-transform-es2015-modules-commonjs": "6.1.x",
78 | * "babel-preset-es2015": "6.1.x",
79 | * "babel-runtime": "6.1.x",
80 | * Updated dependencies
81 | * 0.0.9
82 | * Fixed watch/reload issues with BrowserSync
83 | * Removed the 'gen-ts-refs' task. No longer needed now that TS can get typings from node modules
84 | * Updated docs
85 | * 0.0.8
86 | * Added support for both TypeScript and ES2015
87 | * Changed back the way the build is organized
88 | * TS > ES5
89 | * ES2015 > ES5
90 | * Removed unnecessary package.json validation
91 | * 0.0.7
92 | * Fixed an npm publish issue (configured files and folders to include and exclude from the page)
93 | * 0.0.6
94 | * Updated dependencies
95 | * Fixed an npm publish issue
96 | * 0.0.5
97 | * First functional version ;-)
98 | * 0.0.4
99 | * Fixed an issue with Gulp and Babel
100 | * 0.0.3
101 | * Fixed an issue with Gulp and Babel
102 | * 0.0.2
103 | * Fixed an issue with node-sass
104 | * 0.0.1
105 | * Initial version
106 |
--------------------------------------------------------------------------------
/JSCS.intellij.formatter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/LICENSE.MD:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) dSebastien (https://www.dsebastien.net)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Modern Web Dev Build
2 |
3 | [](https://www.npmjs.com/package/modern-web-dev-build)
4 | [](https://www.npmjs.com/package/modern-web-dev-build)
5 | [](https://travis-ci.org/dsebastien/modernWebDevBuild)
6 | [](
9 | https://coveralls.io/github/dsebastien/modernWebDevBuild?branch=master
10 | )
11 | [](https://david-dm.org/dsebastien/modernWebDevBuild)
12 | [](https://david-dm.org/dsebastien/modernWebDevBuild#info=devDependencies)
13 | [](https://gitter.im/dsebastien/modernWebDevBuild?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
14 | [](LICENSE.MD)
15 |
16 | ## About
17 | A modern build for Web development.
18 |
19 | Get started and use ES2015, TypeScript, SASS, code quality & style checking, testing, minification, bundling and whatnot TODAY! :)
20 |
21 | ModernWebDevBuild abstracts away all the build pipeline boilerplate. Use it if you're not willing to dive too deep in the boring details of how to setup a proper build chain that takes care of transpiling, minifying, optimizing images and whatnot for production.
22 |
23 | This project is very opinionated and technology choices are embedded. Although, the build is pretty flexible about code/assets organization (to some extent). Over time, it'll be interesting to see how customizable we can make this thing.
24 |
25 | The provided build tasks are based on [Gulp](http://gulpjs.com/). Instructions are available below to get you started.
26 |
27 | This project is available as an npm package: https://www.npmjs.com/package/modern-web-dev-build
28 |
29 | ## Demo
30 |
33 |
34 | ## Background
35 |
36 | The idea for this project emerged as I was rediscovering the state of the art for Web development (early 2015) and from my frustration of not finding everything I needed in a ready-to-use form.
37 |
38 | What surprised me at first was that tooling had become so much more complex than it was in the past. I would argue that it is way too complex nowadays and that isn't good for the accessibility of the Web platform. Unfortunately for now, there aren't many alternatives and the benefits of a good build chain are too important to keep aside (who wouldn't want to use all the good stuff ES2015 has brought us?).
39 |
40 | Note that this project is heavily inspired from:
41 | * Google's [Web Starter Kit](https://github.com/google/web-starter-kit)
42 | * Countless blog articles about Gulp, TypeScript, ...
43 | * Many others I'm forgetting :(
44 |
45 | ## Features
46 | * ES2015 and TypeScript support
47 | * built-in HTTP server with live reloading & cross-device synchronization (BrowserSync)
48 | * configured to support CORS
49 | * awesome developer experience with a change detection mechanism that automagically:
50 | * transpiles TypeScript > ESx w/ sourcemaps (you choose the target version)
51 | * transpiles ES2015 > ESx w/ sourcemaps (you choose the target version)
52 | * transpiles SASS > CSS w/ sourcemaps
53 | * checks JavaScript/TypeScript code quality/style and report on the console (without breaking the build)
54 | * ...
55 | * production bundle creation support with:
56 | * CSS bundle creation
57 | * CSS optimization & minification
58 | * JS bundle creation
59 | * JS minification
60 | * HTML minification
61 | * images optimization
62 | * ...
63 |
64 | Check out the [change log](CHANGELOG.MD)
65 |
66 | ## Status & roadmap
67 | Check out the issues/labels & milestones to get an idea of what's next.
68 | For existing features, refer to the previous section.
69 |
70 | ## Embedded choices
71 | As state above, some important technology choices are clearly embedded with this project. Here's a rundown of those choices:
72 | * [TypeScript](http://www.typescriptlang.org/) and ES2015 (although the final output is ES5 for wider compatibility)
73 | * [SystemJS](https://github.com/systemjs/systemjs): module loader
74 | * [JSPM](http://jspm.io/) to manage your application dependencies (through jspm.conf.js)
75 | * JSPM support can be disabled if you wish
76 | * [Karma](http://karma-runner.github.io/) to run tests
77 | * [SASS](http://sass-lang.com/): who doesn't want variables and mixins?
78 | * component based code & assets organization (Angular friendly)
79 | * [JSCS](http://jscs.info/) and included code style rules
80 | * [JSHint](http://jshint.com/) and included code quality rules
81 | * [TSLint](https://github.com/palantir/tslint) and included code quality/style rules
82 | * [BrowserSync](http://www.browsersync.io/) development Web Server
83 |
84 | ## Upgrade
85 | Check out the [upgrade](UPGRADE.md) page
86 |
87 | ## Installation
88 |
89 | ### General prereqs
90 | Before you install the build (whether manually or through the project generator), you need to install some dependencies globally:
91 | * `npm install --global gulp`
92 |
93 | ### New projects
94 | The easiest approach to integrate this build is to use our Yeoman Generator available over at https://github.com/dsebastien/modernWebDevGenerator and on npm: https://www.npmjs.com/package/generator-modern-web-dev.
95 | The generator will set up (almost) everything for you.
96 |
97 | ### Existing projects
98 | First configure the required dependencies in your package.json file:
99 | * add Modern Web Dev Build to your devDependencies: `npm install modern-web-dev-build --save-dev`
100 | * execute `npm install --no-optional`
101 |
102 | You should get warnings about missing peer dependencies. Those are dependencies that are required by the build but that you should add to your own project.
103 | Install these one by one.
104 |
105 | For now the required peer dependencies are as follows:
106 | * babel-core
107 | * gulp
108 | * jspm
109 | * nodemon (required by recommended npm scripts)
110 | * typescript
111 |
112 | Next, check the minimal require file contents below!
113 |
114 | ## Required folder structure and files
115 | The build tries to provide a flexible structure, but given the technical choices that are embedded, some rules must be respected and the build expects certain folders and files to be present. In the future we'll see if we can make this more configurable.
116 |
117 | ### Mandatory folder structure & files
118 | Here's an overview of the structure imposed by ModernWebDevBuild.
119 | Note that if you've generated your project using the Yeoman generator, README files will be there to guide you.
120 |
121 | Please make sure to check the file organization section for more background about the organization and usage guidelines.
122 |
123 | * project root
124 | * app: folder containing all the files of the application
125 | * components: folder containing components of your application (e.g., login, menu, ...); basically reusable pieces
126 | * core: folder containing at least the entrypoint of your application
127 | * commons: folder containing common reusable code (e.g., base utilities)
128 | * services: folder containing generic services (e.g., for local storage)
129 | * boot.ts: the entrypoint of your application
130 | * app.ts: the application root
131 | * fonts: folder containing fonts of your application (if any)
132 | * images: folder for image assets
133 | * pages: folder for full-blown pages of your application
134 | * scripts: folder for scripts
135 | * styles: folder for the main stylesheets
136 | * main.scss: file used to import all application-specific stylesheets
137 | * vendor.scss: file used to import all third-party stylesheets
138 | * note that the goal isn't to put ALL your stylesheets in there, basically just the entrypoints and the generic parts (e.g., variables, mixins, responsive styles, ...)
139 | * index.html: the entrypoint of your application
140 | * .babelrc: Babel configuration file
141 | * .jscsrc: JSCS rule set to use while checking JavaScript code style
142 | * reference: http://jscs.info/overview
143 | * .jshintrc: JSHint rule set to use while checking JavaScript code quality
144 | * reference: http://jshint.com/docs/
145 | * note that the file is actually optional but indeed recommended!
146 | * .jshintignore: files and folders to ignore while checking JavaScript code quality
147 | * gulpfile.babel.js: gulp configuration file
148 | * jspm.conf.js: SystemJS/JSPM configuration file
149 | * can have another name if you do not use JSPM (see options)
150 | * karma.conf.js: Karma configuration file (configuration of the test runner)
151 | * package.json: NPM configuration file (also used by JSPM)
152 | * tsconfig.json: TypeScript compiler configuration
153 | * tslint.json: TypeScript code quality/style rules
154 |
155 | ### Minimal (build-related) required file contents
156 | Although we want to limit this list as much as possible, for everything to build successfully, some files need specific contents:
157 |
158 | #### .babelrc
159 | ```
160 | {
161 | "presets": ["es2015"],
162 | "plugins": ["transform-es2015-modules-commonjs"],
163 | "comments": false
164 | }
165 | ```
166 |
167 | With the configuration above, Babel will transpile ES2015 code to ES5 commonjs.
168 | For that configuration to work, the following devDependencies must also be added to your project:
169 |
170 | ```
171 | "babel-plugin-transform-es2015-modules-commonjs": "6.3.x",
172 | "babel-preset-es2015": "6.3.x",
173 | ```
174 |
175 | #### gulpfile.babel.js
176 | In order to use ModernWebDevBuild, your gulpfile must at least contain the following.
177 | The code below uses ES2015 (via gulpfile.babel.js), but if you're old school you can also simply use a gulpfile.js with ES5.
178 | Note that the build tasks provided by ModernWebDevBuild are transpiled to ES5 before being published
179 |
180 | ```
181 | "use strict";
182 |
183 | import gulp from "gulp";
184 |
185 | import modernWebDevBuild from "modern-web-dev-build";
186 | let options = undefined; // no options are supported yet
187 |
188 | //options.minifyHTML = false;
189 | //...
190 |
191 | modernWebDevBuild.registerTasks(gulp, options);
192 | ```
193 |
194 | With the above, all the gulp tasks provided by ModernWebDevBuild will be available to you.
195 |
196 | #### .jscsrc
197 | Valid configuration
198 |
199 | #### .jshintrc
200 | At least the following:
201 |
202 | ```
203 | node_modules/**/*
204 | jspm_packages/**/*
205 | jspm.conf.js
206 | dist/**/*
207 | .tmp/**/*
208 | ```
209 |
210 | #### jspm.conf.js
211 | The SystemJS/JSPM configuration file plays a very important role;
212 | * it is where all your actual application dependencies are to be defined
213 | * it is where you can define your own 'paths', allowing you to load modules of your application easily without having to specify relative paths (i.e., create aliases for paths).
214 |
215 | If you choose to use the default JSPM support, then you can add dependencies to your project using `jspm install`; check the [official JSPM documentation](http://jspm.io/) to know more about how to install packages.
216 |
217 | With the help of this configuration file, SystemJS will be able to load your own application modules and well as third party dependencies.
218 | In your code, you'll be able to use ES2015 style (e.g., `import {x} from "y"`). In order for this to work, you'll also need to load SystemJS and the SystemJS/JSPM configuration file in your index.html (more on this afterwards).
219 |
220 | If you have disabled the use of JSPM by the build then you can rename that file if you wish and inform the build (see the options), BUT make sure that any reference in the various configuration files is updated (e.g., karma configuration, jshint configuration, etc). Only rename it if it is really really useful to you :)
221 |
222 | Here's an example:
223 | ```
224 | System.config({
225 | defaultJSExtensions: true,
226 | transpiler: false,
227 | paths: {
228 | "github:*": "jspm_packages/github/*",
229 | "npm:*": "jspm_packages/npm/*",
230 | "core/*": "./.tmp/core/*",
231 | "pages/*": "./.tmp/pages/*"
232 | }
233 | });
234 | ```
235 |
236 | In the above:
237 | * defaultJSExtensions: is mandatory so that extensions don't have to be specified when importing modules
238 | * transpiler: is set to false because we don't use in-browser transpilation
239 | * paths
240 | * core/*, pages/* allow you to import modules from different parts of your codebase without having to specify relative or absolute paths. This is covered in the folder structure section above.
241 | * you can rename those if you wish
242 |
243 | #### package.json
244 | In addition to the dependencies listed previously, you also need to have the following in your package.json file, assuming that you want to use JSPM:
245 |
246 | ```
247 | "jspm": {
248 | "directories": {},
249 | "configFile": "jspm.conf.js",
250 | "dependencies": {
251 | },
252 | "devDependencies: {
253 | }
254 | }
255 | ```
256 |
257 | This is where you let JSPM know where to save the information about dependencies you install. This is also where you can easily add new dependencies; for example: `"angular2": "npm:angular2@^2.0.0-beta.1",`.
258 | Once a dependency is added there, you can invoke `jspm install` to get the files and transitive dependencies installed and get an updated jspm.conf.js file.
259 |
260 | #### tsconfig.json
261 | Given that TypeScript is one of the (currently) embedded choices of this project, the TypeScript configuration file is mandatory.
262 |
263 | The tsconfig.json file contains:
264 | * the configuration of the TypeScript compiler
265 | * TypeScript code style rules
266 | * the list of files/folders to include/exclude
267 |
268 | Here's is the minimal required contents for ModernWebDevBuild. Note that the outDir value is important as it tells the compiler where to write the generated code! Make sure that you also DO have the rootDir property defined and pointing to "./app", otherwise the build will fail (more precisely, `npm run serve` will fail).
269 |
270 | The build depends on the presence of those settings.
271 |
272 | ```
273 | {
274 | "version": "1.7.3",
275 | "compilerOptions": {
276 | "target": "es5",
277 | "module": "commonjs",
278 | "declaration": false,
279 | "noImplicitAny": true,
280 | "suppressImplicitAnyIndexErrors": true,
281 | "removeComments": false,
282 | "emitDecoratorMetadata": true,
283 | "experimentalDecorators": true,
284 | "noEmitOnError": false,
285 | "preserveConstEnums": true,
286 | "inlineSources": false,
287 | "sourceMap": false,
288 | "outDir": "./.tmp",
289 | "rootDir": "./app",
290 | "moduleResolution": "node",
291 | "listFiles": false
292 | },
293 | "exclude": [
294 | "node_modules",
295 | "jspm_packages",
296 | "typings/browser",
297 | "typings/browser.d.ts"
298 | ]
299 | }
300 | ```
301 |
302 | Here's a more complete example including code style rules:
303 |
304 | ```
305 | {
306 | "version": "1.7.3",
307 | "compilerOptions": {
308 | "target": "es5",
309 | "module": "commonjs",
310 | "declaration": false,
311 | "noImplicitAny": true,
312 | "suppressImplicitAnyIndexErrors": true,
313 | "removeComments": false,
314 | "emitDecoratorMetadata": true,
315 | "experimentalDecorators": true,
316 | "noEmitOnError": false,
317 | "preserveConstEnums": true,
318 | "inlineSources": false,
319 | "sourceMap": false,
320 | "outDir": "./.tmp",
321 | "rootDir": "./app",
322 | "moduleResolution": "node",
323 | "listFiles": false
324 | },
325 | "formatCodeOptions": {
326 | "indentSize": 2,
327 | "tabSize": 4,
328 | "newLineCharacter": "\r\n",
329 | "convertTabsToSpaces": false,
330 | "insertSpaceAfterCommaDelimiter": true,
331 | "insertSpaceAfterSemicolonInForStatements": true,
332 | "insertSpaceBeforeAndAfterBinaryOperators": true,
333 | "insertSpaceAfterKeywordsInControlFlowStatements": true,
334 | "insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
335 | "insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
336 | "placeOpenBraceOnNewLineForFunctions": false,
337 | "placeOpenBraceOnNewLineForControlBlocks": false
338 | },
339 | "exclude": [
340 | "node_modules",
341 | "jspm_packages",
342 | "typings/browser",
343 | "typings/browser.d.ts"
344 | ]
345 | }
346 | ```
347 |
348 | Note the exclusion that we have made, all of which are relevant and there to avoid known issues (e.g., https://github.com/typings/discussions/issues/6 if you are using typings).
349 |
350 | #### tslint.json
351 | tslint.json is the configuration file for [TSLint](https://github.com/palantir/tslint).
352 |
353 | Although it is not strictly mandatory (the build will work without this file), we heavily recommend you to use it as it is very useful to ensure a minimal code quality level and can help you avoid common mistakes and unnecessary complicated code:
354 |
355 | Here's an example:
356 | ```
357 | {
358 | "rules": {
359 | "class-name": true,
360 | "curly": true,
361 | "eofline": true,
362 | "forin": true,
363 | "indent": [false, "tabs"],
364 | "interface-name": false,
365 | "label-position": true,
366 | "label-undefined": true,
367 | "max-line-length": false,
368 | "no-any": false,
369 | "no-arg": true,
370 | "no-bitwise": true,
371 | "no-console": [false,
372 | "debug",
373 | "info",
374 | "time",
375 | "timeEnd",
376 | "trace"
377 | ],
378 | "no-construct": true,
379 | "no-debugger": true,
380 | "no-duplicate-key": true,
381 | "no-duplicate-variable": true,
382 | "no-empty": true,
383 | "no-eval": true,
384 | "no-imports": true,
385 | "no-string-literal": false,
386 | "no-trailing-comma": true,
387 | "no-unused-variable": false,
388 | "no-unreachable": true,
389 | "no-use-before-declare": null,
390 | "one-line": [true,
391 | "check-open-brace",
392 | "check-catch",
393 | "check-else",
394 | "check-whitespace"
395 | ],
396 | "quotemark": [true, "double"],
397 | "radix": true,
398 | "semicolon": true,
399 | "triple-equals": [true, "allow-null-check"],
400 | "variable-name": false,
401 | "no-trailing-whitespace": true,
402 | "whitespace": [false,
403 | "check-branch",
404 | "check-decl",
405 | "check-operator",
406 | "check-separator",
407 | "check-type",
408 | "check-typecast"
409 | ]
410 | }
411 | }
412 | ```
413 |
414 | #### karma.conf.js
415 | Karma loads his configuration from karma.conf.js. That file contains everything that Karma needs to know to execute your unit tests.
416 |
417 | Here's an example configuration file that uses Jasmine. Note that the main Karma dependencies including PhantomJS are included in the build.
418 | You only need to add a dependency to jasmine, karma-jasmine and karma-jspm for the following to work.
419 |
420 | If you choose not to use JSPM, then you can use karma-systemjs instead: https://www.npmjs.com/package/karma-systemjs
421 |
422 | Example:
423 | ```
424 | // Karma configuration
425 | // reference: http://karma-runner.github.io/0.13/config/configuration-file.html
426 |
427 | module.exports = function (config) {
428 | config.set({
429 |
430 | // base path that will be used to resolve all patterns (eg. files, exclude)
431 | //basePath: ".tmp/",
432 |
433 | plugins: [
434 | "karma-jspm",
435 | "karma-jasmine",
436 | "karma-phantomjs-launcher",
437 | "karma-chrome-launcher",
438 | "karma-firefox-launcher",
439 | "karma-ie-launcher",
440 | "karma-junit-reporter",
441 | "karma-spec-reporter"
442 | ],
443 |
444 | // frameworks to use
445 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
446 | frameworks: [
447 | "jspm",
448 | "jasmine"
449 | ],
450 |
451 | // list of files / patterns to load in the browser (loaded before SystemJS)
452 | files: [],
453 |
454 | // list of files to exclude
455 | exclude: [],
456 |
457 | // list of paths mappings
458 | // can be used to map paths served by the Karma web server to /base/ content
459 | // knowing that /base corresponds to the project root folder (i.e., where this config file is located)
460 | proxies: {
461 | "/.tmp": "/base/.tmp" // without this, karma-jspm can't load the files
462 | },
463 |
464 | // preprocess matching files before serving them to the browser
465 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
466 | preprocessors: {},
467 |
468 | // test results reporter to use
469 | // possible values: 'dots', 'progress', 'spec', 'junit'
470 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
471 | // https://www.npmjs.com/package/karma-junit-reporter
472 | // https://www.npmjs.com/package/karma-spec-reporter
473 | reporters: ["spec"],
474 |
475 | // web server port
476 | port: 9876,
477 |
478 | // enable / disable colors in the output (reporters and logs)
479 | colors: true,
480 |
481 | // level of logging
482 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
483 | logLevel: config.LOG_INFO,
484 |
485 | // enable / disable watching file and executing tests whenever any file changes
486 | autoWatch: true,
487 |
488 | // start these browsers
489 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
490 | browsers: [
491 | "PhantomJS"
492 | //"Chrome",
493 | //"Firefox",
494 | //"PhantomJS",
495 | //"IE"
496 | ],
497 |
498 | // Continuous Integration mode
499 | // if true, Karma captures browsers, runs the tests and exits
500 | singleRun: false,
501 |
502 | junitReporter: {
503 | outputFile: "target/reports/tests-unit/unit.xml",
504 | suite: "unit"
505 | },
506 |
507 | // doc: https://www.npmjs.com/package/karma-jspm
508 | // reference config: https://github.com/gunnarlium/babel-jspm-karma-jasmine-istanbul
509 | jspm: {
510 | // Path to your SystemJS/JSPM configuration file
511 | config: "jspm.conf.js",
512 |
513 | // Where to find jspm packages
514 | //packages: "jspm_packages",
515 |
516 | // One use case for this is to only put test specs in loadFiles, and jspm will only load the src files when and if the test files require them.
517 | loadFiles: [
518 | // load all tests
519 | ".tmp/*.spec.js", // in case there are tests in the root folder
520 | ".tmp/**/*.spec.js"
521 | ],
522 |
523 | // Make additional files/a file pattern available for jspm to load, but not load it right away.
524 | serveFiles: [
525 | ".tmp/**/!(*.spec).js" // make sure that all files are available
526 | ],
527 |
528 | // SystemJS configuration specifically for tests, added after your config file.
529 | // Good for adding test libraries and mock modules
530 | paths: {}
531 | }
532 | });
533 | };
534 | ```
535 |
536 | Dev dependencies to add for the above Karma configuration:
537 | ```
538 | "jasmine": "...",
539 | "karma-jasmine": "...",
540 | "karma-jspm": "..."
541 | ```
542 |
543 | ### Minimal (application-specific) required file contents
544 | Although we want to limit this list as much as possible, for everything to build successfully, some files need specific contents:
545 |
546 | #### core/app.ts
547 | This should be the top element of your application. This should be loaded by core/boot.ts (see below).
548 |
549 | ```
550 | "use strict";
551 |
552 | export class App {
553 | ...
554 | constructor(){
555 | console.log("Hello world!");
556 | }
557 | }
558 | ```
559 |
560 | #### core/boot.ts
561 | The boot.ts file is the entrypoint of your application. Currently, it is mandatory for this file to exist (with that specific name), although that could change or be customizable later.
562 |
563 | The contents are actually not important but here's a starting point:
564 |
565 | ```
566 | "use strict";
567 |
568 | import {App} from "core/app";
569 | // bootstrap your app
570 | ```
571 |
572 | #### styles/main.scss
573 | The main.scss file is where you should load all the stylesheets scattered around in your application.
574 |
575 | Here's an example of a main.scss:
576 | ```
577 | //
578 | // Main stylesheet.
579 | // Should import all the other stylesheets
580 | //
581 |
582 | // Variables, functions, mixins and utils
583 | @import "base/variables";
584 | @import "base/functions";
585 | @import "base/mixins";
586 | @import "base/utils";
587 |
588 | // Base/generic style rules
589 | @import "base/reset";
590 | @import "base/responsive";
591 | @import "base/fonts";
592 | @import "base/typography";
593 | @import "base/base";
594 |
595 | // Layout
596 | @import "layout/layout";
597 | @import "layout/theme";
598 | @import "layout/print";
599 |
600 | // Components
601 | @import "../components/posts/posts";
602 |
603 | // Pages
604 | @import "../pages/home/home";
605 | ```
606 |
607 | In the example above, you can see that in the main.scss file, we import many other stylesheets (sass partials).
608 |
609 | Here's another example, this time for vendor.scss:
610 | ```
611 | //
612 | // Includes/imports all third-party stylesheets used throughout the application.
613 | // Should be loaded before the application stylesheets
614 | //
615 |
616 | // Nicolas Gallagher's Normalize.css
617 | @import '../../jspm_packages/github/necolas/normalize.css@3.0.3/normalize.css'; // the path refers to the file at BUILD time
618 | ```
619 |
620 | As you can see above, a third-party stylesheet is imported.
621 |
622 |
623 | ### index.html
624 | The index.html file is the entrypoint of your application. It is not mandatory per se for ModernWebDevBuild, but when you run `npm run serve`, it'll be opened. Also, the `html` build task will try and replace/inject content in it.
625 |
626 | Here's the minimal required contents for index.html (required for production builds with minification and bundling):
627 |
628 | ```
629 |
630 |
631 |
632 |
633 | ...
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
651 |
652 |
653 |
654 | ```
655 |
656 | In the above, the most important parts are:
657 | * for production, the contents of ` ... ` will be replaced by the vendor bundle created by the build
658 | * for production, the contents of ` ... ` will be replaced by the application's CSS bundle created by the build
659 | * for production, the contents of ` ... ` will be replaced by the application's JS bundle created by the build
660 |
661 | Also, note that during development, SystemJS is loaded (system.src.js), the JSPM configuration is loaded (jspm.conf.js) and SystemJS is used to load the entrypoint of the application (core/core.bootstrap).
662 |
663 | ## Commands
664 | Once you have added ModernWebDevBuild to your project, you can list all the available commands using `gulp help`.
665 | The command will give you a description of each task. The most important to start discovering are:
666 | * `gulp serve`: start a Web server with live reload, watching files, transpiling, generating sourcemaps, etc
667 | * `gulp serve-dist`: same with the production version
668 | * `gulp clean`
669 | * `gulp ts-lint`: check TypeScript code quality/style
670 | * `gulp check-js-quality`: check JavaScript code quality
671 | * `gulp check-js-style`: check JavaScript code style
672 | * `gulp prepare-test-unit`: clean, compile and check quality/style
673 | * `gulp test-unit`: run unit tests using Karma (prereq: `gulp prepare-test-unit`
674 |
675 | You can run the `gulp -T` command get an visual idea of the links between the different tasks.
676 |
677 | ## Scripts
678 | To make your life easier, you can add the following scripts to your package.json file. Note that if you have used the generator to create your project, you normally have these already:
679 |
680 | ```
681 |
682 | ```
683 |
684 | ## Options
685 | The build can be customized by passing options.
686 | Defining options is done as in the following example gulpfile.babel.js:
687 |
688 | ```
689 | "use strict";
690 |
691 | import gulp from "gulp";
692 |
693 | import modernWebDevBuild from "modern-web-dev-build";
694 |
695 | let options = {};
696 |
697 | options.distEntryPoint = "core/core.bootstrap";
698 | ```
699 |
700 | Available options:
701 | * distEntryPoint
702 | * must be a relative path from .tmp/ to the file to use as entry point for creating the production JS bundle. The extension does not need to be specified (SystemJS is used to load the file)
703 | * by default, the following file is used: `core/boot.js`
704 | * minifyProductionJSBundle (default: true)
705 | * by default, the production JS bundle is minified
706 | * you can disable it by setting this option to false
707 | * mangleProductionJSBundle (default: true)
708 | * by default, the production JS bundle is mangled
709 | * you can disable it by setting this option to false
710 | * useJSPM (default: true)
711 | * by default, the production JS bundle is created using the JSPM API, which requires jspm configuration in your package.json
712 | * you can disable JSPM by setting this option to false
713 | * if you disable the usage of JSPM, then the SystemJS builder API will be used to create the production JS bundle: https://www.npmjs.com/package/systemjs-builder
714 | * systemjsConfigurationFile (default: jspm.conf.js)
715 | * by default, if you disable JSPM usage by the build, it will expect to find 'jspm.conf.js' as your SystemJS configuration file
716 | * you can define this option to customize the file name
717 | * minifyProductionHTML (default: true)
718 | * by default, the production HTML is minified
719 | * you can disable it by setting this option to false
720 | * browserSync
721 | * Pass in browserSync options. See http://www.browsersync.io/docs/options/
722 | * this should be defined like this: "browserSync": { // BrowserSync options }
723 |
724 | ## FAQ
725 |
726 | ### How can I inline some script in the production version of some HTML page?
727 | * add `inline ` right above the script tag that you want to have inlined; this tells the gulp-inline-source plugin where to find the resource that should be inlined
728 | * add the `inline` attribute to the script tag itself: ``````
729 |
730 | Example:
731 | ```
732 |
733 |
734 | ```
735 |
736 | Note that the path specified in the ` directory path used for resolving inlineable paths
61 | }))
62 |
63 | // Minify HTML
64 | .pipe(iff(minifyProductionHTML && config.files.any + config.extensions.html, minifyHtml({
65 | quotes: true // do not remove quotes (Angular 2 does not like that)
66 | })))
67 |
68 | // Output files
69 | .pipe(gulp.dest(config.html.dest))
70 |
71 | // Task result
72 | .pipe(size({
73 | title: "html"
74 | }));
75 | });
76 | }
77 | }
78 |
79 | module.exports = new HtmlTaskLoader();
80 |
--------------------------------------------------------------------------------
/src/gulp/tasks/images.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | import utils from "../utils";
6 |
7 | import eventStream from "event-stream";
8 | import cache from "gulp-cache";
9 | import imageMin from "gulp-imagemin";
10 | import size from "gulp-size";
11 | //import debug from "gulp-debug";
12 |
13 | class ImagesTaskLoader extends AbstractTaskLoader {
14 | registerTask(gulp){
15 | super.registerTask(gulp);
16 |
17 | gulp.task("images", "Optimize images", () =>{
18 | return gulp.plumbedSrc(
19 | config.images.src
20 | )
21 | // Filter out the empty directories
22 | .pipe(utils.filterEmptyDirectories(eventStream))
23 |
24 | // Display the files in the stream
25 | //.pipe(debug({title: "Stream contents:", minimal: true}))
26 |
27 | // Minify and cache
28 | .pipe(cache(imageMin({
29 | progressive: true,
30 | interlaced: true
31 | })))
32 |
33 | // Output files
34 | .pipe(gulp.dest(config.images.dest))
35 |
36 | // Task result
37 | .pipe(size({
38 | title: "images"
39 | }));
40 | });
41 | }
42 | }
43 |
44 | module.exports = new ImagesTaskLoader();
45 |
--------------------------------------------------------------------------------
/src/gulp/tasks/scripts-javascript-dist.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 |
7 | //import size from "gulp-size";
8 |
9 | import path from "path";
10 | import gutil from "gulp-util";
11 |
12 | class ScriptsJavaScriptDistTaskLoader extends AbstractTaskLoader {
13 | registerTask(gulp){
14 | super.registerTask(gulp);
15 |
16 | gulp.task("scripts-javascript-dist", "Package all JavaScript code for production", () =>{
17 | // Assuming that all TS and ES2015 code has already been converted to ES5 using the System module type
18 | // Assuming that there is a single entrypoint for the application
19 | // We only need to create the final bundle
20 |
21 | // Determine if the bundle should be minified or not
22 | let minifyProductionJSBundle = true;
23 |
24 | if(typeof gulp.options.minifyProductionJSBundle !== "undefined"){
25 | minifyProductionJSBundle = gulp.options.minifyProductionJSBundle;
26 |
27 | if(minifyProductionJSBundle === false){
28 | gutil.log("The production JS bundle will NOT be minified!");
29 | }
30 | }
31 |
32 | // Determine if the bundle should be mangled or not
33 | let mangleProductionJSBundle = true;
34 |
35 | if(typeof gulp.options.mangleProductionJSBundle !== "undefined"){
36 | mangleProductionJSBundle = gulp.options.mangleProductionJSBundle;
37 |
38 | if(mangleProductionJSBundle === false){
39 | gutil.log("The production JS bundle will NOT be mangled!");
40 | }
41 | }
42 |
43 | // Determine if JSPM should be used or not
44 | let useJSPM = true;
45 |
46 | if(typeof gulp.options.useJSPM !== "undefined"){
47 | useJSPM = gulp.options.useJSPM;
48 |
49 | if(useJSPM === false){
50 | gutil.log("The production JS bundle will be built using SystemJS-builder rather than with JSPM!");
51 | }
52 | }
53 |
54 | // Determine the entry point for the bundle creation (i.e., where to start from)
55 | let distEntryPoint = config.javascript.srcDist;
56 |
57 | if(typeof gulp.options.distEntryPoint !== "undefined"){
58 | distEntryPoint = path.join(config.folders.temp, gulp.options.distEntryPoint);
59 | gutil.log("The production JS bundle entry point has been customized: ", distEntryPoint);
60 | }
61 |
62 | // Determine the SystemJS configuration file name (only used if JSPM is disabled)
63 | let systemjsConfigurationFile = config.files.systemjsConfigDefault;
64 |
65 | if(typeof gulp.options.systemjsConfigurationFile !== "undefined"){
66 | systemjsConfigurationFile = gulp.options.systemjsConfigurationFile;
67 | gutil.log("The SystemJS configuration file has been customized: ", systemjsConfigurationFile);
68 | }
69 |
70 | // Create the bundle
71 |
72 | let bundleConfiguration = {
73 | sourceMaps: false,
74 | minify: minifyProductionJSBundle,
75 | mangle: mangleProductionJSBundle,
76 | //format: 'cjs', // to output the SFX bundle in the AMD module format
77 | // runtime: false, // to exclude the Traceur or Babel runtime
78 | globalDefs: {
79 | DEBUG: false
80 | }
81 | };
82 |
83 | // default: using JSPM
84 | if(useJSPM === true){
85 | // Reference: https://github.com/systemjs/builder/issues/203
86 | let jspm = require("jspm");
87 |
88 | jspm.setPackagePath(".");
89 |
90 | return jspm.bundleSFX(
91 | distEntryPoint, // where to start creating the bundle from
92 | config.javascript.destDist,
93 | bundleConfiguration
94 | );
95 | } else{
96 | let Builder = require("systemjs-builder");
97 | let systemjsBuilder = new Builder(".", systemjsConfigurationFile);
98 |
99 | return systemjsBuilder.buildStatic(
100 | distEntryPoint, // where to start creating the bundle from
101 | config.javascript.destDist,
102 | bundleConfiguration
103 | );
104 | }
105 | });
106 | }
107 | }
108 |
109 | module.exports = new ScriptsJavaScriptDistTaskLoader();
110 |
--------------------------------------------------------------------------------
/src/gulp/tasks/scripts-javascript.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 |
7 | import changed from "gulp-changed";
8 | import sourcemaps from "gulp-sourcemaps";
9 | import babel from "gulp-babel";
10 | import size from "gulp-size";
11 | //import debug from "gulp-debug";
12 |
13 | class ScriptsJavaScriptTaskLoader extends AbstractTaskLoader {
14 | registerTask(gulp){
15 | super.registerTask(gulp);
16 |
17 | gulp.task("scripts-javascript", "Transpile JavaScript (ES2015 to ES5 using Babel) and generate sourcemaps", () =>{
18 | return gulp.plumbedSrc(// handle errors nicely (i.e., without breaking watch)
19 | config.javascript.src
20 | )
21 |
22 | // Display the files in the stream
23 | //.pipe(debug({title: "Stream contents:", minimal: true}))
24 |
25 | // speed things up by ignoring unchanged resources
26 | .pipe(changed(config.javascript.dest))
27 |
28 | // Initialize sourcemap generation
29 | .pipe(sourcemaps.init({
30 | loadMaps: true
31 | //debug: true
32 | }))
33 |
34 | // Transpile ES2015 to ES5
35 | // options: see .babelrc file
36 | .pipe(babel())
37 |
38 | // Write sourcemaps: https://www.npmjs.com/package/gulp-sourcemaps
39 | //.pipe($.sourcemaps.write()) // use "." to write the sourcemap to a separate file in the same dir
40 | .pipe(sourcemaps.write(".", { // use "." to write the sourcemap to a separate file in the same dir
41 | includeContent: false, // alternative: include the contents and remove sourceRoot. Avoids issues but prevents from editing the sources directly in the browser
42 | sourceRoot: "/" // use an absolute path because we have scripts in different subpaths
43 | }))
44 |
45 | // Copy files
46 | .pipe(gulp.dest(config.javascript.dest))
47 |
48 | // Display the files in the stream
49 | //.pipe(debug({title: "Stream contents:", minimal: true}))
50 |
51 | // Task result
52 | .pipe(size({
53 | title: "scripts-javascript"
54 | }));
55 | });
56 | }
57 | }
58 |
59 | module.exports = new ScriptsJavaScriptTaskLoader();
60 |
--------------------------------------------------------------------------------
/src/gulp/tasks/scripts-typescript.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | //import config from "../config";
5 | //import utils from "../utils";
6 |
7 | import sourcemaps from "gulp-sourcemaps";
8 | import ts from "gulp-typescript";
9 | import size from "gulp-size";
10 | //import debug from "gulp-debug";
11 |
12 | class ScriptsTypeScriptTaskLoader extends AbstractTaskLoader {
13 | registerTask(gulp){
14 | super.registerTask(gulp);
15 |
16 | gulp.task("scripts-typescript", "Transpile TypeScript to ES, include references to library and app .d.ts files and generate sourcemaps", () =>{
17 | // references:
18 | // https://www.npmjs.com/package/gulp-typescript
19 | const tsProject = ts.createProject("tsconfig.json", {
20 | typescript: require("typescript") // override the typescript version by that defined in package.json
21 |
22 | // configuration defined in tsconfig.json
23 | // other overrides here if needed
24 | // http://json.schemastore.org/tsconfig
25 | // https://github.com/Microsoft/TypeScript/wiki/Compiler%20Options
26 | });
27 | //console.log(tsProject.config.compilerOptions);
28 | const tsConfigOutDir = tsProject.config.compilerOptions.outDir;
29 |
30 | const tsResult = tsProject.src()
31 | // Display the files in the stream
32 | //.pipe(debug({title: "Stream contents:", minimal: true}))
33 | .pipe(sourcemaps.init())
34 | .pipe(ts(
35 | tsProject
36 | ));
37 |
38 | // Output type definition files
39 | tsResult.dts.pipe(gulp.dest(tsConfigOutDir));
40 |
41 | // Output js files
42 | return tsResult.js
43 |
44 | // Display the files in the stream
45 | //.pipe(debug({title: "Stream contents:", minimal: true}))
46 |
47 | .pipe(sourcemaps.write(".", { // use "." to write the sourcemap to a separate file in the same dir
48 | includeContent: false, // alternative: include the contents and remove sourceRoot. Avoids issues but prevents from editing the sources directly in the browser
49 | sourceRoot: "/" // use an absolute path because we have scripts in different subpaths
50 | }))
51 |
52 | // Output files
53 | .pipe(gulp.dest(tsConfigOutDir))
54 |
55 | // Task result
56 | .pipe(size({
57 | title: "scripts-typescript"
58 | }));
59 | });
60 | }
61 | }
62 |
63 | module.exports = new ScriptsTypeScriptTaskLoader();
64 |
65 |
--------------------------------------------------------------------------------
/src/gulp/tasks/serve-dist.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 | import wait from "gulp-wait";
7 |
8 | const browserSync = require("browser-sync").create(config.webServerNames.dist);
9 |
10 | import historyApiFallback from "connect-history-api-fallback"; // fix for SPAs w/ BrowserSync & others: https://github.com/BrowserSync/browser-sync/issues/204
11 |
12 | class ServeDistTaskLoader extends AbstractTaskLoader {
13 | registerTask(gulp){
14 | super.registerTask(gulp);
15 |
16 | let runSequence = require("run-sequence");
17 |
18 | runSequence = runSequence.use(gulp); // needed to bind to the correct gulp object (alternative is to pass gulp to runSequence as first argument)
19 |
20 | const startBrowserSync = () =>{
21 | browserSync.init({
22 | notify: false,
23 | //port: 8000,
24 |
25 | // Customize the BrowserSync console logging prefix
26 | logPrefix: "MWD", // Modern Web Dev
27 |
28 | // Run w/ https by uncommenting "https: true"
29 | // Note: this uses an unsigned certificate which on first access
30 | // will present a certificate warning in the browser.
31 | // https: true,
32 | server: {
33 | baseDir: config.webServerFolders.dist,
34 |
35 | // fix for SPAs w/ BrowserSync & others: https://github.com/BrowserSync/browser-sync/issues/204
36 | // reference: https://github.com/BrowserSync/browser-sync/issues/204
37 | // todo extract common middleware config
38 | middleware: [
39 | historyApiFallback(), // not necessary if the app uses hash based routing
40 | function(req, res, next){
41 | res.setHeader("Access-Control-Allow-Origin", "*"); // add CORS to the response headers (for resources served by BrowserSync)
42 | next();
43 | }
44 | ]
45 | },
46 | reloadDelay: 1000,
47 | reloadDebounce: 1000
48 | });
49 | };
50 |
51 | gulp.task("wait-a-bit", "Wait a second...", () =>{
52 | return gulp.src("./package.json").
53 | pipe(wait(1500));
54 | });
55 |
56 | gulp.task("serve-dist", "Build and serve the production version (i.e., dist folder contents", () =>{
57 | return runSequence([ "default" ], [ "wait-a-bit" ], startBrowserSync); // here we need to ensure that all the other tasks are done before we start BrowserSync
58 | });
59 | }
60 | }
61 |
62 | module.exports = new ServeDistTaskLoader();
63 |
--------------------------------------------------------------------------------
/src/gulp/tasks/serve.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | import utils from "../utils";
6 |
7 | let browserSync = require("browser-sync").create(config.webServerNames.dev);
8 |
9 | import historyApiFallback from "connect-history-api-fallback"; // fix for SPAs w/ BrowserSync & others: https://github.com/BrowserSync/browser-sync/issues/204
10 | //import debug from "gulp-debug";
11 |
12 | class ServeTaskLoader extends AbstractTaskLoader {
13 | registerTask(gulp){
14 | super.registerTask(gulp);
15 |
16 | let runSequence = require("run-sequence");
17 |
18 | runSequence = runSequence.use(gulp); // needed to bind to the correct gulp object (alternative is to pass gulp to runSequence as first argument)
19 |
20 | // TypeScript
21 | gulp.task("serve-scripts-typescript", "Transpile TypeScript to ES5 and reload the browser (this task should only be called during serve)", [ "prepare-serve-scripts-typescript" ], () =>{
22 | return browserSync.reload();
23 | }); // reload BrowserSync once everything is ready
24 |
25 | gulp.task("prepare-serve-scripts-typescript", "Transpile TypeScript to ES5 and generate sourcemaps", [
26 | "ts-lint"
27 | ], (callback) =>{
28 | return runSequence(
29 | "scripts-typescript",
30 | callback);
31 | });
32 |
33 | // JavaScript
34 | gulp.task("serve-scripts-javascript", "Transpile JavaScript to ES5 and reload the browser (this task should only be called during serve)", [ "prepare-serve-scripts-javascript" ], () =>{
35 | return browserSync.reload();
36 | }); // reload BrowserSync once everything is ready
37 | gulp.task("prepare-serve-scripts-javascript", "Transpile JavaScript to ES5 and generate sourcemaps", [
38 | "check-js-style",
39 | "check-js-quality"
40 | ], (callback) =>{
41 | return runSequence(
42 | "scripts-javascript",
43 | callback);
44 | });
45 |
46 | let options = { // http://www.browsersync.io/docs/options/
47 | notify: false,
48 | //port: 8000,
49 |
50 | // Customize the BrowserSync console logging prefix
51 | logPrefix: "MWD", // Modern Web Dev
52 |
53 | // Run w/ https by uncommenting "https: true"
54 | // Note: this uses an unsigned certificate which on first access
55 | // will present a certificate warning in the browser.
56 | // https: true,
57 | ghostMode: { // replicate actions in all clients
58 | clicks: false,
59 | forms: false,
60 | scroll: false
61 | },
62 | server: {
63 | baseDir: config.webServerFolders.dev,
64 | //routes: alternative way to map content that is above the base dir
65 | // fix for SPAs w/ BrowserSync & others: https://github.com/BrowserSync/browser-sync/issues/204
66 | // reference: https://github.com/BrowserSync/browser-sync/issues/204
67 | middleware: [
68 | historyApiFallback(), // not necessary if the app uses hash based routing
69 | function(req, res, next){
70 | res.setHeader("Access-Control-Allow-Origin", "*"); // add CORS to the response headers (for resources served by BrowserSync)
71 | next();
72 | }
73 | ]
74 | }//,
75 | //reloadDebounce: 500 // restrict the frequency in which browser reload events can be emitted to connected clients
76 | };
77 |
78 | let startBrowserSync = () =>{
79 | browserSync.init(utils.mergeOptions(options, gulp.options.browserSync));
80 |
81 | gulp.watch(config.html.src).on("change", browserSync.reload); // force a reload when html changes
82 | gulp.watch(config.styles.src, [ "styles" ]); // stylesheet changes will be streamed if possible or will force a reload
83 | gulp.watch(config.typescript.srcAppOnly, [
84 | "serve-scripts-typescript"
85 | ]); // TypeScript changes will force a reload
86 | gulp.watch(config.javascript.src, [
87 | "serve-scripts-javascript"
88 | ]); // JavaScript changes will force a reload
89 | gulp.watch(config.images.src).on("change", browserSync.reload); // force a reload when images change
90 | };
91 |
92 | gulp.task("serve", "Watch files for changes and rebuild/reload automagically", () =>{
93 | runSequence("prepare-serve", startBrowserSync); // here we need to ensure that all the other tasks are done before we start BrowserSync
94 | });
95 |
96 | gulp.task("prepare-serve", "Do all the necessary preparatory work for the serve task", () =>{
97 | return runSequence([
98 | "clean",
99 | "ts-lint",
100 | "check-js-style",
101 | "check-js-quality"
102 | ], [
103 | "scripts-typescript",
104 | "scripts-javascript",
105 | "styles",
106 | "validate-package-json"
107 | ]);
108 | });
109 | }
110 | }
111 |
112 | module.exports = new ServeTaskLoader();
113 |
--------------------------------------------------------------------------------
/src/gulp/tasks/styles-dist.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 |
7 | import sass from "gulp-sass";
8 | import cssimport from "gulp-cssimport";
9 | import concat from "gulp-concat";
10 | import csso from "gulp-csso";
11 | import minifyCss from "gulp-clean-css";
12 | import size from "gulp-size";
13 |
14 | class StylesDistTaskLoader extends AbstractTaskLoader {
15 | registerTask(gulp){
16 | super.registerTask(gulp);
17 |
18 | gulp.task("styles-dist", "Optimize and minimize stylesheets for production", () =>{
19 | return gulp.plumbedSrc(// handle errors nicely (i.e., without breaking watch)
20 | config.styles.srcWithoutVendor
21 | )
22 |
23 | // Display the files in the stream
24 | //.pipe(debug({title: "Stream contents:", minimal: true}))
25 |
26 | // Process Sass files
27 | .pipe(sass({
28 | style: "compressed"
29 | //errLogToConsole: true
30 | }))
31 |
32 | // Replace CSS imports by actual contents
33 | .pipe(cssimport())
34 |
35 | // Remove any unused CSS
36 | // Note that it breaks the sourcemaps (but we shouldn't care for dist since we don't need sourcemaps there)
37 | // Note that it also causes weird output during build in combination w/ Angular
38 | //.pipe($.uncss({
39 | // html: [
40 | // config.html.src
41 | // ],
42 | // // CSS Selectors for UnCSS to ignore
43 | // ignore: [
44 | // ]
45 | //}))
46 |
47 | // Regroup all files together
48 | .pipe(concat(config.styles.finalCssBundleFilename))
49 |
50 | // Optimize and minimize
51 | .pipe(csso()) // https://www.npmjs.com/package/gulp-csso
52 | .pipe(minifyCss(
53 | config.minifyCss
54 | ))
55 |
56 | // Output file
57 | .pipe(gulp.dest(config.styles.destDist))
58 |
59 | // Task result
60 | .pipe(size({
61 | title: "styles-dist"
62 | }));
63 | });
64 | }
65 | }
66 |
67 | module.exports = new StylesDistTaskLoader();
68 |
--------------------------------------------------------------------------------
/src/gulp/tasks/styles-vendor-dist.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 |
7 | import sass from "gulp-sass";
8 | import cssimport from "gulp-cssimport";
9 | import concat from "gulp-concat";
10 | import csso from "gulp-csso";
11 | import minifyCss from "gulp-clean-css";
12 | import size from "gulp-size";
13 | //import debug from "gulp-debug";
14 |
15 | class StylesVendorDistTaskLoader extends AbstractTaskLoader {
16 | registerTask(gulp){
17 | super.registerTask(gulp);
18 |
19 | gulp.task("styles-vendor-dist", "Optimize and minimize vendor stylesheets for production", () =>{
20 | return gulp.plumbedSrc(// handle errors nicely (i.e., without breaking watch)([
21 | config.styles.srcVendorOnly
22 | )
23 |
24 | // Display the files in the stream
25 | //.pipe(debug({title: "Stream contents:", minimal: true}))
26 |
27 | // Process Sass files
28 | .pipe(sass({
29 | style: "compressed"
30 | //errLogToConsole: true
31 | }))
32 |
33 | // Replace CSS imports by actual contents
34 | .pipe(cssimport())
35 |
36 | // Remove any unused CSS
37 | // Note that it breaks the sourcemaps (but we shouldn't care for dist since we don't need sourcemaps there)
38 | // Note that it also causes weird output during build in combination w/ Angular
39 | //.pipe($.uncss({
40 | // html: [
41 | // config.html.src
42 | // ],
43 | // // CSS Selectors for UnCSS to ignore
44 | // ignore: [
45 | // ]
46 | //}))
47 |
48 | // Regroup all files together
49 | .pipe(concat(config.styles.finalVendorCssBundleFilename))
50 |
51 | // Optimize and minimize
52 | .pipe(csso()) // https://www.npmjs.com/package/gulp-csso
53 | .pipe(minifyCss(
54 | config.minifyCss
55 | ))
56 |
57 | // Output file
58 | .pipe(gulp.dest(config.styles.destDist))
59 |
60 | // Task result
61 | .pipe(size({
62 | title: "styles-vendor-dist"
63 | }));
64 | });
65 | }
66 | }
67 |
68 | module.exports = new StylesVendorDistTaskLoader();
69 |
--------------------------------------------------------------------------------
/src/gulp/tasks/styles.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 |
7 | import sass from "gulp-sass";
8 | import sourcemaps from "gulp-sourcemaps";
9 | import autoprefixer from "gulp-autoprefixer";
10 | import iff from "gulp-if";
11 | import size from "gulp-size";
12 | //import debug from "gulp-debug";
13 |
14 | let browserSync = require("browser-sync").get(config.webServerNames.dev);
15 |
16 | class StylesTaskLoader extends AbstractTaskLoader {
17 | registerTask(gulp){
18 | super.registerTask(gulp);
19 |
20 | gulp.task("styles", "Compile, add vendor prefixes and generate sourcemaps", () =>{
21 | return gulp.plumbedSrc(// handle errors nicely (i.e., without breaking watch)
22 | config.styles.src
23 | )
24 |
25 | // Display the files in the stream
26 | //.pipe(debug({title: "Stream contents:", minimal: true}))
27 |
28 | // Initialize sourcemap generation
29 | .pipe(sourcemaps.init({
30 | //debug: true
31 | }))
32 |
33 | // Process the sass files
34 | .pipe(sass({
35 | style: "compressed"
36 | //errLogToConsole: true
37 | }))
38 |
39 | // Write sourcemaps: https://www.npmjs.com/package/gulp-sourcemaps
40 | .pipe(sourcemaps.write(".", { // use "." to write the sourcemap to a separate file in the same dir
41 | includeContent: false, // alternative: include the contents and remove sourceRoot. Avoids issues but prevents from editing the sources directly in the browser
42 | sourceRoot: "/" // use an absolute path because we have scripts in different subpaths
43 | }))
44 |
45 | // Include vendor prefixes
46 | // The if clause prevents autoprefixer from messing up the sourcemaps (necessary if the maps are put in separate files)
47 | // reference: https://github.com/sindresorhus/gulp-autoprefixer/issues/8#issuecomment-93817177
48 | .pipe(iff([ config.extensions.css, "!*.map" ], autoprefixer({
49 | browsers: config.autoprefixerBrowsers // alternative: $.autoprefixer("last 2 version")
50 | })))
51 |
52 | // Output files
53 | .pipe(gulp.dest(config.styles.dest))
54 |
55 | // Reload Browser if needed
56 | // Stream if possible
57 | .pipe(iff(browserSync.active, browserSync.stream({
58 | once: true,
59 | stream: true
60 | })))
61 |
62 | // Task result
63 | .pipe(size({
64 | title: "styles"
65 | }));
66 | });
67 | }
68 | }
69 |
70 | module.exports = new StylesTaskLoader();
71 |
72 |
--------------------------------------------------------------------------------
/src/gulp/tasks/test-unit.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | //import config from "../config";
5 | //import utils from "../utils";
6 |
7 | //import debug from "gulp-debug";
8 | let KarmaServer = require("karma").Server; // TODO replace by import {Server as KarmaServer} from "karma";
9 | let path = require("path");
10 |
11 | class TestUnitTaskLoader extends AbstractTaskLoader {
12 | registerTask(gulp){
13 | super.registerTask(gulp);
14 |
15 | let runSequence = require("run-sequence");
16 |
17 | runSequence = runSequence.use(gulp); // needed to bind to the correct gulp object (alternative is to pass gulp to runSequence as first argument)
18 |
19 | let karmaConfigFilePath = path.resolve("karma.conf.js");
20 |
21 | gulp.task("test-unit", "Execute all unit tests", (callback) =>{
22 | return new KarmaServer({
23 | configFile: karmaConfigFilePath, // necessary otherwise the file is not resolved correctly by the karma runtime
24 | singleRun: true
25 | }, callback).start();
26 | });
27 |
28 | gulp.task("test-unit-dev", "Execute all unit tests continuously (watches files)", (callback) =>{
29 | return new KarmaServer({
30 | configFile: karmaConfigFilePath, // necessary otherwise the file is not resolved correctly by the karma runtime
31 | singleRun: false
32 | }, callback).start();
33 | });
34 |
35 | gulp.task("prepare-test-unit", "Do all the necessary preparatory work for the test-unit task", () =>{
36 | return runSequence([
37 | "clean",
38 | "ts-lint",
39 | "check-js-style",
40 | "check-js-quality"
41 | ], [
42 | "scripts-typescript",
43 | "scripts-javascript"
44 | ]);
45 | });
46 | }
47 | }
48 |
49 | module.exports = new TestUnitTaskLoader();
50 |
--------------------------------------------------------------------------------
/src/gulp/tasks/ts-lint.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 |
7 | import tslint from "gulp-tslint";
8 | import iff from "gulp-if";
9 | import size from "gulp-size";
10 | //import debug from "gulp-debug";
11 |
12 | let browserSync = require("browser-sync").get(config.webServerNames.dev);
13 |
14 | class TsLintTaskLoader extends AbstractTaskLoader {
15 | registerTask(gulp){
16 | super.registerTask(gulp);
17 |
18 | gulp.task("ts-lint", "Lint TypeScript code", () =>{
19 | return gulp.plumbedSrc(// handle errors nicely (i.e., without breaking watch)
20 | config.typescript.srcAppOnly // only the application's code needs to be checked
21 | )
22 |
23 | // Display the files in the stream
24 | //.pipe(debug({title: "Stream contents:", minimal: true}))
25 |
26 | // Check the code quality
27 | .pipe(tslint())
28 |
29 | // Fail the build only if BrowserSync is not active
30 | .pipe(iff(!browserSync.active, tslint.report("prose")))
31 | .pipe(iff(browserSync.active, tslint.report("prose", {
32 | emitError: false
33 | })))
34 |
35 | // Task result
36 | .pipe(size({
37 | title: "ts-lint"
38 | }));
39 | });
40 | }
41 | }
42 |
43 | module.exports = new TsLintTaskLoader();
44 |
--------------------------------------------------------------------------------
/src/gulp/tasks/validate-package-json.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | import config from "../config";
5 | //import utils from "../utils";
6 |
7 | import packageJsonValidator from "gulp-nice-package";
8 |
9 | class PackageJSONTaskLoader extends AbstractTaskLoader {
10 | registerTask(gulp){
11 | super.registerTask(gulp);
12 |
13 | gulp.task("validate-package-json", "Validate the package.json file", () =>{
14 | return gulp.plumbedSrc(config.files.packageJSON)
15 | .pipe(packageJsonValidator());
16 | });
17 | }
18 | }
19 |
20 | module.exports = new PackageJSONTaskLoader();
21 |
--------------------------------------------------------------------------------
/src/gulp/templates/taskLoaderTemplate.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import AbstractTaskLoader from "../abstractTaskLoader";
4 | //import config from "../config";
5 | //import utils from "../utils";
6 |
7 | class TemplateTaskLoader extends AbstractTaskLoader {
8 | registerTask(gulp){
9 | super.registerTask(gulp);
10 |
11 | console.log("Hello world!");
12 | }
13 | }
14 |
15 | module.exports = new TemplateTaskLoader();
16 |
--------------------------------------------------------------------------------
/src/gulp/utils.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | import notify from "gulp-notify";
4 | import gutil from "gulp-util";
5 | import plumber from "gulp-plumber";
6 |
7 | /**
8 | * Whether we should make the house explode whenever errors occur (e.g., stop gulp serve)
9 | * @type {boolean}
10 | */
11 | let exitOnError = false;
12 |
13 | /**
14 | * Display errors nicely and avoid having errors breaking tasks/watch
15 | * Reference: https://github.com/mikaelbr/gulp-notify/issues/81
16 | * @param error the error to report
17 | */
18 | let reportError = function(error){ // note that we do not use an arrow function to get the expected value of this when this function is called (i.e., context of the caller)
19 | let lineNumber = error.lineNumber ? `LINE ${error.lineNumber} -- ` : "";
20 | let report = "";
21 | let chalk = gutil.colors.white.bgRed;
22 |
23 | notify({
24 | title: `Task Failed [${error.plugin}]`,
25 | message: `${lineNumber} See console.`,
26 | sound: true
27 |
28 | // the version below probably works on OSX
29 | //sound: 'Sosumi' // See: https://github.com/mikaelbr/node-notifier#all-notification-options-with-their-defaults
30 | }).write(error);
31 |
32 | // Inspect the error object
33 | //gutil.log(error);
34 |
35 | // Easy error reporting
36 | //console.log(error.toString());
37 |
38 | // Pretty error reporting
39 | report += chalk("TASK:") + " [" + error.plugin + "]\n";
40 | report += chalk("ISSUE:") + " " + error.message + "\n";
41 |
42 | if(error.lineNumber){
43 | report += chalk("LINE:") + " " + error.lineNumber + "\n";
44 | }
45 |
46 | if(error.fileName){
47 | report += chalk("FILE:") + " " + error.fileName + "\n";
48 | }
49 |
50 | console.error(report);
51 |
52 | if(exitOnError){
53 | process.exit(1);
54 | } else{
55 | // Prevent the 'watch' task from stopping
56 | this.emit("end");
57 | }
58 | };
59 |
60 | /**
61 | * Exclude files from globs
62 | * @param providedPath the path that should be excluded
63 | * @returns {string} the exclusion string
64 | */
65 | let exclude = providedPath => "!" + providedPath;
66 |
67 | /**
68 | * Filter out empty directories
69 | * reference: http://stackoverflow.com/questions/23719731/gulp-copying-empty-directories
70 | * @param es the list to filter
71 | * @returns {*} the filtered list
72 | */
73 | let filterEmptyDirectories = es =>{
74 | return es.map((file, cb) =>{
75 | if(file.stat.isFile()){
76 | return cb(null, file);
77 | } else{
78 | return cb();
79 | }
80 | });
81 | };
82 |
83 | /**
84 | * Validate that the passed argument is not null or undefined.
85 | * If validation fails, an error is thrown.
86 | * @param arg the argument to check
87 | * @param errorMessage the error message to use if validation fails
88 | * @throws Error if validation fails
89 | */
90 | let validateArgument = (arg, errorMessage) =>{
91 | errorMessage = errorMessage || "the provided argument cannot be null or undefined!";
92 |
93 | if(arg === null || arg === undefined){
94 | throw new TypeError(errorMessage);
95 | }
96 | };
97 |
98 | /**
99 | * Validate that the passed argument is a valid gulp object
100 | * @param obj the object to validate
101 | * @throws Error if validation fails
102 | */
103 | let validateGulpObject = obj =>{
104 | validateArgument(obj);
105 | validateArgument(obj.tasks, "the provided argument must be a gulp instance!");
106 | };
107 |
108 | /**
109 | * Validate that the passed argument is a valid gulp object and is configured as expected.
110 | * It should have the help task defined
111 | * @param obj the object to validate
112 | * @throws Error if validation fails
113 | */
114 | let validateGulpObjectIsConfigured = obj =>{
115 | const notCorrectlyConfiguredErrorMessage = "the provided argument is a valid gulp object but it isn't configured properly. You need to invoke the configureGulpObject utility function before passing it to the tasks!";
116 |
117 | validateGulpObject(obj);
118 | validateArgument(obj.tasks.help, notCorrectlyConfiguredErrorMessage);
119 | validateArgument(obj.plumbedSrc, notCorrectlyConfiguredErrorMessage);
120 | };
121 |
122 | /**
123 | * Configure the passed in gulp object so that it is customized as we need:
124 | * - gulp help loaded and enabled
125 | * - gulp plumbedSrc function added (integrates plumber)
126 | * @param obj the object to validate
127 | * @param options the build options
128 | * @throws Error if validation fails
129 | */
130 | let configureGulpObject = (obj, options) =>{
131 | validateGulpObject(obj);
132 | const help = require("gulp-help");
133 |
134 | let configuredGulpObject = help(obj); // provide help through 'gulp help' -- the help text is the second gulp task argument (https://www.npmjs.com/package/gulp-help/)
135 |
136 | configuredGulpObject.options = options; // keep the options on the gulp object (useful as it'll be passed around and can be manipulated)
137 |
138 | // Easily integrate plumber invocation
139 | // Reference: https://gist.github.com/floatdrop/8269868
140 | configuredGulpObject.plumbedSrc = function(){ // should be a function
141 | return configuredGulpObject.src.apply(configuredGulpObject, arguments)
142 | .pipe(plumber({
143 | errorHandler: reportError
144 | }));
145 | };
146 |
147 | return configuredGulpObject;
148 | };
149 |
150 | /**
151 | * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
152 | * @param obj1
153 | * @param obj2
154 | * @returns obj3 a new object based on obj1 and obj2
155 | */
156 | let mergeOptions = (obj1 = {}, obj2 = {}) =>{
157 | let retVal = {};
158 |
159 | for(let attrname in obj1){
160 | if(obj1.hasOwnProperty(attrname)){
161 | retVal[ attrname ] = obj1[ attrname ];
162 | }
163 | }
164 |
165 | for(let attrname in obj2){
166 | if(obj2.hasOwnProperty(attrname)){
167 | retVal[ attrname ] = obj2[ attrname ];
168 | }
169 | }
170 |
171 | return retVal;
172 | };
173 |
174 | export default {
175 | exclude,
176 | reportError,
177 | filterEmptyDirectories,
178 | validateArgument,
179 | validateGulpObjectIsConfigured,
180 | configureGulpObject,
181 | mergeOptions
182 | };
183 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* Rather than manage one giant configuration file responsible for creating multiple gulp tasks, each task has been broken out into
2 | its own file. Any files in that directory get automatically required below.
3 |
4 | To add a new task, simply add a new task file that directory.
5 | gulp/tasks/default.js specifies the default set of tasks to run
6 | when you run `gulp`.
7 |
8 | Principle taken from gulp-starter: https://github.com/greypants/gulp-starter
9 | */
10 |
11 | "use strict";
12 |
13 | import requireDir from "require-dir";
14 |
15 | import utils from "./gulp/utils";
16 |
17 | /**
18 | * This class takes care of loading gulp tasks.
19 | */
20 | class TasksLoader {
21 | constructor(){
22 | }
23 |
24 | /**
25 | * Looks for and registers all available tasks.
26 | * @param inputGulp the gulp object to use. If not provided, it'll be loaded
27 | * @param inputOptions the build options to use. If not provided, an empty object is used
28 | */
29 | registerTasks(inputGulp, inputOptions){
30 | let gulp = inputGulp || require("gulp"); // this module can be imported without a defined gulp instance
31 | let options = inputOptions || {};
32 |
33 | gulp = utils.configureGulpObject(gulp, options); // we need to customize the gulp object a bit
34 |
35 | // Load all tasks in gulp/tasks
36 | const loadedModules = requireDir("./gulp/tasks", {
37 | recurse: false
38 | });
39 |
40 | // request each module to register its tasks
41 | for(let key in loadedModules){
42 | if(loadedModules.hasOwnProperty(key)){
43 | let loadedModule = loadedModules[ key ];
44 |
45 | if(loadedModule.registerTask){
46 | //console.log(`Registering module: ${key}`);
47 | loadedModule.registerTask(gulp);
48 | } else{
49 | throw new TypeError(`The following module does not expose the expected interface: ${key}`);
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
56 | export default new TasksLoader();
57 |
--------------------------------------------------------------------------------
/src/tests/sanity_test.spec.js:
--------------------------------------------------------------------------------
1 | describe("sanity checks", () =>{
2 | it("should be able to test", () =>{
3 | expect(true).toBe(true);
4 | });
5 |
6 | xit("should skip this", () =>{
7 | expect(4).toEqual(40);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------