├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE.md ├── README.md ├── index.js ├── karma.conf.js ├── package.json ├── spec.js ├── src ├── .eslintrc ├── app.js ├── app │ ├── about │ │ ├── about.html │ │ ├── about_controller.js │ │ ├── about_directive.js │ │ └── index.js │ ├── app_router_config.js │ ├── haml │ │ ├── haml.haml │ │ ├── haml.sass │ │ ├── haml_directive.js │ │ └── index.js │ └── home │ │ ├── home.css │ │ ├── home.html │ │ ├── home_controller.js │ │ ├── home_directive.js │ │ ├── index.js │ │ ├── trollface.png │ │ └── true-story.png ├── blah │ ├── blah_service.js │ └── index.js ├── hello │ ├── hello.html │ ├── hello.sass │ ├── hello_controller.js │ ├── hello_controller_spec.js │ ├── hello_directive.js │ ├── hello_directive_spec.js │ ├── hello_hello_service.js │ ├── hello_service.js │ ├── hello_service_spec.js │ └── index.js └── index.html ├── vendor └── angular.src.js └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dest 3 | node_modules 4 | vendor 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./src/.eslintrc", 3 | "env": { 4 | "browser": false, 5 | "es6": false 6 | }, 7 | "rules": { 8 | "object-shorthand": 0, 9 | "no-var": 0, 10 | "no-console": 0, 11 | "no-process-env": 0 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dest 2 | 3 | # 4 | # nodejs 5 | # https://github.com/github/gitignore/blob/master/Node.gitignore 6 | # 7 | 8 | # Logs 9 | logs 10 | *.log 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directory 33 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 34 | node_modules 35 | 36 | 37 | # 38 | # OSX 39 | # https://github.com/github/gitignore/blob/master/Global/OSX.gitignore 40 | # 41 | .DS_Store 42 | .AppleDouble 43 | .LSOverride 44 | 45 | # Icon must end with two \r 46 | Icon 47 | 48 | 49 | # Thumbnails 50 | ._* 51 | 52 | # Files that might appear on external disk 53 | .Spotlight-V100 54 | .Trashes 55 | 56 | # Directories potentially created on remote AFP share 57 | .AppleDB 58 | .AppleDesktop 59 | Network Trash Folder 60 | Temporary Items 61 | .apdisk 62 | 63 | 64 | # 65 | # JetBrains 66 | # https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore 67 | # 68 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 69 | 70 | *.iml 71 | 72 | ## Directory-based project format: 73 | .idea/ 74 | # if you remove the above rule, at least ignore the following: 75 | 76 | # User-specific stuff: 77 | # .idea/workspace.xml 78 | # .idea/tasks.xml 79 | # .idea/dictionaries 80 | 81 | # Sensitive or high-churn files: 82 | # .idea/dataSources.ids 83 | # .idea/dataSources.xml 84 | # .idea/sqlDataSources.xml 85 | # .idea/dynamic.xml 86 | # .idea/uiDesigner.xml 87 | 88 | # Gradle: 89 | # .idea/gradle.xml 90 | # .idea/libraries 91 | 92 | # Mongo Explorer plugin: 93 | # .idea/mongoSettings.xml 94 | 95 | ## File-based project format: 96 | *.ipr 97 | *.iws 98 | 99 | ## Plugin-specific files: 100 | 101 | # IntelliJ 102 | out/ 103 | 104 | # mpeltonen/sbt-idea plugin 105 | .idea_modules/ 106 | 107 | # JIRA plugin 108 | atlassian-ide-plugin.xml 109 | 110 | # Crashlytics plugin (for Android Studio and IntelliJ) 111 | com_crashlytics_export_strings.xml 112 | crashlytics.properties 113 | crashlytics-build.properties 114 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nik Butenko 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 | # angular-webpack 2 | 3 | [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/nkbt/help) 4 | 5 | Angular + Webpack build with Karma tests support 6 | 7 | ### Install 8 | 9 | ```sh 10 | git clone git@github.com:packetloop/angular-webpack.git 11 | cd angular-webpack 12 | npm install 13 | npm start 14 | ``` 15 | 16 | Then 17 | 18 | ```sh 19 | open http://localhost:8080 20 | ``` 21 | 22 | To run tests 23 | 24 | ```sh 25 | npm test 26 | ``` 27 | 28 | Coverage 29 | 30 | ```sh 31 | open ./coverage/index.html 32 | ``` 33 | 34 | 35 | ### Features, TODO 36 | 37 | - [x] Build basic Angular app with webpack 38 | - [x] Couple of dependent modules `Hello` and `Blah` 39 | - [x] Controller, directive and service for Hello module 40 | - [x] Tests for `HelloService`, mocking `HelloHelloService` and `BlahService` 41 | - [x] Tests for `` directive mocking `HelloService` 42 | - [x] Tests for `HelloController` mocking `HelloService` 43 | - [x] `npm test` script with CI-friendly karma + webpack run 44 | - [x] Separate HTML template for `` + webpack build 45 | - [x] Separate CSS for `` + webpack build 46 | - [x] Embed images 47 | - [x] Coverage report with full ES6 support 48 | - [x] Multiple pages to load additional components asynchronously 49 | - [x] ESLint `npm run lint` 50 | - [x] ESLint loader for compile-time validation 51 | - [x] FIX dynamic Angular modules loading (fixed in #2) 52 | - [x] Allow to use HAML templates 53 | - [x] BabelJS and full ES6/7 support 54 | - [x] Running tests in PhantomJS 55 | 56 | 57 | ### Demo build 58 | 59 | [http://packetloop.github.io/angular-webpack/dest/](http://packetloop.github.io/angular-webpack/dest/) 60 | 61 | ### Coverage 62 | 63 | [http://packetloop.github.io/angular-webpack/coverage/](http://packetloop.github.io/angular-webpack/coverage/) 64 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/packetloop/angular-webpack/c55210eaa4db8775c2cdbdbaee9a986038a885f8/index.js -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var hasCoverage = global.process.argv.reduce(function (result, arg) { 3 | return arg.indexOf('coverage') !== -1 || result; 4 | }); 5 | 6 | var include = [ 7 | path.resolve('./src') 8 | ]; 9 | 10 | var preLoaders = hasCoverage ? [ 11 | 12 | // Process test code with Babel 13 | {test: /_spec\.js$/, loader: 'babel', include: include}, 14 | 15 | // Process all non-test code with Isparta 16 | {test: /\.js$/, loader: 'isparta', include: include, exclude: /_spec\.js$/} 17 | ] : [ 18 | {test: /\.js$/, loader: 'babel', include: include} 19 | ]; 20 | var loaders = [ 21 | {test: /\.sass$/, loader: 'style!css?sourceMap!sass?sourceMap&indentedSyntax=true'}, 22 | {test: /\.png$/, loader: 'null'}, 23 | {test: /\.jpg$/, loader: 'null'}, 24 | 25 | // Loader for JSON, may be used in some tests 26 | {test: /\.json$/, loader: 'json'}, 27 | 28 | // Need some real loaders for HTML, because angular directives are coupled with templates 29 | {test: /\.haml$/, loader: 'hamlc-loader'}, 30 | {test: /\.html$/, loader: 'ng-cache?prefix=[dir]/[dir]'} 31 | ]; 32 | 33 | 34 | module.exports = function (config) { 35 | config.set({ 36 | basePath: '', 37 | frameworks: ['jasmine'], 38 | files: [ 39 | 'spec.js' 40 | ], 41 | webpack: { 42 | devtool: 'eval', 43 | module: { 44 | loaders: loaders, 45 | preLoaders: preLoaders 46 | }, 47 | cache: true 48 | }, 49 | webpackMiddleware: { 50 | stats: { 51 | chunkModules: false, 52 | colors: true 53 | } 54 | }, 55 | preprocessors: { 56 | 'spec.js': ['webpack'] 57 | }, 58 | reporters: ['progress', 'coverage'], 59 | coverageReporter: { 60 | dir: 'coverage/', 61 | subdir: '.', 62 | reporters: [ 63 | {type: 'cobertura', file: 'cobertura.xml'}, 64 | {type: 'text', file: 'text.txt'}, 65 | {type: 'text-summary', file: 'text-summary.txt'}, 66 | {type: 'html'} 67 | ] 68 | }, 69 | port: 9876, 70 | colors: true, 71 | logLevel: config.LOG_INFO, 72 | autoWatch: false, 73 | browsers: ['PhantomJS'], 74 | singleRun: true 75 | }); 76 | }; 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-webpack", 3 | "version": "1.0.0", 4 | "description": "Angular + Webpack build with Karma tests support", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server", 8 | "build": "rm -rf dest && webpack", 9 | "test": "karma start ./karma.conf.js --single-run", 10 | "validate": "eslint ." 11 | }, 12 | "author": { 13 | "name": "Nik Butenko", 14 | "email": "nik@butenko.me" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/packetloop/angular-webpack.git" 19 | }, 20 | "license": "MIT", 21 | "devDependencies": { 22 | "angular-mocks": "^1.4.3", 23 | "babel-loader": "^5.3.2", 24 | "css-loader": "^0.15.6", 25 | "eslint": "^1.0.0", 26 | "eslint-loader": "^0.14.2", 27 | "exports-loader": "^0.6.2", 28 | "extract-text-webpack-plugin": "^0.8.2", 29 | "file-loader": "^0.8.4", 30 | "hamlc-loader": "0.0.11", 31 | "html-webpack-plugin": "^1.6.0", 32 | "imports-loader": "^0.6.4", 33 | "isparta-loader": "^0.2.0", 34 | "jasmine-core": "^2.3.4", 35 | "karma": "^0.13.3", 36 | "karma-coverage": "^0.4.2", 37 | "karma-jasmine": "^0.3.6", 38 | "karma-phantomjs-launcher": "^0.2.0", 39 | "karma-sourcemap-loader": "^0.3.5", 40 | "karma-webpack": "^1.7.0", 41 | "merge-stream": "^0.1.8", 42 | "ng-cache-loader": "0.0.9", 43 | "null-loader": "^0.1.1", 44 | "phantomjs": "^1.9.17", 45 | "sass-loader": "^1.0.3", 46 | "style-loader": "^0.12.3", 47 | "url-loader": "^0.5.6", 48 | "webpack": "^1.10.5", 49 | "webpack-dev-server": "^1.10.1" 50 | }, 51 | "dependencies": { 52 | "angular": "^1.4.3", 53 | "angular-route": "^1.4.3" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /spec.js: -------------------------------------------------------------------------------- 1 | var testsContext; 2 | 3 | require('babel-core/polyfill'); 4 | require('./vendor/angular.src'); 5 | require('angular-mocks'); 6 | 7 | testsContext = require.context('./src', true, /_spec\.js$/); 8 | testsContext.keys().forEach(testsContext); 9 | -------------------------------------------------------------------------------- /src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true, 6 | "jasmine": true 7 | }, 8 | 9 | "globals": { 10 | "angular": true 11 | }, 12 | 13 | "ecmaFeatures": { 14 | "arrowFunctions": true, 15 | "binaryLiterals": true, 16 | "blockBindings": true, 17 | "classes": true, 18 | "defaultParams": true, 19 | "destructuring": true, 20 | "forOf": true, 21 | "generators": true, 22 | "modules": true, 23 | "objectLiteralComputedProperties": true, 24 | "objectLiteralDuplicateProperties": true, 25 | "objectLiteralShorthandMethods": true, 26 | "objectLiteralShorthandProperties": true, 27 | "octalLiterals": true, 28 | "regexUFlag": true, 29 | "regexYFlag": true, 30 | "restParams": true, 31 | "spread": true, 32 | "superInFunctions": true, 33 | "templateStrings": true, 34 | "unicodeCodePointEscapes": true, 35 | "globalReturn": true, 36 | "jsx": true 37 | }, 38 | 39 | "rules": { 40 | // 41 | //Possible Errors 42 | // 43 | // The following rules point out areas where you might have made mistakes. 44 | // 45 | "comma-dangle": 2, // disallow or enforce trailing commas 46 | "no-cond-assign": 2, // disallow assignment in conditional expressions 47 | "no-console": 2, // disallow use of console (off by default in the node environment) 48 | "no-constant-condition": 2, // disallow use of constant expressions in conditions 49 | "no-control-regex": 2, // disallow control characters in regular expressions 50 | "no-debugger": 2, // disallow use of debugger 51 | "no-dupe-args": 2, // disallow duplicate arguments in functions 52 | "no-dupe-keys": 2, // disallow duplicate keys when creating object literals 53 | "no-duplicate-case": 2, // disallow a duplicate case label. 54 | "no-empty-character-class": 2, //disallow the use of empty character classes in regular expressions 55 | "no-empty": 2, // disallow empty statements 56 | "no-ex-assign": 2, // disallow assigning to the exception in a catch block 57 | "no-extra-boolean-cast": 2, // disallow double-negation boolean casts in a boolean context 58 | "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) 59 | "no-extra-semi": 2, // disallow unnecessary semicolons 60 | "no-func-assign": 2, // disallow overwriting functions written as function declarations 61 | "no-inner-declarations": 2, // disallow function or variable declarations in nested blocks 62 | "no-invalid-regexp": 2, // disallow invalid regular expression strings in the RegExp constructor 63 | "no-irregular-whitespace": 2, // disallow irregular whitespace outside of strings and comments 64 | "no-negated-in-lhs": 2, // disallow negation of the left operand of an in expression 65 | "no-obj-calls": 2, // disallow the use of object properties of the global object (Math and JSON) as functions 66 | "no-regex-spaces": 2, // disallow multiple spaces in a regular expression literal 67 | "no-sparse-arrays": 2, // disallow sparse arrays 68 | "no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement 69 | "use-isnan": 2, // disallow comparisons with the value NaN 70 | "valid-jsdoc": 2, // Ensure JSDoc comments are valid (off by default) 71 | "valid-typeof": 2, // Ensure that the results of typeof are compared against a valid string 72 | "no-unexpected-multiline": 2, // Avoid code that looks like two expressions but is actually one (off by default) 73 | 74 | 75 | // 76 | // Best Practices 77 | // 78 | // These are rules designed to prevent you from making mistakes. 79 | // They either prescribe a better way of doing something or help you avoid footguns. 80 | // 81 | "accessor-pairs": 2, // Enforces getter/setter pairs in objects (off by default) 82 | "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default). 0: deep destructuring is not compatible https://github.com/eslint/eslint/issues/1863 83 | "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) 84 | "consistent-return": 2, // require return statements to either always or never specify values 85 | "curly": 2, // specify curly brace conventions for all control statements 86 | "default-case": 2, // require default case in switch statements (off by default) 87 | "dot-notation": 2, // encourages use of dot notation whenever possible 88 | "dot-location": [2, "property"], // enforces consistent newlines before or after dots (off by default) 89 | "eqeqeq": 2, // require the use of === and !== 90 | "guard-for-in": 2, // make sure for-in loops have an if statement (off by default) 91 | "no-alert": 2, // disallow the use of alert, confirm, and prompt 92 | "no-caller": 2, // disallow use of arguments.caller or arguments.callee 93 | "no-div-regex": 2, // disallow division operators explicitly at beginning of regular expression (off by default) 94 | "no-else-return": 2, // disallow else after a return in an if (off by default) 95 | "no-empty-label": 2, // disallow use of labels for anything other then loops and switches 96 | "no-eq-null": 2, // disallow comparisons to null without a type-checking operator (off by default) 97 | "no-eval": 2, // disallow use of eval() 98 | "no-extend-native": 2, // disallow adding to native types 99 | "no-extra-bind": 2, // disallow unnecessary function binding 100 | "no-fallthrough": 2, // disallow fallthrough of case statements 101 | "no-floating-decimal": 2, // disallow the use of leading or trailing decimal points in numeric literals (off by default) 102 | "no-implied-eval": 2, // disallow use of eval()-like methods 103 | "no-iterator": 2, // disallow usage of __iterator__ property 104 | "no-labels": 2, // disallow use of labeled statements 105 | "no-lone-blocks": 2, // disallow unnecessary nested blocks 106 | "no-loop-func": 2, // disallow creation of functions within loops 107 | "no-multi-spaces": 2, // disallow use of multiple spaces 108 | "no-multi-str": 2, // disallow use of multiline strings 109 | "no-native-reassign": 2, // disallow reassignments of native objects 110 | "no-new": 2, // disallow use of new operator when not part of the assignment or comparison 111 | "no-new-func": 2, // disallow use of new operator for Function object 112 | "no-new-wrappers": 2, // disallows creating new instances of String,Number, and Boolean 113 | "no-octal": 2, // disallow use of octal literals 114 | "no-octal-escape": 2, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; 115 | "no-param-reassign": 2, // disallow reassignment of function parameters (off by default) 116 | "no-process-env": 0, // disallow use of process.env (off by default) 117 | "no-proto": 2, // disallow usage of __proto__ property 118 | "no-redeclare": 2, // disallow declaring the same variable more then once 119 | "no-return-assign": 2, // disallow use of assignment in return statement 120 | "no-script-url": 2, // disallow use of javascript: urls. 121 | "no-self-compare": 2, // disallow comparisons where both sides are exactly the same (off by default) 122 | "no-sequences": 2, // disallow use of comma operator 123 | "no-throw-literal": 2, // restrict what can be thrown as an exception (off by default) 124 | "no-unused-expressions": 2, // disallow usage of expressions in statement position 125 | "no-void": 2, // disallow use of void operator (off by default) 126 | "no-warning-comments": [0, {"terms": ["todo", "fixme"], "location": "start"}], // disallow usage of configurable warning terms in comments": 2, // e.g. TODO or FIXME (off by default) 127 | "no-with": 2, // disallow use of the with statement 128 | "radix": 2, // require use of the second argument for parseInt() (off by default) 129 | "vars-on-top": 2, // requires to declare all vars on top of their containing scope (off by default) 130 | "wrap-iife": 2, // require immediate function invocation to be wrapped in parentheses (off by default) 131 | "yoda": 2, // require or disallow Yoda conditions 132 | 133 | 134 | // 135 | // Strict Mode 136 | // 137 | // These rules relate to using strict mode. 138 | // 139 | "strict": [2, "never"], // controls location of Use Strict Directives. 0: required by `babel-eslint` 140 | 141 | 142 | // 143 | // Variables 144 | // 145 | // These rules have to do with variable declarations. 146 | // 147 | "no-catch-shadow": 2, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) 148 | "no-delete-var": 2, // disallow deletion of variables 149 | "no-label-var": 2, // disallow labels that share a name with a variable 150 | "no-shadow": 2, // disallow declaration of variables already declared in the outer scope 151 | "no-shadow-restricted-names": 2, // disallow shadowing of names such as arguments 152 | "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block 153 | "no-undef-init": 2, // disallow use of undefined when initializing variables 154 | "no-undefined": 2, // disallow use of undefined variable (off by default) 155 | "no-unused-vars": 2, // disallow declaration of variables that are not used in the code 156 | "no-use-before-define": 2, // disallow use of variables before they are defined 157 | 158 | 159 | // 160 | //Stylistic Issues 161 | // 162 | // These rules are purely matters of style and are quite subjective. 163 | // 164 | "array-bracket-spacing": [2, "never"], // enforce spacing inside array brackets (off by default) 165 | "brace-style": 2, // enforce one true brace style (off by default) 166 | "camelcase": 2, // require camel case names 167 | "comma-spacing": [2, {"before": false, "after": true}], // enforce spacing before and after comma 168 | "comma-style": [2, "last"], // enforce one true comma style (off by default) 169 | "computed-property-spacing": [2, "never"], //require or disallow padding inside computed properties (off by default) 170 | "consistent-this": [2, "_this"], // enforces consistent naming when capturing the current execution context (off by default) 171 | "eol-last": 2, // enforce newline at the end of file, with no multiple empty lines 172 | "func-names": 0, // require function expressions to have a name (off by default) 173 | "func-style": 0, // enforces use of function declarations or expressions (off by default) 174 | "indent": [2, 2], // this option sets a specific tab width for your code (off by default) 175 | "key-spacing": [2, {"beforeColon": false, "afterColon": true}], // enforces spacing between keys and values in object literal properties 176 | "lines-around-comment": [2, {"beforeBlockComment": true, "afterBlockComment": false, "beforeLineComment": true, "afterLineComment": false, "allowBlockStart": true, "allowBlockEnd": true}], //enforces empty lines around comments (off by default) 177 | "linebreak-style": [2, "unix"], //disallow mixed 'LF' and 'CRLF' as linebreaks (off by default) 178 | "max-nested-callbacks": [2, 3], // specify the maximum depth callbacks can be nested (off by default) 179 | "new-cap": [2, {"newIsCap": true, "capIsNew": false}], // require a capital letter for constructors 180 | "new-parens": 2, // disallow the omission of parentheses when invoking a constructor with no arguments 181 | "newline-after-var": [2, "always"], // allow/disallow an empty newline after var statement (off by default) 182 | "no-array-constructor": 2, // disallow use of the Array constructor 183 | "no-continue": 2, //disallow use of the continue statement (off by default) 184 | "no-inline-comments": 2, // disallow comments inline after code (off by default) 185 | "no-lonely-if": 2, // disallow if as the only statement in an else block (off by default) 186 | "no-mixed-spaces-and-tabs": 2, // disallow mixed spaces and tabs for indentation 187 | "no-multiple-empty-lines": [2, {"max": 2}], // disallow multiple empty lines (off by default) 188 | "no-nested-ternary": 2, // disallow nested ternary expressions (off by default) 189 | "no-new-object": 2, // disallow use of the Object constructor 190 | "no-spaced-func": 2, // disallow space between function identifier and application 191 | "no-ternary": 0, // disallow the use of ternary operators (off by default) 192 | "no-trailing-spaces": 2, // disallow trailing whitespace at the end of lines 193 | "no-underscore-dangle": 2, // disallow dangling underscores in identifiers 194 | "no-unneeded-ternary": 2, //disallow the use of Boolean literals in conditional expressions (off by default) 195 | "object-curly-spacing": [2, "never"], //require or disallow padding inside curly braces (off by default) 196 | "one-var": [2, "never"], // allow just one var statement per function (off by default) 197 | "operator-assignment": [2, "never"], // require assignment operator shorthand where possible or prohibit it entirely (off by default) 198 | "operator-linebreak": [2, "after"], //enforce operators to be placed before or after line breaks (off by default) 199 | "padded-blocks": [2, "never"], // enforce padding within blocks (off by default) 200 | "quote-props": [2, "as-needed"], // require quotes around object literal property names (off by default) 201 | "quotes": [2, "single"], // specify whether double or single quotes should be used 202 | "semi": [2, "always"], // require or disallow use of semicolons instead of ASI 203 | "semi-spacing": [2, {"before": false, "after": true}], // enforce spacing before and after semicolons 204 | "sort-vars": 0, // sort variables within the same declaration block (off by default) 205 | "space-after-keywords": [2, "always"], // require a space after certain keywords (off by default) 206 | "space-before-blocks": [2, "always"], // require or disallow space before blocks (off by default) 207 | "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], // require or disallow space before function opening parenthesis (off by default) 208 | "space-in-parens": [2, "never"], // require or disallow spaces inside parentheses (off by default) 209 | "space-infix-ops": 2, // require spaces around operators 210 | "space-return-throw-case": 2, // require a space after return, throw, and case 211 | "space-unary-ops": [2, {"words": true, "nonwords": false}], // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default) 212 | "spaced-comment": [2, "always"], //require or disallow a space immediately following the // or /* in a comment (off by default) 213 | "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) 214 | 215 | 216 | // 217 | // ECMAScript 6 218 | // 219 | // These rules are only relevant to ES6 environments and are off by default. 220 | // 221 | "constructor-super": 2, //verify super() callings in constructors (off by default) 222 | "generator-star-spacing": [2, "before"], //enforce the spacing around the * in generator functions (off by default) 223 | "no-this-before-super": 2, //disallow to use this/super before super() calling in constructors. (off by default) 224 | "no-var": 2, //require let or const instead of var (off by default) 225 | "object-shorthand": 2, //require method and property shorthand syntax for object literals (off by default) 226 | "prefer-const": 2, //suggest using of const declaration for variables that are never modified after declared (off by default) 227 | 228 | 229 | // 230 | // Legacy 231 | // 232 | // The following rules are included for compatibility with JSHint and JSLint. 233 | // While the names of the rules may not match up with the JSHint/JSLint counterpart, 234 | // the functionality is the same. 235 | // 236 | "max-depth": [2, 3], // specify the maximum depth that blocks can be nested (off by default) 237 | "max-len": [2, 100, 2], // specify the maximum length of a line in your program (off by default) 238 | "max-params": [2, 5], // limits the number of parameters that can be used in the function declaration. (off by default) 239 | "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) 240 | "no-bitwise": 0, // disallow use of bitwise operators (off by default) 241 | "no-plusplus": 2 // disallow use of unary operators, ++ and -- (off by default) 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const app = angular.module('app', [ 2 | 'ngRoute' 3 | ]); 4 | 5 | app.config(require('./app/app_router_config')(app)); 6 | 7 | app.run(['$injector', $injector => app.register = $injector.loadModule.bind($injector)]); 8 | 9 | export default app; 10 | -------------------------------------------------------------------------------- /src/app/about/about.html: -------------------------------------------------------------------------------- 1 |
{{controller.name}}
2 | -------------------------------------------------------------------------------- /src/app/about/about_controller.js: -------------------------------------------------------------------------------- 1 | const $inject = ['BlahService']; 2 | const AboutController = function (blahService) { 3 | this.name = 'About: ' + blahService.blah(); 4 | }; 5 | 6 | AboutController.$inject = $inject; 7 | 8 | export default AboutController; 9 | -------------------------------------------------------------------------------- /src/app/about/about_directive.js: -------------------------------------------------------------------------------- 1 | const $inject = []; 2 | const about = function () { 3 | return { 4 | controller: 'AboutController', 5 | controllerAs: 'controller', 6 | template: require('./about.html') 7 | }; 8 | }; 9 | 10 | about.$inject = $inject; 11 | 12 | export default about; 13 | -------------------------------------------------------------------------------- /src/app/about/index.js: -------------------------------------------------------------------------------- 1 | const about = angular.module('app.about', [ 2 | require('../../blah').name 3 | ]); 4 | 5 | about.controller('AboutController', require('./about_controller')); 6 | about.directive('about', require('./about_directive')); 7 | 8 | export default about; 9 | -------------------------------------------------------------------------------- /src/app/app_router_config.js: -------------------------------------------------------------------------------- 1 | const route = (entry, resolve) => ({ 2 | template: '<' + entry + '>', 3 | resolve: { 4 | async: ['$q', function ($q) { 5 | const defer = $q.defer(); 6 | 7 | resolve(defer.resolve); 8 | return defer.promise; 9 | }] 10 | } 11 | }); 12 | 13 | 14 | export default app => { 15 | const $inject = ['$routeProvider']; 16 | 17 | // We have to use hardcoded value for 'require' so it can be statically built 18 | const RouterConfig = function ($routeProvider) { 19 | $routeProvider 20 | 21 | .when('/', {template: ''}) 22 | 23 | .when('/home', route('home', callback => 24 | require.ensure([], () => 25 | callback(app.register(require('./home').name))))) 26 | 27 | .when('/about', route('about', callback => 28 | require.ensure([], () => 29 | callback(app.register(require('./about').name))))) 30 | 31 | .when('/haml', route('haml', callback => 32 | require.ensure([], () => 33 | callback(app.register(require('./haml').name))))) 34 | 35 | .otherwise({redirectTo: '/'}); 36 | }; 37 | 38 | RouterConfig.$inject = $inject; 39 | 40 | return RouterConfig; 41 | }; 42 | -------------------------------------------------------------------------------- /src/app/haml/haml.haml: -------------------------------------------------------------------------------- 1 | %h1 2 | Hello, {{hello}}! 3 | 4 | %h2 5 | Here is some title: #{@title} 6 | 7 | %p<> 8 | %span<> 9 | I am a HAML template: 10 | 11 | %pre 12 | = "%h1" 13 | = " Hello, {{hello}}!" 14 | 15 | = "%h2" 16 | = " Here is some title: \#{@title}" 17 | 18 | = ".center<>" 19 | = "%span<>" 20 | = " I am a HAML template" 21 | 22 | -------------------------------------------------------------------------------- /src/app/haml/haml.sass: -------------------------------------------------------------------------------- 1 | haml 2 | 3 | h1 4 | color: red 5 | padding: 1em 3em 6 | 7 | p 8 | padding: 1em 3em 9 | 10 | 11 | pre 12 | padding: 1em 13 | border: 1px solid #ccc 14 | background: #f1f2f3 15 | border-radius: 0.5em 16 | -------------------------------------------------------------------------------- /src/app/haml/haml_directive.js: -------------------------------------------------------------------------------- 1 | const $inject = []; 2 | const test = function () { 3 | require('./haml.sass'); 4 | 5 | const link = $scope => { 6 | $scope.hello = 'World'; 7 | }; 8 | 9 | return { 10 | restrict: 'E', 11 | link, 12 | template: require('./haml.haml')({ 13 | title: 'I am replacing #{@title}' 14 | }), 15 | scope: {} 16 | }; 17 | }; 18 | 19 | test.$inject = $inject; 20 | 21 | export default test; 22 | -------------------------------------------------------------------------------- /src/app/haml/index.js: -------------------------------------------------------------------------------- 1 | const mod = angular.module('app.haml', []); 2 | 3 | mod.directive('haml', require('./haml_directive')); 4 | 5 | export default mod; 6 | -------------------------------------------------------------------------------- /src/app/home/home.css: -------------------------------------------------------------------------------- 1 | .trollface { 2 | margin: 5px; 3 | width: 300px; 4 | height: 200px; 5 | background: transparent url("./trollface.png") 0 0 no-repeat; 6 | background-size: contain; 7 | } 8 | 9 | .true-story { 10 | margin: 5px; 11 | width: 300px; 12 | height: 200px; 13 | background: transparent url("./true-story.png") 0 0 no-repeat; 14 | background-size: contain; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/home/home.html: -------------------------------------------------------------------------------- 1 |
{{controller.name}}
2 | 3 |
4 | HelloDirective: 5 | 6 |
7 | 8 |
9 | HelloController as hello: {{hello.test}} 10 |
11 | 12 | Base64-embedded image (<32kb) 13 |
14 | 15 | External image (>32kb) 16 |
17 | -------------------------------------------------------------------------------- /src/app/home/home_controller.js: -------------------------------------------------------------------------------- 1 | const $inject = ['HelloService']; 2 | const HomeController = function (helloService) { 3 | this.name = `Home: ${helloService.hello()}`; 4 | }; 5 | 6 | HomeController.$inject = $inject; 7 | 8 | export default HomeController; 9 | -------------------------------------------------------------------------------- /src/app/home/home_directive.js: -------------------------------------------------------------------------------- 1 | const $inject = []; 2 | const home = function () { 3 | require('./home.css'); 4 | 5 | return { 6 | controller: 'HomeController', 7 | controllerAs: 'controller', 8 | template: require('./home.html') 9 | }; 10 | }; 11 | 12 | home.$inject = $inject; 13 | 14 | export default home; 15 | -------------------------------------------------------------------------------- /src/app/home/index.js: -------------------------------------------------------------------------------- 1 | const home = angular.module('app.home', [ 2 | require('../../hello').name 3 | ]); 4 | 5 | home.controller('HomeController', require('./home_controller')); 6 | home.directive('home', require('./home_directive')); 7 | 8 | export default home; 9 | -------------------------------------------------------------------------------- /src/app/home/trollface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/packetloop/angular-webpack/c55210eaa4db8775c2cdbdbaee9a986038a885f8/src/app/home/trollface.png -------------------------------------------------------------------------------- /src/app/home/true-story.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/packetloop/angular-webpack/c55210eaa4db8775c2cdbdbaee9a986038a885f8/src/app/home/true-story.png -------------------------------------------------------------------------------- /src/blah/blah_service.js: -------------------------------------------------------------------------------- 1 | const $inject = []; 2 | const BlahService = function () { 3 | this.blah = () => 'blah'; 4 | }; 5 | 6 | BlahService.$inject = $inject; 7 | 8 | module.exports = BlahService; 9 | -------------------------------------------------------------------------------- /src/blah/index.js: -------------------------------------------------------------------------------- 1 | const blah = angular.module('blahBlahBlah', []); 2 | 3 | blah 4 | .service('BlahService', require('./blah_service')); 5 | 6 | module.exports = blah; 7 | -------------------------------------------------------------------------------- /src/hello/hello.html: -------------------------------------------------------------------------------- 1 | {{hello}} 2 | -------------------------------------------------------------------------------- /src/hello/hello.sass: -------------------------------------------------------------------------------- 1 | hello 2 | display: inline-block 3 | padding: 3px 4 | background: #f00 5 | color: #fff 6 | font-size: 20px 7 | -------------------------------------------------------------------------------- /src/hello/hello_controller.js: -------------------------------------------------------------------------------- 1 | const $inject = ['HelloService']; 2 | const HelloController = function (helloService) { 3 | this.test = helloService.hello(); 4 | }; 5 | 6 | HelloController.$inject = $inject; 7 | 8 | module.exports = HelloController; 9 | -------------------------------------------------------------------------------- /src/hello/hello_controller_spec.js: -------------------------------------------------------------------------------- 1 | describe('Hello Controller', () => { 2 | let helloService; 3 | let controller; 4 | 5 | beforeEach(() => { 6 | angular.module('test', []) 7 | .controller('HelloController', require('./hello_controller')); 8 | }); 9 | beforeEach(angular.mock.module('test')); 10 | 11 | beforeEach(angular.mock.module($provide => { 12 | helloService = jasmine.createSpyObj('HelloService', ['hello']); 13 | helloService.hello.and.returnValue('test'); 14 | $provide.value('HelloService', helloService); 15 | })); 16 | 17 | beforeEach(angular.mock.inject($injector => { 18 | controller = $injector.get('$controller')('HelloController'); 19 | })); 20 | 21 | it('should have `test` property', () => { 22 | expect(controller.test).toEqual('test'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/hello/hello_directive.js: -------------------------------------------------------------------------------- 1 | require('./hello.sass'); 2 | 3 | const $inject = ['HelloService']; 4 | const Hello = function (helloService) { 5 | const link = $scope => { 6 | $scope.hello = helloService.hello(); 7 | }; 8 | 9 | return { 10 | template: require('./hello.html'), 11 | restrict: 'E', 12 | link, 13 | scope: {} 14 | }; 15 | }; 16 | 17 | Hello.$inject = $inject; 18 | 19 | export default Hello; 20 | -------------------------------------------------------------------------------- /src/hello/hello_directive_spec.js: -------------------------------------------------------------------------------- 1 | describe('Hello Directive', function () { 2 | let helloService; 3 | let $scope; 4 | let $element; 5 | 6 | beforeEach(() => { 7 | angular.module('test', []) 8 | .directive('hello', require('./hello_directive')); 9 | }); 10 | beforeEach(angular.mock.module('test')); 11 | 12 | beforeEach(angular.mock.module($provide => { 13 | helloService = jasmine.createSpyObj('HelloService', ['hello']); 14 | helloService.hello.and.returnValue('test'); 15 | $provide.value('HelloService', helloService); 16 | })); 17 | 18 | beforeEach(angular.mock.inject($injector => { 19 | $scope = $injector.get('$rootScope').$new(true); 20 | $element = $injector.get('$compile')(angular.element(''))($scope); 21 | $scope.$digest(); 22 | })); 23 | 24 | it('should insert `test` into element', () => { 25 | expect($element.html()).toEqual('test'); 26 | }); 27 | 28 | it('should apply styles from hello.sass', () => { 29 | window.document.body.appendChild($element[0]); 30 | const styles = window.getComputedStyle($element[0]); 31 | 32 | expect(styles['background-color']).toEqual('rgb(255, 0, 0)'); 33 | window.document.body.removeChild($element[0]); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/hello/hello_hello_service.js: -------------------------------------------------------------------------------- 1 | const $inject = []; 2 | const HelloHelloService = function () { 3 | this.hello = () => 'hello'; 4 | }; 5 | 6 | HelloHelloService.$inject = $inject; 7 | 8 | export default HelloHelloService; 9 | -------------------------------------------------------------------------------- /src/hello/hello_service.js: -------------------------------------------------------------------------------- 1 | const $inject = ['HelloHelloService', 'BlahService']; 2 | const HelloService = function (helloService, blahService) { 3 | this.hello = () => helloService.hello() + blahService.blah(); 4 | }; 5 | 6 | HelloService.$inject = $inject; 7 | 8 | export default HelloService; 9 | -------------------------------------------------------------------------------- /src/hello/hello_service_spec.js: -------------------------------------------------------------------------------- 1 | describe('Hello Service', function () { 2 | let service; 3 | let helloHelloService; 4 | let blahService; 5 | 6 | beforeEach(angular.mock.module(require('./index.js').name)); 7 | 8 | beforeEach(angular.mock.module(function ($provide) { 9 | helloHelloService = jasmine.createSpyObj('HelloHelloService', ['hello']); 10 | blahService = jasmine.createSpyObj('BlahService', ['blah']); 11 | $provide.value('HelloHelloService', helloHelloService); 12 | $provide.value('BlahService', blahService); 13 | })); 14 | 15 | beforeEach(angular.mock.inject(function ($injector) { 16 | service = $injector.get('HelloService'); 17 | })); 18 | 19 | describe('.hello', () => { 20 | it('should have `hello` method', function () { 21 | expect(service.hello).toBeDefined(); 22 | }); 23 | 24 | it('should call HelloHelloService::hello', function () { 25 | service.hello(); 26 | expect(helloHelloService.hello).toHaveBeenCalled(); 27 | }); 28 | 29 | it('should call BlahService::blah', function () { 30 | service.hello(); 31 | expect(blahService.blah).toHaveBeenCalled(); 32 | }); 33 | 34 | it('should return mocked `test` value', function () { 35 | helloHelloService.hello.and.returnValue('1234'); 36 | blahService.blah.and.returnValue('qwer'); 37 | expect(service.hello()).toEqual('1234qwer'); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/hello/index.js: -------------------------------------------------------------------------------- 1 | const hello = angular.module('hello', [ 2 | require('./../blah/index').name 3 | ]); 4 | 5 | hello 6 | .service('HelloService', require('./hello_service')) 7 | .service('HelloHelloService', require('./hello_hello_service')) 8 | .directive('hello', require('./hello_directive')) 9 | .controller('HelloController', require('./hello_controller')); 10 | 11 | module.exports = hello; 12 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Webpack 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 3 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | module.exports = { 6 | entry: { 7 | app: [ 8 | './vendor/angular.src.js', 9 | './node_modules/angular-route/angular-route.js', 10 | './src/app.js' 11 | ] 12 | }, 13 | output: { 14 | filename: '[name].js', 15 | path: './dest', 16 | chunkFilename: '[id].chunk.js' 17 | }, 18 | module: { 19 | loaders: [ 20 | {test: /\.js$/, loader: 'babel', include: path.resolve('src')}, 21 | {test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css?sourceMap')}, 22 | 23 | { 24 | test: /\.sass$/, 25 | loader: ExtractTextPlugin 26 | .extract('style', 'css?sourceMap!sass?sourceMap&indentedSyntax=true') 27 | }, 28 | {test: /\.(png|jpg)$/, loader: 'url?limit=32768'}, 29 | {test: /\.html$/, loader: 'ng-cache?prefix=[dir]/[dir]'}, 30 | {test: /\.haml$/, loader: 'hamlc-loader'} 31 | ], 32 | preLoaders: [{test: /\.js$/, loader: 'eslint', include: path.resolve('src')}], 33 | noParse: [ 34 | /angular\.src\.js/ 35 | ] 36 | }, 37 | plugins: [ 38 | new ExtractTextPlugin('style.css', {allChunks: true}), 39 | new HtmlWebpackPlugin({ 40 | template: path.resolve('src', 'index.html'), 41 | inject: 'body' 42 | }) 43 | ], 44 | devtool: 'eval-source-map', 45 | devServer: { 46 | historyApiFallback: true, 47 | stats: { 48 | chunkModules: false, 49 | colors: true 50 | }, 51 | contentBase: './src' 52 | }, 53 | eslint: { 54 | configFile: 'src/.eslintrc' 55 | } 56 | }; 57 | --------------------------------------------------------------------------------