chore(gulp): updates to build process to use app-scripts for watch/build

updates to build process to use app-scripts for watch/build
This commit is contained in:
Dan Bucholtz
2017-03-02 14:28:15 -06:00
parent 0fdff89b67
commit 0d32e5e791
30 changed files with 808 additions and 1068 deletions

View File

@ -28,20 +28,18 @@ Run `gulp build` or `gulp watch` to watch for changes.
#### Development #### Development
1. Run `gulp e2e` or `gulp e2e.watch` to watch for changes. 1. Run `gulp e2e.watch --folder nav/basic` to watch for changes, where `nav` is the component, and `basic` is the test name
2. Navigate to `http://localhost:8000/dist/e2e` 2. The browser will launch just like when using `ionic serve`. Make changes to an app in the `src` directory and the app will rebuild.
#### Validation #### Validation
The following commands take longer to run because they use AoT compilation. They should really only be used to validate that our components work with AoT, and fix them if not. The following commands take longer to run because they use AoT compilation. They should really only be used to validate that our components work with AoT, and fix them if not.
1. Run `gulp e2e.prod` to bundle all e2e tests. Folder is optional, see the flags section below. 1. Run `gulp e2e.prod` to bundle all e2e tests.
2. Run `gulp e2e.watchProd` with a folder passed to watch a test. Folder is required, see the flags section below.
3. Navigate to `http://localhost:8000/dist/e2e`
##### Flags ##### Flags
- `--f | -folder` will run the command with a test folder. For example, `gulp e2e.watchProd --f=select/single-value` will watch the test in `src/components/select/test/single-value`. - `--f | -folder` will run the command with a test folder.
- `--debug` will run the `ionic-app-scripts` command with debug output printed. - `--debug` will run the `ionic-app-scripts` command with debug output printed.
@ -98,8 +96,6 @@ To remove the linked version of `ionic-angular` do `npm rm ionic-angular`, and t
- `gulp snapshot` will run the `gulp e2e.prod` task with AoT compilation. - `gulp snapshot` will run the `gulp e2e.prod` task with AoT compilation.
- `gulp snapshot.skipBuild` will skip the `gulp e2e.prod` task with AoT compilation. - `gulp snapshot.skipBuild` will skip the `gulp e2e.prod` task with AoT compilation.
- `gulp snapshot.dev` will run a development build using the `gulp e2e` task.
- `gulp snapshot.quick` will skip the build and run snapshot without uploading to the server.
#### Flags #### Flags

View File

@ -1,7 +1,25 @@
// we don't want to run copy for the demos, so just override the config for now // we don't want to run copy for the demos, so just override the config for now
var path = require('path');
module.exports = { module.exports = {
copyAssets: { }, copyAssets: {
copyIndexContent: { }, src: [path.join(path.dirname(process.env.IONIC_APP_ENTRY_POINT), '..', 'assets', '**', '*')],
copyFonts: { }, dest: '{{WWW}}/assets'
copyPolyfills: { } },
copyIndexContent: {
src: [path.join(process.cwd(), 'scripts', 'demos', 'index.html')],
dest: '{{WWW}}'
},
copyFonts: {
src: [`${process.cwd()}/node_modules/ionicons/dist/fonts/**/*`, `${process.cwd()}/src/fonts/**/*`],
dest: '{{WWW}}/assets/fonts'
},
copyPolyfills: {
src: [path.join(process.cwd(), 'dist', 'demos', 'polyfills', 'polyfills.js')],
dest: '{{BUILD}}'
},
sharedCss: {
src: [path.join(process.cwd(), 'scripts', 'demos', 'demos.shared.css')],
dest: `{{BUILD}}`
}
} }

View File

@ -1,57 +0,0 @@
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<title>Ionic E2E</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="apple-mobile-web-app-capable" content="yes">
<link id="ionicLink" href="../css/ionic.css" rel="stylesheet">
<link href="../css/demos.shared.css" rel="stylesheet">
<script src="/node_modules/systemjs/dist/system.src.js"></script>
<script>
System.config({
map: {
'@angular/core': '/node_modules/@angular/core/bundles/core.umd.js',
'@angular/compiler': '/node_modules/@angular/compiler/bundles/compiler.umd.js',
'@angular/common': '/node_modules/@angular/common/bundles/common.umd.js',
'@angular/forms': '/node_modules/@angular/forms/bundles/forms.umd.js',
'@angular/http': '/node_modules/@angular/http/bundles/http.umd.js',
'@angular/platform-browser': '/node_modules/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': '/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'ionic-angular': location.protocol + '//' + location.host + '/dist/ionic-angular',
'rxjs': '/node_modules/rxjs'
},
packages: {
'ionic-angular': {
main: 'index',
defaultExtension: false
},
'rxjs': {
defaultExtension: 'js'
}
}
});
</script>
<script src="../polyfills/polyfills.js"></script>
<script src="../../bundles/ionic.system.js"></script>
</head>
<body>
<ion-app></ion-app>
<script>
System.import('@angular/platform-browser-dynamic').then(function(platformBrowserDynamic) {
System.import('app.module.js').then(function(appModule) {
platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(appModule.AppModule);
}, console.error.bind(console));
}, console.error.bind(console));
</script>
</body>
</html>

View File

@ -7,13 +7,13 @@
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<link id="ionicLink" href="./build/main.css" rel="stylesheet"> <link id="ionicLink" href="./build/main.css" rel="stylesheet">
<link href="../css/demos.shared.css" rel="stylesheet"> <link href="./build/e2e.shared.css" rel="stylesheet">
</head> </head>
<body> <body>
<ion-app></ion-app> <ion-app></ion-app>
<script src="../polyfills/polyfills.js"></script> <script src="./build/polyfills.js"></script>
<script src="./build/main.js"></script> <script src="./build/main.js"></script>
</body> </body>
</html> </html>

View File

@ -1,5 +0,0 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -0,0 +1,15 @@
var path = require('path');
var watch = require('../../node_modules/@ionic/app-scripts/dist/watch');
var entryPointDirectory = path.dirname(process.env.IONIC_APP_ENTRY_POINT)
module.exports = {
demoSrc: {
paths: [path.join(entryPointDirectory, '..', '**', '*.(ts|html|s(c|a)ss)')],
options: { ignored: [path.join(entryPointDirectory, '..', '**', '*.spec.ts'),
path.join(entryPointDirectory, '..', '**', '*.e2e.ts'),
'**/*.DS_Store'] },
callback: watch.buildUpdate
}
}

View File

@ -1,7 +1,25 @@
// we don't want to run copy for the demos, so just override the config for now // we don't want to run copy for the demos, so just override the config for now
var path = require('path');
module.exports = { module.exports = {
copyAssets: { }, copyAssets: {
copyIndexContent: { }, src: [path.join(path.dirname(process.env.IONIC_APP_ENTRY_POINT), '..', 'assets', '**', '*')],
copyFonts: { }, dest: '{{WWW}}/assets'
copyPolyfills: { } },
copyIndexContent: {
src: [path.join(process.cwd(), 'scripts', 'e2e', 'index.html')],
dest: '{{WWW}}'
},
copyFonts: {
src: [`${process.cwd()}/node_modules/ionicons/dist/fonts/**/*`, `${process.cwd()}/src/fonts/**/*`],
dest: '{{WWW}}/assets/fonts'
},
copyPolyfills: {
src: [path.join(process.cwd(), 'dist', 'e2e', 'polyfills', 'polyfills.ng.js')],
dest: '{{BUILD}}'
},
sharedCss: {
src: [path.join(process.cwd(), 'scripts', 'e2e', 'e2e.shared.css')],
dest: `{{BUILD}}`
}
} }

View File

@ -1,83 +0,0 @@
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<title>Ionic E2E</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="apple-mobile-web-app-capable" content="yes">
<link id="ionicLink" href="../../css/ionic.css" rel="stylesheet">
<link href="../../css/e2e.shared.css" rel="stylesheet">
<script>
var port;
if (location.href.indexOf('snapshot=true') > -1) {
port = 8876;
document.documentElement.classList.add('snapshot');
} else {
port = 8000;
document.documentElement.classList.remove('snapshot');
}
if (location.href.indexOf('cordova=true') > -1) {
window.cordova = {};
}
if (location.href.indexOf('rtl=true') > -1) {
document.dir = 'rtl';
} else {
document.dir = 'ltr';
}
if (location.href.indexOf('theme=dark') > -1) {
var link = document.getElementById('ionicLink');
link.setAttribute('href', link.getAttribute('href').replace('.css', '.dark.css'));
}
</script>
<script src="/node_modules/systemjs/dist/system.src.js"></script>
<script>
System.config({
map: {
'@angular/core': '/node_modules/@angular/core/bundles/core.umd.js',
'@angular/compiler': '/node_modules/@angular/compiler/bundles/compiler.umd.js',
'@angular/common': '/node_modules/@angular/common/bundles/common.umd.js',
'@angular/forms': '/node_modules/@angular/forms/bundles/forms.umd.js',
'@angular/http': '/node_modules/@angular/http/bundles/http.umd.js',
'@angular/platform-browser': '/node_modules/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': '/node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'ionic-angular': location.protocol + '//' + location.host + '/ionic-angular',
'rxjs': '/node_modules/rxjs'
},
packages: {
'ionic-angular': {
main: 'index',
defaultExtension: false
},
'rxjs': {
defaultExtension: 'js'
}
}
});
</script>
<script src="../../polyfills/polyfills.ng.js"></script>
<script src="../../../bundles/ionic.system.js"></script>
</head>
<body>
<ion-app></ion-app>
<script>
System.import('@angular/platform-browser-dynamic').then(function(platformBrowserDynamic) {
System.import('app.module.js').then(function(appModule) {
platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(appModule.AppModule);
}, console.error.bind(console));
}, console.error.bind(console));
</script>
</body>
</html>

View File

@ -1,4 +1,4 @@
describe('<%= relativePath %>: <%= platform %>', function() { describe('<%= relativePathBackwardsCompatibility %>: <%= platform %>', function() {
it('should init', function() { it('should init', function() {
browser.get('http://localhost:<%= buildConfig.protractorPort %>/dist/e2e/<%= relativePath %>/index.html?ionicplatform=<%= platform %>&ionicOverlayCreatedDiff=0&ionicanimate=false&snapshot=true'); browser.get('http://localhost:<%= buildConfig.protractorPort %>/dist/e2e/<%= relativePath %>/index.html?ionicplatform=<%= platform %>&ionicOverlayCreatedDiff=0&ionicanimate=false&snapshot=true');

View File

@ -7,7 +7,7 @@
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<link id="ionicLink" href="./build/main.css" rel="stylesheet"> <link id="ionicLink" href="./build/main.css" rel="stylesheet">
<link href="../../../../css/e2e.shared.css" rel="stylesheet"> <link href="build/e2e.shared.css" rel="stylesheet">
<script> <script>
if (location.href.indexOf('snapshot=true') > -1) { if (location.href.indexOf('snapshot=true') > -1) {
@ -43,7 +43,7 @@
document.body.classList.remove('rtl'); document.body.classList.remove('rtl');
} }
</script> </script>
<script src="../../../../polyfills/polyfills.js"></script> <script src="./build/polyfills.ng.js"></script>
<script src="./build/main.js"></script> <script src="./build/main.js"></script>
</body> </body>
</html> </html>

View File

@ -1,5 +0,0 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -3,7 +3,7 @@
// Font path is used to include ionicons, // Font path is used to include ionicons,
// roboto, and noto sans fonts // roboto, and noto sans fonts
$font-path: "../../../../../fonts"; $font-path: "../assets/fonts";
@import "ionic.globals"; @import "ionic.globals";

View File

@ -31,7 +31,7 @@ export const DIST_BUILD_ROOT = join(DIST_ROOT, PACKAGE_NAME);
export const DIST_BUNDLE_ROOT = join(DIST_BUILD_ROOT, BUNDLES); export const DIST_BUNDLE_ROOT = join(DIST_BUILD_ROOT, BUNDLES);
export const DIST_BUILD_UMD_ROOT = join(DIST_BUILD_ROOT, UMD_MODULE); export const DIST_BUILD_UMD_ROOT = join(DIST_BUILD_ROOT, UMD_MODULE);
export const DIST_BUILD_UMD_BUNDLE_ENTRYPOINT = join(DIST_BUILD_ROOT, INDEX_JS); export const DIST_BUILD_UMD_BUNDLE_ENTRYPOINT = join(DIST_BUILD_ROOT, INDEX_JS);
export const DIST_BUILD_ES2015_ROOT = join(DIST_BUILD_ROOT, ES_2015); export const DIST_BUILD_ESM_ROOT = join(DIST_BUILD_ROOT, 'esm');
export const DIST_VENDOR_ROOT = join(DIST_ROOT, VENDOR_NAME); export const DIST_VENDOR_ROOT = join(DIST_ROOT, VENDOR_NAME);
export const NODE_MODULES_ROOT = join(PROJECT_ROOT, NODE_MODULES); export const NODE_MODULES_ROOT = join(PROJECT_ROOT, NODE_MODULES);
export const SCRIPTS_ROOT = join(PROJECT_ROOT, SCRIPTS_NAME); export const SCRIPTS_ROOT = join(PROJECT_ROOT, SCRIPTS_NAME);

View File

@ -15,6 +15,7 @@ declare module 'gulp-tslint';
declare module 'gulp-typescript'; declare module 'gulp-typescript';
declare module 'html-entities'; declare module 'html-entities';
declare module 'inquirer'; declare module 'inquirer';
declare module 'p-all';
declare module 'path'; declare module 'path';
declare module 'rollup'; declare module 'rollup';
declare module 'rollup-plugin-commonjs'; declare module 'rollup-plugin-commonjs';

View File

@ -5,7 +5,6 @@ import './tasks/demos';
import './tasks/demos.dev'; import './tasks/demos.dev';
import './tasks/demos.prod'; import './tasks/demos.prod';
import './tasks/docs'; import './tasks/docs';
import './tasks/e2e';
import './tasks/e2e.dev'; import './tasks/e2e.dev';
import './tasks/e2e.prod'; import './tasks/e2e.prod';
import './tasks/lint'; import './tasks/lint';

View File

@ -1,6 +1,6 @@
import { task } from 'gulp'; import { task } from 'gulp';
import { DIST_BUILD_ROOT, DIST_BUILD_ES2015_ROOT, DIST_BUILD_UMD_ROOT, ES5, ES_2015, PROJECT_ROOT, UMD_MODULE } from '../constants'; import { DIST_BUILD_ROOT, DIST_BUILD_ESM_ROOT, DIST_BUILD_UMD_ROOT, ES5, ES_2015, PROJECT_ROOT, UMD_MODULE } from '../constants';
import { copySourceToDest, createTempTsConfig, deleteFiles, runNgc } from '../util'; import { copySourceToDest, createTempTsConfig, deleteFiles, runNgc, runTsc } from '../util';
export function buildIonicAngularUmd(excludeSpec: boolean, stripDebug: boolean, done: Function) { export function buildIonicAngularUmd(excludeSpec: boolean, stripDebug: boolean, done: Function) {
@ -23,11 +23,51 @@ export function buildIonicAngularUmd(excludeSpec: boolean, stripDebug: boolean,
}); });
} }
export function buildIonicAngularUmdTsc(excludeSpec: boolean, stripDebug: boolean, done: Function) {
const stream = copySourceToDest(DIST_BUILD_UMD_ROOT, excludeSpec, true, stripDebug);
stream.on('end', () => {
// the source files are copied, copy over a tsconfig from
createTempTsConfig(['./**/*.ts'], ES5, UMD_MODULE, `${PROJECT_ROOT}/tsconfig.json`, `${DIST_BUILD_UMD_ROOT}/tsconfig.json`);
runTsc(`${DIST_BUILD_UMD_ROOT}/tsconfig.json`, (err) => {
if (err) {
done(err);
return;
}
// clean up any .ts files that remain as well as ngc metadata
deleteFiles([`${DIST_BUILD_UMD_ROOT}/**/*.ts`,
`${DIST_BUILD_UMD_ROOT}/node_modules`,
`${DIST_BUILD_UMD_ROOT}/tsconfig.json`,
`!${DIST_BUILD_UMD_ROOT}/**/*.d.ts`], done);
});
});
}
export function buildIonicAngularEsm(stripDebug: boolean, done: Function) { export function buildIonicAngularEsm(stripDebug: boolean, done: Function) {
const stream = copySourceToDest(DIST_BUILD_ESM_ROOT, true, true, stripDebug);
stream.on('end', () => {
// the source files are copied, copy over a tsconfig from
createTempTsConfig(['./**/*.ts'], ES_2015, ES_2015, `${PROJECT_ROOT}/tsconfig.json`, `${DIST_BUILD_ESM_ROOT}/tsconfig.json`);
runNgc(`${DIST_BUILD_ESM_ROOT}/tsconfig.json`, (err) => {
if (err) {
done(err);
return;
}
// clean up any .ts files that remain as well as ngc metadata
deleteFiles([`${DIST_BUILD_ESM_ROOT}/**/*.ts`,
`${DIST_BUILD_ESM_ROOT}/node_modules`,
`${DIST_BUILD_ESM_ROOT}/tsconfig.json`,
`!${DIST_BUILD_ESM_ROOT}/**/*.d.ts`], done);
});
});
}
export function buildIonicPureEs6(stripDebug: boolean, done: Function) {
const stream = copySourceToDest(DIST_BUILD_ROOT, true, true, stripDebug); const stream = copySourceToDest(DIST_BUILD_ROOT, true, true, stripDebug);
stream.on('end', () => { stream.on('end', () => {
// the source files are copied, copy over a tsconfig from // the source files are copied, copy over a tsconfig from
createTempTsConfig(['./**/*.ts'], ES5, ES_2015, `${PROJECT_ROOT}/tsconfig.json`, `${DIST_BUILD_ROOT}/tsconfig.json`); createTempTsConfig(['./**/*.ts'], ES_2015, ES_2015, `${PROJECT_ROOT}/tsconfig.json`, `${DIST_BUILD_ROOT}/tsconfig.json`);
runNgc(`${DIST_BUILD_ROOT}/tsconfig.json`, (err) => { runNgc(`${DIST_BUILD_ROOT}/tsconfig.json`, (err) => {
if (err) { if (err) {
done(err); done(err);
@ -42,28 +82,9 @@ export function buildIonicAngularEsm(stripDebug: boolean, done: Function) {
}); });
} }
export function buildIonicPureEs6(stripDebug: boolean, done: Function) {
const stream = copySourceToDest(DIST_BUILD_ES2015_ROOT, true, true, stripDebug);
stream.on('end', () => {
// the source files are copied, copy over a tsconfig from
createTempTsConfig(['./**/*.ts'], ES_2015, ES_2015, `${PROJECT_ROOT}/tsconfig.json`, `${DIST_BUILD_ES2015_ROOT}/tsconfig.json`);
runNgc(`${DIST_BUILD_ES2015_ROOT}/tsconfig.json`, (err) => {
if (err) {
done(err);
return;
}
// clean up any .ts files that remain as well as ngc metadata
deleteFiles([`${DIST_BUILD_ES2015_ROOT}/**/*.ts`,
`${DIST_BUILD_ES2015_ROOT}/node_modules`,
`${DIST_BUILD_ES2015_ROOT}/tsconfig.json`,
`!${DIST_BUILD_ES2015_ROOT}/**/*.d.ts`], done);
});
});
}
/* this task builds out the necessary stuff for karma */ /* this task builds out the necessary stuff for karma */
task('compile.karma', (done: Function) => { task('compile.karma', (done: Function) => {
buildIonicAngularUmd(false, false, done); buildIonicAngularUmdTsc(false, false, done);
}); });
/* this task builds out the ionic-angular (commonjs and esm) directories for release */ /* this task builds out the ionic-angular (commonjs and esm) directories for release */

View File

@ -1,183 +1,44 @@
import { readFileSync } from 'fs'; import { join } from 'path';
import { dirname, join } from 'path';
import { dest, src, start, task } from 'gulp'; import { task } from 'gulp';
import * as babel from 'gulp-babel';
import * as cache from 'gulp-cached';
import * as concat from 'gulp-concat';
import * as connect from 'gulp-connect';
import * as gulpif from 'gulp-if';
import * as remember from 'gulp-remember';
import * as tsc from 'gulp-typescript';
import * as watch from 'gulp-watch';
import { template } from 'lodash';
import * as merge from 'merge2';
import * as runSequence from 'run-sequence';
import { obj } from 'through2';
import * as VinylFile from 'vinyl';
import { DEMOS_NAME, DIST_DEMOS_ROOT, DIST_NAME, ES5, ES_2015, SCRIPTS_ROOT } from '../constants'; import { DEMOS_ROOT, DIST_DEMOS_ROOT, ES_2015, PROJECT_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, runAppScriptsServe } from '../util';
const buildConfig = require('../../build/config'); task('demos.watch', ['demos.prepare'], (done: Function) => {
const folderInfo = getFolderInfo();
/** if (!folderInfo || !folderInfo.componentName ) {
* Builds Ionic demos tests to dist/demos and creates the necessary files for tests done(new Error(`Usage: gulp e2e.watch --folder modal`));
* to run.
*/
task('demos', demosBuild);
function demosBuild(done: (err: any) => void) {
runSequence(
'demos.clean',
'demos.build',
'demos.polyfill',
'demos.copyExternalDependencies',
'demos.sass',
'demos.fonts',
'demos.bundle',
done);
}
/**
* Builds Ionic demos tests to dist/demos.
*/
task('demos.build', function () {
var indexTemplate = template(
readFileSync(`${SCRIPTS_ROOT}/${DEMOS_NAME}/demos.template.dev.html`).toString()
)({
buildConfig: buildConfig
});
// Get each test folder with src
var tsResult = src([
'demos/src/*/**/*.ts'
])
.pipe(cache('demos.ts'))
.pipe(tsc(getTscOptions(), undefined, tscReporter))
.on('error', function (error) {
console.log(error.message);
})
.pipe(gulpif(/app.module.js$/, createIndexHTML()));
var testFiles = src([
'demos/src/*/**/*',
'!demos/src/*/**/*.ts'
])
.pipe(cache('demos.files'));
return merge([
tsResult,
testFiles
])
.pipe(dest(DIST_DEMOS_ROOT))
.pipe(connect.reload());
function createIndexHTML() {
return obj(function (file, enc, next) {
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTemplate),
path: join(dirname(file.path), 'index.html'),
}));
next(null, file);
});
} }
});
/** serveDemo(folderInfo.componentName).then(() => {
* Creates SystemJS bundle from Ionic source files. done();
*/ }).catch((err: Error) => {
task('demos.bundle', function () { done(err);
return tsCompile(getTscOptions('es6'), 'system')
.pipe(babel(babelOptions))
.pipe(remember('system'))
.pipe(concat('ionic.system.js'))
.pipe(dest(`${DIST_NAME}/bundles`))
.pipe(connect.reload());
});
function tsCompile(options, cacheName) {
return src([
'typings/main.d.ts',
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/components/*/test/**/*',
'!src/util/test/*',
'!src/config/test/*',
'!src/platform/test/*',
'!src/**/*.spec.ts'
])
.pipe(cache(cacheName, { optimizeMemory: true }))
.pipe(tsc(options, undefined, tscReporter));
}
function getTscOptions(name?: string) {
var opts = {
emitDecoratorMetadata: true,
experimentalDecorators: true,
target: ES5,
module: 'commonjs',
isolatedModules: true,
typescript: require('typescript'),
declaration: false
};
if (name === 'typecheck') {
opts.declaration = true;
delete opts.isolatedModules;
} else if (name === 'es6') {
opts.target = 'es6';
delete opts.module;
}
return opts;
}
var tscReporter = {
error: function (error) {
console.error(error.message);
}
};
// We use Babel to easily create named System.register modules
// See: https://github.com/Microsoft/TypeScript/issues/4801
// and https://github.com/ivogabe/gulp-typescript/issues/211
const babelOptions = {
moduleIds: true,
getModuleId: function (name) {
return 'ionic-angular/' + name;
},
plugins: ['transform-es2015-modules-systemjs'],
presets: [ES_2015]
};
/**
* Builds demos tests to dist/demos and watches for changes. Runs 'demos.bundle' or
* 'sass' on Ionic source changes and 'demos.build' for demos test changes.
*/
task('demos.watch', ['demos'], function () {
watchTask('demos.bundle');
watch('demos/src/**/*', function (file) {
start('demos.build');
}); });
}); });
function watchTask(task) { function serveDemo(folderName: any) {
watch([
'src/**/*.ts',
'!src/components/*/test/**/*',
'!src/util/test/*'
],
function (file) {
if (file.event !== 'unlink') {
start(task);
}
}
);
watch('src/**/*.scss', function () { const ionicAngularDir = join(PROJECT_ROOT, 'src');
start('demos.sass'); const srcTestRoot = join(DEMOS_ROOT, 'src', folderName);
}); const distDemoRoot = join(DIST_DEMOS_ROOT, folderName);
const includeGlob = [ join(ionicAngularDir, '**', '*.ts'),
join(srcTestRoot, '**', '*.ts')];
start('demos.serve');
const pathToWriteFile = join(distDemoRoot, 'tsconfig.json');
const pathToReadFile = join(PROJECT_ROOT, 'tsconfig.json');
createTempTsConfig(includeGlob, ES_2015, ES_2015, pathToReadFile, pathToWriteFile, { removeComments: true});
const sassConfigPath = join('scripts', 'demos', 'sass.config.js');
const copyConfigPath = join('scripts', 'demos', 'copy.config.js');
const watchConfigPath = join('scripts', 'demos', 'watch.config.js');
const appEntryPoint = join(srcTestRoot, 'app', 'main.ts');
const appNgModulePath = join(srcTestRoot, 'app', 'app.module.ts');
const distDir = join(distDemoRoot, 'www');
return runAppScriptsServe(folderName, appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath, watchConfigPath);
} }

View File

@ -1,193 +1,193 @@
import { accessSync, F_OK, readFileSync, stat } from 'fs'; import { dirname, join, relative } from 'path';
import { dirname, join } from 'path'; import { readFileSync } from 'fs';
import { dest, src, start, task } from 'gulp'; import * as glob from 'glob';
import * as gulpif from 'gulp-if'; import { task } from 'gulp';
import * as watch from 'gulp-watch'; import * as del from 'del';
import { template } from 'lodash';
import * as runSequence from 'run-sequence'; import * as runSequence from 'run-sequence';
import { obj } from 'through2'; import { argv } from 'yargs';
import * as VinylFile from 'vinyl';
import { DEMOS_SRC_ROOT, DIST_DEMOS_ROOT, DIST_NAME, DEMOS_NAME, ES5, ES_2015, LOCAL_SERVER_PORT, SCRIPTS_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, getFolders, runAppScripts } from '../util';
task('demos.prod', demosBuild); import { DEMOS_SRC_ROOT, ES_2015, PROJECT_ROOT, SRC_ROOT, SRC_COMPONENTS_ROOT, SCRIPTS_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, readFileAsync, runAppScriptsBuild, writeFileAsync, writePolyfills } from '../util';
function demosBuild(done: (err: any) => void) { import * as pAll from 'p-all';
runSequence(
'demos.copyIonic', task('demos.prepare', (done: Function) => {
'demos.clean', runSequence('demos.clean', 'demos.polyfill', (err: any) => done(err));
'demos.polyfill', });
'demos.copySource',
'demos.copyExternalDependencies', task('demos.prod', ['demos.prepare'], (done: Function) => {
'demos.sass',
'demos.fonts', // okay, first find out all of the demos tests to run by finding all of the 'main.ts' files
'demos.compileTests', filterDemosEntryPoints().then((filePaths: string[]) => {
done); console.log(`Compiling ${filePaths.length} Demos ...`);
console.log('filePaths: ', filePaths);
return buildDemos(filePaths);
}).then(() => {
done();
}).catch((err: Error) => {
done(err);
});
});
function filterDemosEntryPoints() {
return getDemosEntryPoints().then((entryPoints: string[]) => {
const folderInfo = getFolderInfo();
if (folderInfo && folderInfo.componentName) {
const filtered = entryPoints.filter(entryPoint => {
return entryPoint.indexOf(folderInfo.componentName) >= 0;
});
return filtered;
}
return entryPoints;
});
} }
task('demos.copyIonic', (done: (err: any) => void) => { function getDemosEntryPoints() {
runSequence( return new Promise((resolve, reject) => {
'compile.release', const mainGlob = join(DEMOS_SRC_ROOT, '**', 'main.ts');
'release.compileSass', glob(mainGlob, (err: Error, matches: string[]) => {
'release.fonts', if (err) {
'release.sass', return reject(err);
'release.createUmdBundle',
done);
});
task('demos.copySource', (done: Function) => {
const stream = src([`${DEMOS_SRC_ROOT}/**/*`])
.pipe(gulpif(/app.module.ts$/, createIndexHTML()))
.pipe(dest(DIST_DEMOS_ROOT));
stream.on('end', done);
function createIndexHTML() {
const indexTemplate = readFileSync(`${SCRIPTS_ROOT}/${DEMOS_NAME}/demos.template.prod.html`);
const indexTs = readFileSync(`${SCRIPTS_ROOT}/${DEMOS_NAME}/main.ts`);
return obj(function (file, enc, next) {
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTemplate),
path: join(dirname(file.path), 'index.html'),
}));
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTs),
path: join(dirname(file.path), 'main.ts'),
}));
next(null, file);
});
}
});
task('demos.compileTests', (done: Function) => {
let folderInfo = getFolderInfo();
if (folderInfo.componentName && folderInfo.componentTest) {
buildTest(folderInfo);
} else {
buildAllTests(done);
}
});
function buildTest(folderInfo: any) {
let includeGlob = [`./dist/demos/${folderInfo.componentName}/*.ts`];
let pathToWriteFile = `${DIST_DEMOS_ROOT}/${folderInfo.componentName}/tsconfig.json`;
createTempTsConfig(includeGlob, ES5, ES_2015, `${DEMOS_SRC_ROOT}/tsconfig.json`, pathToWriteFile);
let sassConfigPath = 'scripts/demos/sass.config.js';
let appEntryPoint = `dist/demos/${folderInfo.componentName}/main.ts`;
let appNgModule = `dist/demos/${folderInfo.componentName}/app.module.ts`;
let distDir = `dist/demos/${folderInfo.componentName}/`;
return runAppScripts(folderInfo, sassConfigPath, appEntryPoint, appNgModule, distDir);
}
function buildAllTests(done: Function) {
let folders = getFolders('./dist/demos/');
let promises: Promise<any>[] = [];
folders.forEach(folder => {
stat(`./dist/demos/${folder}/app.module.ts`, function(err, stat) {
if (err == null) {
let folderInfo = {
componentName: folder,
componentTest: 'basic'
};
const promise = buildTest(folderInfo);
promises.push(promise);
} }
resolve(matches);
}); });
}); });
}
Promise.all(promises).then(() => {
function buildDemos(filePaths: string[]) {
const functions = filePaths.map(filePath => () => {
return buildDemo(filePath);
});
return pAll(functions, {concurrency: 8});
}
function buildDemo(filePath: string) {
const start = Date.now();
const ionicAngularDir = join(process.cwd(), 'src');
const componentDir = dirname(dirname(filePath));
const relativePathFromComponents = relative(dirname(DEMOS_SRC_ROOT), componentDir);
console.log('relativePathFromComponents: ', relativePathFromComponents);
const distTestRoot = join(process.cwd(), 'dist', 'demos', relativePathFromComponents);
const includeGlob = [ join(ionicAngularDir, '**', '*.ts'), join(componentDir, '**', '*.ts')];
const pathToWriteFile = join(distTestRoot, 'tsconfig.json');
const pathToReadFile = join(PROJECT_ROOT, 'tsconfig.json');
createTempTsConfig(includeGlob, ES_2015, ES_2015, pathToReadFile, pathToWriteFile, { removeComments: true});
const sassConfigPath = join('scripts', 'demos', 'sass.config.js');
const copyConfigPath = join('scripts', 'demos', 'copy.config.js');
const appEntryPoint = filePath;
const appNgModulePath = join(dirname(filePath), 'app.module.ts');
const distDir = join(distTestRoot, 'www');
return runAppScriptsBuild(appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath).then(() => {
const end = Date.now();
console.log(`${filePath} took a total of ${(end - start) / 1000} seconds to build`);
});
}
function copyProtractorTestContent(filePaths: string[]): Promise<any> {
return readDemosTestFiles(filePaths)
.then((map: Map<string, string>) => {
return applyTemplate(map);
}).then((map: Map<string, string>) => {
writeDemosJsFiles(map);
});
}
function applyTemplate(filePathContent: Map<string, string>) {
const buildConfig = require('../../build/config');
const templateFileContent = readFileSync(join(SCRIPTS_ROOT, 'demos', 'demos.template.js'));
const templater = template(templateFileContent.toString());
const modifiedMap = new Map<string, string>();
const platforms = ['android', 'ios', 'windows'];
filePathContent.forEach((fileContent: string, filePath: string) => {
const srcRelativePath = relative(SRC_ROOT, dirname(filePath));
const wwwRelativePath = join(srcRelativePath, 'www');
platforms.forEach(platform => {
const platformContents = templater({
contents: fileContent,
buildConfig: buildConfig,
relativePath: wwwRelativePath,
platform: platform,
relativePathBackwardsCompatibility: dirname(wwwRelativePath)
});
const newFilePath = join(wwwRelativePath, `${platform}.demos.js`);
modifiedMap.set(newFilePath, platformContents);
});
});
return modifiedMap;
}
function writeDemosJsFiles(map: Map<string, string>) {
const promises: Promise<any>[] = [];
map.forEach((fileContent: string, filePath: string) => {
const destination = join(process.cwd(), 'dist', 'demos', filePath);
promises.push(writeFileAsync(destination, fileContent));
});
return Promise.all(promises);
}
function readDemosTestFiles(mainFilePaths: string[]): Promise<Map<string, string>> {
const demosFiles = mainFilePaths.map(mainFilePath => {
return join(dirname(mainFilePath), 'demos.ts');
});
const promises: Promise<any>[] = [];
const map = new Map<string, string>();
for (const demosFile of demosFiles) {
const promise = readDemosFile(demosFile);
promises.push(promise);
promise.then((content: string) => {
map.set(demosFile, content);
});
}
return Promise.all(promises).then(() => {
return map;
});
}
function readDemosFile(filePath: string) {
return readFileAsync(filePath).then((content: string) => {
// purge the import statement at the top
const purgeImportRegex = /.*?import.*?'protractor';/g;
return content.replace(purgeImportRegex, '');
});
}
task('demos.clean', (done: Function) => {
// this is a super hack, but it works for now
if (argv.skipClean) {
return done();
}
del(['dist/demos/**']).then(() => {
done(); done();
}).catch(err => { }).catch(err => {
done(err); done(err);
}); });
}
task('demos.watchProd', (done: Function) => {
const folderInfo = getFolderInfo();
let demoTestPath = DEMOS_SRC_ROOT;
if (folderInfo.componentName && folderInfo.componentTest) {
demoTestPath = join(DEMOS_SRC_ROOT, folderInfo.componentName, 'app.module.ts');
}
try {
accessSync(demoTestPath, F_OK);
} catch (e) {
done(new Error(`Could not find demos test: ${demoTestPath}`));
return;
}
if (demosComponentsExists(folderInfo)) {
// already generated the demos directory
demosWatch(folderInfo.componentName, folderInfo.componentTest);
} else {
// generate the demos directory
console.log('Generate demo builds first...');
demosBuild(() => {
demosWatch(folderInfo.componentName, folderInfo.componentTest);
});
}
}); });
function demosWatch(componentName: string, componentTest: string) { task('demos.polyfill', (done: Function) => {
// If any tests change within components then run demos.resources. if (argv.skipPolyfill) {
watch([ return done();
'demos/src/**/*' }
],
function (file) {
console.log('start demos.resources - ' + JSON.stringify(file.history, null, 2));
start('demos.copyAndCompile');
});
// If any src files change except for tests then transpile only the source ionic files writePolyfills('dist/demos/polyfills').then(() => {
watch([ done();
'src/**/*.ts', }).catch(err => {
'!src/components/*/test/**/*', done(err);
'!src/util/test/*'
],
function (file) {
console.log('start demos.ngcSource - ' + JSON.stringify(file.history, null, 2));
start('demos.copyAndCompile');
});
// If any scss files change then recompile all sass
watch(['src/**/*.scss'], (file) => {
console.log('start sass - ' + JSON.stringify(file.history, null, 2));
start('demos.sass');
}); });
});
let serverUrl = `http://localhost:${LOCAL_SERVER_PORT}/${DIST_NAME}/${DEMOS_NAME}`;
if (componentName) {
serverUrl += `/${componentName}`;
}
console.log(serverUrl);
start('demos.serve');
}
function demosComponentsExists(folderInfo: any): boolean {
let componentPath = DIST_DEMOS_ROOT;
if (folderInfo.componentName && folderInfo.componentTest) {
componentPath += `/${folderInfo.componentName}/build`;
}
try {
accessSync(componentPath, F_OK);
} catch (e) {
return false;
}
return true;
}

View File

@ -1,222 +1,50 @@
import { dirname, join } from 'path';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { dirname, join, sep } from 'path';
import { dest, src, start, task } from 'gulp'; import { task } from 'gulp';
import * as babel from 'gulp-babel';
import * as cache from 'gulp-cached';
import * as concat from 'gulp-concat';
import * as connect from 'gulp-connect';
import * as gulpif from 'gulp-if';
import * as remember from 'gulp-remember';
import * as rename from 'gulp-rename';
import * as tsc from 'gulp-typescript';
import * as watch from 'gulp-watch';
import { template } from 'lodash';
import * as merge from 'merge2';
import * as runSequence from 'run-sequence';
import { obj } from 'through2';
import * as VinylFile from 'vinyl';
import { DIST_E2E_ROOT, DIST_NAME, E2E_NAME, ES5, ES_2015, SCRIPTS_ROOT } from '../constants'; import { ES_2015, PROJECT_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, runAppScriptsServe } from '../util';
const buildConfig = require('../../build/config'); task('e2e.watch', ['e2e.prepare'], (done: Function) => {
const folderInfo = getFolderInfo();
/** if (!folderInfo || !folderInfo.componentName || !folderInfo.componentTest) {
* Builds Ionic e2e tests to dist/e2e and creates the necessary files for tests done(new Error(`Usage: gulp e2e.watch --folder nav/basic`));
* to run.
*/
task('e2e', e2eBuild);
function e2eBuild(done: (err: any) => void) {
runSequence(
'e2e.clean',
'e2e.build',
'e2e.polyfill',
'e2e.copyExternalDependencies',
'e2e.sass',
'e2e.fonts',
'e2e.bundle',
done);
}
/**
* Builds Ionic e2e tests to dist/e2e.
*/
task('e2e.build', function () {
var indexTemplate = template(
readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.dev.html`).toString()
)({
buildConfig: buildConfig
});
// Get each test folder with src
var tsResult = src([
'src/components/*/test/*/**/*.ts',
'!src/components/*/test/*/**/*.spec.ts'
])
.pipe(cache('e2e.ts'))
.pipe(tsc(getTscOptions(), undefined, tscReporter))
.on('error', function (error) {
console.log(error.message);
})
.pipe(gulpif(/app.module.js$/, createIndexHTML()))
.pipe(gulpif(/e2e.js$/, createPlatformTests()));
var testFiles = src([
'src/components/*/test/*/**/*',
'!src/components/*/test/*/**/*.ts'
])
.pipe(cache('e2e.files'));
return merge([
tsResult,
testFiles
])
.pipe(rename(function (file) {
file.dirname = file.dirname.replace(sep + 'test' + sep, sep);
}))
.pipe(dest(DIST_E2E_ROOT))
.pipe(connect.reload());
function createIndexHTML() {
return obj(function (file, enc, next) {
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTemplate),
path: join(dirname(file.path), 'index.html'),
}));
next(null, file);
});
} }
function createPlatformTests() { serveTest(folderInfo).then(() => {
let platforms = [ done();
'android', }).catch((err: Error) => {
'ios', done(err);
'windows'
];
let testTemplate = template(readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.js`).toString());
return obj(function (file, enc, next) {
let self = this;
let relativePath = dirname(file.path.replace(/^.*?src(\/|\\)components(\/|\\)/, ''));
relativePath = relativePath.replace('/test/', '/');
let contents = file.contents.toString();
platforms.forEach(function (platform) {
let platformContents = testTemplate({
contents: contents,
buildConfig: buildConfig,
relativePath: relativePath,
platform: platform
});
self.push(new VinylFile({
base: file.base,
contents: new Buffer(platformContents),
path: file.path.replace(/e2e.js$/, platform + '.e2e.js')
}));
});
next();
});
}
});
/**
* Creates SystemJS bundle from Ionic source files.
*/
task('e2e.bundle', function () {
return tsCompile(getTscOptions('es6'), 'system')
.pipe(babel(babelOptions))
.pipe(remember('system'))
.pipe(concat('ionic.system.js'))
.pipe(dest(`${DIST_NAME}/bundles`))
.pipe(connect.reload());
});
function tsCompile(options, cacheName) {
return src([
'typings/main.d.ts',
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/components/*/test/**/*',
'!src/util/test/*',
'!src/config/test/*',
'!src/platform/test/*',
'!src/**/*.spec.ts'
])
.pipe(cache(cacheName, { optimizeMemory: true }))
.pipe(tsc(options, undefined, tscReporter));
}
function getTscOptions(name?: string) {
var opts = {
emitDecoratorMetadata: true,
experimentalDecorators: true,
target: ES5,
module: 'commonjs',
isolatedModules: true,
typescript: require('typescript'),
declaration: false
};
if (name === 'typecheck') {
opts.declaration = true;
delete opts.isolatedModules;
} else if (name === 'es6') {
opts.target = 'es6';
delete opts.module;
}
return opts;
}
var tscReporter = {
error: function (error) {
console.error(error.message);
}
};
// We use Babel to easily create named System.register modules
// See: https://github.com/Microsoft/TypeScript/issues/4801
// and https://github.com/ivogabe/gulp-typescript/issues/211
const babelOptions = {
moduleIds: true,
getModuleId: function (name) {
return 'ionic-angular/' + name;
},
plugins: ['transform-es2015-modules-systemjs'],
presets: [ES_2015]
};
/**
* Builds e2e tests to dist/e2e and watches for changes. Runs 'e2e.bundle' or
* 'sass' on Ionic source changes and 'e2e.build' for e2e test changes.
*/
task('e2e.watch', ['e2e'], function () {
watchTask('e2e.bundle');
watch('src/components/*/test/**/*', function (file) {
start('e2e.build');
}); });
}); });
function watchTask(task) { function serveTest(folderInfo: any) {
watch([
'src/**/*.ts',
'!src/components/*/test/**/*',
'!src/util/test/*'
],
function (file) {
if (file.event !== 'unlink') {
start(task);
}
}
);
watch('src/**/*.scss', function () { const ionicAngularDir = join(PROJECT_ROOT, 'src');
start('e2e.sass'); const srcTestRoot = join(PROJECT_ROOT, 'src', 'components', folderInfo.componentName, 'test', folderInfo.componentTest);
}); const distTestRoot = join(PROJECT_ROOT, 'dist', 'e2e', 'components', folderInfo.componentName, 'test', folderInfo.componentTest);
const includeGlob = [ join(ionicAngularDir, '**', '*.ts')];
const pathToWriteFile = join(distTestRoot, 'tsconfig.json');
const pathToReadFile = join(PROJECT_ROOT, 'tsconfig.json');
start('e2e.serve'); createTempTsConfig(includeGlob, ES_2015, ES_2015, pathToReadFile, pathToWriteFile, { removeComments: true});
const sassConfigPath = join('scripts', 'e2e', 'sass.config.js');
const copyConfigPath = join('scripts', 'e2e', 'copy.config.js');
let appEntryPoint = join(srcTestRoot, 'app', 'main.ts');
try {
// check if the entry point exists, otherwise fall back to the legacy entry point without 'app' folder
readFileSync(appEntryPoint);
} catch (ex) {
// the file doesn't exist, so use the legacy entry point
appEntryPoint = join(srcTestRoot, 'main.ts');
}
// this assume that app.module.ts and main.ts are peers, which they should be no matter what
const appNgModulePath = join(dirname(appEntryPoint), 'app.module.ts');
const distDir = join(distTestRoot, 'www');
return runAppScriptsServe(folderInfo.componentName + '/' + folderInfo.componentTest, appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath, null);
} }

View File

@ -1,237 +1,220 @@
import { accessSync, F_OK, readFileSync, stat } from 'fs'; import { dirname, join, relative } from 'path';
import { dirname, join } from 'path'; import { readFileSync, writeFileSync } from 'fs';
import { dest, src, start, task } from 'gulp'; import * as glob from 'glob';
import * as gulpif from 'gulp-if'; import { task } from 'gulp';
import * as watch from 'gulp-watch'; import * as del from 'del';
import { template } from 'lodash'; import { template } from 'lodash';
import * as runSequence from 'run-sequence'; import * as runSequence from 'run-sequence';
import { obj } from 'through2'; import { argv } from 'yargs';
import * as VinylFile from 'vinyl';
import { DIST_E2E_ROOT, DIST_NAME, E2E_NAME, ES5, ES_2015, LOCAL_SERVER_PORT, DEMOS_SRC_ROOT, SCRIPTS_ROOT, SRC_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, getFolders, runAppScripts} from '../util';
task('e2e.prod', e2eBuild); import { ES_2015, PROJECT_ROOT, SRC_ROOT, SRC_COMPONENTS_ROOT, SCRIPTS_ROOT } from '../constants';
import { createTempTsConfig, createTimestamp, getFolderInfo, readFileAsync, runAppScriptsBuild, writeFileAsync, writePolyfills } from '../util';
function e2eBuild(done: (err: any) => void) { import * as pAll from 'p-all';
runSequence(
'e2e.copyIonic',
'e2e.clean',
'e2e.polyfill',
'e2e.copySource',
'e2e.copyExternalDependencies',
'e2e.sass',
'e2e.fonts',
'e2e.compileTests',
done);
}
task('e2e.copyIonic', (done: (err: any) => void) => { task('e2e.prepare', (done: Function) => {
runSequence( runSequence('e2e.clean', 'e2e.polyfill', 'e2e.prepareSass', (err: any) => done(err));
'compile.release',
'release.compileSass',
'release.fonts',
'release.sass',
'release.createUmdBundle',
done);
}); });
task('e2e.copySource', (done: Function) => { task('e2e.prepareSass', (done: Function) => {
const version = `E2E-${createTimestamp()}`;
writeFileSync(join(SRC_ROOT, 'themes/version.scss'), `$ionic-version: "${version}";`);
done();
});
const buildConfig = require('../../build/config'); task('e2e.prod', ['e2e.prepare'], (done: Function) => {
const stream = src([`${SRC_ROOT}/**/*`, `!${SRC_ROOT}/**/*.spec.ts`]) // okay, first find out all of the e2e tests to run by finding all of the 'main.ts' files
.pipe(gulpif(/app.module.ts$/, createIndexHTML())) filterE2eTestfiles().then((filePaths: string[]) => {
.pipe(gulpif(/e2e.ts$/, createPlatformTests())) console.log(`Compiling ${filePaths.length} E2E tests ...`);
.pipe(dest(DIST_E2E_ROOT)); return buildTests(filePaths);
}).then(() => {
done();
}).catch((err: Error) => {
done(err);
});
});
stream.on('end', done); function filterE2eTestfiles() {
return getE2eTestFiles().then((filePaths: string[]) => {
function createIndexHTML() { const entryPoints = filePaths.map(filePath => {
const indexTemplate = readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.prod.html`); const directoryName = dirname(filePath);
const indexTs = readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/main.ts`); return join(directoryName, 'app', 'main.ts');
return obj(function (file, enc, next) {
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTemplate),
path: join(dirname(file.path), 'index.html'),
}));
this.push(new VinylFile({
base: file.base,
contents: new Buffer(indexTs),
path: join(dirname(file.path), 'main.ts'),
}));
next(null, file);
}); });
} return entryPoints;
}).then((entryPoints: string[]) => {
// TODO this is almost the same as dev, diff and combine const folderInfo = getFolderInfo();
function createPlatformTests() { if (folderInfo && folderInfo.componentName && folderInfo.componentTest) {
let platforms = [ const filtered = entryPoints.filter(entryPoint => {
'android', return entryPoint.indexOf(folderInfo.componentName) >= 0 && entryPoint.indexOf(folderInfo.componentTest) >= 0;
'ios',
'windows'
];
let testTemplate = template(readFileSync(`${SCRIPTS_ROOT}/${E2E_NAME}/e2e.template.js`).toString());
return obj(function (file, enc, next) {
let self = this;
let relativePath = dirname(file.path.replace(/^.*?src(\/|\\)/, ''));
let contents = file.contents.toString();
platforms.forEach(function (platform) {
let platformContents = testTemplate({
contents: contents,
buildConfig: buildConfig,
relativePath: relativePath,
platform: platform
});
self.push(new VinylFile({
base: file.base,
contents: new Buffer(platformContents),
path: file.path.replace(/e2e.ts$/, platform + '.e2e.js')
}));
}); });
next(); return filtered;
}); }
} return entryPoints;
}); });
task('e2e.compileTests', (done: Function) => {
let folderInfo = getFolderInfo();
if (folderInfo.componentName && folderInfo.componentTest) {
buildTest(folderInfo);
} else {
buildAllTests(done);
}
});
function buildTest(folderInfo: any) {
let includeGlob = [`./dist/e2e/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/*.ts`];
let pathToWriteFile = `${DIST_E2E_ROOT}/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/tsconfig.json`;
createTempTsConfig(includeGlob, ES5, ES_2015, `${DEMOS_SRC_ROOT}/tsconfig.json`, pathToWriteFile);
let sassConfigPath = 'scripts/e2e/sass.config.js';
let appEntryPoint = `dist/e2e/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/main.ts`;
let appNgModule = `dist/e2e/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/app.module.ts`;
let distDir = `dist/e2e/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/`;
return runAppScripts(folderInfo, sassConfigPath, appEntryPoint, appNgModule, distDir);
} }
function buildAllTests(done: Function) { function getE2eTestFiles() {
let folders = getFolders('./dist/e2e/components'); return new Promise((resolve, reject) => {
let promises: Promise<any>[] = []; const mainGlob = join(SRC_COMPONENTS_ROOT, '*', 'test', '*', 'e2e.ts');
glob(mainGlob, (err: Error, matches: string[]) => {
folders.forEach(folder => { if (err) {
console.log(folder); return reject(err);
stat(`./dist/e2e/components/${folder}/test`, function(err, stat) {
if (err == null) {
let testFolders = getFolders(`./dist/e2e/components/${folder}/test`);
testFolders.forEach(test => {
console.log('build test for ', folder, test);
let folderInfo = {
componentName: folder,
componentTest: test
};
const promise = buildTest(folderInfo);
promises.push(promise);
});
} }
resolve(matches);
}); });
}); });
}
Promise.all(promises).then(() => {
function buildTests(filePaths: string[]) {
const functions = filePaths.map(filePath => () => {
return buildTest(filePath);
});
return pAll(functions, {concurrency: 8}).then(() => {
// copy over all of the protractor tests to the correct location now
return copyProtractorTestContent(filePaths);
});
}
function buildTest(filePath: string) {
const start = Date.now();
const ionicAngularDir = join(process.cwd(), 'src');
let appEntryPoint = filePath;
let srcTestRoot = dirname(dirname(appEntryPoint));
try {
// check if the entry point exists, otherwise fall back to the legacy entry point without 'app' folder
readFileSync(appEntryPoint);
} catch (ex) {
// the file doesn't exist, so use the legacy entry point
appEntryPoint = join(dirname(dirname(appEntryPoint)), 'main.ts');
srcTestRoot = dirname(appEntryPoint);
}
const relativePathFromComponents = relative(dirname(SRC_COMPONENTS_ROOT), srcTestRoot);
const distTestRoot = join(process.cwd(), 'dist', 'e2e', relativePathFromComponents);
const includeGlob = [ join(ionicAngularDir, '**', '*.ts')];
const pathToWriteFile = join(distTestRoot, 'tsconfig.json');
const pathToReadFile = join(PROJECT_ROOT, 'tsconfig.json');
createTempTsConfig(includeGlob, ES_2015, ES_2015, pathToReadFile, pathToWriteFile, { removeComments: true});
const sassConfigPath = join('scripts', 'e2e', 'sass.config.js');
const copyConfigPath = join('scripts', 'e2e', 'copy.config.js');
const appNgModulePath = join(dirname(appEntryPoint), 'app.module.ts');
const distDir = join(distTestRoot, 'www');
return runAppScriptsBuild(appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, sassConfigPath, copyConfigPath).then(() => {
const end = Date.now();
console.log(`${filePath} took a total of ${(end - start) / 1000} seconds to build`);
});
}
function copyProtractorTestContent(filePaths: string[]): Promise<any> {
const e2eTestPaths = filePaths.map(filePath => {
return join(dirname(dirname(filePath)), 'e2e.ts');
});
return readE2ETestFiles(e2eTestPaths)
.then((map: Map<string, string>) => {
return applyTemplate(map);
}).then((map: Map<string, string>) => {
writeE2EJsFiles(map);
});
}
function applyTemplate(filePathContent: Map<string, string>) {
const buildConfig = require('../../build/config');
const templateFileContent = readFileSync(join(SCRIPTS_ROOT, 'e2e', 'e2e.template.js'));
const templater = template(templateFileContent.toString());
const modifiedMap = new Map<string, string>();
const platforms = ['android', 'ios', 'windows'];
filePathContent.forEach((fileContent: string, filePath: string) => {
const srcRelativePath = relative(SRC_ROOT, dirname(filePath));
const wwwRelativePath = join(srcRelativePath, 'www');
platforms.forEach(platform => {
const platformContents = templater({
contents: fileContent,
buildConfig: buildConfig,
relativePath: wwwRelativePath,
platform: platform,
relativePathBackwardsCompatibility: dirname(wwwRelativePath)
});
const newFilePath = join(wwwRelativePath, `${platform}.e2e.js`);
modifiedMap.set(newFilePath, platformContents);
});
});
return modifiedMap;
}
function writeE2EJsFiles(map: Map<string, string>) {
const promises: Promise<any>[] = [];
map.forEach((fileContent: string, filePath: string) => {
const destination = join(process.cwd(), 'dist', 'e2e', filePath);
promises.push(writeFileAsync(destination, fileContent));
});
return Promise.all(promises);
}
function readE2ETestFiles(mainFilePaths: string[]): Promise<Map<string, string>> {
const e2eFiles = mainFilePaths.map(mainFilePath => {
return join(dirname(mainFilePath), 'e2e.ts');
});
const promises: Promise<any>[] = [];
const map = new Map<string, string>();
for (const e2eFile of e2eFiles) {
const promise = readE2EFile(e2eFile);
promises.push(promise);
promise.then((content: string) => {
map.set(e2eFile, content);
});
}
return Promise.all(promises).then(() => {
return map;
});
}
function readE2EFile(filePath: string) {
return readFileAsync(filePath).then((content: string) => {
// purge the import statement at the top
const purgeImportRegex = /.*?import.*?'protractor';/g;
return content.replace(purgeImportRegex, '');
});
}
task('e2e.clean', (done: Function) => {
// this is a super hack, but it works for now
if (argv.skipClean) {
return done();
}
del(['dist/e2e/**']).then(() => {
done(); done();
}).catch(err => { }).catch(err => {
done(err); done(err);
}); });
}
task('e2e.watchProd', (done: Function) => {
const folderInfo = getFolderInfo();
let e2eTestPath = SRC_ROOT;
if (folderInfo.componentName && folderInfo.componentTest) {
e2eTestPath = join(`${SRC_ROOT}/components/${folderInfo.componentName}/test/${folderInfo.componentTest}/app.module.ts`);
}
try {
accessSync(e2eTestPath, F_OK);
} catch (e) {
done(new Error(`Could not find e2e test: ${e2eTestPath}`));
return;
}
if (e2eComponentsExists(folderInfo)) {
// already generated the e2e directory
e2eWatch(folderInfo.componentName, folderInfo.componentTest);
} else {
// generate the e2e directory
console.log('Generate e2e builds first...');
e2eBuild(() => {
e2eWatch(folderInfo.componentName, folderInfo.componentTest);
});
}
}); });
function e2eWatch(componentName: string, componentTest: string) { task('e2e.polyfill', (done: Function) => {
// If any tests change within components then run e2e.resources. if (argv.skipPolyfill) {
watch([ return done();
'e2e/src/**/*' }
],
function (file) {
console.log('start e2e.resources - ' + JSON.stringify(file.history, null, 2));
start('e2e.copyAndCompile');
});
// If any src files change except for tests then transpile only the source ionic files writePolyfills('dist/e2e/polyfills').then(() => {
watch([ done();
'src/**/*.ts', }).catch(err => {
'!src/components/*/test/**/*', done(err);
'!src/util/test/*'
],
function (file) {
console.log('start e2e.ngcSource - ' + JSON.stringify(file.history, null, 2));
start('e2e.copyAndCompile');
});
// If any scss files change then recompile all sass
watch(['src/**/*.scss'], (file) => {
console.log('start sass - ' + JSON.stringify(file.history, null, 2));
start('e2e.sass');
}); });
});
let serverUrl = `http://localhost:${LOCAL_SERVER_PORT}/${DIST_NAME}/${E2E_NAME}`;
if (componentName) {
serverUrl += `/${componentName}`;
}
console.log(serverUrl);
start('e2e.serve');
}
function e2eComponentsExists(folderInfo: any): boolean {
let componentPath = `${DIST_E2E_ROOT}/components`;
if (folderInfo.componentName && folderInfo.componentTest) {
componentPath += `/${folderInfo.componentName}/test/${folderInfo.componentTest}/build`;
}
try {
accessSync(componentPath, F_OK);
} catch (e) {
return false;
}
return true;
}

View File

@ -1,55 +0,0 @@
import { dest, src, task } from 'gulp';
import * as connect from 'gulp-connect';
import * as del from 'del';
import * as runSequence from 'run-sequence';
import { DIST_E2E_ROOT, LOCAL_SERVER_PORT, SCRIPTS_ROOT } from '../constants';
import { compileSass, copyFonts, createTimestamp, setSassIonicVersion, writePolyfills } from '../util';
task('e2e.clean', (done: Function) => {
del(['dist/e2e/**']).then(() => {
done();
}).catch(err => {
done(err);
});
});
task('e2e.polyfill', (done: Function) => {
writePolyfills('dist/e2e/polyfills').then(() => {
done();
}).catch(err => {
done(err);
});
});
task('e2e.copyAndCompile', (done: (err: any) => void) => {
runSequence(
'e2e.copySource',
'e2e.compileTests',
'e2e.bundle',
done);
});
task('e2e.copyExternalDependencies', () => {
src([`${SCRIPTS_ROOT}/e2e/*.css`]).pipe(dest(`${DIST_E2E_ROOT}/css`));
});
task('e2e.sass', () => {
// ensure there is a version.scss file
setSassIonicVersion(`E2E-${createTimestamp()}`);
return compileSass(`${DIST_E2E_ROOT}/css`);
});
task('e2e.fonts', () => {
return copyFonts(`${DIST_E2E_ROOT}/fonts`);
});
task('e2e.serve', function() {
connect.server({
root: './',
port: LOCAL_SERVER_PORT,
livereload: {
port: 35700
}
});
});

View File

@ -7,27 +7,19 @@ import { task } from 'gulp';
import * as serveStatic from 'serve-static'; import * as serveStatic from 'serve-static';
import { argv } from 'yargs'; import { argv } from 'yargs';
import { DIST_E2E_ROOT, DIST_E2E_COMPONENTS_ROOT, PROJECT_ROOT, SCRIPTS_ROOT } from '../constants'; import { DIST_E2E_COMPONENTS_ROOT, PROJECT_ROOT, SCRIPTS_ROOT } from '../constants';
import { mergeObjects } from '../util'; import { mergeObjects } from '../util';
task('snapshot', ['e2e.prod'], (done: Function) => { task('snapshot', ['e2e.prod'], (done: Function) => {
snapshot(false, false, done); snapshot(false, done);
}); });
task('snapshot.skipBuild', ['e2e.sass'], (done: Function) => { task('snapshot.skipBuild', (done: Function) => {
snapshot(false, false, done); snapshot(false, done);
}); });
task('snapshot.dev', ['e2e'], (done: Function) => { function snapshot(quickMode: boolean, callback: Function) {
snapshot(false, true, done);
});
task('snapshot.quick', ['e2e.sass'], (done: Function) => {
snapshot(true, true, done);
});
function snapshot(quickMode: boolean, devMode: boolean, callback: Function) {
const snapshotConfig = require('../../snapshot/snapshot.config').config; const snapshotConfig = require('../../snapshot/snapshot.config').config;
const protractorConfigFile = resolve(SCRIPTS_ROOT, 'snapshot/protractor.config.js'); const protractorConfigFile = resolve(SCRIPTS_ROOT, 'snapshot/protractor.config.js');
@ -41,6 +33,7 @@ function snapshot(quickMode: boolean, devMode: boolean, callback: Function) {
let component = '*'; let component = '*';
let e2eSpecs = '*'; let e2eSpecs = '*';
const folderArg: string = argv.folder || argv.f; const folderArg: string = argv.folder || argv.f;
if (folderArg && folderArg.length) { if (folderArg && folderArg.length) {
const folderArgPaths = folderArg.split('/'); const folderArgPaths = folderArg.split('/');
@ -49,10 +42,10 @@ function snapshot(quickMode: boolean, devMode: boolean, callback: Function) {
e2eSpecs = folderArgPaths[1]; e2eSpecs = folderArgPaths[1];
} }
} }
var specs = join(DIST_E2E_COMPONENTS_ROOT, component, 'test', e2eSpecs, '*e2e.js');
if (devMode) specs = join(DIST_E2E_ROOT, component, e2eSpecs, '*e2e.js');
console.log('[snapshot] Running with', devMode ? 'Development' : 'Production', 'build'); const specs = join(DIST_E2E_COMPONENTS_ROOT, component, 'test', e2eSpecs, 'www', '*e2e.js');
console.log('[snapshot] Running with', 'Production', 'build');
console.log(`[snapshot] Specs: ${specs}`); console.log(`[snapshot] Specs: ${specs}`);
const testId = generateTestId(); const testId = generateTestId();
@ -71,7 +64,6 @@ function snapshot(quickMode: boolean, devMode: boolean, callback: Function) {
'--params.height=' + snapshotValues.params.height, '--params.height=' + snapshotValues.params.height,
'--params.test_id=' + snapshotValues.params.test_id, '--params.test_id=' + snapshotValues.params.test_id,
'--params.upload=' + snapshotValues.params.upload, '--params.upload=' + snapshotValues.params.upload,
'--params.dev=' + devMode,
'--specs=' + specs '--specs=' + specs
]; ];

View File

@ -6,6 +6,10 @@ task('test', ['test.assembleVendorJs', 'compile.karma'], (done: Function) => {
karmaTest(false, done); karmaTest(false, done);
}); });
task('test.fast', ['compile.karma'], (done: Function) => {
karmaTest(false, done);
});
task('test.watch', ['test.assembleVendorJs', 'compile.karma'], (done: Function) => { task('test.watch', ['test.assembleVendorJs', 'compile.karma'], (done: Function) => {
karmaTest(true, done); karmaTest(true, done);
}); });

View File

@ -1,8 +1,8 @@
import { spawnSync } from 'child_process'; import { spawn } from 'child_process';
import { NODE_MODULES_ROOT, SRC_ROOT } from './constants'; import { NODE_MODULES_ROOT, SRC_ROOT } from './constants';
import { src, dest } from 'gulp'; import { src, dest } from 'gulp';
import { join } from 'path'; import { dirname, join } from 'path';
import { readdirSync, readFileSync, statSync, writeFileSync } from 'fs'; import { ensureDirSync, readdirSync, readFile, readFileSync, statSync, writeFile, writeFileSync } from 'fs-extra';
import { rollup } from 'rollup'; import { rollup } from 'rollup';
import { Replacer } from 'strip-function'; import { Replacer } from 'strip-function';
import * as commonjs from 'rollup-plugin-commonjs'; import * as commonjs from 'rollup-plugin-commonjs';
@ -12,6 +12,8 @@ import * as through from 'through2';
import * as uglifyPlugin from 'rollup-plugin-uglify'; import * as uglifyPlugin from 'rollup-plugin-uglify';
import { argv } from 'yargs'; import { argv } from 'yargs';
import { runWorker } from './utils/app-scripts-worker-client';
// These packages lack of types. // These packages lack of types.
const resolveBin = require('resolve-bin'); const resolveBin = require('resolve-bin');
@ -39,7 +41,7 @@ function getRootTsConfig(pathToReadFile): any {
return tsConfig; return tsConfig;
} }
export function createTempTsConfig(includeGlob: string[], target: string, moduleType: string, pathToReadFile: string, pathToWriteFile: string): any { export function createTempTsConfig(includeGlob: string[], target: string, moduleType: string, pathToReadFile: string, pathToWriteFile: string, overrideCompileOptions: any = null): any {
let config = getRootTsConfig(pathToReadFile); let config = getRootTsConfig(pathToReadFile);
if (!config.compilerOptions) { if (!config.compilerOptions) {
config.compilerOptions = {}; config.compilerOptions = {};
@ -53,12 +55,20 @@ export function createTempTsConfig(includeGlob: string[], target: string, module
config.compilerOptions.target = target; config.compilerOptions.target = target;
} }
config.include = includeGlob; config.include = includeGlob;
if (overrideCompileOptions) {
config.compilerOptions = Object.assign(config.compilerOptions, overrideCompileOptions);
}
let json = JSON.stringify(config, null, 2); let json = JSON.stringify(config, null, 2);
const dirToCreate = dirname(pathToWriteFile);
ensureDirSync(dirToCreate);
writeFileSync(pathToWriteFile, json); writeFileSync(pathToWriteFile, json);
} }
function removeDebugStatements() { function removeDebugStatements() {
let replacer = new Replacer(['console.debug', 'assert', 'runInDev']); let replacer = new Replacer(['console.debug', 'console.time', 'console.timeEnd', 'assert', 'runInDev']);
return through.obj(function (file, encoding, callback) { return through.obj(function (file, encoding, callback) {
const content = file.contents.toString(); const content = file.contents.toString();
const cleanedJs = replacer.replace(content); const cleanedJs = replacer.replace(content);
@ -75,7 +85,7 @@ export function copySourceToDest(destinationPath: string, excludeSpecs: boolean,
glob.push(`${SRC_ROOT}/**/*.spec.ts`); glob.push(`${SRC_ROOT}/**/*.spec.ts`);
} }
if (excludeE2e) { if (excludeE2e) {
glob.push(`!${SRC_ROOT}/components/*/test/*/*.ts`); glob.push(`!${SRC_ROOT}/components/*/test/**/*.ts`);
} }
let stream = src(glob); let stream = src(glob);
if (stripDebug) { if (stripDebug) {
@ -178,41 +188,58 @@ export function runWebpack(pathToWebpackConfig: string, done: Function) {
}); });
} }
export function runAppScripts(folderInfo: any, sassConfigPath: string, appEntryPoint: string, appNgModulePath: string, distDir: string) { export function runAppScriptsServe(testOrDemoName: string, appEntryPoint: string, appNgModulePath: string, srcDir: string, distDir: string, tsConfig: string, ionicAngularDir: string, sassConfigPath: string, copyConfigPath: string, watchConfigPath: string) {
console.log('Running ionic-app-scripts build with', folderInfo.componentName, '/', folderInfo.componentTest); console.log('Running ionic-app-scripts serve with', testOrDemoName);
let tsConfig = distDir + 'tsconfig.json';
let scriptArgs = [ let scriptArgs = [
'build', 'serve',
'--sass', sassConfigPath,
'--appEntryPoint', appEntryPoint, '--appEntryPoint', appEntryPoint,
'--appNgModulePath', appNgModulePath, '--appNgModulePath', appNgModulePath,
'--srcDir', distDir, '--srcDir', srcDir,
'--wwwDir', distDir, '--wwwDir', distDir,
'--tsconfig', tsConfig '--tsconfig', tsConfig,
]; '--readConfigJson', 'false',
'--experimentalParseDeepLinks', 'true',
'--ionicAngularDir', ionicAngularDir,
'--sass', sassConfigPath,
'--copy', copyConfigPath
];
if (watchConfigPath) {
scriptArgs.push('--watch');
scriptArgs.push(watchConfigPath);
}
const debug: boolean = argv.debug; const debug: boolean = argv.debug;
if (debug) { if (debug) {
scriptArgs.push('--debug'); scriptArgs.push('--debug');
scriptArgs.push('--aot');
} else {
scriptArgs.push('--prod');
} }
try { return new Promise((resolve, reject) => {
console.log('$ node ./node_modules/.bin/ionic-app-scripts', scriptArgs.join(' ')); const args = ['./node_modules/.bin/ionic-app-scripts'].concat(scriptArgs);
const scriptsCmd = spawnSync('node', ['./node_modules/.bin/ionic-app-scripts'].concat(scriptArgs)); console.log(`node ${args.join(' ')}`);
const spawnedCommand = spawn('node', args);
if (scriptsCmd.status !== 0) { spawnedCommand.stdout.on('data', (buffer: Buffer) => {
console.log(scriptsCmd.stderr.toString()); console.log(buffer.toString());
return Promise.reject(scriptsCmd.stderr.toString()); });
}
console.log(scriptsCmd.output.toString()); spawnedCommand.stderr.on('data', (buffer: Buffer) => {
return Promise.resolve(); console.error(buffer.toString());
} catch (ex) { });
return Promise.reject(ex);
} spawnedCommand.on('close', (code: number) => {
if (code === 0) {
return resolve();
}
reject(new Error('App-scripts failed with non-zero status code'));
});
});
}
export function runAppScriptsBuild(appEntryPoint: string, appNgModulePath: string, srcDir: string, distDir: string, tsConfig: string, ionicAngularDir: string, sassConfigPath: string, copyConfigPath: string) {
const pathToAppScripts = join(NODE_MODULES_ROOT, '.bin', 'ionic-app-scripts');
const debug: boolean = argv.debug;
return runWorker(pathToAppScripts, debug, appEntryPoint, appNgModulePath, srcDir, distDir, tsConfig, ionicAngularDir, sassConfigPath, copyConfigPath);
} }
/** Resolves the path for a node package executable. */ /** Resolves the path for a node package executable. */
@ -302,7 +329,10 @@ function bundlePolyfill(pathsToIncludeInPolyfill: string[], outputPath: string)
}), }),
commonjs(), commonjs(),
uglifyPlugin() uglifyPlugin()
] ],
onwarn: () => {
return () => {};
}
}).then((bundle) => { }).then((bundle) => {
return bundle.write({ return bundle.write({
format: 'iife', format: 'iife',
@ -333,3 +363,25 @@ export function getFolders(dir) {
return statSync(join(dir, file)).isDirectory(); return statSync(join(dir, file)).isDirectory();
}); });
} }
export function readFileAsync(filePath: string) {
return new Promise((resolve, reject) => {
readFile(filePath, (err: Error, buffer: Buffer) => {
if (err) {
return reject(err);
}
return resolve(buffer.toString());
});
});
}
export function writeFileAsync(filePath: string, fileContent: string) {
return new Promise((resolve, reject) => {
writeFile(filePath, fileContent, (err: Error) => {
if (err) {
return reject(err);
}
return resolve();
});
});
}

View File

@ -0,0 +1,102 @@
import { fork, ChildProcess } from 'child_process';
import { join } from 'path';
import { MessageToWorker, WorkerProcess } from './interfaces';
export function runWorker(pathToAppScripts: string, debug: boolean, appEntryPoint: string, appNgModulePath: string, srcDir: string, distDir: string, tsConfig: string, ionicAngularDir: string, sassConfigPath: string, copyConfigPath: string) {
return new Promise((resolve, reject) => {
const msgToWorker: MessageToWorker = {
pathToAppScripts: pathToAppScripts,
appEntryPoint: appEntryPoint,
appNgModulePath: appNgModulePath,
debug: debug,
srcDir: srcDir,
distDir: distDir,
tsConfig: tsConfig,
ionicAngularDir: ionicAngularDir,
sassConfigPath: sassConfigPath,
copyConfigPath: copyConfigPath
};
const worker = <ChildProcess>createWorker(msgToWorker);
console.log(`Starting to build test ${appEntryPoint}`);
worker.on('error', (err: any) => {
console.error(`worker error, entrypoint: ${appEntryPoint}, pid: ${worker.pid}, error: ${err}`);
reject(err);
});
worker.on('exit', (code: number) => {
console.log(`Finished building test ${appEntryPoint}`);
if (code === 0) {
resolve(code);
} else {
reject(new Error(`${appEntryPoint} exited with non-zero status code`));
}
});
});
}
export function createWorker(msg: MessageToWorker): any {
for (var i = workers.length - 1; i >= 0; i--) {
if (workers[i].appEntryPoint === msg.appEntryPoint) {
try {
workers[i].worker.kill('SIGKILL');
} catch (e) {
console.log(`createWorker, kill('SIGKILL'): ${e}`);
} finally {
delete workers[i].worker;
workers.splice(i, 1);
}
}
}
try {
let scriptArgs = [
'build',
'--aot',
'--optimizejs',
'--appEntryPoint', msg.appEntryPoint,
'--appNgModulePath', msg.appNgModulePath,
'--srcDir', msg.srcDir,
'--wwwDir', msg.distDir,
'--tsconfig', msg.tsConfig,
'--readConfigJson', 'false',
'--experimentalParseDeepLinks', 'true',
'--experimentalManualTreeshaking', 'false',
'--experimentalPurgeDecorators', 'false',
'--ionicAngularDir', msg.ionicAngularDir,
'--sass', msg.sassConfigPath,
'--copy', msg.copyConfigPath,
'--enableLint', 'false',
// '--disableLogging', 'true'
];
if (msg.debug) {
scriptArgs.push('--debug');
}
const workerModule = join(process.cwd(), 'node_modules', '@ionic', 'app-scripts', 'bin', 'ionic-app-scripts.js');
const worker = fork(workerModule, scriptArgs, {
env: {
FORCE_COLOR: true,
npm_config_argv: process.env.npm_config_argv
}
});
workers.push({
appEntryPoint: msg.appEntryPoint,
worker: worker
});
return worker;
} catch (e) {
throw new Error(`unable to create worker-process: ${e.msg}`);
}
}
export const workers: WorkerProcess[] = [];

View File

@ -0,0 +1,18 @@
export interface WorkerProcess {
appEntryPoint: string;
worker: any;
};
export interface MessageToWorker {
pathToAppScripts: string;
debug: boolean;
appEntryPoint: string;
appNgModulePath: string;
srcDir: string;
distDir: string;
tsConfig: string;
ionicAngularDir: string;
sassConfigPath: string;
copyConfigPath: string;
};

View File

@ -8,7 +8,7 @@
"url": "https://github.com/driftyco/ionic.git" "url": "https://github.com/driftyco/ionic.git"
}, },
"license": "MIT", "license": "MIT",
"main": "umd/index.js", "main": "esm/index.js",
"module": "index.js", "module": "index.js",
"peerDependencies": { "peerDependencies": {
"@angular/common": "", "@angular/common": "",

View File

@ -115,10 +115,13 @@ var IonicSnapshot = function(options) {
var specIdString = '[' + (spec.id+1) + '/' + self.testData.total_specs + ']'; var specIdString = '[' + (spec.id+1) + '/' + self.testData.total_specs + ']';
self.testData.spec_index = spec.id; self.testData.spec_index = spec.id;
// console.log('spec.id: ', spec.id);
self.testData.highest_mismatch = self.highestMismatch; self.testData.highest_mismatch = self.highestMismatch;
self.testData.png_base64 = pngBase64; self.testData.png_base64 = pngBase64;
self.testData.description = spec.getFullName().replace('components/', '').replace('test/', ''); self.testData.description = spec.getFullName().replace('components/', '').replace('test/', '').replace('www', '');
self.testData.url = currentUrl.replace('dist', '').replace('components/', '').replace('test/', '').replace('&ionicanimate=false', ''); self.testData.url = currentUrl.replace('dist', '').replace('components/', '').replace('test/', '').replace('&ionicanimate=false', '').replace('www/', '');
//console.log('self.testData.description: ', self.testData.description);
//console.log('self.testData.url: ', self.testData.url);
pngBase64 = null; pngBase64 = null;
var requestDeferred = q.defer(); var requestDeferred = q.defer();

View File

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { $CLASSNAMEComponent } from './$FILENAME';
import { IonicModule } from 'ionic-angular';
@NgModule({
declarations: [
$CLASSNAMEComponent,
],
imports: [
IonicModule.forChild($CLASSNAMEComponent)
],
entryComponents: [
$CLASSNAMEComponent
],
providers: []
})
export class $CLASSNAMEComponentModule {}

View File

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { $CLASSNAMEPage } from './$FILENAME';
import { IonicModule } from 'ionic-angular';
@NgModule({
declarations: [
$CLASSNAMEPage,
],
imports: [
IonicModule.forChild($CLASSNAMEPage)
],
entryComponents: [
$CLASSNAMEPage
],
providers: []
})
export class $CLASSNAMEPageModule {}