chore(): begin adding ionic components to mono-repo.

This commit is contained in:
Josh Thomas
2017-06-21 09:33:06 -05:00
parent 1181fe98fc
commit bd5b67304d
2159 changed files with 15687 additions and 147 deletions

View File

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

View File

@ -0,0 +1,18 @@
import * as del from 'del';
import { task } from 'gulp';
task('clean', (done: Function) => {
del(['dist/**']).then(() => {
done();
}).catch(err => {
done(err);
});
});
task('clean.src', (done: Function) => {
del(['src/**/*.js', 'src/**/*.d.ts']).then(() => {
done();
}).catch(err => {
done(err);
});
});

View File

@ -0,0 +1,56 @@
import { PROJECT_ROOT } from '../constants';
import { task } from 'gulp';
import { accessSync } from 'fs';
import { join } from 'path';
task('core', (done) => {
buildAngularBinding(false, done);
});
task('core.watch', (done) => {
buildAngularBinding(true, done);
});
function buildAngularBinding(isDevAndWatch: boolean, done: Function) {
let hasRunDone = false;
const cwd = join(PROJECT_ROOT, '../ionic-core');
const args = [
'run',
'build.angular'
];
if (isDevAndWatch) {
args.push('--', 'dev', 'watch');
}
try {
accessSync(cwd);
} catch (e) {
console.log('core directory not found:', cwd);
process.exit(1);
}
const spawn = require('child_process').spawn;
const ls = spawn('npm', args, { cwd: cwd });
ls.stdout.on('data', (data) => {
console.log(data.toString().trim());
if (!hasRunDone && data.toString().trim().indexOf('compile, done') > -1) {
hasRunDone = true;
done();
}
});
ls.stderr.on('data', (data) => {
console.log(data.toString().trim());
});
ls.on('close', (code) => {
if (!hasRunDone) {
hasRunDone = true;
done();
}
});
}

View File

@ -0,0 +1,19 @@
import * as gulp from 'gulp';
import * as runSequence from 'run-sequence';
gulp.task('default', help);
gulp.task('help', help);
function help() {
const taskList = Object.keys((gulp as any).tasks)
.filter(taskName => taskName !== 'default' && taskName !== 'help')
.sort()
.map(taskName => taskName = ' ' + taskName);
console.log(taskList.join('\n'));
}
gulp.task('validate', (done: (err: any) => void) => {
runSequence('clean', ['lint', 'test'], done);
});

View File

@ -0,0 +1,46 @@
import { join } from 'path';
import { task } from 'gulp';
import { DEMOS_ROOT, DIST_DEMOS_ROOT, ES_2015, PROJECT_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, runAppScriptsServe } from '../util';
task('demos.watch', ['demos.prepare'], (done: Function) => {
const folderInfo = getFolderInfo();
if (!folderInfo || !folderInfo.componentName ) {
done(new Error(`Usage: gulp e2e.watch --folder modal`));
}
serveDemo(folderInfo.componentName).then(() => {
done();
}).catch((err: Error) => {
done(err);
});
});
function serveDemo(folderName: any) {
const testOrDemoName = folderName;
const ionicAngularDir = join(PROJECT_ROOT, 'src');
const coreCompilerFilePath = join(PROJECT_ROOT, 'dist', 'ionic-angular', 'compiler');
const coreDir = join(PROJECT_ROOT, 'dist', 'ionic-angular');
const srcTestRoot = join(DEMOS_ROOT, 'src', folderName);
const distDemoRoot = join(DIST_DEMOS_ROOT, folderName);
const includeGlob = [ join(ionicAngularDir, '**', '*.ts'),
join(srcTestRoot, '**', '*.ts')];
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(testOrDemoName, appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, coreCompilerFilePath, coreDir, sassConfigPath, copyConfigPath, watchConfigPath);
}

View File

@ -0,0 +1,229 @@
import { dirname, join, relative } from 'path';
import * as glob from 'glob';
import { task } from 'gulp';
import * as del from 'del';
import * as runSequence from 'run-sequence';
import * as s3 from 's3';
import { argv } from 'yargs';
import { DEMOS_SRC_ROOT, ES_2015, PROJECT_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, runAppScriptsBuild, writePolyfills } from '../util';
import * as pAll from 'p-all';
import * as dotenv from 'dotenv';
dotenv.config();
task('demos.prepare', (done: Function) => {
runSequence('demos.clean', 'demos.polyfill', 'demos.sass', (err: any) => done(err));
});
task('demos.prod', ['demos.prepare'], (done: Function) => {
// okay, first find out all of the demos tests to run by finding all of the 'main.ts' files
filterDemosEntryPoints().then((filePaths: string[]) => {
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;
});
}
function getDemosEntryPoints() {
return new Promise((resolve, reject) => {
const mainGlob = join(DEMOS_SRC_ROOT, '**', 'main.ts');
glob(mainGlob, (err: Error, matches: string[]) => {
if (err) {
return reject(err);
}
resolve(matches);
});
});
}
function buildDemos(filePaths: string[]) {
var batches = chunkArrayInGroups(filePaths, argv.batches || 1);
var batch = argv.batch || 0;
if(batch >= batches.length) {
throw new Error(`Batch number higher than total number of batches.`);
}
console.log(`Compiling ${batches[batch].length} of ${filePaths.length} Demos ...`);
const functions = batches[batch].map(filePath => () => {
return buildDemo(filePath);
});
let concurrentNumber = 2;
if (argv.concurrency) {
concurrentNumber = argv.concurrency;
}
return pAll(functions, {concurrency: concurrentNumber});
}
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);
const distTestRoot = join(process.cwd(), 'dist', 'demos', relativePathFromComponents);
const coreCompilerFilePath = join(PROJECT_ROOT, '..', 'ionic-core', 'dist', 'compiler');
const coreDir = join(PROJECT_ROOT, '..', 'ionic-core', 'dist', 'compiled-ionic-angular');
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,
coreCompilerFilePath,
coreDir,
distDir,
pathToWriteFile,
ionicAngularDir,
sassConfigPath,
copyConfigPath,
argv.dev
).then(() => {
const end = Date.now();
console.log(`${filePath} took a total of ${(end - start) / 1000} seconds to build`);
uploadToS3(pathToWriteFile);
});
}
function chunkArrayInGroups(arr, size) {
var result = [];
for(var i = 0; i < arr.length; i++) {
if (!Array.isArray(result[i % size])) {
result[i % size] = [];
}
result[i % size].push(arr[i]);
}
return result;
}
function uploadToS3(path) {
// fail silently if envars not present
if (!process.env.AWS_KEY || !process.env.AWS_SECRET) {
return new Promise((resolve) => {resolve();});
}
let client = s3.createClient({
s3Options: {
accessKeyId: process.env.AWS_KEY,
secretAccessKey: process.env.AWS_SECRET
},
});
// get demo name from path
let demo = path.split('/')[path.split('/').length - 2];
let params = {
localDir: path.replace('tsconfig.json',''),
deleteRemoved: true,
s3Params: {
Bucket: "ionic-demos",
Prefix: demo,
},
};
var uploader = client.uploadDir(params);
return new Promise((resolve, reject) => {
uploader.on('error', function(err) {
console.error("s3 Upload Error:", err.stack);
reject();
});
uploader.on('end', function() {
console.log(demo, " demo uploaded to s3");
resolve();
});
});
}
task('demos.download', (done: Function) => {
if (!process.env.AWS_KEY || !process.env.AWS_SECRET) {
return new Promise((resolve) => {resolve();});
}
let client = s3.createClient({
s3Options: {
accessKeyId: process.env.AWS_KEY,
secretAccessKey: process.env.AWS_SECRET
},
});
let params = {
localDir: join(process.cwd(), 'dist', 'demos', 'src'),
s3Params: {
Bucket: "ionic-demos",
},
};
let uploader = client.downloadDir(params);
return new Promise((resolve, reject) => {
uploader.on('error', function(err) {
console.error("s3 Download Error:", err.stack);
reject();
});
uploader.on('end', function() {
console.log("Demos downloaded from s3");
resolve();
});
});
})
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();
}).catch(err => {
done(err);
});
});
task('demos.polyfill', (done: Function) => {
if (argv.skipPolyfill) {
return done();
}
writePolyfills('dist/demos/polyfills').then(() => {
done();
}).catch(err => {
done(err);
});
});

View File

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

View File

@ -0,0 +1,153 @@
import { createReadStream, writeFileSync } from 'fs';
import { join, relative } from 'path';
import * as Dgeni from 'dgeni';
import { split, map } from 'event-stream';
import { src, dest, task } from 'gulp';
import { AllHtmlEntities } from 'html-entities';
import * as mkdirp from 'mkdirp';
import { valid }from 'semver';
import { argv } from 'yargs';
import { DIST_DEMOS_ROOT } from '../constants';
import { SITE_ROOT } from '../constants';
import { PROJECT_ROOT } from '../constants';
task('docs', ['docs.dgeni', 'docs.demos', 'docs.sassVariables']);
task('docs.dgeni', () => {
const docVersion = argv['doc-version'] || 'nightly';
const initialVersionBuild = argv['initial-build'] || false;
if (docVersion !== 'nightly' && ! valid(docVersion)) {
console.log('Usage: gulp docs --doc-version=(nightly|versionName)\nversionName must be a valid semver version.');
return process.exit(1);
}
try {
const ionicPackage = require('../../docs/dgeni-config')(docVersion, initialVersionBuild);
const dgeni = new Dgeni([ionicPackage]);
return dgeni.generate();
} catch (err) {
console.log(err.stack);
}
});
task('docs.demos', (done: Function) => {
// Copy demos already built from gulp demos.prod task to ionic-site
const config = require('../../config.json');
const outputDir = join(config.docsDest, 'demos');
let promises = [];
promises.push(copyDemoCss(join(outputDir, 'css')));
promises.push(copyDemoFonts(join(outputDir, 'fonts')));
promises.push(copyDemoPolyfills(join(outputDir, 'polyfills')));
promises.push(copyDemoContent(join(outputDir, 'src')));
Promise.all(promises).then(() => {
done();
}).catch(err => {
done(err);
});
});
function copyDemoCss(outputDir: string) {
return new Promise((resolve, reject) => {
const stream = src(`${DIST_DEMOS_ROOT}/css/*`).pipe(dest(outputDir));
stream.on('end', () => {
resolve();
});
});
}
function copyDemoFonts(outputDir: string) {
return new Promise((resolve, reject) => {
const stream = src(`${DIST_DEMOS_ROOT}/fonts/*`).pipe(dest(outputDir));
stream.on('end', () => {
resolve();
});
});
}
function copyDemoPolyfills(outputDir: string) {
return new Promise((resolve, reject) => {
const stream = src(`${DIST_DEMOS_ROOT}/polyfills/*`).pipe(dest(outputDir));
stream.on('end', () => {
resolve();
});
});
}
function copyDemoContent(outputDir: string) {
return new Promise((resolve, reject) => {
const stream = src([
`${DIST_DEMOS_ROOT}/src/**/*`
]).pipe(dest(outputDir));
stream.on('end', () => {
resolve();
});
});
}
task('docs.sassVariables', () => {
let variables = [];
const outputFile = 'tmp/sass.json';
function addVariable(variableName, defaultValue, file) {
const entities = new AllHtmlEntities();
defaultValue = entities.encode(defaultValue);
defaultValue = defaultValue.replace('!default;', '');
variables.push({
name: variableName,
defaultValue: defaultValue.trim(),
file: relative('./', file.path)
});
}
return src('./src/**/*.scss')
.pipe(map((file, callback) => {
let variableLine, variableName, defaultValue, multiline;
createReadStream(file.path, { flags: 'r'})
.pipe(split())
.pipe(map((line, callback) => {
if (line.charAt(0) === '$') {
variableLine = line.split(/:(.+)/);
variableName = variableLine[0];
defaultValue = variableLine[1];
// If there is a semicolon then it isn't a multiline value
multiline = line.indexOf(';') > -1 ? false : true;
if (!multiline && line.indexOf('!default') > -1) {
addVariable(variableName, defaultValue, file);
}
} else if (multiline === true) {
defaultValue += '\n' + line;
// If the line has a semicolon then we've found the end of the value
if (line.indexOf(';') > -1 && line.indexOf('!default') > -1) {
addVariable(variableName, defaultValue, file);
multiline = false;
}
}
callback();
}));
callback();
}).on('end', () => {
const config = require('../../config.json');
console.log(`Writing to file at /ionic-team/ionic/${outputFile}`);
console.log(`Place this file in /ionic-team/ionic-site/${config.v2DocsDir}/theming/overriding-ionic-variables in order to update the docs`);
mkdirp.sync('tmp');
writeFileSync(outputFile, JSON.stringify(variables));
}));
});
task('docs.homepageVersionUpdate', () => {
// This assumes you're currently releasing
const sourcePackageJSON = require(`${PROJECT_ROOT}/package.json`);
let now = new Date();
const frameworkInfo = JSON.stringify({
version: sourcePackageJSON.version,
date: now.toISOString().split('T')[0]
}, null, 2);
writeFileSync(`${SITE_ROOT}/server/data/framework-info.json`, frameworkInfo);
});

View File

@ -0,0 +1,53 @@
import { dirname, join } from 'path';
import { readFileSync } from 'fs';
import { task } from 'gulp';
import { ES_2015, PROJECT_ROOT } from '../constants';
import { createTempTsConfig, getFolderInfo, runAppScriptsServe } from '../util';
task('e2e.watch', ['e2e.prepare', 'core.watch'], (done: Function) => {
const folderInfo = getFolderInfo();
if (!folderInfo || !folderInfo.componentName || !folderInfo.componentTest) {
done(new Error(`Usage: gulp e2e.watch --folder nav/basic`));
return;
}
serveTest(folderInfo).then(() => {
done();
}).catch((err: Error) => {
done(err);
});
});
function serveTest(folderInfo: any) {
const testOrDemoName = join(folderInfo.componentName, folderInfo.componentTest);
const ionicAngularDir = join(PROJECT_ROOT, 'src');
const coreCompilerFilePath = join(PROJECT_ROOT, '..', 'ionic-core', 'dist', 'compiler');
const coreDir = join(PROJECT_ROOT, '..', 'ionic-core', 'dist', 'compiled-ionic-angular');
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');
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(testOrDemoName, appEntryPoint, appNgModulePath, ionicAngularDir, distDir, pathToWriteFile, ionicAngularDir, coreCompilerFilePath, coreDir, sassConfigPath, copyConfigPath, null);
}

View File

@ -0,0 +1,263 @@
import { spawn } from 'child_process';
import { accessSync, readFileSync, writeFileSync } from 'fs';
import { dirname, join, relative } from 'path';
import * as glob from 'glob';
import { task } from 'gulp';
import * as del from 'del';
import { template } from 'lodash';
import * as runSequence from 'run-sequence';
import { argv } from 'yargs';
import { DIST_E2E_COMPONENTS_ROOT, ES_2015, PROJECT_ROOT, SRC_ROOT, SRC_COMPONENTS_ROOT, SCRIPTS_ROOT } from '../constants';
import { createTempTsConfig, createTimestamp, getFolderInfo, readFileAsync, runAppScriptsBuild, writeFileAsync, writePolyfills } from '../util';
import * as pAll from 'p-all';
task('e2e.prepare', (done: Function) => {
runSequence('e2e.clean', 'e2e.polyfill', 'e2e.prepareSass', (err: any) => done(err));
});
task('e2e.prepareSass', (done: Function) => {
const version = `E2E-${createTimestamp()}`;
writeFileSync(join(SRC_ROOT, 'themes', 'version.scss'), `$ionic-version: "${version}";`);
done();
});
task('e2e.prod', ['e2e.prepare', 'core'], (done: Function) => {
// okay, first find out all of the e2e tests to run by finding all of the 'main.ts' files
filterE2eTestfiles().then((filePaths: string[]) => {
if (filePaths && filePaths.length > 0) {
console.log(`Compiling ${filePaths.length} E2E tests ...`);
return buildTests(filePaths);
}
}).then(() => {
done();
}).catch((err: Error) => {
done(err);
process.exit(1);
});
});
function e2eComponentExists(folderInfo: any): boolean {
let componentPath = join(SRC_COMPONENTS_ROOT, folderInfo.componentName, 'test', folderInfo.componentTest, 'app');
try {
accessSync(componentPath);
} catch (e) {
return false;
}
return true;
}
function filterE2eTestfiles() {
return getE2eTestFiles().then((filePaths: string[]) => {
const entryPoints = filePaths.map(filePath => {
const directoryName = dirname(filePath);
return join(directoryName, 'app', 'main.ts');
});
return entryPoints;
}).then((entryPoints: string[]) => {
const folderInfo = getFolderInfo();
if (folderInfo && folderInfo.componentName && folderInfo.componentTest) {
if (!e2eComponentExists(folderInfo)) {
console.log('Cannot find E2E test ', join(folderInfo.componentName, 'test', folderInfo.componentTest), '. Make sure that the test exists and you are passing the correct folder.');
return [];
}
const filtered = entryPoints.filter(entryPoint => {
return entryPoint.indexOf(join(folderInfo.componentName, 'test', folderInfo.componentTest)) >= 0;
});
return filtered;
}
return entryPoints;
});
}
function getE2eTestFiles() {
return new Promise((resolve, reject) => {
const mainGlob = join(SRC_COMPONENTS_ROOT, '*', 'test', '*', 'e2e.ts');
glob(mainGlob, (err: Error, matches: string[]) => {
if (err) {
return reject(err);
}
resolve(matches);
});
});
}
function buildTests(filePaths: string[]) {
const functions = filePaths.map(filePath => () => {
return buildTest(filePath);
});
// Run 2 tests at a time unless the `concurrency` arg is passed
let concurrentNumber = 2;
if (argv.concurrency) {
concurrentNumber = argv.concurrency;
}
return pAll(functions, {concurrency: concurrentNumber}).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 coreCompilerFilePath = join(PROJECT_ROOT, '..', 'ionic-core', 'dist', 'compiler');
const coreDir = join(PROJECT_ROOT, '..', 'ionic-core', 'dist', 'compiled-ionic-angular');
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, coreCompilerFilePath, coreDir, sassConfigPath, copyConfigPath, argv.dev).then(() => {
const end = Date.now();
console.log(`${filePath} took a total of ${(end - start) / 1000} seconds to build`);
}).catch((err) => {
console.log(`${err}`);
});
}
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(join('..', '..', '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();
}).catch(err => {
done(err);
});
});
task('e2e.polyfill', (done: Function) => {
if (argv.skipPolyfill) {
return done();
}
writePolyfills(join('dist', 'e2e', 'polyfills')).then(() => {
done();
}).catch(err => {
done(err);
});
});
task('e2e.openProd', (done: Function) => {
runSequence('e2e.prod', 'e2e.open', (err: any) => done(err));
});
task('e2e.open', (done: Function) => {
const folderInfo = getFolderInfo();
if (folderInfo && folderInfo.componentName && folderInfo.componentTest) {
const filePath = join(DIST_E2E_COMPONENTS_ROOT, folderInfo.componentName, 'test', folderInfo.componentTest, 'www', 'index.html');
const spawnedCommand = spawn('open', [filePath]);
spawnedCommand.on('close', (code: number) => {
done();
});
} else {
console.log(`Can't open without folder argument.`);
}
});

View File

@ -0,0 +1,25 @@
import { task, src } from 'gulp';
import * as scsslint from 'gulp-scss-lint';
import * as tslint from 'gulp-tslint';
task('lint', ['lint.sass', 'lint.ts']);
task('lint.ts', () => {
return src([
'src/**/*.ts'
]).pipe(tslint({
formatter: 'verbose'
}))
.pipe(tslint.report());
});
task('lint.sass', function() {
return src([
'src/**/*.scss',
'!src/components/*/test/**/*',
'!src/util/test/*',
'!src/themes/normalize.scss',
])
.pipe(scsslint())
.pipe(scsslint.failReporter());
});

View File

@ -0,0 +1,12 @@
import { task } from 'gulp';
import { writePolyfills } from '../util';
import { join } from 'path';
task('src.polyfill', (done: Function) => {
writePolyfills(join('scripts', 'polyfills')).then(() => {
done();
}).catch(err => {
done(err);
});
});

View File

@ -0,0 +1,18 @@
import { task, src, dest } from 'gulp';
import { writePolyfills } from '../util';
import { join } from 'path';
task('polyfill', ['polyfill.copy-readme', 'polyfill.write']);
task('polyfill.write', (done: Function) => {
writePolyfills(join('dist', 'ionic-angular', 'polyfills')).then(() => {
done();
}).catch(err => {
done(err);
});
});
task('polyfill.copy-readme', (done: Function) => {
return src(join('scripts', 'polyfill', 'readme.md'))
.pipe(dest(join('dist', 'ionic-angular', 'polyfills')), done);
});

View File

@ -0,0 +1,330 @@
import { exec, spawnSync, spawn } from 'child_process';
import { writeFileSync } from 'fs';
import * as changelog from 'conventional-changelog';
import * as GithubApi from 'github';
import { dest, src, start, task } from 'gulp';
import { prompt } from 'inquirer';
import { rollup } from 'rollup';
import * as commonjs from 'rollup-plugin-commonjs';
import * as nodeResolve from 'rollup-plugin-node-resolve';
import * as runSequence from 'run-sequence';
import * as semver from 'semver';
import { obj } from 'through2';
import { DIST_BUILD_UMD_BUNDLE_ENTRYPOINT, DIST_BUILD_ROOT, DIST_BUNDLE_ROOT, PROJECT_ROOT, SCRIPTS_ROOT, SRC_ROOT } from '../constants';
import { compileSass, copyFonts, createTimestamp, setSassIonicVersion, writePolyfills } from '../util';
var promptAnswers;
// Nightly: releases a nightly version
task('nightly', (done: (err: any) => void) => {
runSequence('release.pullLatest',
'validate',
'release.prepareReleasePackage',
'release.publishNightly',
done);
});
// Release: prompt, update, publish
task('release', (done: (err: any) => void) => {
runSequence('release.pullLatest',
'validate',
'release.prepareReleasePackage',
'release.promptVersion',
'release.update',
'release.publish',
done);
});
// Release.test: prompt and update
task('release.test', (done: (err: any) => void) => {
runSequence('validate',
'release.prepareReleasePackage',
'release.promptVersion',
'release.update',
done);
});
// Release.update: update package.json and changelog
task('release.update', (done: (err: any) => void) => {
if (promptAnswers.confirmRelease === 'yes') {
runSequence('release.copyProdVersion',
'release.prepareChangelog',
done);
} else {
console.log('Did not run release.update tasks, aborted release');
done(null);
}
});
// Release.publish: publish to GitHub and npm
task('release.publish', (done: (err: any) => void) => {
if (promptAnswers.confirmRelease === 'yes') {
runSequence('release.publishNpmRelease',
'release.publishGithubRelease',
done);
} else {
console.log('Did not run release.publish tasks, aborted release');
done(null);
}
});
task('release.publishGithubRelease', (done: Function) => {
const packageJSON = require('../../../package.json');
const github = new GithubApi({
version: '3.0.0'
});
github.authenticate({
type: 'oauth',
token: process.env.GH_TOKEN
});
return changelog({
preset: 'angular'
})
.pipe(obj(function(file, enc, cb){
github.releases.createRelease({
owner: 'ionic-team',
repo: 'ionic',
target_commitish: 'master',
tag_name: 'v' + packageJSON.version,
name: packageJSON.version,
body: file.toString(),
prerelease: false
}, done);
}));
});
task('release.publishNpmRelease', (done: Function) => {
const npmCmd = spawn('npm', ['publish', DIST_BUILD_ROOT]);
npmCmd.stdout.on('data', function (data) {
console.log(data.toString());
});
npmCmd.stderr.on('data', function (data) {
console.log('npm err: ' + data.toString());
});
npmCmd.on('close', function() {
done();
});
});
task('release.promptVersion', (done: Function) => {
prompt([
{
type: 'list',
name: 'release',
message: 'What type of release is this?',
choices: [
{
name: 'Major: Incompatible API changes',
value: 'major'
}, {
name: 'Minor: Backwards-compatible functionality',
value: 'minor'
}, {
name: 'Patch: Backwards-compatible bug fixes',
value: 'patch'
}, {
name: 'Premajor',
value: 'premajor'
}, {
name: 'Preminor',
value: 'preminor'
}, {
name: 'Prepatch',
value: 'prepatch'
}, {
name: 'Prerelease',
value: 'prerelease'
}
]
}, {
type: 'list',
name: 'confirmRelease',
default: 'no',
choices: [
{
name: 'Yes',
value: 'yes'
}, {
name: 'Abort release',
value: 'no'
}
],
message: function(answers) {
var SEP = '---------------------------------';
console.log('\n' + SEP + '\n' + getVersion(answers) + '\n' + SEP + '\n');
return 'Are you sure you want to proceed with the release version above?';
}
}
]).then(function (answers) {
// Continue with the release if version was confirmed
promptAnswers = answers;
done();
});
});
function getVersion(answers) {
const sourcePackageJSON = require(`${PROJECT_ROOT}/package.json`);
return semver.inc(sourcePackageJSON.version, answers.release, true);
}
task('release.copyProdVersion', () => {
// Increment the version and update the source package file
const sourcePackageJSON = require(`${PROJECT_ROOT}/package.json`);
sourcePackageJSON.version = semver.inc(sourcePackageJSON.version, promptAnswers.release, true);
const sourcePrettyPrintedJson = JSON.stringify(sourcePackageJSON, null, 2);
writeFileSync(`${PROJECT_ROOT}/package.json`, sourcePrettyPrintedJson);
// Copy the source package version and update it in the build package file
const packageJsonToUpdate = require(`${DIST_BUILD_ROOT}/package.json`);
packageJsonToUpdate.version = sourcePackageJSON.version;
const prettyPrintedJson = JSON.stringify(packageJsonToUpdate, null, 2);
writeFileSync(`${DIST_BUILD_ROOT}/package.json`, prettyPrintedJson);
});
task('release.prepareReleasePackage', (done: (err: any) => void) => {
runSequence('clean',
'core',
'release.polyfill',
'compile.release',
'release.copyTemplates',
'release.copyNpmInfo',
'release.preparePackageJsonTemplate',
'release.nightlyPackageJson',
'release.compileSass',
'release.fonts',
'release.sass',
'release.createUmdBundle',
done);
});
task('release.createUmdBundle', (done: Function) => {
return rollup({
entry: DIST_BUILD_UMD_BUNDLE_ENTRYPOINT,
plugins: [
nodeResolve({
module: true,
jsnext: true,
main: true
}),
commonjs()
]
}).then((bundle) => {
return bundle.write({
format: 'umd',
moduleName: 'ionicBundle',
dest: `${DIST_BUNDLE_ROOT}/ionic.umd.js`
});
});
});
task('release.polyfill', (done: Function) => {
writePolyfills('dist/ionic-angular/polyfills').then(() => {
done();
}).catch(err => {
done(err);
});
});
task('release.publishNightly', (done: Function) => {
const npmCmd = spawn('npm', ['publish', '--tag=nightly', DIST_BUILD_ROOT]);
npmCmd.stdout.on('data', function (data) {
console.log(data.toString());
});
npmCmd.stderr.on('data', function (data) {
console.log('npm err: ' + data.toString());
});
npmCmd.on('close', function() {
done();
});
});
task('release.compileSass', () => {
return compileSass(`${DIST_BUILD_ROOT}/css`);
});
task('release.fonts', () => {
return copyFonts(`${DIST_BUILD_ROOT}/fonts`);
});
task('release.sass', () => {
return src([`${SRC_ROOT}/**/*.scss`, `!${SRC_ROOT}/components/*/test/**/*`, `!${SRC_ROOT}/util/test/*`]).pipe(dest(`${DIST_BUILD_ROOT}`));
});
task('release.pullLatest', (done: Function) => {
exec('git status --porcelain', (err: Error, stdOut: string) => {
if (err) {
done(err);
} else if ( stdOut && stdOut.length > 0) {
done(new Error('There are uncommited changes. Please commit or stash changes.'));
} else {
const gitPullResult = spawnSync('git', ['pull', 'origin', 'master']);
if (gitPullResult.status !== 0) {
done(new Error('Error running git pull'));
}
done();
}
});
});
task('release.prepareChangelog', () => {
const changelog = require('gulp-conventional-changelog');
return src(`${PROJECT_ROOT}/CHANGELOG.md`)
.pipe(changelog({
preset: 'angular'
}))
.pipe(dest(`${PROJECT_ROOT}`));
});
task('release.copyTemplates', () => {
return src([`${SCRIPTS_ROOT}/templates/**/*`]).pipe(dest(`${DIST_BUILD_ROOT}/templates`));
});
task('release.copyNpmInfo', () => {
return src([`${PROJECT_ROOT}/scripts/npm/.npmignore`, `${PROJECT_ROOT}/scripts/npm/README.md`]).pipe(dest(DIST_BUILD_ROOT));
});
task('release.preparePackageJsonTemplate', () => {
let templatePackageJSON = require(`${PROJECT_ROOT}/scripts/npm/package.json`);
const sourcePackageJSON = require(`${PROJECT_ROOT}/package.json`);
// copy source package.json data to template
templatePackageJSON.version = sourcePackageJSON.version;
templatePackageJSON.description = sourcePackageJSON.description;
templatePackageJSON.keywords = sourcePackageJSON.keywords;
// copy source dependencies versions to the template's peerDependencies
// only copy dependencies that show up as peerDependencies in the template
for (let dependency in sourcePackageJSON.dependencies) {
// if the dependency is in both, AND the value of the entry is empty, copy it over
if (dependency in templatePackageJSON.peerDependencies && templatePackageJSON.peerDependencies[dependency] === '') {
templatePackageJSON.peerDependencies[dependency] = sourcePackageJSON.dependencies[dependency];
}
}
writeFileSync(`${DIST_BUILD_ROOT}` + '/package.json', JSON.stringify(templatePackageJSON, null, 2));
});
task('release.nightlyPackageJson', () => {
const packageJson: any = require(`${DIST_BUILD_ROOT}/package.json`);
packageJson.version = packageJson.version.split('-')
.slice(0, 2)
.concat(createTimestamp())
.join('-');
writeFileSync(`${DIST_BUILD_ROOT}/package.json`, JSON.stringify(packageJson, null, 2));
setSassIonicVersion(packageJson.version);
});

View File

@ -0,0 +1,119 @@
import { spawn } from 'child_process';
import { createServer } from 'http';
import { join, resolve } from 'path';
import * as connect from 'connect';
import { task } from 'gulp';
import * as serveStatic from 'serve-static';
import { argv } from 'yargs';
import { DIST_E2E_COMPONENTS_ROOT, PROJECT_ROOT, SCRIPTS_ROOT } from '../constants';
import { mergeObjects } from '../util';
task('snapshot', ['e2e.prod'], (done: Function) => {
snapshot(false, done);
});
task('snapshot.skipBuild', (done: Function) => {
snapshot(false, done);
});
function snapshot(quickMode: boolean, callback: Function) {
const snapshotConfig = require('../../snapshot/snapshot.config').config;
const protractorConfigFile = resolve(SCRIPTS_ROOT, 'snapshot/protractor.config.js');
const snapshotDefaults = snapshotConfig.platformDefaults || {};
const snapshotValues: any = mergeObjects(snapshotDefaults, argv || {});
if (!snapshotConfig.accessKey || !snapshotConfig.accessKey.length) {
console.error('Missing IONIC_SNAPSHOT_KEY environment variable');
return callback(new Error('Missing IONIC_SNAPSHOT_KEY environment variable'));
}
let component = '*';
let e2eSpecs = '*';
const folderArg: string = argv.folder || argv.f;
if (folderArg && folderArg.length) {
const folderArgPaths = folderArg.split('/');
component = folderArgPaths[0];
if (folderArgPaths.length > 1) {
e2eSpecs = folderArgPaths[1];
}
}
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}`);
const testId = generateTestId();
console.log(`[snapshot] TestId: ${testId}`);
snapshotValues.params.test_id = testId;
snapshotValues.params.upload = !quickMode;
var protractorArgs = [
'--browser ' + snapshotValues.browser,
'--platform ' + snapshotValues.platform,
'--params.platform_id=' + snapshotValues.params.platform_id,
'--params.platform_index=' + snapshotValues.params.platform_index,
'--params.platform_count=' + snapshotValues.params.platform_count,
'--params.width=' + snapshotValues.params.width,
'--params.height=' + snapshotValues.params.height,
'--params.test_id=' + snapshotValues.params.test_id,
'--params.upload=' + snapshotValues.params.upload,
'--specs=' + specs
];
return protractor(callback, [protractorConfigFile].concat(protractorArgs), testId);
}
function protractor(callback, args, testId: string) {
const buildConfig = require('../../build/config');
const app = connect().use(serveStatic(PROJECT_ROOT));
const protractorHttpServer = createServer(app).listen(buildConfig.protractorPort);
console.log(`Serving ${process.cwd()} on http://localhost:${buildConfig.protractorPort}`);
let spawnCommand = process.platform === 'win32' ? 'protractor.cmd' : 'protractor';
const child = spawn(spawnCommand, args, {
stdio: [process.stdin, process.stdout, 'pipe']
});
let errored = false;
let callbackCalled = false;
child.stderr.on('data', function(data) {
protractorHttpServer.close();
console.error(data.toString());
if (!errored) {
errored = true;
if (!callbackCalled) {
callback('Protractor tests failed.');
callbackCalled = true;
}
}
});
child.on('exit', function() {
protractorHttpServer.close();
if (!callbackCalled) {
console.log(`[snapshot] TestId: ${testId}`);
callback();
callbackCalled = true;
}
});
}
function generateTestId() {
let chars = 'abcdefghjkmnpqrstuvwxyz';
let id = chars.charAt(Math.floor(Math.random() * chars.length));
chars += '0123456789';
while (id.length < 3) {
id += chars.charAt(Math.floor(Math.random() * chars.length));
}
return id;
}

View File

@ -0,0 +1,130 @@
import { join } from 'path';
import { dest, src, task } from 'gulp';
import { DIST_VENDOR_ROOT, NPM_VENDOR_FILES, PROJECT_ROOT, SCRIPTS_ROOT } from '../constants';
task('test', ['test.assembleVendorJs', 'compile.karma'], (done: Function) => {
karmaTest(false, done);
});
task('test.fast', ['compile.karma'], (done: Function) => {
karmaTest(false, done);
});
task('test.watch', ['test.assembleVendorJs', 'compile.karma'], (done: Function) => {
karmaTest(true, done);
});
task('test.coverage', ['test.assembleVendorJs', 'compile.karma'], (done: Function) => {
karmaTest(false, () => {
createKarmaCoverageReport(done);
});
});
task('test.imageserver', () => {
const http = require('http');
const url = require('url');
const port = 8900;
const requestedUrls = [];
let start = Date.now();
function handleRequest(req, res) {
const urlParse = url.parse(req.url, true);
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET');
res.setHeader('Connection', 'keep-alive');
res.setHeader('Age', '0');
res.setHeader('cache-control', 'no-store');
if (urlParse.pathname === '/reset') {
console.log('Image Server Reset');
console.log('---------------------------');
requestedUrls.length = 0;
start = Date.now();
res.setHeader('Content-Type', 'text/plain');
res.end('reset');
return;
}
const delay = urlParse.query.d || 1000;
const id = urlParse.query.id || Math.round(Math.random() * 1000);
const width = urlParse.query.w || 80;
const height = urlParse.query.h || 80;
const color = urlParse.query.c || 'yellow';
requestedUrls.push(req.url);
console.log(`id: ${id}, requested: ${requestedUrls.filter(f => f === req.url).length}, timestamp: ${Date.now() - start}`);
setTimeout(() => {
res.setHeader('Content-Type', 'image/svg+xml');
res.end(`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 ${width} ${height}" style="background-color: ${color};">
<text x="5" y="22" style="font-family: Courier; font-size: 24px">${id}</text>
</svg>`);
}, delay);
}
http.globalAgent.maxSockets = 1;
http.createServer(handleRequest).listen(port, () => {
console.log(` Mock image server listening on: http://localhost:${port}/?d=2000&id=99`);
console.log(` Possible querystrings:`);
console.log(` id: the text to go in the svg image, defaults to a random number`);
console.log(` d: how many milliseconds it should take to respond, defaults to 1000`);
console.log(` w: image width, defaults to 80`);
console.log(` h: image height, defaults to 80`);
console.log(` c: image color, defaults to yellow`);
});
});
function karmaTest(watch: boolean, done: Function) {
const karma = require('karma');
const argv = require('yargs').argv;
let karmaConfig = {
configFile: join(SCRIPTS_ROOT, 'karma/karma.conf.js'),
singleRun: true,
};
if (watch) {
(karmaConfig as any).singleRun = false;
}
if (argv.testGrep) {
(<any>karmaConfig).client = {
args: ['--grep', argv.testGrep]
};
}
if (typeof argv.debug !== 'undefined') {
karmaConfig.singleRun = false;
}
new karma.Server(karmaConfig, done).start();
}
task('test.assembleVendorJs', () => {
const files = NPM_VENDOR_FILES.map((root) => {
const glob = join(root, '**/*.+(js|js.map)');
return src(join('node_modules', glob))
.pipe(dest(join(DIST_VENDOR_ROOT, root)));
});
const gulpMerge = require('merge2');
return gulpMerge(files);
});
/* creates a karma code coverage report */
function createKarmaCoverageReport(done: Function) {
console.log('Generating Unit Test Coverage Report...');
let exec = require('child_process').exec;
let command = `node_modules/.bin/remap-istanbul -i coverage/coverage-final.json -o coverage -t html`;
exec(command, function(err: any, stdout: any, stderr: any) {
console.log(`file://${PROJECT_ROOT}/coverage/index.html`);
done(err);
});
}

View File

@ -0,0 +1,115 @@
import { task } from 'gulp';
import { SRC_ROOT, SRC_COMPONENTS_ROOT } from '../constants';
import * as path from 'path';
import * as fs from 'fs';
task('theme', (done: () => void) => {
let opts: GenerateThemeOptions = {
src: path.join(SRC_COMPONENTS_ROOT),
dest: path.join(SRC_ROOT, 'ionic-generate.scss')
};
generateThemeSource(opts);
});
export function generateThemeSource(opts: GenerateThemeOptions) {
console.log(`[theme] src: ${opts.src}`);
console.log(`[theme] desc: ${opts.dest}`);
let components = getSourceComponents(opts);
generateManifest(opts, components);
}
function generateManifest(opts: GenerateThemeOptions, components: Component[]) {
components.forEach(c => {
console.log(c.name);
c.modes.forEach(m => {
console.log(` ${m.mode} ${m.src}`);
});
});
}
function getSourceComponents(opts: GenerateThemeOptions) {
let components: Component[] = [];
function readFiles(src: string, fillFiles: string[]) {
fs.readdirSync(src).forEach((file, index) => {
var filePath = path.join(src, file);
var fsStats = fs.statSync(filePath);
if (fsStats.isDirectory()) {
readFiles(filePath, fillFiles);
} else if (fsStats.isFile()) {
fillFiles.push(filePath);
}
});
}
let files: string[] = [];
readFiles(opts.src, files);
files = files.filter(f => f.slice(-5) === '.scss');
files.sort();
files.forEach(f => {
var componentRoot = f.replace(opts.src + '/', '');
var fileSplit = componentRoot.split('/');
var componentName = fileSplit[0];
var fileName = fileSplit[1];
var component = components.find(c => c.name === componentName);
if (!component) {
component = {
name: componentName,
modes: []
};
components.push(component);
}
fileSplit = fileName.split('.');
if (fileSplit.length === 3) {
component.modes.push({
src: f,
mode: fileSplit[1]
});
} else {
component.modes.unshift({
src: f,
mode: DEFAULT_MODE
});
}
});
console.log(`[theme] components: ${components.length}`);
return components;
}
export interface GenerateThemeOptions {
src: string;
dest: string;
includeModes?: string[];
excludeModes?: string[];
includeComponents?: string[];
excludeComponents?: string[];
}
export interface Component {
name: string;
modes: FileDetails[];
}
export interface FileDetails {
src: string;
mode: string;
}
const DEFAULT_MODE = '*';