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

Commit 033ffa3

Browse files
committed
chore(bundle): prepend Ionic global to window
1 parent 1803e38 commit 033ffa3

12 files changed

+245
-49
lines changed

src/core/bundle-components.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { BuildContext, CoreCompiler } from '../util/interfaces';
2+
import { Logger } from '../logger/logger';
3+
import * as fs from 'fs';
4+
import * as path from 'path';
5+
import * as nodeSass from 'node-sass';
6+
import * as rollup from 'rollup';
7+
import * as typescript from 'typescript';
8+
import * as uglify from 'uglify-js';
9+
10+
11+
export function bundleCoreComponents(context: BuildContext) {
12+
const compiler = getCoreCompiler(context);
13+
14+
if (!compiler) {
15+
Logger.debug(`skipping core component bundling`);
16+
return Promise.resolve();
17+
}
18+
19+
const config = {
20+
srcDir: context.coreDir,
21+
destDir: context.buildDir,
22+
packages: {
23+
fs: fs,
24+
path: path,
25+
nodeSass: nodeSass,
26+
rollup: rollup,
27+
typescript: typescript,
28+
uglify: uglify
29+
}
30+
};
31+
32+
return compiler.bundle(config).then(results => {
33+
if (results.errors) {
34+
results.errors.forEach((err: string) => {
35+
Logger.error(`compiler.bundle, results: ${err}`);
36+
});
37+
}
38+
}).catch(err => {
39+
Logger.error(`compiler.bundle: ${err}`);
40+
});
41+
}
42+
43+
44+
function getCoreCompiler(context: BuildContext): CoreCompiler {
45+
try {
46+
return require(context.coreCompilerFilePath);
47+
} catch (e) {
48+
Logger.debug(`error loading core compiler: ${context.coreCompilerFilePath}, ${e}`);
49+
}
50+
return null;
51+
}

src/core/ionic-global.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { BuildContext } from '../util/interfaces';
2+
import { buildIonicGlobal, prependIonicGlobal } from './ionic-global';
3+
4+
5+
describe('Ionic Global', () => {
6+
7+
describe('prependIonicGlobal', () => {
8+
9+
it('should prepend window.Ionic to code', () => {
10+
const ctx: BuildContext = {
11+
rootDir: '/Users/elliemae/myapp',
12+
wwwDir: '/Users/elliemae/myapp/www',
13+
buildDir: '/Users/elliemae/myapp/www/build'
14+
};
15+
16+
const code = 'var name = "Ellie Mae";';
17+
18+
const r = prependIonicGlobal(ctx, 'main.js', code);
19+
20+
expect(r.code.indexOf('Ionic') > -1).toBeTruthy();
21+
expect(r.map).toBeDefined();
22+
});
23+
24+
});
25+
26+
27+
describe('buildIonicGlobal', () => {
28+
29+
it('should cache windowIonic', () => {
30+
const ctx: BuildContext = {
31+
rootDir: '/Users/elliemae/myapp',
32+
wwwDir: '/Users/elliemae/myapp/www',
33+
buildDir: '/Users/elliemae/myapp/www/build'
34+
};
35+
36+
expect((<any>ctx).windowIonic).toBeUndefined();
37+
38+
const r = buildIonicGlobal(ctx);
39+
40+
expect(r).toBeDefined();
41+
expect((<any>ctx).windowIonic).toBeDefined();
42+
});
43+
44+
});
45+
46+
});

src/core/ionic-global.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { BuildContext } from '../util/interfaces';
2+
import { getSystemData, toUnixPath } from '../util/helpers';
3+
import { Logger } from '../logger/logger';
4+
import * as MagicString from 'magic-string';
5+
6+
7+
export function prependIonicGlobal(context: BuildContext, fileName: string, code: string) {
8+
const rtn: { code: string, map: any } = {
9+
code: code,
10+
map: undefined
11+
};
12+
13+
try {
14+
const ionicGlobal = buildIonicGlobal(context);
15+
16+
const s = new MagicString(code);
17+
18+
s.prepend(ionicGlobal);
19+
20+
rtn.code = s.toString();
21+
22+
rtn.map = s.generateMap({
23+
source: fileName,
24+
file: fileName,
25+
includeContent: true
26+
});
27+
28+
} catch (e) {
29+
Logger.error(`prependIonicGlobal: ${e}`);
30+
}
31+
32+
return rtn;
33+
}
34+
35+
36+
export function buildIonicGlobal(context: BuildContext) {
37+
if ((<any>context).windowIonic) {
38+
// just a quick way to cache this to avoid unnecessary readFiles
39+
return (<any>context).windowIonic;
40+
}
41+
42+
const systemData = getSystemData(context.rootDir);
43+
44+
let staticDir = toUnixPath(context.buildDir.replace(context.wwwDir, ''));
45+
staticDir += '/';
46+
47+
if (staticDir.charAt(0) === '/') {
48+
staticDir = staticDir.substring(1);
49+
}
50+
51+
let output = `
52+
(function(w){
53+
var i = w.Ionic = w.Ionic || {};
54+
${systemData.ionicFramework ? `i.version = '${systemData.ionicFramework}';` : ''}
55+
${systemData.angularCore ? `i.angular = '${systemData.angularCore}';` : ''}
56+
${systemData.ionicNative ? `i.ionicNative = '${systemData.ionicNative}';` : ''}
57+
i.staticDir = '${staticDir}';
58+
})(window);`;
59+
60+
// real quick minification hacks
61+
output = output.replace('var i', 'var_i');
62+
output = output.replace(/\s/g, '');
63+
output = output.replace('var_i', 'var i');
64+
65+
return (<any>context).windowIonic = output;
66+
}

src/postprocess.ts

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { Logger } from './logger/logger';
2-
import { getSystemData, readFileAsync, writeFileAsync } from './util/helpers';
32
import { BuildContext } from './util/interfaces';
43
import { purgeSourceMapsIfNeeded } from './util/source-maps';
54
import { removeUnusedFonts } from './optimization/remove-unused-fonts';
6-
import * as path from 'path';
75

86

97
export function postprocess(context: BuildContext) {
@@ -19,46 +17,7 @@ export function postprocess(context: BuildContext) {
1917

2018
function postprocessWorker(context: BuildContext) {
2119
return Promise.all([
22-
addIonicGlobal(context),
2320
purgeSourceMapsIfNeeded(context),
2421
removeUnusedFonts(context)
2522
]);
2623
}
27-
28-
29-
function addIonicGlobal(context: BuildContext) {
30-
const outputFilePath = path.join(context.buildDir, context.outputJsFileName);
31-
32-
return readFileAsync(outputFilePath).then(outputContent => {
33-
const ionicGlobal = buildIonicGlobal(context);
34-
35-
if (outputContent.indexOf(ionicGlobal) === -1) {
36-
outputContent += ionicGlobal;
37-
return writeFileAsync(outputFilePath, outputContent);
38-
}
39-
});
40-
}
41-
42-
43-
function buildIonicGlobal(context: BuildContext) {
44-
if ((<any>context).windowIonic) {
45-
// just a quick way to cache this to avoid unnecessary readFiles
46-
return (<any>context).windowIonic;
47-
}
48-
49-
const systemData = getSystemData(context.rootDir);
50-
51-
let output = `
52-
(function(w){
53-
var i = w.Ionic = w.Ionic || {};
54-
${systemData.ionicFramework ? `i.version = '${systemData.ionicFramework}';` : ''}
55-
${systemData.angularCore ? `i.angular = '${systemData.angularCore}';` : ''}
56-
${systemData.ionicNative ? `i.ionicNative = '${systemData.ionicNative}';` : ''}
57-
})(window);`;
58-
59-
// real quick minification hack
60-
output = output.replace(/\s/g, '');
61-
output = output.replace('vari=', 'var i=');
62-
63-
return (<any>context).windowIonic = `\n\n${output}`;
64-
}

src/preprocess.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { getBooleanPropertyValue } from './util/helpers';
88
import { BuildContext, ChangedFile } from './util/interfaces';
99
import { optimization } from './optimization';
1010
import { deepLinking, deepLinkingUpdate } from './deep-linking';
11+
import { bundleCoreComponents } from './core/bundle-components';
12+
1113

1214
export function preprocess(context: BuildContext) {
1315
const logger = new Logger(`preprocess`);
@@ -22,8 +24,10 @@ export function preprocess(context: BuildContext) {
2224
}
2325

2426
function preprocessWorker(context: BuildContext) {
27+
const bundlePromise = bundleCoreComponents(context);
2528
const deepLinksPromise = getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS) ? deepLinking(context) : Promise.resolve();
26-
return deepLinksPromise
29+
30+
return Promise.all([bundlePromise, deepLinksPromise])
2731
.then(() => {
2832
if (context.optimizeJs) {
2933
return optimization(context, null);
@@ -51,8 +55,15 @@ export function writeFilesToDisk(context: BuildContext) {
5155
}
5256

5357
export function preprocessUpdate(changedFiles: ChangedFile[], context: BuildContext) {
58+
const promises: Promise<any>[] = [];
59+
60+
if (changedFiles.some(cf => cf.ext === '.scss')) {
61+
promises.push(bundleCoreComponents(context));
62+
}
63+
5464
if (getBooleanPropertyValue(Constants.ENV_PARSE_DEEPLINKS)) {
55-
return deepLinkingUpdate(changedFiles, context);
65+
promises.push(deepLinkingUpdate(changedFiles, context));
5666
}
57-
return Promise.resolve();
67+
68+
return Promise.all(promises);
5869
}

src/rollup.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { join, isAbsolute, normalize, sep } from 'path';
1+
import { prependIonicGlobal } from './core/ionic-global';
2+
import { basename, join, isAbsolute, normalize, sep } from 'path';
23
import * as rollupBundler from 'rollup';
34

45
import { Logger } from './logger/logger';
@@ -90,6 +91,11 @@ export function rollupWorker(context: BuildContext, configFile: string): Promise
9091

9192
const bundleOutput = bundle.generate(rollupConfig);
9293

94+
const ionicBundle = prependIonicGlobal(context, basename(rollupConfig.dest), bundleOutput.code);
95+
96+
bundleOutput.code = ionicBundle.code;
97+
bundleOutput.map = ionicBundle.map;
98+
9399
// write the bundle
94100
const promises: Promise<any>[] = [];
95101
promises.push(writeFileAsync(rollupConfig.dest, bundleOutput.code));

src/util/config.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ describe('config', () => {
8282
expect(context.nodeModulesDir).toEqual(join(process.cwd(), Constants.NODE_MODULES));
8383
expect(context.ionicAngularDir).toEqual(join(process.cwd(), Constants.NODE_MODULES, Constants.IONIC_ANGULAR));
8484
expect(fakeConfig[Constants.ENV_VAR_AT_ANGULAR_DIR]).toEqual(join(process.cwd(), Constants.NODE_MODULES, Constants.AT_ANGULAR));
85+
expect(context.coreCompilerFilePath).toEqual(join(context.ionicAngularDir, 'compiler'));
86+
expect(context.coreDir).toEqual(context.ionicAngularDir);
8587
expect(fakeConfig[Constants.ENV_VAR_RXJS_DIR]).toEqual(join(process.cwd(), Constants.NODE_MODULES, Constants.RXJS));
8688
expect(fakeConfig[Constants.ENV_VAR_IONIC_ANGULAR_TEMPLATE_DIR]).toEqual(join(context.ionicAngularDir, 'templates'));
8789
expect(context.platform).toEqual(null);

src/util/config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ export function generateContext(context?: BuildContext): BuildContext {
119119
setProcessEnvVar(Constants.ENV_VAR_AT_ANGULAR_DIR, angularDir);
120120
Logger.debug(`angularDir set to ${angularDir}`);
121121

122+
const defaultCoreCompilerFilePath = join(context.ionicAngularDir, 'compiler');
123+
context.coreCompilerFilePath = resolve(context.coreCompilerFilePath || getConfigValue(context, '--coreCompilerFilePath', null, Constants.ENV_VAR_CORE_COMPILER_FILE_PATH, Constants.ENV_VAR_CORE_COMPILER_FILE_PATH.toLowerCase(), defaultCoreCompilerFilePath));
124+
setProcessEnvVar(Constants.ENV_VAR_CORE_COMPILER_FILE_PATH, context.coreCompilerFilePath);
125+
Logger.debug(`coreCompilerFilePath set to ${context.coreCompilerFilePath}`);
126+
127+
const defaultCoreDir = context.ionicAngularDir;
128+
context.coreDir = resolve(context.coreDir || getConfigValue(context, '--coreDir', null, Constants.ENV_VAR_CORE_DIR, Constants.ENV_VAR_CORE_DIR.toLowerCase(), defaultCoreDir));
129+
setProcessEnvVar(Constants.ENV_VAR_CORE_DIR, context.coreDir);
130+
Logger.debug(`coreDir set to ${context.coreDir}`);
131+
122132
const rxjsDir = resolve(getConfigValue(context, '--rxjsDir', null, Constants.ENV_VAR_RXJS_DIR, Constants.ENV_VAR_RXJS_DIR.toLowerCase(), join(context.nodeModulesDir, Constants.RXJS)));
123133
setProcessEnvVar(Constants.ENV_VAR_RXJS_DIR, rxjsDir);
124134
Logger.debug(`rxjsDir set to ${rxjsDir}`);

src/util/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export const ENV_VAR_NODE_MODULES_DIR = 'IONIC_NODE_MODULES_DIR';
4141
export const ENV_VAR_AT_ANGULAR_DIR = 'IONIC_AT_ANGULAR_DIR';
4242
export const ENV_VAR_RXJS_DIR = 'IONIC_RXJS_DIR';
4343
export const ENV_VAR_IONIC_ANGULAR_DIR = 'IONIC_ANGULAR_DIR';
44+
export const ENV_VAR_CORE_COMPILER_FILE_PATH = 'IONIC_CORE_COMPILER_FILE_PATH';
45+
export const ENV_VAR_CORE_DIR = 'IONIC_CORE_DIR';
4446
export const ENV_VAR_IONIC_ANGULAR_TEMPLATE_DIR = 'IONIC_ANGULAR_TEMPLATE_DIR';
4547
export const ENV_VAR_TARGET = 'IONIC_TARGET';
4648
export const ENV_VAR_PLATFORM = 'IONIC_PLATFORM';

src/util/interfaces.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export interface BuildContext {
2121
outputCssFileName?: string;
2222
nodeModulesDir?: string;
2323
ionicAngularDir?: string;
24+
coreCompilerFilePath?: string;
25+
coreDir?: string;
2426
bundledFilePaths?: string[];
2527
moduleFiles?: string[];
2628
appNgModulePath?: string;
@@ -208,3 +210,25 @@ export interface MagicString {
208210
toString(): string;
209211
prependLeft(index: number, contentToPrepend: string): string;
210212
}
213+
214+
215+
export interface CoreCompiler {
216+
bundle: {
217+
(config: {
218+
srcDir: string;
219+
destDir: string;
220+
packages: Packages;
221+
debug?: boolean;
222+
}): Promise<any>;
223+
};
224+
}
225+
226+
227+
export interface Packages {
228+
path?: any;
229+
fs?: any;
230+
typescript?: any;
231+
nodeSass?: any;
232+
rollup?: any;
233+
uglify?: any;
234+
}

src/webpack.spec.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ describe('Webpack Task', () => {
1212

1313
const context = {
1414
fileCache: new FileCache(),
15-
buildDir: buildDir
15+
buildDir: buildDir,
16+
outputJsFileName: 'main.js'
1617
};
1718

1819
const fileOnePath = join(buildDir, 'main.js');
@@ -47,11 +48,14 @@ describe('Webpack Task', () => {
4748

4849
return promise.then(() => {
4950
expect(writeFileSpy).toHaveBeenCalledTimes(6);
51+
5052
expect(writeFileSpy.calls.all()[0].args[0]).toEqual(fileOnePath);
51-
expect(writeFileSpy.calls.all()[0].args[1]).toEqual(fileOnePath + 'content');
53+
54+
// igore the appended ionic global
55+
let mainBundleContent = fileOnePath + 'content';
56+
expect(mainBundleContent.indexOf(writeFileSpy.calls.all()[0].args[1])).toEqual(-1);
5257

5358
expect(writeFileSpy.calls.all()[1].args[0]).toEqual(fileTwoPath);
54-
expect(writeFileSpy.calls.all()[1].args[1]).toEqual(fileTwoPath + 'content');
5559

5660
expect(writeFileSpy.calls.all()[2].args[0]).toEqual(fileThreePath);
5761
expect(writeFileSpy.calls.all()[2].args[1]).toEqual(fileThreePath + 'content');

0 commit comments

Comments
 (0)