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

Commit 967f784

Browse files
committed
feat(fonts): remove used fonts for cordova builds
1 parent efc4039 commit 967f784

File tree

7 files changed

+132
-10
lines changed

7 files changed

+132
-10
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { BuildContext } from '../util/interfaces';
2+
import { join } from 'path';
3+
import { Logger } from '../logger/logger';
4+
import { unlinkAsync } from '../util/helpers';
5+
import * as glob from 'glob';
6+
7+
8+
export function removeUnusedFonts(context: BuildContext) {
9+
// For webapps, we pretty much need all fonts to be available because
10+
// the web server deployment never knows which browser/platform is
11+
// opening the app. Additionally, webapps will request fonts on-demand,
12+
// so having them all sit in the www/assets/fonts directory doesn’t
13+
// hurt anything if it’s never being requested.
14+
15+
// However, with Cordova, the entire directory gets bundled and
16+
// shipped in the ipa/apk, but we also know exactly which platform
17+
// is opening the webapp. For this reason we can safely delete font
18+
// files we know would never be opened by the platform. So app-scripts
19+
// will continue to copy all font files over, but the cordova build
20+
// process would delete those we know are useless and just taking up
21+
// space. End goal is that the Cordova ipa/apk filesize is smaller.
22+
23+
// Font Format Support:
24+
// ttf: http://caniuse.com/#feat=ttf
25+
// woff: http://caniuse.com/#feat=woff
26+
// woff2: http://caniuse.com/#feat=woff2
27+
28+
if (context.target === 'cordova') {
29+
const fontsRemoved: string[] = [];
30+
// all cordova builds should remove .eot, .svg, .ttf, and .scss files
31+
fontsRemoved.push('*.eot');
32+
fontsRemoved.push('*.ttf');
33+
fontsRemoved.push('*.svg');
34+
fontsRemoved.push('*.scss');
35+
36+
// all cordova builds should remove Noto-Sans
37+
// Only windows would use Noto-Sans, and it already comes with
38+
// a system font so it wouldn't need our own copy.
39+
fontsRemoved.push('noto-sans*');
40+
41+
if (context.platform === 'android') {
42+
// Remove all Roboto fonts. Android already comes with Roboto system
43+
// fonts so shipping our own is unnecessary. Including roboto fonts
44+
// is only useful for PWAs and during development.
45+
fontsRemoved.push('roboto*');
46+
47+
} else if (context.platform === 'ios') {
48+
// Keep Roboto for now. Apps built for iOS may still use Material Design,
49+
// so in that case Roboto should be available. Later we can improve the
50+
// CLI to be smarter and read the user’s ionic config. Also, the roboto
51+
// fonts themselves are pretty small.
52+
}
53+
54+
let filesToDelete: string[] = [];
55+
56+
let promises = fontsRemoved.map(pattern => {
57+
return new Promise(resolve => {
58+
let searchPattern = join(context.wwwDir, 'assets', 'fonts', pattern);
59+
60+
glob(searchPattern, (err, files) => {
61+
if (err) {
62+
Logger.error(`removeUnusedFonts: ${err}`);
63+
64+
} else {
65+
files.forEach(f => {
66+
if (filesToDelete.indexOf(f) === -1) {
67+
filesToDelete.push(f);
68+
}
69+
});
70+
}
71+
72+
resolve();
73+
});
74+
75+
});
76+
});
77+
78+
return Promise.all(promises).then(() => {
79+
return unlinkAsync(filesToDelete).then(() => {
80+
if (filesToDelete.length) {
81+
Logger.info(`removed unused font files`);
82+
return true;
83+
}
84+
return false;
85+
});
86+
});
87+
}
88+
89+
// nothing to do here, carry on
90+
return Promise.resolve();
91+
}

src/postprocess.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Logger } from './logger/logger';
22
import { BuildContext } from './util/interfaces';
33
import { purgeSourceMapsIfNeeded } from './util/source-maps';
4+
import { removeUnusedFonts } from './optimization/remove-unused-fonts';
45

56

67
export function postprocess(context: BuildContext) {
@@ -15,5 +16,8 @@ export function postprocess(context: BuildContext) {
1516

1617

1718
function postprocessWorker(context: BuildContext) {
18-
return purgeSourceMapsIfNeeded(context);
19+
return Promise.all([
20+
purgeSourceMapsIfNeeded(context),
21+
removeUnusedFonts(context)
22+
]);
1923
}

src/util/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ export function generateContext(context?: BuildContext): BuildContext {
8080
context.ionicAngularDir = resolve(context.ionicAngularDir || getConfigValue(context, '--ionicAngularDir', null, Constants.ENV_VAR_IONIC_ANGULAR_DIR, Constants.ENV_VAR_IONIC_ANGULAR_DIR.toLowerCase(), join(context.nodeModulesDir, Constants.IONIC_ANGULAR)));
8181
setProcessEnvVar(Constants.ENV_VAR_IONIC_ANGULAR_DIR, context.ionicAngularDir);
8282

83+
context.platform = getConfigValue(context, '--platform', null, Constants.ENV_VAR_PLATFORM, null, null);
84+
setProcessEnvVar(Constants.ENV_VAR_PLATFORM, context.platform);
85+
86+
context.target = getConfigValue(context, '--target', null, Constants.ENV_VAR_TARGET, null, null);
87+
setProcessEnvVar(Constants.ENV_VAR_TARGET, context.target);
88+
8389
const ionicAngularEntryPoint = resolve(getConfigValue(context, '--ionicAngularEntryPoint', null, Constants.ENV_VAR_IONIC_ANGULAR_ENTRY_POINT, Constants.ENV_VAR_IONIC_ANGULAR_ENTRY_POINT.toLowerCase(), join(context.ionicAngularDir, 'index.js')));
8490
setProcessEnvVar(Constants.ENV_VAR_IONIC_ANGULAR_ENTRY_POINT, ionicAngularEntryPoint);
8591

src/util/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export const ENV_VAR_WWW_DIR = 'IONIC_WWW_DIR';
2525
export const ENV_VAR_BUILD_DIR = 'IONIC_BUILD_DIR';
2626
export const ENV_VAR_NODE_MODULES_DIR = 'IONIC_NODE_MODULES_DIR';
2727
export const ENV_VAR_IONIC_ANGULAR_DIR = 'IONIC_ANGULAR_DIR';
28+
export const ENV_VAR_TARGET = 'IONIC_TARGET';
29+
export const ENV_VAR_PLATFORM = 'IONIC_PLATFORM';
2830
export const ENV_VAR_IONIC_ANGULAR_ENTRY_POINT = 'IONIC_ANGULAR_ENTRY_POINT';
2931
export const ENV_VAR_APP_SCRIPTS_DIR = 'IONIC_APP_SCRIPTS_DIR';
3032
export const ENV_VAR_GENERATE_SOURCE_MAP = 'IONIC_GENERATE_SOURCE_MAP';

src/util/helpers.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,29 @@ export function readAndCacheFile(filePath: string, purge: boolean = false): Prom
128128
});
129129
}
130130

131-
export function unlinkAsync(filePath: string): Promise<any> {
132-
return new Promise((resolve, reject) => {
133-
unlink(filePath, (err: Error) => {
134-
if (err) {
135-
return reject(err);
136-
}
137-
return resolve();
131+
export function unlinkAsync(filePath: string|string[]): Promise<any> {
132+
let filePaths: string[];
133+
134+
if (typeof filePath === 'string') {
135+
filePaths = [filePath];
136+
} else if (Array.isArray(filePath)) {
137+
filePaths = filePath;
138+
} else {
139+
return Promise.reject('unlinkAsync, invalid filePath type');
140+
}
141+
142+
let promises = filePaths.map(filePath => {
143+
return new Promise((resolve, reject) => {
144+
unlink(filePath, (err: Error) => {
145+
if (err) {
146+
return reject(err);
147+
}
148+
return resolve();
149+
});
138150
});
139151
});
152+
153+
return Promise.all(promises);
140154
}
141155

142156
export function rimRafAsync(directoryPath: string): Promise<null> {

src/util/interfaces.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ export interface BuildContext {
3030
transpileState?: BuildState;
3131
templateState?: BuildState;
3232
bundleState?: BuildState;
33-
cordova?: any;
33+
34+
// target examples: cordova, browser, electron
35+
target?: string;
36+
37+
// platform examples: ios, android, windows
38+
platform?: string;
3439
}
3540

3641

src/util/source-maps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as Constants from './constants';
33
import { getBooleanPropertyValue, readDirAsync, unlinkAsync } from './helpers';
44
import { BuildContext } from './interfaces';
55

6-
export function purgeSourceMapsIfNeeded(context: BuildContext) {
6+
export function purgeSourceMapsIfNeeded(context: BuildContext): Promise<any> {
77
if (getBooleanPropertyValue(Constants.ENV_VAR_GENERATE_SOURCE_MAP)) {
88
// keep the source maps and just return
99
return Promise.resolve();

0 commit comments

Comments
 (0)