From 99922ce127fde7c29aa3b8d9f69c229ccd180c78 Mon Sep 17 00:00:00 2001 From: Josh Thomas Date: Thu, 8 Dec 2016 13:52:19 -0600 Subject: [PATCH] feat(build): replace --dev flag with --prod and add flags --aot, --minifyJs, --minifyCss, --optimizeJs --- src/build.ts | 71 +++------------------- src/rollup/ionic-rollup-resolver-plugin.ts | 2 +- src/spec/build.spec.ts | 70 +++++++++++++++++++++ src/spec/config.spec.ts | 65 ++++++-------------- src/transpile-worker.ts | 1 - src/transpile.ts | 5 +- src/util/config.ts | 63 ++++++++----------- src/util/interfaces.ts | 4 ++ src/watch.ts | 14 +++-- src/worker-client.ts | 4 ++ 10 files changed, 142 insertions(+), 157 deletions(-) create mode 100644 src/spec/build.spec.ts diff --git a/src/build.ts b/src/build.ts index 3b1e924e..82c3760f 100644 --- a/src/build.ts +++ b/src/build.ts @@ -48,13 +48,7 @@ function buildWorker(context: BuildContext) { // load any 100% required files to ensure they exist return validateRequiredFilesExist(); }).then(() => { - if (context.isProd) { - // production build - return buildProd(context); - } - - // dev build - return buildDev(context); + return buildProject(context); }); } @@ -64,34 +58,22 @@ function validateRequiredFilesExist() { return readFileAsync(process.env.IONIC_APP_ENTRY_POINT_PATH); } -function buildProd(context: BuildContext) { - // sync empty the www/build directory - clean(context); - - buildId++; - - // async tasks - // these can happen all while other tasks are running - const copyPromise = copy(context); +function buildProject(context: BuildContext) { + var compilePromise = (context.runAot) ? ngc(context) : transpile(context); - // kick off ngc to run the Ahead of Time compiler - return ngc(context) + return compilePromise .then(() => { - // ngc has finished, now let's bundle it all together return bundle(context); }) .then(() => { - // js minify can kick off right away - const jsPromise = minifyJs(context); - - // sass needs to finish, then css minify can run when sass is done + const minPromise = (context.runMinifyJs) ? minifyJs(context) : Promise.resolve(); const sassPromise = sass(context) .then(() => { - return minifyCss(context); + return (context.runMinifyCss) ? minifyCss(context) : Promise.resolve() }); return Promise.all([ - jsPromise, + minPromise, sassPromise ]); }) @@ -99,51 +81,12 @@ function buildProd(context: BuildContext) { // kick off the tslint after everything else // nothing needs to wait on its completion lint(context); - - // ensure the async tasks have fully completed before resolving - return Promise.all([ - copyPromise - ]); }) .catch(err => { throw new BuildError(err); }); } - -function buildDev(context: BuildContext) { - // sync empty the www/build directory - clean(context); - - buildId++; - - // async tasks - // these can happen all while other tasks are running - const copyPromise = copy(context); - - // just bundle, and if that passes then do the rest at the same time - return transpile(context) - .then(() => { - return bundle(context); - }) - .then(() => { - return Promise.all([ - sass(context), - copyPromise - ]); - }) - .then(() => { - // kick off the tslint after everything else - // nothing needs to wait on its completion - lint(context); - return Promise.resolve(); - }) - .catch(err => { - throw new BuildError(err); - }); -} - - export function buildUpdate(changedFiles: ChangedFile[], context: BuildContext) { return new Promise(resolve => { const logger = new Logger('build'); diff --git a/src/rollup/ionic-rollup-resolver-plugin.ts b/src/rollup/ionic-rollup-resolver-plugin.ts index 236ab986..fe0d55f8 100644 --- a/src/rollup/ionic-rollup-resolver-plugin.ts +++ b/src/rollup/ionic-rollup-resolver-plugin.ts @@ -57,7 +57,7 @@ export function ionicRollupResolverPlugin(context: BuildContext) { } // remove decorators if prod build - if (context.isProd) { + if (context.optimizeJs) { file.content = optimizeJavascript(jsSourcePath, file.content); } diff --git a/src/spec/build.spec.ts b/src/spec/build.spec.ts new file mode 100644 index 00000000..4860e5d3 --- /dev/null +++ b/src/spec/build.spec.ts @@ -0,0 +1,70 @@ +import { BuildContext } from '../util/interfaces'; + +import * as build from '../build'; +import * as bundle from '../bundle'; +import * as copy from '../copy'; +import * as minify from '../minify'; +import * as lint from '../lint'; +import * as ngc from '../ngc'; +import * as sass from '../sass'; +import * as transpile from '../transpile'; + +describe('build', () => { + beforeEach(() => { + spyOn(copy, 'copy').and.returnValue(Promise.resolve()); + spyOn(ngc, 'ngc').and.returnValue(Promise.resolve()); + spyOn(bundle, 'bundle').and.returnValue(Promise.resolve()); + spyOn(minify, 'minifyJs').and.returnValue(Promise.resolve()); + spyOn(sass, 'sass').and.returnValue(Promise.resolve()); + spyOn(minify, 'minifyCss').and.returnValue(Promise.resolve()); + spyOn(lint, 'lint').and.returnValue(Promise.resolve()); + spyOn(transpile, 'transpile').and.returnValue(Promise.resolve()); + }); + + describe('build', () => { + it('isProd', () => { + let context: BuildContext = { + isProd: true, + optimizeJs: true, + runMinifyJs: true, + runMinifyCss: true, + runAot: true + }; + + build.build(context).then(() => { + expect(copy.copy).toHaveBeenCalled(); + expect(ngc.ngc).toHaveBeenCalled(); + expect(bundle.bundle).toHaveBeenCalled(); + expect(minify.minifyJs).toHaveBeenCalled(); + expect(sass.sass).toHaveBeenCalled(); + expect(minify.minifyCss).toHaveBeenCalled(); + expect(lint.lint).toHaveBeenCalled(); + + expect(transpile.transpile).not.toHaveBeenCalled(); + }); + }); + + it('isDev', () => { + let context: BuildContext = { + isProd: false, + optimizeJs: false, + runMinifyJs: false, + runMinifyCss: false, + runAot: false + }; + + build.build(context).then(() => { + expect(copy.copy).toHaveBeenCalled(); + expect(transpile.transpile).toHaveBeenCalled(); + expect(bundle.bundle).toHaveBeenCalled(); + expect(sass.sass).toHaveBeenCalled(); + expect(lint.lint).toHaveBeenCalled(); + + expect(ngc.ngc).not.toHaveBeenCalled(); + expect(minify.minifyJs).not.toHaveBeenCalled(); + expect(minify.minifyCss).not.toHaveBeenCalled(); + }); + }); + }); + +}); diff --git a/src/spec/config.spec.ts b/src/spec/config.spec.ts index a9086a3c..721f7b79 100644 --- a/src/spec/config.spec.ts +++ b/src/spec/config.spec.ts @@ -1,5 +1,5 @@ import { BuildContext } from '../util/interfaces'; -import { bundlerStrategy, generateContext, getConfigValue, getUserConfigFile, getIsProd, replacePathVars } from '../util/config'; +import { bundlerStrategy, generateContext, getConfigValue, getUserConfigFile, replacePathVars } from '../util/config'; import { addArgv, setAppPackageJsonData, setProcessEnvVar, setProcessArgs, setProcessEnv, setCwd } from '../util/config'; import { resolve } from 'path'; @@ -39,7 +39,7 @@ describe('config', () => { it('should set isProd by default', () => { const context = generateContext(); - expect(context.isProd).toEqual(true); + expect(context.isProd).toEqual(false); }); it('should create an object when passed nothing', () => { @@ -47,52 +47,27 @@ describe('config', () => { expect(context).toBeDefined(); }); - }); - - describe('getIsProd', () => { - - it('should set isProd false with env var', () => { - context = {}; - setProcessEnvVar('IONIC_DEV', 'true'); - expect(getIsProd(context)).toEqual(false); - }); - - it('should set isProd false with package.json string config', () => { - context = {}; - setAppPackageJsonData({ config: { ionic_dev: 'true' }}); - expect(getIsProd(context)).toEqual(false); - }); - - it('should set isProd false with package.json config', () => { - context = {}; - setAppPackageJsonData({ config: { ionic_dev: true }}); - expect(getIsProd(context)).toEqual(false); - }); - - it('should not reassign isProd when already set', () => { - context = {}; - context.isProd = true; - addArgv('--dev'); - expect(getIsProd(context)).toEqual(true); - }); - - it('should set isProd false with short --d arg', () => { - context = {}; - addArgv('-d'); - expect(getIsProd(context)).toEqual(false); - }); - - it('should set isProd false with full --dev arg', () => { - context = {}; - addArgv('--dev'); - expect(getIsProd(context)).toEqual(false); + it('should set default prod specific build flag defaults to false', () => { + const context = generateContext({ + isProd: false + }); + expect(context.isProd).toEqual(false); + expect(context.runAot).toEqual(false); + expect(context.runMinifyJs).toEqual(false); + expect(context.runMinifyCss).toEqual(false); + expect(context.optimizeJs).toEqual(false); }); - it('should default to isProd true', () => { - context = {}; - expect(getIsProd(context)).toEqual(true); + it('should set default prod specific build flags to true when isProd is true', () => { + const context = generateContext({ + isProd: true + }); + expect(context.isProd).toEqual(true); + expect(context.runAot).toEqual(true); + expect(context.runMinifyJs).toEqual(true); + expect(context.runMinifyCss).toEqual(true); + expect(context.optimizeJs).toEqual(true); }); - }); describe('replacePathVars', () => { diff --git a/src/transpile-worker.ts b/src/transpile-worker.ts index 1d999bbd..63720ba6 100644 --- a/src/transpile-worker.ts +++ b/src/transpile-worker.ts @@ -7,7 +7,6 @@ const context: BuildContext = {}; process.on('message', (incomingMsg: TranspileWorkerMessage) => { context.rootDir = incomingMsg.rootDir; context.buildDir = incomingMsg.buildDir; - context.isProd = incomingMsg.isProd; const workerConfig: TranspileWorkerConfig = { configFile: incomingMsg.configFile, diff --git a/src/transpile.ts b/src/transpile.ts index c1fa59d3..9c4d6c16 100644 --- a/src/transpile.ts +++ b/src/transpile.ts @@ -246,7 +246,6 @@ function runDiagnosticsWorker(context: BuildContext) { const msg: TranspileWorkerMessage = { rootDir: context.rootDir, buildDir: context.buildDir, - isProd: context.isProd, configFile: getTsConfigPath(context) }; diagnosticsWorker.send(msg); @@ -256,7 +255,6 @@ function runDiagnosticsWorker(context: BuildContext) { export interface TranspileWorkerMessage { rootDir?: string; buildDir?: string; - isProd?: boolean; configFile?: string; transpileSuccess?: boolean; } @@ -264,8 +262,7 @@ export interface TranspileWorkerMessage { function cleanFileNames(context: BuildContext, fileNames: string[]) { // make sure we're not transpiling the prod when dev and stuff - const removeFileName = (context.isProd) ? 'main.dev.ts' : 'main.prod.ts'; - return fileNames.filter(f => (f.indexOf(removeFileName) === -1)); + return fileNames; } function writeSourceFiles(fileCache: FileCache, sourceFiles: ts.SourceFile[]) { diff --git a/src/util/config.ts b/src/util/config.ts index 5506617a..a46a3ca3 100644 --- a/src/util/config.ts +++ b/src/util/config.ts @@ -61,8 +61,31 @@ export function generateContext(context?: BuildContext): BuildContext { context.bundler = bundlerStrategy(context); } - context.isProd = getIsProd(context); - setIonicEnvironment(context.isProd); + context.isProd = [ + context.isProd, + hasArg('--prod') + ].find(val => typeof val === 'boolean'); + + // If context is prod then the following flags must be set to true + context.runAot = [ + context.runAot, + context.isProd || hasArg('--aot'), + ].find(val => typeof val === 'boolean'); + + context.runMinifyJs = [ + context.runMinifyJs, + context.isProd || hasArg('--minifyJs') + ].find(val => typeof val === 'boolean'); + + context.runMinifyCss = [ + context.runMinifyCss, + context.isProd || hasArg('--minifyCss') + ].find(val => typeof val === 'boolean'); + + context.optimizeJs = [ + context.optimizeJs, + context.isProd || hasArg('--optimizeJs') + ].find(val => typeof val === 'boolean'); if (typeof context.isWatch !== 'boolean') { context.isWatch = hasArg('--watch'); @@ -75,31 +98,6 @@ export function generateContext(context?: BuildContext): BuildContext { return context; } - -export function getIsProd(context: BuildContext) { - // only check if isProd hasn't already been manually set - if (typeof context.isProd === 'boolean') { - return context.isProd; - } - if (hasArg('--dev', '-d')) { - // not production: has a --dev or -d cmd line arg - return false; - } - - let val = getPackageJsonConfig(context, ENV_VAR_IONIC_DEV.toLowerCase()); - if (typeof val === 'boolean') { - return !val; - } - - val = getProcessEnvVar(ENV_VAR_IONIC_DEV); - if (typeof val === 'boolean') { - return !val; - } - - return true; -} - - export function getUserConfigFile(context: BuildContext, task: TaskInfo, userConfigFile: string) { if (userConfigFile) { return resolve(userConfigFile); @@ -311,12 +309,6 @@ export function isDebugMode() { return (processEnv.ionic_debug_mode === 'true'); } - -export function setIonicEnvironment(isProd: boolean) { - setProcessEnvVar(ENV_VAR_IONIC_ENV, (isProd ? ENV_VAR_PROD : ENV_VAR_DEV)); -} - - let processArgv: string[]; export function setProcessArgs(argv: string[]) { processArgv = argv; @@ -401,11 +393,6 @@ const TMP_DIR = '.tmp'; const WWW_DIR = 'www'; const WWW_INDEX_FILENAME = 'index.html'; -const ENV_VAR_PROD = 'prod'; -const ENV_VAR_DEV = 'dev'; - -const ENV_VAR_IONIC_ENV = 'IONIC_ENV'; -const ENV_VAR_IONIC_DEV = 'IONIC_DEV'; const ENV_VAR_ROOT_DIR = 'IONIC_ROOT_DIR'; const ENV_VAR_TMP_DIR = 'IONIC_TMP_DIR'; const ENV_VAR_SRC_DIR = 'IONIC_SRC_DIR'; diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index 2eda7997..bc7c0027 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -11,6 +11,10 @@ export interface BuildContext { moduleFiles?: string[]; isProd?: boolean; isWatch?: boolean; + runAot?: boolean; + runMinifyJs?: boolean; + runMinifyCss?: boolean; + optimizeJs?: boolean; bundler?: string; fileCache?: FileCache; diff --git a/src/watch.ts b/src/watch.ts index 89e37944..0f6c38bc 100644 --- a/src/watch.ts +++ b/src/watch.ts @@ -3,7 +3,7 @@ import { copyUpdate as copyUpdateHandler} from './copy'; import { BuildContext, BuildState, ChangedFile, TaskInfo } from './util/interfaces'; import { BuildError } from './util/errors'; import { canRunTranspileUpdate } from './transpile'; -import { fillConfigDefaults, generateContext, getUserConfigFile, replacePathVars, setIonicEnvironment } from './util/config'; +import { fillConfigDefaults, generateContext, getUserConfigFile, replacePathVars } from './util/config'; import { extname, join, normalize, resolve as pathResolve } from 'path'; import { Logger } from './logger/logger'; import * as chokidar from 'chokidar'; @@ -12,11 +12,19 @@ import * as chokidar from 'chokidar'; // https://github.com/paulmillr/chokidar export function watch(context?: BuildContext, configFile?: string) { + context = generateContext(context); + configFile = getUserConfigFile(context, taskInfo, configFile); - // force watch options + // Override all build options if watch is ran. context.isProd = false; + context.optimizeJs = false; + context.runMinifyJs = false; + context.runMinifyCss = false; + context.runAot = false; + + // Ensure that watch is true in context context.isWatch = true; context.sassState = BuildState.RequiresBuild; @@ -103,8 +111,6 @@ function startWatcher(name: string, watcher: Watcher, context: BuildContext) { event = watcher.eventName; } - setIonicEnvironment(context.isProd); - filePath = normalize(pathResolve(join(context.rootDir, filePath))); Logger.debug(`watch callback start, id: ${watchCount}, isProd: ${context.isProd}, event: ${event}, path: ${filePath}`); diff --git a/src/worker-client.ts b/src/worker-client.ts index f0104139..ef81d653 100644 --- a/src/worker-client.ts +++ b/src/worker-client.ts @@ -22,6 +22,10 @@ export function runWorker(taskModule: string, taskWorker: string, context: Build buildDir: context.buildDir, isProd: context.isProd, isWatch: context.isWatch, + runAot: context.runAot, + runMinifyJs: context.runMinifyJs, + runMinifyCss: context.runMinifyCss, + optimizeJs: context.optimizeJs, bundler: context.bundler, inlineTemplates: context.inlineTemplates, },