Skip to content

Commit b38055c

Browse files
committed
Extract module ids and wrap around bundled code in a function to aid in minification
1 parent 982f714 commit b38055c

2 files changed

Lines changed: 181 additions & 2 deletions

File tree

build/lib/bundle.js

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,95 @@ function emitEntryPoints(modules, entryPoints) {
8686
}
8787
});
8888
return {
89-
files: removeDuplicateTSBoilerplate(result),
89+
files: extractStrings(removeDuplicateTSBoilerplate(result)),
9090
bundleData: bundleData
9191
};
9292
}
93+
function extractStrings(destFiles) {
94+
var parseDefineCall = function (moduleMatch, depsMatch) {
95+
var module = moduleMatch.replace(/^"|"$/g, '');
96+
var deps = depsMatch.split(',');
97+
deps = deps.map(function (dep) {
98+
dep = dep.trim();
99+
dep = dep.replace(/^"|"$/g, '');
100+
dep = dep.replace(/^'|'$/g, '');
101+
var prefix = null;
102+
var _path = null;
103+
var pieces = dep.split('!');
104+
if (pieces.length > 1) {
105+
prefix = pieces[0] + '!';
106+
_path = pieces[1];
107+
}
108+
else {
109+
prefix = '';
110+
_path = pieces[0];
111+
}
112+
if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) {
113+
var res = path.join(path.dirname(module), _path).replace(/\\/g, '/');
114+
return prefix + res;
115+
}
116+
return prefix + _path;
117+
});
118+
return {
119+
module: module,
120+
deps: deps
121+
};
122+
};
123+
destFiles.forEach(function (destFile, index) {
124+
if (!/\.js$/.test(destFile.dest)) {
125+
return;
126+
}
127+
if (/\.nls\.js$/.test(destFile.dest)) {
128+
return;
129+
}
130+
// Do one pass to record the usage counts for each module id
131+
var useCounts = {};
132+
destFile.sources.forEach(function (source) {
133+
var matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/);
134+
if (!matches) {
135+
return;
136+
}
137+
var defineCall = parseDefineCall(matches[1], matches[2]);
138+
useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1;
139+
defineCall.deps.forEach(function (dep) {
140+
useCounts[dep] = (useCounts[dep] || 0) + 1;
141+
});
142+
});
143+
var sortedByUseModules = Object.keys(useCounts);
144+
sortedByUseModules.sort(function (a, b) {
145+
return useCounts[b] - useCounts[a];
146+
});
147+
var replacementMap = {};
148+
sortedByUseModules.forEach(function (module, index) {
149+
replacementMap[module] = index;
150+
});
151+
destFile.sources.forEach(function (source) {
152+
source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, function (_, moduleMatch, depsMatch) {
153+
var defineCall = parseDefineCall(moduleMatch, depsMatch);
154+
return "define(__m[" + replacementMap[defineCall.module] + "], __M([" + defineCall.deps.map(function (dep) { return replacementMap[dep]; }).join(',') + "])";
155+
});
156+
});
157+
destFile.sources.unshift({
158+
path: null,
159+
contents: [
160+
'(function() {',
161+
("var __m = " + JSON.stringify(sortedByUseModules) + ";"),
162+
"var __M = function(deps) {",
163+
" var result = [];",
164+
" for (var i = 0, len = deps.length; i < len; i++) {",
165+
" result[i] = __m[deps[i]];",
166+
" }",
167+
" return result;",
168+
"};"
169+
].join('\n')
170+
});
171+
destFile.sources.push({
172+
path: null,
173+
contents: '}).call(this);'
174+
});
175+
});
176+
return destFiles;
177+
}
93178
function removeDuplicateTSBoilerplate(destFiles) {
94179
// Taken from typescript compiler => emitFiles
95180
var BOILERPLATE = [

build/lib/bundle.ts

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,105 @@ function emitEntryPoints(modules:IBuildModuleInfo[], entryPoints:IEntryPointMap)
185185
});
186186

187187
return {
188-
files: removeDuplicateTSBoilerplate(result),
188+
files: extractStrings(removeDuplicateTSBoilerplate(result)),
189189
bundleData: bundleData
190190
};
191191
}
192192

193+
function extractStrings(destFiles:IConcatFile[]):IConcatFile[] {
194+
let parseDefineCall = (moduleMatch:string, depsMatch:string) => {
195+
let module = moduleMatch.replace(/^"|"$/g, '');
196+
let deps = depsMatch.split(',');
197+
deps = deps.map((dep) => {
198+
dep = dep.trim();
199+
dep = dep.replace(/^"|"$/g, '');
200+
dep = dep.replace(/^'|'$/g, '');
201+
let prefix:string = null;
202+
let _path:string = null;
203+
let pieces = dep.split('!');
204+
if (pieces.length > 1) {
205+
prefix = pieces[0] + '!';
206+
_path = pieces[1];
207+
} else {
208+
prefix = '';
209+
_path = pieces[0];
210+
}
211+
212+
if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) {
213+
let res = path.join(path.dirname(module), _path).replace(/\\/g, '/');
214+
return prefix + res;
215+
}
216+
return prefix + _path;
217+
});
218+
return {
219+
module: module,
220+
deps: deps
221+
};
222+
};
223+
224+
destFiles.forEach((destFile, index) => {
225+
if (!/\.js$/.test(destFile.dest)) {
226+
return;
227+
}
228+
if (/\.nls\.js$/.test(destFile.dest)) {
229+
return;
230+
}
231+
232+
// Do one pass to record the usage counts for each module id
233+
let useCounts: {[moduleId:string]:number;} = {};
234+
destFile.sources.forEach((source) => {
235+
let matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/);
236+
if (!matches) {
237+
return;
238+
}
239+
240+
let defineCall = parseDefineCall(matches[1], matches[2]);
241+
useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1;
242+
defineCall.deps.forEach((dep) => {
243+
useCounts[dep] = (useCounts[dep] || 0) + 1;
244+
});
245+
});
246+
247+
let sortedByUseModules = Object.keys(useCounts);
248+
sortedByUseModules.sort((a, b) => {
249+
return useCounts[b] - useCounts[a];
250+
});
251+
252+
let replacementMap: {[moduleId:string]:number;} = {};
253+
sortedByUseModules.forEach((module, index) => {
254+
replacementMap[module] = index;
255+
});
256+
257+
destFile.sources.forEach((source) => {
258+
source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, (_, moduleMatch, depsMatch) => {
259+
let defineCall = parseDefineCall(moduleMatch, depsMatch);
260+
return `define(__m[${replacementMap[defineCall.module]}], __M([${defineCall.deps.map(dep => replacementMap[dep]).join(',')}])`;
261+
});
262+
});
263+
264+
destFile.sources.unshift({
265+
path: null,
266+
contents: [
267+
'(function() {',
268+
`var __m = ${JSON.stringify(sortedByUseModules)};`,
269+
`var __M = function(deps) {`,
270+
` var result = [];`,
271+
` for (var i = 0, len = deps.length; i < len; i++) {`,
272+
` result[i] = __m[deps[i]];`,
273+
` }`,
274+
` return result;`,
275+
`};`
276+
].join('\n')
277+
});
278+
279+
destFile.sources.push({
280+
path: null,
281+
contents: '}).call(this);'
282+
});
283+
});
284+
return destFiles;
285+
}
286+
193287
function removeDuplicateTSBoilerplate(destFiles:IConcatFile[]):IConcatFile[] {
194288
// Taken from typescript compiler => emitFiles
195289
let BOILERPLATE = [

0 commit comments

Comments
 (0)