├── .editorconfig ├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── LICENSE ├── PUBLISHING.md ├── README.md ├── install.js ├── karma-shim.js ├── karma.conf.js ├── nativescript-ngx-magic.ts ├── package.json ├── packagedev.json ├── packagepublish.json ├── prep.js ├── protractor.conf.js ├── src ├── app │ ├── app.component.html │ ├── app.component.ios.html │ ├── app.component.scss │ ├── app.component.spec.ts │ ├── app.component.tns.html │ ├── app.component.ts │ ├── app.module.ts │ └── plugin │ │ ├── decorators │ │ ├── magic.component.ts │ │ └── utils.ts │ │ └── services │ │ ├── magic.service.spec.ts │ │ └── magic.service.ts ├── main.ts ├── polyfills.ts ├── public │ ├── img │ │ ├── angular.png │ │ ├── favicon.ico │ │ └── shield.png │ └── index.html ├── style │ └── app.scss └── vendor.ts ├── tsconfig-build.json ├── tsconfig.json ├── tslint.json ├── typedoc.json └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Users Environment Variables 23 | .lock-wscript 24 | 25 | # OS generated files # 26 | .DS_Store 27 | ehthumbs.db 28 | Icon? 29 | Thumbs.db 30 | 31 | # Node Files # 32 | /node_modules 33 | /bower_components 34 | 35 | # Coverage # 36 | /coverage/ 37 | 38 | # Typing # 39 | /src/typings/tsd/ 40 | /typings/ 41 | /tsd_typings/ 42 | 43 | # Dist # 44 | /dist 45 | /public/__build__/ 46 | /src/*/__build__/ 47 | __build__/** 48 | .webpack.json 49 | 50 | # Doc # 51 | /doc/ 52 | 53 | # IDE # 54 | .idea/ 55 | *.swp 56 | 57 | *.js 58 | *.js.map 59 | *.d.ts 60 | !*.e2e.js 61 | !install.js 62 | !prep.js 63 | !make.js 64 | !karma*.js 65 | !protractor.conf.js 66 | !webpack.config.js 67 | nativescript/app/app 68 | nativescript/hooks 69 | nativescript/lib 70 | nativescript/node_modules 71 | nativescript/platforms 72 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Users Environment Variables 23 | .lock-wscript 24 | .tsdrc 25 | 26 | #IntelliJ configuration files 27 | .idea 28 | 29 | dist 30 | dev 31 | docs 32 | lib 33 | test 34 | 35 | Thumbs.db 36 | .DS_Store 37 | *.yml 38 | *.ts 39 | !*.d.ts 40 | nativescript 41 | src 42 | src/public/img 43 | src/public/index.html 44 | src/style/app.scss 45 | src/app/app.* 46 | !src/app/plugin/decorators/magic.component.js 47 | !src/app/plugin/decorators/utils.js 48 | !src/app/plugin/services/magic.service.js 49 | *.spec.* 50 | *.e2e.* 51 | CONTRIBUTING.md 52 | karma-shim.js 53 | karma.conf.js 54 | protractor.conf.js 55 | tsconfig.json 56 | tsconfig-build.json 57 | tslint.json 58 | typedoc.json 59 | webpack.config.js 60 | .travis.yml 61 | .jshintrc 62 | .editorconfig 63 | node_modules 64 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Submitting Pull Requests 2 | 3 | **Please follow these basic steps to simplify pull request reviews - if you don't you'll probably just be asked to anyway.** 4 | 5 | * Please rebase your branch against the current master 6 | * Please ensure that the test suite passes **and** that code is lint free before submitting a PR by running: 7 | * ```npm test``` 8 | * If you've added new functionality, **please** include tests which validate its behaviour 9 | * Make reference to possible [issues](https://github.com/preboot/angular-library-seed/issues) on PR comment 10 | 11 | ## Submitting bug reports 12 | 13 | * Please detail the affected browser(s) and operating system(s) 14 | * Please be sure to state which version of node **and** npm you're using 15 | 16 | ## How to get setup and run the code as well as test 17 | 18 | **Note** To run the demo, you must have node v4.x.x or higher and npm 3.x.x. 19 | 20 | ```bash 21 | git clone https://github.com/preboot/angular-library-seed.git 22 | cd angular-library-seed 23 | npm install # or `npm run reinstall` if you get an error 24 | npm start # start with --env dev 25 | ``` 26 | 27 | # Running tests 28 | 29 | ```bash 30 | npm test 31 | ``` 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Nathan Walker 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /PUBLISHING.md: -------------------------------------------------------------------------------- 1 | ## Publishing 2 | 3 | `packagedev.json` should always mirror your `package.json` for development. 4 | After installing dependencies for your library and running your demo, just copy contents of `package.json` to `packagedev.json`. 5 | 6 | Then to publish: 7 | 8 | 1. Setup `packagepublish.json` the way you want your library published (Bumping correct version and setting the description, author, keywords, repo, main, and typings correctly for instance). 9 | 2. `node prep publish` (This will set your `package.json` to be your `packagepublish.json`). 10 | 3. `npm run build` (Create a fresh build of your library) 11 | 4. `npm publish` 12 | 13 | ### Back to development 14 | 15 | After publishing, it's a good idea to set your `package.json` back to development mode before committing your changes. Your `packagedev.json` should be exactly what your development mode package was before publishing above. 16 | 17 | 1. `node prep dev` 18 | 2. Continue developing. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Angular 2 Style Guide](https://mgechev.github.io/angular2-style-guide/images/badge.svg)](https://github.com/mgechev/angular2-style-guide) 2 | [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT) 3 | 4 | ![nativescript-ngx-magic](https://cdn.filestackcontent.com/XXMT4f8S8OGngNsJj0pr?v=0) 5 | 6 | Magically drop a [NativeScript](https://www.nativescript.org/) app into your existing [Angular](https://angular.io/) web application and reuse all your code.* 7 | 8 | *You will be adding NativeScript views, but you already knew that.* 9 | 10 | * [Supported projects that can use magic](#supported-projects) 11 | * [Watch video on how to integrate with Angular CLI](http://www.nativescriptsnacks.com/videos/2016/05/12/magic-scaffolding.html) 12 | * The video is slightly outdated with latest published version but still very applicable. 13 | 14 | ## Install 15 | 16 | ``` 17 | npm i @wwwalkerrun/nativescript-ngx-magic --save 18 | ``` 19 | 20 | ## Usage 21 | 22 | 1. Use `Component` from `@wwwalkerrun/nativescript-ngx-magic` instead of `@angular/core`. [Why?](#why-different-component) 23 | 2. Create NativeScript views ending with `.tns.html` (and/or styles ending with `.tns.css`) for each of your component's. [How?](#how-to-create-nativescript-views) 24 | 3. Ensure your components are declared and routes (if any) are defined in `nativescript/app/app.module.ts`. NativeScript's root `AppModule` is defined seperately to allow you to hook into and/or load distinct native modules if needed. 25 | 4. [Run your truly *native* mobile app with NativeScript!](#run-for-first-time) 26 | 27 | ## Example 28 | 29 | A sample root component, **app.component.ts**: 30 | 31 | **BEFORE**: 32 | ``` 33 | import { Component } from '@angular/core'; 34 | 35 | @Component({ 36 | selector: 'app', 37 | templateUrl: './app.component.html', 38 | styleUrls: ['./app.component.css'] 39 | }) 40 | export class AppComponent {} 41 | ``` 42 | 43 | **AFTER**: 44 | ``` 45 | import { Component } from '@wwwalkerrun/nativescript-ngx-magic'; // notice! 46 | 47 | @Component({ 48 | // notice! - Important to resolve path to templateUrl and/or styleUrls correctly 49 | moduleId: module.id, 50 | selector: 'app', 51 | templateUrl: 'app.component.html', // notice! - removed leading './' 52 | styleUrls: ['app.component.css'] // notice! - removed leading './' 53 | }) 54 | export class AppComponent {} 55 | ``` 56 | 57 | Then create a `.tns.html` NativeScript view template for this component, for example: 58 | 59 | * `app.component.tns.html`: 60 | 61 | ``` 62 | 63 | 64 | 65 | 66 | 67 | ``` 68 | 69 | Then if your component has `styleUrls` defined, create a `.tns.css` file, for example: 70 | 71 | * `app.component.tns.css`: 72 | 73 | ``` 74 | // any custom css you want to use with your {N} view 75 | ``` 76 | 77 | ### Run for first time! 78 | 79 | You will need to have fully completed steps 1 and 2 above. 80 | 81 | Run your app in the iOS Simulator with: 82 | 83 | ``` 84 | npm run start.ios 85 | ``` 86 | 87 | Run your app in an Android emulator with: 88 | 89 | ``` 90 | npm run start.android 91 | ``` 92 | 93 | Welcome to the wonderfully magical world of NativeScript! 94 | 95 | ## How to create NativeScript views 96 | 97 | Based on our example above, assume `app.component.html` looks like this: 98 | 99 | ``` 100 |
101 |
This is my root component
102 |
103 | ``` 104 | 105 | You would then create a new file in `app.component.tns.html` like this: 106 | 107 | ``` 108 | 109 | 110 | 111 | ``` 112 | 113 | You can **also** use platform specific views if desired with the `platformSpecific` Component metadata: 114 | 115 | ``` 116 | import { Component } from '@wwwalkerrun/nativescript-ngx-magic'; 117 | 118 | @Component({ 119 | moduleId: module.id, 120 | selector: 'app', 121 | templateUrl: 'app.component.html', 122 | platformSpecific: true 123 | }) 124 | export class AppComponent {} 125 | ``` 126 | 127 | Then you could create separate views for iOS and Android: 128 | 129 | * `app.component.ios.html` 130 | * `app.component.android.html` 131 | 132 | You can [learn more about NativeScript view options here](https://docs.nativescript.org/ui/ui-views). 133 | 134 | You can also install helpful view snippets for [VS Code here](https://marketplace.visualstudio.com/items?itemName=wwwalkerrun.nativescript-ngx-snippets) or [Atom Editor here](https://atom.io/packages/nativescript-ngx-atom-snippets). 135 | 136 | You can [learn more here](http://angularjs.blogspot.com/2016/03/code-reuse-in-angular-2-native-mobile.html?m=1) about how this setup works and why. 137 | 138 | ## Supported Projects 139 | 140 | * [angular-cli](https://cli.angular.io/) 141 | * [angular-seed](https://github.com/angular/angular-seed) 142 | * [angular2-webpack-seed](https://github.com/NathanWalker/angular2-webpack-seed) 143 | 144 | ### Why different Component? 145 | 146 | `Component` from `nativescript-ngx-magic` is identical to `Component` from `@angular/core`, except it automatically uses NativeScript views when your app runs in a NativeScript mobile app. 147 | 148 | The library provides a custom `Decorator` under the hood. 149 | Feel free to [check it out here](https://github.com/wwwalkerrun/nativescript-ngx-magic/blob/master/src/app/plugin/decorators/magic.component.ts) and it uses a [utility here](https://github.com/wwwalkerrun/nativescript-ngx-magic/blob/master/src/app/plugin/decorators/utils.ts). 150 | 151 | You can see more elaborate use cases of this magic with [angular-seed-advanced](https://github.com/NathanWalker/angular-seed-advanced). 152 | 153 | ### Special Note About AoT 154 | 155 | Currently you cannot use custom component decorators with AoT compilation. This may change in the future but for now you can use this pattern for when you need to create AoT builds for the web: 156 | 157 | ``` 158 | import { Component } from '@angular/core'; 159 | 160 | // just comment this out and use Component from '@angular/core' 161 | // import { Component } from '@wwwalkerrun/nativescript-ngx-magic'; 162 | 163 | @Component({ 164 | // etc. 165 | ``` 166 | 167 | After doing the above, running AoT build will succeed. :) 168 | 169 | The Component from `nativescript-ngx-magic` does the auto `templateUrl` switching to use {N} views when running in the {N} app therefore you don't need it when creating AoT builds for the web. However just note that when going back to run your {N} app, you should comment back in the `Component` from `nativescript-ngx-magic`. Again this temporary inconvenience may be unnecessary in the future. 170 | 171 | ## Requirements 172 | 173 | * [Install NativeScript](http://docs.nativescript.org/start/getting-started#install-nativescript-and-configure-your-environment) 174 | 175 | # License 176 | 177 | [MIT](/LICENSE) 178 | -------------------------------------------------------------------------------- /install.js: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------- 2 | // version 1.09 3 | // ----------------------------------------------------------- 4 | "use strict"; 5 | 6 | var debugging = false; 7 | 8 | var fs = require('fs'); 9 | var cp = require('child_process'); 10 | var path = require('path'); 11 | 12 | var isRanFromNativeScript = fs.existsSync("../../../app/App_Resources"); 13 | var hasNativeScript = fs.existsSync("../../../nativescript"); 14 | 15 | // Figure out angular Seed Path for symlink... 16 | var angularSeedPath = '../../../src/'; 17 | var seeds = ['client', 'app']; 18 | var seedId = 0; 19 | 20 | // Search for the actual seed, depending on where we are run from 21 | for (var i=0;i 2) { 36 | if (process.argv[2] === 'symlink') { 37 | createRootSymLink(); 38 | console.log("Created Symlink"); 39 | } 40 | return 0; 41 | } 42 | 43 | if (!hasNativeScript && !isRanFromNativeScript) { 44 | console.log("Installing NativeScript Angular Template..."); 45 | cp.execSync('tns create nativescript --template https://github.com/NathanWalker/template-hello-world-ng.git', {cwd: '../../..'}); 46 | console.log("Installing NativeScript support files..."); 47 | cp.execSync('npm install', {cwd: '../../../nativescript'}); 48 | 49 | console.log("Installing support files"); 50 | if (process.platform === 'darwin') { 51 | try { 52 | cp.execSync('npm install image-to-ascii-cli', {cwd: '../../..'}); 53 | } 54 | catch (Err) { 55 | console.log("Install Error", Err); 56 | } 57 | } 58 | 59 | console.log("Configuring..."); 60 | if (debugging) { 61 | cp.execSync('tns plugin add ../node_modules/@wwwalkerrun/nativescript-ngx-magic', {cwd: '../../../nativescript'}); 62 | } else { 63 | cp.execSync('tns plugin add @wwwalkerrun/nativescript-ngx-magic', {cwd: '../../../nativescript'}); 64 | } 65 | 66 | // remove sample component 67 | if (fs.existsSync('../../../nativescript/app/app.component.ts')) { 68 | console.log("Removing sample component"); 69 | fs.unlinkSync('../../../nativescript/app/app.component.ts'); 70 | } 71 | if (fs.existsSync('../../../nativescript/app/app.component.html')) { 72 | console.log("Removing sample component html"); 73 | fs.unlinkSync('../../../nativescript/app/app.component.html'); 74 | } 75 | 76 | 77 | 78 | // We need to create a symlink 79 | try { 80 | createSymLink(); 81 | } catch (err) { 82 | if (debugging) { 83 | console.log("Symlink error: ", err); 84 | } 85 | // Failed, which means they weren't running root; so lets try to get root 86 | AttemptRootSymlink(); 87 | } 88 | 89 | // Might silent fail on OSX, so we have to see if it exists 90 | if (!fs.existsSync(nativescriptClientPath)) { 91 | AttemptRootSymlink(); 92 | } 93 | 94 | // This does not look good on windows; windows ansi support in node sucks... So we aren't going to do this in windows 95 | if (process.platform === "darwin") { 96 | // image to ascii uses GM which may not be installed, so if it isn't installed; don't print the error message 97 | try { 98 | var ascii = cp.execSync('image-to-ascii -i https://cdn.filestackcontent.com/XXMT4f8S8OGngNsJj0pr', {cwd: '../../image-to-ascii-cli/bin/'}).toString(); 99 | if (ascii.length > 30) { 100 | console.log(ascii); 101 | } 102 | } 103 | catch (err) { 104 | // Do Nothing; if the site can't be resolved; we don't want to fail the script 105 | } 106 | } 107 | 108 | displayFinalHelp(); 109 | 110 | if (!fs.existsSync(nativescriptClientPath)) { 111 | console.log("We were unable to create a symlink - from -"); 112 | console.log(" ", resolve(angularSeedPath), " - to - "); 113 | console.log(" ", resolve(nativescriptClientPath)); 114 | console.log("If you don't create this symlink, you will have to manually copy the code each time you change it."); 115 | } 116 | 117 | 118 | } 119 | 120 | if (isRanFromNativeScript) { 121 | if (!fs.existsSync('installed.ns') ) { 122 | fs.writeFileSync('installed.ns', 'installed'); 123 | fixTsConfig(); 124 | fixRefFile(); 125 | fixNativeScriptPackage(); 126 | fixAngularPackage(); 127 | fixMainFile( figureOutRootComponent()); 128 | 129 | // when being run from inside {N} app, the directory is different 130 | var srcRoot = '../../../../src/'; 131 | for (var i=0;i 1) { 211 | return result(r); 212 | } 213 | } else { 214 | // angular cli 215 | return result(r); 216 | } 217 | return null; 218 | } 219 | return result(r); 220 | } 221 | 222 | /** 223 | * This will attempt to run the install script as root to make a symlink 224 | * 225 | */ 226 | function AttemptRootSymlink() { 227 | 228 | if (process.platform === 'win32') { 229 | var curPath = resolve("./"); 230 | if (debugging) { 231 | console.log("RootSymlink Base path is", curPath); 232 | } 233 | cp.execSync("powershell -Command \"Start-Process 'node' -ArgumentList '"+curPath+"/install.js symlink' -verb runas\""); 234 | } else { 235 | console.log("To automatically create a SymLink between your web app and NativeScript, we need root for a second."); 236 | cp.execSync("sudo "+process.argv[0] + " " + process.argv[1] +" symlink"); 237 | } 238 | } 239 | 240 | /** 241 | * Create the symlink when running as root 242 | */ 243 | function createRootSymLink() { 244 | var li1 = process.argv[1].lastIndexOf('\\'), li2 = process.argv[1].lastIndexOf('/'); 245 | if (li2 > li1) { li1 = li2; } 246 | var AppPath = process.argv[1].substring(0,li1); 247 | var p1 = resolve(AppPath + "/" + nativescriptClientPath); 248 | var p2 = resolve(AppPath + "/" + angularSeedPath); 249 | if (debugging) { 250 | console.log("Path: ", p1, p2); 251 | } 252 | fs.symlinkSync(p2,p1,'junction'); 253 | } 254 | 255 | /** 256 | * Create Symlink 257 | */ 258 | function createSymLink() { 259 | if (debugging) { 260 | console.log("Attempting to Symlink", angularSeedPath, nativescriptClientPath); 261 | } 262 | fs.symlinkSync(resolve(angularSeedPath),resolve(nativescriptClientPath),'junction'); 263 | } 264 | 265 | /** 266 | * This fixes the TS Config file in the nativescript folder 267 | */ 268 | function fixTsConfig() { 269 | var tsConfig={}, tsFile = '../../../tsconfig.json'; 270 | if (fs.existsSync(tsFile)) { 271 | tsConfig = require(tsFile); 272 | } 273 | if (!tsConfig.compilerOptions || !tsConfig.compilerOptions.typeRoots) { 274 | tsConfig.compilerOptions = { 275 | target: "es5", 276 | module: "commonjs", 277 | declaration: false, 278 | removeComments: true, 279 | noLib: false, 280 | emitDecoratorMetadata: true, 281 | experimentalDecorators: true, 282 | lib: [ 283 | "dom" 284 | ], 285 | sourceMap: true, 286 | pretty: true, 287 | allowUnreachableCode: false, 288 | allowUnusedLabels: false, 289 | noImplicitAny: false, 290 | noImplicitReturns: true, 291 | noImplicitUseStrict: false, 292 | noFallthroughCasesInSwitch: true, 293 | typeRoots: [ 294 | "node_modules/@types", 295 | "node_modules" 296 | ], 297 | types: [ 298 | "jasmine" 299 | ] 300 | }; 301 | } 302 | 303 | // See: https://github.com/NativeScript/nativescript-angular/issues/205 304 | // tsConfig.compilerOptions.noEmitHelpers = false; 305 | // tsConfig.compilerOptions.noEmitOnError = false; 306 | 307 | if (!tsConfig.exclude) { 308 | tsConfig.exclude = []; 309 | } 310 | if (tsConfig.exclude.indexOf('node_modules') === -1) { 311 | tsConfig.exclude.push('node_modules'); 312 | } 313 | if (tsConfig.exclude.indexOf('platforms') === -1) { 314 | tsConfig.exclude.push('platforms'); 315 | } 316 | 317 | fs.writeFileSync(tsFile, JSON.stringify(tsConfig, null, 4), 'utf8'); 318 | } 319 | 320 | /** 321 | * This fixes the references file to work with TS 2.0 in the nativescript folder 322 | */ 323 | function fixRefFile() { 324 | var existingRef='', refFile = '../../../references.d.ts'; 325 | if (fs.existsSync(refFile)) { 326 | existingRef = fs.readFileSync(refFile).toString(); 327 | } 328 | 329 | if (existingRef.indexOf('typescript/lib/lib.d.ts') === -1) { 330 | // has not been previously modified 331 | var fix = '/// \n' + 332 | '/// \n'; 333 | 334 | 335 | fs.writeFileSync(refFile, fix, 'utf8'); 336 | } 337 | } 338 | 339 | /** 340 | * Fix the NativeScript Package file 341 | */ 342 | function fixNativeScriptPackage() { 343 | // var packageJSON = {}, packageFile = '../../../package.json'; 344 | // packageJSON.name = "NativeScriptApp"; 345 | // packageJSON.version = "0.0.0"; 346 | 347 | // // var AngularJSON = {}; 348 | // if (fs.existsSync(packageFile)) { 349 | // packageJSON = require(packageFile); 350 | // } else { 351 | // console.log("This should not happen, your are missing your package.json file!"); 352 | // return; 353 | // } 354 | // // if (fs.existsSync('../angular2/package.json')) { 355 | // // AngularJSON = require('../angular2/package.json'); 356 | // // } else { 357 | // // // Copied from the Angular2.0.0-beta-16 package.json, this is a fall back 358 | // // AngularJSON.peerDependencies = { 359 | // // "es6-shim": "^0.35.0", 360 | // // "reflect-metadata": "0.1.2", 361 | // // "rxjs": "5.0.0-beta.6", 362 | // // "zone.js": "^0.6.12" 363 | // // }; 364 | // // } 365 | 366 | // packageJSON.nativescript['tns-ios'] = { version: "2.4.0" }; 367 | // packageJSON.nativescript['tns-android'] = {version: "2.4.1" }; 368 | 369 | // // Copy over all the Peer Dependencies 370 | // // for (var key in AngularJSON.peerDependencies) { 371 | // // if (AngularJSON.peerDependencies.hasOwnProperty(key)) { 372 | // // packageJSON.dependencies[key] = AngularJSON.peerDependencies[key]; 373 | // // } 374 | // // } 375 | 376 | 377 | // // TODO: Can we get these from somewhere rather than hardcoding them, maybe need to pull/download the package.json from the default template? 378 | // if (!packageJSON.devDependencies) { 379 | // packageJSON.devDependencies = {}; 380 | // } 381 | // packageJSON.devDependencies["@types/jasmine"] = "^2.5.35"; 382 | // packageJSON.devDependencies["babel-traverse"] = "6.12.0"; 383 | // packageJSON.devDependencies["babel-types"] = "6.11.1"; 384 | // packageJSON.devDependencies.babylon = "6.8.4"; 385 | // packageJSON.devDependencies.filewalker = "0.1.2"; 386 | // packageJSON.devDependencies.lazy = "1.0.11"; 387 | // // packageJSON.devDependencies["nativescript-dev-typescript"] = "^0.3.2"; 388 | // packageJSON.devDependencies.typescript = "^2.0.10"; 389 | 390 | // fs.writeFileSync(packageFile, JSON.stringify(packageJSON, null, 4), 'utf8'); 391 | } 392 | 393 | /** 394 | * Fix the Angular Package 395 | */ 396 | function fixAngularPackage() { 397 | var packageJSON = {}, packageFile = '../../../../package.json'; 398 | if (fs.existsSync(packageFile)) { 399 | packageJSON = require(packageFile); 400 | } else { 401 | console.log("This should not happen, your are missing your main package.json file!"); 402 | return; 403 | } 404 | 405 | if (!packageJSON.scripts) { 406 | packageJSON.scripts = {}; 407 | } 408 | 409 | packageJSON.scripts["start.ios"] = "cd nativescript && tns run ios --emulator"; 410 | packageJSON.scripts["start.livesync.ios"] = "cd nativescript && tns livesync ios --emulator --watch"; 411 | packageJSON.scripts["start.android"] = "cd nativescript && tns run android --emulator"; 412 | packageJSON.scripts["start.livesync.android"] = "cd nativescript && tns livesync android --emulator --watch"; 413 | 414 | fs.writeFileSync(packageFile, JSON.stringify(packageJSON, null, 4), 'utf8'); 415 | } 416 | 417 | /** 418 | * Fix the Main NativeScript File 419 | * @param component 420 | */ 421 | function fixMainFile(component) { 422 | // var mainTS = '', mainFile = '../../../app/main.ts'; 423 | // if (fs.existsSync(mainFile)) { 424 | // mainTS = fs.readFileSync(mainFile).toString(); 425 | // } 426 | 427 | // if (mainTS.indexOf('MagicService') === -1) { 428 | // // has not been previously modified 429 | // var fix = '// this import should be first in order to load some required settings (like globals and reflect-metadata)\n' + 430 | // 'import { platformNativeScriptDynamic, NativeScriptModule } from "nativescript-angular/platform";\n' + 431 | // 'import { NgModule } from "@angular/core";\n' + 432 | // 'import { AppComponent } from "./app/app.component";\n' + 433 | // '\n' + 434 | // '@NgModule({\n' + 435 | // ' declarations: [AppComponent],\n' + 436 | // ' bootstrap: [AppComponent],\n' + 437 | // ' imports: [NativeScriptModule],\n' + 438 | // '})\n' + 439 | // 'class AppComponentModule {}\n\n' + 440 | // 'platformNativeScriptDynamic().bootstrapModule(AppComponentModule);'; 441 | 442 | 443 | // fs.writeFileSync(mainFile, fix, 'utf8'); 444 | // } 445 | } 446 | 447 | /** 448 | * Fix .gitignore 449 | * @param path 450 | */ 451 | function fixGitIgnore(ignorePattern) { 452 | var fileString = '', ignoreFile = '../../../../.gitignore'; 453 | if (fs.existsSync(ignoreFile)) { 454 | fileString = fs.readFileSync(ignoreFile).toString(); 455 | } 456 | 457 | if (fileString.indexOf(ignorePattern) === -1) { 458 | // has not been previously modified 459 | var fix = fileString + 460 | '\n' + 461 | ignorePattern; 462 | 463 | 464 | fs.writeFileSync(ignoreFile, fix, 'utf8'); 465 | } 466 | } 467 | 468 | /** 469 | * Display final help screen! 470 | */ 471 | function displayFinalHelp() 472 | { 473 | console.log("-------------- Welcome to the Magical World of NativeScript -----------------------------"); 474 | console.log("To finish, follow this guide https://github.com/wwwalkerrun/nativescript-ngx-magic#usage"); 475 | console.log("After you have completed the steps in the usage guide, you can then:"); 476 | console.log(""); 477 | console.log("Run your app in the iOS Simulator with these options:"); 478 | console.log(" npm run start.ios"); 479 | console.log(" npm run start.livesync.ios"); 480 | console.log(""); 481 | console.log("Run your app in an Android emulator with these options:"); 482 | console.log(" npm run start.android"); 483 | console.log(" npm run start.livesync.android"); 484 | console.log("-----------------------------------------------------------------------------------------"); 485 | console.log(""); 486 | } 487 | 488 | function splitPath(v) { 489 | var x; 490 | if (v.indexOf('/') !== -1) { 491 | x = v.split('/'); 492 | } else { 493 | x = v.split("\\"); 494 | } 495 | return x; 496 | } 497 | 498 | function resolve(v) { 499 | var cwdPath = splitPath(process.argv[1]); 500 | // Kill the Script name 501 | cwdPath.length = cwdPath.length - 1; 502 | 503 | var resolvePath = splitPath(v); 504 | 505 | // Eliminate a trailing slash/backslash 506 | if (cwdPath[cwdPath.length-1] === "") { cwdPath.pop(); } 507 | 508 | if (v[0] === '/' || v[0] === "\\") { cwdPath = []; } 509 | for (var i=0;i (http://github.com/NathanWalker)", 8 | "contributors": ["Nathanael Anderson (http://github.com/NathanaelA)"], 9 | "repository": { 10 | "url": "https://github.com/wwwalkerrun/nativescript-ngx-magic" 11 | }, 12 | "nativescript": { 13 | "platforms": { 14 | "ios": "2.4.0", 15 | "android": "2.4.1" 16 | } 17 | }, 18 | "license": "MIT", 19 | "scripts": { 20 | "clean": "rimraf node_modules doc dist && npm cache clean", 21 | "clean-install": "npm run clean && npm install", 22 | "clean-start": "npm run clean-install && npm start", 23 | "watch": "webpack --watch --progress --profile", 24 | "build": "tsc --p tsconfig-build.json", 25 | "server": "webpack-dev-server --inline --progress --port 8080", 26 | "webdriver-update": "webdriver-manager update", 27 | "webdriver-start": "webdriver-manager start", 28 | "lint": "tslint --force \"src/**/*.ts\"", 29 | "e2e": "protractor", 30 | "e2e-live": "protractor --elementExplorer", 31 | "pretest": "npm run lint", 32 | "test": "karma start", 33 | "posttest": "remap-istanbul -i coverage/json/coverage-final.json -o coverage/html -t html", 34 | "test-watch": "karma start --no-single-run --auto-watch", 35 | "ci": "npm run e2e && npm run test", 36 | "docs": "typedoc --options typedoc.json src/app/app.component.ts", 37 | "start": "npm run server", 38 | "start:hmr": "npm run server -- --hot", 39 | "postinstall": "npm run webdriver-update" 40 | }, 41 | "dependencies": { 42 | "@angular/common": "2.4.2", 43 | "@angular/compiler": "2.4.2", 44 | "@angular/core": "2.4.2", 45 | "@angular/forms": "2.4.2", 46 | "@angular/http": "2.4.2", 47 | "@angular/platform-browser": "2.4.2", 48 | "@angular/platform-browser-dynamic": "2.4.2", 49 | "@angular/router": "3.4.2", 50 | "core-js": "^2.4.1", 51 | "reflect-metadata": "^0.1.3", 52 | "rxjs": "5.0.1", 53 | "zone.js": "^0.7.2" 54 | }, 55 | "devDependencies": { 56 | "@angularclass/hmr": "^1.0.1", 57 | "@angularclass/hmr-loader": "^3.0.2", 58 | "@types/core-js": "^0.9.0", 59 | "@types/jasmine": "^2.2.29", 60 | "@types/node": "^6.0.38", 61 | "@types/selenium-webdriver": "2.53.33", 62 | "angular2-template-loader": "^0.6.0", 63 | "autoprefixer": "^6.3.2", 64 | "awesome-typescript-loader": "^3.0.0-beta.17", 65 | "codelyzer": "1.0.0-beta.3", 66 | "copy-webpack-plugin": "^4.0.0", 67 | "css-loader": "^0.26.1", 68 | "extract-text-webpack-plugin": "^2.0.0-beta.4", 69 | "file-loader": "^0.9.0", 70 | "html-loader": "^0.4.0", 71 | "html-webpack-plugin": "^2.8.1", 72 | "istanbul-instrumenter-loader": "^0.2.0", 73 | "jasmine-core": "^2.3.4", 74 | "jasmine-spec-reporter": "^2.4.0", 75 | "json-loader": "^0.5.3", 76 | "karma": "1.3.0", 77 | "karma-chrome-launcher": "^2.0.0", 78 | "karma-coverage": "^1.0.0", 79 | "karma-jasmine": "^1.0.2", 80 | "karma-mocha-reporter": "^2.0.3", 81 | "karma-remap-istanbul": "0.2.1", 82 | "karma-sourcemap-loader": "^0.3.7", 83 | "karma-webpack": "1.8.0", 84 | "node-sass": "^3.4.2", 85 | "null-loader": "0.1.1", 86 | "postcss-loader": "^1.1.0", 87 | "protractor": "^4.0.10", 88 | "raw-loader": "0.5.1", 89 | "remap-istanbul": "^0.6.4", 90 | "rimraf": "^2.5.1", 91 | "sass-loader": "^4.0.0", 92 | "shelljs": "^0.7.0", 93 | "style-loader": "^0.13.0", 94 | "ts-helpers": "^1.1.1", 95 | "tslint": "^3.4.0", 96 | "tslint-loader": "^2.1.0", 97 | "typedoc": "^0.5.1", 98 | "typescript": "2.0.10", 99 | "url-loader": "^0.5.6", 100 | "webpack": "2.1.0-beta.25", 101 | "webpack-dev-server": "2.1.0-beta.9" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packagedev.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wwwalkerrun/nativescript-ngx-magic", 3 | "version": "2.0.4", 4 | "description": "Magically integrate your Angular web app with NativeScript.", 5 | "main": "nativescript-ngx-magic", 6 | "typings": "nativescript-ngx-magic.d.ts", 7 | "author": "Nathan Walker (http://github.com/NathanWalker)", 8 | "contributors": ["Nathanael Anderson (http://github.com/NathanaelA)"], 9 | "repository": { 10 | "url": "https://github.com/wwwalkerrun/nativescript-ngx-magic" 11 | }, 12 | "nativescript": { 13 | "platforms": { 14 | "ios": "2.4.0", 15 | "android": "2.4.1" 16 | } 17 | }, 18 | "license": "MIT", 19 | "scripts": { 20 | "clean": "rimraf node_modules doc dist && npm cache clean", 21 | "clean-install": "npm run clean && npm install", 22 | "clean-start": "npm run clean-install && npm start", 23 | "watch": "webpack --watch --progress --profile", 24 | "build": "tsc --p tsconfig-build.json", 25 | "server": "webpack-dev-server --inline --progress --port 8080", 26 | "webdriver-update": "webdriver-manager update", 27 | "webdriver-start": "webdriver-manager start", 28 | "lint": "tslint --force \"src/**/*.ts\"", 29 | "e2e": "protractor", 30 | "e2e-live": "protractor --elementExplorer", 31 | "pretest": "npm run lint", 32 | "test": "karma start", 33 | "posttest": "remap-istanbul -i coverage/json/coverage-final.json -o coverage/html -t html", 34 | "test-watch": "karma start --no-single-run --auto-watch", 35 | "ci": "npm run e2e && npm run test", 36 | "docs": "typedoc --options typedoc.json src/app/app.component.ts", 37 | "start": "npm run server", 38 | "start:hmr": "npm run server -- --hot", 39 | "postinstall": "npm run webdriver-update" 40 | }, 41 | "dependencies": { 42 | "@angular/common": "2.4.2", 43 | "@angular/compiler": "2.4.2", 44 | "@angular/core": "2.4.2", 45 | "@angular/forms": "2.4.2", 46 | "@angular/http": "2.4.2", 47 | "@angular/platform-browser": "2.4.2", 48 | "@angular/platform-browser-dynamic": "2.4.2", 49 | "@angular/router": "3.4.2", 50 | "core-js": "^2.4.1", 51 | "reflect-metadata": "^0.1.3", 52 | "rxjs": "5.0.1", 53 | "zone.js": "^0.7.2" 54 | }, 55 | "devDependencies": { 56 | "@angularclass/hmr": "^1.0.1", 57 | "@angularclass/hmr-loader": "^3.0.2", 58 | "@types/core-js": "^0.9.0", 59 | "@types/jasmine": "^2.2.29", 60 | "@types/node": "^6.0.38", 61 | "@types/selenium-webdriver": "2.53.33", 62 | "angular2-template-loader": "^0.6.0", 63 | "autoprefixer": "^6.3.2", 64 | "awesome-typescript-loader": "^3.0.0-beta.17", 65 | "codelyzer": "1.0.0-beta.3", 66 | "copy-webpack-plugin": "^4.0.0", 67 | "css-loader": "^0.26.1", 68 | "extract-text-webpack-plugin": "^2.0.0-beta.4", 69 | "file-loader": "^0.9.0", 70 | "html-loader": "^0.4.0", 71 | "html-webpack-plugin": "^2.8.1", 72 | "istanbul-instrumenter-loader": "^0.2.0", 73 | "jasmine-core": "^2.3.4", 74 | "jasmine-spec-reporter": "^2.4.0", 75 | "json-loader": "^0.5.3", 76 | "karma": "1.3.0", 77 | "karma-chrome-launcher": "^2.0.0", 78 | "karma-coverage": "^1.0.0", 79 | "karma-jasmine": "^1.0.2", 80 | "karma-mocha-reporter": "^2.0.3", 81 | "karma-remap-istanbul": "0.2.1", 82 | "karma-sourcemap-loader": "^0.3.7", 83 | "karma-webpack": "1.8.0", 84 | "node-sass": "^3.4.2", 85 | "null-loader": "0.1.1", 86 | "postcss-loader": "^1.1.0", 87 | "protractor": "^4.0.10", 88 | "raw-loader": "0.5.1", 89 | "remap-istanbul": "^0.6.4", 90 | "rimraf": "^2.5.1", 91 | "sass-loader": "^4.0.0", 92 | "shelljs": "^0.7.0", 93 | "style-loader": "^0.13.0", 94 | "ts-helpers": "^1.1.1", 95 | "tslint": "^3.4.0", 96 | "tslint-loader": "^2.1.0", 97 | "typedoc": "^0.5.1", 98 | "typescript": "2.0.10", 99 | "url-loader": "^0.5.6", 100 | "webpack": "2.1.0-beta.25", 101 | "webpack-dev-server": "2.1.0-beta.9" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packagepublish.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wwwalkerrun/nativescript-ngx-magic", 3 | "version": "2.0.4", 4 | "description": "Magically integrate your Angular web app with NativeScript.", 5 | "main": "nativescript-ngx-magic", 6 | "typings": "nativescript-ngx-magic.d.ts", 7 | "author": "Nathan Walker (http://github.com/NathanWalker)", 8 | "contributors": ["Nathanael Anderson (http://github.com/NathanaelA)"], 9 | "repository": { 10 | "url": "https://github.com/wwwalkerrun/nativescript-ngx-magic" 11 | }, 12 | "keywords": [ 13 | "angular", 14 | "nativescript", 15 | "ngx", 16 | "ng2" 17 | ], 18 | "nativescript": { 19 | "platforms": { 20 | "ios": "2.4.0", 21 | "android": "2.4.1" 22 | } 23 | }, 24 | "license": "MIT", 25 | "scripts": { 26 | "postinstall": "node install.js" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /prep.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | fs.createReadStream('package' + process.argv[2] + '.json').pipe(fs.createWriteStream('package.json')); 3 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | baseUrl: 'http://localhost:8080/', 3 | 4 | specs: [ 5 | 'src/**/*.e2e-spec.js' 6 | ], 7 | exclude: [], 8 | 9 | framework: 'jasmine2', 10 | 11 | allScriptsTimeout: 110000, 12 | 13 | jasmineNodeOpts: { 14 | showTiming: true, 15 | showColors: true, 16 | isVerbose: false, 17 | includeStackTrace: false, 18 | defaultTimeoutInterval: 400000 19 | }, 20 | directConnect: true, 21 | 22 | capabilities: { 23 | 'browserName': 'chrome' 24 | }, 25 | 26 | onPrepare: function () { 27 | var SpecReporter = require('jasmine-spec-reporter'); 28 | // add jasmine spec reporter 29 | jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: true})); 30 | 31 | browser.ignoreSynchronization = true; 32 | }, 33 | 34 | 35 | /** 36 | * Angular 2 configuration 37 | * 38 | * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching 39 | * `rootEl` 40 | * 41 | */ 42 | useAllAngular2AppRoots: true 43 | }; 44 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Magic!

3 |
4 |
5 | 6 | 7 | 8 |
{{statement}}
9 |
10 | 13 | -------------------------------------------------------------------------------- /src/app/app.component.ios.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | // styles applied on :host are applied on the current component, "app" in this case 2 | :host { 3 | display: block; 4 | } 5 | 6 | header { 7 | background-color: #fff; 8 | padding:0; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | width: 100%; 13 | box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5); 14 | } 15 | 16 | h1 { 17 | color: #000; 18 | text-align: center; 19 | font-family: sans-serif; 20 | } 21 | 22 | img { 23 | width:50%; 24 | } 25 | 26 | main { 27 | padding: 1em; 28 | font-family: Arial, Helvetica, sans-serif; 29 | text-align: center; 30 | margin-top: 50px; 31 | display: block; 32 | } 33 | 34 | footer { 35 | text-align: center; 36 | font-size: 0.8em; 37 | width: 100%; 38 | position: absolute; 39 | bottom: 20px; 40 | 41 | a { 42 | color: #fff; 43 | text-decoration: none; 44 | font-family: sans-serif; 45 | 46 | &:hover { 47 | text-decoration: underline; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('App', () => { 6 | // provide our implementations or mocks to the dependency injector 7 | beforeEach(() => { 8 | TestBed.configureTestingModule({ 9 | declarations: [ 10 | AppComponent 11 | ] 12 | }); 13 | }); 14 | 15 | it('should have an url', () => { 16 | let fixture = TestBed.createComponent(AppComponent); 17 | fixture.detectChanges(); 18 | expect(fixture.debugElement.componentInstance.url).toEqual('https://github.com/NathanWalker/nativescript-ngx-magic'); 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /src/app/app.component.tns.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | // import { Component } from '@angular/core'; 2 | import { Component } from './plugin/decorators/magic.component'; 3 | 4 | import '../style/app.scss'; 5 | 6 | @Component({ 7 | selector: 'my-app', // 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.scss'], 10 | }) 11 | export class AppComponent { 12 | statement: string = 'Creating magic with NativeScript + Angular'; 13 | url: string = 'https://github.com/NathanWalker/nativescript-ngx-magic'; 14 | } 15 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, ApplicationRef } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { HttpModule } from '@angular/http'; 4 | import { FormsModule } from '@angular/forms'; 5 | 6 | import { AppComponent } from './app.component'; 7 | 8 | import { removeNgStyles, createNewHosts } from '@angularclass/hmr'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | BrowserModule, 13 | HttpModule, 14 | FormsModule 15 | ], 16 | declarations: [ 17 | AppComponent 18 | ], 19 | bootstrap: [AppComponent] 20 | }) 21 | export class AppModule { 22 | constructor(public appRef: ApplicationRef) {} 23 | hmrOnInit(store) { 24 | console.log('HMR store', store); 25 | } 26 | hmrOnDestroy(store) { 27 | let cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement); 28 | // recreate elements 29 | store.disposeOldHosts = createNewHosts(cmpLocation); 30 | // remove styles 31 | removeNgStyles(); 32 | } 33 | hmrAfterDestroy(store) { 34 | // display new elements 35 | store.disposeOldHosts(); 36 | delete store.disposeOldHosts; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/plugin/decorators/magic.component.ts: -------------------------------------------------------------------------------- 1 | import { MagicDecoratorUtils } from './utils'; 2 | 3 | export function Component(metadata: any = {}) { 4 | return function(cls: any) { 5 | return MagicDecoratorUtils.annotateComponent(cls, metadata); 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/plugin/decorators/utils.ts: -------------------------------------------------------------------------------- 1 | // angular 2 | import { Component } from '@angular/core'; 3 | 4 | // app 5 | import { MagicService } from '../services/magic.service'; 6 | 7 | declare var Reflect: any; 8 | const _reflect: any = Reflect; 9 | 10 | export class MagicDecoratorUtils { 11 | public static getMetadata(metadata: any = {}, customDecoratorMetadata?: any) { 12 | 13 | if (metadata.templateUrl) { 14 | // correct view for platform target 15 | metadata.templateUrl = MagicService.TEMPLATE_URL(metadata.templateUrl, !!metadata.platformSpecific); 16 | } 17 | 18 | if (metadata.styleUrls) { 19 | // correct style for platform target 20 | metadata.styleUrls = MagicService.STYLE_URLS(metadata.styleUrls); 21 | } 22 | 23 | return metadata; 24 | } 25 | 26 | public static annotateComponent(cls: any, metadata: any = {}, customDecoratorMetadata?: any) { 27 | let annotations = _reflect.getMetadata('annotations', cls) || []; 28 | annotations.push(new Component(MagicDecoratorUtils.getMetadata(metadata, customDecoratorMetadata))); 29 | _reflect.defineMetadata('annotations', annotations, cls); 30 | return cls; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/plugin/services/magic.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Injector } from '@angular/core'; 2 | import { MagicService } from './magic.service'; 3 | import { getTestBed, TestBed } from '@angular/core/testing'; 4 | 5 | describe('SampleService', () => { 6 | beforeEach(() => { 7 | spyOn(console, 'error'); 8 | }); 9 | 10 | it('should provide resilient magic', () => { 11 | expect(MagicService.IS_NATIVESCRIPT()).toBe(true); 12 | expect(MagicService.IS_IOS()).toBe(true); 13 | expect(MagicService.IS_ANDROID()).toBe(false); 14 | expect(MagicService.TEMPLATE_URL('./components/test.html')).toBe('./components/test.html'); 15 | expect(MagicService.STYLE_URLS(['./components/test.css'])).toBe('./components/test.css'); 16 | }); 17 | 18 | it('should provide platform specific magic', () => { 19 | expect(MagicService.TEMPLATE_URL('./components/test.html', true)).toBe('./components/test.html'); 20 | expect(MagicService.STYLE_URLS(['./components/test.css'], true)).toBe('./components/test.css'); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/app/plugin/services/magic.service.ts: -------------------------------------------------------------------------------- 1 | declare var NSObject, NSString, android, java; 2 | 3 | const getFilepath = function (path: string) { 4 | return path.replace('.html', '').replace('.css', ''); 5 | }; 6 | 7 | const getPlatform = function (platformSpecific: boolean) { 8 | return platformSpecific ? (MagicService.IS_ANDROID() ? 'android' : 'ios') : 'tns'; 9 | }; 10 | 11 | export class MagicService { 12 | 13 | public static TEMPLATE_URL(path: string, platformSpecific?: boolean): string { 14 | if (MagicService.IS_NATIVESCRIPT()) { 15 | let filepath = getFilepath(path); 16 | let platform = getPlatform(platformSpecific); 17 | return `${filepath}.${platform}.html`; 18 | } else { 19 | return path; 20 | } 21 | } 22 | 23 | public static STYLE_URLS(paths: string[], platformSpecific?: boolean): string[] { 24 | if (MagicService.IS_NATIVESCRIPT()) { 25 | return paths.map((path) => { 26 | let filepath = getFilepath(path); 27 | let platform = getPlatform(platformSpecific); 28 | return `${filepath}.${platform}.css`; 29 | }); 30 | } else { 31 | return paths; 32 | } 33 | } 34 | 35 | public static IS_NATIVESCRIPT() { 36 | return (MagicService.IS_IOS() || MagicService.IS_ANDROID()); 37 | } 38 | 39 | public static IS_IOS() { 40 | return (typeof NSObject !== 'undefined' && typeof NSString !== 'undefined'); 41 | } 42 | 43 | public static IS_ANDROID() { 44 | return (typeof android !== 'undefined' && typeof java !== 'undefined'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | import { AppModule } from './app/app.module'; 4 | 5 | // UNCOMMENT BELOW to test NativeScript Mock Env 6 | let win: any = window || {}; 7 | // mock a {N} env 8 | win.NSObject = {}; 9 | win.NSString = 'test'; 10 | 11 | // depending on the env mode, enable prod mode or add debugging modules 12 | if (process.env.ENV === 'build') { 13 | enableProdMode(); 14 | } 15 | 16 | export function main() { 17 | return platformBrowserDynamic().bootstrapModule(AppModule); 18 | } 19 | 20 | if (document.readyState === 'complete') { 21 | main(); 22 | } else { 23 | document.addEventListener('DOMContentLoaded', main); 24 | } 25 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/client/shim'; 2 | import 'reflect-metadata'; 3 | require('zone.js/dist/zone'); 4 | 5 | import 'ts-helpers'; 6 | 7 | if (process.env.ENV === 'build') { 8 | // Production 9 | 10 | } else { 11 | // Development 12 | 13 | Error['stackTraceLimit'] = Infinity; 14 | 15 | require('zone.js/dist/long-stack-trace-zone'); 16 | } 17 | -------------------------------------------------------------------------------- /src/public/img/angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwalkerrun/nativescript-ngx-magic/e5d71c5dde696eb0e51f58db7e106c3e90019caf/src/public/img/angular.png -------------------------------------------------------------------------------- /src/public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwalkerrun/nativescript-ngx-magic/e5d71c5dde696eb0e51f58db7e106c3e90019caf/src/public/img/favicon.ico -------------------------------------------------------------------------------- /src/public/img/shield.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wwwalkerrun/nativescript-ngx-magic/e5d71c5dde696eb0e51f58db7e106c3e90019caf/src/public/img/shield.png -------------------------------------------------------------------------------- /src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Library Demo 6 | 7 | 8 | 9 | 10 | 11 | Loading... 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/style/app.scss: -------------------------------------------------------------------------------- 1 | // styles in src/style directory are applied to the whole page 2 | body { 3 | background: #0147A7; 4 | color: #fff; 5 | } 6 | a { 7 | color: #03A9F4; 8 | } 9 | -------------------------------------------------------------------------------- /src/vendor.ts: -------------------------------------------------------------------------------- 1 | // Angular 2 | import '@angular/platform-browser'; 3 | import '@angular/platform-browser-dynamic'; 4 | import '@angular/core'; 5 | import '@angular/common'; 6 | import '@angular/http'; 7 | import '@angular/router'; 8 | 9 | import 'rxjs'; 10 | import '@angularclass/hmr'; 11 | 12 | // Other vendors for example jQuery, Lodash or Bootstrap 13 | // You can import js, ts, css, sass, ... 14 | -------------------------------------------------------------------------------- /tsconfig-build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "sourceMap": true, 9 | "noEmitHelpers": true 10 | }, 11 | "files": [ 12 | "nativescript-ngx-magic.ts" 13 | ], 14 | "exclude": [ 15 | "bundles", 16 | "nativescript", 17 | "node_modules", 18 | "src" 19 | ], 20 | "compileOnSave": false, 21 | "buildOnSave": false, 22 | "awesomeTypescriptLoaderOptions": { 23 | "forkChecker": true, 24 | "useWebpackText": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "commonjs", 5 | "emitDecoratorMetadata": true, 6 | "experimentalDecorators": true, 7 | "sourceMap": true, 8 | "noEmitHelpers": true 9 | }, 10 | "compileOnSave": false, 11 | "buildOnSave": false, 12 | "exclude": [ 13 | "bundles", 14 | "nativescript", 15 | "node_modules" 16 | ], 17 | "awesomeTypescriptLoaderOptions": { 18 | "forkChecker": true, 19 | "useWebpackText": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "class-name": true, 7 | "comment-format": [ 8 | true, 9 | "check-space" 10 | ], 11 | "curly": true, 12 | "eofline": true, 13 | "forin": true, 14 | "indent": [ 15 | true, 16 | "spaces" 17 | ], 18 | "label-position": true, 19 | "label-undefined": true, 20 | "max-line-length": [ 21 | true, 22 | 140 23 | ], 24 | "member-access": false, 25 | "member-ordering": [ 26 | true, 27 | "static-before-instance", 28 | "variables-before-functions" 29 | ], 30 | "no-arg": true, 31 | "no-bitwise": true, 32 | "no-console": [ 33 | true, 34 | "debug", 35 | "info", 36 | "time", 37 | "timeEnd", 38 | "trace" 39 | ], 40 | "no-construct": true, 41 | "no-debugger": true, 42 | "no-duplicate-key": true, 43 | "no-duplicate-variable": true, 44 | "no-empty": false, 45 | "no-eval": true, 46 | "no-inferrable-types": true, 47 | "no-shadowed-variable": true, 48 | "no-string-literal": false, 49 | "no-switch-case-fall-through": true, 50 | "no-trailing-whitespace": true, 51 | "no-unused-expression": true, 52 | "no-unused-variable": true, 53 | "no-unreachable": true, 54 | "no-use-before-declare": true, 55 | "no-var-keyword": true, 56 | "object-literal-sort-keys": false, 57 | "one-line": [ 58 | true, 59 | "check-open-brace", 60 | "check-catch", 61 | "check-else", 62 | "check-whitespace" 63 | ], 64 | "quotemark": [ 65 | true, 66 | "single" 67 | ], 68 | "radix": true, 69 | "semicolon": [ 70 | "always" 71 | ], 72 | "triple-equals": [ 73 | true, 74 | "allow-null-check" 75 | ], 76 | "typedef-whitespace": [ 77 | true, 78 | { 79 | "call-signature": "nospace", 80 | "index-signature": "nospace", 81 | "parameter": "nospace", 82 | "property-declaration": "nospace", 83 | "variable-declaration": "nospace" 84 | } 85 | ], 86 | "variable-name": false, 87 | "whitespace": [ 88 | true, 89 | "check-branch", 90 | "check-decl", 91 | "check-operator", 92 | "check-separator", 93 | "check-type" 94 | ], 95 | 96 | "directive-selector-name": [true, "camelCase"], 97 | "component-selector-name": [true, "kebab-case"], 98 | "directive-selector-type": [true, "attribute"], 99 | "component-selector-type": [true, "element"], 100 | "use-input-property-decorator": true, 101 | "use-output-property-decorator": true, 102 | "use-host-property-decorator": true, 103 | "no-input-rename": true, 104 | "no-output-rename": true, 105 | "use-life-cycle-interface": true, 106 | "use-pipe-transform-interface": true, 107 | "component-class-suffix": true, 108 | "directive-class-suffix": true, 109 | "directive-selector-prefix": [true, "my"], 110 | "component-selector-prefix": [true, "my"], 111 | "pipe-naming": [true, "camelCase", "my"] 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "modules", 3 | "out": "doc", 4 | "theme": "default", 5 | "ignoreCompilerErrors": "true", 6 | "experimentalDecorators": "true", 7 | "emitDecoratorMetadata": "true", 8 | "target": "ES5", 9 | "moduleResolution": "node", 10 | "preserveConstEnums": "true", 11 | "stripInternal": "true", 12 | "suppressExcessPropertyErrors": "true", 13 | "suppressImplicitAnyIndexErrors": "true", 14 | "module": "commonjs" 15 | } 16 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // Helper: root() is defined at the bottom 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | 5 | // Webpack Plugins 6 | var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; 7 | var autoprefixer = require('autoprefixer'); 8 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 9 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 10 | var CopyWebpackPlugin = require('copy-webpack-plugin'); 11 | 12 | /** 13 | * Env 14 | * Get npm lifecycle event to identify the environment 15 | */ 16 | var ENV = process.env.npm_lifecycle_event; 17 | var isTestWatch = ENV === 'test-watch'; 18 | var isTest = ENV === 'test' || isTestWatch; 19 | var isProd = ENV === 'build'; 20 | 21 | module.exports = function makeWebpackConfig() { 22 | /** 23 | * Config 24 | * Reference: http://webpack.github.io/docs/configuration.html 25 | * This is the object where all configuration gets set 26 | */ 27 | var config = {}; 28 | 29 | /** 30 | * Devtool 31 | * Reference: http://webpack.github.io/docs/configuration.html#devtool 32 | * Type of sourcemap to use per build type 33 | */ 34 | if (isProd) { 35 | config.devtool = 'source-map'; 36 | } 37 | else if (isTest) { 38 | config.devtool = 'inline-source-map'; 39 | } 40 | else { 41 | config.devtool = 'eval-source-map'; 42 | } 43 | 44 | /** 45 | * Entry 46 | * Reference: http://webpack.github.io/docs/configuration.html#entry 47 | */ 48 | config.entry = isTest ? {} : { 49 | 'polyfills': './src/polyfills.ts', 50 | 'vendor': './src/vendor.ts', 51 | 'app': './src/main.ts' // our angular app 52 | }; 53 | 54 | /** 55 | * Output 56 | * Reference: http://webpack.github.io/docs/configuration.html#output 57 | */ 58 | config.output = isTest ? {} : { 59 | path: root('dist'), 60 | publicPath: isProd ? '/' : 'http://localhost:8080/', 61 | filename: isProd ? 'js/[name].[hash].js' : 'js/[name].js', 62 | chunkFilename: isProd ? '[id].[hash].chunk.js' : '[id].chunk.js' 63 | }; 64 | 65 | /** 66 | * Resolve 67 | * Reference: http://webpack.github.io/docs/configuration.html#resolve 68 | */ 69 | config.resolve = { 70 | // only discover files that have those extensions 71 | extensions: ['.ts', '.js', '.json', '.css', '.scss', '.html'], 72 | }; 73 | 74 | var atlOptions = ''; 75 | if (isTest && !isTestWatch) { 76 | // awesome-typescript-loader needs to output inlineSourceMap for code coverage to work with source maps. 77 | atlOptions = 'inlineSourceMap=true&sourceMap=false'; 78 | } 79 | 80 | /** 81 | * Loaders 82 | * Reference: http://webpack.github.io/docs/configuration.html#module-loaders 83 | * List: http://webpack.github.io/docs/list-of-loaders.html 84 | * This handles most of the magic responsible for converting modules 85 | */ 86 | config.module = { 87 | rules: [ 88 | // Support for .ts files. 89 | { 90 | test: /\.ts$/, 91 | loaders: ['awesome-typescript-loader?' + atlOptions, 'angular2-template-loader', '@angularclass/hmr-loader'], 92 | exclude: [isTest ? /\.(e2e)\.ts$/ : /\.(spec|e2e)\.ts$/, /node_modules\/(?!(ng2-.+))/] 93 | }, 94 | 95 | // copy those assets to output 96 | { 97 | test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)(\?v=[0-9]\.[0-9]\.[0-9])?$/, 98 | loader: 'file-loader?name=fonts/[name].[hash].[ext]?' 99 | }, 100 | 101 | // Support for *.json files. 102 | {test: /\.json$/, loader: 'json-loader'}, 103 | 104 | // Support for CSS as raw text 105 | // use 'null' loader in test mode (https://github.com/webpack/null-loader) 106 | // all css in src/style will be bundled in an external css file 107 | { 108 | test: /\.css$/, 109 | exclude: root('src', 'app'), 110 | loader: isTest ? 'null-loader' : ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: ['css-loader', 'postcss-loader']}) 111 | }, 112 | // all css required in src/app files will be merged in js files 113 | {test: /\.css$/, include: root('src', 'app'), loader: 'raw-loader!postcss-loader'}, 114 | 115 | // support for .scss files 116 | // use 'null' loader in test mode (https://github.com/webpack/null-loader) 117 | // all css in src/style will be bundled in an external css file 118 | { 119 | test: /\.(scss|sass)$/, 120 | exclude: root('src', 'app'), 121 | loader: isTest ? 'null-loader' : ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: ['css-loader', 'postcss-loader', 'sass-loader']}) 122 | }, 123 | // all css required in src/app files will be merged in js files 124 | {test: /\.(scss|sass)$/, exclude: root('src', 'style'), loader: 'raw-loader!postcss-loader!sass-loader'}, 125 | 126 | // support for .html as raw text 127 | // todo: change the loader to something that adds a hash to images 128 | {test: /\.html$/, loader: 'raw-loader', exclude: root('src', 'public')} 129 | ] 130 | }; 131 | 132 | if (isTest && !isTestWatch) { 133 | // instrument only testing sources with Istanbul, covers ts files 134 | config.module.rules.push({ 135 | test: /\.ts$/, 136 | enforce: 'post', 137 | include: path.resolve('src'), 138 | loader: 'istanbul-instrumenter-loader', 139 | exclude: [/\.spec\.ts$/, /\.e2e\.ts$/, /node_modules/] 140 | }); 141 | } 142 | 143 | if (!isTest || !isTestWatch) { 144 | // tslint support 145 | config.module.rules.push({ 146 | test: /\.ts$/, 147 | enforce: 'pre', 148 | loader: 'tslint-loader' 149 | }); 150 | } 151 | 152 | /** 153 | * Plugins 154 | * Reference: http://webpack.github.io/docs/configuration.html#plugins 155 | * List: http://webpack.github.io/docs/list-of-plugins.html 156 | */ 157 | config.plugins = [ 158 | // Define env variables to help with builds 159 | // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin 160 | new webpack.DefinePlugin({ 161 | // Environment helpers 162 | 'process.env': { 163 | ENV: JSON.stringify(ENV) 164 | } 165 | }), 166 | 167 | // Workaround needed for angular 2 angular/angular#11580 168 | new webpack.ContextReplacementPlugin( 169 | // The (\\|\/) piece accounts for path separators in *nix and Windows 170 | /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, 171 | root('./src') // location of your src 172 | ), 173 | 174 | // Tslint configuration for webpack 2 175 | new webpack.LoaderOptionsPlugin({ 176 | options: { 177 | /** 178 | * Apply the tslint loader as pre/postLoader 179 | * Reference: https://github.com/wbuchwalter/tslint-loader 180 | */ 181 | tslint: { 182 | emitErrors: false, 183 | failOnHint: false 184 | }, 185 | /** 186 | * Sass 187 | * Reference: https://github.com/jtangelder/sass-loader 188 | * Transforms .scss files to .css 189 | */ 190 | sassLoader: { 191 | //includePaths: [path.resolve(__dirname, "node_modules/foundation-sites/scss")] 192 | }, 193 | /** 194 | * PostCSS 195 | * Reference: https://github.com/postcss/autoprefixer-core 196 | * Add vendor prefixes to your css 197 | */ 198 | postcss: [ 199 | autoprefixer({ 200 | browsers: ['last 2 version'] 201 | }) 202 | ] 203 | } 204 | }) 205 | ]; 206 | 207 | if (!isTest && !isTestWatch) { 208 | config.plugins.push( 209 | // Generate common chunks if necessary 210 | // Reference: https://webpack.github.io/docs/code-splitting.html 211 | // Reference: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin 212 | new CommonsChunkPlugin({ 213 | name: ['vendor', 'polyfills'] 214 | }), 215 | 216 | // Inject script and link tags into html files 217 | // Reference: https://github.com/ampedandwired/html-webpack-plugin 218 | new HtmlWebpackPlugin({ 219 | template: './src/public/index.html', 220 | chunksSortMode: 'dependency' 221 | }), 222 | 223 | // Extract css files 224 | // Reference: https://github.com/webpack/extract-text-webpack-plugin 225 | // Disabled when in test mode or not in build mode 226 | new ExtractTextPlugin({filename: 'css/[name].[hash].css', disable: !isProd}) 227 | ); 228 | } 229 | 230 | // Add build specific plugins 231 | if (isProd) { 232 | config.plugins.push( 233 | // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin 234 | // Only emit files when there are no errors 235 | new webpack.NoErrorsPlugin(), 236 | 237 | // // Reference: http://webpack.github.io/docs/list-of-plugins.html#dedupeplugin 238 | // // Dedupe modules in the output 239 | // new webpack.optimize.DedupePlugin(), 240 | 241 | // Reference: http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin 242 | // Minify all javascript, switch loaders to minimizing mode 243 | new webpack.optimize.UglifyJsPlugin({sourceMap: true, mangle: { keep_fnames: true }}), 244 | 245 | // Copy assets from the public folder 246 | // Reference: https://github.com/kevlened/copy-webpack-plugin 247 | new CopyWebpackPlugin([{ 248 | from: root('src/public') 249 | }]) 250 | ); 251 | } 252 | 253 | /** 254 | * Dev server configuration 255 | * Reference: http://webpack.github.io/docs/configuration.html#devserver 256 | * Reference: http://webpack.github.io/docs/webpack-dev-server.html 257 | */ 258 | config.devServer = { 259 | contentBase: './src/public', 260 | historyApiFallback: true, 261 | quiet: true, 262 | stats: 'minimal' // none (or false), errors-only, minimal, normal (or true) and verbose 263 | }; 264 | 265 | return config; 266 | }(); 267 | 268 | // Helper functions 269 | function root(args) { 270 | args = Array.prototype.slice.call(arguments, 0); 271 | return path.join.apply(path, [__dirname].concat(args)); 272 | } 273 | --------------------------------------------------------------------------------