Skip to content
This repository has been archived by the owner on May 1, 2020. It is now read-only.

Commit

Permalink
feat(minification): code-split bundles will be minified (#814)
Browse files Browse the repository at this point in the history
* feat(code-splitting): uglify now minifies all bundles

* style(): semicolons

* chore(babili): get multiple bundles working with babili

* style(imports): remove un-used import

* test(uglify): add error path tests)

* test(babili): add tests for babili

* fix(babili): improve error handling

* test(babili): modify error test

* chore(babili): make error handling fail correctly

* test(babili): more error path tests

* fix(uglify): file name is now correct

* fix(transpile): file name is now correct

* feat(code-splitting): uglify now minifies all bundles

* style(): semicolons

* chore(babili): get multiple bundles working with babili

* style(imports): remove un-used import

* test(uglify): add error path tests)

* test(babili): add tests for babili

* fix(babili): improve error handling

* test(babili): modify error test

* chore(babili): make error handling fail correctly

* test(babili): more error path tests

* fix(uglify): file name is now correct

* fix(transpile): file name is now correct

* style(console): remove un-needed console logs
  • Loading branch information
jgw96 authored Mar 13, 2017
1 parent 3c36e0c commit d8d9a4e
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 53 deletions.
41 changes: 41 additions & 0 deletions src/babili.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as babili from './babili';
import * as configUtil from './util/config';

describe('babili function', () => {
beforeEach(() => {
spyOn(configUtil, 'getUserConfigFile').and.returnValue('fileContents');
});

it('should call main babili function', () => {
const context = {
rootDir: '/Users/justinwillis/Projects/ionic-conference-app'
};
const configFile = 'configFileContents';

return babili.babili(context, configFile).then(() => {
expect(configUtil.getUserConfigFile).toHaveBeenCalledWith(context, babili.taskInfo, configFile);
});
});

it('should throw if context does not have a rootDir', () => {
const context = {};
const configFile = 'configFileContents';

expect(babili.babili(context, configFile)).toThrow();
});

it('should fail because it does not have a valid build context', () => {
const context: null = null;
const configFile = 'configFileContents';

expect(babili.babili(context, configFile)).toThrow();
});

it('should fail because it does not have a valid config file', () => {
const context = {};
const configFile: null = null;

expect(babili.babili(context, configFile)).toThrow();
});

});
41 changes: 14 additions & 27 deletions src/babili.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,54 @@ import { spawn } from 'cross-spawn';
import { fillConfigDefaults, getUserConfigFile } from './util/config';
import { BuildContext, TaskInfo } from './util/interfaces';
import { Logger } from './logger/logger';
import { writeFileAsync } from './util/helpers';

export function babili(context: BuildContext, configFile?: string) {

configFile = getUserConfigFile(context, taskInfo, configFile);

const logger = new Logger('babili - experimental');

return babiliWorker(context, configFile).then(() => {
logger.finish();
})
.catch(err => {
throw logger.fail(err);
return logger.fail(err);
});
}


export function babiliWorker(context: BuildContext, configFile: string) {
const babiliConfig: BabiliConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
// TODO - figure out source maps??
return runBabili(context, babiliConfig).then((minifiedCode: string) => {
// write the file back to disk
const fileToWrite = join(context.buildDir, babiliConfig.destFileName);
return writeFileAsync(fileToWrite, minifiedCode);
});
return runBabili(context, babiliConfig);
}

function runBabili(context: BuildContext, config: BabiliConfig) {
const babiliPath = join(context.rootDir, 'node_modules', '.bin', 'babili');
const bundlePath = join(context.buildDir, config.sourceFile);
return runBabiliImpl(babiliPath, bundlePath);
return runBabiliImpl(context);
}

function runBabiliImpl(pathToBabili: string, pathToBundle: string) {
function runBabiliImpl(context: BuildContext) {
// TODO - is there a better way to run this?
let chunks: string[] = [];
return new Promise((resolve, reject) => {
const command = spawn(pathToBabili, [pathToBundle]);
command.stdout.on('data', (buffer: Buffer) => {
const stringRepresentation = buffer.toString();
Logger.debug(`[Babili] ${stringRepresentation}`);
chunks.push(stringRepresentation);
});

command.stderr.on('data', (buffer: Buffer) => {
Logger.warn(`[Babili] ${buffer.toString()}`);
});

if (!context.rootDir) {
return reject(new Error('Babili failed because the context passed did not have a rootDir'));
}
const babiliPath = join(context.rootDir, 'node_modules', '.bin', 'babili');
const command = spawn(babiliPath, [context.buildDir, '--out-dir', context.buildDir]);
command.on('close', (code: number) => {
if ( code !== 0) {
if (code !== 0) {
return reject(new Error('Babili failed with a non-zero status code'));
}
return resolve(chunks.join(''));
return resolve();
});
});
}

export const taskInfo: TaskInfo = {
fullArg: '--babili',
shortArg: null,
envVar: 'IONIC_EXP_BABILI',
packageConfig: 'ionic_exp_babili',
envVar: 'IONIC_USE_EXPERIMENTAL_BABILI',
packageConfig: 'ionic_use_experimental_babili',
defaultConfigFile: 'babili.config'
};

Expand Down
31 changes: 18 additions & 13 deletions src/transpile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { EventEmitter } from 'events';
import { fork, ChildProcess } from 'child_process';
import { inlineTemplate } from './template';
import { Logger } from './logger/logger';
import { readFileSync, writeFileSync } from 'fs';
import { readFileSync, writeFileSync, readdirSync } from 'fs';
import { runTypeScriptDiagnostics } from './logger/logger-typescript';
import { printDiagnostics, clearDiagnostics, DiagnosticsType } from './logger/logger-diagnostics';
import * as path from 'path';
Expand Down Expand Up @@ -310,18 +310,23 @@ export function transpileBundle(context: BuildContext, target: ts.ScriptTarget =
function transpileBundleImpl(context: BuildContext, target: ts.ScriptTarget) {
const logger = new Logger('transpile bundle');
try {
const bundlePath = path.join(context.buildDir, process.env[Constants.ENV_OUTPUT_JS_FILE_NAME]);
const bundleContent = readFileSync(bundlePath).toString();
const tsConfig = getTsConfig(context);
const transpileOptions: ts.TranspileOptions = {
compilerOptions: tsConfig.options,
fileName: bundlePath,
reportDiagnostics: true
};
// override the target value
transpileOptions.compilerOptions.target = target;
const transpiledOutput = ts.transpileModule(bundleContent, transpileOptions);
writeFileSync(bundlePath, transpiledOutput.outputText);
const files = readdirSync(context.buildDir);
files.forEach((file) => {
if (file.indexOf('deptree') === -1 && file.indexOf('map') === -1 && file.indexOf('sw-toolbox') === -1 && file.indexOf('polyfills') === -1) {
const bundlePath = path.join(context.buildDir, file);
const bundleContent = readFileSync(bundlePath).toString();
const tsConfig = getTsConfig(context);
const transpileOptions: ts.TranspileOptions = {
compilerOptions: tsConfig.options,
fileName: bundlePath,
reportDiagnostics: true
};
// override the target value
transpileOptions.compilerOptions.target = target;
const transpiledOutput = ts.transpileModule(bundleContent, transpileOptions);
writeFileSync(bundlePath, transpiledOutput.outputText);
}
});
logger.finish();
} catch (ex) {
throw logger.fail(ex);
Expand Down
24 changes: 24 additions & 0 deletions src/uglifyjs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,28 @@ describe('uglifyjs function', () => {
expect(workerClient.runWorker).toHaveBeenCalledWith('uglifyjs', 'uglifyjsWorker', context, 'fileContents');
});
});

it('should fail because it does not have a valid build context', () => {
const context: null = null;
const configFile = 'configFileContents';

expect(uglifyjs.uglifyjs(context, configFile)).toThrow();
});

it('should fail because it does not have a valid config file', () => {
const context = {};
const configFile: null = null;

expect(uglifyjs.uglifyjs(context, configFile)).toThrow();
});

it('should not fail if a config is not passed', () => {
const context = {};
let configFile: any;

return uglifyjs.uglifyjs(context).then(() => {
expect(configUtil.getUserConfigFile).toHaveBeenCalledWith(context, uglifyjs.taskInfo, configFile);
expect(workerClient.runWorker).toHaveBeenCalledWith('uglifyjs', 'uglifyjsWorker', context, 'fileContents');
});
});
});
29 changes: 16 additions & 13 deletions src/uglifyjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { join } from 'path';
import * as uglify from 'uglify-js';

import { Logger } from './logger/logger';
import { readdirSync, writeFileSync } from 'fs';
import { fillConfigDefaults, generateContext, getUserConfigFile } from './util/config';
import { BuildError } from './util/errors';
import { writeFileAsync } from './util/helpers';
import { BuildContext, TaskInfo } from './util/interfaces';
import { runWorker } from './worker-client';

Expand All @@ -30,21 +30,24 @@ export function uglifyjsWorker(context: BuildContext, configFile: string): Promi
try {
// provide a full path for the config options
context = generateContext(context);
const uglifyJsConfig: UglifyJsConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
uglifyJsConfig.sourceFile = join(context.buildDir, uglifyJsConfig.sourceFile);
uglifyJsConfig.inSourceMap = join(context.buildDir, uglifyJsConfig.inSourceMap);
uglifyJsConfig.destFileName = join(context.buildDir, uglifyJsConfig.destFileName);
const files = readdirSync(context.buildDir);

const minifiedOutputPath = join(context.buildDir, uglifyJsConfig.outSourceMap);
const minifyOutput: uglify.MinifyOutput = runUglifyInternal(uglifyJsConfig);
files.forEach((file) => {
if (file.indexOf('deptree') === -1 && file.indexOf('map') === -1 && file.indexOf('sw-toolbox') === -1 && file.indexOf('polyfills') === -1) {
const uglifyJsConfig: UglifyJsConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
uglifyJsConfig.sourceFile = join(context.buildDir, file);
uglifyJsConfig.inSourceMap = join(context.buildDir, uglifyJsConfig.inSourceMap);
uglifyJsConfig.destFileName = join(context.buildDir, file);

const writeFilePromises = [
writeFileAsync(uglifyJsConfig.destFileName, minifyOutput.code),
writeFileAsync(minifiedOutputPath, minifyOutput.map)
];
const minifiedOutputPath = join(context.buildDir, uglifyJsConfig.outSourceMap);
const minifyOutput: uglify.MinifyOutput = runUglifyInternal(uglifyJsConfig);

return Promise.all(writeFilePromises).then(() => {
resolve();

writeFileSync(uglifyJsConfig.destFileName, minifyOutput.code);
writeFileSync(minifiedOutputPath, minifyOutput.map);

resolve();
}
});

} catch (e) {
Expand Down

0 comments on commit d8d9a4e

Please sign in to comment.