diff --git a/addon/ng2/blueprints/mobile/files/__path__/main-app-shell.ts b/addon/ng2/blueprints/mobile/files/__path__/main-app-shell.ts index d3e009cb19c5..af95d2f90172 100644 --- a/addon/ng2/blueprints/mobile/files/__path__/main-app-shell.ts +++ b/addon/ng2/blueprints/mobile/files/__path__/main-app-shell.ts @@ -2,12 +2,12 @@ import 'angular2-universal-polyfills'; import { provide } from '@angular/core'; import { APP_BASE_HREF } from '@angular/common'; import { APP_SHELL_BUILD_PROVIDERS } from '@angular/app-shell'; -import { - REQUEST_URL, - ORIGIN_URL, - Bootloader, - BootloaderConfig, - AppConfig +import { + REQUEST_URL, + ORIGIN_URL, + Bootloader, + BootloaderConfig, + AppConfig } from 'angular2-universal'; import { AppComponent } from './app/'; @@ -39,9 +39,9 @@ export function getBootloader() : Bootloader { return new Bootloader(bootloaderConfig); } -// The build system will call this function with the bootloader from +// The build system will call this function with the bootloader from // getBootloader and the contents of the index page export function serialize(bootloader: Bootloader, template: string) : string { appConfig.template = template; return bootloader.serializeApplication(appConfig); -} \ No newline at end of file +} diff --git a/addon/ng2/blueprints/ng2/files/__path__/vendor.ts b/addon/ng2/blueprints/ng2/files/__path__/vendor.ts new file mode 100644 index 000000000000..a74ebfcb3957 --- /dev/null +++ b/addon/ng2/blueprints/ng2/files/__path__/vendor.ts @@ -0,0 +1,17 @@ +// Typescript emit helpers polyfill +import 'ts-helpers'; + +import '@angular/core'; +import '@angular/common'; +import '@angular/compiler'; +import '@angular/http'; +import '@angular/router'; +import '@angular/platform-browser'; +import '@angular/platform-browser-dynamic'; + +<% if(isMobile) { %> + import '@angular/app-shell'; +<% } %> + +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/mergeMap'; diff --git a/addon/ng2/commands/build.ts b/addon/ng2/commands/build.ts index 935102fa8484..c4d553f2fd8c 100644 --- a/addon/ng2/commands/build.ts +++ b/addon/ng2/commands/build.ts @@ -32,7 +32,7 @@ module.exports = Command.extend({ } if (commandOptions.target === 'production') { commandOptions.environment = 'prod'; - } + } } var project = this.project; diff --git a/addon/ng2/commands/init.js b/addon/ng2/commands/init.js index 7c7fff8aa460..3244bf94e2c0 100644 --- a/addon/ng2/commands/init.js +++ b/addon/ng2/commands/init.js @@ -9,6 +9,7 @@ var GitInit = require('../tasks/git-init'); var LinkCli = require('../tasks/link-cli'); var NpmInstall = require('../tasks/npm-install'); + module.exports = Command.extend({ name: 'init', description: 'Creates a new angular-cli project in the current folder.', @@ -94,7 +95,7 @@ module.exports = Command.extend({ return Promise.reject(new SilentError(message)); } - + var blueprintOpts = { dryRun: commandOptions.dryRun, blueprint: commandOptions.blueprint || this._defaultBlueprint(), diff --git a/addon/ng2/commands/test.js b/addon/ng2/commands/test.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/addon/ng2/models/builder.ts b/addon/ng2/models/builder.ts new file mode 100644 index 000000000000..62b16304c71d --- /dev/null +++ b/addon/ng2/models/builder.ts @@ -0,0 +1,161 @@ +const fs = require('fs-extra'); +const existsSync = require('exists-sync'); +const path = require('path'); +const Promise = require('ember-cli/lib/ext/promise'); +const Task = require('ember-cli/lib/models/task'); +const SilentError = require('silent-error'); +const chalk = require('chalk'); +const attemptNeverIndex = require('ember-cli/lib/utilities/attempt-never-index'); +const findBuildFile = require('ember-cli/lib/utilities/find-build-file'); +const viz = require('broccoli-viz'); +const FSMonitor = require('fs-monitor-stack'); +const Sync = require('tree-sync'); +const mkdirp = require('mkdirp'); + +let resolve = null; +let promise = new Promise((r) => resolve = r); + + + + +var signalsTrapped = false; +var buildCount = 0; + +function outputViz(count, result, monitor) { + var processed = viz.process(result.graph); + + processed.forEach(function(node) { + node.stats.fs = monitor.statsFor(node); + }); + + fs.writeFileSync('graph.' + count + '.dot', viz.dot(processed)); + fs.writeFileSync('graph.' + count + '.json', JSON.stringify({ + summary: { + buildCount: count, + output: result.directory, + totalTime: result.totalTime, + totalNodes: processed.length, + stats: { + fs: monitor.totalStats() + } + }, + nodes: processed + })); +} + +module.exports = Task.extend({ + setupBuilder: function() { + this.environment = this.environment || 'development'; + process.env.ANGULAR_ENV = process.env.ANGULAR_ENV || process.env.EMBER_CLI || this.environment; + process.env.EMBER_ENV = process.env.ANGULAR_ENV; + + var buildFile = findBuildFile('angular-cli-build.js'); + this.tree = buildFile({ project: this.project }); + + if (webpack) { + console.log('webpack'); + } else { + var broccoli = require('ember-cli-broccoli'); + this.builder = new broccoli.Builder(this.tree); + } + }, + + trapSignals: function() { + if (!signalsTrapped) { + process.on('SIGINT', this.onSIGINT.bind(this)); + process.on('SIGTERM', this.onSIGTERM.bind(this)); + process.on('message', this.onMessage.bind(this)); + signalsTrapped = true; + } + }, + + init: function() { + this.setupBuilder(); + this.trapSignals(); + }, + + /** + Determine whether the output path is safe to delete. If the outputPath + appears anywhere in the parents of the project root, the build would + delete the project directory. In this case return `false`, otherwise + return `true`. + */ + canDeleteOutputPath: function(outputPath) { + var rootPathParents = [this.project.root]; + var dir = path.dirname(this.project.root); + rootPathParents.push(dir); + while (dir !== path.dirname(dir)) { + dir = path.dirname(dir); + rootPathParents.push(dir); + } + return rootPathParents.indexOf(outputPath) === -1; + }, + + copyToOutputPath: function(inputPath) { + var outputPath = this.outputPath; + + mkdirp.sync(outputPath); + + if (!this.canDeleteOutputPath(outputPath)) { + throw new SilentError('Using a build destination path of `' + outputPath + '` is not supported.'); + } + + var sync = this._sync; + if (sync === undefined) { + this._sync = sync = new Sync(inputPath, path.resolve(this.outputPath)); + } + + sync.sync(); + }, + + build: function(...args: any[]) { + attemptNeverIndex('tmp'); + return promise; + // return Promise.resolve(); + // if (this.webpack) { + // console.log(1, process.cwd()); + // return new Promise((resolve, reject) => { + // this.webpack.run((err, stats) => { + // console.log(!!err, stats); + // if (err) { + // reject(err); + // } + // resolve(); + // }); + // }); + // } + // return this.builder.build(...args); + }, + + cleanup: function() { + var ui = this.ui; + + // if (this.webpack) { + // this.webpack.cleanupAndExit(); + return Promise.resolve(); + // } else { + // return this.builder.cleanup().catch(function (err) { + // ui.writeLine(chalk.red('Cleanup error.')); + // ui.writeError(err); + // }); + // } + }, + + cleanupAndExit: function() { + this.cleanup().finally(function() { + process.exit(1); + }); + }, + + onSIGINT: function() { + this.cleanupAndExit(); + }, + onSIGTERM: function() { + this.cleanupAndExit(); + }, + onMessage: function(message) { + if (message.kill) { + this.cleanupAndExit(); + } + } +}); diff --git a/addon/ng2/models/save-for-later.ts b/addon/ng2/models/save-for-later.ts new file mode 100644 index 000000000000..643ff92cfaf9 --- /dev/null +++ b/addon/ng2/models/save-for-later.ts @@ -0,0 +1,272 @@ + +// new webpack.LoaderOptionsPlugin({ +// minimize: true +// }), +// new webpack.LoaderOptionsPlugin({ +// test: /\.js$/, +// jsfile: true +// }) +// new ClosureCompilerPlugin({ +// compiler: { +// language_in: 'ECMASCRIPT5', +// language_out: 'ECMASCRIPT5', +// compilation_level: 'SIMPLE' +// }, +// concurrency: 3, +// }) + + + + +// ts: { +// configFileName: ngAppResolve('./src/tsconfig.json'), +// silent: true +// }, +// output: { +// path: './dist/', +// filename: '[name].[chunkhash].js', +// sourceMapFilename: '[name].[chunkhash].map', +// chunkFilename: '[chunkhash].js' +// }, +// recordsPath: path.join(__dirname, "records.json"), +// +// +// +// +// new webpack.optimize.CommonsChunkPlugin({ +// names: ['main', 'vendors', 'polyfills'] +// }), +// new webpack.optimize.CommonsChunkPlugin({ +// minChunks: Infinity, +// name: 'inline', +// filename: 'inline.js', +// sourceMapFilename: 'inline.map' +// }), +// new HtmlWebpackPlugin({ +// template:'./src/index.html', +// chunksSortMode: "dependency" +// }) +// +// +// export const materialEntryConfig: {[key: string]: any} = { +// demoMain: [ngAppResolve('./src/demo-app/main.ts')], +// e2eMain: [ngAppResolve('./src/e2e-app/main.ts')], +// core: [ngAppResolve('./src/core/core.ts')], +// vendor: [ +// "@angular/common", +// "@angular/compiler", +// "@angular/core", +// "@angular/http", +// "@angular/platform-browser", +// "@angular/platform-browser-dynamic", +// "@angular/router", +// ], +// polyfills: [ +// "core-js", +// "hammerjs", +// "rxjs", +// "systemjs", +// "zone.js" +// ] +// } + +// export const materialPluginsConfig: any[] = [ +// new webpack.optimize.CommonsChunkPlugin({ +// name: ['polyfills', 'vendor'].reverse() +// }), +// new HtmlWebpackPlugin({ +// template: ngAppResolve('./demo-app/index.html'), +// chunksSortMode: 'dependency' +// }) +// ]; +// +// +// +// +// +// const webpack = require('webpack'); +// const HtmlWebpackPlugin = require('html-webpack-plugin'); +// const CopyWebpackPlugin = require('copy-webpack-plugin'); +// const path = require('path'); +// const ClosureCompilerPlugin = require('webpack-closure-compiler'); +// const autoprefixer = require('autoprefixer'); +// const cssnano = require('cssnano'); +// const ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin; +// // Resolve to the generated applications + + + + +// let baseHtmlTemplateConfig = { +// template: ngAppResolve('./src/index.html'), +// chunksSortMode: 'dependency' +// }; +// // These are the output +// const webpackTestPartial = { +// module: { +// plugins: [ +// new ForkCheckerPlugin(), +// new HtmlWebpackPlugin(baseHtmlTemplateConfig), +// ], +// preLoaders: [ +// { +// test: /\.ts$/, +// loader: 'tslint-loader', +// exclude: ['node_modules'] +// }, +// { +// test: /\.js$/, +// loader: 'source-map-loader', +// exclude: [ +// // these packages have problems with their sourcemaps +// ngAppResolve('node_modules/rxjs'), +// ngAppResolve('node_modules/@angular') +// ]} +// ], +// loaders: [ +// { +// test: /\.ts$/, +// loaders: [ +// { +// loader: 'awesome-typescript-loader', +// query: { +// useWebpackText: true, +// tsconfig: ngAppResolve('./src/tsconfig.json'), +// resolveGlobs: false, +// module: "es2015", +// target: "es5", +// library: 'es6', +// useForkChecker: true, +// removeComments: true +// } +// }, +// { +// loader: 'angular2-template-loader' +// } +// ], +// exclude: [/\.(spec|e2e)\.ts$/] +// }, +// { test: /\.json$/, loader: 'json-loader', exclude: [ngAppResolve('src/index.html')] }, +// { test: /\.css$/, loader: 'raw-loader', exclude: [ngAppResolve('src/index.html')] }, +// { test: /\.html$/, loader: 'raw-loader', exclude: [ngAppResolve('src/index.html')] } +// ] +// }, +// tslint: { +// emitErrors: false, +// failOnHint: false, +// resourcePath: 'src' +// }, +// node: { +// global: 'window', +// process: false, +// crypto: 'empty', +// module: false, +// clearImmediate: false, +// setImmediate: false +// } +// }; + +// Webpack Configuration Object +// Used in build.ts + +// var webpack = require('webpack'); +// var path = require('path'); + + +// // Webpack Config +// var webpackConfig = { +// devtool: "#source-map", +// entry: { +// 'angular2polyfills': [ +// // 'ie-shim', +// 'core-js/es6/symbol', +// 'core-js/es6/object', +// 'core-js/es6/function', +// 'core-js/es6/parse-int', +// 'core-js/es6/parse-float', +// 'core-js/es6/number', +// 'core-js/es6/math', +// 'core-js/es6/string', +// 'core-js/es6/date', +// 'core-js/es6/array', +// 'core-js/es6/regexp', +// 'core-js/es6/map', +// 'core-js/es6/set', +// 'core-js/es6/weak-map', +// 'core-js/es6/weak-set', +// 'core-js/es6/typed', +// 'core-js/es6/reflect', +// // 'core-js/es6/promise', // problem with firefox +// 'core-js/es7/reflect', +// 'zone.js/dist/zone', +// 'zone.js/dist/long-stack-trace-zone', +// ], +// 'angular2vendor': [ +// '@angular/platform-browser', +// '@angular/platform-browser-dynamic', +// '@angular/core', +// '@angular/common', +// '@angular/forms', +// '@angular/http', +// '@angular/router', +// ] +// }, + +// output: { +// path: './dist', +// filename: 'dll.[name].[hash].bundle.js', +// sourceMapFilename: '[name].map', +// chunkFilename: '[id].chunk.js', +// library: '__DLL_[name]', +// }, + + +// plugins: [ +// new webpack.DllPlugin({ +// name: '[vendor]', +// path: 'dist/vendor-manifest.json', +// }), +// new webpack.DllPlugin({ +// name: 'polyfills', +// path: 'dist/polyfills-manifest.json', +// }), +// ], + +// module: { +// preLoaders: [ +// { +// test: /\.js$/, +// loader: 'source-map-loader', +// exclude: [ +// // these packages have problems with their sourcemaps +// path.resolve(__dirname, 'node_modules', 'rxjs'), +// path.resolve(__dirname, 'node_modules', '@angular'), +// path.resolve(__dirname, 'node_modules', '@ngrx'), +// path.resolve(__dirname, 'node_modules', '@angular2-material'), +// ] +// } + +// ], +// loaders: [ +// ] +// }, + +// node: { +// global: 'window', +// crypto: 'empty', +// module: false, +// clearImmediate: false, +// setImmediate: false +// } + +// }; + + + + + + + + + + diff --git a/addon/ng2/models/webpack-build-material2.ts b/addon/ng2/models/webpack-build-material2.ts new file mode 100644 index 000000000000..e7629a217bd9 --- /dev/null +++ b/addon/ng2/models/webpack-build-material2.ts @@ -0,0 +1,224 @@ +// Angular Material2 Custom CLI Webpack Plugin: This allows for the following: +// To build, serve, and watchmode the angular2-material repo. +// +// Requirements: +// +// Do a find and replace on the src directory +// .css'] => .scss'] +// This allows for angular2-template-loader to transpile the sass correctly. +import * as webpack from 'webpack'; +import {LoaderConfig} from '../utilities/ts-path-mappings-webpack-plugin'; + +const path = require('path'); +const ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin; +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + + +// var components = [ +// 'button', +// 'card', +// 'checkbox', +// 'grid-list', +// 'icon', +// 'input', +// 'list', +// 'progress-bar', +// 'progress-circle', +// 'radio', +// 'sidenav', +// 'slide-toggle', +// 'button-toggle', +// 'tabs', +// 'toolbar' +// ]; + +export const getWebpackMaterialConfig = function(projectRoot: string) { + const awesomeTypescriptLoaderConfig: LoaderConfig | any = { + useWebpackText: true, + useForkChecker: true, + tsconfig: path.resolve(projectRoot, './src/demo-app/tsconfig.json') + } + /** Map relative paths to URLs. */ + // var aliasMap: any = { + // '@angular2-material/core': path.resolve(projectRoot, './src/core'), + // }; + + // components.forEach(function (name) { + // aliasMap[("@angular2-material/" + name)] = path.resolve(projectRoot, "./src/components/" + name); + // return aliasMap[("@angular2-material/" + name)] = path.resolve(projectRoot, "./src/components/" + name); + // }); + + return { + devtool: 'inline-source-map', + resolve: { + extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js', '.css', '.scss'], + plugins: [ + ] + // alias: aliasMap + }, + sassLoader: { + includePaths: [ + // This allows for automatic resolving of @import's for sass for variables. + path.resolve(projectRoot, './src/core/style') + ] + }, + context: path.resolve(__dirname, './'), + entry: { + main: [path.resolve(projectRoot, './src/demo-app/main.ts')], + vendor: path.resolve(projectRoot, './src/demo-app/vendor.ts') + }, + output: { + path: './dist', + filename: '[name].bundle.js' + }, + module: { + preLoaders: [ + { + test: /\.js$/, + loader: 'source-map-loader', + exclude: [ + path.resolve(projectRoot, 'node_modules/rxjs'), + path.resolve(projectRoot, 'node_modules/@angular'), + ] + } + ], + // ts: { + // configFileName: path.resolve(projectRoot, './src/demo-app/tsconfig.json') + // }, + loaders: [ + { + test: /\.ts$/, + loaders: [ + { + loader: 'awesome-typescript-loader', + query: awesomeTypescriptLoaderConfig + }, + { + loader: 'angular2-template-loader' + } + ], + exclude: [/\.(spec|e2e)\.ts$/] + }, + { test: /\.json$/, loader: 'json-loader'}, + { test: /\.css$/, loaders: ['raw-loader', 'postcss-loader'] }, + { test: /\.styl$/, loaders: ['raw-loader', 'postcss-loader', 'stylus-loader'] }, + { test: /\.less$/, loaders: ['raw-loader', 'less-loader'] }, + { test: /\.s?css$/, loaders: ['raw-loader', 'postcss-loader', 'sass-loader'] }, + { test: /\.(jpg|png)$/, loader: 'url-loader?limit=128000'}, + { test: /\.html$/, loader: 'raw-loader' } + ] + }, + plugins: [ + new webpack.optimize.CommonsChunkPlugin({name: 'vendor'}), + new ForkCheckerPlugin(), + new HtmlWebpackPlugin({ + template: path.resolve(projectRoot, './src/demo-app/index.html'), + chunksSortMode: 'dependency' + }), + ], + node: { + global: 'window', + crypto: 'empty', + module: false, + clearImmediate: false, + setImmediate: false + } + }; +} + +export const getWebpackMaterialE2EConfig = function(projectRoot: string) { + const awesomeTypescriptLoaderConfig: LoaderConfig | any = { + useWebpackText: true, + useForkChecker: true, + tsconfig: path.resolve(projectRoot, './src/e2e-app/tsconfig.json') + } + /** Map relative paths to URLs. */ + // var aliasMap: any = { + // '@angular2-material/core': path.resolve(projectRoot, './src/core'), + // }; + + // components.forEach(function (name) { + // aliasMap[("@angular2-material/" + name)] = path.resolve(projectRoot, "./src/components/" + name); + // return aliasMap[("@angular2-material/" + name)] = path.resolve(projectRoot, "./src/components/" + name); + // }); + + return { + devtool: 'inline-source-map', + resolve: { + extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js', '.css', '.scss'], + + plugins: [ + ] + // alias: aliasMap + }, + sassLoader: { + includePaths: [ + // This allows for automatic resolving of @import's for sass for variables. + path.resolve(projectRoot, './src/core/style') + ] + }, + debug: true, + context: path.resolve(__dirname, './'), + entry: { + main: [path.resolve(projectRoot, './src/e2e-app/main.ts')], + vendor: path.resolve(projectRoot, './src/e2e-app/vendor.ts') + }, + output: { + path: './dist', + filename: '[name].bundle.js' + }, + module: { + preLoaders: [ + { + test: /\.js$/, + loader: 'source-map-loader', + exclude: [ + path.resolve(projectRoot, 'node_modules/rxjs'), + path.resolve(projectRoot, 'node_modules/@angular'), + ] + } + ], + // ts: { + // configFileName: path.resolve(projectRoot, './src/e2e-app/tsconfig.json') + // }, + loaders: [ + { + test: /\.ts$/, + loaders: [ + { + loader: 'awesome-typescript-loader', + query: awesomeTypescriptLoaderConfig + }, + { + loader: 'angular2-template-loader' + } + ], + exclude: [/\.(spec|e2e)\.ts$/] + }, + { test: /\.json$/, loader: 'json-loader'}, + { test: /\.css$/, loaders: ['raw-loader', 'postcss-loader'] }, + { test: /\.styl$/, loaders: ['raw-loader', 'postcss-loader', 'stylus-loader'] }, + { test: /\.less$/, loaders: ['raw-loader', 'less-loader'] }, + { test: /\.s?css$/, loaders: ['raw-loader', 'postcss-loader', 'sass-loader'] }, + { test: /\.(jpg|png)$/, loader: 'url-loader?limit=128000'}, + { test: /\.html$/, loader: 'raw-loader' } + ] + }, + plugins: [ + new webpack.optimize.CommonsChunkPlugin({name: 'vendor'}), + new ForkCheckerPlugin(), + new HtmlWebpackPlugin({ + template: path.resolve(projectRoot, './src/e2e-app/index.html'), + chunksSortMode: 'dependency' + }), + ], + node: { + global: 'window', + crypto: 'empty', + module: false, + clearImmediate: false, + setImmediate: false + } + }; +} diff --git a/addon/ng2/models/webpack-build-test.ts b/addon/ng2/models/webpack-build-test.ts new file mode 100644 index 000000000000..6a1d15bf4410 --- /dev/null +++ b/addon/ng2/models/webpack-build-test.ts @@ -0,0 +1,92 @@ +import * as webpack from 'webpack'; + +const path = require('path'); + +export const getWebpackTestConfig = function(projectRoot: string) { + return { + devtool: 'inline-source-map', + context: path.resolve(__dirname, './'), + resolve: { + extensions: ['', '.ts', '.js'], + root: path.resolve(projectRoot, './src') + }, + entry: { + test: path.resolve(projectRoot, './src/test.ts') + }, + output: { + path: './dist.test', + filename: '[name].bundle.js' + }, + module: { + preLoaders: [ + { + test: /\.ts$/, + loader: 'tslint-loader', + exclude: [ + path.resolve(projectRoot, 'node_modules') + ] + }, + { + test: /\.js$/, + loader: 'source-map-loader', + exclude: [ + path.resolve(projectRoot, 'node_modules/rxjs'), + path.resolve(projectRoot, 'node_modules/@angular') + ] + } + ], + loaders: [ + { + test: /\.ts$/, + loaders: [ + { + loader: 'awesome-typescript-loader', + query: { + useWebpackText: true, + tsconfig: path.resolve(projectRoot, './src/tsconfig.json'), + // resolveGlobs: false, + module: "commonjs", + target: "es5", + useForkChecker: true, + removeComments: true + } + }, + { + loader: 'angular2-template-loader' + } + ], + exclude: [/\.e2e\.ts$/] + }, + { test: /\.json$/, loader: 'json-loader'}, + { test: /\.css$/, loaders: ['raw-loader', 'postcss-loader'] }, + { test: /\.styl$/, loaders: ['raw-loader', 'postcss-loader', 'stylus-loader'] }, + { test: /\.less$/, loaders: ['raw-loader', 'postcss-loader', 'less-loader'] }, + { test: /\.scss$/, loaders: ['raw-loader', 'postcss-loader', 'sass-loader'] }, + { test: /\.(jpg|png)$/, loader: 'url-loader?limit=128000'}, + { test: /\.html$/, loader: 'raw-loader', exclude: [path.resolve(projectRoot, 'src/index.html')] } + ], + postLoaders: [ + { + test: /\.(js|ts)$/, loader: 'istanbul-instrumenter-loader', + exclude: [ + /\.(e2e|spec)\.ts$/, + /node_modules/ + ] + } + ] + }, + tslint: { + emitErrors: false, + failOnHint: false, + resourcePath: 'src' + }, + node: { + global: 'window', + process: false, + crypto: 'empty', + module: false, + clearImmediate: false, + setImmediate: false + } + }; +} diff --git a/addon/ng2/models/webpack-karma-config.ts b/addon/ng2/models/webpack-karma-config.ts new file mode 100644 index 000000000000..1640043fd31f --- /dev/null +++ b/addon/ng2/models/webpack-karma-config.ts @@ -0,0 +1,30 @@ +export default function(config) { + var testWebpackConfig = require('./webpack.test.js'); + config.set({ + basePath: '', + frameworks: ['jasmine'], + exclude: [ ], + files: [ { pattern: './config/spec-bundle.js', watched: false } ], + coverageReporter: { + dir : 'coverage/', + reporters: [ + { type: 'text-summary' }, + { type: 'json' }, + { type: 'html' } + ] + }, + preprocessors: { './config/spec-bundle.js': ['coverage', 'webpack', 'sourcemap'] }, + webpack: testWebpackConfig, + webpackServer: { noInfo: true }, + reporters: ['progress', 'mocha', 'coverage' ], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: false, + browsers: [ + // 'Chrome', + 'PhantomJS' + ], + singleRun: true + }); +}; diff --git a/addon/ng2/tasks/build-watch.ts b/addon/ng2/tasks/build-watch.ts new file mode 100644 index 000000000000..d816d47a3fe2 --- /dev/null +++ b/addon/ng2/tasks/build-watch.ts @@ -0,0 +1,30 @@ +const chalk = require('chalk'); +const Task = require('ember-cli/lib/models/task'); +const Watcher = require('ember-cli/lib/models/watcher'); +const Promise = require('ember-cli/lib/ext/promise'); + +const Builder = require('../models/builder'); + + +module.exports = Task.extend({ + run: function(options) { + console.log(2); + this.ui.startProgress( + chalk.green('Building'), chalk.green('.') + ); + + return new Watcher({ + ui: this.ui, + builder: new Builder({ + ui: this.ui, + outputPath: options.outputPath, + environment: options.environment, + project: this.project + }), + analytics: this.analytics, + options: options + }).then(function() { + return new Promise(function () {}); // Run until failure or signal to exit + }); + } +}); diff --git a/addon/ng2/tasks/build.ts b/addon/ng2/tasks/build.ts new file mode 100644 index 000000000000..8af2961d35b3 --- /dev/null +++ b/addon/ng2/tasks/build.ts @@ -0,0 +1,55 @@ +const chalk = require('chalk'); +const Task = require('ember-cli/lib/models/task'); + +const Builder = require('../models/builder'); + + +module.exports = Task.extend({ + // Options: String outputPath + run: function(options) { + var ui = this.ui; + var analytics = this.analytics; + + ui.startProgress(chalk.green('Building'), chalk.green('.')); + + var builder = new Builder({ + ui: ui, + outputPath: options.outputPath, + environment: options.environment, + project: this.project + }); + + return builder.build() + .then(function(results) { + if (!results) { + return; + } + var totalTime = results.totalTime / 1e6; + + analytics.track({ + name: 'ember build', + message: totalTime + 'ms' + }); + + analytics.trackTiming({ + category: 'rebuild', + variable: 'build time', + label: 'broccoli build time', + value: parseInt(totalTime, 10) + }); + }) + .finally(function() { + ui.stopProgress(); + return builder.cleanup(); + }) + .then(function() { + ui.writeLine(chalk.green('Built project successfully. Stored in "' + + options.outputPath + '".')); + }) + .catch(function(err) { + ui.writeLine(chalk.red('Build failed.')); + + throw err; + }); + } +}); diff --git a/addon/ng2/tasks/serve.ts b/addon/ng2/tasks/serve.ts new file mode 100644 index 000000000000..b283942396fc --- /dev/null +++ b/addon/ng2/tasks/serve.ts @@ -0,0 +1,64 @@ +'use strict'; + +var existsSync = require('exists-sync'); +var path = require('path'); +var LiveReloadServer = require('ember-cli/lib/tasks/server/livereload-server'); +var ExpressServer = require('ember-cli/lib/tasks/server/express-server'); +var Promise = require('ember-cli/lib/ext/promise'); +var Task = require('ember-cli/lib/models/task'); +var Watcher = require('ember-cli/lib/models/watcher'); +var Builder = require('../models/builder'); +var ServerWatcher = require('ember-cli/lib/models/server-watcher'); + +module.exports = Task.extend({ + run: function(options) { + var builder = new Builder({ + ui: this.ui, + outputPath: options.outputPath, + project: this.project, + environment: options.environment + }); + + var watcher = new Watcher({ + ui: this.ui, + builder: builder, + analytics: this.analytics, + options: options + }); + + var serverRoot = './server'; + var serverWatcher = null; + if (existsSync(serverRoot)) { + serverWatcher = new ServerWatcher({ + ui: this.ui, + analytics: this.analytics, + watchedDir: path.resolve(serverRoot) + }); + } + + var expressServer = new ExpressServer({ + ui: this.ui, + project: this.project, + watcher: watcher, + serverRoot: serverRoot, + serverWatcher: serverWatcher + }); + + var liveReloadServer = new LiveReloadServer({ + ui: this.ui, + analytics: this.analytics, + project: this.project, + watcher: watcher, + expressServer: expressServer + }); + + return Promise.all([ + liveReloadServer.start(options), + expressServer.start(options) + ]).then(function() { + return new Promise(function() { + // hang until the user exits. + }); + }); + } +}); diff --git a/addon/ng2/tasks/test.js b/addon/ng2/tasks/test.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/addon/ng2/utilities/sw-install.js b/addon/ng2/utilities/sw-install.js index eadc268397fc..2f5cc59818da 100644 --- a/addon/ng2/utilities/sw-install.js +++ b/addon/ng2/utilities/sw-install.js @@ -1,3 +1,3 @@ -// this file is used in ../models/webpack-build-mobile.ts to add +// this file is used in ../models/webpack-build-mobile.ts to add // service worker functionality to mobile projects -require('offline-plugin/runtime').install(); \ No newline at end of file +require('offline-plugin/runtime').install(); diff --git a/addon/ng2/utilities/ts-path-mappings-webpack-plugin.ts b/addon/ng2/utilities/ts-path-mappings-webpack-plugin.ts new file mode 100644 index 000000000000..13bdeb057703 --- /dev/null +++ b/addon/ng2/utilities/ts-path-mappings-webpack-plugin.ts @@ -0,0 +1,255 @@ +import * as path from 'path'; +import * as ts from 'typescript'; +import * as _ from 'lodash'; + +const ModulesInRootPlugin: new (a: string, b: string, c: string) => ResolverPlugin = require('enhanced-resolve/lib/ModulesInRootPlugin'); + +const createInnerCallback: CreateInnerCallback = require('enhanced-resolve/lib/createInnerCallback'); +const getInnerRequest: getInnerRequest = require('enhanced-resolve/lib/getInnerRequest'); + +export type ResolverCallback = (request: Request, callback: Callback) => void; +export type QueryOptions = LoaderConfig & ts.CompilerOptions; +export type TsConfig = ts.ParsedCommandLine; + +export type CreateInnerCallback = (callback: Callback, options: Callback, message?: string, messageOptional?: string) => Callback; +type getInnerRequest = (resolver: Resolver, request: Request) => string; + +export interface TSCompilerInfo { + compilerPath: string; + tsImpl: typeof ts; +} + +export interface Request { + request?: Request; + relativePath: string; +} + +export interface Callback { + (err?: Error, result?: any): void; + + log?: any; + stack?: any; + missing?: any; +} + +export interface Configs { + configFilePath: string; + compilerConfig: TsConfig; + loaderConfig: LoaderConfig; +} + +export interface LoaderConfig { + instanceName?: string; + showRecompileReason?: boolean; + compiler?: string; + emitRequireType?: boolean; + reEmitDependentFiles?: boolean; + tsconfig?: string; + useWebpackText?: boolean; + externals?: string[]; + doTypeCheck?: boolean; + ignoreDiagnostics?: number[]; + forkChecker?: boolean; + forkCheckerSilent?: boolean; + useBabel?: boolean; + babelCore?: string; + babelOptions?: any; + usePrecompiledFiles?: boolean; + skipDeclarationFilesCheck?: boolean; + useCache?: boolean; + cacheDirectory?: string; + resolveGlobs?: boolean; + library: string; +} + +export interface ResolverPlugin { + apply(resolver: Resolver): void; +} + +export interface Resolver { + apply(plugin: ResolverPlugin): void; + plugin(source: string, cb: ResolverCallback): void; + doResolve(target: string, req: Request, desc: string, Callback: any): void; + join(relativePath: string, innerRequest: Request): Request; +} + +export interface Mapping { + onlyModule: boolean; + alias: string; + aliasPattern: RegExp; + target: string; +} + +export function readConfigFile(baseDir: string, query: QueryOptions, tsImpl: typeof ts): Configs { + let configFilePath: string; + if (query.tsconfig && query.tsconfig.match(/\.json$/)) { + configFilePath = path.dirname(path.resolve(process.cwd(),query.tsconfig)); + } else { + configFilePath = tsImpl.findConfigFile(process.cwd(), tsImpl.sys.fileExists); + } + + let existingOptions = tsImpl.convertCompilerOptionsFromJson(query, process.cwd(), 'atl.query'); + + if (!configFilePath) { + return { + configFilePath: process.cwd(), + compilerConfig: tsImpl.parseJsonConfigFileContent( + {}, + tsImpl.sys, + process.cwd(), + _.extend({}, ts.getDefaultCompilerOptions(), existingOptions.options) as ts.CompilerOptions, + process.cwd() + ), + loaderConfig: query as LoaderConfig + }; + } + + debugger; + + let jsonConfigFile = tsImpl.readConfigFile(configFilePath, tsImpl.sys.readFile); + + let compilerConfig = tsImpl.parseJsonConfigFileContent( + jsonConfigFile.config, + tsImpl.sys, + process.cwd(), + existingOptions.options, + configFilePath + ); + + return { + jsonConfigFile, + configFilePath, + compilerConfig, + loaderConfig: _.defaults( + query, + jsonConfigFile.config.awesomeTypescriptLoaderOptions) + }; +} + + + +export function setupTs(compiler: string): TSCompilerInfo { + let compilerPath = compiler || 'typescript'; + + let tsImpl: typeof ts; + let tsImplPath: string; + try { + tsImplPath = require.resolve(compilerPath); + tsImpl = require(tsImplPath); + } catch (e) { + console.error(e); + process.exit(1); + } + + let compilerInfo: TSCompilerInfo = { + compilerPath, + tsImpl, + }; + + return compilerInfo; +} + + +function escapeRegExp(str: string) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); +} + +export class PathsPlugin implements ResolverPlugin { + source: string; + target: string; + ts: typeof ts; + configFilePath: string; + options: ts.CompilerOptions; + + baseUrl: string; + mappings: Mapping[]; + absoluteBaseUrl: string; + + + constructor(config: LoaderConfig & ts.CompilerOptions = {} as any) { + this.source = 'described-resolve'; + this.target = 'resolve'; + + this.ts = setupTs(config.compiler).tsImpl; + + let { configFilePath, compilerConfig, jsonConfigFile } = readConfigFile(process.cwd(), config, this.ts); + this.options = compilerConfig.options; + this.configFilePath = configFilePath; + + this.baseUrl = this.options.configFilePath ? this.options.configFilePath : './'; + + this.absoluteBaseUrl = path.resolve( + path.dirname(this.configFilePath), + this.baseUrl + ); + + console.log("CONFIG FILE AND BASE URL"); + console.log(this.configFilePath, this.absoluteBaseUrl); + + this.mappings = []; + let paths = this.options.paths || {}; + Object.keys(paths).forEach(alias => { + let onlyModule = alias.indexOf('*') === -1; + let excapedAlias = escapeRegExp(alias); + let targets = paths[alias]; + targets.forEach(target => { + let aliasPattern: RegExp; + if (onlyModule) { + aliasPattern = new RegExp(`^${excapedAlias}$`); + } else { + let withStarCapturing = excapedAlias.replace('\\*', '(.*)'); + aliasPattern = new RegExp(`^${withStarCapturing}`); + } + + this.mappings.push({ + onlyModule, + alias, + aliasPattern, + target: target + }); + }); + }); + } + + apply(resolver: Resolver) { + let { baseUrl, mappings, absoluteBaseUrl } = this; + + if (baseUrl) { + resolver.apply(new ModulesInRootPlugin("module", absoluteBaseUrl, "resolve")); + } + + mappings.forEach(mapping => { + // resolver.plugin(this.source, this.createPlugin(resolver, mapping)); + resolver.plugin(this.source, function(request, callback) { + var innerRequest = getInnerRequest(resolver, request); + if(!innerRequest) return callback(); + + var newRequestStr = mapping.target; + var match = innerRequest.match(mapping.aliasPattern); + if (!match) { + return callback(); + } + if (!mapping.onlyModule) { + newRequestStr = newRequestStr.replace('*', match[1]); + } + if (newRequestStr[0] === '.') { + newRequestStr = path.resolve(absoluteBaseUrl, newRequestStr); + } + var obj: Request = Object.assign({}, request, { + request: newRequestStr + }); + + console.log("aliased'" + innerRequest + "': '" + mapping.alias + "' to '" + newRequestStr + "'", newRequest); + + return resolver.doResolve(this.target, obj,"aliased with mapping '" + innerRequest + "': '" + mapping.alias + "' to '" + newRequestStr + "'", createInnerCallback(function(err, result) { + if(arguments.length > 0) return callback(err, result); + + // don't allow other aliasing or raw request + callback(null, null); + }, callback)); + + return callback(); + }); + }); + } +}